main.lua 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. local cam = require'cam'
  2. local player_pos = Vec3()
  3. local player_vel = Vec3(0, 0, -0.01)
  4. -- next three functions convert mouse coordinate from screen to the 3D position on the ground plane
  5. local function getWorldFromScreen(pass)
  6. local w, h = pass:getDimensions()
  7. local clip_from_screen = mat4(-1, -1, 0):scale(2 / w, 2 / h, 1)
  8. local view_pose = mat4(pass:getViewPose(1))
  9. local view_proj = pass:getProjection(1, mat4())
  10. return view_pose:mul(view_proj:invert()):mul(clip_from_screen)
  11. end
  12. local function getRay(world_from_screen, distance)
  13. local NEAR_PLANE = 0.01
  14. distance = distance or 1e3
  15. local ray = {}
  16. local x, y = lovr.system.getMousePosition()
  17. ray.origin = vec3(world_from_screen:mul(x, y, NEAR_PLANE / NEAR_PLANE))
  18. ray.target = vec3(world_from_screen:mul(x, y, NEAR_PLANE / distance))
  19. return ray
  20. end
  21. local function mouseOnGround(ray)
  22. if ray.origin:distance(ray.target) < 1e-2 then
  23. return vec3(0, 0, 0)
  24. end
  25. local ray_direction = (ray.target - ray.origin):normalize()
  26. -- intersect the ray onto ground plane
  27. local plane_direction = vec3(0, 1, 0)
  28. local dot = ray_direction:dot(plane_direction)
  29. if dot == 0 then
  30. return vec3(0, 0, 0)
  31. end
  32. local ray_length = (-ray.origin):dot(plane_direction) / dot
  33. local hit_spot = ray.origin + ray_direction * ray_length
  34. return hit_spot
  35. end
  36. local crumbs = {}
  37. local crumbs_eaten = 0
  38. local crumbs_txt = 'Find\nsome\ncrumbs'
  39. for i = 1, 10 do
  40. table.insert(crumbs, Vec2(-0.5 + lovr.math.random(), -0.5 + lovr.math.random()):mul(20))
  41. end
  42. function lovr.draw(pass)
  43. lovr.graphics.setBackgroundColor(0, 0, 0)
  44. -- player control
  45. local dt = lovr.timer.getDelta()
  46. if lovr.system.isMouseDown(2) then
  47. local world_from_screen = getWorldFromScreen(pass)
  48. local ray = getRay(world_from_screen)
  49. local spot = mouseOnGround(ray)
  50. local mouse_dir = spot - player_pos
  51. if #mouse_dir > 15 then
  52. mouse_dir:normalize():mul(15)
  53. end
  54. player_vel:add(mouse_dir * dt)
  55. end
  56. -- arrows with relative camera
  57. local arrows_dir = vec3(0, 0, 0)
  58. if lovr.system.isKeyDown('up') then
  59. arrows_dir:add(0, 0, -1)
  60. elseif lovr.system.isKeyDown('down') then
  61. arrows_dir:add(0, 0, 1)
  62. end
  63. if lovr.system.isKeyDown('left') then
  64. arrows_dir:add(-1, 0, 0)
  65. elseif lovr.system.isKeyDown('right') then
  66. arrows_dir:add(1, 0, 0)
  67. end
  68. arrows_dir = quat(cam.pose) * arrows_dir
  69. arrows_dir.y = 0
  70. player_vel:add(arrows_dir * 7 * dt)
  71. -- basic euler integration
  72. player_pos:add(player_vel * dt)
  73. player_vel:mul(0.95)
  74. -- basic game loop
  75. for i, crumb in ipairs(crumbs) do
  76. if crumb:distance(player_pos.xz) < 0.5 then
  77. crumbs_eaten = crumbs_eaten + 1
  78. crumbs_txt = 'Crumbs eaten\n' .. crumbs_eaten
  79. if crumbs_eaten > 7 then
  80. crumbs_txt = crumbs_txt .. '\n...are you\nententained\nalready?'
  81. end
  82. crumb:set(-0.5 + lovr.math.random(), -0.5 + lovr.math.random()):mul(20)
  83. lovr.graphics.setBackgroundColor(1, 1, 1)
  84. end
  85. pass:setColor(0x40a0ff)
  86. pass:sphere(crumb.x, 0, crumb.y, 0.2)
  87. end
  88. pass:setColor(1,1,1)
  89. pass:text('Hold right\nmouse button\nto move\ntoward it', -2, 0.05, 0, 0.5, -math.pi/2, 1,0,0)
  90. pass:text(crumbs_txt, 2, 0.05, 0, 0.5, -math.pi/2, 1,0,0)
  91. pass:setColor(0x101010)
  92. pass:plane(0, 0, 0, 20, 20, -math.pi/2, 1,0,0)
  93. pass:setColor(0x505050)
  94. pass:plane(0, 0.01, 0, 20, 20, -math.pi/2, 1,0,0, 'line', 100, 100)
  95. pass:setColor(0xD0A010)
  96. pass:capsule(player_pos, player_pos + vec3(0, 0.4, 0), 0.3)
  97. local player_azimuth = math.atan2(player_vel.z, player_vel.x)
  98. pass:setColor(0x804000)
  99. pass:cone(player_pos, 0.3, 0.6, -player_azimuth - math.pi/2, 0,1,0)
  100. cam.center:lerp(player_pos, 0.1)
  101. d_azimuth = player_azimuth - cam.azimuth + math.pi
  102. d_azimuth = (d_azimuth + math.pi) % (2 * math.pi) - math.pi -- wrap angle to -PI to PI range
  103. cam.nudge(d_azimuth * 0.005)
  104. end
  105. cam.integrate()