Platformer Tutorial
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