Jump to content

Recommended Posts

Posted

I've been staring at this code for way too long and now I need different brains to look at it. This is the beginnings of a 3rd person character controller. I am now working at adding mouse look features but there seems to be a bug when the camera is at specific angles, notable y45 and y270. At those specific locations the camera glitches out.

 

I will give it another try tomorrow, and no I don't want to create a pivot and anchor the camera to it and spin that -_-

 


 

--[[

Title: Third Person Controller

Author: Einlander

Start Date: 1-30-2016

Version: .01

Description: Script to control Player in 3rd person with keyboard and mouse

Notes:

This script is designed to mimic classical 3rd person game controls:

*Look with mouse

*Move with WASD

*Follow Player as they move

]]--

 

Script.Cam =nil --entity "Camera"

 

Script.moveSpeed = 2.5 --float "Move Speed"

Script.speedMultiplier = 1.5 --float "Run Multiplier"

Script.strafeSpeed = 4 --float "Strafe Speed"

Script.jumpForce = 8 --float "Jump Force"

 

function Script:Start()

self.input={}

self.unitangle = Vec3()

self.anglemagnitude = nil

end

 

 

--[[

function Script:UpdateWorld()

 

end

--]]

 

function Script:UpdatePhysics()

local movex=0

local movez=0

self.input[0]=0

self.input[1]=0

local playerMovement = Vec3()

-- I learned the boolean shortcuts from my years programming on a Casio Graphing Calculator

self.input[1] = self.input[1] + ((window:KeyDown(Key.W) and 1 or 0) - (window:KeyDown(Key.S) and 1 or 0))

self.input[0] = self.input[0] - ((window:KeyDown(Key.A) and 1 or 0) - (window:KeyDown(Key.D) and 1 or 0))

playerMovement.z = self.input[1] * self.moveSpeed

-- Strafing

playerMovement.x = self.input[0] * self.moveSpeed

if self.carryingEntity == nil and window:KeyDown(Key.Shift) then

playerMovement.z = playerMovement.z * self.speedMultiplier

-- Run while Strafed

-- playerMovement.x = playerMovement.x * self.speedMultiplier

end

 

local jump = 0

if window:KeyHit(Key.Space) and self:IsAirborne() == 0 then

jump = self.jumpForce

playerMovement = playerMovement * 1.6

end

 

self.entity:SetInput(0, playerMovement.z, playerMovement.x, jump , false, 1.0, 0.5, true)

 

local startpos = Vec3( self.entity:GetPosition(true).x, self.entity:GetPosition(true).y + 5 , self.entity:GetPosition(true).z -5 ) -- y distance to get above players head, z - distance we want from player

local endpos = Vec3()

 

-- rotate the starting position (where the camera is at) around the player position

 

if self.rot == nil then self.rot = 0 end

self.rot = self.rot+.5

 

local finalpos = Vec3()

finalpos = self:rotateX3D({startpos},self.entity:GetPosition(true) , 0)[1]

 

 

finalpos = self:rotatePointY({finalpos},self.entity:GetPosition(true) , self.rot)[1] -- when self.rot ~ 45 or 270 it flips just for that 1 angle

 

 

-- set camera at the final rotated positon

self.Cam:SetPosition(finalpos)

 

 

-- make camera look at player

local lookAt = self:LookAt(self.entity:GetPosition(true))

lookAt.z = 0 -- THIS LINE IS IMPORTANT. DO NOT REMOVE IT, IF YOU DO IT WILL CAUSE A JUMP WHEN THE Z ANGLE CHANGES FROM NEGATIVE TO POSITIVE OR VICE VERSA

self.Cam:SetRotation(lookAt,true)

end

 

function Script:IsAirborne()

return self.entity:GetAirborne() and 1 or 0

end

 

function Script:LookAt(lookAt)

--http://www.leadwerks.com/werkspace/topic/10191-short-example-of-mathatan2/page__hl__lookat

--http://stackoverflow.com/questions/1251828/calculate-rotations-to-look-at-a-3d-point

--http://leadwerks.wikidot.com/wiki:face-entity

 

--// Calculate angle from point A towards point B

local tv = lookAt - self.Cam:GetPosition(true)

 

local tRoty = Math:ATan2(tv.x, tv.z)

local tRotx = 0

if lookAt.z >= self.Cam:GetPosition(true).z then

tRotx = -Math:ATan2(tv.y* Math:Cos(tRoty), tv.z)

else

tRotx = Math:ATan2(tv.y* Math:Cos(tRoty), -tv.z)

end

tRotz = Math:ATan2( Math:Cos(tRotx), Math:Sin(tRotx) * Math:Sin(tRoty) )

return Vec3(tRotx,tRoty,tRotz-90)

end

 

 

function Script:Get3dDistance(pointa --[[as vec3--]], pointb--[[as vec3--]]) --[[as float--]]

return math.sqrt((pointb.x - pointa.x)^2+ (pointb.y - pointa.y)^2+ (pointb.z - pointa.z)^2)

end

 

function Script:Get3dMagnitude(pointa --[[as vec3--]]) --[[as float--]]

--http://www.fundza.com/vectors/normalize/

return math.abs(math.sqrt((pointa.x * pointa.x)+ (pointa.y * pointa.y)+ (pointa.z * pointa.z)))

end

 

function Script:NormalizeVector(pointa--[[as vec3--]], length--[[as float--]])--[[as vec3--]]

--Yes I know leadwerks has these functions built in somewhere, but sometimes you just need to learn what it is you are exactly doing

--http://www.fundza.com/vectors/normalize/

pointa.x = pointa.x / math.abs(length)

pointa.y = pointa.y / math.abs(length)

pointa.z = pointa.z / math.abs(length)

return pointa

end

 

toRadians = function(degrees) return degrees / 180 * math.pi end

 

function Script:rotatePointY(points, origin, degrees)

local pointsout = {}

for i = 1, #points do

local point = Vec3(points.x,points.y,points.z)

local x = origin.x + ( math.cos(toRadians(degrees)) * (point.x - origin.x) - math.sin(toRadians(degrees)) * (point.z - origin.z) )

local z = origin.z + ( math.sin(toRadians(degrees)) * (point.x - origin.x) + math.cos(toRadians(degrees)) * (point.z - origin.z) )

point.x = x

point.z = z

table.insert(pointsout, point)

end

return pointsout

 

end

 

 

function Script:rotatePointZ(points, origin, degrees)

local pointsout = {}

for i = 1, #points do

local point = Vec3(points.x,points.y,points.z)

local x = origin.x + ( math.cos(toRadians(degrees)) * (point.x - origin.x) - math.sin(toRadians(degrees)) * (point.y - origin.y) )

local y = origin.y + ( math.sin(toRadians(degrees)) * (point.x - origin.x) + math.cos(toRadians(degrees)) * (point.y - origin.y) )

point.x = x

point.y = y

table.insert(pointsout, point)

end

return pointsout

end

 

function Script:rotatePointX(points, origin, degrees)

local pointsout = {}

for i = 1, #points do

local point = Vec3(points.x,points.y,points.z)

local y = origin.y + ( math.cos(toRadians(degrees)) * (point.y - origin.y) - math.sin(toRadians(degrees)) * (point.z - origin.z) )

local z = origin.z + ( math.sin(toRadians(degrees)) * (point.y - origin.y) + math.cos(toRadians(degrees)) * (point.z - origin.z) )

point.y = y

point.z = z

table.insert(pointsout, point)

end

return pointsout

 

end

 

function Script:rotateX3D (points, origin, degrees)

local pointsout = {}

 

for i = 1, #points do

local point = Vec3(points.x,points.y,points.z)

local y = point.y;

local z = point.z;

point.y = origin.y + (y- origin.y) * math.cos(toRadians(degrees)) - (z - origin.z) * math.sin(toRadians(degrees));

point.z = origin.z + (z- origin.z) * math.cos(toRadians(degrees)) + (y - origin.y) * math.sin(toRadians(degrees));

table.insert(pointsout, point)

end

return pointsout

end

 

 

function Script:WrapAngle(angle)

local currentrotation = angle

if currentrotation.x < 0 then

currentrotation.x = 359 - math.mod(currentrotation.x , 359)

end

 

if currentrotation.y < 0 then

currentrotation.y = math.mod(currentrotation.y , 359)

currentrotation.y = 359 - math.abs(currentrotation.y)

end

 

if currentrotation.z < 0 then

currentrotation.z = 359 - math.mod(currentrotation.z , 359)

end

 

currentrotation.x = math.mod(currentrotation.x , 359)

currentrotation.y = math.mod(currentrotation.y , 359)

currentrotation.z = math.mod(currentrotation.z , 359)

return currentrotation

end

 

--[[

function Script:Collision(entity, position, normal, speed)

 

end

--]]

 

--[[

function Script:Draw()

 

end

--]]

 

--[[

function Script:DrawEach(camera)

 

end

--]]

 

 

--This function will be called after the world is rendered, before the screen is refreshed.

--Use this to perform any 2D drawing you want the entity to display.

function Script:PostRender(context)

context:SetBlendMode(Blend.Alpha)

local pos = self.Cam:GetRotation(true)

local outText = "Actual angle" .. pos.x .. "|" ..pos.y .."|" ..pos.z

 

--local outText = self:FaceEntity(self.Cam:GetPosition(true),self.entity:GetPosition(true)).x .."|" ..

--self:FaceEntity(self.Cam:GetPosition(true),self.entity:GetPosition(true)).y .."|" ..

--self:FaceEntity(self.Cam:GetPosition(true),self.entity:GetPosition(true)).z

context:DrawText(outText,0,150)

context:DrawText("Distance:" .. self:Get3dDistance(self.Cam:GetPosition(true) , self.entity:GetPosition(true)),0,165)

 

 

pos = self.unitangle

local outText = "unit angle" .. pos.x .. "|" ..pos.y .."|" ..pos.z

context:DrawText(outText,0,180)

 

pos = self.Cam:GetPosition(true)

local outText = "Cam Position" .. pos.x .. "|" ..pos.y .."|" ..pos.z

context:DrawText(outText,0,195)

 

pos = self.entity:GetPosition(true)

local outText = "Player Position" .. pos.x .. "|" ..pos.y .."|" ..pos.z

context:DrawText(outText,0,210)

end

 

 

--[[

--This function will be called when the entity is deleted.

function Script:Detach()

 

end

--]]

 

--[[

--This function will be called when the last instance of this script is deleted.

function Script:Cleanup()

 

end

--]]

 

Posted

Since you dont want to use a pivot parent with a child camera (even though it makes it straightforward to do), you essentially just need to determine the points on a circle on the XZ plane using the basic circle equation:

X = Cx + (r * cos(angle))

Z = Cz + (r * sin(angle))

where Cx/Cz are the center of the circle, r is the radius of the circle, angle is in radians.

Cx/Cz, would be the global X&Z position of the player with r being the distance you want the camera from the player.

This is an example of that:

Script.Cam =nil --entity "Camera"

Script.radius = 5 --float "Radius"

Script.moveSpeed = 2.5 --float "Move Speed"

Script.speedMultiplier = 1.5 --float "Run Multiplier"

Script.strafeSpeed = 4 --float "Strafe Speed"

Script.jumpForce = 8 --float "Jump Force"

 

function Script:Start()

self.input={}

self.rot = 0

end

 

function Script:UpdateWorld()

self.rot = self.rot+.5

if self.rot>=360 then self.rot = 0 end

entitypos = self.entity:GetPosition(true)

CamX = entitypos.x + (self.radius * math.cos(math.rad(self.rot)))

CamZ = entitypos.z + (self.radius * math.sin(math.rad(self.rot)))

self.Cam:SetPosition(Vec3(CamX, entitypos.y+5, CamZ),true)

self.Cam:Point(self.entity)

end

 

function Script:UpdatePhysics()

self.input[0]=0

self.input[1]=0

local playerMovement = Vec3()

self.input[1] = self.input[1] + ((window:KeyDown(Key.W) and 1 or 0) - (window:KeyDown(Key.S) and 1 or 0))

self.input[0] = self.input[0] - ((window:KeyDown(Key.A) and 1 or 0) - (window:KeyDown(Key.D) and 1 or 0))

playerMovement.z = self.input[1] * self.moveSpeed

playerMovement.x = self.input[0] * self.moveSpeed

local jump = 0

if window:KeyHit(Key.Space) and self.entity:GetAirborne() == false then

jump = self.jumpForce

playerMovement = playerMovement * 1.6

end

self.entity:SetInput(0, playerMovement.z, playerMovement.x, jump , false, 1.0, 0.5, true)

end

 

function Script:PostRender(context)

context:SetBlendMode(Blend.Alpha)

context:DrawText("Actual Angle: "..self.Cam:GetRotation(true):ToString(),0,150)

context:SetBlendMode(Blend.Solid)

end

 

If you have your heart on not using Entity:Point() for whatever reason, then using the Math:ATan2() function with the camera and player's position will give the angle you need.

Win7 64bit / Intel i7-2600 CPU @ 3.9 GHz / 16 GB DDR3 / NVIDIA GeForce GTX 590

LE / 3DWS / BMX / Hexagon

macklebee's channel

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

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