国际惯例,先上个效果图:
战斗部分的实现应该是游戏里最复杂的部分,具体实现我的小游戏还没做完整,这里只是给出一些我的思路和做法。
战斗部分很复杂,全文大概分成以下几点:
- 战斗属性设置
- 战斗触发
- 战斗过程(重点)
- 战斗结束
由于游戏初衷是给我家MM玩的,所以过程都设置得很简单,宝宝跟怪物遇上,就自动开打,战斗过程全部自动,根据双方的速度定时发动攻击。
宝宝的战斗属性我的设置如下
var bobo = {
pos : {X:1, Y:1}, //地图上的坐标位置
name : '辛德瑞拉 杰克罗琳', //宝宝的名字,GF喜欢个这么拉风的。。。
hp : 100, //full hp
curHp : 100, //current hp
level : 0, //等级
exp : 0, //经验
ap : 15, //攻击力attack power
def : 1, //防御力defence
speed : 3, //速度
luck : 15, //幸运
eyeshot : 2, //视野
type : 'bobo', //类型,用于区分怪兽,宝宝,NPC
file : 'baozi' //文件名称
};
怪物的属性基本跟这个类似。但是不同的是,一个地图上可能有多种多样的怪物,所以怪物我使用对象数组来保存:
var monsters = [
{
pos:{ X:1, Y:8 },
name:'青蛙', hp:30, curHp:30, level:0,
exp:0, ap:10, def:0, speed:5,
luck:15, type:'monster', file:'qingwa'
},
{
pos:{ X:5, Y:6 },
name:'青蛙', hp:30, curHp:30, level:0,
exp:0, ap:20, def:0, speed:5,
luck:15, type:'monster', file:'qingwa'
},
{
pos:{ X:4, Y:5 },
name:'青蛙', hp:130, curHp:30, level:0,
exp:0, ap:5, def:0, speed:5,
luck:15, type:'monster', file:'qingwa'
},
{
pos:{ X:10, Y:9 },
name:'青蛙', hp:50, curHp:30, level:0,
exp:0, ap:20, def:0, speed:5,
luck:15, type:'monster', file:'qingwa'
}
];
当然,如果以后做成了多人互动,也可以将宝宝改成使用对象数组来保存。
有了这样的数据结构后,还需要将这样的数据结构转化为页面上的表现形式。比如宝宝要变成页面上的一个图片,怪物要变成在各个地点散落出现的图片。这就需要一个将数组转化为html元素的过程。这个过程我是采用jquery的map函数实现的。
//将所有怪物对象转换为jQuery的img对象
var monstersFace = $.map(monsters,function(i){
return $('<img class="monster" title="'+i.name+'" src="images/'+i.type+'/'+i.file+'/lv'+i.level+'/common.gif" />');
});
map的作用,就是将一个数组映射为另一个数组返回。这里就省却了我们自己不少功夫。关于jquery的map具体说明可以参看http://docs.jquery.com/Utilities/jQuery.map#arraycallback
在开发篇的2已经提到了如果将这些对象插入到地图中,这里不再重复。接下来介绍下战斗如何触发以及如何进行。
战斗的触发设定我的实现是:每次宝宝移动,都判断当前坐标上面的图片数目,如果图片为2,即宝宝跟怪物在同一个格子上面,那么触发战斗。用jquery的方式实现就很简单了
var curPosObj = $("#"+"X"+curPos.X+"Y"+curPos.Y); //当前坐标对象
if($(curPosObj).find('img').length > 1 ){ //如果有2张图片
var obj = $(curPosObj).find('img').get(0); //获得第一张
switch(obj.className){ //使用classname来区分图片是怪物还是物品,物品功能将在以后介绍
case "monster": //与怪物战斗
battle(curPos,prePos);
break;
case "item": //获得物品
getItem(curPos);
break;
}
}
触发了战斗后,开打。开篇的效果图就是开打后的效果,相信大家对thickbox的效果都很熟悉了,不过这个使用jquery的另一个插件blockUI实现的,因为觉得这个界面的定制性更强一些。关于jquery的blockUI插件:http://malsup.com/jquery/block/#overview
进入战斗
var curPosObj = $("#"+"X"+curPos.X+"Y"+curPos.Y); //当前战斗坐标
var prePosObj = $("#"+"X"+prePos.X+"Y"+prePos.Y); //战斗前宝宝的位置
var boboFace = $(curPosObj).find('img').get(1);
var monsterFace = $(curPosObj).find('img').get(0);
$('#boboFace').append(boboFace); //将宝宝的头像设置到战斗画面中
$('#monsterFace').append(monsterFace);//怪物的头像设置
var monster = getObjByPos(curPos.X,curPos.Y,'monster');
setBattleValue('monster', monster); //将宝宝的战斗信息设置到战斗画面中
setBattleValue('bobo', bobo); //怪物的战斗信息设置
//利用blockUI显示战斗画面
$.blockUI({
message : $('#battle') ,
css : {
top : '15%',
width : '60%',
left : '20%',
cursor : 'default'
}
});
战斗部分应该说是一个大难点,篇幅也比较长,时间关系,所以本篇分成了上下2篇,我有时间就继续往下写。等不及的同志可以先玩味下代码,下篇我就继续介绍的战斗实现的一些细节。主要是战斗过程以及战斗结束的处理。
//开始战斗,当前双方hp都大于0
if(bobo.curHp >0 || monster.curHp > 0 ){
var boboWin = true;
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);
//宝宝定时发动攻击
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.floor(Math.random()*100) < bobo.luck; //暴击判断,返回bool值
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;
}
//显示战斗信息
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);
//怪物定时发动攻击
var attackBoboT = setInterval(function(){
var dodge = Math.floor(Math.random()*100) < bobo.luck; //躲闪
if(dodge){
battleMessage(monster,bobo,'dodge',0,timerStr,bobo.curHp);
}
else{
var crit = Math.floor(Math.random()*100) < monster.luck; //暴击
var damage = monster.ap*(Number(crit)+1) - bobo.def > 0 ? monster.ap*(Number(crit)+1) - bobo.def : 0;
bobo.curHp -= damage;
if(bobo.curHp < 0){
bobo.curHp = 0;
}
battleMessage(monster,bobo,crit?'crit':'common',damage,timerStr,bobo.curHp);
if(bobo.curHp <=0){
clearInterval(attackMonsterT);
clearInterval(attackBoboT);
clearInterval(battleTimer);
boboWin = false;
battleOver(boboWin,monster);
}
}
},monsterSpeed*1000);
}
$('#battleCloseIcon').click(function(){
if(bobo.curHp>0
&& monster.curHp>0
&& confirm('战斗还未结束,逃跑将被怪兽追击,确定宝宝要逃跑吗?')){ //战斗未结束
$.unblockUI({
onUnblock: function(){
bobo.curHp = bobo.curHp - monster.ap >0?bobo.curHp - monster.ap : 1;
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" );
}
});
}
else{
$.unblockUI({
onUnblock: function(){
clearInterval(attackMonsterT);
clearInterval(attackBoboT);
clearInterval(battleTimer);
$('#battleInfo,#monsterFace').empty(); //清空战斗信息
$('#boboAction').width('0%'); //重置战斗计时
$('#monsterAction').width('0%');
//将图片改为普通图片,占据位置
if(boboWin){
$(boboFace).attr({
src:'images/'+bobo.type+'/'+bobo.file+'/'+'lv'+bobo.level+'/common.gif'
});
$(curPosObj).append(boboFace);
}
else{
$(monsterFace).attr({
src:'images/'+monster.type+'/'+monster.file+'/'+'lv'+monster.level+'/common.gif'
});
$(curPosObj).append(monsterFace);
}
}
});
}
});