Functions and Event Handling - Understanding the Code
Understand the code
Now let's analyze the code section by section in detail!
Lines 1-6: Game initialization
Description:
- Line 1: Import everything from the Ursina engine.
- Line 2:
osImport the module (to check environment variables). - Line 6:
Ursina(forced_aspect_ratio=.6)creates the app.forced_aspect_ratio=.6makes the aspect ratio tall (well suited for a bullet-hell shooter).
Lines 13-18: Set up the background, player, and bullet renderer
bg = Entity(model='quad', scale=(30, 50), texture='grass', color=hsv(0,0,.2))
player = Entity(model=Circle(3), color=color.azure, speed=8, y=-.4, z=-1)
player.bullet_renderer = Entity(model=Mesh(mode='point', thickness=.2), texture='circle', color=color.yellow)
scene.fog_density = (10,50)
ec = EditorCamera(rotation_x=-20)
Description:
- Line 13: Create the background.
quadis a rectangular plane. - Line 14: Create the player.
Circle(3): a circle with 3 vertices (a triangle)speed=8: adds a custom property (you can freely add properties to an Entity!)
- Line 15: Key point! Create the bullet renderer.
Mesh(mode='point'): a mesh drawn as a set of pointsthickness=.2: the thickness of the points
- Line 17: Set up the fog effect.
- Line 18: Set the camera to look down from slightly above.
The mode option of Mesh
'point': draws each vertex as a point (good for bullets)'line': connects the vertices with lines'triangle': fills the vertices into triangles
Lines 21-25: Fire function and sequence
def shoot():
player.bullet_renderer.model.vertices.append(player.position)
shoot_cooldown = .1
shoot_sequence = Sequence(Func(shoot), Wait(shoot_cooldown), loop=True)
Description:
- Lines 21-22:
shoot()Define the function.player.bullet_renderer.model.vertices: a list that stores the bullet coordinatesappend(player.position): add the current player position to the list
- Line 24: Set the firing interval to 0.1 seconds.
- Line 25: Key point! Create the sequence.
Func(shoot): run the shoot functionWait(shoot_cooldown): wait 0.1 secondsloop=True: loop forever
Lines 28-50: Main update function
def update():
move_direction = Vec2(held_keys['d']-held_keys['a'], held_keys['w']-held_keys['s']).normalized()
player.position += move_direction * player.speed * time.dt
bg.texture_offset += Vec2(0, time.dt)
Description:
- Line 29: Compute the movement direction.
held_keys['d']-held_keys['a']: 1 if D is pressed, -1 if A is pressed.normalized(): normalize so diagonal movement isn't faster
- Line 30: Move the player.
player.speed * time.dt: speed ร time = distance moved
- Line 31: Scroll the background texture to give a sense of motion.
for i, bullet in enumerate(player.bullet_renderer.model.vertices):
player.bullet_renderer.model.vertices[i] += Vec3(0, time.dt * 10, 0)
for enemy in enemies:
if distance_2d(bullet, enemy) < .5:
enemy.hp -= 1
enemy.blink(color.white)
if enemy.hp <= 0:
enemies.remove(enemy)
destroy(enemy)
player.bullet_renderer.model.vertices.remove(bullet)
Description:
- Lines 33-34: Move all bullets upward.
- Lines 35-43: Check each bullet for collisions with enemies.
distance_2d(): a function that computes 2D distanceenemy.blink(): flash white when an enemy is hitdestroy(): remove the object entirely
if len(player.bullet_renderer.model.vertices):
player.bullet_renderer.model.vertices = player.bullet_renderer.model.vertices[-100:]
player.bullet_renderer.model.generate()
Description:
- Lines 47-48: If there are too many bullets, keep only the most recent 100.
- Line 50: Required!
generate()must be called for the changes to show on screen.
Lines 53-57: Input handling
def input(key):
if key == 'space':
shoot_sequence.start()
if key == 'space up':
shoot_sequence.paused = True
Description:
- Lines 54-55: Pressing the spacebar starts the firing sequence.
- Lines 56-57: Releasing the spacebar pauses the sequence.
'space up': the event for when a key is released
Lines 62-73: Enemy setup and update
enemies = []
enemy = Entity(model=Circle(3), rotation_z=180, position=(0, 16), color=color.red, z=-1, speed=3, hp=5)
enemies.append(enemy)
def enemy_update():
for e in enemies:
e.position += e.up * enemy.speed * time.dt
enemy_handler = Entity(update=enemy_update)
Description:
- Lines 62-64: Create the enemy list and add enemies.
rotation_z=180: rotate to face downwardhp=5: add a health property
- Lines 66-68: The function that moves the enemies.
e.up: the enemy's up direction (since rotation_z=180, it moves downward)
- Line 72: Tip!
Entity(update=ํจ์)lets you set an update callback.