[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Event Handling and Collision Detection



On Sat, 2009-01-31 at 22:01 -0600, Jake b wrote:
> Why are you making events for draw, update start, collisions, etc?
> Seems like that would just make it more complicated. 

It's sort of an experiment, and it's part of the philosophy behind the
SDK, but that's kind of hard to explain now. Suffice it to say that it
seems more complicated now but it will make sense later when its all
more fleshed out and documented better. Besides, the way things are
right now it isn't actually necessary to use those events if one doesn't
want to, and anyway, the project is still relatively young and things
could change later.

> 
> 
> Most of your events 'draw', 'end', 'begin', 'update' are states, so
> you could have a 'game_state' variable that you set as the current
> state. ( Allows you to branch code if you add a title screen 

I don't really follow you. Are you talking about "states" as in "modes,"
like "option mode," "level select mode," etc.? If so, that's not really
what BEGIN, END, and UPDATE are. They each represent a different part of
the main loop; BEGIN is stuff that happens before anything else (even
before player input), UPDATE is general-purpose, and END stuff that
happens last, right before the scene (not a typo) is drawn.

> 
> 
> if state == "title":
>     self.title_loop()
> else:
>     self.loop()
> 

In the context of my library one might do something like this in order
to implement that sort of thing:


import sbak

def main():
        global game_quit
        
        # ...Init code...
        
        game_quit = False
        while not game_quit:
        
                # This occurs outside the engine's management
                title_loop()
                
                # This starts the event handling and "flow control"
                stuff
                sbak.engine.start(noPygameQuit = True)
                
                # The noPygameQuit=False is to prevent the engine from
                # calling pygame.quit() after the internal main loop has
                # ended. (I'm probably going to remove this "feature" as
                it
                # usually causes more problems than it solves...) 
                
                # After the engine quits the game goes back to the
                # title screen because of the outer loop.
                
if __name__ == "__main__":
        main()


Obviously that's a bit strange and awkward. (However, you could do away
with the outer loop if you didn't plan on going back to the title screen
after the engine quit.) There are several ways you could do a title
screen within the context of this library...I'm going to wait till later
in development to deal with that, though.

> 
> ==
> 
> 
> I would do something like this: ( You can keep event callbacks if you
> want. I think sending copies makes it cleaner )

What do you mean by "sending copies"? Copies of Event objects?

> 
> 
> Then on collisions, I'm calling 'oncollide(other)' on both actors. So
> they both can do class-specific stuff and know who hit them.
> 
> 
> # psuedo-code
> 
> 
> class Actor():
> def oncollide( self, other ):
> # might be useful to know what with or where you where hit.
> self.dead = True
> def update(self,delta):
> self.loc += self.vel *delta # ...
> 
> 
> class Game():
> def init(self): # do non-__init__ stuff
> 
> def loop(self):
> """main loop: events; update(); draw()"""
> while not self.done:
> for event in eventlist():
> self.handle_event(event)
> player.handle_event(event)
> 
> self.handle_keys()
> 
> self.update()
> self.draw()
> 
> def update(self, delta):
> """physics / collisions"""
> for u in self.unit_list[:]: # iterate on copy cause may delete some
> u.move(delta)
> if u.dead:
> self.unit_list.remove( u )
> continue
> 
> # brute force collision
> for other in self.unit_list:
> if colliderect( u, other):
> a.oncollide(other)
> other.oncollide( a )
> 
> 
> def draw(self):
> draw_it()
> for unit in self.unit_list: unit.draw()
> 
> display.flip()
> 
> def handle_event(self, event):
> """Game()'s handling of events.
> events also get copied to anyone else that is 'registered', ie:
> player.
> ATM all types are sent and he just ignores what he doesn't want"""
> if key == ESC: self.done = True
> 
> def handle_keys(self): # if you need non event input
> 
> 
> -- 
> Jake
> 

I forgot to mention something before: in addition to the event handling
system I already talked about there is a second way of dealing with
certain kinds of events, only this kind of event isn't a real "event" in
the sense of it being an Event object that can be posted, etc. Rather,
its more of a pseudo-event.

This secondary system is implemented as a set of special properties that
exist on the Entity class (this is my base "Actor" class as you call it)
and other such classes. Basically it allows you to attach one or more
event handlers to an entity (or actor, or whatever) and whenever the
pseudo-event occurs the handler is executed.

This is different than posting events and handling them later as with
the `event` manager because, first of all, there's not actually any
event occuring; its just one object calling functions that were given to
it by another object, there's no "management" happening. Secondly, the
handlers are called _immediately_ after the trigger action occurs and
not later on as with the event manager.

The only pseudo-event handler that entities support right now is the
"death" handler. This is the handler for when the entity gets "killed".

The following example uses (mostly) real code from the library. It
demonstrates the use of pseudo-event handlers as well as some existing
collision detection code:


from pygame import Rect
import sbak

def main():

	# ...Init code...

	# Create a world
	world = sbak.world.World(...)

	# Create a Bomb entity in the world at coordinates 43,23
	bomb = Bomb(
		world=world,
		pos=(43,23)
	)

	# Create an object for the bomb to collide with
	block = sbak.entity.Entity(
		world=world,
		pos=(43,80),     # Put it below the bomb
		bbox=Rect(0,0,20,20), # Give it some size
		solid=True   # Let the entity partake in collisions
	)

	# Attach some arbitrary action to the bomb's death
	def something(ent):
		print "BOOOOOM!"
	bomb.on_kill.append(something) # on_kill is the "event" handler

	# Attach the world's updater to the UPDATE event
	world.set_auto_update(True)

	# Start the engine
	sbak.engine.start()

class Bomb(sbak.entity.Entity):
	""" An entity that explodes when it hits something. """

	def __init__(self, world, pos):
		# The Entity constructor will handle the given position
		# and world
		super(Bomb,self).__init__(
			world=world,
			pos=pos,
			bbox=Rect(0,0,20,20),  # Give it some size
			solid=True  # Let it partake in collisions
		)

		# This will make the bomb fall (so it can hit something)
		self.ygravity = 1

	def update(self):
		# This will make sure that the basic Entity behavior is
		# applied to the bomb, such as falling in response to
		# gravity.
		super(Bomb,self).update()

		# NOTE: This does NOT cause a COLLISION event to occur;
		# it just checks the surrounding area for other 
		# entities.
		if self.collide_entity():
			# This will remove the entity from the world
			# that it's in and will call whatever handlers
			# are attached to it.
			self.kill()

if __name__ == "__main__":
	main()


Now, I know what you're thinking: Why can't you just handle collisions
like that? And you're right; you can do it that way, and you probably
will often. BUT, handling collisions that way means that responding to
the collision happens _the moment the collision occurs_. That can cause
problems in some situations, and for those situations it is better to
use event-based collisions.