回合制战斗设计四 资源加载和Soldier初始化及攻击动画的实现[本人原创,转载请注明出处]如前文所述,Soldier下有多个动作资源需要加载。如果战斗人物比较多,就需要提前加载战斗相关的资源。
我事先使用神仙道资源做了一个naruto的3个动作,naruto_attack.swf,naruto_defense,naruto_stand.swf.
目前这三个swf都是手动编辑的,内部都是导出BMP为AnimateData对象,并且实现IAnimateData接口。每个动作图片的大小和序列数量是不一样的,故实际上,每个动作都需要单独创建一个AnimateData对象,用来生成各自动作的SWF,这些使用脚本+JSFL是可以实现的,难度实际上也不大,后续如果有时间我会把这个脚本贡献出来,当然也欢迎牛人贡献一下。
var domain:ApplicationDomain = this._loader.getMovieClip(id).loaderInfo.applicationDomain;
if(domain) return domain.getDefinition("AnimateData") as Class;//资源中对BMP进行导出
回合制战斗设计五 自动回合的战斗设计
[本人原创,转载请注明出处]
前面我们已经可以控制两个玩家的交互动作了,下面我们可以考虑战斗了。
回合制一个基本特点是轮询机制
1.选择攻击者
2.选择被攻击者
选定这两个soldier之后,就可以构成一个攻击回合。
对于自动回合制,这部分处理通常在后端处理,对于交互性的可能要更为复杂点。
先考虑自动回合制,自动回合意味着前端仅仅播放动画,执行后端已经生成好的Command。
我们设定一个Battle对象用于处理战斗过程。
基本逻辑:Battle双方为A和B(左右各一个Soldier),其中一个Health<=0,回合结束,显示战斗结果。
1.初始化A,B站位(后面我们会加入阵型处理)
2.执行后端的命令
3.显示战斗结果
Battle中Soldier有两个,攻击方和被攻击方法,我们是A和B来区分
var soldierDict={};
soldierDict['A']=soldierA;
soldierDict['B']=soldierB;
soldierA和soldierB的生成请参考前面文章。
一系列的cmd数据我们定义为一个数据
var cmdTestData:Array=[
{"attacker":"A","target":"B","hurt":310,"cmd":"attack"},
{"attacker":"B","target":"A","hurt":90,"cmd":"attack"},
{"attacker":"A","target":"B","hurt":370,"cmd":"attack"},
{"attacker":"B","target":"A","hurt":360,"cmd":"attack"}//这个CMD执行完之后,B应该挂了
]
动画播是按次序的播放的,我们将Command数据解析成单个动作数据,即ActionItem数据ActionItem结构:private var mSoldiers:Array;//动作执行者,可能有多个。如果是群攻,防御者将为多个
private var mActionType:String;//动作类型
对于回合制战斗,一个Command通常包括两个ActionItem,一个是攻击者,另外一个是防守者。更为复杂的情况我们后面再讨论。//解析命令数据为ActionItempublic function interpret(data:Array):void{
var cmdData:Object;
var attacker:Soldier;
var defenser:Soldier;
var item:ActionItem;
for each(cmdData in data){
attacker=soldierDict[cmdData["attacker"]];
defenser=soldierDict[cmdData["target"]];
item=new ActionItem([attacker],ActionItem.Atk);
this.actionItems.push(item);
item=new ActionItem([defenser],ActionItem.Def);
this.actionItems.push(item);
}
}}解析为之后需要执行这些ActionItem
对于之前Animate和Soldier对象我们需要适当重构一下首先,我们将Animate中对动画的数据处理提取出来,放入到ActionData中,并在ActionData保存
ActionData: private var mAnimateData:Object;
private var mId:String; private var mFrames:Array;
public function ActionData(data:Object,id:String){
this.mAnimateData=data;
this.mId=id;
this.mFrames=new Array();
init();
}
private function init():void{
var count:int=this.mAnimateData.imageCount;
var content:BitmapData=this.mAnimateData.animateContent;
var h:int=content.height;
var w:int=content.width/count;
var bitmapData:BitmapData;
for(var i:int=0;i<count;i++){
bitmapData = new BitmapData(w, h, true, 0);
bitmapData.copyPixels(content, new Rectangle(i*w, 0 , w, h), new Point());
this.mFrames.push(bitmapData);
}
}
Animate只处理动画播放,同时添加一个帧播放结束的函数回调。重构后的主要代码如下:protected static var mActionDataDict:Object={};public function Animate(frameRate:int=12):void{
this.mBitmap=new Bitmap(null,"auto",true);
this.addChild(mBitmap);
this.mFrameRate=frameRate;
}
protected function onTimer(e:TimerEvent):void{
this.mBitmap.bitmapData= this.mFrames[this.mCurrentFrame++];
if (this.mCurrentFrame == this.mFrames.length){
this.mCurrentFrame = 0;
if (!this.mLoop){
this.stop();
}
//very important:flash是顺序执行的,最好是将onTimer执行结束之后再调用
if(this.mFrameEndAction is Function){
this.mFrameEndAction();
}
}
}
private function stop():void{
if(this.mTimer){
this.mTimer.stop();
}
}
public function init(loader:AnimateLoader,id:String):void{
this.mTypeId=id;
var AssetClass:Class;
for each(var action:String in ActionItem.Actions){
if(!mActionDataDict[id+"_"+action]){//保存动画资源
AssetClass = loader.getAssetClass(id+"_"+action);
mActionDataDict[id+"_"+action]=new ActionData(new AssetClass(),id+"_"+action);
}
}
}
protected function getActionData(action:String):ActionData{
return mActionDataDict[this.mTypeId+"_"+action] as ActionData;
}
//播放某个动作
public function play(action:String,loop:Boolean=false):void{
if (this.mTimer){
this.mTimer.stop();
} else {
this.mTimer = new Timer(1000 / this.mFrameRate);
this.mTimer.addEventListener(TimerEvent.TIMER, this.onTimer, false, 0, true);
};
this.mCurrentFrame=0;
this.mLoop=loop;
this.mCurrentAction=action;
var adata:ActionData=getActionData(action);
this.mFrames=adata.frames;
this.mTimer.start();
}
public function set frameEndAction(value:Function):void{
this.mFrameEndAction=value;
}
Soldier继承Animate对象:public class Soldier extends Animate{
public static var LEFT:int=1;//面朝左
public static var RIGHT:int=-1;
public function Soldier(name:String,direction:int=1,rate:int=12){
this.mDirection=direction;
this.soldierName=name;
this.scaleX=this.scaleX*this.mDirection;
this.name=name;
super(rate);
}
override public function init(loader:AnimateLoader,id:String):void{
super.init(loader,id);
standby();//战士默认的动作是待命状态
}
public function standby():void{
play(ActionItem.Standby,true);//默认动作,代码状态始终播放
}
}
Battle对象的作用1. 初始化战场2. 初始化战斗成员3. 初始化战斗数据,生成ActionItem序列4. 执行战斗序列5. 显示战斗结果关键是第四步,如何按顺序的执行战斗序列,我们通过在Animate中的frameEndAction,来判断一个战斗序列是否执行结束。通过添加匿名函数: s.frameEndAction=function():void{
s.frameEndAction=null;
s.standby();
doNextAction();
}可能达到我们的目的。
Battle:/*** main参数为当前战斗舞台,Battle本身不继承Sprite,是一个普通AS类**/public function Battle(main:Sprite,a:Soldier,b:Soldier):void{
soldierDict['A']=a;
soldierDict['B']=b;
soldierLayer=new Sprite();
main.addChild(soldierLayer);
this.soldierLayer.addChild(a);
this.soldierLayer.addChild(b);
this.actionItems=new Array();
}
public function start():void{
interpret(this.cmdDataArr);//解析ActionItem序列
var item:ActionItem=actionItems.shift();
doAction(item);
}
//执行ActionItem序列public function doAction(item:ActionItem):void{
var s:Soldier=item.soldiers.shift();
s.frameEndAction=function():void{ ...//如上
}
s.play(item.type);
if(item.soldiers.length>0){
for each(s in item.soldiers){
s.frameEndAction=function():void{
...//如上
}
s.play(item.type);
}
}
}
//执行下一个序列public function doNextAction():void{
if(actionItems.length==0) return;
var item:ActionItem=this.actionItems.shift();
doAction(item);
}
在主程序中:在资源价值完毕后,onComplete(e:BulkProgressEvent):soldierA=new Soldier("A",Soldier.RIGHT);
soldierA.init(animateLoader,"naruto");
soldierA.x=300;
soldierB=new Soldier("B");
soldierB.init(animateLoader,"naruto");
soldierB.x=500;
var battle:Battle=new Battle(this,soldierA,soldierB);
battle.start();
即可看到一个简单回合战斗过程。