Hello, everyone!
I have just started learning 3D yesterday and I’m already having a blast! Today, I made a First Person project with joystick controls! It took all day since I’m not that good with math. But that’s why I’m giving the code away to all those folks who need a simple way of making a First Person Camera.
Here’s the code:
displayMode(FULLSCREEN)
-- Use this function to perform your initial setup
function setup()
Camera = FPCam({x = 0, y = 200, z = 0, limitYRot = true, fov = 90})
Ground = mesh()
Ground.vertices = {vec3(-700,0,-700),vec3(-700,0,700),vec3(700,0,-700),
vec3(700,0,-700),vec3(-700,0,700),vec3(700,0,700)}
Ground.texCoords = {vec2(0,0),vec2(0,1),vec2(1,0),vec2(1,0),vec2(0,1),vec2(1,1)}
Ground.texture = readImage("SpaceCute:Background")
UI = {
JSRot = Joystick({x = WIDTH-150, y = 150, size = 170, colour = color(255,190,0)}),
JSMov = Joystick({x = 150, y = 150, size = 170, colour = color(255,0,0)})
}
touches = {}
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
do
local RotJs = UI.JSRot
if RotJs.stickPos ~= vec2(0,0) then
Camera.rotation.x = Camera.rotation.x + (RotJs.value.x*0.0005)
Camera.rotation.y = Camera.rotation.y + (RotJs.value.y*0.0005)
end
local MovJs = UI.JSMov
if MovJs.stickPos ~= vec2(0,0) then
local Movement = (MovJs.value/10):rotate(-Camera.rotation.x)
Camera.pos.x = Camera.pos.x + Movement.y
Camera.pos.z = Camera.pos.z + Movement.x
end
end
Camera:view()
Ground:draw()
viewMatrix(matrix())
ortho()
for _,ui in pairs(UI) do
ui:draw()
end
for _,t in pairs(touches) do
for _,ui in pairs(UI) do
if ui.touched then
ui:touched(t)
end
end
if t.state == ENDED or t.state == CANCELLED then
touches[t.id] = nil
end
end
end
function touched(t)
touches[t.id] = {state=t.state,x=t.x,y=t.y,id=t.id}
end
FPCam = class()
function FPCam:init(data)
self.pos = vec3()
self.pos.x = data.x or 0
self.pos.y = data.y or 100
self.pos.z = data.z or 0
self.fov = data.fov or 75
self.rotation = vec2()
self.rotation.x = data.xRot or 0
self.rotation.y = data.yRot or 0
self.limitYRot = data.limitYRot or false
self.lookAt = vec3()
self.lookAt.x = data.xLook or 0
self.lookAt.y = data.yLook or 0
self.lookAt.z = data.zLook or 0
self.camM = data.camMode or 1
end
function FPCam:view()
local posx,posy,posz = self.pos.x,self.pos.y,self.pos.z
perspective()
if self.camM == 1 then
if self.limitYRot then
if self.rotation.y > math.pi/2 then
self.rotation.y = math.pi/2
elseif self.rotation.y < -math.pi/2 then
self.rotation.y = -math.pi/2
end
end
self.lookAt.x = posx+math.cos(self.rotation.x)
self.lookAt.y = posy+math.tan(self.rotation.y)
self.lookAt.z = posz+math.sin(self.rotation.x)
end
camera(posx,posy,posz,self.lookAt.x,self.lookAt.y,self.lookAt.z)
end
Joystick = class()
function Joystick:init(data)
self.pos = vec2(data.x,data.y)
self.stickPos = vec2(0,0)
self.c = color(data.colour.r,data.colour.g,data.colour.b,data.colour.a)
self.size = data.size or 100
self.visible = true
if data.initialValue then
self.value = data.initialValue * 1
else
self.value = vec2(0,0)
end
end
function Joystick:draw()
if self.visible then
pushStyle()
pushMatrix()
fill(0,0)
stroke(self.c)
strokeWidth(self.size/20)
ellipseMode(CENTER)
translate(self.pos.x,self.pos.y)
ellipse(0,0,self.size)
fill(self.c)
noStroke()
translate(self.stickPos.x,self.stickPos.y)
ellipse(0,0,self.size/1.5)
popStyle()
popMatrix()
end
end
function Joystick:touched(t)
if self.visible then
local tv = vec2(t.x,t.y)
local inside = tv:dist(self.pos) <= self.size/2
if (t.state == ENDED or t.state == CANCELLED) and self.touching == t.id then
self.touching = nil
self.stickPos = vec2(0,0)
elseif self.touching == t.id or (t.state == BEGAN and inside) then
if t.state == BEGAN and inside then
self.touching = t.id
end
if inside then
self.stickPos = tv - self.pos
else
self.stickPos = (tv - self.pos):normalize() * (self.size/2)
end
self.value = self.stickPos
end
end
end
How to use the class:
The class has 2 modes:
- Look at mode (Forces the player to look at a 3D point)
- Rotate mode (The x and y rotation of the camera can be set)
Look at mode:
With the look at mode, you can set a 3D point for the camera to look at. You can switch your camera to look at mode with the .init()
function (C = FPCam({camMode=0})
) or with the .camM
variable (C.camM = 0
). set it to anything that isn’t 1
because mode 1
is rotate mode.
To set the x,y,z position to look at, you can do it like this
C = FPCam({xLook=100,yLook=0,zLook=300})
or like this
C.lookAt = vec3(100,0,300)
Rotate mode:
With the rotate mode, you can just rotate the camera rather than setting a location to look at. You can switch your camera to rotate mode, the same way you would switch your camera to look at mode except that you have to set it to mode 1 (example: C = FPCam({camMode=1})
).
To change the rotation of the camera, all you have to do is this
C = FPCam({XRot=100,YRot=-200})
or this
C.rotation = vec2(100,200)
Note that the rotation is in radians, not degrees.
Also note that setting your camera to rotate mode and then running FPCam.view()
will overwrite .lookAt
.
You can stop your “head” from going below -math.pi
and above math.pi
(straight down and straight up) by setting limitYRot to true in either the .init()
function or with the .limitYRot
variable.
Other settings:
You can set the x,y,z position of the camera like this
C = Camera({x=0,y=300,z=400})
or like this
C.pos = vec3(0,300,400)
You can also set the FOV of your camera either like this
C = Camera({fov = 90})
or like this
C.fov = 90
Enjoy!