main.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. -- Copyright (c) 2012-2025 Daniele Bartolini et al.
  2. -- SPDX-License-Identifier: MIT
  3. require "core/game/camera"
  4. Game = Game or {
  5. physics_world = nil,
  6. render_world = nil,
  7. scene_graph = nil,
  8. camera = nil,
  9. world_gui = nil,
  10. cursor_disabled = false,
  11. -- Player movement.
  12. character_unit = nil,
  13. gravity = 6*9.8,
  14. vertical_speed = 0,
  15. walk_speed = 8,
  16. run_multiplier = 1.8,
  17. -- Bullets management.
  18. max_bullets = 20,
  19. num_bullets = 0,
  20. bullets_head = 1,
  21. bullets_tail = 1,
  22. bullets = {},
  23. ages = {},
  24. -- Actionable door.
  25. door_open = false,
  26. -- Debug.
  27. debug_graphics = false,
  28. debug_physics = false,
  29. }
  30. GameBase.game = Game
  31. GameBase.game_level = "levels/mover"
  32. GameBase.show_help = true
  33. function Game.level_loaded()
  34. Game.physics_world = World.physics_world(GameBase.world)
  35. Game.render_world = World.render_world(GameBase.world)
  36. Game.scene_graph = World.scene_graph(GameBase.world)
  37. Game.camera = FPSCamera(GameBase.world, GameBase.camera_unit)
  38. Game.world_gui = World.create_world_gui(GameBase.world)
  39. -- Create character controller.
  40. Game.character_unit = World.unit_by_name(GameBase.world, "character")
  41. -- Link camera to mover.
  42. if Game.character_unit then
  43. local character_transform = SceneGraph.instance(Game.scene_graph, Game.character_unit)
  44. local camera_transform = SceneGraph.instance(Game.scene_graph, GameBase.camera_unit)
  45. if SceneGraph.parent(Game.scene_graph, camera_transform) == nil then
  46. SceneGraph.link(Game.scene_graph, character_transform, camera_transform)
  47. end
  48. end
  49. -- Create a pool of reusable bullets.
  50. for i=1, Game.max_bullets do
  51. local bullet_unit = World.spawn_unit(GameBase.world, "units/bullet/bullet", Vector3(0, 0, -100))
  52. local bullet_actor = PhysicsWorld.actor_instance(Game.physics_world, bullet_unit)
  53. -- Avoid overlapping bullets interaction.
  54. PhysicsWorld.actor_disable_gravity(Game.physics_world, bullet_actor)
  55. PhysicsWorld.actor_disable_collision(Game.physics_world, bullet_actor)
  56. Game.bullets[i] = bullet_unit
  57. Game.ages[i] = 0
  58. end
  59. -- Debug.
  60. PhysicsWorld.enable_debug_drawing(Game.physics_world, Game.debug_physics)
  61. RenderWorld.enable_debug_drawing(Game.render_world, Game.debug_graphics)
  62. end
  63. function Game.update(dt)
  64. -- Toggle cursor state and quit logic.
  65. if Keyboard.any_pressed() then
  66. if Keyboard.pressed(Keyboard.button_id("escape")) then
  67. if Game.cursor_disabled then
  68. Window.set_cursor_mode("normal")
  69. Game.cursor_disabled = false
  70. else
  71. Device.quit()
  72. end
  73. else
  74. Window.set_cursor_mode("disabled")
  75. Game.cursor_disabled = true
  76. end
  77. end
  78. local camera_world = Game.camera:world_pose()
  79. World.set_listener_pose(GameBase.world, camera_world)
  80. -- Recycle used bullets after they expire.
  81. local head = Game.bullets_head
  82. for i=1, Game.num_bullets do
  83. if Game.ages[head] < 4 then
  84. Game.ages[head] = Game.ages[head] + dt
  85. head = head % Game.max_bullets + 1
  86. else
  87. local bullet_unit = Game.bullets[Game.bullets_head]
  88. local bullet_actor = PhysicsWorld.actor_instance(Game.physics_world, bullet_unit)
  89. -- Avoid overlapping bullets interaction.
  90. PhysicsWorld.actor_disable_gravity(Game.physics_world, bullet_actor)
  91. PhysicsWorld.actor_disable_collision(Game.physics_world, bullet_actor)
  92. -- Bullets may have been recycled while still in motion.
  93. PhysicsWorld.actor_set_linear_velocity(Game.physics_world, bullet_actor, Vector3.zero())
  94. PhysicsWorld.actor_set_angular_velocity(Game.physics_world, bullet_actor, Vector3.zero())
  95. -- Teleport bullet somewhere player can't see it.
  96. PhysicsWorld.actor_teleport_world_position(Game.physics_world, bullet_actor, Vector3(0, 0, -100))
  97. Game.ages[Game.bullets_head] = 0
  98. Game.bullets_head = Game.bullets_head % Game.max_bullets + 1
  99. Game.num_bullets = Game.num_bullets - 1
  100. end
  101. end
  102. -- Shoot a bullet when left mouse button is pressed.
  103. if Mouse.pressed(Mouse.button_id("left")) then
  104. local tr = SceneGraph.instance(Game.scene_graph, Game.camera:unit())
  105. local pos = SceneGraph.world_position(Game.scene_graph, tr)
  106. local dir = Matrix4x4.y(SceneGraph.local_pose(Game.scene_graph, tr))
  107. Vector3.normalize(dir)
  108. if Game.num_bullets == Game.max_bullets then
  109. -- Empty magazine. Play a sad sound maybe.
  110. else
  111. local bullet_unit = Game.bullets[Game.bullets_tail]
  112. local bullet_actor = PhysicsWorld.actor_instance(Game.physics_world, bullet_unit)
  113. -- Gravity and collision is disabled when the bullet gets recycled. Re-enable both.
  114. PhysicsWorld.actor_enable_gravity(Game.physics_world, bullet_actor)
  115. PhysicsWorld.actor_enable_collision(Game.physics_world, bullet_actor)
  116. -- Move the bullet in front of player and shoot it forward.
  117. PhysicsWorld.actor_teleport_world_position(Game.physics_world, bullet_actor, pos + dir * 1.5)
  118. PhysicsWorld.actor_add_impulse(Game.physics_world, bullet_actor, dir * 50.0)
  119. World.play_sound(GameBase.world, "sfx/shoot", false, 0.6)
  120. Game.bullets_tail = Game.bullets_tail % Game.max_bullets + 1
  121. Game.num_bullets = Game.num_bullets + 1
  122. end
  123. end
  124. -- Actionable door.
  125. if Keyboard.pressed(Keyboard.button_id("e")) then
  126. local camera_transform = SceneGraph.instance(Game.scene_graph, Game.camera:unit())
  127. local camera_position = SceneGraph.world_position(Game.scene_graph, camera_transform)
  128. local camera_forward = Matrix4x4.y(SceneGraph.world_pose(Game.scene_graph, camera_transform))
  129. local hit, hit_pos, normal, time, unit, actor = PhysicsWorld.cast_ray(Game.physics_world, camera_position, camera_forward, 100)
  130. if hit then
  131. local door_button = World.unit_by_name(GameBase.world, "raycast_door_button")
  132. -- If we hit the door button and we are not too far, open/close the door.
  133. if unit == door_button and Vector3.distance(camera_position, hit_pos) < 10 then
  134. local door = World.unit_by_name(GameBase.world, "raycast_door")
  135. local door_transform = SceneGraph.instance(Game.scene_graph, door)
  136. local door_position = SceneGraph.local_position(Game.scene_graph, door_transform)
  137. local door_dx = 2.3
  138. local new_door_position = door_position + Vector3(Game.door_open and door_dx or -door_dx, 0, 0)
  139. SceneGraph.set_local_position(Game.scene_graph, door_transform, new_door_position)
  140. Game.door_open = not Game.door_open
  141. end
  142. end
  143. end
  144. local dx = Keyboard.button(Keyboard.button_id("d")) - Keyboard.button(Keyboard.button_id("a"))
  145. local dy = Keyboard.button(Keyboard.button_id("w")) - Keyboard.button(Keyboard.button_id("s"))
  146. if Game.character_unit then
  147. local mover = PhysicsWorld.mover_instance(Game.physics_world, Game.character_unit)
  148. -- Player mover.
  149. local coll_sides = PhysicsWorld.mover_collides_sides(Game.physics_world, mover)
  150. local coll_up = PhysicsWorld.mover_collides_up(Game.physics_world, mover)
  151. local coll_down = PhysicsWorld.mover_collides_down(Game.physics_world, mover)
  152. local camera_transform = SceneGraph.instance(Game.scene_graph, Game.camera:unit())
  153. local camera_forward = Matrix4x4.y(SceneGraph.local_pose(Game.scene_graph, camera_transform))
  154. local camera_right = Matrix4x4.x(SceneGraph.local_pose(Game.scene_graph, camera_transform))
  155. local delta = Vector3(camera_forward.x, camera_forward.y, 0) * dy
  156. + Vector3(camera_right.x, camera_right.y, 0) * dx
  157. if Vector3.length(delta) > 0.0001 then
  158. Vector3.normalize(delta)
  159. end
  160. local jump_pressed = Keyboard.pressed(Keyboard.button_id("space"))
  161. local run_pressed = Keyboard.button(Keyboard.button_id("shift_left"))
  162. local speed = Game.walk_speed
  163. if run_pressed ~= 0 then
  164. speed = speed * Game.run_multiplier
  165. end
  166. delta = delta * speed
  167. if jump_pressed and coll_down then
  168. Game.vertical_speed = Game.gravity * 0.25
  169. else
  170. if coll_down then
  171. Game.vertical_speed = -0.1
  172. else
  173. Game.vertical_speed = math.max(-50, Game.vertical_speed - Game.gravity * dt)
  174. end
  175. end
  176. delta.z = Game.vertical_speed
  177. PhysicsWorld.mover_move(Game.physics_world, mover, delta*(1/120))
  178. -- Copy mover position to character position.
  179. local mover_pos = PhysicsWorld.mover_position(Game.physics_world, mover)
  180. local character_transform = SceneGraph.instance(Game.scene_graph, Game.character_unit)
  181. assert(character_transform)
  182. SceneGraph.set_local_position(Game.scene_graph, character_transform, mover_pos)
  183. else
  184. Game.camera:move(dt, dx, dy)
  185. end
  186. -- Update camera.
  187. local cursor_delta = Mouse.axis(Mouse.axis_id("cursor_delta"))
  188. Game.camera:rotate(dt, cursor_delta.x, cursor_delta.y)
  189. -- Toggle help.
  190. if Keyboard.pressed(Keyboard.button_id("f1")) then
  191. GameBase.show_help = not GameBase.show_help
  192. end
  193. -- Draw 3D labels.
  194. local camera_view = Matrix4x4.y(camera_world)
  195. local camera_right = Matrix4x4.x(camera_world)
  196. for k, v in pairs({ label_slopes = "Slopes", label_moving_platforms = "Moving Platforms", label_trigger = "Trigger", label_raycast = "Raycast" }) do
  197. local label_unit = World.unit_by_name(GameBase.world, k)
  198. if label_unit then
  199. local label_transform = SceneGraph.instance(Game.scene_graph, label_unit)
  200. local label_position = SceneGraph.local_position(Game.scene_graph, label_transform)
  201. local font_resource = "core/game/hud/debug"
  202. local font_size = 1.5
  203. local label_extents = Gui.text_extents(Game.world_gui, font_size, v, font_resource)
  204. local label_pose = Matrix4x4.from_axes(camera_right
  205. , Vector3(0, 0, 1)
  206. , camera_view
  207. , label_position - camera_right * label_extents.x * 0.5
  208. )
  209. Gui.text_3d(Game.world_gui, label_pose, Vector3.zero(), font_size, v, font_resource)
  210. end
  211. end
  212. -- Draw crosshair.
  213. local win_w, win_h = Device.resolution()
  214. Gui.rect(GameBase.screen_gui, Vector2(win_w/2, win_h/2), Vector2(4, 4), Color4(255, 0, 255, 255))
  215. GameBase.draw_help({{ key = "f1", desc = "Toggle help" },
  216. { key = "w/a/s/d", desc = "Walk" },
  217. { key = "shift", desc = "Run" },
  218. { key = "space", desc = "Jump" },
  219. { key = "e", desc = "Interact" },
  220. { key = "left click", desc = "Shoot" },
  221. { key = "z", desc = "Toggle physics debug" },
  222. { key = "x", desc = "Toggle graphics debug" },
  223. { key = "esc", desc = "Quit" }}
  224. , "Crown Physics Sample"
  225. )
  226. -- Toggle debug drawing.
  227. if Keyboard.released(Keyboard.button_id("z")) then
  228. Game.debug_physics = not Game.debug_physics
  229. PhysicsWorld.enable_debug_drawing(Game.physics_world, Game.debug_physics)
  230. end
  231. if Keyboard.released(Keyboard.button_id("x")) then
  232. Game.debug_graphics = not Game.debug_graphics
  233. RenderWorld.enable_debug_drawing(Game.render_world, Game.debug_graphics)
  234. end
  235. end
  236. function Game.render(dt)
  237. end
  238. function Game.shutdown()
  239. end