Title: 'Vector Bob' Sprite/Particle Example

Author: Diez B. Roggisch (deets at web.de)
Submission date: September 17, 2002

Description: A well known old demo effect. A set of 3d points is transformed and every point is blitted as a surface.

Download: vector_bob.tgz

pygame version required: Any with Surfarray
SDL version required: Any
Python version required: 2.2 with Numeric

Comments: This is a vector-driven particle toolkit, where each particle is a sprite, rather than a pixel. The example script allows you to see three basic data sets - choose between them with the '1', '2' and '3' keys (on the main keyboard, not the numeric keypad.

This code, while not very thoroughly-commented, is concise and well-structured, and just about as fast as it can be. Consider using derivitives of this code for explosion or weapons effects, or for enemy movement routines in Galaxian-style games.

Messages: 2


"""
Name	:	vector bob
Desc	:

A well known old demo effect. A set of 3d points is transformed and every point is blitted as
a surface.
"""

try:
	import sys, types, pygame, pygame.image, pygame.display, pygame.rect, pygame.key #, log4py
	import profile
	from Numeric import *
	#from collision import *
	
except ImportError, e:
	print "import exception occured"
	print e
	sys.exit()


MODE = (640, 480)
BACKGROUND = None
BALL = None

class BobObject:
	""" A BobObject. The list _.coords are the coordinates of the points to be blitted """
	def __init__(_, surface, scadjust):
		""" surface - the points are blitted as that
		scadjust - a tuple representing the screen (width/2, height/2) to center the transformed coordinates
		"""		
		_.surface = surface
		_.coords = []
		_.scadjust = scadjust - array(surface.get_rect().size) / 2
		_.position = array((0,0,0))
		_.reset()
		_.colObs = []
		
	def translate(_, trans):
		_.position += trans

	def reset(_):
		_.orientation = array(identity(3), Float)
		
	def rotateZ(_, deg):
		rMat = array(identity(3), Float)
		cos = math.cos(deg)
		sin = math.sin(deg)
		rMat[0][0] = cos
		rMat[0][1] = -sin
		rMat[1][0] = sin
		rMat[1][1] = cos
		_.orientation = matrixmultiply(_.orientation, rMat)

	def rotateY(_, deg):
		rMat = array(identity(3), Float)
		cos = math.cos(deg)
		sin = math.sin(deg)
		rMat[0][0] = cos
		rMat[0][2] = -sin
		rMat[2][0] = sin
		rMat[2][2] = cos
		_.orientation = matrixmultiply(_.orientation, rMat)
		_.rot = deg

	def rotateX(_, deg):
		rMat = array(identity(3), Float)
		cos = math.cos(deg)
		sin = math.sin(deg)
		rMat[1][1] = cos
		rMat[1][2] = -sin
		rMat[2][1] = sin
		rMat[2][2] = cos
		_.orientation = matrixmultiply(_.orientation, rMat)

	def screenCoords(_):
		sc = []
		_.colObs = []
		for c in _.coords:
			p = matrixmultiply(c , _.orientation) + _.position
			sc.append((p[2], p[0] * 1000 / p[2] + _.scadjust[0], p[1] * 1000 / p[2] + _.scadjust[1]))

		sc.sort()
		sc.reverse()
		return map(lambda x: x[1:], sc)

	def getBobs(_):
		return map(lambda x, b=_.surface: (x, b), _.screenCoords())

		

class Cube(BobObject):

	def __init__(_, dim, surface, scad):
		def char(a):
			if a:
				return 1
			return -1
		BobObject.__init__(_, surface, scad)
		for i in xrange(8):
			_.coords.append((char(i & 1) * dim, char(i & 2) * dim, char(i & 4) * dim))

class Ring(BobObject):
	def __init__(_, dim, surface, scad, segments = 8):
		BobObject.__init__(_, surface, scad)
		for i in xrange(segments):
			x = dim * cos(math.pi * i / segments * 2)
			y = dim * sin(math.pi * i / segments * 2)
			_.coords.append((x, y, 0))


class Sphere(BobObject):
	def __init__(_, dim, surface, scad, segments = 8):
		BobObject.__init__(_, surface, scad)
		nums = [(1, 0)]
		for n in xrange(1, segments >> 1):
			deg = n / float(segments >> 1) * math.pi / 2
			radius = dim * cos(deg)
			offset = dim * sin(deg)
			num = floor(segments * cos(deg))
			if int(num) & 1:
				num += 1
			for i in xrange(num):
				x = radius * cos(math.pi * i / num * 2)
				y = radius * sin(math.pi * i / num * 2)
				_.coords.append((x, y, offset))
				_.coords.append((x, y, -offset))

		for i in xrange(segments):
			x = dim * cos(math.pi * i / segments * 2)
			y = dim * sin(math.pi * i / segments * 2)
			_.coords.append((x, y, 0))
		_.coords.append((0, 0, -dim))
		_.coords.append((0, 0, dim))
				 
class DisplayList:
	def __init__(_):
		_.saveStack = []
		_.bobs = []
		_.display = pygame.display.get_surface()
		
	def addBob(_, bob):
		# Create the rect in the display which has to be saved
		rect = bob[1].get_rect().move(bob[0])
		bgSave = pygame.Surface(rect[2:])
		bgSave.blit(_.display, (0,0), rect)
		_.saveStack.append((bob[0], bgSave, rect))
		_.bobs.append(bob)

	def blitBobs(_):
		rectList = []
		for bob in _.bobs:
			rectList.append(bob[1].get_rect().move(bob[0]))
			_.display.blit(bob[1], bob[0])
		_.bobs = []
		return rectList
	
	def restoreBackground(_):
		rectList = []
		count = 0
		y = 48
		while len(_.saveStack):
			resBob = _.saveStack.pop()			
			rectList.append(resBob[2])			
			_.display.blit(resBob[1], resBob[0])
			count += 1
		return rectList




def main():
	global DISPLAY
	global SCREENRECT
	global LEVEL
	global DIST_DEC
	DIST_DEC = 1
	pygame.init()
	pygame.display.init()

	rect = (0, 0) + MODE
	DISPLAY = pygame.display.set_mode(MODE, 0)
	BACKGROUND = pygame.image.load("back.png").convert()
	BALL = pygame.image.load("ball.png").convert()
	BALL.set_colorkey(BALL.get_at((0,0)))
	
	DISPLAY.blit(BACKGROUND, (0, 0), BACKGROUND.get_rect())

	dl = DisplayList()
	sc = array(BACKGROUND.get_rect()[2:])*0.5

	vBob = Sphere(1000, BALL, sc)
	vBob.translate(array((0, 0, 10009)))

	pygame.display.update()
	oldts = pygame.time.get_ticks() -100
	switchTs = oldts
	showBBox = 0
	speed = 50.0
	delay = 200
	at = 0
	r = 1.0
	while 1==1:
		ts = pygame.time.get_ticks()
		elapsed = ts - oldts
		oldts = ts
		pygame.event.pump()		
		keystate = pygame.key.get_pressed()
		if keystate[pygame.K_ESCAPE]:
			break

		if keystate[pygame.K_1]:
			vBob = Cube(1000 / 1.5, BALL, sc)
			vBob.translate(array((0, 0, 10009)))

		if keystate[pygame.K_2]:
			vBob = Sphere(1000, BALL, sc)
			vBob.translate(array((0, 0, 10009)))

		if keystate[pygame.K_3]:
			vBob = Ring(1000, BALL, sc)
			vBob.translate(array((0, 0, 10009)))

		invalidRects = dl.restoreBackground()

		vBob.reset()
		r = r + speed * elapsed / 1000
		vBob.rotateX((r / 180.0) * math.pi)
		vBob.rotateY((r / 180.0) * math.pi)

		bobs = vBob.getBobs()
		for b in bobs:
			dl.addBob(b)
		invalidRects += dl.blitBobs()
		pygame.display.update(invalidRects)		
		pygame.time.wait(50)

if __name__ == "__main__":
	main()
	#profile.run('main()')


From: Korruptor

Date: September 23, 2002 21:53 GMT

Very nice! :-)

 

From: krister

Date: January 12, 2003 19:07 GMT

The tar-ball has import log4py in it, that is not needed. It is commented out in the code on he webpage.

 

Main - Repository - Submit - News

Feedback