I recently startet getting into pyglet and rabbyt from pygame, but I have hit something of a brick wall.
I created a basic example where one Sprite (of the type found in pyglet.sprite.Sprite) is displayed at 60 frames per second. The problem is that this simple program is using up 50% of the CPU time somehow. I repeated the experiment with the sprite type found in the rabbyt library with the same result.
I decided to render 1000, then 10 000 sprites at 60 frames per second, and to my surprise the CPU usage stays at 50%. The only thing is that moving or animating a sprite results in slight stuttering.
Lastly, I tried running at 360 frames per second. Same result, 50% usage.
Here is the sample code:
import pyglet
import rabbyt
def on_draw(dt):
window.clear()
spr.render()
global window
window = pyglet.window.Window(800, 600)
spr = rabbyt.Sprite('ship.png')
spr.x = 100
spr.y = 100
pyglet.clock.schedule_interval(on_draw, 1.0/60.0)
if __name__ == '__main__':
pyglet.app.run()
I am using a Core 2 Duo with an ATI HD 3500 card.
Any advice/ideas are appreciated.
Be aware that the default pyglet event handler will fire an 'on_draw' event every time it clears the event queue.
http://www.pyglet.org/doc/programming_guide/the_application_event_loop.html
The pyglet application event loop dispatches window events (such as for mouse and keyboard input) as they occur and dispatches the on_draw event to each window after every iteration through the loop.
This means that any event can trigger a redraw.
So if you're moving the mouse about or doing anything that fires events, you will get massive slow down as it begins to trigger render calls.
This also caused problems because I was doing my own render calls, so I would get the two buffers fighting which created a 'ghost' effect on the screen. Took me a while to realise this was the cause.
I monkey patched the event loop to not do this.
https://github.com/adamlwgriffiths/PyGLy/blob/master/pygly/monkey_patch.py
Be aware that this patched event loop will no longer render on it's own, you must manually flip the buffers or trigger an 'on_draw' event.
It might be the case that, although you've hooked in at 60fps, but the internal render loop is ticking at the maximum possible rate.
I dislike code that takes away control, hence my patch lets me decide when render events occur.
Hmm.. You might want to know the fps at which the game runs, if it helps:
cldis = pyglet.clock.ClockDisplay()
Then add this to your on_draw function:
cldis.draw()
it draws the current fps at the bottomleft corner of the screen in a semi-transparent color.
I know that in Pygame there is the built-in called "Clock". You can put a limit on how many times the game loops per second using the tick method. In my example I have put a limit of 30 FPS. This prevents your CPU being on constant demand.
clock = pygame.time.Clock()
While 1:
clock.tick(30) # Puts a limit of 30 frames per second on the loop
In pyglet there appears to be something similar:
pyglet.clock.schedule_interval(on_draw, 1.0/60.0)
clock.set_fps_limit(60)
Hope that helps!
edit: documentation on fps limit: http://pyglet.org/doc/api/pyglet.clock-module.html#set_fps_limit
Related
I recently wrote my first pygame program and am working on a second, and discovered an issue with running the program. I use the editor MuEditor for Python 3.8.6, and when I run the game using this editor it works exactly how I want it to. Although, if I run the program from ANY other editor such as IDLE or PyCharm, it ramps up the game speed and displays the fonts incorrectly, making the game much more difficult to play. My friend wants to be able to play the games I write, so I sent him the code through an email and they ran it through MuEditor, and it still worked incorrectly. The game only runs how it should when I run it from my computer in MuEditor, and I was wondering if anyone else has had this problem or if there's a fix for it? I highly doubt the error here is in my code, but I can provide it if necessary.
Use pygame.time.Clock to control the frames per second and thus the game speed. See pygame.time.Clock.tick():
This method should be called once per frame. It will compute how many milliseconds have passed since the previous call.
The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time.
That means that the loop:
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
runs 60 times per second.
I was experimenting with pygame and noticed it raised a VideoExpose event when I press alt+tab and the window is fullscreen. when I switch press alt+tab again, everything on the screen is moved to the bottom left.
I know that this is supposed to mean that 'portions of the window must be redrawn', but how am I supposed to redraw them and what why does pygame even have this event in the first place?
If you are writing a program to use the windowing Event Model, the windowing environment sends the program events to notify it of environmental changes - window resize, mouse move, need to re-paint, etc.
Not handling these events will cause your application to be considered "non responsive" by the environment. Here on SO, there's about one question a week with PyGame and exactly this issue.
When working with PyGame re-drawing event handling may seem superfluous as the majority of PyGame games redraw the entire screen every frame, e.g.: 60 FPS. But if unnecessary, this method is a complete waste of resources (CPU-time, electricity, etc.) It is quite simple though, so good for beginners.
Say you were writing a card game like Solitaire... the screen updates only when interacting with the user. In terms of CPU, it's doing nothing 99.9% of the time while the user contemplates their next move. In this case, the program could be written to only re-draw the screen when necessary. When is it necessary? When the player gives input, or the program receives a pygame.VIDEOEXPOSE event from the widowing environment.
If your program is redrawing the window constantly, you can simply ignore the message. If not, when receiving the message call whatever block of code is normally used to render the window. The expose message may come with the region of the screen that needs to be re-drawn, in this case a really good application would only update that section of the display.
I have a simple Pygame game which I need to create a splash screen for that displays the game name for 5 seconds. This is currently being achieved by blitting some standard text to the display surface and calling pygame.time.delay(5000).
However, whilst the 5 seconds elapse my game does not seem to be capturing any events. The event procedure is located in a while True: loop below the splash screen function but it is obvious that pygame.time.delay() is freezing my entire code... but I need the event procedure to still function so I can detect quit /escape key (to skip the splash screen) events.
Thanks in advance,
Ilmiont
With Pygame you can use the pygame.time.get_ticks function when the splash screen appears and record this time. Continue getting input from the user and calling get_ticks to see if 5000 milliseconds have elapsed and when it has you may discontinue the splash screen and continue as normal.
In the same game as last time, I've ran into a new problem. When I move the mouse, FPS increases from around 60 to over 500. I know what you're thinking; it's not because of on_draw() getting fired each event (I think), since I already did override of pyglet.app.EventLoop.idle:
class EventLoop:
def idle(self):
pyglet.clock.tick(poll=True)
return pyglet.clock.get_sleep_time(sleep_idle=True)
pyglet.app.EventLoop = EventLoop()
Also I call flip() on the window in my drawing function. All the useless mouse motion events take up a lot of CPU, which is annoying. What can I do about it?
Edit
I added window.invalid = False to my drawing function and window.invalid = True to my update function, this seems to reduce CPU usage with other mouse actions.
Edit 2
The drawing function is a typical on_draw() function.
Edit 3
After some more investigating, it seems that all those events don't take that much CPU as I thought they would. Still it would be good to know if this is the way Pyglet is supposed to act, or if it's something that should be avoided.
All you should be doing on mouse events is updating your apps' model of the input control state and using that in the next regular scheduled update and redisplay of the world model (which is presumably much more complex with physics and rendering and stuff).
ie just because mouse events come in at ~300fps doesn't mean you actually have to do all the stuff you want to do at 300fps.
It's been a while since I did any Pyglet, but the pattern I seemed to use was to subclass Pyglet's window.Window as MyGameWindow, then that registered event handlers like
#self.event
def on_mouse_motion(x,y,dx,dy):
self.mouse_position=(x,y)
self.mouse_buttons=0
self.invalid = False
(also on_mouse_drag, on_mouse_press, on_mouse_release). Hmmm... actually, I think that assignment to self.invalid might have been crucial for overriding Pyglet's default behaviour and defering any further updating/drawing until the next "clock tick".
I set the on_motion to handle EVT_MOTION. I want the mouse location for interactively generating a coordinate-specific image, but WxPython has a ~400ms delay in registering successive motion events. Which makes the interface sluggish.
Why is EVT_MOTION so slow and how do I fix it? I tried it in Ubuntu 11.10 and WinXP and the delays are comparable?
I need fast response times for selecting a portion from an image like the picture shows. As it stands, the "cross-hairs" follow the mouse too slowly.
Here is the code which I tried EVT_MOTION:
def on_motion(self, event):
"""mouse in motion"""
#pt = event.GetPosition()
self.mouseover_location = event.GetPosition()
self.t2 = time.time()
print "delay",self.t2 - self.t1
self.t1 = self.t2
delay 0.379776954651
delay 0.00115919113159
delay 0.421130895615
delay 0.416938066483
delay 0.376848936081
delay 0.387464046478
delay 0.40311384201
delay 0.392899036407
delay 0.385301113129
delay 0.422554969788
delay 0.355197906494
The question as it stands is incomplete, as there is no sample app to demonstrate the problem. However, I would say that the motion handler has got nothing to do with your problem, because most likely you are doing some expensive operation between subsequent motion handlers (like refreshing your entire drawing canvas).
If this is the case (and you can easily check if your paint routine is called between mouse motion events), I would suggest the following:
If your drawing that stuff yourself, ensure that you are using double buffering (via wx.BufferedPaintDC);
If your paint routine is indeed called between mouse motions, try and refresh only the damaged portion of your plot (via RefreshRect);
Use wx.Overlay to draw your rectangular selection (there are a few demos available on how to do that);
Post a small, runnable sample app that demonstrate the problem.
The EVT_MOTION is fired every time the mouse is moved! If you then call event.GetPosition() on every movement and also process the data, this will slow down performance.
How would it be to use EVT_LEFT_DOWN or something similar, and then get the position and process that data.
This will be much more efficient since you are only looking for a certain area of the image.
We'll really need to see what else is going on in the application to be able to give you any meaningful answers, although many people are able to solve the problems themselves in the process of creating a small sample demonstrating the problem to share with others.
http://wiki.wxpython.org/MakingSampleApps
Optimizing how you are drawing the cross-hairs and/or how you are refreshing the main content of the window is probably your best bet, but until you share more details all we can do is guess.