Milestone 0.9

Published January 29, 2008
Advertisement
It moves, but not as we know it.



(Pretend that it's moving in some direction you didn't click.)

I just gotta work out the details of the navigation function a bit. But hey, it works in a component based framework.

Let's see if we can't get some particles shooting out the back of there, then some asteroids floating around, some collision detection ... and the navigation working properly, of course.

I'm kinda fried on technical details, but at least I got the trig mostly figured out this evening with little trouble. It's much better than that one program I made with gravity inversions in certain quadrants of this space simulation.

Let's call it milestone 0.9


Due to popular demand, here's some code and a little explanation:

An example of the components in-use


Building the ship:
class Ship(Entity):	def __init__(self):		Entity.__init__(self)		# TEST		self.addState( 'position' )		self.getStateByName('position').coords = [100,100]		self.getStateByName('position').rotation = 2.0		self.addStateObject( Sprite( 'ship64_redA' ) )		self.addStateObject( ShipHull() )		self.pushAction( 'move' )


Here're the states, which are stuck into things to contain info of various sorts. Entities are handled depending on which states they have and what's in said states.

class Position(State):	''' contains position coordinates, x/y velocity vectors, and rotation '''	name = 'position'	def __init__(self, coords=[0.0,0.0],velocity=0.0,angle=0.0,rotation=0.0):		self.coords = coords		self.velocity = velocity		self.rotation = rotation		self.angle = angle		# flags		self.noMove = False#from ..gfx import imageCacheclass Sprite(State):	''' contains a graphic -- be it a texture? '''	name = 'sprite'	def __init__(self, imageName = 'testFiller'):		#self.image = imageCache.get( 'testFiller' ) # can't get image before imagecache has been init'ed		self.imageName = imageName # handle by name?		self.color = [1.0,1.0,1.0,1.0]		class NavInfo(State):	''' contains navigation infomartion '''	name = 'navinfo'	def __init__(self):		# list of targets to 'hit', in order		# last target on list is 'full stop'; 		# others are 'follow through'		self.targets = [] 		from ..ship.components import componentCache		class ShipHull(State):	''' contains various components and stuff '''	name = 'shiphull'	def __init__(self):		# TEST: use some defaults		self.engine = componentCache.get( 'engTest' )		


Here are the actions which can be run on entities so far. "Move" is basically a physics movement updater, "Navigate" is a bit more complex (and is somewhat broken). Actions get called from action stacks held in entities and these repeating actions re-add themselves to the stack, usually. It's downright procedural, actually.

class Move(Action):	name = 'move'	id = False	def __call__(self,parent):		if parent.hasState( 'position'):			posObj = parent.getState('position')						## rotate ship by rotation			posObj.angle += posObj.rotation						## convert from velocity & angle to x/y components			# TEST: hope this is correct			vectorX = posObj.velocity * math.cos( math.radians( posObj.angle ) )			vectorY = posObj.velocity * math.sin( math.radians( posObj.angle ) )						## find new position from vectors			posObj.coords = add( (vectorX, vectorY), posObj.coords )						## write new info to parent			parent.setStateObject( posObj )				if self.id: parent.pushActionByID( self.id )		else: parent.pushAction( 'move' )		class Navigate(Action):	''' use NavInfo to move parent toward navinfo.targets[1:] '''	name = 'navigate'	id = False	def __call__(self,parent):		# TODO: make sure parent has everything it needs?		# eg. position, navinfo, shiphull w/ engines, power, etc.		#if parent.hasState( 'navinfo' ):		#	# do stuff		#print 'navigate!'		fullstop = False		position = parent.getState( STATE_POSITION )		shipHull = parent.getState( STATE_SHIPHULL )		navInfo = parent.getState( STATE_NAVINFO )				delta = timer.getDelta()				## first: Angle		# aim self at target as much as possible		# 1: get angle between self and target		angleDif = angleBetween( position.coords, navInfo.targets[0] )		# 2: if not same, set angle toward target as much as possible 		if not angleDif == 0.0: 			turnRate = shipHull.engine['turnRate'] * delta			# if dif < turnRate, set angle to target 			if angleDif <= turnRate:				position.angle = angleDif				position.rotation = 0.0			elif angleDif < 0.0: # right				position.rotation = -turnRate					else: # angleDif > 0 # left				position.rotation = turnRate						## then: Position		distToTarget = distanceBetween( position.coords, navInfo.targets[0] )		# if at target, stop (unless there are more targets!)		if distToTarget < 1.0:			position.coords = navInfo.targets[0]			navInfo.targets = navInfo[:1]			if len( navInfo.targets ) == 0:				# full stop				self.position.velocity = 0.0				fullstop = True			else:				# go to next target ... when this gets called next				pass						maxSpeed = shipHull.engine['maxSpeed']		thrust = shipHull.engine['thrust']		# if within decel range of target, do 'manuever speed'		if distToTarget < maxSpeed / thrust:			maxSpeed = maxSpeed * 0.5				else:			# else speed up to maxvelocity!			if position.velocity < maxSpeed:				position.velocity += thrust			if position.velocity > maxSpeed:				position.velocity = maxSpeed		## write back		parent.setStateObject(navInfo)		parent.setStateObject(position)				# end		if not fullstop:			if self.id: 				parent.pushActionByID( self.id )				return			else: 				parent.pushAction( 'navigate' )				return


And finally, the generic entity class. Basically it holds and handles States, and has an action queue that gets actions pushed to it which get executed upon update():

class Entity:	def __init__(self):		self.name = False # add this later		# self.id =  #??		#self.possibleActions = {} # store by id ## remove this dict?		self.states  = {} # actual state objects		self.actionQueue = []		self.children = [] # for sub-entities			## *** Adders	def addState(self,stateName):		''' add new state by name w/ default settings '''		#if not self.states.has_key( stateName ):		state = stateCache.getObject( stateName )		self.states[ stateCache.getID( stateName ) ] = state	def addStateObject(self,stateObject):		''' add new state by passing in the object itself '''		self.states[ stateCache.getID( stateObject.name ) ] = stateObject	def setStateObject(self,stateObject):		''' set current state to given state '''		self.states[ stateObject.id ] = stateObject			## *** Removers	def removeState(self,stateName):		if self.hasState(stateName): 			del self.states[stateName]			## *** Has boolean checks	def hasState(self,nameOrID):		stateID = self.stateNameToID( nameOrID )		if self.states.has_key( stateID ): 			return True		else: 			return False			## *** Getters	def getStateByName(self,stateName):		stateID = stateCache.getID(stateName)		if self.hasState( stateID ): 			return self.states[ stateID ]		else:			return False	def getStateByID(self,stateID):		#print stateID		if self.hasState( stateID ):			return self.states[ stateID ]		else:			return False	def getState(self,nameOrID):		return self.getStateByID( self.stateNameToID( nameOrID ) )		## *** Name/ID utils	def actionNameToID(self,nameOrID):		if type( nameOrID ) == IntType:			return nameOrID		elif type( nameOrID ) == StringType:			return actionCache.getID( nameOrID )		else:			return False	def stateNameToID(self,nameOrID):		if type( nameOrID ) == IntType:			return nameOrID		elif type( nameOrID ) == StringType:			return stateCache.getID( nameOrID )		else:			return False	# *** Action/Update handling 	def pushAction(self,nameOrID):		''' push action onto self.actionQueue '''		return self.pushActionByID( self.actionNameToID(nameOrID) )	def pushActionByID(self,actionID):		''' push action onto self.actionQueue by ID '''		#print 'push:', actionID		self.actionQueue.append( actionID )		## forget hasAction ?		#if self.hasAction( actionID ):		#	self.actionQueue.append( actionID )		#	return True		#else: 		#	return False				def update(self):		#print self.actionQueue		# separate current queue from new queue		tempQueue = copy( self.actionQueue )		self.actionQueue = []		while( len(tempQueue) > 0 ):			actionID = tempQueue.pop(0)			#print actionID			actionCache.getByID( actionID )( self )			#self.actions[ action ](self) # pass self into function as parent 					for child in self.children:			child.update()
Previous Entry Shoulda called in sick
Next Entry Glow and Greeble
0 likes 4 comments

Comments

Ravuya
I'm always interested to hear about component systems -- how is yours structured? It seems like it might be a good way to start increasing my reusability.
January 29, 2008 11:38 PM
darkpegasus
No obligatory dcosborn reference? What is the world coming to?

Incidentally, I tried imagining clicking and the damn thing kept going in the direction I expected.
January 30, 2008 12:20 AM
dcosborn
Quote:Oort: An Intrastellar Romance
Nice name. You don't see too many Romance-genre games out there. [wink] Its looking good so far. Kind of like an "Attas: The Second Attempt", but it sounds like with a more finishable scope.
January 30, 2008 12:32 AM
dbaumgart
Ravuya
Posted code and such. I'm still working the system out, but I like how it's working so far. And yeah, it's a lot of trouble for getting one ship flying around, but I think it'll really start paying off when all sorts of entities with different attributes and behaviours are added to the world.

Will probably be some major refactorings involved, but 'eh. I'll keep everything posted.

darkpegasus
Better start imaginin' a lot harder than that!
And dcosborn showed up, so it's not like the post is completely without him.

dcosborn
Well, it's more of a romance is the classical sense, like uh, a "grand story". Or maybe there can be other romance too, that'd be nice. I mean, I really just wanted a fun window title, but maybe you'll force my hand on this and it'll actually have to have romance. Hmm ...

But yeah, it is a lot like ATTAS 2, but smaller. Maybe if it takes off and we want to go ahead and build something on the scale of ATTAS out of Oort, you'll want to join in? The engine (and openGL parts especially) would no doubt benefit from your sk1llZ.

Well no point in planning that yet, we'll sort it out all in good time. And hey, there's always Procyon: Plan B from outer space!
January 30, 2008 01:07 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement