Jump to content

Create your own FPS Character Controller in LUA - Part 1



In Leadwerks FPS game, if you want to do something like Leaning, HeadBobbing, ThirdPersonView, modifing default FPSCharacterController to fit these needs is a painful task. But it is much easier when you make your own Character Controller (CC) then alter it.

This tutorial series will guide you to:

  • Create a CC which can: Move, Look around from scratch.
  • Implement advanced actions: Lean, Jump, Run, Crouch

I. Create a basic CC


Step 1: Understand CC's components

  • PlayerBase (Blue): Control Player Movement (Forward, Backward, Strafe Left/Right and Jump)
  • PlayerNeck (Yellow): Control Player Mouse Look function.
  • PlayerEyes (White): Yes, it is just a camera, will be controlled by PlayerNeck (its parent)
  • CameraLead (Pink): Where the camera is pointed at (this is useful for Leaning process in later part of this series)

For your curiousity

- What is PlayerNeck for ? Why don't just apply MouseLook on Camera ?

In reallife, when you look up / down, your neck will be static, only head moves around and your eyes are attached to your head so that they move too. This will help you to see your feet model if need.

- Why CameraLead ?

In reallife, you can keep your head straight and move your eyes to look around, right? CameraLead solve that. This is useful for Leaning and HeadBobbing process too.

Step 2: Setup CC's components

  • In Leadwerks Editor, create a large platform by using Primitive Box > Rename to "Ground"
  • Create another box(es) for obtacle test.
  • Create a new Pivot > rename to "CharacterController", put it above "Ground"
  • Create new script > rename to "CharacterControl.lua > Apply it to "CharacterController" Pivot



Here is the code

--Character components variables
Script.playerWeight = 10 --Adjustable, not really important, this variable affects how player interact to other physical objects
Script.playerHeight = 1.7
Script.neckLength = 0.2
Script.leadDistance = 10

--Character movement variables
Script.playerSpeed = 2 --higer = faster

--Character look variables
Script.camerarotation = Vec3()
Script.playerFOV = 70 --Default player field of view
Script.lookUpLimit = -70
Script.lookDownLimit = 80 --Adjust lookUpLimit and lookDownLimit for your experiment

function Script:Setup_Character_Components()
  --Create Player Base
  self.playerBase = self.entity

  --Create Player Neck
  self.playerNeck = Pivot:Create()
  --Do not make playerBase become playerNeck's parent, it will be a infinitive loop for Mouse Look
  self.playerNeck:SetPosition(--default position for playerNeck
  self.playerBase:GetPosition(true).y + self.playerHeight - self.neckLength,

  --Create Player Camera
  self.playerEyes = Camera:Create()

  --Create Camera Lead, remember, this is good for Leaning action
  self.cameraLead = Pivot:Create()

function Script:Character_Look()
  local sx = Math:Round(context:GetWidth()/2)
  local sy = Math:Round(context:GetHeight()/2)
  local mouseposition = window:GetMousePosition()
  local dx = mouseposition.x - sx
  local dy = mouseposition.y - sy

  --Limit Look up / Look down angle, the fact is you can not look up 90 degrees most of time.. ehehe just kidding
  self.camerarotation.x = Math:Clamp(self.camerarotation.x + dy,self.lookUpLimit,self.lookDownLimit)
  self.camerarotation.y = self.camerarotation.y + dx

  --Smooth camera rotation
  local neckRot = self.playerNeck:GetRotation()
  self.camerarotation.x = Math:Curve(self.camerarotation.x,neckRot.x,10)
  self.camerarotation.y = Math:Curve(self.camerarotation.y,neckRot.y,10)


function Script:Character_Movement()
  local playerMove = ((window:KeyDown(Key.W) and 1 or 0) - (window:KeyDown(Key.S) and 1 or 0)) * self.playerSpeed
  local playerStrafe = ((window:KeyDown(Key.D)and 1 or 0) - (window:KeyDown(Key.A) and 1 or 0)) * self.playerSpeed
  local playerTurn = self.playerNeck:GetRotation(true).y

function Script:Bind_Character_Components_Together()
  local basePos = self.playerBase:GetPosition(true)
  --Must use this reposition process because playerBase is not playerNeck's parent, they are indipendent.
  self.playerNeck:SetPosition(basePos.x,basePos.y + self.playerHeight - self.neckLength,basePos.z)

function Script:Start()

function Script:UpdateWorld()
Step 3: Check it out !

Press F6 to start. Now you can move around by using AWSD, look around by using mouse and movement is blocked by an obtacle. So basic, so pure biggrin.png

Next tutorial about Lean, Jump, Run, Crouch coming soon!

  • Like 2
  • Thanks 1

1 Comment

Recommended Comments

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...