技能系统
技能的流程:从 能否释放 到 对谁放 再到 生效结果。任何技能必将经历此流程
一,释放类型
- 主动释放:满足释放前置条件,玩家主动按键释放。
- 自动释放:满足释放前置条件,自动释放。
- 被动释放:玩家在战斗中因达成某些事件而触发。
可配置性模板
角色模板(玩家和怪物)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| using Sirenix.OdinInspector; using System.Collections; using System.Collections.Generic;
namespace Runtime.Logic { public class LogicActorData { [LabelText("角色ID")] public int actorID; [LabelText("逻辑动画组")] public Dictionary<string, LogicClipData> stateClipDatas = new Dictionary<string, LogicClipData>(); }
public class LogicClipData { [LabelText("动画名称")] public string name; [LabelText("动画长度")] public int renderFrames; [LabelText("总长度")] public int totalFrames; [LabelText("循环")] public bool isLoop; [LabelText("关键帧")] public Dictionary<int, LogicFrameData> keyFrames = new Dictionary<int, LogicFrameData>();
public List<SpilitData> renderSpilitDatas = null; }
public class SpilitData { [LabelText("起始帧")] public int startFrame; [LabelText("结束帧")] public int endFrame; [LabelText("长度")] public int length; }
public class LogicFrameData { [LabelText("帧号")] public int frameIndex; [LabelText("脚本列表")] public List<LogicScriptData> scriptDatas = new List<LogicScriptData>(); } }
|
locomotion: idle - 走 - 跑 这三个被称为locomotion 是一个角色的基础动作组。BlendTree
动力学和动画模块分离
LogicActor中负责处理输入的组件,处理后内容交给动力学组件,最后交给ViewActor的Animato组件
- 按键模块:接收按键输入(SynCmd),交给LogicActor去执行指令,同时缓存起来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| using System.Collections; using System.Collections.Generic;
namespace Runtime.Logic { public class KeyCacheInfo { public int keyValue; public bool isDown; public int lastDownFrame; }
public class LKeyComponent : LComponentBase { private Dictionary<int, KeyCacheInfo> m_cacheInfos = new Dictionary<int, KeyCacheInfo>();
public void InputKey(int key, int[] args) { CacheKey(key, args); m_actor.ExecVkey(key, args); }
private void CacheKey(int key, int[] args) { bool isDown = key < VkeyDefine.UP_DELTA; int keyValue = isDown ? key : key - VkeyDefine.UP_DELTA;
if (!m_cacheInfos.TryGetValue(keyValue, out var cacheInfo)) { cacheInfo = new KeyCacheInfo(); cacheInfo.keyValue = keyValue; m_cacheInfos.Add(key, cacheInfo); } cacheInfo.isDown = isDown; if (isDown) cacheInfo.lastDownFrame = LTime.frameCount; }
public bool IsVkeyDown(int key, int recordFrames = 0) { if (m_cacheInfos.TryGetValue(key, out var cacheInfo)) { if (recordFrames <= 0) return cacheInfo.isDown; else return cacheInfo.isDown || (LTime.frameCount - cacheInfo.lastDownFrame <= recordFrames); } return false; }
public int GetKeyDownFrame(int key) { if (m_cacheInfos.TryGetValue(key, out var cacheInfo)) return cacheInfo.lastDownFrame; return -1; }
}
}
|
- 接收输入后,LogicAction执行状态切换,LogicAnimator播放逻辑动画,
1 2 3 4 5 6
| internal virtual void SetCurrentState(int state) { m_state = state; m_actor.aniCom.Play(MotionDefine.GetMotionNameByID(m_actionID * 100 + m_state)); }
|
GamePlay常见的几个业务
- 穿墙问题
- 逻辑的位置到了墙,就应该停下,同时有输入动画还是会播,那显示层墙怎么办呢?
- 为了看起来正常,配置定义逻辑层墙的时候的位置,要宽于显示层墙的位置
- 边缘检测,逻辑层算好位置就可以了,位置都是由逻辑层处理,表现层只是负责表现,所以不会出现抖得问题
- 跳跃1:原地跳
- 跳跃2:冲刺跳
- 爬梯子
- 爬墙壁
状态管理
输入的处理
输入分为几个类型
- 普通事件:封装成帧命令,逻辑处理,再表现,而不是输入直接操作动画,操作一定要做一层抽象;
- 状态转换型输入:不同状态下,针对相同的输入指令,在逻辑状态转换中做好处理,
- 输入的时效:前摇,执行中,后摇
- 按键指令的重排序:???
动作细节
- 攻击中的顿帧: 对应的LogicActor的 Tick里 跳过要停顿的帧数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| internal void Tick() { if (IsPaused) return; if (m_freezeTime != 0) { if (m_freezeTime > LTime.time) return; else m_freezeTime = 0; }
m_curAction.Tick(); aniCom.Tick(); scriptCom.Tick(); moveCom.Tick(); }
|