Skip to content

Functions and Event Handling - Understanding the Code

โ† Core concepts โฑ๏ธ 20 min Quiz โ†’

Understand the code

Now let's analyze the code section by section in detail!

Lines 1-6: Game initialization

from ursina import *
import os

app = Ursina(forced_aspect_ratio=.6)

Description:

  • Line 1: Import everything from the Ursina engine.
  • Line 2: os Import the module (to check environment variables).
  • Line 6: Ursina(forced_aspect_ratio=.6) creates the app.
    • forced_aspect_ratio=.6 makes 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. quad is 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 points
    • thickness=.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 coordinates
    • append(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 function
    • Wait(shoot_cooldown): wait 0.1 seconds
    • loop=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 distance
    • enemy.blink(): flash white when an enemy is hit
    • destroy(): 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 downward
    • hp=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.