# FPS-Style 3D Game with Sui Language # Demonstrates WASD movement, mouse look, jumping, sprinting, physics-based gameplay, # and procedurally generated voxel terrain fn main() -> int do # Enable antialiasing enable_msaa_4x() # Initialize graphics init_window(1280, 720, "Sui FPS Game - WASD to move, Mouse to look, SPACE to jump, SHIFT to sprint, F1 cursor, F2 hide FPS, F3 show FPS") defer close_window() # set_target_fps(60) # Show FPS meter show_fps_meter(Vec2 { x: 10.0, y: 10.0 }) # Hide cursor and capture mouse for FPS controls disable_cursor() # Initialize physics ode_init() defer ode_close() # Create physics world let world = ode_world_create() defer ode_world_destroy(world) let null_space = 0 as *() let space = ode_simple_space_create(null_space) defer ode_space_destroy(space) # Set gravity ode_world_set_gravity(world, 0.0, -9.81, 0.0) # Create contact joint group let contactgroup = ode_joint_group_create(0) defer ode_joint_group_destroy(contactgroup) # Create ground plane let ground_geom = ode_create_plane_geom(space, 0.0, 1.0, 0.0, 0.0) defer ode_geom_destroy(ground_geom) # Create player physics body (capsule shape) let player_body = ode_body_create(world) defer ode_body_destroy(player_body) ode_body_set_position(player_body, 0.0, 2.0, 0.0) # Create box geometry for player (0.8 x 1.6 x 0.8) let player_geom = ode_create_box_geom(space, 0.8, 1.6, 0.8) defer ode_geom_destroy(player_geom) ode_geom_set_body(player_geom, player_body) # Set player mass (box with density 1.0) ode_body_set_box_mass(player_body, 1.0, 0.8, 1.6, 0.8) # Create cube physics bodies let cube1_body = ode_body_create(world) defer ode_body_destroy(cube1_body) ode_body_set_position(cube1_body, 0.0, 1.0, -5.0) ode_body_set_box_mass(cube1_body, 1.0, 1.0, 1.0, 1.0) let cube1_geom = ode_create_box_geom(space, 1.0, 1.0, 1.0) defer ode_geom_destroy(cube1_geom) ode_geom_set_body(cube1_geom, cube1_body) let cube2_body = ode_body_create(world) defer ode_body_destroy(cube2_body) ode_body_set_position(cube2_body, 5.0, 1.0, -3.0) ode_body_set_box_mass(cube2_body, 1.0, 1.0, 1.0, 1.0) let cube2_geom = ode_create_box_geom(space, 1.0, 1.0, 1.0) defer ode_geom_destroy(cube2_geom) ode_geom_set_body(cube2_geom, cube2_body) let cube3_body = ode_body_create(world) defer ode_body_destroy(cube3_body) ode_body_set_position(cube3_body, -5.0, 1.0, -3.0) ode_body_set_box_mass(cube3_body, 1.0, 1.0, 1.0, 1.0) let cube3_geom = ode_create_box_geom(space, 1.0, 1.0, 1.0) defer ode_geom_destroy(cube3_geom) ode_geom_set_body(cube3_geom, cube3_body) # Set player movement direction (looking down -Z axis) let mut forward_x = 0.0 let mut forward_y = 0.0 let mut forward_z = -1.0 let mut right_x = 1.0 let mut right_y = 0.0 let mut right_z = 0.0 # ==== CAMERA AND PLAYER STATE ==== let mut camera_x = 0.0 let mut camera_y = 2.0 let mut camera_z = 0.0 let mut target_x = 0.0 let mut target_y = 2.0 let mut target_z = -5.0 # Mouse look state let mut yaw = 0.0 let mut pitch = 0.0 let mut mouse_sensitivity = 0.003 # FPS meter toggle let mut fps_meter_visible = 1 # Inventory system let mut inventory = ["Sword", "Shield", "Potion"] # Array of item names let mut inventory_open = false # Whether inventory UI is open # Cube position variables let mut cube1_x = 0.0 let mut cube1_y = 0.0 let mut cube1_z = 0.0 let mut cube2_x = 0.0 let mut cube2_y = 0.0 let mut cube2_z = 0.0 let mut cube3_x = 0.0 let mut cube3_y = 0.0 let mut cube3_z = 0.0 # Player position (initial values) let mut player_x = 0.0 let mut player_y = 2.0 let mut player_z = 0.0 # Main game loop while window_should_close() == false do # Get actual frame delta time let delta_time = get_frame_time() # Mouse look let mouse_delta_x = get_mouse_delta_x() let mouse_delta_y = get_mouse_delta_y() yaw = yaw + (mouse_delta_x * mouse_sensitivity) pitch = pitch - (mouse_delta_y * mouse_sensitivity) # Negate Y for natural FPS controls # Clamp pitch to prevent flipping if pitch > 1.5 do pitch = 1.5 end if pitch < -1.5 do pitch = -1.5 end # Calculate camera direction from yaw and pitch let cos_yaw = cos(yaw) let sin_yaw = sin(yaw) let cos_pitch = cos(pitch) let sin_pitch = sin(pitch) # Update forward and right vectors FIRST (before movement) forward_x = sin_yaw forward_y = sin_pitch forward_z = -cos_yaw right_x = cos_yaw right_y = 0.0 right_z = sin_yaw # WASD movement - set velocities on physics body let move_speed = 5.0 # Units per second let mut target_vx = 0.0 let mut target_vz = 0.0 if is_key_down(KEY_W) != 0 do target_vx = target_vx + forward_x * move_speed target_vz = target_vz + forward_z * move_speed end if is_key_down(KEY_S) != 0 do target_vx = target_vx - forward_x * move_speed target_vz = target_vz - forward_z * move_speed end if is_key_down(KEY_D) != 0 do target_vx = target_vx + right_x * move_speed target_vz = target_vz + right_z * move_speed end if is_key_down(KEY_A) != 0 do target_vx = target_vx - right_x * move_speed target_vz = target_vz - right_z * move_speed end # Get current velocity for Y (preserve gravity) let mut current_vx = 0.0 let mut current_vy = 0.0 let mut current_vz = 0.0 ode_body_get_linear_vel(player_body, ¤t_vx, ¤t_vy, ¤t_vz) # Jump - set upward velocity if is_jump_pressed() != 0 do # Get current position to check if on ground let mut dummy_x = 0.0 let mut current_y = 0.0 let mut dummy_z = 0.0 ode_body_get_position(player_body, &dummy_x, ¤t_y, &dummy_z) if current_y <= 1.3 do # On ground (box bottom is at y - 0.8 from center) current_vy = 8.0 # Jump velocity end end # Set new velocity (keep Y velocity for gravity/jumping) ode_body_set_linear_vel(player_body, target_vx, current_vy, target_vz) # Update physics (just for cubes) ode_space_collide(world, space, contactgroup) # Only step physics if delta_time is valid (guard against first frame which is 0) if delta_time > 0.0 do ode_world_step(world, delta_time) end ode_joint_group_empty(contactgroup) # Get player position from physics body ode_body_get_position(player_body, &player_x, &player_y, &player_z) # Adjust mouse sensitivity if is_key_pressed(KEY_EQUAL) != 0 or is_key_pressed(KEY_KP_ADD) != 0 do # + key mouse_sensitivity = mouse_sensitivity + 0.001 end if is_key_pressed(KEY_MINUS) != 0 or is_key_pressed(KEY_KP_SUBTRACT) != 0 do # - key mouse_sensitivity = mouse_sensitivity - 0.001 if mouse_sensitivity < 0.001 do mouse_sensitivity = 0.001 # Minimum sensitivity end end # Show cursor with F1 (useful for debugging) if is_key_pressed(KEY_F1) != 0 do enable_cursor() end # Toggle FPS meter with F2 (hide) and F3 (show) if is_key_pressed(KEY_F2) != 0 do hide_fps_meter() fps_meter_visible = 0 end if is_key_pressed(KEY_F3) != 0 do show_fps_meter(Vec2 { x: 10.0, y: 10.0 }) fps_meter_visible = 1 end # Toggle inventory with I key if is_key_pressed(KEY_I) != 0 do if inventory_open do inventory_open = false disable_cursor() # Hide cursor when inventory is closed end else do inventory_open = true enable_cursor() # Show cursor when inventory is open end end # Direction vectors are updated above # Camera follows player (at eye position, slightly in front) camera_x = player_x + forward_x * 0.5 # Offset forward camera_y = player_y + 0.9 # Eye height camera_z = player_z + forward_z * 0.5 # Offset forward # Camera looks where player is looking target_x = player_x + forward_x * 5.0 target_y = player_y + forward_y * 5.0 target_z = player_z + forward_z * 5.0 # Player rotation is handled visually during rendering # Get cube positions from physics bodies ode_body_get_position(cube1_body, &cube1_x, &cube1_y, &cube1_z) ode_body_get_position(cube2_body, &cube2_x, &cube2_y, &cube2_z) ode_body_get_position(cube3_body, &cube3_x, &cube3_y, &cube3_z) # ==== RENDERING ==== begin_drawing() clear_background(135, 206, 235, 255) # Sky blue # 3D mode with calculated camera begin_mode3d(camera_x, camera_y, camera_z, target_x, target_y, target_z, 0.0, 1.0, 0.0, 45.0, 0) # Draw cubes draw_cube(cube1_x, cube1_y, cube1_z, 1.0, 1.0, 1.0, 255, 0, 0, 255) # Red draw_cube_wires(cube1_x, cube1_y, cube1_z, 1.0, 1.0, 1.0, 0, 0, 0, 255) draw_cube(cube2_x, cube2_y, cube2_z, 1.0, 1.0, 1.0, 0, 255, 0, 255) # Green draw_cube_wires(cube2_x, cube2_y, cube2_z, 1.0, 1.0, 1.0, 0, 0, 0, 255) draw_cube(cube3_x, cube3_y, cube3_z, 1.0, 1.0, 1.0, 0, 0, 255, 255) # Blue draw_cube_wires(cube3_x, cube3_y, cube3_z, 1.0, 1.0, 1.0, 0, 0, 0, 255) # Draw player model with rotation (yellow cube to represent the player) rl_push_matrix() rl_translate_f(player_x, player_y, player_z) rl_rotate_f(-yaw * 180.0 / 3.14159, 0.0, 1.0, 0.0) # Convert radians to degrees for Y-axis rotation, negate for correct direction draw_cube(0.0, 0.0, 0.0, 0.8, 1.6, 0.8, 255, 255, 0, 255) # Yellow player draw_cube_wires(0.0, 0.0, 0.0, 0.8, 1.6, 0.8, 0, 0, 0, 255) rl_pop_matrix() end_mode3d() # 2D UI Rendering # Direction indicator (compass) draw_text("N", 1260, 20, 20, 255, 255, 255, 255) draw_line(1270, 40, 1270, 60, 255, 255, 255, 255) # North indicator line # Simple minimap (top-right corner) draw_rectangle(1100, 20, 150, 150, 0, 0, 0, 128) # Semi-transparent background draw_rectangle_lines(1100, 20, 150, 150, 255, 255, 255, 128) # Border # Player position on minimap (center) let minimap_center_x = 1175 let minimap_center_y = 95 draw_circle(minimap_center_x, minimap_center_y, 3.0, 255, 255, 0, 128) # Yellow dot for player # Inventory UI (when open) if inventory_open do # Semi-transparent background draw_rectangle(200, 100, 400, 300, 0, 0, 0, 200) # Title draw_text("Inventory", 220, 120, 24, 255, 255, 255, 255) # Inventory items (hardcoded for now) draw_text("Sword", 220, 160, 18, 200, 200, 200, 255) draw_text("Shield", 220, 190, 18, 200, 200, 200, 255) draw_text("Potion", 220, 220, 18, 200, 200, 200, 255) # Instructions draw_text("Press I to close", 220, 350, 16, 150, 150, 150, 255) end # HUD elements (always visible) # Health bar draw_rectangle(20, 680, 200, 20, 255, 0, 0, 128) # Red background draw_rectangle(20, 680, 150, 20, 0, 255, 0, 128) # Green fill (75% health) # Crosshair let screen_width = 1280 let screen_height = 720 let crosshair_x = screen_width / 2 let crosshair_y = screen_height / 2 draw_line(crosshair_x - 10, crosshair_y, crosshair_x + 10, crosshair_y, 255, 255, 255, 255) draw_line(crosshair_x, crosshair_y - 10, crosshair_x, crosshair_y + 10, 255, 255, 255, 255) end_drawing() end 0 end