commit:c4fa26fbf7a775bc07b6a8bc86ad40cb9f62bea7
author:chip
committer:chip
date:Fri Nov 23 09:50:37 2007 +0000
parents:ffafe84871d82a090dd9867299891c311102514b
Added Actor object, collision now works on rects as well as points
diff --git a/Actor.py b/Actor.py
line changes: +115/-0
index 0000000..13c65fe
--- /dev/null
+++ b/Actor.py
@@ -0,0 +1,115 @@
+from Sprite import *
+
+class Actor:
+	def __init__(self, surfaces=None):
+		self.states = {}
+		self.currentstate = None
+		self.surfaces = surfaces
+		self.position = [0,0]
+		self.vgravity = 0
+
+	
+	def defineState(self, statename, sprite):
+		self.states[statename] = sprite
+
+
+	def setState(self, statename):
+		self.currentstate = statename
+		self.states[self.currentstate].reset()
+		self.states[self.currentstate].play()
+
+
+	def setPosition(self, x, y):
+		self.position = [x,y]
+
+
+	def collideDelta(self, delta):
+		if not self.surfaces:
+			return False
+		s = self.states[self.currentstate]
+		# Don't panic. We don't care about the sprite's position since
+		# we keep track of it ourself.
+		s.setPosition(self.position[0], self.position[1])
+		s.hitbox.move_ip(delta[0], delta[1])
+		if self.surfaces.collideRect(s.hitbox):
+			return True
+		else:   
+			return False
+
+
+	def moveDelta(self, delta):
+		collided = False
+		s = self.states[self.currentstate]
+		s.setPosition(self.position[0], self.position[1])
+		d = [0,0]
+		if self.surfaces.collideRect(s.hitbox) or (delta[0] == 0 and delta[1] == 0):
+			return (True, (0,0))
+		if abs(delta[0]) > abs(delta[1]):
+			dx = delta[0] / abs(delta[0])
+			dy = float(delta[1]) / float(abs(delta[0]))
+			xrange = abs(int(delta[0]))
+			while xrange > 0:
+				d[0] += dx
+				d[1] += dy
+				nrect = s.hitbox.move(d[0],d[1])
+				if self.surfaces.collideRect(nrect):
+					# Slide along surfaces if possible
+					nrect = s.hitbox.move(d[0],d[1]-dy)
+					if not self.surfaces.collideRect(nrect):
+						d[1] -= dy
+						xrange -= 1
+						continue
+					nrect = s.hitbox.move(d[0]-dx,d[1])
+					if dy != 0 and not self.surfaces.collideRect(nrect):
+						d[0] -= dx
+						xrange -= 1
+						continue
+					d[0] -= dx
+					d[1] -= dy
+					collided = True
+					break
+				xrange -= 1
+			d[1] = int(d[1])
+		else:
+			dy = delta[1] / abs(delta[1])
+			dx = float(delta[0]) / float(abs(delta[1]))
+			yrange = abs(int(delta[1]))
+			while yrange > 0:
+				d[1] += dy
+				d[0] += dx
+				nrect = s.hitbox.move(d[0],d[1])
+				if self.surfaces.collideRect(nrect):
+					# Slide along surfaces if possible
+					nrect = s.hitbox.move(d[0]-dx,d[1])
+					if not self.surfaces.collideRect(nrect):
+						d[0] -= dx
+						yrange -= 1
+						continue
+					nrect = s.hitbox.move(d[0],d[1]-dy)
+					if dx != 0 and not self.surfaces.collideRect(nrect):
+						d[1] -= dy
+						yrange -= 1
+						continue
+					d[1] -= dy
+					d[0] -= dx
+					collided = True
+					break
+				yrange -= 1
+			d[0] = int(d[0])
+		return (collided, d)
+
+
+	def move(self, delta):
+		(collided, delta) = self.moveDelta(delta)
+		self.position[0] += delta[0]
+		self.position[1] += delta[1]
+		return collided
+
+
+	def update(self):
+		"Override this function. Called once per frame."
+		pass
+
+
+	def draw(self):
+		self.states[self.currentstate].drawAt(self.position)

diff --git a/Engine.py b/Engine.py
line changes: +0/-1
index 9490031..9f72e79
--- a/Engine.py
+++ b/Engine.py
@@ -16,7 +16,6 @@ import config
 
 fpsman = pygame.time.Clock()
 
-
 def init(resolution=(640,480)):
 	pygame.init()
 	try:

diff --git a/Sprite.py b/Sprite.py
line changes: +44/-17
index 31c2ef9..c767565
--- a/Sprite.py
+++ b/Sprite.py
@@ -19,22 +19,15 @@ class Sprite:
 	animating = False
 	anim_t = 0
 	size = (0,0)
-	uparrow = False
 	displaylists = None
 
+	showup = False
+	showhitbox = False
+
 	def __init__(self, file, position=(0,0), scale=1.0, rotation=0,
 		     mirror=False, flip=False, color=(1.0,1.0,1.0,1.0),
 		     framerate=0.0, gravity=(LEFT,BOTTOM)):
 
-		self.position = position
-		self.scale = scale
-		self.rotation = rotation
-		self.mirror = mirror
-		self.flip = flip
-		self.color= color
-		self.setFramerate(framerate)
-		self.gravity = gravity
-
 		self.frames = []
 		d = None
 		if type(file) == type([]):
@@ -46,9 +39,18 @@ class Sprite:
 			d = TexMan.load(file)
 			self.frames.append(d['texture'])
 		self.size = d['size']
-		self.rect = pygame.Rect(self.position, self.size)
 		self.tsize = d['tsize']
 
+		self.rotation = rotation
+		self.mirror = mirror
+		self.flip = flip
+		self.color = color
+		self.setFramerate(framerate)
+		self.scale = scale
+		self.position = position
+		self.gravity = gravity
+
+		self.fixHitbox()
 		self.gen_displaylist()
 
 
@@ -106,7 +108,7 @@ class Sprite:
 
 	def setPosition(self, x, y):
 		self.position = (x,y)
-		self.rect.move_ip(x,y)
+		self.hitbox.topleft = (x + self.hitbox_offset[0], y + self.hitbox_offset[1])
 
 
 	def setColor(self, r, g, b, a = 1.0):
@@ -116,12 +118,27 @@ class Sprite:
 
 	def setScale(self, scale):
 		self.scale = scale
-	
+		self.fixHitbox()
+
 
-	def setGravity(self, xg, yg):
+	def fixHitbox(self):
+		self.hitbox_offset = [0,0]
+		for i in range(0,2):
+			if self.gravity[i] == -1:
+				self.hitbox_offset[i] = 0
+			elif self.gravity[i] == 0:
+				self.hitbox_offset[i] = -(self.size[0] * self.scale) / 2
+			elif self.gravity[i] == 1:
+				self.hitbox_offset[i] = -(self.size[0] * self.scale)
+		self.hitbox = pygame.Rect((self.position[0] + self.hitbox_offset[0], self.position[1] + self.hitbox_offset[1]), (self.size[0] * self.scale, self.size[1] * self.scale))
+
+
+	def setGravity(self, xg, yg, genlist=True):
 		if self.gravity != (xg,yg):
 			self.gravity = (xg,yg)
-			self.gen_displaylist()
+			if genlist:
+				self.gen_displaylist()
+			self.fixHitbox()
 		
 
 	def setFramerate(self, rate):
@@ -140,7 +157,8 @@ class Sprite:
 
 
 	def play(self):
-		self.animating = True
+		if len(self.frames) > 1:
+			self.animating = True
 
 
 	def stop(self):
@@ -165,7 +183,7 @@ class Sprite:
 		if self.scale != 1.0:
 			glScalef(self.scale, self.scale, 1.0)
 		glCallList(self.displaylists[self.frameno])
-		if self.uparrow:
+		if self.showup:
 			glDisable(GL_TEXTURE_2D)
 			glColor3f(0.0,0.0,1.0)
 			glBegin(GL_TRIANGLES)
@@ -174,3 +192,12 @@ class Sprite:
 			glVertex(5, 0, 0)
 			glEnd()
 		glPopMatrix()
+		if self.showhitbox:
+			glDisable(GL_TEXTURE_2D)
+			glColor3f(1.0,1.0,1.0)
+			glBegin(GL_LINE_LOOP)
+			glVertex(self.hitbox.left, self.hitbox.bottom, 0)
+			glVertex(self.hitbox.left, self.hitbox.top, 0)
+			glVertex(self.hitbox.right, self.hitbox.top, 0)
+			glVertex(self.hitbox.right, self.hitbox.bottom, 0)
+			glEnd()

diff --git a/Surface.py b/Surface.py
line changes: +89/-30
index c85402e..8869e23
--- a/Surface.py
+++ b/Surface.py
@@ -25,37 +25,43 @@ class Surface:
 		# detailed check
 		self.rect = pygame.Rect(p1,(self.dx,self.dy))
 		self.rect.normalize()
-		self.rect.width += 1
-		self.rect.height += 1
-		# Expand the collision rect for nearly horizontal or vertical
-		# surfaces
-		if abs(self.m_x) < 0.2:		# 5:1 ratio or more
-			self.rect.height += 2*self.fuzz
-			self.rect.top -= self.fuzz
-		elif abs(self.m_y) < 0.2:	# 1:5 ratio or less
-			self.rect.width += 2*self.fuzz
-			self.rect.left -= self.fuzz
-
-
-	def collide(self, point):
-		if self.rect.collidepoint(point[0],point[1]):
-			if self.m_x > -1.0 and self.m_x < 1.0:
-				if self.dx > 0:
-					if point[1] < self.m_x * point[0] + self.b_x:
-						return True
-				else:
-					if point[1] > self.m_x * point[0] + self.b_x:
-						return True
+		#self.rect.width += 1
+		#self.rect.height += 1
+
+
+	def lineSideTest(self, point):
+		if self.m_x > -1.0 and self.m_x < 1.0:
+			if self.dx > 0:
+				if point[1] < self.m_x * point[0] + self.b_x:
+					return True
+			else:
+				if point[1] > self.m_x * point[0] + self.b_x:
+					return True
+		else:
+			if self.dy > 0:
+				if point[0] > self.m_y * point[1] + self.b_y:
+					return True
 			else:
-				if self.dy > 0:
-					if point[0] > self.m_y * point[1] + self.b_y:
-						return True
-				else:
-					if point[0] < self.m_y * point[1] + self.b_y:
-						return True
+				if point[0] < self.m_y * point[1] + self.b_y:
+					return True
 		return False
 
 
+	def collidePoint(self, point):
+		if self.rect.collidepoint(point[0],point[1]):
+			return self.lineSideTest(point)
+
+
+	def collideRect(self, rect):
+		if not self.rect.colliderect(rect):
+			return False
+		#print self.collidePoint(rect.bottomleft), self.collidePoint(rect.bottomright), self.collidePoint(rect.topright), self.collidePoint(rect.topright)
+		return self.lineSideTest(rect.bottomleft) or \
+		       self.lineSideTest(rect.bottomright) or \
+		       self.lineSideTest(rect.topright) or \
+		       self.lineSideTest(rect.topright)
+
+
 	def draw(self):
 		angle = math.atan(self.m_x)
 		if self.dx < 0 or self.dy < 0:
@@ -63,8 +69,25 @@ class Surface:
 		else:
 			arrow = (angle+math.pi/2) % (2*math.pi)
 		anglepoint = (math.cos(arrow) * 10, math.sin(arrow) * 10)
+
 		glPushMatrix()
 		glDisable(GL_TEXTURE_2D)
+
+		if self.dx > 0:
+			py = self.rect.top	# rects are upside-down in our space
+		else:
+			py = self.rect.bottom - 1
+		if self.dy > 0:
+			px = self.rect.right - 1
+		else:
+			px = self.rect.left
+		glColor4f(1.0,0.0,0.0,0.25)
+		glBegin(GL_TRIANGLES)
+		glVertex3f(self.p2[0], self.p2[1], 0)
+		glVertex3f(self.p1[0], self.p1[1], 0)
+		glVertex3f(px, py, 0)
+		glEnd()
+
 		glColor3f(1.0,1.0,1.0)
 		glBegin(GL_LINES)
 		glVertex3f(self.p1[0],self.p1[1],0)
@@ -72,18 +95,44 @@ class Surface:
 		glVertex3f(self.rect.centerx, self.rect.centery, 0)
 		glVertex3f(self.rect.centerx + anglepoint[0], self.rect.centery + anglepoint[1], 0)
 		glEnd()
+
 		glPopMatrix()
 
 
 class Solid:
 	def __init__(self, p1, p2):
 		self.rect = pygame.Rect(p1,(p2[0]-p1[0],p2[1]-p1[1]))
+		self.rect.normalize()
 	
 
-	def collide(self, point):
+	def collidePoint(self, point):
 		return self.rect.collidepoint(point[0],point[1])
 
 
+	def collideRect(self, rect):
+		return bool(self.rect.colliderect(rect))
+
+
+	def draw(self):
+		glPushMatrix()
+		glDisable(GL_TEXTURE_2D)
+		glColor4f(1.0,0.0,0.0,0.25)
+		glBegin(GL_QUADS)
+		glVertex(self.rect.left, self.rect.bottom, 0)
+		glVertex(self.rect.left, self.rect.top, 0)
+		glVertex(self.rect.right, self.rect.top, 0)
+		glVertex(self.rect.right, self.rect.bottom, 0)
+		glEnd()
+		glColor3f(1.0,1.0,1.0)
+		glBegin(GL_LINE_LOOP)
+		glVertex(self.rect.left, self.rect.bottom, 0)
+		glVertex(self.rect.left, self.rect.top, 0)
+		glVertex(self.rect.right, self.rect.top, 0)
+		glVertex(self.rect.right, self.rect.bottom, 0)
+		glEnd()
+		glPopMatrix()
+
+
 class SurfaceSet:
 	surfaces = None
 
@@ -117,11 +166,21 @@ class SurfaceSet:
 		f.close()
 	
 
-	def collide(self,point):
+	def collidePoint(self,point):
 		colliders = filter(lambda x: x.rect.collidepoint(point), self.surfaces)
 		if not colliders:
 			return False
-		if filter(lambda x: not x, [c.collide(point) for c in colliders]):
+		if filter(lambda x: not x, [c.collidePoint(point) for c in colliders]):
+			return False
+		return True
+
+
+	def collideRect(self, rect):
+		colliders = filter(lambda x: x.rect.colliderect(rect), self.surfaces)
+		if not colliders:
+			return False
+		#print [(c,c.rect,c.collideRect(rect)) for c in colliders]
+		if filter(lambda x: not x, [c.collideRect(rect) for c in colliders]):
 			return False
 		return True
 

diff --git a/data/example1 b/data/example1
line changes: +3/-11
index 6e5646e..cb041dd
--- a/data/example1
+++ b/data/example1
@@ -20,18 +20,10 @@ tile 4 4
 tile 6 1
 tile 6 2
 
-solid 0 2 1 3
-surface 0 1 2 1
+solid 0 0 10 1
 surface 2 1 4 2
-solid   2 0 4 1
-surface 4 2 4 5
-surface 4 5 5 5
-surface 5 5 5 1
-surface 5 1 6 1
-surface 6 1 6 3
-surface 6 3 7 3
-surface 7 3 7 1
-surface 7 1 10 1
+solid 4 1 5 5
+solid 6 1 7 3
 
 tilesize 1 1
 texture img/ByteIco.png

diff --git a/platformtest.py b/platformtest.py
line changes: +86/-63
index dfc9669..a2c1d86
--- a/platformtest.py
+++ b/platformtest.py
@@ -4,6 +4,7 @@ import pygame
 from Sprite import *
 from Collage import *
 from Surface import *
+from Actor import *
 import Engine
 
 #resolution = (1280,1024)
@@ -15,100 +16,122 @@ Engine.init(resolution)
 pygame.display.set_caption("platformtest")
 
 ground = Collage('data/example1')
-ground.position = [0,0]
 surfaces = SurfaceSet('data/example1')
 stars = Collage('data/stars')
-stars.position = [0,0]
 candelabra = Sprite(['Sprites/candelabra_short/cand_s_1.png','Sprites/candelabra_short/cand_s_2.png','Sprites/candelabra_short/cand_s_3.png','Sprites/candelabra_short/cand_s_4.png'])
-candelabra.position = [64, 64]
+candelabra.setPosition(64, 64)
 candelabra.setScale(2.0)
 candelabra.setGravity(CENTER, BOTTOM)
 candelabra.setFramerate(10.0)
 candelabra.play()
 
-player_l = Sprite(['img/richter%d.png' % n for n in range(0,8)],
-		framerate=10.0, scale=2.0, gravity=(CENTER,BOTTOM))
-player_r = Sprite(['img/richter%d.png' % n for n in range(0,8)], mirror=True,
-		framerate=10.0, scale=2.0, gravity=(CENTER,BOTTOM))
+class Richter(Actor):
+	def __init__(self, surfaces=None):
+		Actor.__init__(self, surfaces)
 
+		self.defineState('walk_l',
+			Sprite(['img/richter%d.png' % n for n in range(0,8)],
+			framerate=10.0, scale=2.0, gravity=(CENTER,BOTTOM)))
+		self.defineState('walk_r',
+			Sprite(['img/richter%d.png' % n for n in range(0,8)], mirror=True,
+			framerate=10.0, scale=2.0, gravity=(CENTER,BOTTOM)))
+		self.defineState('idle_l',
+			Sprite("img/richter0.png", scale=2.0, gravity=(CENTER,BOTTOM)))
+		self.defineState('idle_r',
+			Sprite("img/richter0.png", mirror=True, scale=2.0, gravity=(CENTER,BOTTOM)))
+		self.setState('idle_r')
 
-def moveObject(pos, delta):
-	npos = list(pos)
-	npos[0] += delta[0]
-	npos[1] += delta[1]
-	if surfaces.collide(npos):
-		npos[1] += 1
-		if surfaces.collide(npos):
-			return pos
+		self.delta = [0,0]
+		self.jumping = False
+		self.grounded = self.collideDelta((0,-1))
+
+
+	def jump(self):
+		if not self.jumping and self.grounded:
+			self.delta[1] = 15.0
+			self.jumping = True
+			self.grounded = False
+
+
+	def jumpCancel(self):
+		self.jumping = False
+		if self.delta[1] > 0.0:
+			self.delta[1] = 0.0
+
+
+	def walkRight(self):
+		self.delta[0] = 2
+		self.setState('walk_r')
+
+
+	def walkLeft(self):
+		self.delta[0] = -2
+		self.setState('walk_l')
+
+
+	def idle(self):
+		if self.currentstate == 'walk_l':
+			self.setState('idle_l')
 		else:
-			return npos
-	else:   
-		return npos
+			self.setState('idle_r')
+		self.delta[0] = 0
+
+
+	def update(self):
+		if self.grounded:
+			self.delta[1] = 0
+		else:
+			self.delta[1] -= 0.5
+		# Climb up inclines (visually poetic, is it not?)
+		if self.grounded and self.delta[0] != 0:
+			delta = list(self.delta)
+			if self.collideDelta(delta):
+				for i in range(0,abs(delta[0])):
+					delta[1] += 1
+					if not self.collideDelta(delta):
+						self.delta = delta
+						break
+		self.move(self.delta)
+		self.grounded = self.collideDelta((0,-1))
+
+
+richter = Richter(surfaces)
+richter.setPosition(32, 64)
 
 
 class gamestate:
-	face = 1
-	direction = [0,0]
-	vgravity = 0
-	position = [32,64]
+	drawsurface = False
 
 
 def input(e):
 	if e.type == pygame.KEYDOWN:
 		if e.key == pygame.K_LEFT:
-			gamestate.direction[0] = -1
-			gamestate.face = 0
-			player_l.play()
+			richter.walkLeft()
 		elif e.key == pygame.K_RIGHT:
-			gamestate.direction[0] = 1
-			gamestate.face = 1
-			player_r.play()
-		elif e.key == pygame.K_DOWN:
-			gamestate.direction[1] = -1
+			richter.walkRight()
 		elif e.key == pygame.K_UP:
-			gamestate.direction[1] = 1
+			richter.jump()
+		elif e.key == pygame.K_1:
+			gamestate.drawsurface = not gamestate.drawsurface
 	elif e.type == pygame.KEYUP:
 		if e.key == pygame.K_LEFT or e.key == pygame.K_RIGHT:
-			gamestate.direction[0] = 0
-			player_l.stop()
-			player_l.reset()
-			player_r.stop()
-			player_r.reset()
+			richter.idle()
 		elif e.key == pygame.K_UP:
-			gamestate.direction[1] = 0
-			if gamestate.vgravity != 0.0:
-				gamestate.vgravity = 0.0
-		elif e.key == pygame.K_DOWN:
-			gamestate.direction[1] = 0
+			richter.jumpCancel()
 
 
 def update():
-	if gamestate.direction[1] == 1 and gamestate.vgravity == 0:
-		gamestate.vgravity = 20.0
-	gamestate.vgravity -= 0.5
-	npos = [gamestate.position[0], gamestate.position[1] + gamestate.vgravity]
-	if surfaces.collide(npos):
-		npos[1] -= 1
-		if surfaces.collide(npos):
-			gamestate.vgravity = 0
-	else:
-		gamestate.position = npos
-	# Move two pixels in whatever direction we're going, only horizontal
-	gamestate.position = moveObject(gamestate.position,(gamestate.direction[0]*2,0))
-	#ground.position[0] = gamestate.position[0] - resolution[0]/2
-	#stars.position[0] = int(ground.position[0] / 5.0)
-	#candelabra.position[0] -= ground.position[0] + 64
-	if gamestate.position[1] < 0:
-		gamestate.position = [gamestate.position[0],480]
+	richter.update()
+	if richter.position[1] < 0:
+		richter.position = [richter.position[0],480]
 
 def draw():
 	stars.draw()
 	ground.draw()
 	candelabra.draw()
-	if gamestate.face == 1:
-		player_r.drawAt(gamestate.position)
-	else:
-		player_l.drawAt(gamestate.position)
+	richter.draw()
+	if gamestate.drawsurface:
+		surfaces.draw()
 
 n = 0
 def fpsthing():
@@ -116,7 +139,7 @@ def fpsthing():
 	n += 1
 	if n == 100:
 		fps = "%0.1f FPS" % Engine.fpsman.get_fps()
-		pygame.display.set_caption("bouncetest [%s]" % fps)
+		pygame.display.set_caption("platformtest [%s]" % fps)
 		print fps
 		n = 0