原创作者: playfish   阅读:1609次   评论:0条   更新时间:2011-05-26    
战斗过程图示意:



[img]/upload/attachment/50153/d795065a-57e8-343c-9b37-8d1f27bfe3c8.jpg" alt="[/img]


在《自动战斗实现以及一些游戏细节》的上一篇里面介绍了游戏人物战斗属性的设计以及战斗的触发,本章节是一个重点,主要介绍战斗过程的一系列函数与算法的实现。


本作的一个基本的思路很简单:

伤害 = 攻击-防御

速度决定攻击的间隔,其值为最大速度-速度,比如最大速度设置为12,那攻击间隔就是12-3 =9.也就是9秒发动一次攻击。

幸运决定了暴击跟躲闪,如果躲闪,伤害为0,如果暴击,伤害为攻击*2-防御

双方有任何一方HP小于0,则认为战斗结束,如果双方HP都不小于0结束战斗,即战斗中断,则判断人物逃跑,扣除一次攻击的hp。来看看在上篇就给出的代码:
	//开始战斗,当前双方hp都大于0
	if(bobo.curHp >0 || monster.curHp > 0 ){
		
		var timer = 0;				//战斗计时
		var timerStr = '[00:00]';	//显示时间字符串
		
		sysBattleMessage('战斗开始');
		
		var boboSpeed = maxSpeed-bobo.speed;		//宝宝速度计算
		var monsterSpeed = maxSpeed-monster.speed;	//怪物速度计算
		
		var battleTimer=setInterval(function(){		//计时,定时显示信息
			var s = timer%60;
			var m = Math.floor(timer/60);
			if (s<10) {
				s = "0" + s;
			}
			if(m<10){
				m = "0" + m;
			}
			timerStr = '['+m+':'+s+']';
			
			//显示宝宝动作进度条
			var temp = (timer+1)%boboSpeed == 0  ? boboSpeed : (timer+1)%boboSpeed;
			$('#boboAction').width(temp/boboSpeed*100+'%');
			//显示怪物动作进度条
			temp = (timer+1)%monsterSpeed == 0 ? monsterSpeed : (timer+1)%monsterSpeed;
			$('#monsterAction').width(temp/monsterSpeed*100+'%');
			//计时增加
			timer++;
		},1000);

这段代码前面主要是拼凑一个时间戳,类似[00:00]的格式。值得注意的地方在于下面的动作进度条,这个进度条是每秒推进一次,可以很直观的让用户感觉到战斗的过程。实现算是一个小小的技巧:
2个div都设置成绿色的背景,初始的css设置宽度为0。宝宝需要9秒进度条才会满,怪物需要7秒,满之前显示的就是进度的百分比。比如战斗进行了4秒,宝宝显示的进度的长度应该是4/9,而怪物是4/7。据此,temp是战斗时间对宝宝速度的求余操作,每次过9秒宽度就要从0开始。注意的是到9秒的时候进度是满的,求余结果是0,所以要进行一个判断。

接下来是一个核心部分,双方发动攻击的代码:
		//宝宝定时发动攻击
		var attackMonsterT = setInterval(function(){
			
			//战斗模式,先判断对方躲闪,如果躲闪,此次攻击无效
			var dodge =  Math.floor(Math.random()*100) < monster.luck;	//躲闪
			
			if(dodge){
				battleMessage(bobo,monster,'dodge',0,timerStr,monster.curHp);
			}
			else{
				var crit = Math.random()*100 < bobo.luck;	//暴击判断,返回bool值
				//暴击crit=1,否则为0。使用Number强制类型转换
				var damage = bobo.ap*(Number(crit)+1) - monster.def > 0 ? bobo.ap*(Number(crit)+1) - monster.def : 0; //考虑攻击<防御
				
				monster.curHp -= damage;
				
				if(monster.curHp < 0 ){
					monster.curHp = 0;//hp不能小于0
				}
				
				//显示战斗信息
				battleMessage(bobo,monster,crit?'crit':'common',damage,timerStr,monster.curHp);
				
				//战斗结束,清除所有定时任务
				if(monster.curHp <=0){
					clearInterval(attackMonsterT);
					clearInterval(attackBoboT);
					clearInterval(battleTimer);
					battleOver(boboWin,monster);
				}
			}
		},boboSpeed*1000);

我这里用的是setInterval(function(){},boboSpeed*1000)这样的方式,就是按照宝宝的速度定时发动攻击。注意setInterval在没有进行clear之前是一直执行的。因为设计的初衷是给自家MM玩,战斗过程不要人操作,遇到怪物就直接开打一直到结束。如果你要要进行攻击设定,可能用setTimeout程序控制战斗过程更合适。

战斗逻辑部分的处理是这样的:根据对方的幸运判断对方躲闪,没有躲闪的话,根据自己幸运判断自己是否出现暴击。这里有个隐藏的bug陷阱,由于伤害是简单的攻击-防御来计算的,如果不加判断,遇到敌人的防御大于攻击的情况,那每次攻击没伤害还会让敌人的hp增加。所以damage的计算要有这个考虑。另外,hp是不能小于0的,这个也有考虑到。

战斗结束后一定要记得清除所有的定时任务,否则会在后台不断运行。这个是宝宝对怪物进行攻击的实现。怪物对宝宝进行攻击基本相同,不再赘述。

接下来看看战斗结束部分。战斗的结束主要要进行的一些动作包括:对一些临时的数据进行清除,判断战斗结果,数据重置等。战斗的结束分为2种,一种是中断逃跑,一种是完全结束,一方阵亡。对于中断逃跑的情况:
		if(bobo.curHp>0 
		&& monster.curHp>0 
		&& confirm('战斗还未结束,逃跑将被怪兽追击,确定宝宝要逃跑吗?')){		//战斗未结束
			$.unblockUI({ 
                onUnblock: function(){ 
						bobo.curHp = bobo.curHp - monster.ap >0?bobo.curHp - monster.ap : 1; //逃跑惩罚,扣除一次hp
						//ajax  到服务器
						setBoradValue('#boboHp',bobo.curHp+'/'+bobo.hp);	//设置面板上面宝宝的信息
						clearInterval(attackMonsterT);
						clearInterval(attackBoboT);
						clearInterval(battleTimer);	
						$('#battleInfo').html('');	//清空战斗信息
						$('#monsterFace').empty();	//清空头像
						
						$('#boboAction').width('0%');	//重置战斗计时
						$('#monsterAction').width('0%');
						
						//将图片改为普通图片,占据位置
						$(boboFace).attr({
							src:'images/'+bobo.type+'/'+bobo.file+'/'+'lv'+bobo.level+'/common.gif'
						});
						$(prePosObj).append(boboFace);
						//设置坐标
						bobo.pos.X = prePos.X;
						bobo.pos.Y = prePos.Y;
						//怪兽图片
						$(monsterFace).attr({
							src:'images/'+monster.type+'/'+monster.file+'/'+'lv'+monster.level+'/common.gif'
						});
						$(curPosObj).append(monsterFace);
						$("#battleCloseIcon").unbind( "click" );
					}
				});
			}		

注释部分基本做了流程的概括。需要注意的一点是,如果战斗是中断的,那么宝宝跟怪兽都要回到原来的格子上面,所以战斗前要保存宝宝原来的位置的值。而如果战斗进行到底的话,肯定会有一方阵亡。宝宝赢了就占据当前格,输了则直接被移除,就不存在这个问题。
完全结束,一方阵亡的情况基本是相同的流程,只是少了逃跑惩罚以及位置回复。

战斗部分完整的代码在上一篇的最后有给出,可以点击这里查看http://playfish.iteye.com/blog/265264

本章的最后介绍一下在上面代码里面出现的一些很重要的函数,
/*
 * 战斗进行时显示战斗信息
 * @attacker	攻击者
 * @target		攻击目标
 * @result		攻击结果,暴击,躲闪,普通
 * @damage		造成的伤害
 * @time		攻击发动的时间
 * @hp			当前生命值
*/
function battleMessage(attacker,target,result,damage,time,hp){
	if(!attacker || !target ||!result || !time){
		console.error('msg function param error');
	}
	
	setBoradValue('#_'+target.type+'Hp',hp);	//设置HP显示
	
	var s = $("#battleInfo").html();
	s += '<p class="'+attacker.type+'">';
	s += time;
	s += ' '+attacker.name+'对'+target.name+'发起攻击,';
	
	switch(result){
		case 'dodge': //躲闪
			s += target.name+'RP爆发,躲过本次攻击,';		//头像表情切换
			$('#'+attacker.type+'Face img').attr({
				src:'images/'+attacker.type+'/'+attacker.file+'/'+'lv'+attacker.level+'/bedodge.gif'
			});
			$('#'+target.type+'Face img').attr({
				src:'images/'+target.type+'/'+target.file+'/'+'lv'+target.level+'/dodge.gif'
			});
			break;
		case 'crit'	://暴击
			s += attacker.name+'RP爆发,攻击命中要害,';
			$('#'+attacker.type+'Face img').attr({
				src:'images/'+attacker.type+'/'+attacker.file+'/'+'lv'+attacker.level+'/crit.gif'
			});
			$('#'+target.type+'Face img').attr({
				src:'images/'+target.type+'/'+target.file+'/'+'lv'+target.level+'/becrit.gif'
			});
			break;
		case 'common'://普通攻击
		default :
			$('#'+attacker.type+'Face img').attr({
				src:'images/'+attacker.type+'/'+attacker.file+'/'+'lv'+attacker.level+'/common.gif'
			});
			$('#'+target.type+'Face img').attr({
				src:'images/'+target.type+'/'+target.file+'/'+'lv'+target.level+'/common.gif'
			});
			break;
	}
	s += '造成伤害'+damage+'</p>';
	
	$("#battleInfo").html(s);					//战斗信息显示
	$("#battleInfo")[0].scrollTop = 10000;	//使得滚动条保持在底部
	
};


这个函数的作用就是拼凑出战斗信息字符串,如开头的示意图所示。函数的最后使用了$("#battleInfo")[0].scrollTop = 10000;来使得滚动条保持在最底部,现实的效果就是战斗信息会自动往下滚,不然战斗信息多了以后还要手动的下拉滚动条。

在整个程序中大量的用到了jquery的attr函数,稍微做一下介绍,attr支持对dom的属性进行获取跟设置,本代码里面大部分应用都是用来修改宝宝与怪物在各种情况下的表情,例如战斗最后结束,胜利的一方会摆出胜利的表情,而失败会摆出郁闷的表情。
宝宝攻击被躲闪的表情:


另外一个重要的函数是战斗结果的函数,目前还不完整,还缺少了获得战利品跟经验等级的实现。
function battleOver(result,monster){
	//清除计时并显示战斗结果

	if (result) {
		sysBattleMessage('宝宝赢了 ^_^');
		$('#boboFace img').attr({
			src: 'images/' + bobo.type + '/' + bobo.file + '/' + 'lv' + bobo.level + '/win.gif'
		});
		$('#monsterFace img').attr({
			src: 'images/' + monster.type + '/' + monster.file + '/' + 'lv' + monster.level + '/lose.gif'
		});
		
	}
	else {
		sysBattleMessage('宝宝输了 >_<');
		$('#boboFace img').attr({
			src: 'images/' + bobo.type + '/' + bobo.file + '/' + 'lv' + bobo.level + '/lose.gif'
		});
		$('#monsterFace img').attr({
			src: 'images/' + monster.type + '/' + monster.file + '/' + 'lv' + monster.level + '/win.gif'
		});
	}
	
};



战斗部分就暂时介绍到这里。以后再下去的部分会介绍物品的设计实现,以及对之前的介绍过的地图设计实现做一些补充,增加了地图障碍实现等。
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

文章信息

  • playfish在2009-04-03创建
  • playfish在2011-05-26更新
  • 标签: webgame, jquery, 战斗
Global site tag (gtag.js) - Google Analytics