Jump to content

Create your own FPS Character Controller in LUA - Part 2

tipforeveryone

1,833 views

II. Implement advanced actions

Previous tutorial guided you to create your own FPS Character Controller with basic actions: Move, Look.

This tutorial will help you add some advanced actions like Jump, Run, Crouch, and Lean

*Note: Below steps will use code from previous tutorial (CharacterController.lua)

1. Jump / Run / Crouch

Firstly, build obtacle with space under it to test crouching, the space height shoud be at least 120cm (leadwerks editor will show you this height in other viewports) You can not crouch and pass space under 120cm, this was hardcoded

crouchheightest_zpsjy2bkvjg.jpg

crouchheightest2_zpshwme61xh.jpg

Next, modify the code.

Add new variables under --Character movement variables comment. Note that putting script variables at the top of script will allow you to modify character stat easier and faster than finding variables deep inside script's functions

--Character movement variables
Script.playerSpeed = 2 --higer = faster
Script.playerRunSpeedMultipier = 2 --if this variable < 1, you can make slower movement
Script.playerJumpForce = 7 --This defines how high you can jump
Add new funtion to smooth movement of character components
function Script:SmoothPosition(position,entity,rate)
local smoothX = Math:Curve(position.x,entity:GetPosition().x,rate)
local smoothY = Math:Curve(position.y,entity:GetPosition().y,rate)
local smoothZ = Math:Curve(position.z,entity:GetPosition().z,rate)
entity:SetPosition(smoothX,smoothY,smoothZ)
end
Replace code in Character_Movement() function by this code
function Script:Character_Movement()
	local playerSpeed, playerJumpForce, playerCrouch

	--Press Space bar to Jump
	if window:KeyHit(Key.Space) then playerJumpForce = self.playerJumpForce else playerJumpForce = 0 end

	--Hold Ctrl key to Crouch
	if window:KeyDown(Key.ControlKey) then
		playerCrouch = true
	else
		if self.playerHeadBlocked then playerCrouch = true else playerCrouch = false end
	end

	--Hold Shift key + AWSD to Run
	if window:KeyDown(Key.Shift) then
		playerSpeed = self.playerSpeed * self.playerRunSpeedMultipier
	else
		playerSpeed = self.playerSpeed
	end

	local playerMove = ((window:KeyDown(Key.W) and 1 or 0) - (window:KeyDown(Key.S) and 1 or 0)) * playerSpeed
	local playerStrafe = ((window:KeyDown(Key.D)and 1 or 0) - (window:KeyDown(Key.A) and 1 or 0)) * playerSpeed
	--Using local playerSpeed varialbe instead of self.playerSpeed in the old code
	local playerTurn = self.playerNeck:GetRotation(true).y

	self.playerBase:SetInput(playerTurn,playerMove,playerStrafe,playerJumpForce,playerCrouch)
end
Replace code in Bind_Character_Components_Together() function too
function Script:Bind_Character_Components_Together()
	--Must use this reposition process because playerBase is not playerNeck's parent, they are indipendent.

	local basePos = self.playerBase:GetPosition(true)
	local height

	if window:KeyDown(Key.ControlKey) then
		height = basePos.y + (self.playerHeight - self.neckLength) / 2
		--You can adjust this variable to get desired crouch height for playerNeck (and playerEyes too)
	else
		if self.playerHeadBlocked then
			height = basePos.y + (self.playerHeight - self.neckLength) / 2
		else
			height = basePos.y + self.playerHeight - self.neckLength
		end
	end

	self:SmoothPosition(Vec3(basePos.x,height,basePos.z),self.playerNeck,10)
end
Add a new function to script, this will keep player crouching if something block above character when release Ctrl key, prevent from being pushed around by obtacle when standing.
function Script:Check_Head_Block()
	--We can use a raycast to check if something block character head when crouching
	local pickInfo = PickInfo()
	local point1 = self.playerNeck:GetPosition(true)
	local point2 = self.playerBase:GetPosition(true) + Vec3(0,self.playerHeight,0)
	if world:Pick(point1,point2,pickInfo,0.3,true) then
		self.playerHeadBlocked = true
	else
		self.playerHeadBlocked = false
	end
end
Update Script:UpdateWorld() function with new Check_Head_Block() function
function Script:UpdateWorld()
	self:Bind_Character_Components_Together()
	self:Check_Head_Block()
	self:Character_Look()
	self:Character_Movement()
end
Now, you can crouch under obtacle, release Ctrl Key and you are still crouching. Continue moving for auto standing.

2. Leaning

In Character_Movement() function, add this code after Crouching code

--Hold Q/E to lean Left/Right
local leanDistance = 0.025 --leanDistance should be < 0.025 or camera will pass through wall when you get too close
if window:KeyDown(Key.Q) then self.playerEyes:Move(-leanDistance,-0.01,0) end
if window:KeyDown(Key.E) then self.playerEyes:Move(leanDistance,-0.01,0) end
And add this line at the bottom of Bind_Character_Components_Together() function
self:SmoothPosition(Vec3(0,self.neckLength,0),self.playerEyes,10)
You can lean now, so smooth.

Final script file was attached to this entry. You can apply it to a pivot point (PlayerControl).

CharacterController.lua



5 Comments


Recommended Comments

I have recently come back to this blog to go through the code again to make a character controller and I seem to be getting an issue with the player camera. I have attached my script to a CSG box and when I move the camera rotates but doesn't follow the box, it's as if the camera's position is fixed but I can still rotate it. Not to sure where I have gone wrong as all I have done is copy and paste the code.

Share this comment


Link to comment

If you press the jump button while in the air you can still jump, you can fix this by using raycasts to find if the player is grounded or not.

Share this comment


Link to comment

Join the conversation

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

Guest
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.

  • Blog Entries

    • By Josh in Josh's Dev Blog 4
      Here are some screenshots showing more complex interface items scaled at different resolutions. First, here is the interface at 100% scaling:

      And here is the same interface at the same screen resolution, with the DPI scaling turned up to 150%:

      The code to control this is sort of complex, and I don't care. GUI resolution independence is a complicated thing, so the goal should be to create a system that does what it is supposed to do reliably, not to make complicated things simpler at the expense of functionality.
      function widget:Draw(x,y,width,height) local scale = self.gui:GetScale() self.primitives[1].size = iVec2(self.size.x, self.size.y - self.tabsize.y * scale) self.primitives[2].size = iVec2(self.size.x, self.size.y - self.tabsize.y * scale) --Tabs local n local tabpos = 0 for n = 1, #self.items do local tw = self:TabWidth(n) * scale if n * 3 > #self.primitives - 2 then self:AddRect(iVec2(tabpos,0), iVec2(tw, self.tabsize.y * scale), self.bordercolor, false, self.itemcornerradius * scale) self:AddRect(iVec2(tabpos+1,1), iVec2(tw, self.tabsize.y * scale) - iVec2(2 * scale,-1 * scale), self.backgroundcolor, false, self.itemcornerradius * scale) self:AddTextRect(self.items[n].text, iVec2(tabpos,0), iVec2(tw, self.tabsize.y*scale), self.textcolor, TEXT_CENTER + TEXT_MIDDLE) end if self:SelectedItem() == n then self.primitives[2 + (n - 1) * 3 + 1].position = iVec2(tabpos, 0) self.primitives[2 + (n - 1) * 3 + 1].size = iVec2(tw, self.tabsize.y * scale) + iVec2(0,2) self.primitives[2 + (n - 1) * 3 + 2].position = iVec2(tabpos + 1, 1) self.primitives[2 + (n - 1) * 3 + 2].color = self.selectedtabcolor self.primitives[2 + (n - 1) * 3 + 2].size = iVec2(tw, self.tabsize.y * scale) - iVec2(2,-1) self.primitives[2 + (n - 1) * 3 + 3].color = self.hoveredtextcolor self.primitives[2 + (n - 1) * 3 + 1].position = iVec2(tabpos,0) self.primitives[2 + (n - 1) * 3 + 2].position = iVec2(tabpos + 1, 1) self.primitives[2 + (n - 1) * 3 + 3].position = iVec2(tabpos,0) else self.primitives[2 + (n - 1) * 3 + 1].size = iVec2(tw, self.tabsize.y * scale) self.primitives[2 + (n - 1) * 3 + 2].color = self.tabcolor self.primitives[2 + (n - 1) * 3 + 2].size = iVec2(tw, self.tabsize.y * scale) - iVec2(2,2) if n == self.hovereditem then self.primitives[2 + (n - 1) * 3 + 3].color = self.hoveredtextcolor else self.primitives[2 + (n - 1) * 3 + 3].color = self.textcolor end self.primitives[2 + (n - 1) * 3 + 1].position = iVec2(tabpos,2) self.primitives[2 + (n - 1) * 3 + 2].position = iVec2(tabpos + 1, 3) self.primitives[2 + (n - 1) * 3 + 3].position = iVec2(tabpos,2) end self.primitives[2 + (n - 1) * 3 + 3].text = self.items[n].text tabpos = tabpos + tw - 2 end end  
    • By 💎Yue💎 in Dev Log 5
      The prototype of a four-wheeled vehicle is completed, where the third person player can get on and off the vehicle by pressing the E key.  To move the vehicle either forward or backward, is done with the keys W, and the key S, to brake with the space key.  And the principle is the same as when driving the character, a third person camera goes behind the car orbiting 360 degrees.

      I don't think the vehicle is that bad, but I'm absolutely sure it can be improved.  The idea is that this explorer works with batteries, which eventually run out during the night when there is no sunlight.
      Translated with www.DeepL.com/Translator
       
      Mechanics of the game.
      I'm going to focus on the mechanics of the game, establish starting point (Landing area), after the orbiter accident on Mars where all your companions died, now, to survive, you will have to repair your suit, oxygen runs out, good luck.  This involves replacing the oxygen condenser that is failing and the suit is stuck.

      On the ground and performance.
      The rocks, the terrain and the vehicle kill the SPF, but there is a solution, and everything is related to the chassis of the vehicle. That is to say that if I put a simple collision bucket for the vehicle, the yield recovers, something that does not happen if I put a collider of precise calculation for the car. This has the advantage of better performance but is not very accurate, especially when the car crashes with an object in front, because the horn of the car has no collision. And the solution to this, is to put a sliding joint, as was done with the area in which the player climbs the car and descends from it.


       
      On the rocks, I am trying to make them with the slightest polygons and the most distant from each other. 
      Obviously on Mars I can not create canyons, high mountains, is because the terrain does not produce shadows on itself, that's why the terrain tries to be as flat as possible, simulating a desert with dunes. 

      That's all for now.
       
    • By 💎Yue💎 in Dev Log 9
      The prototype is finished, and the mechanics of the game can be given way.  It has established a desert terrain in the form of dunes, this implies that there are no cannons or anything similar, because Leadwerks does not allow a terrain to cast shadows on that same terrain and this looks visually rare.
      So the terrain is like low-slope dunes. On the other hand, I think the texture of the terrain is already the definitive one, with the possibility of changes and suggestions on the part of those involved in this project.
      On the other hand we have taken the model of a habitat of the nasa, which certainly looks very nice. 
      The next steps, are to establish the starting point of the player, this must start near the capsule return to Mars somewhere on the map of 2024 x 2.
      And think about the first thing you should do, repair your suit? Seek a shelter? things like that.  


×
×
  • Create New...