AnimePlayer.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /**
  2. * 魔力宝贝图档解析脚本 - CGTool
  3. *
  4. * @Author HonorLee (dev@honorlee.me)
  5. * @Version 1.0 (2023-04-15)
  6. * @License GPL-3.0
  7. *
  8. * AnimePlayer.cs 动画播放器-挂载类
  9. */
  10. using System.Collections.Generic;
  11. using UnityEngine;
  12. namespace CGTool
  13. {
  14. //动画周期回调
  15. public delegate void AnimeCallback();
  16. //动画动作帧监听
  17. public delegate void AnimeEffectListener(Anime.EffectType effect);
  18. //动画音频帧监听
  19. public delegate void AnimeAudioListener(int audioIndex);
  20. //鼠标移入事件监听
  21. public delegate void MouseListener(AnimePlayer animePlayer);
  22. /**
  23. * 动画播放器,用于播放CG动画,支持多动画队列播放
  24. * 脚本需绑定至挂载了SpriteRenderer和RectTransform的对象上
  25. * 除此之外,还需绑定BoxCollider2D(可选),用于监听鼠标的移入移出事件
  26. *
  27. * 当动画播放完成后会自动调用onFinishCallback回调函数
  28. * 另外可指定onActionListener和onAudioListener监听动画动作帧和音频帧
  29. * 目前已知的动作帧有:
  30. * 击中 0x27 | 0x28
  31. * 伤害结算 0x4E | 0x4F
  32. */
  33. public class AnimePlayer : MonoBehaviour
  34. {
  35. //动画帧数据
  36. private class AnimeFrame
  37. {
  38. public int Index;
  39. public GraphicInfoData GraphicInfo;
  40. public Sprite Sprite;
  41. public AnimeFrameInfo AnimeFrameInfo;
  42. }
  43. //播放配置数据
  44. private class AnimeOption
  45. {
  46. public uint AnimeSerial;
  47. public Anime.DirectionType Direction;
  48. public Anime.ActionType ActionType;
  49. public bool Infinity;
  50. public float Speed;
  51. public float FrameRate;
  52. public AnimeDetail AnimeDetail;
  53. public AnimeCallback onFinishCallback;
  54. }
  55. //当前播放
  56. private AnimeOption _currentAnime;
  57. private AnimeFrame[] _frames;
  58. private int _currentFrame;
  59. //是否播放
  60. private bool isPlayable;
  61. //待播放队列
  62. private Queue<AnimeOption> _animeQueue = new Queue<AnimeOption>();
  63. //计时器
  64. private float _timer;
  65. //绑定SpriteRenderer
  66. private SpriteRenderer _spriteRenderer;
  67. //绑定RectTransform
  68. private RectTransform _rectTransform;
  69. //绑定BoxCollider2D(可选)
  70. private BoxCollider2D _boxCollider2D;
  71. //动画动作帧监听
  72. public AnimeEffectListener onEffectListener;
  73. public AnimeAudioListener onAudioListener;
  74. //鼠标移入事件监听
  75. public MouseListener onMouseEnterListener;
  76. //鼠标移出事件监听
  77. public MouseListener onMouseExitListener;
  78. //获取偏移量(无用)
  79. public Vector2 offset
  80. {
  81. get
  82. {
  83. float offsetX = -_frames[_currentFrame].GraphicInfo.OffsetX;
  84. float offsetY = _frames[_currentFrame].GraphicInfo.OffsetY;
  85. return new Vector2(offsetX, offsetY);
  86. }
  87. }
  88. //实例初始化时获取相关绑定
  89. private void Awake()
  90. {
  91. _spriteRenderer = GetComponentInParent<SpriteRenderer>();
  92. _rectTransform = GetComponentInParent<RectTransform>();
  93. //碰撞盒,仅当需要添加鼠标事件时使用
  94. _boxCollider2D = GetComponent<BoxCollider2D>();
  95. }
  96. //鼠标移入监听
  97. private void OnMouseEnter()
  98. {
  99. if(onMouseEnterListener!=null) onMouseEnterListener(this);
  100. }
  101. //鼠标移出监听
  102. private void OnMouseExit()
  103. {
  104. if(onMouseExitListener!=null) onMouseExitListener(this);
  105. }
  106. /**
  107. * 播放动画,调用此方法将会清空当前播放队列,调用完成可通过链式调用nextPlay方法添加动画到播放队列
  108. * @param Serial 动画序列号
  109. * @param Direction 动画方向
  110. * @param ActionType 动画动作
  111. * @param Infinity 是否循环
  112. * @param Speed 播放速度,以 1s 为基准,根据动画帧率计算实际播放周期时长
  113. * @param onFinishCallback 动画结束回调
  114. * @return AnimePlayer
  115. */
  116. public AnimePlayer play(uint Serial,Anime.DirectionType Direction,Anime.ActionType ActionType,bool Infinity = false,float Speed=1f,AnimeCallback onFinishCallback=null)
  117. {
  118. if (_spriteRenderer == null)
  119. {
  120. Debug.Log("AnimePlayer:SpriteRenderer is null");
  121. return this;
  122. }
  123. AnimeOption animeOption = CreateAnimeOption(Serial, Direction, ActionType, Infinity, Speed, onFinishCallback);
  124. if (animeOption == null)
  125. {
  126. Debug.Log("AnimePlayer:AnimeOption create failed");
  127. return this;
  128. }
  129. //清空播放队列
  130. _animeQueue.Clear();
  131. //播放
  132. _play(animeOption);
  133. //链式调用,后续可通过nextPlay方法添加动画到播放队列
  134. return this;
  135. }
  136. //调整动画方向
  137. public void changeDirection(Anime.DirectionType directionType)
  138. {
  139. _currentAnime = CreateAnimeOption(_currentAnime.AnimeSerial, directionType, _currentAnime.ActionType,
  140. _currentAnime.Infinity, _currentAnime.Speed, _currentAnime.onFinishCallback);
  141. _play(_currentAnime);
  142. }
  143. //调整动画动作类型
  144. public void changeActionType(Anime.ActionType actionType)
  145. {
  146. _currentAnime = CreateAnimeOption(_currentAnime.AnimeSerial, _currentAnime.Direction,actionType,
  147. _currentAnime.Infinity, _currentAnime.Speed, _currentAnime.onFinishCallback);
  148. _play(_currentAnime);
  149. }
  150. //播放
  151. private void _play(AnimeOption animeOption)
  152. {
  153. isPlayable = false;
  154. _currentAnime = animeOption;
  155. _frames = new AnimeFrame[animeOption.AnimeDetail.FrameCount];
  156. //获取动画帧数据
  157. for (int i = 0; i < animeOption.AnimeDetail.AnimeFrameInfos.Length; i++)
  158. {
  159. GraphicInfoData graphicInfoData = GraphicInfo.GetGraphicInfoDataByIndex(animeOption.AnimeDetail.Version, animeOption.AnimeDetail.AnimeFrameInfos[i].GraphicIndex);
  160. if (graphicInfoData == null)
  161. {
  162. Debug.Log("GraphicInfo Version:" + animeOption.AnimeDetail.Version + " Index:" +
  163. animeOption.AnimeDetail.AnimeFrameInfos[i] + " is null");
  164. continue;
  165. }
  166. GraphicData graphicData = Graphic.GetGraphicData(graphicInfoData);
  167. if (graphicData == null)
  168. {
  169. Debug.Log("GraphicData Version:" + animeOption.AnimeDetail.Version + " Index:" +
  170. animeOption.AnimeDetail.AnimeFrameInfos[i] + " is null");
  171. continue;
  172. }
  173. //创建帧数据
  174. _frames[i] = new AnimeFrame();
  175. _frames[i].Index = i;
  176. _frames[i].GraphicInfo = graphicInfoData;
  177. _frames[i].Sprite = graphicData.Sprite;
  178. _frames[i].AnimeFrameInfo = animeOption.AnimeDetail.AnimeFrameInfos[i];
  179. }
  180. _currentFrame = -1;
  181. isPlayable = true;
  182. UpdateFrame();
  183. }
  184. //创建动画配置
  185. private AnimeOption CreateAnimeOption(uint Serial, Anime.DirectionType Direction, Anime.ActionType ActionType,
  186. bool Infinity = false, float Speed = 1f, AnimeCallback onFinishCallback = null)
  187. {
  188. AnimeDetail animeDetail = Anime.GetAnimeDetail(Serial, Direction, ActionType);
  189. if (animeDetail == null)
  190. {
  191. Debug.Log("AnimePlayer:AnimeDetail is null");
  192. return null;
  193. }
  194. AnimeOption animeOption = new AnimeOption()
  195. {
  196. AnimeSerial = Serial,
  197. Direction = Direction,
  198. ActionType = ActionType,
  199. Infinity = Infinity,
  200. Speed = Speed,
  201. FrameRate = animeDetail.CycleTime / Speed / animeDetail.FrameCount,
  202. AnimeDetail = animeDetail,
  203. onFinishCallback = onFinishCallback,
  204. };
  205. return animeOption;
  206. }
  207. //加入链式动画播放队列
  208. public AnimePlayer nextPlay(uint Serial, Anime.DirectionType Direction, Anime.ActionType ActionType,
  209. bool Infinity = false, float Speed = 1f, AnimeCallback onFinishCallback = null)
  210. {
  211. AnimeOption animeOption = CreateAnimeOption(Serial, Direction, ActionType, Infinity, Speed, onFinishCallback);
  212. if (animeOption == null) return this;
  213. _animeQueue.Enqueue(animeOption);
  214. return this;
  215. }
  216. //更新计算
  217. private void Update()
  218. {
  219. float now = Time.time * 1000;
  220. if (_currentAnime != null && (now - _timer) >= _currentAnime.FrameRate) UpdateFrame();
  221. }
  222. //更新帧
  223. private void UpdateFrame()
  224. {
  225. if (!isPlayable || _frames.Length == 0) return;
  226. _currentFrame++;
  227. //动画结束
  228. if (_currentFrame >= _currentAnime.AnimeDetail.FrameCount)
  229. {
  230. if(_currentAnime.onFinishCallback!=null) _currentAnime.onFinishCallback();
  231. //循环播放
  232. if (_currentAnime.Infinity)
  233. {
  234. _currentFrame = 0;
  235. }
  236. //播放下一个动画
  237. else if(_animeQueue.Count>0)
  238. {
  239. AnimeOption animeOption = _animeQueue.Dequeue();
  240. _play(animeOption);
  241. return;
  242. }
  243. }
  244. //问题帧自动跳过
  245. if (_frames[_currentFrame] == null) return;
  246. //自动偏移
  247. // float graphicWidth = _frames[_currentFrame].Sprite.rect.width;
  248. // float graphicHeight = _frames[_currentFrame].Sprite.rect.height;
  249. // float offsetX = -_frames[_currentFrame].GraphicInfo.OffsetX;
  250. // float offsetY = _frames[_currentFrame].GraphicInfo.OffsetY;
  251. //根据当前帧Sprite动态调整对象大小
  252. float width = _frames[_currentFrame].Sprite.rect.width * 1f;
  253. float height = _frames[_currentFrame].Sprite.rect.height * 1f;
  254. _spriteRenderer.sprite =
  255. _frames[_currentFrame].Sprite;
  256. _rectTransform.sizeDelta = new Vector2(width, height);
  257. _spriteRenderer.size = new Vector2(width, height);
  258. _rectTransform.pivot = new Vector2(0.5f,0f);
  259. // 2D碰撞器自动调整,但是动态碰撞器反而会导致重叠大物体选中效果不稳定,效果不如固定大小碰撞器好
  260. // if (_boxCollider2D != null)
  261. // {
  262. // Vector2 newSize =_boxCollider2D.size
  263. // _boxCollider2D.size = new Vector2(width, height);
  264. // }
  265. // _rectTransform.pivot = new Vector2(offsetX,offsetY);
  266. // _rectTransform.localPosition = new Vector3(0f, 0f);
  267. _timer = Time.time * 1000;
  268. //动画事件帧监听
  269. if(_frames[_currentFrame].AnimeFrameInfo.Effect >0 && onEffectListener!=null) onEffectListener(_frames[_currentFrame].AnimeFrameInfo.Effect);
  270. //音频事件帧监听
  271. if(_frames[_currentFrame].AnimeFrameInfo.AudioIndex >0 && onAudioListener!=null) onAudioListener(_frames[_currentFrame].AnimeFrameInfo.AudioIndex);
  272. }
  273. }
  274. }