在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);
   }
  }
 }
}

posted on 2010-07-18 02:20  Reginald  阅读(1747)  评论(5编辑  收藏  举报

导航