Jump to content

Sample FPS Player Script in New Engine

Josh

262 views

Here is a script for a first-person player. All of this is actually working now. It's very interesting that physics, rendering, culling, and Lua game code are all executing at the same time on different threads. CPU usage in a simple scene and some physics is about 35% on a quad core processor, which is good.

I think the most interesting thing here is that to break the kinematic joint used to control an object the player picks up, you simply set the variable to nil. However, I did run into one problem with the garbage collector. If you do not call collectgarbage() after setting the joint variable to nil, the joint will not get deleted, and it will still affect the entity. This is not good. I got around this by placing a call to collectgarbage() immediately after getting rid of the joint, but I don't think I like that.  I could just make the engine call the Lua garbage collector once before the world update and once before the world render. Originally, I thought Lua would be a big problem for VR applications but since the Lua game code gets its own thread, with a maximum execution time of about 16 milliseconds just for your game code, that is plenty of time to be wasteful with the GC, and I think we will be just fine even with high-performance VR games! Remember, the rendering thread runs at 90 hz but the game logic thread only runs at 60 or even 30 hz, so it's no big deal.

However, I am leaning towards adding a Destroy() function that frees everything it can from an object without actually deleting it.

Also note that window and context are global variables. There is no "current" window or context, so this code expect that these have been created in Lua or C++ and set as global variables.

Script.lookspeed=0.1--float
Script.movespeed=3--float
Script.maxcarrymass=10--float
Script.throwforce=100--float

function Script:Start()

	--Player physics
	if self.mass==0 then self.mass=70 end
	self:EnablePhysics(PHYSICS_PLAYER)
	self.collisiontype=COLLISION_PLAYER
	
	--Set up camera
	self.camera=CreateCamera(self:GetWorld())
	local cx=Round(context:GetWidth()/2)
	local cy=Round(context:GetHeight()/2)
	window:HideMouse()
	window:SetMousePosition(cx,cy)
	self.camerarotation=self:GetRotation(true)

end

function Script:Update()

	--Mouse look
	local cx = Round(context:GetWidth()/2)
	local cy = Round(context:GetHeight()/2)
	local mousepos = window:GetMousePosition()
	window:SetMousePosition(cx,cy)
	local mx = mousepos.x - cx
	local my = mousepos.y - cy
	self.camerarotation.x = self.camerarotation.x + my * self.lookspeed
	self.camerarotation.y = self.camerarotation.y + mx * self.lookspeed
	self.camerarotation.x = Clamp(self.camerarotation.x,-90,90)
	self.camera:SetRotation(self.camerarotation,true)

	--Player movement
	local jump=0
	if window:KeyDown(KEY_SPACE) then
		if self:GetAirborne()==false then jump=6 end
	end
	if self:GetAirborne() then jump = 0 end
	local move=0
	if window:KeyDown(KEY_W) then move=move+1 end
	if window:KeyDown(KEY_S) then move=move-1 end
	local strafe=0
	if window:KeyDown(KEY_D) then strafe=strafe+1 end
	if window:KeyDown(KEY_A) then strafe=strafe-1 end
	self:SetPlayerInput(self.camerarotation.y, move*self.movespeed, strafe*self.movespeed, jump, false,0,0,true,0)
	self.camera:SetPosition(self:GetPosition(true),true)
	self.camera:Translate(0,1.7,0,true)

	--Update carried object
	if self.carryjoint~=nil then
		
		--Update kinematic joint
		local position=TransformPoint(self.carryposition,self.camera,nil)
		local rotation=TransformRotation(self.carryrotation,self.camera,nil)
		self.carryjoint:SetTarget(position,rotation)
		
		--Throw
		if window:MouseHit(MOUSE_LEFT) then
			self.carryjoint.child:AddForce(TransformNormal(0,0,1,self.camera,nil)*self.throwforce)
			self.carryjoint.child:EnableGravity(true)
			self.carryjoint=nil
			collectgarbage()
		end

	end

	--Interact with environment
	if window:KeyHit(KEY_E) then
		if self.carryjoint~=nil then

			--Drop carried object
			self.carryjoint.child:EnableGravity(true)
			self.carryjoint=nil
			collectgarbage()

		else

			--Find new object to interact with
			local pickinfo = PickInfo()
			if self.camera:Pick(0,0,10,pickinfo,0,true,0) then
				local entity=pickinfo.entity
				if entity.Activate~=nil then

					--Activate the object
					entity:Activate(self)

				else

					--Pick it up if its light enough
					if entity.mass>0 and entity.mass<=self.maxcarrymass then

						if self.carryjoint~=nil then

							--Drop carried object
							self.carryjoint=nil
							collectgarbage()

						end

						local p=entity:GetPosition(true)
						entity:EnableGravity(false)
						self.carryjoint=CreateKinematicJoint(p.x,p.y,p.z,entity)
						self.carryposition=TransformPoint(entity:GetPosition(true),nil,self.camera)
						self.carryrotation=TransformRotation(entity:GetQuaternion(true),nil,self.camera)
					end

				end
			end

		end
	end

end

 



3 Comments


Recommended Comments

Here's what I did:

--Drop carried object
self.carryjoint.child:EnableGravity(true)
self.carryjoint:Destroy()
self.carryjoint=nil

 

Share this comment


Link to comment

I also added getters / setters for "globalposition" and "globalrotation" so you can do this:

self.globalposition=Vec3(1,2,3)

 

Share this comment


Link to comment

I can't wait to write my own scripts. Nice to see physics online. 🙂

On the note of the Destroy function, I think Free() would be a better name. Destroy sounds like kill/delete. Free is what I name functions that clean out pointers and values with my RaspPi project.

Who knows, I may just be weird. I'd be interested in others take on it.

  • Like 1

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Create Your Account

Sign in

Already have an account? Sign in here.

Sign In Now
×