自己写一个Animation叫GAnimation,用组件的方式添加到Player类中,这不是一个框架,而是一个类似于状态机的东西,我使用的是Animation动画,然后给每个动画定义优先级,比如攻击的优先级小于被攻击,即是说一个人的攻击可以被打断,类似这样的。辅助类有Trigger和GAnimationClip(带动画帧事件)。如何做到同步,如果数据是同步的,那么我只要根据数据来一帧一帧播放,来一个数据包就播放一帧,因为状态机是我写的,所以他的状态转移是确定的(或者说精确的)。现在只要精确播放动画的哪一帧即可,精确的播放只要利用Animation的NormalizeTime,比如我现在播放Idle动画,长度是30帧,现在来一个数据包,那我就播放第一帧,设置的NormalizeTime = 1.0f/30,再来一帧,播放第二帧,NormalizeTime = 2.0f/30,动画的长度和名字均可通过配置管理器加载,这样就可以操纵动画播放到哪里了。因为每一帧都是整数,所以也可以精确触发动画帧事件。这样动画就同步了。
注意:因为网络卡了没有数据包,动画是不该继续播放的,所以Animation在初始化的时候要把每一个AnimationClip的Speed设置为0,这样他就不会自己播放了。附上代码,状态转移那里可以不看,因为我想歪了把配置文件的AnimationState和这里的AnimStatement分开了,其实是可以合并的,很乱。
namespace Eating.Module.Game.Component
{
/// <summary>
/// 只有第一次get的时候才翻转,而不是每get一次就翻转一次
/// </summary>
public class Trigger
{
private bool _isFirstTimeToGet;
private bool _val;
public bool Value
{
get
{
if (_isFirstTimeToGet)
{
_isFirstTimeToGet = !_isFirstTimeToGet;
bool temp = _val;
_val = !_val;
return temp;
}
return _val;
}
set
{
_val = value;
_isFirstTimeToGet = true;
}
}
}
public class GAnimationClip
{
//private int _frameCount;
public int FrameCount;
public int CurFrame;
/// <summary>
/// 动画帧事件
/// </summary>
public Dictionary<int, Action> FrameEvent;
public bool IsRoop;
public GAnimationClip(bool isRoop)
{
CurFrame = 0;
FrameEvent = new Dictionary<int, Action>();
IsRoop = isRoop;
}
}
public class GAnimation : IComponent
{
enum AnimStatement
{
Idle,
Attack,
Damage,
Move,
Die,
};
//父对象的Animation
public Dictionary<int, string> AnimClipID;
//动画播放的信息
private Dictionary<int, GAnimationClip> _animInfo;
private bool _isInit = false;
private Player _player;
private AnimStatement _animStatement;
public Trigger IsDamage;
public Trigger IsDead;
public void Release()
{
_isInit = false;
_player = null;
AnimClipID = null;
_animInfo = null;
}
public GAnimationClip GetClip(int ID)
{
return _animInfo[ID];
}
public void Init(object arg = null)
{
if (_isInit)
{
this.LogWarning("Init() Has Been Inited");
return;
}
IsDamage =new Trigger();
IsDead = new Trigger();
_animStatement = AnimStatement.Idle;
Player player = (Player) arg;
if (player == null)
{
this.LogError("Init() arg Is Null!");
return;
}
_isInit = true;
_player = player;
AnimClipID = ConfigManager.Instance.GetMonsterAnimClipName();
_animInfo = ConfigManager.Instance.GetAnimationClipsInfo();
}
public void UpdateFrame(FClientKey fk = null)
{
HandleAnim(fk);
SetPlayerAnimState();
}
private void SetPlayerAnimState()
{
_player.AnimName = AnimClipID[GetID(_animStatement)];
_player.AnimNormalizeTime = 1.0f * _animInfo[GetID(_animStatement)].CurFrame
/ _animInfo[GetID(_animStatement)].FrameCount;
}
private void HandleAnim(FClientKey fk = null)
{
if (IsDead.Value && _animStatement != AnimStatement.Die)
{
ResetCurStatement();
_animStatement = AnimStatement.Die;
}
if (_animStatement == AnimStatement.Die)
{
PlayCurAnim();
}
else
{
//这个做成trigger把
if (IsDamage.Value)
{
ResetCurStatement();
_animStatement = AnimStatement.Damage;
}
if (_animStatement == AnimStatement.Damage)
{
bool isEnding = PlayCurAnim();
if (isEnding)
{
HandleAttackAnim(fk);
}
else this.Log("Damage");
}
else
{
HandleAttackAnim(fk);
}
}
}
private void HandleAttackAnim(FClientKey fk = null)
{
//攻击中 忽略重复的攻击
if (_animStatement == AnimStatement.Attack)
{
bool isEnding = PlayCurAnim();
if (isEnding)
{
HandleMoveAnim(fk);
}
}
//正在开始攻击
else if (fk != null && fk.IsAttack)
{
ResetCurStatement();
EnterNewState(AnimStatement.Attack);
}
//不在攻击中,检查移动
else
{
HandleMoveAnim(fk);
}
}
private void HandleMoveAnim(FClientKey fk = null)
{
//没有移动
if (fk == null || (fk.MoveX == 0 && fk.MoveY == 0))
{
if (_animStatement != AnimStatement.Idle)
{
ResetCurStatement();
EnterNewState(AnimStatement.Idle);
}
else PlayCurAnim();
}
else
{
//x和y有值
if (_animStatement != AnimStatement.Move)
{
ResetCurStatement();
EnterNewState(AnimStatement.Move);
}
else
{
PlayCurAnim();
}
}
}
//把AnimStatement转化为AnimClipNameDef
private int GetID(AnimStatement statement)
{
int id = -1;
if (statement
== AnimStatement.Idle)
{
id = AnimClipNameDef.IDLE;
}
else if (statement
== AnimStatement.Attack)
{
id = AnimClipNameDef.ATTACK;
}
else if (statement
== AnimStatement.Damage)
{
id = AnimClipNameDef.DAMAGE;
}
else if (statement
== AnimStatement.Move)
{
id = AnimClipNameDef.MOVE;
}
else if (statement
== AnimStatement.Die)
{
id = AnimClipNameDef.DEAD;
}
return id;
}
private bool PlayCurAnim()
{
int id = GetID(_animStatement);
//如果已经播放完这个动画而且不是一个循环动画
//就该播放其他动画了
//则通知调用者播放其他动画比如IDLE或者MOVE
if (_animInfo[id].CurFrame == _animInfo[id].FrameCount && !_animInfo[id].IsRoop)
{
return true;
}
_animInfo[id].CurFrame %= _animInfo[id].FrameCount;
_animInfo[id].CurFrame++;
if (_animInfo[id].FrameEvent.ContainsKey(_animInfo[id].CurFrame))
{
_animInfo[id].FrameEvent[_animInfo[id].CurFrame].Invoke();
}
return false;
}
private void EnterNewState(AnimStatement statement)
{
int id = GetID(statement);
if (id == -1)
{
this.LogError("EnterNewState() No Such Statement In AnimClipNameDef. statement:{0}!",
statement.ToString());
return;
}
_animStatement = statement;
PlayCurAnim();
}
private void ResetCurStatement()
{
_animInfo[GetID(_animStatement)].CurFrame = 0;
}
}
}