Skip to content

Instantly share code, notes, and snippets.

@EncodeTheCode
Created January 5, 2025 18:24
Show Gist options
  • Select an option

  • Save EncodeTheCode/937b1b1b27008569b45fc55ee8020ec8 to your computer and use it in GitHub Desktop.

Select an option

Save EncodeTheCode/937b1b1b27008569b45fc55ee8020ec8 to your computer and use it in GitHub Desktop.
from ursina import *
from time import time as time_lib
# Start Ursina app
app = Ursina()
# Set FPS limit to 60
application.fps_limit = 60 # Limit the FPS to 60
# Set up FPS camera
camera.position = (0, 1.6, 5) # Set camera height (1.6 meters is a typical first-person height)
camera.rotation_x = -10
camera.fov = 90 # Field of view
# Player movement speed
player_speed = 5
# Ammo system
max_magazine = 15 # Maximum ammo in the magazine
spare_ammo = 120 # Ammo available in reserve
current_ammo = max_magazine # Starting with a full magazine
is_reloading = False
reload_time = 5 # Reload time in seconds
reload_start_time = 0 # Time when reloading started
reload_text = Text(text="", position=(0, 0), scale=2, color=color.white)
# Projectile Settings
projectile_speed = 25 # Speed of the projectile in meters per second
projectile_lifetime = 30 # Lifetime of the projectile in seconds
max_distance = 50 # Maximum distance the projectile can travel before being removed
# Floor plane settings
floor_size = 10 # 10 meters by 10 meters
floor_height = 0 # The floor's Y position
# Custom projectile class to handle movement and lifetime
class Projectile(Entity):
def __init__(self, position, direction):
super().__init__(
model='sphere',
color=color.green,
scale=0.2,
position=position,
collider='sphere',
always_on_top=True
)
self.direction = direction
self.start_position = position
self.lifetime = 0
self.max_distance = max_distance
def update(self):
# Move the projectile in the direction it's facing
self.position += self.direction * projectile_speed * time.dt # Correct use of `time.dt`
self.lifetime += time.dt # Track lifetime
# Calculate the distance traveled from the start position
distance_travelled = (self.position - self.start_position).length()
# If the projectile exceeds the max distance or lifetime, disable it
if distance_travelled > self.max_distance or self.lifetime > projectile_lifetime:
self.disable() # Disable the projectile
# Create a projectile when the player fires
def create_projectile():
global current_ammo
if current_ammo > 0: # Only shoot if there is ammo in the magazine
projectile = Projectile(camera.position + camera.forward, camera.forward)
projectiles.append(projectile) # Add to the list of projectiles
current_ammo -= 1 # Decrease ammo after shooting
else:
print("Out of ammo, reload!") # Optional: Notify when out of ammo
# Store projectiles in a list for management
projectiles = []
# Set the rate of fire (time between shots)
rate_of_fire = 0.5 # seconds
last_shot_time = 0
# Reload function
def reload():
global current_ammo, spare_ammo, is_reloading, reload_start_time
if not is_reloading and current_ammo < max_magazine and spare_ammo > 0:
is_reloading = True
reload_start_time = time_lib()
reload_text.text = "Reloading..." # Show reloading text
# Update function for handling player input and shooting
def update():
global last_shot_time, reload_start_time, is_reloading, current_ammo, spare_ammo
# Player movement controls (WASD)
if held_keys['w']:
camera.position += camera.forward * player_speed * time.dt
if held_keys['s']:
camera.position -= camera.forward * player_speed * time.dt
if held_keys['a']:
camera.position -= camera.right * player_speed * time.dt
if held_keys['d']:
camera.position += camera.right * player_speed * time.dt
# Mouse look controls (for FPS movement)
camera.rotation_y -= mouse.delta[0] * 0.1
camera.rotation_x -= mouse.delta[1] * 0.1
camera.rotation_x = clamp(camera.rotation_x, -80, 80) # Limit vertical camera rotation
# Shooting (left mouse button) - prevent shooting while reloading
if mouse.left and time_lib() - last_shot_time > rate_of_fire and not is_reloading:
create_projectile()
last_shot_time = time_lib()
# Reloading system
if held_keys['r'] and not is_reloading:
reload()
if is_reloading:
# Check if reload time is complete
if time_lib() - reload_start_time >= reload_time:
# Perform the reload
ammo_needed = max_magazine - current_ammo
ammo_to_reload = min(spare_ammo, ammo_needed)
current_ammo += ammo_to_reload
spare_ammo -= ammo_to_reload
is_reloading = False
reload_text.text = "" # Hide the reloading text after completion
# Update projectiles
for projectile in projectiles:
projectile.update()
# Handle player falling into the abyss (below the floor)
if camera.position.y < floor_height - 5: # Check if player is below the floor by more than 5 meters (i.e., into the abyss)
respawn_player()
# Toggle cursor lock/unlock with the ESC key
if held_keys['escape']:
window.cursor_lock = False # Unlock the mouse cursor
else:
window.cursor_lock = True # Lock the mouse cursor to the window
# Handle respawn logic
def respawn_player():
camera.position = (0, 1.6, 5) # Respawn the player at the default spawn position
print("You died and respawned!")
# Create floor plane
floor = Entity(
model='plane',
color=color.green,
scale=(floor_size, 1, floor_size), # 10x10 meters plane
position=(0, floor_height, 0), # Position the floor at height 0
collider='box' # Enable collision detection with the floor
)
# Enable mouse capture initially (locking the mouse cursor inside the window)
window.cursor_lock = True
# Run the application
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment