FPS Shooter

First-person shooter demo with enemies and health

Official
Pokepetter
By Pokepetter

How it works

Code

from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.shaders import lit_with_shadows_shader

app = Ursina()

random.seed(0)
Entity.default_shader = lit_with_shadows_shader

ground = Entity(
    model='plane', collider='box', scale=64,
    texture='grass', texture_scale=(4,4)
)

editor_camera = EditorCamera(enabled=False, ignore_paused=True)
player = FirstPersonController(
    model='cube', z=-10, color=color.orange,
    origin_y=-.5, speed=8, collider='box'
)
player.collider = BoxCollider(player, Vec3(0,1,0), Vec3(1,2,1))

gun = Entity(
    model='cube', parent=camera,
    position=(.5,-.25,.25), scale=(.3,.2,1),
    origin_z=-.5, color=color.red,
    on_cooldown=False
)
gun.muzzle_flash = Entity(
    parent=gun, z=1, world_scale=.5,
    model='quad', color=color.yellow,
    enabled=False
)

shootables_parent = Entity()
mouse.traverse_target = shootables_parent

# spawn random cubes as enemies
for i in range(16):
    Entity(
        model='cube', origin_y=-.5, scale=2,
        texture='brick', texture_scale=(1,2),
        x=random.uniform(-8,8),
        z=random.uniform(-8,8) + 8,
        collider='box',
        scale_y=random.uniform(2,3),
        color=color.hsv(0, 0, random.uniform(.9, 1))
    )

def update():
    if held_keys['left mouse']:
        shoot()

def shoot():
    if not gun.on_cooldown:
        gun.on_cooldown = True
        gun.muzzle_flash.enabled = True
        from ursina.prefabs.ursfx import ursfx
        ursfx(
            [(0.0,0.0),(0.1,0.9),(0.15,0.75),
             (0.3,0.14),(0.6,0.0)],
            volume=0.5, wave='noise',
            pitch=random.uniform(-13,-12),
            pitch_change=-12, speed=3.0
        )
        invoke(gun.muzzle_flash.disable, delay=.05)
        invoke(setattr, gun, 'on_cooldown', False, delay=.15)

        hit = mouse.hovered_entity
        if hit and hasattr(hit, 'hp'):
            hit.hp -= 10
            hit.blink(color.red)


from ursina.prefabs.health_bar import HealthBar

class Enemy(Entity):
    def __init__(self, **kwargs):
        super().__init__(
            parent=shootables_parent, model='cube',
            scale_y=2, origin_y=-.5, color=color.light_gray,
            collider='box', **kwargs
        )
        self.health_bar = Entity(
            parent=self, y=1.2, model='cube',
            color=color.red, world_scale=(1.5,.1,.1)
        )
        self.max_hp = 100
        self.hp = self.max_hp

    def update(self):
        dist = distance_xz(player.position, self.position)
        if dist > 40:
            return

        self.health_bar.alpha = max(0, self.health_bar.alpha - time.dt)
        self.look_at_2d(player.position, 'y')
        hit_info = raycast(
            self.world_position + Vec3(0,1,0),
            self.forward, 30, ignore=(self,)
        )
        if hit_info.entity == player and dist > 2:
            self.position += self.forward * time.dt * 5

    @property
    def hp(self):
        return self._hp

    @hp.setter
    def hp(self, value):
        self._hp = value
        if value <= 0:
            destroy(self)
            return
        self.health_bar.world_scale_x = (self.hp / self.max_hp) * 1.5
        self.health_bar.alpha = 1

# create four enemies in a line
enemies = [Enemy(x=x*4) for x in range(4)]

def pause_input(key):
    if key == 'tab':
        editor_camera.enabled = not editor_camera.enabled
        player.visible_self = editor_camera.enabled
        player.cursor.enabled = not editor_camera.enabled
        gun.enabled = not editor_camera.enabled
        mouse.locked = not editor_camera.enabled
        editor_camera.position = player.position
        application.paused = editor_camera.enabled

pause_handler = Entity(ignore_paused=True, input=pause_input)

sun = DirectionalLight()
sun.look_at(Vec3(1,-1,-1))
Sky()

app.run()

View source on GitHub

Back to Ursina Originals