在2d游戏中,模型动画一般是按帧播放的,通常我们希望我们的模型足够“智能”, 在一段动画过程里面可以精准地触发一系列事件,或做出一些调整。比如说一个模型的攻击动作是举起大刀往下砍,这里我们希望它把刀举过头顶往下砍的时候出现一刀光特效,在刀挥至胸前时砍中对方,配合特效在某处停留一两秒等等。这时就需要模型内部有“芯片”供我们预设或动态的输入指令。
这可以做一个不依赖资源的,适用于任何有帧系列概念的自建类。这作用有点类似ActionScript 中 MovieClip的 addFrameScript, 但要它是独立的,更灵活、可扩展。
这样,模型资源里面加上一些标签, 在Flash中是这样的:
要植入的“芯片”: FrameScript
它提供根据标签植入脚本接口 addLabelIndexScript
addLabelIndexScript("attack", fillSkill, [hitId, ...]);
/** * 如果FrameScript设置了labelIndex的标签映射字典, 则可以根据标签来执行script * @param label_ script执行的标签 * @param callback_ 具体的script * @param param_ script 的参数 * @param once_ 为true时,执行一次会清除,默认为true * */ public function addLabelIndexScript(label_:String, callback_:Function, param_:Object=null, once_:Boolean=true):void { var frame_:int= _excuteIndex; if(!_hasSetLabel) { _preLabelDic[label_] = {call:callback_, param:param_, once:once_}; } else { if(_labelIndexDic.hasOwnProperty(label_)) { frame_ = int(_labelIndexDic[label_]); } addFrameScript(frame_, callback_, param_, once_); _preLabelDic[frame_] = {call:callback_, param:param_, once:once_}; } } /** * 增加一个script * @param frame_ 执行的计数值 * @param callback_ * @param param_ * @param once_ 为true时,执行一次会清除,默认为true */ public function addFrameScript(frame_:int, callback_:Function, param_:Object=null, once_:Boolean=true):void { var callbacks_:Array = []; var frameStr_:String = frame_.toString(); if(_frameScriptDic.hasOwnProperty(frameStr_)) { callbacks_ = _frameScriptDic[frameStr_] as Array; } else { callbacks_ = _frameScriptDic[frameStr_] = []; } callbacks_.push({call:callback_, param:param_, once:once_}); }
这里还应该可以接受即时插入的脚本。
/** * 增加从当前计数往前的计数的script * @param forwardFrame_ 从当前计数起多少个计数后执行 * @param callback_ 具体的script * @param param_ script 的参数 * @param once_ 为true时,执行一次会清除,默认为true */ public function addForwardFrameScript(forwardFrame_:int, callback_:Function, param_:Object=null, once_:Boolean=true):void { var frame_:int = forwardFrame_ + _excuteIndex; addFrameScript(frame_, callback_, param_, once_); }
插入的脚本由自定义的帧数来触发。
frameScript.excuteIndex++;//帧计数完全自控制
/** * 更新FrameScript的计数 * @param value_ value_为0时表示清除FrameScript * 当value_大于当前计数时,会从当前计数开始执行(value_ - excuteIndex)次 * 并把excuteIndex 赋值为 value_ */ public function set excuteIndex(value_:int):void { if(!value_) { clear(); } else { var dis:int = value_ - _excuteIndex; while(dis) { step(); dis--; } } } /** * 执行下一次计数的script * 同时excuteIndex 加 1 */ private function step():void { excuteFrameScript(_excuteIndex); _excuteIndex++; }
这里如果要实现加速机制,改成:
frameScript.excuteIndex += multiple;//multiple 是倍数
完整代码:
package utils.display { import events.CacheEvent; import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; import flash.geom.Point; import flash.utils.Dictionary; import flash.utils.getTimer; import modules.scene.interfaces.IRender; import tools.time.CallBack; import tools.time.FrameScript; import tools.time.TimerManage; import utils.cache.SourceCacheManage; import utils.consts.MovieClipDataType; import utils.loader.LoadLevel; import utils.loader.LoadType; import utils.loader.LoadUrlUtils; import utils.loader.ResourceLoaderPool; import utils.loader.items.DgLoaderItem; import utils.loader.items.GifLoaderItem; import utils.loader.items.NdgLoaderItem; import utils.render.ModelRenderManager; public class MultiBodydisplayer extends Sprite implements IRender { public var resourceKey:String; public var centerPoint:Point; public var frameScript:FrameScript; public var stepFrame:int; private var _needLoadNum:int; private var _resourceDic:Dictionary; private var _bitmapDic:Array; private var _displayIndexs:Array; private var _currentLabel:String; private var _loopDic:Dictionary; private var _delay:int; private var _currentIndex:int; private var _scaleX:int; private var _mainHeight:int; private var _mainWidth:int; private var _everyStep:Boolean; private var _bitmapNum:int; public function MultiBodydisplayer() { frameScript = new FrameScript; _bitmapNum = 0; } public function set everyStep(value_:Boolean):void { _everyStep = value_; ModelRenderManager.getInstance().remove(this); ModelRenderManager.getInstance().add(this, _everyStep); } public function getPostion(centerPoint_:Point):Point { if(!centerPoint) { centerPoint = centerPoint_; this.x = (scaleX == 1) ? -centerPoint.x : centerPoint.x;// - (-1*scaleX)* this.y = -centerPoint.y; } return new Point(centerPoint.x - centerPoint_.x, centerPoint.y - centerPoint_.y); } private function setBodyXY(x_:Number, y_:Number):void { if(!centerPoint) { this.x = (scaleX == 1) ? -x_ : x_;// - (-1*scaleX)* this.y = -y_; centerPoint = new Point(x_, y_); } } private function clearResourceDic():void { for (var url_:String in _resourceDic) { SourceCacheManage.getInstance().resBack(url_); } _resourceDic = new Dictionary; ModelRenderManager.getInstance().remove(this); } public function setResourceUrls(urls_:Array, LoadLevel_:int=LoadLevel.LOW):Boolean { var hasResource:Boolean = false; _needLoadNum = 0;//urls_.length; clearResourceDic(); var loaderItem:NdgLoaderItem; for each(var url:String in urls_) { _needLoadNum++; _resourceDic[url] = null; if(LoadUrlUtils.getType(url) == LoadType.NDG_TYPE) { loaderItem = new NdgLoaderItem(url, LoadLevel_, true, loadCompletedHandler, url); hasResource = ResourceLoaderPool.getInstance().loadResourceByItem(loaderItem, true) || hasResource; } else if(LoadUrlUtils.getType(url) == LoadType.GIF_TYPE) { var gifItem:GifLoaderItem = new GifLoaderItem(url, LoadLevel_, true, loadCompletedHandler, url); hasResource = ResourceLoaderPool.getInstance().loadResourceByItem(gifItem, true) || hasResource; } else if(LoadUrlUtils.getType(url) == LoadType.DG_TYPE) { var dgLoaderItem:DgLoaderItem = new DgLoaderItem(url, LoadLevel_, true, loadCompletedHandler, url); hasResource = ResourceLoaderPool.getInstance().loadResourceByItem(dgLoaderItem, true) || hasResource; dgLoaderItem = null; } } loaderItem = null; return hasResource; } private function loadCompletedHandler(content_:MovieClipData, url:String):void { if(!_resourceDic.hasOwnProperty(url)) return; _resourceDic[url] = content_; _needLoadNum--; } public function set delay(value_:int):void { _delay = value_; } /** * 得到一张已经摆放好层次的位图 */ public function getBitmapByIndex(type_:Object, index_:Object):Bitmap { var index:int = int(index_); var bitmap:Bitmap; if(!_bitmapDic) _bitmapDic = []; if(null != _bitmapDic[type_]) bitmap = _bitmapDic[type_] as Bitmap; else bitmap = _bitmapDic[type_] = new Bitmap; appendNewBitmap(index, bitmap); return bitmap; } public function appendNewBitmap(index_:int, bitmap_:Bitmap):void { if(!_displayIndexs) _displayIndexs = []; if(_displayIndexs.indexOf(index_) == -1) { var hasAdd_:Boolean = contains(bitmap_); if(hasAdd_) { var oIndex_:int = getChildIndex(bitmap_); if(_displayIndexs.length > oIndex_ && _displayIndexs.length == _bitmapNum) _displayIndexs.splice(oIndex_, 1); } _displayIndexs.push(index_); _displayIndexs.sort(Array.NUMERIC); var index:int = _displayIndexs.indexOf(index_); if(!hasAdd_) { _bitmapNum ++; addChildAt(bitmap_, index); } else if(oIndex_ != index) { setChildIndex(bitmap_, index); } } } public function isCopy():Boolean { for each(var data:MovieClipData in _resourceDic) { if(!data) continue; if(null == data.labelDic[_currentLabel]) { var reLabel:String = _currentLabel.substr(0,2) + "6"; if(null != data.labelDic[reLabel]) _currentLabel = reLabel; } var frame:BitmapFrame = data.getFrame(_currentLabel, frameScript.excuteIndex); if(frame) { return frame.isCopy; } } return true; } public function step():void { // if(resourceKey == "10148") // trace("catch"); // trace(TimerManage.currentFrame + " enter multiBody time dis is " + (getTimer() - TimerManage.frameStartTime)); var isRender:Boolean = false; var needStop:Boolean = false; for each(var data:MovieClipData in _resourceDic) { if(!data) continue; if(null == data.labelDic[_currentLabel]) { var reLabel:String = _currentLabel.substr(0,2) + "6"; if(null != data.labelDic[reLabel]) _currentLabel = reLabel; } var frame:BitmapFrame = data.getFrame(_currentLabel, frameScript.excuteIndex); if(frame) { var bitmap:Bitmap = getBitmapByIndex(data.type, frame.index); if(scaleX != _scaleX) scaleX = _scaleX; if(!frame.isCopy) { setBodyXY(frame.rx, frame.ry); bitmap.x = centerPoint.x - frame.rx; bitmap.y = centerPoint.y - frame.ry; bitmap.bitmapData = frame.bitmapData; } isRender = true; if(data.type == MovieClipDataType.BODY) { _mainHeight = bitmap.height; _mainWidth = bitmap.width; if(frameScript.excuteIndex == 0) frameScript.setLabelIndexDic(data.labelDic); if(frame.isLastFrame) needStop = checkLoopEnd(); } } } _currentIndex++; if(isRender) {frameScript.excuteIndex++;} if(needStop) {stop();} // trace(TimerManage.currentFrame + " end multiBody time dis is " + (getTimer() - TimerManage.frameStartTime)); } public function imitateStep(isCopy_:Boolean):void { _currentIndex++; if(isCopy_) {frameScript.excuteIndex++;} } private var _startT:Number; public function play(label_:String, scale_:Number, delay_:int):void { _startT = getTimer(); clearBitmapData(); frameScript.excuteIndex = _currentIndex = 0; _currentLabel = label_;//ModelConst.formatFrame(label_); delay = delay_; _scaleX = scale_; playBack(); } public function playBack():void { ModelRenderManager.getInstance().add(this, _everyStep); } public function stop():void { ModelRenderManager.getInstance().remove(this); } public function setLoopInfo(label_:String, loopEndFunc_:Function, loopTime_:int=1, param:Array = null):void { if(!_loopDic) _loopDic = new Dictionary; _loopDic[label_] = {endFunc: new CallBack(loopEndFunc_, param, true), loopTime:loopTime_}; } private function checkLoopEnd():Boolean { if(_loopDic && _loopDic.hasOwnProperty(_currentLabel)) { var obj:Object = _loopDic[_currentLabel]; obj.loopTime --; if(obj.loopTime <= 0) { var callBack:CallBack = obj.endFunc as CallBack; if(callBack) callBack.execute(); delete _loopDic[_currentLabel]; return true; } } return false; } public function get isHit():Boolean { var pixel:Number; var result:Boolean = false; for each(var bitmap:Bitmap in _bitmapDic) { if(contains(bitmap) && bitmap.bitmapData) { pixel = bitmap.bitmapData.getPixel32(bitmap.mouseX, bitmap.mouseY); result = result || pixel > 0; } } return result; } public function get mainHeight():int { return _mainHeight; } public function get mainWidth():int { return _mainWidth; } public function clearBitmapData():void { for each(var bitmap:Bitmap in _bitmapDic) { bitmap.bitmapData = null; } _displayIndexs = []; centerPoint = null; } public function clear():void { stop(); for each(var bitmap:Bitmap in _bitmapDic) { if(contains(bitmap)) removeChild(bitmap); } _displayIndexs = []; centerPoint = null; _bitmapNum = 0; } public function dispose():void { clear(); clearResourceDic(); ModelRenderManager.getInstance().remove(this); } } }