Jump to content

Need help with LookAt function


Einlander
 Share

Recommended Posts

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

--]]

 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

 Share

×
×
  • Create New...