Created
January 5, 2025 18:24
-
-
Save EncodeTheCode/937b1b1b27008569b45fc55ee8020ec8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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