Pausing in 1.1

35 views
Skip to first unread message

simpsus_science

unread,
Jul 17, 2008, 12:14:31 PM7/17/08
to pyglet-users, wst...@googlemail.com
Hallo,

in 1.0 I had to control the main loop myself, so i could check if i am
paused or not. Happily, this cpu thrashing is gone with 1.1. But, how
would I implement pause now?

Thank you.

Bastian

Drew Smathers

unread,
Jul 17, 2008, 12:56:38 PM7/17/08
to pyglet...@googlegroups.com

I think it will help if you consider `pausing' a separate aspect that
isn't intertwined with the event loop. For example, the action of
pausing could result in a flag being set on a physics engine which
stops updating positions on managed objects. Or you have intermediary
event handlers (maybe a function decorator - @pausable) which obey a
global paused flag and block inputs to certain objects, etc. Exactly
what you do depends on the structure of your application, but in any
case. you're better off not coupling a high-level application concern
such as pausing with low-level event handling. Hope this helps.

Cheers,
--
\\\\\/\"/\\\\\\\\\\\
\\\\/ // //\/\\\\\\\
\\\/ \\// /\ \/\\\\
\\/ /\/ / /\/ /\ \\\
\/ / /\/ /\ /\\\ \\
/ /\\\ /\\\ \\\\\/\
\/\\\\\/\\\\\/\\\\\\
doryuu

simpsus_science

unread,
Jul 17, 2008, 2:03:00 PM7/17/08
to pyglet-users
Thank you for your answer.
Unfortunately, it does not help (a lot).
You talk of applications that update things. In that case, I could
just define a state and use it.
My application works without any constant updating. It works purely
through scheduling and callbacks.

For example:

If I want a sprite to move, I do

sprite.x = rabbyt.lerp(0, 100, dt = 1)
clock.schedule(lambda dt:sprite.end_move(), 1)

(Yes, I use rabbyt for the sprites). So, in order to pause, I need the
scheduler to stop counting, and stop the lerp from "lerping". I do not
want to stop drawing, as I want to show menus and stuff.

Thank you in advance.

dio...@gmail.com

unread,
Jul 17, 2008, 2:15:29 PM7/17/08
to pyglet-users
This page should help you:
http://pyglet.org/doc/1.1/programming_guide/user_defined_clocks.html

In other words, you can make your own Clock class that knows when the
game is paused. When the game is paused, it gives the moment of the
pause as the current time. When it is unpaused, it goes back to giving
the system time, *minus the total amount of time spent in the paused
state.* It's as if that period of time never happened.

To rephrase a second time:
-Subclass pyglet.clock.Clock (see API docs)
-Add fields total_pause_time and pause_start
-When unpaused, return system_time_in_millisecs() - total_pause_time
-When paused, pause_start = system_time_in_millisecs() return
pause_start - total_pause_time
-When unpausing from paused state, total_pause_time +=
system_time_in_millisecs() - pause_start

Richard Jones

unread,
Jul 17, 2008, 6:07:01 PM7/17/08
to pyglet...@googlegroups.com
On Fri, 18 Jul 2008, simpsus_science wrote:
> in 1.0 I had to control the main loop myself, so i could check if i am
> paused or not. Happily, this cpu thrashing is gone with 1.1. But, how
> would I implement pause now?

If you had a single update method for game behaviour then you could just
unschedule it.


Richard

dio...@gmail.com

unread,
Jul 17, 2008, 7:04:42 PM7/17/08
to pyglet-users
That would not work if powerup life/death was based on
scheduling...would it?

Richard Jones

unread,
Jul 17, 2008, 8:30:20 PM7/17/08
to pyglet...@googlegroups.com
On Fri, Jul 18, 2008 at 9:04 AM, dio...@gmail.com <dio...@gmail.com> wrote:
> That would not work if powerup life/death was based on
> scheduling...would it?

You are correct if they're individually scheduled.


Richard

Keeyai

unread,
Jul 18, 2008, 12:00:47 PM7/18/08
to pyglet-users
I think the custom clock mentioned by diordna is a great place to
start and is probably the 'right' way to do it.

A little bit more hackish would be to only schedule your game events
with the rabbyt scheduler, then stop calling rabbyt.add_time. Pausing
like this is even mentioned in the docs.
http://matthewmarshall.org/projects/rabbyt/docs/rabbyt/anims/

This approach is probably confusing to others reading your code, but
gives you a way to split up your game clock and the low level pylet
clock. Still, I would go with the custom clock option from above for
clarity and style.

Casey Duncan

unread,
Jul 18, 2008, 12:30:43 PM7/18/08
to pyglet...@googlegroups.com
The pyglet clock class allows you to pass in your own time function to
facilitate things like pausing. You could just instantiate your own
app clock and schedule all pause-able tasks with it. When paused, your
time function would not advance.

-Casey

dio...@gmail.com

unread,
Jul 18, 2008, 1:22:17 PM7/18/08
to pyglet-users
I haven't written much Python in a couple of months, and I didn't test
the code below at all except to check that it compiles, but it's a
start. Feel free to steal, tweak, and correct. If I have Python's
module behavior straight, you should be able to stick this in
gametime.py, import it, and then just call gametime.pause() and
gametime.unpause().

import pyglet.clock, time

pause_start = 0
total_pause_time = 0
paused = False

def game_time():
if paused:
return pause_start - total_pause_time
else:
return time.time() - total_pause_time

def pause():
if not paused:
paused = True
pause_start = time.time()

def unpause():
if paused:
paused = False
total_pause_time += time.time() - pause_start

pyglet.clock.get_default().time = game_time

dio...@gmail.com

unread,
Jul 18, 2008, 3:00:45 PM7/18/08
to pyglet-users
I just did some actual testing and came up with a better solution that
actually works. Drop it in 'gameclock.py', import it, and then you can
gameclock.pause(), gameclock.unpause(), and gameclock.toggle_pause()
at will. I also included gameclock().dt(), because I like to treat
delta_t as a read-only global.

import pyglet.clock, time

class GameClock(pyglet.clock.Clock):
def __init__(self):
self.dt = 0.0
self.paused = False
self.pause_start = 0
self.total_pause_time = 0
super(GameClock, self).__init__(time_function=self.game_time)

def tick(self,poll=False):
self.dt = super(GameClock,self).tick(poll)
if self.dt > 0.2: self.dt = 0 #sanity check
if self.paused: self.dt = 0

def game_time(self):
if self.paused:
return self.pause_start - self.total_pause_time
else:
return time.time() - self.total_pause_time

def pause(self):
if not self.paused: self.toggle_pause

def unpause(self):
if self.paused: self.toggle_pause()

def toggle_pause(self):
self.paused = not self.paused
if self.paused: self.total_pause_time += time.time() -
self.pause_start
else: self.pause_start = time.time()

#convenience functions
def dt(): return pyglet.clock.get_default().dt
def toggle_pause(): pyglet.clock.get_default().toggle_pause()
def pause(): pyglet.clock.get_default().pause()
def unpause(): pyglet.clock.get_default().unpause()

pyglet.clock.set_default(GameClock())

infinite8s

unread,
Jul 18, 2008, 5:14:36 PM7/18/08
to pyglet-users

Actually, you could combine these ideas, and use the custom clock to
only call rabbyt.add_time. Basically the custom clock would be a
rabbyt clock.

On Jul 18, 12:00 pm, Keeyai <kee...@gmail.com> wrote:
> I think the custom clock mentioned by diordna is a great place to
> start and is probably the 'right' way to do it.
>
> A little bit more hackish would be to only schedule your game events
> with the rabbyt scheduler, then stop calling rabbyt.add_time. Pausing
> like this is even mentioned in the docs.http://matthewmarshall.org/projects/rabbyt/docs/rabbyt/anims/

simpsus_science

unread,
Jul 19, 2008, 6:21:54 AM7/19/08
to pyglet-users
what is a showstopper for me is that rabbyts scheduler does not
support unscheduling in contrast to pyglets scheduler. pyglet has a
glitch to that because pyglets scheduler passes the dt time to the
called function, which is a problem is you schedule lambda dt: call()
because you cannot unschedule that (unless you store it). But thats
just a sidenote (I wrote abount it in another thread).

Anyway, to keep my app sane I need to unschedule things from time to
time, so using rabbyts scheduler is not an option.

But thanks for the masses of replies! Especially diordna's class seems
very promising. I will definetely try that!
Although I kind of wonder: Why isn't this functionality already inside
the default pyglet clock? Am I the only one who wants to pause his
game?
And to me pausing seems to be something that global that it would
easily fit in a standard clock.

Thanks again. Your help is much appreciated!

Cheers

Bastian

On Jul 18, 11:14 pm, infinite8s <naveen.michaudagra...@gmail.com>
wrote:

Keeyai

unread,
Jul 20, 2008, 4:39:52 AM7/20/08
to pyglet-users
You aren't the only one to want to pause, you just want to do it in a
different way. :) When I pause, I change the state of the game to a
pause state, which stops the game state from receiving time events.
Not claiming it is a better way, it just doesn't require me to mess
with the clock at all, which is good because that drives the whole
application.

simpsus_science

unread,
Jul 20, 2008, 7:03:52 AM7/20/08
to pyglet-users
I gave diordnas proposal a spin, and ran into some strange errors.
When taking a closer look at the code, I realized that I did not
understand what (s)he was doing.
So I adjusted the skeletton to my level of python skills and even got
it to work (in a 2 second test..)
Here it goes:

import pyglet.clock, time

class GameClock(pyglet.clock.Clock):

def __init__(self):
self.dt = 0.0
self.paused = False
self.pause_start = 0
self.total_pause_time = 0
super(GameClock, self).__init__(time_function=self.game_time)
pyglet.clock.set_default(self)

def tick(self,poll=False):
self.dt = super(GameClock,self).tick(poll)
if self.paused: self.total_pause_time += self.dt

def game_time(self):
if self.paused:
return self.pause_start
else:
return time.time() - self.total_pause_time

def pause(self):
if not self.paused:
self.pause_start = time.time()
self.toggle_pause()

def unpause(self):
if self.paused:
self.toggle_pause()

def toggle_pause(self):
self.paused = not self.paused

I do not know if one approach is better. what is a drawback of mine is
that I store the total pause time and substract it every time I return
the current time. So if there are long periods of pause, a high number
has to be substracted each frame when the game runs again. The game
"stays" in the past so to speak. I can't think of any other way to do
it atm, but this may be because of my bad design with scheduling
callbacks.

Cheers

Bastian

simpsus_science

unread,
Jul 20, 2008, 8:50:44 AM7/20/08
to pyglet-users
[Update] my mate was babbeling about accelleration, so I gave it a
spin, and it works.
The code is a bit longer than my previous post, but it looks so
generic to me that I could make put it in the File Section, if someone
is interested.

Matthew Marshall

unread,
Jul 20, 2008, 2:14:13 PM7/20/08
to pyglet...@googlegroups.com
simpsus_science wrote:
> what is a showstopper for me is that rabbyts scheduler does not
> support unscheduling in contrast to pyglets scheduler. pyglet has a
> glitch to that because pyglets scheduler passes the dt time to the
> called function, which is a problem is you schedule lambda dt: call()
> because you cannot unschedule that (unless you store it). But thats
> just a sidenote (I wrote abount it in another thread).
>
> Anyway, to keep my app sane I need to unschedule things from time to
> time, so using rabbyts scheduler is not an option.

You know, it's not all that hard to write your own scheduler that does
things the way you want. The one in Rabbyt is only 10 lines of code or so.

MWM

Alex Holkner

unread,
Jul 20, 2008, 3:59:36 PM7/20/08
to pyglet...@googlegroups.com

For my PyWeek entry, I created a game clock (another instance of
pyglet.clock.Clock), with a custom time function that returned the
game time. The game clock is then manually ticked by the global clock
only if the game is not paused. In this example a fixed timestep is
used on the game clock, which is useful for collision detection.

class Game(object):
game_time = 0
accum_time = 0
update_t = 1/60.

def __init__(self):
self.clock = pyglet.clock.Clock(time_function=lambda: self.game_time)
pyglet.clock.schedule_interval(self.update, self.update_t)

def update(self, dt):
if self.paused:
return

# Align updates to fixed timestep
self.accum_t += dt
if self.accum_t > self.update_t * 3:
self.accum_t = self.update_t
while self.accum_t >= self.update_t:
self.game_time += self.update_t
self.clock.tick()
self.accum_t -= self.update_t

Now just remember to schedule game events on the game clock, and
real-world events on the pyglet clock (usually just menu animations).

Alex.

Drew Smathers

unread,
Jul 20, 2008, 4:13:19 PM7/20/08
to pyglet...@googlegroups.com

If think rather than writing you own scheduler, proxying the main one
is better (for some use cases). pyglet has support for more complex
media event scheduling (schedule_interval_soft) to prevent trampling
the CPU, for example. Also, by proxying per scene/level in the
context of the game you can keep references to things scheduled in the
context of a scene/level and prevent accidentally leaking references
by: 1) "noop"-ing on schedule requests after a scene is completed and
2) explicitly tracking/unscheduling functions scheduled in the scene.
Just my two cents on this.

--
\\\\\/\"/\\\\\\\\\\\
\\\\/ // //\/\\\\\\\
\\\/ \\// /\ \/\\\\
\\/ /\/ / /\/ /\ \\\
\/ / /\/ /\ /\\\ \\
/ /\\\ /\\\ \\\\\/\
\/\\\\\/\\\\\/\\\\\\

d.p.s

Reply all
Reply to author
Forward
0 new messages