在flash中用双向链表实现受控动画
虽然flash中对实现连续的动画有很好的支持, 但是在编写游戏的时候,这还远远不够好。
场景:你的英雄比怪物先一帧击中了对方,对方因为受到攻击原本准备法术的动作被停止了。这个时候下一帧不能继续播放准备法术/释放法术的动画了,而应该及时播放怪物被击中的动画。
游戏中这类场景是比较常见的,这就要求重新实现一个更好控制,更易改变的动画控制系统。
我决定自己实现一个动画调度器来控制所有的动画。这个调度器需要能在整个系统绘制新帧的时候做出合适响应。我采用的方式是监听系统的enterFrame事件,并且保留一个自系统开始以来frame计数。按FPS=60计算,使用uint型能在数十天内良好运行,考虑到游戏规模,这个时间已经足够长了 ^-^
public class AnimationDispatcher{ //帧数统计 private var _frameCounter : uint; public function listenEnterFrame(e:Event):void{ _frameCounter++; } }
接下来实现被控制的动画。
public interface IActionFrame{ //下一次需要执行的帧数 function get nextActionFrame():uint; //执行动作 function doAction():void; //执行完doAction后是否需要把对象重新放回list中 function needToPushBack():Boolean; }
链表的节点:
internal class ListNode{ private var _previous : ListNode; private var _next : ListNode; private var _item : IActionFrame; public function ListNode(item:IActionFrame){ _item = item; } public function get previous():ListNode{ return _previous; } public function set previous(value:ListNode){ _previous = value; } public function get next():ListNode{ return _next; } public function set next(value:ListNode):void{ _next = value; } public function get item():IActionFrame{ return _item; } }
链表:
public class LinkedList{ private var _head : ListNode; private var _tail : ListNode; public function LinkedList(){ _head = new ListNode(null); _tail = new ListNode(null); _head.next = _tail; _tail.previous = _head; } public function add(item:IActionFrame):void{ if (item == null) return; var newNode : ListNode = new ListNode(item); var currentNode : ListNode = _head.next; while (currentNode != null){ if (currentNode == _tail){ _tail.previous.next = newNode; newNode.next = _tail; tail.previous = newNode; break; } if (currentNode.item.nextActionFrame > newNode.nextActionFrame){ currentNode.previous.next = newNode; currentNode.previous = newNode; newNode.previous = currentNode.previous; newNode.next = newNode; break; } currentNode = currentNode.next; } } //移除列表中的item public function remove(item:IActionFrame):void{ if (item == null)return; if (_head.next == _tail) return; var currentNode : ListNode = _head.next; while (currentNode != tail){ if (currentNode.item == item){ currentNode.previous.next = currentNode.next; currentNode.next.previous = currentNode.previous; break; } currentNode = currentNode.next; } } //获取下一个需要处理的对象,同时从列表中移除此对象 public function nextActiveFrame(currentFrame:uint):IActionFrame{ if (_tail.next == _tail) return null; if (_tail.next.item.nextActionFrame > currentFrame) return null; var result : ListNode = _tail.next; _tail.next = _tail.next.next; _tail.next.previous = _tail; return result.item; } }
最后修改AnimationDispatcher类,使之能管理所有的动画:
public class AnimationDispatcher{ private var _frameCounter : uint; private var _list : LinkedList = new LinkedList(); public function listenEnterFrame(e:Event):void{ _frameCounter++; while ((var item : IActionFrame = _list.nextActiveFrame) != null){ //执行动作及处理下一帧间隔 item.doAction(); if (item.needToPushBack()){ _list.add(item); } } } }