-- Left thumbstick: move and strafe -- Right thumbstick: rotate the view horizontally -- Right grip: switch to snap locomotion mode -- Left grip: enable flying mode -- Left trigger: switch orientation to left hand controller (can look around while moving) local motion = { pose = lovr.math.newMat4(), -- Transformation in VR initialized to origin (0,0,0) looking down -Z thumbstickDeadzone = 0.4, -- Smaller thumbstick displacements are ignored (too much noise) directionFrom = 'head', -- Movement can be relative to orientation of head or left controller flying = false, -- Snap motion parameters snapTurnAngle = 2 * math.pi / 12, dashDistance = 1.5, thumbstickCooldownTime = 0.3, thumbstickCooldown = 0, -- Smooth motion parameters turningSpeed = 2 * math.pi * 1 / 6, walkingSpeed = 4, } lovr.graphics.setBackgroundColor(0.1, 0.1, 0.1) function motion.smooth(dt) if lovr.headset.isTracked('right') then local x, y = lovr.headset.getAxis('right', 'thumbstick') -- Smooth horizontal turning if math.abs(x) > motion.thumbstickDeadzone then local hx, _, hz = lovr.headset.getPosition() motion.pose:translate(hx, 0, hz) motion.pose:rotate(-x * motion.turningSpeed * dt, 0, 1, 0) motion.pose:translate(-hx, 0, -hz) end end if lovr.headset.isTracked('left') then local x, y = lovr.headset.getAxis('left', 'thumbstick') local direction = quat(lovr.headset.getOrientation(motion.directionFrom)):direction() if not motion.flying then direction.y = 0 end -- Smooth strafe movement if math.abs(x) > motion.thumbstickDeadzone then local strafeVector = quat(-math.pi / 2, 0,1,0):mul(vec3(direction)) motion.pose:translate(strafeVector * x * motion.walkingSpeed * dt) end -- Smooth Forward/backward movement if math.abs(y) > motion.thumbstickDeadzone then motion.pose:translate(direction * y * motion.walkingSpeed * dt) end end end function motion.snap(dt) -- Snap horizontal turning if lovr.headset.isTracked('right') then local x, y = lovr.headset.getAxis('right', 'thumbstick') if math.abs(x) > motion.thumbstickDeadzone and motion.thumbstickCooldown < 0 then local angle = -x / math.abs(x) * motion.snapTurnAngle local hx, _, hz = lovr.headset.getPosition() motion.pose:translate(hx, 0, hz) motion.pose:rotate(angle, 0, 1, 0) motion.pose:translate(-hx, 0, -hz) motion.thumbstickCooldown = motion.thumbstickCooldownTime end end -- Dashing forward/backward if lovr.headset.isTracked('left') then local x, y = lovr.headset.getAxis('left', 'thumbstick') if math.abs(y) > motion.thumbstickDeadzone and motion.thumbstickCooldown < 0 then local moveVector = quat(lovr.headset.getOrientation('head')):direction() if not motion.flying then moveVector.y = 0 end moveVector:mul(y / math.abs(y) * motion.dashDistance) motion.pose:translate(moveVector) motion.thumbstickCooldown = motion.thumbstickCooldownTime end end motion.thumbstickCooldown = motion.thumbstickCooldown - dt end function lovr.update(dt) motion.directionFrom = lovr.headset.isDown('left', 'trigger') and 'left' or 'head' if lovr.headset.isDown('left', 'grip') then motion.flying = true elseif lovr.headset.wasReleased('left', 'grip') then motion.flying = false local height = vec3(motion.pose).y motion.pose:translate(0, -height, 0) end if lovr.headset.isDown('right', 'grip') then motion.snap(dt) else motion.smooth(dt) end end function lovr.draw(pass) pass:transform(mat4(motion.pose):invert()) -- Render hands pass:setColor(1,1,1) local radius = 0.05 for _, hand in ipairs(lovr.headset.getHands()) do -- Whenever pose of hand or head is used, need to account for VR movement local poseRW = mat4(lovr.headset.getPose(hand)) local poseVR = mat4(motion.pose):mul(poseRW) poseVR:scale(radius) pass:sphere(poseVR) end -- Some scenery lovr.math.setRandomSeed(0) local goldenRatio = (math.sqrt(5) + 1) / 2 local goldenAngle = (2 - goldenRatio) * (2 * math.pi) local k = 1.8 for i = 1, 500 do local r = math.sqrt(i) * k local x = math.cos(goldenAngle * i) * r local y = math.sin(goldenAngle * i) * r if lovr.math.random() < 0.05 then pass:setColor(0.5, 0, 0) else local shade = 0.1 + 0.3 * lovr.math.random() pass:setColor(shade, shade, shade) end pass:cylinder(x, 0, y, 1,0.05, math.pi / 2, 1,0,0) end end