我们在制作Lua modifier的时候,第一步自然是这个API
LinkLuaModifier("modifier_your_modifier_name", "path/to/your/modifier/modifier_your_modifier_name.lua", LUA_MODIFIER_MOTION_NONE)
多思考一下就不难想到,最后一个参数,是做什么用的,如果改为其他的结果,比如LUA_MODIFIER_MOTION_HORIZONTAL/LUA_MODIFIER_MOTION_VERTICAL/LUA_MODIFIER_MOTION_BOTH,会有什么不同呢?
答案是,如果你在Lua modifier的代码里面不做什么特殊的处理的话,做出来的效果并没有什么不同。
而这个参数的不同,导致的结果是,我们在modifier_your_modifier.lua中,使用
modifier_your_modifier_name = class({})
所创建的这个class,在你为其指定LUA_MODIFIER_MOTION_HORIZONTAL
的时候,其链接到的class不是CDOTA_Modifier_Lua
,而是CDOTA_Modifier_Lua_Horizontal_Motion
,而仔细查看这个继承自CDOTA_Modifier_Lua
的class的API文档,不难发现,他多出来的几个API就是实现运动控制的关键。
首先,我们需要在modifier的OnCreated
函数中,调用ApplyHorizontalMotionController
,之后在UpdateHorizontalMotion
的函数中为单位刷新位置,之后在modifier的OnDestroy
函数中为单位移除运动控制器RemoveHorizontalMotionController
。
具体实现
modifier_horizontal_motion_example = class({})
function modifier_horizontal_motion_example:OnDestroy()
if IsServer() then
-- 移除运动控制器
self:GetParent():RemoveHorizontalMotionController(self)
end
end
function modifier_horizontal_motion_example:OnCreated(kv)
if IsServer() then
-- 为单位添加水平运动控制器
self:ApplyHorizontalMotionController()
local owner = self:GetParent()
-- 储存一些初始状态
self.vStartingPosition = owner:GetOrigin()
self.flTimeExpired = 0
end
end
function modifier_player_entering_next_level:UpdateHorizontalMotion(me, dt)
if IsServer() then
-- 计算具体的新位置,这个要根据实际需要来
-- me指代的是拥有运动控制器的单位,也就是这个modifier的owner,dt就指距离上次调用这个
-- 函数过去的时间
-- 引擎将会为这两次调用之间平滑地插补动画
me:SetOrigin(newOrigin)
end
end
至于具体想要怎么动,那就看你想要具体怎么计算newOrigin
了。
举一个简单的例子:
newOrigin = self:GetParent():GetForwardVector() * 1000 + self:GetParent():GetOrigin()
这样这个单位就会以1000的速度向前冲刺了。
如果你还要上上下下地动?
原理是一样的,你需要在OnCreated函数中添加 self:ApplyVerticalMotionController()
,并在UpdateVerticalMotion(me, dt)
中为单位确定新的位置。
需要注意的是,你需要先获取单位所在的位置,然后计算单位的Z轴的位置,并使用me:SetOrigin()
来确定单位的位置。
function modifier_vertical_motion_example:UpdateVerticalMotion(me, dt)
if IsServer() then
local origin = me:GetOrigin()
origin.z = CalculateYourZ() -- 具体怎么计算根据你的实际需要来
me:SetOrigin(origin)
end
end
最后放上一段自己的代码
具体的实现效果是,完成一次向指定地点的,高度为4000的抛物线运动:
modifier_creature_pudge_jump = class({})
function modifier_creature_pudge_jump:IsStunDebuff()
return true
end
function modifier_creature_pudge_jump:IsHidden()
return true
end
function modifier_creature_pudge_jump:IsPurgable()
return false
end
function modifier_creature_pudge_jump:RemoveOnDeath()
return false
end
function modifier_creature_pudge_jump:OnCreated(kv)
if IsServer() then
if self:ApplyHorizontalMotionController() == false or self:ApplyVerticalMotionController() == false then
self:Destroy()
return
end
self.vStartPosition = GetGroundPosition( self:GetParent():GetOrigin(), self:GetParent() )
self.vTargetPosition = self:GetAbility():GetCursorPosition()
self.flDuration = 1.7
self.flHeight = 4000
self.vDirection = (self.vTargetPosition - self.vStartPosition):Normalized()
self.flDistance = (self.vTargetPosition - self.vStartPosition):Length2D()
self.flHorizontalSpeed = self.flDistance / self.flDuration
-- 创建开始的特效和音效
EmitSoundOnLocationWithCaster(self.vStartPosition, "Ability.TossThrow", self:GetParent())
end
end
function modifier_creature_pudge_jump:OnDestroy()
if IsServer() then
self:GetParent():RemoveHorizontalMotionController(self)
self:GetParent():RemoveVerticalMotionController(self)
end
end
function modifier_creature_pudge_jump:DeclareFunctions()
local funcs = {
MODIFIER_PROPERTY_OVERRIDE_ANIMATION,
}
return funcs
end
function modifier_creature_pudge_jump:CheckState()
local state = {
[MODIFIER_STATE_STUNNED] = true,
}
return state
end
function modifier_creature_pudge_jump:GetOverrideAnimation()
return ACT_DOTA_CAST_ABILITY_ROT
end
function modifier_creature_pudge_jump:UpdateHorizontalMotion(me, dt)
if IsServer() then
-- 根据速度,设置当前的位置
local vOldPosition = me:GetOrigin()
local vNewPos = vOldPosition + self.vDirection * self.flHorizontalSpeed * dt
vNewPos.z = 0
me:SetOrigin(vNewPos)
-- 判断是否到达了终点
end
end
function modifier_creature_pudge_jump:UpdateVerticalMotion(me, dt)
if IsServer() then
local vOrigin = me:GetOrigin()
local vDistance = (vOrigin - self.vStartPosition):Length2D()
local vZ = - 4 * self.flHeight / (self.flDistance * self.flDistance) * (vDistance * vDistance) + 4 * self.flHeight / self.flDistance * vDistance
vOrigin.z = vZ
-- 判断是否到达了终点
local flGroundHeight = GetGroundHeight( vOrigin, self:GetParent() )
local bLanded = false
if ( vOrigin.z < flGroundHeight and vDistance > self.flDistance / 2 ) then
vOrigin.z = flGroundHeight
bLanded = true
end
me:SetOrigin(vOrigin)
if bLanded == true then
-- ApplyDamage
local units = FindUnitsInRadius(self:GetParent():GetTeamNumber(), self:GetParent():GetOrigin(), nil, 275, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_ALL, DOTA_UNIT_TARGET_FLAG_NONE, FIND_ANY_ORDER, false)
for _, unit in pairs(units) do
DealDamage(self:GetParent(), unit, 1, self)
end
-- Particle
local pid = ParticleManager:CreateParticle("particles/econ/items/earthshaker/earthshaker_totem_ti6/earthshaker_totem_ti6_leap_impact.vpcf", PATTACH_WORLDORIGIN, self:GetParent())
ParticleManager:SetParticleControl(pid, 0, me:GetOrigin())
ParticleManager:ReleaseParticleIndex(pid)
EmitSoundOnLocationWithCaster(self:GetParent():GetOrigin(), "Ability.TossImpact", self:GetParent())
self:GetParent():RemoveHorizontalMotionController(self)
self:GetParent():RemoveVerticalMotionController(self)
self:SetDuration(0.15, true)
end
end
end
教程到此结束。