[Havok学习笔记(4)] INTERACTIVITY TUTORIAL

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZJU_fish1996/article/details/73277476


        在这一教程中,我们将在上一教程的基础上,加入交互模块。

        我们选择Gamepad Configs,选择Add Gamepad Config,在接入游戏手柄的时候可以使用编辑。

        


        我们关注的是移动时的变量,我们为这四个按键创建变量Left Stick Y , Left Stick X,Right Stick X, Right Stick Y , 并把变量范围修改为-1~1.

  

        

        对y我们勾选反向,来调整到正确的方向。

       在我们控制手柄的时候,可以实时看到数值的变化。


        在character菜单script选项卡下可以直接查看当前的脚本,勾选代表会在这些脚本中进行函数查找。

        我们可以在script窗口中看到一列脚本名称,双击可打开任一脚本,如点击tutorial_locomotion.lua,可以看到事先已有的教程代码内容。

       


        现在我们要做的是使用脚本来让I / O设备控制人物的行走状态。

        首先,我们需要对Idle和Run状态添加一个脚本生成器,如下图所示。

        


        这时候可以看到在文件夹对应位置出现了刚刚创建的脚本生成器:

        

        双击可以进入脚本的节点编辑器。

        


        在这里,我们可以看到一系列的回调函数,它们会在特定的条件下发起。

        由于我们需要实现的是实时地控制人物的移动,所以我们可以创建一个On Pre Update Script的函数,我们在Value处填入我们的函数名onUpdateIdle。

       


       在tutorial_locamotion.lua中,我们先把原来的代码删除,然后开始写onUpdateIdle的函数:

function onUpdateIdle()
    local leftStickX = hkbGetVariable("LeftStickX")
    local leftStickY = hkbGetVariable("LeftStickY")
	local magnitude = math.sqrt(leftStickX * leftStickX + leftStickY * leftStickY)
	if(magnitude > 0.5) then
	    hkbFireEvent("Go")
	end
end

        函数非常的简单,函数中以hkb开头的意味着它调用的是havok的SDK函数,这是一个C/C++函数。

        首先,我们获取得到LeftStickX和LeftStickY的值,接下来我们计算这个2D向量的长度,如果它大于0.5,那么我们执行“Go”事件。

        类似的,在相反情况下(小于0.5),我们使其停止。

function onUpdateRun()
    local leftStickX = hkbGetVariable("LeftStickX")
    local leftStickY = hkbGetVariable("LeftStickY")
	local magnitude = math.sqrt(leftStickX * leftStickX + leftStickY * leftStickY)
	if(magnitude < 0.5) then
	    hkbFireEvent("Stop")
	end
end

         我们再次打开脚本的编辑器,选中On Pre Update Script右边的三个点,将会显示我们要执行的函数。如果我们在这里写的函数不存在的话会显示错误。所以我们可以用这个键来检查绑定是否成功。


        我们把原始的样例代码贴入。

-- globals
PI = 3.14159
TWO_PI = PI * 2.0
DEG_TO_RAD = PI / 180.0
RAD_TO_DEG = 180.0 / PI
UP_AXIS = hkVector4.new(0.0, 0.0, 1.0)
FORWARD_AXIS = hkVector4.new(0.0, -1.0, 0.0)

-- helper table to store the state of the game pad.
g_gamepadState = 
{	
	m_leftStickX = 0,
	m_leftStickY = 0,
	m_leftStickMagnitude = 0,
	m_leftStickAngle = 0,
	
	m_rightStickX = 0,
	m_rightStickY = 0,
	m_rightStickMagnitude = 0,
	
	m_lastLeftStickX = 0,
	m_lastLeftStickY = 0,
		
	m_leftStickHoldTime = 0
}

	-- updates the state of the gamepad
function g_gamepadState:update()
	
	self.m_rightStickX = hkbGetVariable("RightStickX")
	self.m_rightStickY = hkbGetVariable("RightStickY")
	self.m_rightStickMagnitude = math.sqrt( self.m_rightStickX * self.m_rightStickX +
										    self.m_rightStickY * self.m_rightStickY )
										   
	self.m_lastLeftStickX = self.m_leftStickX
	self.m_lastLeftStickY = self.m_leftStickY	
			
	self.m_leftStickX = hkbGetVariable("LeftStickX")
	self.m_leftStickY = hkbGetVariable("LeftStickY")
	self.m_leftStickMagnitude = math.sqrt( self.m_leftStickX * self.m_leftStickX +
										   self.m_leftStickY * self.m_leftStickY )

	self.m_leftStickAngle = math.atan2( self.m_leftStickX, self.m_leftStickY )
	
	local stickDifference = (self.m_lastLeftStickX - self.m_leftStickX) * 
							(self.m_lastLeftStickX - self.m_leftStickX) + 
							(self.m_lastLeftStickY - self.m_leftStickY) * 
							(self.m_lastLeftStickY - self.m_leftStickY)
	
	if( stickDifference < 0.1 ) then 	
		self.m_leftStickHoldTime =  self.m_leftStickHoldTime + hkbGetTimestep()		
	else		
		self.m_leftStickHoldTime = 0
	end
		
end

	-- called every time the idle state is updated
function onIdleUpdate()
	
	local numDirections = 8
	
	-- if the magnatiude is high enough start running
	if( g_gamepadState.m_leftStickMagnitude > 0.5 ) then
		
		-- compute the difference between the gamepad's angle and the character's angle
		local directionDifference = computeDifference()
		
		-- select the animation
		local directionVariable = math.floor(directionDifference / (PI / (numDirections / 2)) + 0.5)
		
		-- since the range of the direction variable is [-3, 3] we need to map negative
		-- values to the animation index range in our selector which is [0,7]
		if( directionVariable < 0 ) then
			directionVariable = directionVariable + numDirections
		end
		
		-- select the animation in the manual selector generator
		hkbSetVariable("DirectionAnimation", directionVariable)
		
		-- raise the event to go (but don't spam the event queue)
		if( hkbIsNodeActive("Idle to Run Selector") == false ) then		
			
			hkbFireEvent("Go")
			
		end
		
	end
	
end	

	-- called every time the run state is updated
function onRunUpdate()

	-- if the magnatiude is low enough stop running, otherwise procedurally rotate the character
	if( g_gamepadState.m_leftStickMagnitude < 0.5 and g_gamepadState.m_leftStickHoldTime > 0.1 ) then	
	
		hkbFireEvent("Stop")		
			
	elseif( g_gamepadState.m_leftStickMagnitude > 0.5 ) then
	-- otherwise, check if the difference between the gamepad's angle and the character's angle 
	-- is large enough for a 180 turn	
	
		-- compute the difference between the gamepad's angle and the character's angle
		local directionDifference = computeDifference()	
	
		-- if the difference is greater than this about, turn the character
		local turn180Threashold = 115 * DEG_TO_RAD
	
		-- if the difference is large, then turn 180 degrees
		if ( (math.abs(directionDifference) > turn180Threashold) ) then		
			
			hkbFireEvent("Turn180")
			
		end
	
	end
	
end

	-- called every time the run state is generated
function onRunGenerate()

	-- don't try to turn if the character is already turning	
	if( hkbIsNodeActive("Run Turn 180") ) then
		return
	end

	-- only rotate the character if the user is pushing up on the stick
	if( g_gamepadState.m_leftStickMagnitude > 0.5 ) then
	
		-- compute the difference between the gamepad's angle and the character's angle
		local directionDifference = computeDifference()	
		
		-- compute the amount to turn the character
		local turnSpeed = 4.0
		local turnAmount = directionDifference * turnSpeed * hkbGetTimestep()
		
		-- rotate the character to match the target angle
		hkbSetWorldFromModel(hkbGetWorldFromModel() * hkQsTransform.new( UP_AXIS, -turnAmount))
		
	end
			
end

	-- called every time the idle to run state is updated
function onIdleToRunUpdate()

	if( g_gamepadState.m_leftStickMagnitude < 0.5 ) then
	
		hkbFireEvent("Stop")
		
	end

end

	-- computes the difference between the gamepad's angle and the character's angle
function computeDifference()	
		
	-- compute the angle of the character
	local forward = hkVector4.new(0, 1, 0)
	forward:setRotatedDir(hkbGetOldWorldFromModel():getRotation(), forward)
	local characterAngle = math.atan2( forward[0], forward[1] )
		
	-- compute the difference between the gamepad's angle and the character's angle
	local directionDifference = g_gamepadState.m_leftStickAngle - g_cameraState.m_angle - characterAngle
	
	-- keep the difference in the range of [0, pi]
	if( directionDifference < -PI ) then
		directionDifference = directionDifference + TWO_PI
	end
	if( directionDifference > PI) then
		directionDifference = directionDifference - TWO_PI
	end
	
	return directionDifference
	
end

	-- called every time the locomotion state is updated
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

        这里的代码显示了我们可以如何为lua表中的角色存储一些状态。


        g_gamepadState = 
        {
        m_leftStickX = 0,
        m_leftStickY = 0,
        m_leftStickMagnitude = 0,
        m_leftStickAngle = 0,

        m_rightStickX = 0,
        m_rightStickY = 0,
        m_rightStickMagnitude = 0,

        m_lastLeftStickX = 0,
         m_lastLeftStickY = 0,

        m_leftStickHoldTime = 0
        }


        在函数function g_gamepadState:update()中,我们获取了gamepad的许多状态,通过调用这个函数,我们可以把我们之前的代码修改如下:

function onUpdateIdle()
    g_gamepadState:update()
	if(g_gamepadState.m_leftStickMagnitude > 0.5) then
	    hkbFireEvent("Go")
	end
end

function onUpdateRun()
    g_gamepadState:update()
	if(g_gamepadState.m_leftStickMagnitude < 0.5) then
	    hkbFireEvent("Stop")
	end
end

        这是共享和复用代码的一个好办法。

        接下来我们考虑加入转向的操作,我们首先加入一个计算手柄角度和角色角度之差的函数。

        首先,通过模型矩阵来获取当前角色的旋转方位,存储在局部变量characterAngle中。接下来,直接计算手柄角度和角色角度的差。

	-- computes the difference between the gamepad's angle and the character's angle
function computeDifference()	
		
	-- compute the angle of the character
	local forward = hkVector4.new(0, 1, 0)
	forward:setRotatedDir(hkbGetOldWorldFromModel():getRotation(), forward)
	local characterAngle = math.atan2( forward[0], forward[1] )
		
	-- compute the difference between the gamepad's angle and the character's angle
	local directionDifference = g_gamepadState.m_leftStickAngle - g_cameraState.m_angle - characterAngle
	
	-- keep the difference in the range of [0, pi]
	if( directionDifference < -PI ) then
		directionDifference = directionDifference + TWO_PI
	end
	if( directionDifference > PI) then
		directionDifference = directionDifference - TWO_PI
	end
	
	return directionDifference
	
end

        这时候,我们再次更新之前引入的代码:

function onUpdateIdle()
    g_gamepadState:update()
	local numDirection = 8
	
	if(g_gamepadState.m_leftStickMagnitude > 0.5) then
	    local directionDifference = computeDifference()
		local directionVariable = math.floor(directionDifference/(PI/numDirection))
		if(directionVariable < 0) then
		    directionVariable = directionVariable + numDirections
		end
		
		hkbSetVariable("DirectionAnimation",directionVariable)
	    hkbFireEvent("Go")
	end
end

function onUpdateRun()
    g_gamepadState:update()
	if(g_gamepadState.m_leftStickMagnitude < 0.5 and g_gamepadState.m_leftStickHoldTime > 0.1) then
	    hkbFireEvent("Stop")
	elseif(g_gamepadState.m_leftStickMagnitude > 0.5) then
	    local directionDifference = computeDifference()
		local turn180Threadhold = 115 * DEG_TO_RAD
		if(math.abs(directionDifference) > turn180Threadhold) then
		    hkbFireEvent("Turn180")
		end
	end
end


        为了实现运动转向,我们可以加入如下代码,和之前不一样,我们不在update中写这个代码,而是写在generate中。

function onRunGenerate()

	-- don't try to turn if the character is already turning	
	if( hkbIsNodeActive("Run Turn 180") ) then
		return
	end

	-- only rotate the character if the user is pushing up on the stick
	if( g_gamepadState.m_leftStickMagnitude > 0.5 ) then
	
		-- compute the difference between the gamepad's angle and the character's angle
		local directionDifference = computeDifference()	
		
		-- compute the amount to turn the character
		local turnSpeed = 4.0
		local turnAmount = directionDifference * turnSpeed * hkbGetTimestep()
		
		-- rotate the character to match the target angle
		hkbSetWorldFromModel(hkbGetWorldFromModel() * hkQsTransform.new( UP_AXIS, -turnAmount))
		
	end
			
end

        这一代码的逻辑是:如果我们依旧在往一个方向行走,并且角色的朝向和我们希望的朝向有一定差别的话,我们会基于旋转速度来计算出我们希望角色旋转多少。接下来我们在模型视图执行变换。


        我们再次进入script编辑器,在Generate回调中注册以上代码。
        

猜你喜欢

转载自blog.csdn.net/ZJU_fish1996/article/details/73277476