在上一教程中,我们用了一些简单的脚本来实现通过手柄控制游戏中角色运动的功能。在这一教程中,我们通过加入相机模型引入更多的交互。
我们将在行为图中加入更多的变量,来控制相机的位置和方向。我们创建如下三个四维向量。
在开始我们的工作之前,我们先给这些变量特定的值。
inputcamerafrom:
inputcamerato:
inputcameraup:
设置好了值之后,我们使得相机由这三个变量控制,这时相机跳转到我们指定的视角:
相应的代码写在Tutorial_camera.lua中。它的作用是始终跟随着角色的运动。
一开始,和我们在手柄交互中做的一样,我们先在开头记录一些相机的状态量:
CAMERA_MODE_DEFAULT = 0
CAMERA_MODE_STRAFE = 1
CAMERA_MODE_CLIMBING_LEDGE = 2
g_camera = {
m_from = hkVector4.new( 0.0, 4.0, 2.5 ),
m_to = hkVector4.new( 0.0, 0.0, 1.75 ),
m_up = hkVector4.new( 0, 0, 1 ),
m_forward = hkVector4.new( 0, 0, 0),
m_angle = 0,
m_mode = 0,
m_targetLeadAmount = hkVector4.new(),
m_cameraFollowDistance = 5.0,
m_orbitMaxSpeedX = 1.75,
m_orbitMaxSpeedY = 1,
m_orbitSpeedX = 0.0,
m_orbitSpeedY = 0.0,
m_cameraArmLocalSpace = hkVector4.new(0, 1.0, 0.0)
}
我们需要计算的是轨道的速度以及局部空间的偏移位置。
首先,我们把之前的所有节点封装成一个状态机:
为了便于控制,我们在状态机上再封装一个locomotion的脚本:
我们为这个脚本加入一个Update的回调函数
function onLocomotionUpdate()
-- update the gamepad state
g_gamepadState:update()
-- update the camera state
g_cameraState:update3rdPerson()
-- Compute which foot is forward and store it in a behavior variable
local leftLegIndex = hkbGetBoneIndex("LeftLegCalf")
local rightLegIndex = hkbGetBoneIndex("RightLegCalf")
local leftLegModelSpace = hkbGetOldBoneModelSpace(leftLegIndex)
local rightLegModelSpace = hkbGetOldBoneModelSpace(rightLegIndex)
local leftForward = leftLegModelSpace:getTranslation():dot3(FORWARD_AXIS)
local rightForward = rightLegModelSpace:getTranslation():dot3(FORWARD_AXIS)
if rightForward > leftForward then
hkbSetVariable("IsRightFootForward", 1)
else
hkbSetVariable("IsRightFootForward", 0)
end
end
一开始我们先刷新游戏手柄和相机状态。
当我们调用了g_cameraState::update3rdPerson()的时候,将会执行如下脚本:
-- updates the camera in 3rd person mode
function g_cameraState:update3rdPerson()
-- update the orbit speed
if( g_gamepadState.m_rightStickMagnitude < 0.1 ) then
self.m_orbitSpeedX = self.m_orbitSpeedX - self.m_orbitSpeedX * 0.1
self.m_orbitSpeedY = self.m_orbitSpeedY - self.m_orbitSpeedY * 0.1
else
self.m_orbitSpeedX = clamp( self.m_orbitSpeedX + g_gamepadState.m_rightStickX * 0.05,
-self.m_orbitMaxSpeedX, self.m_orbitMaxSpeedX)
self.m_orbitSpeedY = clamp(self.m_orbitSpeedY + g_gamepadState.m_rightStickY * 0.05,
-self.m_orbitMaxSpeedY, self.m_orbitMaxSpeedY)
end
-- rotate the camera arm if the camera is moving
if( (math.abs(self.m_orbitSpeedX) > 0) or (math.abs(self.m_orbitSpeedY) > 0) ) then
-- prevent camera from rotating too high or too low
if (self.m_cameraArmLocalSpace[2] > 0.7 and self.m_orbitSpeedY > 0) or
(self.m_cameraArmLocalSpace[2] < -0.1 and self.m_orbitSpeedY < 0) then
self.m_orbitSpeedY = 0
end
-- compute the x axis to rotate about
local side = hkVector4.new(0, 0, 0)
side:setCross(self.m_worldUp, self.m_cameraArmLocalSpace)
side:normalize3()
-- rotate about the x axis
local rotationY = hkQuaternion.new(0, 0, 0, 1)
rotationY:setAxisAngle(side, -self.m_orbitSpeedY * hkbGetTimestep() )
self.m_cameraArmLocalSpace:setRotatedDir(rotationY, self.m_cameraArmLocalSpace)
-- rotate about the y axis
local rotationX = hkQuaternion.new(0, 0, 0, 1)
rotationX:setAxisAngle(self.m_worldUp, -self.m_orbitSpeedX * hkbGetTimestep() )
self.m_cameraArmLocalSpace:setRotatedDir(rotationX, self.m_cameraArmLocalSpace)
end
-- compute camera vectors from character position and camera arm
local targetTo = hkVector4.new(0, 0, 0)
local targetFrom = hkVector4.new(0, 0, 0)
targetTo = hkbGetOldWorldFromModel():getTranslation()
targetTo[2] = targetTo[2] + 1.6
targetFrom = hkbGetOldWorldFromModel():getTranslation()
targetFrom[2] = targetTo[2]
targetFrom:addMul4(self.m_cameraArmLocalSpace, self.m_cameraFollowDistance)
self.m_to:setInterpolate4(self.m_to, targetTo, .25)
self.m_from:setInterpolate4(self.m_from, targetFrom, .33)
self.m_up = self.m_worldUp
-- set behavior variables to the local camera variables
hkbSetVariable("inputcamerafrom", self.m_from)
hkbSetVariable("inputcamerato", self.m_to)
hkbSetVariable("inputcameraup", self.m_up)
-- compute camera angle for locomotion logic
local forward = hkVector4.new()
forward[0] = self.m_from[0] - self.m_to[0]
forward[1] = self.m_from[1] - self.m_to[1]
forward[2] = 0
forward:normalize3()
self.m_angle = -math.atan2( forward[0], forward[1] )
end
这时候,我们通过手柄,就可以控制相机的移动了。我们现在要做的是,当人物往特定方向走的时候,相机将会始终跟踪人物当前朝向。
在计算方向差的时候:
local directionDifference = g_gamepadState.m_leftStickAngle - g_cameraState.m_angle - characterAngle
我们已经考虑了相机角度和人物角度的偏差。