Skip to content

Platformer Tutorial

alexrdp90 edited this page Apr 14, 2013 · 4 revisions

In this example, we are going to work on our first PyNgine game: a simple 3D platformer, built enterely from scratch. It is only needed one script which we will name platformer.py.

First of all, we must write the import statements for using PyNgine. The annotation # @UnusedWildImport is optional, in case you don't want to see any warning in your editor.

from pyngine import * # @UnusedWildImport
from threading import Timer

Then we can start declaring the component for the movement of the player. In the start method, we initialize the fields we are going to

class PlayerMovement(Component):
	def start(self):
		self.movementspeed = 10
		self.rotationspeed = 5
		self.jumpforce = 200
		self.canjump = False

In the update method, we add all the logic that is going to be executed each frame. In this scenario, the methods Input.getverticalaxis and Input.gethorizontalaxis check what arrow/WASD keys are currently pressed. With Input.getkey we do almost the same, but passing the key as a parameter. The transform field is a component which all gameobjects have, and contains the values of the position, rotation and scale. For this example, we are going to use also its methods Transform.translate and Transform.rotate.

class PlayerMovement(Component):
	# (continues...)
	def update(self):
		transform = self.transform
		translatevalue = Input.getverticalaxis() * self.movementspeed * Game.delta
		rotationvalue = Input.gethorizontalaxis() * self.rotationspeed * Game.delta
		transform.translate(transform.forward * translatevalue)
		transform.rotate(Vector3D.up, rotationvalue)
		if Input.getkey(pygame.K_SPACE) and self.canjump:
			self.canjump = False
			self.rigidbody.addforce((0,self.jumpforce,0))
	def oncollision(self, other):
		self.canjump = True

In a similar way to previous component, we are going to add a shooting feature. The method newbullethandler and the field canshoot is just a way of preventing the player to create too many bullets, using the Timer class from the high-level threading module.

class Shooter(Component):
    def start(self):
        self.shootforce = 100
        self.canshoot = False
        self.newbullethandler()
    def newbullethandler(self):
        self.canshoot = True
        self.timer = Timer(1., self.newbullethandler)
        self.timer.start()
    def update(self):
        if Input.getkey(pygame.K_q) and self.canshoot:
            self.canshoot = False
            position = self.transform.position + self.transform.forward
            bullet = GameObject(Transform(position, scale=(.5,.5,.5)),
                                Sphere(Color.green), SphereCollider(), Rigidbody(1))
            bullet.tag = 'Bullet'
            bullet.rigidbody.addforce(self.transform.forward * self.shootforce)

class EnemyBehaviour(Component):
    def oncollision(self, other):
        if other.tag == 'Bullet':
            GameObject.destroy(self.gameobject)

Next, we declare two classes for a pair of GameObject subclasses for instantiating two kind of objects we are going to use more than once.

class Platform(GameObject):
    def __init__(self, pos, size):
        GameObject.__init__(self, Transform(position=pos, scale=size),
                            Cube(color=Color.white), BoxCollider())


class Enemy(GameObject):
    def __init__(self, pos):
        GameObject.__init__(self, Transform(position=pos), Rigidbody(1),
                            Cube(color=Color.red), BoxCollider(), EnemyBehaviour())

Note that Platform instances don't have a Rigidbody component attached, while enemies do so. This is because we want platforms prevent other objects from falling down, rather than moving them when they collide with anything.

Finally, we write the code for our Game subclass, called Platformer, in which all the previous classes are used. There are other new components such as Light and Camera, but you don't have to worry about them.

class Platformer(Game):
    def __init__(self):
        Game.__init__(self)
        GameObject(Transform(position=(0,5,-5)), Light())
        Platform(pos=(0, 0 ,0), size=(20, 1, 20))
        Platform(pos=(11.5, 2, 0), size=(10, 1, 5))
        Platform(pos=(-12, 2, 0), size=(10, 1, 5))
        GameObject(Transform((0, 7, 0)), Cube(color=Color.green),
                   BoxCollider(), Rigidbody(1), Camera((0, 2, 20)),
                   PlayerMovement(), Shooter())
        enemypositions = [(3, 3, 3), (-3, 3, -1), (-8, 5, 0), (7, 5, 1)]
        for position in enemypositions:
            Enemy(pos=position)


if __name__ == "__main__":
    p = Platformer()
    p.mainloop()

And it's done! Just run $ python platformer.py to check that your game works nicely. Congrats!

If you have any questions, feel free to ask: alexrdp90 [at] gmail [dot] com