前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【虚幻引擎】简单三连击实现

【虚幻引擎】简单三连击实现

作者头像
六月丶
发布2022-12-26 18:07:12
4960
发布2022-12-26 18:07:12
举报
文章被收录于专栏:六月-游戏开发

演示

https://hctra.cn/usr/uploads/2022/09/3849803929.mp4

实现

一次攻击过程:

思路与过程

可以先将三段攻击的完整动画按顺序放入一个Montage中,通过MontageSections使每一段动画从头到尾播完就结束:

然后将每一段攻击动画过程拆分为准备、释放、检查按键、恢复姿势四个部分,分别用notify通知(每段动画开始时默认为准备状态,所以准备的notify可以省略):

注意,图中这里才是实际的notifyName,改这里:

在准备状态按下按键,会记录起来;在释放状态按下按键或之前记录过,就在notify通知中通过设置初始section实现跳到下一段攻击动画。

代码部分

其中我将对actor的montage动画控制抽象到了一个类中(取名为AnimManager),它提供包括PlayMontage、StopMontage、JumpSectionSetPlayRate等方法,具体代码:

AnimManager.lua(点击展开)

代码语言:javascript
复制
local GD = require("Global.Dispatcher")
local GEVT = require("Global/Events")
local GF = require("Global/Function")
local GE = require("Global/Enums")

local cls = {
    -- 拥有者pawn
    own = nil,

    --播放缓存数据
    mesh_ = nil,
    montage_ = nil,
    playRate_ = nil, 
    startPosition_ = nil,
    startSection_ = nil,
    _CompletedFunc = nil,
    _BlendOutFunc = nil,
    _InteruptedFunc = nil,
    _NotifyBeginFunc = nil,
    _NotifyEndFunc = nil,
}


function cls:ReceiveBeginPlay()
    self.own = {}
end

--[[
===========================================
            定时调用
===========================================
--]]

-- 延迟调用
---@param time 延迟时间
function cls:DelayCall(time, ...)
    GD:PostDelay(self, self, time, GEVT.DELAY_CALL_PARAM, ...)
end

-- 定时循环调用
---@param loopTime 循环调用时间
---@param runNow 是否要马上执行一次
---@param func 循环调用的函数
function cls:LoopCall(loopTime, runNow, func)
    return GF:LoopCall(self, loopTime, runNow, func)
end

function cls:OnDelayCall(func, ...)
    if type(func) == "function" then
        func(...)
    else
        print("hcLog Error OnDelayCall func type", type(func))
    end
end

--[[
===========================================
            蓝图调用
===========================================
--]]


function cls:LUA_OnCompleted()
    if(self._CompletedFunc) then
        self._CompletedFunc(self.own)
    end
    self.isPlayMontage_bp = false
end

function cls:LUA_OnBlendOut()
    if(self._BlendOutFunc) then
        self._BlendOutFunc(self.own)
    end
    self.isPlayMontage_bp = false
end

function cls:LUA_OnInterupted()
    if(self._InteruptedFunc) then
        self._InteruptedFunc(self.own)
    end
end

function cls:LUA_OnNotifyBegin(notifyName)
    if(self._NotifyBeginFunc) then
        self._NotifyBeginFunc(notifyName)
    end
end


function cls:LUA_OnNotifyEnd(notifyName)
    if(self._NotifyEndFunc) then
        self._NotifyEndFunc(self.own, notifyName)
    end
end

--[[
===========================================
            私有函数
===========================================
--]]

function cls:_clearCached()
    self.mesh_ = nil
    self.montage_ = nil
    self.playRate_ = nil
    self._CompletedFunc = nil
    self._BlendOutFunc = nil
    self._InteruptedFunc = nil
    self._NotifyBeginFunc = nil
    self._NotifyEndFunc = nil
    self.isPlayMontage_bp = false
end

--[[
===========================================
            公有函数
===========================================
--]]

function cls:IsPlayMontage()
    return self.isPlayMontage_bp
end

function cls:PlayMontage5(mesh, montage, completedFunc, notifyBeginFunc, notifyEndFunc)
    self:PlayMontage(mesh, montage, nil, nil, nil, completedFunc, nil, nil, notifyBeginFunc, notifyEndFunc)
end

function cls:PlayMontage(mesh, montage, playRate, startPosition, startSection, completedFunc, blendOutFunc, interuptedFunc, notifyBeginFunc, notifyEndFunc)
    if self:IsPlayMontage() then
        print("hcErr Error: 当前正在播放montage")
        return
    end
    self:_clearCached()

    if playRate == nil then
        playRate = 1
    end
    if startPosition == nil then
        startPosition = 0
    end
    if startSection == nil then
        startSection = "None"
    end
    self.mesh_ = mesh
    self.montage_ = montage
    self.playRate_ = playRate
    self.startPosition_ = startPosition
    self.startSection_ = startSection

    if completedFunc then
        self._CompletedFunc = completedFunc
    end
    if blendOutFunc then
        self._BlendOutFunc = blendOutFunc
    end
    if interuptedFunc then
        self._InteruptedFunc = interuptedFunc
    end
    if notifyBeginFunc then
        self._NotifyBeginFunc = notifyBeginFunc
    end
    if notifyEndFunc then
        self._NotifyEndFunc = notifyEndFunc
    end
    self.isPlayMontage_bp = true
    self:BP_PlayMontage(mesh, montage, playRate, startPosition, startSection)
end

-- 跳到指定Section
function cls:JumpSection(sectionName)
    self:BP_PlayMontage(self.mesh_, self.montage_, self.playRate_, self.startPosition_, sectionName)
end

-- 设置播放速率
function cls:SetPlayRate(rate)
    assert(self.montage_ ~= nil, "Error")
    self.mesh_:GetAnimInstance():Montage_SetPlayRate(self.montage_, rate)
end

-- 停止当前montage动画
function cls:StopMontage()
    if self.isPlayMontage_bp == false then
        return
    end
    self.mesh_:GetAnimInstance():Montage_Stop(0, self.montage_)
    self:_clearCached()
end

return cls

代码语言:javascript
复制
-- 普通三连击
function cls:NormalThreeAttack()
    local own = self.own
    self.attackType = CharacterEnums.AttackType.NormalFire

    self.normalThreeAttackIdx = 1
    own:UseEndurance(self.normalThreeAttackEndurance)
    local compFunc = function()
        own:ChangeNormalStatus_()
    end
    local nofityBeginFunc = function(name)
        if name == GE.MontageNotifyName.CheckKey then
            if self.saveAttackKey == true then
                self.saveAttackKey = false  --使用存储
                self.normalThreeAttackIdx = self.normalThreeAttackIdx + 1
                if own:HaveEndurance() then
                    own:UseEndurance(self.normalThreeAttackEndurance)
                    own.animManager_:JumpSection(tostring(self.normalThreeAttackIdx))
                    self.attackStatus = GE.MontageStatus.Ready
                end
            else
                self.attackStatus = GE.MontageStatus.CheckKey
            end
        elseif name == GE.MontageNotifyName.Release then
            -- 1  1.3  1.6
            own:OpenAttack_(1 + (self.normalThreeAttackIdx - 1) * 0.3)
            self.attackStatus = GE.MontageStatus.Release
        elseif name == GE.MontageNotifyName.Recovery then
            own:CloseAttack_()
            if GF:VSize(own.inputXYVector_bp) > 0.1 then
                own.animManager_:JumpSection("End")
            end
            self.attackStatus = GE.MontageStatus.Recovery
        elseif name == GE.MontageNotifyName.HitGround then
            if self.normalThreeAttackIdx == 3 then
                GD:Post(own, nil, GEVT.CAMERA_SHAKE, GE.CameraShakeType.Attack)
            end
        end
    end
    self.attackStatus = GE.MontageStatus.Ready
    own.animManager_:PlayMontage5(own["Mesh"], own.AM_normalThreeAttack123_bp, compFunc, nofityBeginFunc)
end
代码语言:javascript
复制
-- 监听按键
function cls:InputKeyPressed(key, value)
    local own = self.own
    -- 对于普通三连击,如果按下攻击键,且状态是CheckKey就释放下一阶段,否则如果状态是Ready或Release就存储这个键
    if self.attackType == CharacterEnums.AttackType.NormalFire then
        if key == GE.InputKey.NormalFire then
            if self.attackStatus == GE.MontageStatus.CheckKey then
                self.normalThreeAttackIdx = self.normalThreeAttackIdx + 1
                if own:HaveEndurance() then
                    own:UseEndurance(self.normalThreeAttackEndurance)
                    own.animManager_:JumpSection(tostring(self.normalThreeAttackIdx))
                    self.attackStatus = GE.MontageStatus.Ready
                end
            elseif self.attackStatus == GE.MontageStatus.Release then
                self.saveAttackKey = true
            end
        end
    end
end
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022 年 09 月,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 演示
  • 实现
    • 思路与过程
      • 代码部分
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档