[Canvas]Bombman v1.04
Bombman是我仿造红白机上经典游戏爆破小人,用Canvas制作的一款网页版单机游戏, 自我感觉还是有一定的可玩性。
本游戏的胜利条件是用雷消灭所有怪物,但被怪物即使是擦边碰到或是炸弹火焰炸到就算失败。
操作方法是用方向键或是aswd健来控制人物移动,用空格键放雷炸怪物,吃红五星则雷的爆炸范围增加。
点此下载程序1.04版,用浏览器打开,并点“开始”按钮开始游戏,游戏结束后可以点“再来一次”按钮重新玩。
程序的1.06版本由此下载,此版本增加了怪物跟随主角的功能。
本作Github url:https://github.com/horn19782016/Bombman
有空可以玩玩,注意不要操之过急哦。
图例:



近千行源码:
<!DOCTYPE html>
<html lang="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<head>
<title>炸弹人v1.06 19.3.14 6:34 by:逆火狂飙 horn19782016@163.com</title>
<!--1.06版本启用了怪物跟随英雄的功能 -->
<style>
#canvas{
background:#ffffff;
cursor:pointer;
margin-left:10px;
margin-top:10px;
-webkit-box-shadow:3px 3px 6px rgba(0,0,0,0.5);
-moz-box-shadow:3px 3px 6px rgba(0,0,0,0.5);
box-shadow:3px 3px 6px rgba(0,0,0,0.5);
}
#controls{
margin-top:10px;
margin-left:15px;
}
</style>
</head>
<body onload="init()">
<div id="controls">
<input id='animateBtn' type='button' value='开始'/>
<input id='playAgainBtn' type='button' value='再来一盘' style='visibility:hidden'/>
</div>
<canvas id="canvas" width="160px" height="160px" >
出现文字表示你的浏览器不支持HTML5
</canvas>
</body>
</html>
<script type="text/javascript">
<!--
// 暂停/开始游戏
animateBtn.onclick=function(e){
paused=! paused;
if(paused){
animateBtn.value="开始";
screenMsg="Paused!";
}else{
animateBtn.value="停止";
window.requestAnimationFrame(animate);
}
}
// 重来游戏
playAgainBtn.onclick=function(e){
init();
playAgainBtn.style.visibility="hidden";
animateBtn.value="停止";
paused=false;
window.requestAnimationFrame(animate);
}
var paused=true;// 是否暂停
var BL=32; // Block length,边长
var ctx; // 绘图环境
var terrain; // 地形
var hero; // 英雄/主角
var monsterMng; // 怪物管理类
var bombs; // 炸弹数组
var fires; // 火焰数组
var screenMsg; // 显示在屏幕中央的话语
var bonusMng; // 奖励管理类
function init(){
// init Canvas
var canvas=document.getElementById('canvas');
canvas.width =40*BL;
canvas.height=18*BL;
ctx=canvas.getContext('2d');
terrain=new Terrain();
hero=new Hero();
monsterMng=new MonsterMng();
bombs=new Array();
fires=new Array();
bonusMng=new BonusMng();
// 响应键盘事件
canvas.addEventListener('keydown', doKeyDown, true);
canvas.focus();
window.addEventListener('keydown', doKeyDown, true);
};
//------------------------------------
// 响应键盘事件
//------------------------------------
function doKeyDown(e) {
var keyID = e.keyCode ? e.keyCode :e.which;
if(keyID === 38 || keyID === 87) { // up arrow and W
hero.move('up');
e.preventDefault();
}
if(keyID === 39 || keyID === 68) { // right arrow and D
hero.move('right');
e.preventDefault();
}
if(keyID === 40 || keyID === 83) { // down arrow and S
hero.move('down');
e.preventDefault();
}
if(keyID === 37 || keyID === 65) { // left arrow and A
hero.move('left');
e.preventDefault();
}
if(keyID === 32 ) { // SpaceBar
var b=new Bomb(hero.x,hero.y,hero.explosionRange);
bombs.push(b);
//console.log('arrows.length='+arrows.length);
e.preventDefault();
}
}
//------------------------------------
// 更新各实例状态
//------------------------------------
function update(){
monsterMng.move();
var heroX=parseInt(hero.x/BL,10);
var heroY=parseInt(hero.y/BL,10);
// 看英雄碰到怪物没
if(monsterMng.isTouched(heroX,heroY)){
hero.setDead();
}
// 计算爆炸范围
var explosions=[];
for(var i=0;i<fires.length;i++){
var f=fires[i];
if(f.lifeTime>0){
explosions.push(new Point(f.x,f.y));
}
}
// 爆炸对怪物的伤害
monsterMng.makeDeadInRange(explosions);
// 爆炸对英雄的伤害
hero.makeDeadInRange(explosions);
// 奖励
bonusMng.enlargeExplosionRange(hero);
// 让怪物吃奖励提升速度
var aliveMonsters=monsterMng.getAliveMonsters();
bonusMng.raiseSpeed(aliveMonsters);
// 游戏结束条件
if(hero.alive==false){ // 英雄死亡
paused=true;
screenMsg="You lost!";
animateBtn.value="开始";
playAgainBtn.style.visibility="visible";
}
// 游戏胜利条件
if(monsterMng.noMonster() && hero.alive==true){ // 怪物全清除 及 英雄存活
paused=true;
screenMsg="You win!";
animateBtn.value="开始";
playAgainBtn.style.visibility="visible";
}
}
//------------------------------------
// 在ctx里画出各实例
//------------------------------------
function draw(){
// Clear Canvas
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.fillStyle="#ffffff";
ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);
terrain.paint(ctx); // 画地形
bonusMng.paint(ctx);// 画奖励
monsterMng.paint(ctx);
hero.paint(ctx); // 画英雄
// 画炸弹
for(var i=0;i<bombs.length;i++){
var b=bombs[i];
b.paint(ctx);
}
// 画火焰
for(var i=0;i<fires.length;i++){
var b=fires[i];
b.paint(ctx);
}
}
//------------------------------------
// 运行动画
//------------------------------------
function animate(){
if(!paused){
update();
draw();
window.requestAnimationFrame(animate); /// 让浏览器自行决定帧速率
}else{
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font="bold 48px 宋体";
ctx.fillStyle='Red';
ctx.fillText(screenMsg,ctx.canvas.width/2,ctx.canvas.height/2);
}
}
//------------------------------------
// 常规函数:角度得到弧度
//------------------------------------
function getRad(degree){
return degree/180*Math.PI;
}
//------------------------------------------------------------
// 常规函数:得到0到N(包括N)的随机整数
// 如果要得到数组里面的一个要把数组长度减一
//------------------------------------------------------------
function getRndFromZeroToN(n){
return Math.floor(Math.random()*(n+1));
}
//------------------------------------------------------------
// 常规函数:得到lowerLimit到upperlimit(包括端点值)的随机整数
//------------------------------------------------------------
function getRndBetween(lowerLimit,upperlimit){
return Math.floor(Math.random()*(upperlimit-lowerLimit+1))+lowerLimit;
}
//---------------------------------------------------地形类定义开始------------------------------------------------------------------->>
Terrain=function(){
this.files=["road.png", // 0
"tree.png", // 1
"farmerHouse.png", // 2
"twoBuilding.png", // 3
"threeBuilding.png",// 4
"bank.png", // 5
"villa.png", // 6
"blackWhiteRoad.png",]; // 7
/*this.maps=new Array(
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
[0,2,0,0,0],
[0,0,0,3,0],
);*/
this.maps=new Array(
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,1,1,1,1,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,1,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,5,0,0,3,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,3,3,3,3,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,],
[0,1,2,1,2,1,0,1,2,1,0,1,4,4,4,4,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,4,0,0,0,1,0,],
[0,1,2,1,2,1,0,1,2,1,0,1,5,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,4,0,0,0,1,0,],
[0,1,2,1,2,1,0,1,2,1,0,1,4,4,4,4,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,6,1,6,1,6,0,],
[0,1,2,1,2,1,0,1,2,1,0,6,0,0,0,0,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,6,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,0,5,0,0,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,6,0,],
[0,1,2,1,2,1,0,1,2,1,0,6,0,0,0,0,1,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,6,1,6,1,6,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,1,6,1,6,1,0,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
[0,1,2,1,2,1,0,1,2,1,6,1,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,],
[0,1,2,1,2,1,0,1,2,1,6,1,0,4,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,3,0,0,],
[0,1,2,1,2,1,0,1,2,1,6,1,0,0,0,4,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,],
[0,1,2,1,2,1,0,1,2,1,6,1,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
[0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,],
[0,1,2,1,2,1,0,1,2,1,0,2,2,2,0,2,2,2,],
[0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,],
[0,1,2,1,2,1,0,1,2,1,0,2,0,0,0,0,0,2,],
);
}
Terrain.prototype={
files:[],
maps:[],
// method
paint:function(ctx){
for(var i=0;i<this.maps.length;i++){
var arr=this.maps[i];
for(var j=0;j<arr.length;j++){
var value=arr[j];
var img=new Image();
img.src=this.files[value];
ctx.drawImage(img,i*BL,j*BL);
}
}
},
// 是否有障碍
hasObstacle:function(i,j){
if(i<0 || i>=this.maps.length){
return true;
}
var arr=this.maps[i];
if(j<0 || j>=arr.length){
return true;
}
var value=arr[j];
if(value==0){
return false;
}else{
return true;
}
},
getValue:function(i,j){
if(i<0 || i>=this.maps.length){
return undefined;
}
var arr=this.maps[i];
if(j<0 || j>=arr.length){
return undefined;
}
var value=arr[j];
return value;
},
setValue:function(i,j,value){
if(i<0 || i>=this.maps.length){
return undefined;
}
var arr=this.maps[i];
if(j<0 || j>=arr.length){
return undefined;
}
arr[j]=value;
},
// 取得空点数组
getEmptyPoints:function(count){
var allEmptyPoints=[];
for(var i=0;i<this.maps.length;i++){
var arr=this.maps[i];
for(var j=0;j<arr.length;j++){
var value=arr[j];
if(value==0){
allEmptyPoints.push(new Point(i*BL,j*BL));
}
}
}
var retval=[];
//var n=allEmptyPoints.length;
//console.log("allEmptyPoints.length="+allEmptyPoints.length);
for(var i=0;i<count;i++){
var seed=this.getRndFromZeroToN(allEmptyPoints.length-1);
var p=allEmptyPoints[seed];
retval.push(p);
}
return retval;
},
//------------------------------------------------------------
// 得到0到N(包括N)的随机整数
// 如果要得到数组里面的一个要把数组长度减一
//------------------------------------------------------------
getRndFromZeroToN:function(n){
return Math.floor(Math.random()*(n+1));
},
}
//---------------------------------------------------地形类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------英雄类定义开始------------------------------------------------------------------->>
Hero=function(){
this.pngs=[
{left:0,top:10,width:40,height:40},
{left:0,top:68,width:40,height:40},
{left:0,top:123,width:40,height:40},
{left:0,top:180,width:40,height:40},
];
}
Hero.prototype={
pngs:[],
x:19*BL,
y:10*BL,
xTo:19*BL,
yTo:10*BL,
step:BL,
direction:'up', // 面向
alive:true, // 是否活着
explosionRange:4, // 爆炸范围
setDead:function(){
//console.log('Hero dead');
this.alive=false;
},
paint:function(ctx){
var img=new Image();
img.src='bowman.png';
var index=0;
if(this.direction=='up'){
index=3;
}
if(this.direction=='down'){
index=0;
}
if(this.direction=='left'){
index=1;
}
if(this.direction=='right'){
index=2;
}
var pos=this.pngs[index];
ctx.drawImage(img,pos.left,pos.top,pos.width,pos.height,this.x,this.y,32,32);
},
move:function(direction){
this.direction=direction;
if(this.direction=='up'){
this.yTo-=this.step;
}
if(this.direction=='down'){
this.yTo+=this.step;
}
if(this.direction=='left'){
this.xTo-=this.step;
}
if(this.direction=='right'){
this.xTo+=this.step;
}
if(terrain.getValue(this.xTo/this.step,this.yTo/this.step)==0){
this.x=this.xTo;
this.y=this.yTo;
}else{
this.xTo=this.x;
this.yTo=this.y;
}
},
// 爆炸火焰一样对英雄造成伤害
makeDeadInRange:function(explosions){
for(var j=0;j<explosions.length;j++){
var point=explosions[j];
// 爆炸范围判断符合实际情况
var xx=Math.pow((this.x-point.x),2);
var yy=Math.pow((this.y-point.y),2);
var distance=Math.sqrt(xx+ yy);
if(distance<BL/2){
this.setDead();
}
}
},
}
//---------------------------------------------------英雄类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------点类定义开始------------------------------------------------------------------->>
Point=function(x,y){
this.x=x;
this.y=y;
}
Point.prototype={
x:0, // 横坐标
y:0, // 纵坐标
}
//---------------------------------------------------点类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------怪物类定义开始------------------------------------------------------------------->>
Monster=function(x,y){
this.x=x;
this.y=y;
steps=[1,2,4,8];
var rndSeed=this.getRndFromZeroToN(steps.length-1);
this.step=steps[rndSeed];
this.files=["monster1.png", // 0
"monster1Dead.png", // 1
"monster2.png", // 2
"monster2Dead.png", // 3
"monster3.png", // 4
"monster3Dead.png", // 5
"monster4.png", // 6
"monster4Dead.png", // 7
];
}
Monster.prototype={
x:0, // 横坐标,乘以32等于真正的横坐标
y:0, // 纵坐标,乘以32等于真正的纵坐标
step:1, // 行走速度
steps:[], // 行走速度数组
direction:'', // 方向
files:[], // 显示图片
alive:true, // 是否活着
thinking:true, // 是否在找路
targetPoint:[],
// 设置死亡
setDead:function(){
this.alive=false;
},
// 成倍提升速度
doubleStep:function(){
this.step*=2;
if(this.step>=512){
this.step=512;
}
},
// 移动
move:function(){
if(this.alive){
if(this.thinking==true){
// 思考下一步往哪里走
targetPoint=null;
// 选择偏向英雄的方向
var xDiff=this.x-hero.x;
var yDiff=this.y-hero.y;
var emptyPlaces=[];
var monsterX=parseInt(this.x/BL,10);
var monsterY=parseInt(this.y/BL,10);
var pointsTmp=[];
if(xDiff>=0){
// 左边方格
for(var i=1;i<=this.step;i++){
var hasObstacle=terrain.hasObstacle(monsterX-i,monsterY);
if(hasObstacle==false){
pointsTmp.push(new Point(monsterX-i,monsterY));
}else{
break;
}
}
if(pointsTmp.length>0){
emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
pointsTmp=[];
}
}
if(xDiff<0){
// 右边方格
for(var i=1;i<=this.step;i++){
var hasObstacle=terrain.hasObstacle(monsterX+i,monsterY);
if(hasObstacle==false){
pointsTmp.push(new Point(monsterX+i,monsterY));
}else{
break;
}
}
if(pointsTmp.length>0){
emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
pointsTmp=[];
}
}
if(yDiff>=0){
// 上边方格
for(var i=1;i<=this.step;i++){
var hasObstacle=terrain.hasObstacle(monsterX,monsterY-i);
if(hasObstacle==false){
pointsTmp.push(new Point(monsterX,monsterY-1));
}else{
break;
}
}
if(pointsTmp.length>0){
emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
pointsTmp=[];
}
}
if(yDiff<0){
// 下边方格
for(var i=1;i<=this.step;i++){
var hasObstacle=terrain.hasObstacle(monsterX,monsterY+i);
if(hasObstacle==false){
pointsTmp.push(new Point(monsterX,monsterY+1));
}else{
break;
}
}
if(pointsTmp.length>0){
emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
pointsTmp=[];
}
}
// 下面是随机选择方向
var rndSeed=this.getRndFromZeroToN(emptyPlaces.length-1);
this.targetPoint=emptyPlaces[rndSeed];
//console.log(targetPoint);
if(this.targetPoint!=null){
//console.log(this.targetPoint);
this.thinking=false;
}
}else{
// 思考好了就走
var targetX=this.targetPoint.x*BL;
var targetY=this.targetPoint.y*BL;
//console.log(this.targetPoint,this.x,this.y);
if(targetX==this.x && targetY<this.y){
this.y-=this.step;
}
if(targetX==this.x && targetY>this.y){
this.y+=this.step;
}
if(targetX<this.x && targetY==this.y){
this.x-=this.step;
}
if(targetX>this.x && targetY==this.y){
this.x+=this.step;
}
if(targetX==this.x && targetY==this.y){
this.thinking=true;
}
}
}
},
//------------------------------------------------------------
// 得到0到N(包括N)的随机整数
// 如果要得到数组里面的一个要把数组长度减一
//------------------------------------------------------------
getRndFromZeroToN:function(n){
return Math.floor(Math.random()*(n+1));
},
// 画怪物
paint:function(ctx){
var img=new Image();
if(this.alive==true && this.step==1){
img.src=this.files[0];
}
if(this.alive==false && this.step==1){
img.src=this.files[1];
}
if(this.alive==true && this.step==2){
img.src=this.files[2];
}
if(this.alive==false && this.step==2){
img.src=this.files[3];
}
if(this.alive==true && this.step==4){
img.src=this.files[4];
}
if(this.alive==false && this.step==4){
img.src=this.files[5];
}
if(this.alive==true && this.step>=8){
img.src=this.files[6];
}
if(this.alive==false && this.step>=8){
img.src=this.files[7];
}
ctx.drawImage(img,this.x,this.y);
},
}
//---------------------------------------------------怪物类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------怪物管理类定义开始------------------------------------------------------------------->>
MonsterMng=function(){
this.addMonster(9);
}
MonsterMng.prototype={
monsters:[],
// 生成怪物,测试用
generateMonsters:function(){
this.monsters=new Array();
this.monsters.push(new Monster(1*BL,1*BL,3));
this.monsters.push(new Monster(1*BL,10*BL,3));
this.monsters.push(new Monster(16*BL,16*BL,2));
this.monsters.push(new Monster(10*BL,13*BL,5));
this.monsters.push(new Monster(10*BL,10*BL,6));
this.monsters.push(new Monster(24*BL,1*BL,1));
},
// 画怪物
paint:function(ctx){
for(var i=0;i<this.monsters.length;i++){
var m=this.monsters[i];
m.paint(ctx);
}
// 写怪物数目
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.font="bold 24px 宋体";
ctx.fillStyle='maroon';
ctx.fillText("Monsters:"+this.getAliveMonsterNum(),ctx.canvas.width-5*BL,BL/2);
},
// 让怪物移动
move:function(ctx){
for(var i=0;i<this.monsters.length;i++){
var m=this.monsters[i];
m.move();
}
},
// 得到活着的怪兽数量
getAliveMonsterNum:function(){
var count=0;
for(var i=0;i<this.monsters.length;i++){
var m=this.monsters[i];
if(m.alive){
count++;
}
}
return count;
},
// 得到活着的怪兽数组
getAliveMonsters:function(ctx){
var arr=[];
for(var i=0;i<this.monsters.length;i++){
var m=this.monsters[i];
if(m.alive==true){
arr.push(m);
}
}
return arr;
},
// 判断怪物还是否存在
hasMonster:function(){
var count=this.getAliveMonsterNum();
return count==0;
},
// 看英雄是否碰到怪物
isTouched:function(heroX,heroY){
for(var i=0;i<this.monsters.length;i++){
var monster=this.monsters[i];
var monsterX=parseInt(monster.x/BL,10);
var monsterY=parseInt(monster.y/BL,10);
//console.log('isTouched',heroX,heroY,monsterX,monsterY);
if(heroX==monsterX && heroY==monsterY && monster.alive==true){
//console.log('touched',heroX,heroY,monsterX,monsterY);
return true;
}
}
return false;
},
// 看有没有怪物了
noMonster:function(heroX,heroY){
var count=0;
for(var i=0;i<this.monsters.length;i++){
var monster=this.monsters[i];
if(monster.alive){
count++;
}
}
return count==0;
},
// 将爆炸范围内的怪物致死
makeDeadInRange:function(explosions){
//console.log(explosions);
for(var i=0;i<this.monsters.length;i++){
var monster=this.monsters[i];
if(monster.alive){
for(var j=0;j<explosions.length;j++){
var point=explosions[j];
//console.log(monster.x,monster.y,point.x,point.y);
/*if(monster.x==point.x && monster.y==point.y){ // 精确判断不符合实际情况
monster.setDead();
}*/
// 范围判断符合实际情况
var xx=Math.pow((monster.x-point.x),2);
var yy=Math.pow((monster.y-point.y),2);
var distance=Math.sqrt(xx+ yy);
if(distance<BL/2){
monster.setDead();
}
}
}
}
},
// 添加怪物
addMonster:function(n){
this.monsters=new Array();
var points=terrain.getEmptyPoints(n);
for(var i=0;i<points.length;i++){
var p=points[i];
this.monsters.push(new Monster(p.x,p.y));
//this.bonusArr.push(new Bonus(p.x,p.y));
}
console.log(this.monsters);
},
}
//---------------------------------------------------怪物管理类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------炸弹类定义开始------------------------------------------------------------------->>
Bomb=function(x,y,explosionRange){
this.x=x;
this.y=y;
// 放下炸弹,让其起障碍作用
var bombX=parseInt(this.x/BL,10);
var bombY=parseInt(this.y/BL,10);
terrain.setValue(bombX,bombY,7);
this.explosionRange=explosionRange;
this.files=['bomb1.png','bomb2.png','bomb3.png','bomb4.png'];
}
Bomb.prototype={
x:0, // 横坐标
y:0, // 纵坐标
files:[], // 表现炸弹的图片
level:0, // 等级,越高越解决爆炸
exploded:false, // 是否爆炸
explosionRange:1,// 爆炸范围
// 画炸弹
paint:function(ctx){
if(this.exploded==false){
var img=new Image();
this.level+=0.75;
if(this.level<20){
img.src=this.files[0];
}else if(this.level>=20 && this.level<40){
img.src=this.files[1];
}else if(this.level>=40 && this.level<60){
img.src=this.files[2];
}else if(this.level>=60 && this.level<80){
img.src=this.files[3];
}else if(this.level>=80 && this.level<100){
img.src=this.files[4];
}else if(this.level>=100){
// 起爆
this.exploded=true;
// 爆炸完毕,让其恢复通行
var bombX=parseInt(this.x/BL,10);
var bombY=parseInt(this.y/BL,10);
terrain.setValue(bombX,bombY,0);
// 创建火焰对象
this.makeFire();
}
ctx.drawImage(img,this.x,this.y);
}
},
// 制造火焰
makeFire:function(){
var emptyPlaces=[];
var bombX=parseInt(this.x/BL,10);
var bombY=parseInt(this.y/BL,10);
//console.log(bombX,bombY);
// Left
for(var i=1;i<=this.explosionRange;i++){
var hasObstacle=terrain.hasObstacle(bombX-i,bombY);
if(hasObstacle==false){
emptyPlaces.push(new Point(bombX-i,bombY));
}else{
break;
}
}
// Right
for(var i=1;i<=this.explosionRange;i++){
var hasObstacle=terrain.hasObstacle(bombX+i,bombY);
if(hasObstacle==false){
emptyPlaces.push(new Point(bombX+i,bombY));
}else{
break;
}
}
// Up
for(var i=1;i<=this.explosionRange;i++){
var hasObstacle=terrain.hasObstacle(bombX,bombY-i);
if(hasObstacle==false){
emptyPlaces.push(new Point(bombX,bombY-i));
}else{
break;
}
}
// down
for(var i=1;i<=this.explosionRange;i++){
var hasObstacle=terrain.hasObstacle(bombX,bombY+i);
if(hasObstacle==false){
emptyPlaces.push(new Point(bombX,bombY+i));
}else{
break;
}
}
// Center
var hasObstacle=terrain.hasObstacle(bombX,bombY);
if(hasObstacle==false){
emptyPlaces.push(new Point(bombX,bombY));
}
//console.log(emptyPlaces);
// 添加火焰对象
for(var i=0;i<emptyPlaces.length;i++){
var p=emptyPlaces[i];
var f=new Fire(p.x*BL,p.y*BL);
fires.push(f);
}
},
}
//---------------------------------------------------炸弹类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------火焰类定义开始------------------------------------------------------------------->>
Fire=function(x,y){
this.x=x;
this.y=y;
this.files=['fire1.png','fire2.png'];
}
Fire.prototype={
x:0, // 横坐标
y:0, // 纵坐标
files:[], // 火焰图片
lifeTime:50, // 显示时间
paint:function(ctx){
if(this.lifeTime>0){
this.lifeTime--;
var img=new Image();
if(this.lifeTime>25){
img.src=this.files[0];
}else{
img.src=this.files[1];
}
ctx.drawImage(img,this.x,this.y);
}
},
}
//---------------------------------------------------火焰类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------奖励类定义开始------------------------------------------------------------------->>
Bonus=function(x,y){
this.x=x;
this.y=y;
this.files=['bonus.png'];
}
Bonus.prototype={
x:0, // 横坐标
y:0, // 纵坐标
files:[], // 图片
used:false, // 是否使用过
paint:function(ctx){
if(this.used==false){
var img=new Image();
img.src=this.files[0];
ctx.drawImage(img,this.x,this.y);
}
},
}
//---------------------------------------------------奖励类定义结束-------------------------------------------------------------------<<
//---------------------------------------------------奖励管理类定义开始------------------------------------------------------------------->>
BonusMng=function(x,y){
this.bonusArr=new Array();
this.addBonus(2);
}
BonusMng.prototype={
bonusArr:[], // 奖励数组
paint:function(ctx){
for(var i=0;i<this.bonusArr.length;i++){
var b=this.bonusArr[i];
b.paint(ctx);
}
},
// 提升英雄的爆炸范围
enlargeExplosionRange:function(hero){
for(var i=0;i<this.bonusArr.length;i++){
var b=this.bonusArr[i];
if(b.used==false && b.x==hero.x && b.y==hero.y){
hero.explosionRange+=1;
b.used=true;
break;
}
}
this.refresh();
},
// 提升怪物速度
raiseSpeed:function(monsters){
for(var i=0;i<monsters.length;i++){
var monster=monsters[i];
if(monster.alive==true){
for(var j=0;j<this.bonusArr.length;j++){
var b=this.bonusArr[j];
if(b.used==false && b.x==monster.x && b.y==monster.y){
monster.doubleStep();
b.used=true;
console.log(this.bonusArr);
}
}
}
}
this.refresh();
},
// 更新bonus
refresh:function(){
var count=0;
for(var i=0;i<this.bonusArr.length;i++){
var b=this.bonusArr[i];
if(b.used==false){
count++;// 统计未被使用的bonus个数
}
}
if(count==0){
this.addBonus(1);// 如果没有bonus了再动态加几个
}
},
addBonus:function(n){
var points=terrain.getEmptyPoints(n);
for(var i=0;i<points.length;i++){
var p=points[i];
this.bonusArr.push(new Bonus(p.x,p.y));
}
},
}
//---------------------------------------------------奖励管理类定义结束-------------------------------------------------------------------<<
//-->
</script>
2019年3月9日21点28分 (PS:今天在博客园的排名从六百多上升到四百多,不知怎么回事)
1.04版 让怪物吃奖励,改进了怪物寻路算法。2019年3月11日11点23分
[Canvas]Bombman v1.04的更多相关文章
- [Canvas]Bombman v1.00
爆破小人Canvas版,请点此下载,并用浏览器打开试玩. 图例: 源码: <!DOCTYPE html> <html lang="utf-8"> <m ...
- T6跨账套辅助工具[v1.04]
[v1.03] 增加自定义报表,用户可以自行设置报表所打开的数据表,然后设置查询条件 [v1.04]更改单据显示样式,直接以用友打印预览的方式显示,允许用自定义显示,打印样式 下图为新的显示样式 下图 ...
- HTML5用canvas绘制五星红旗
在HTML5一览中,我们提到html 5被冠以很多高帽,其中最高的一顶.备受争议的就是"Flash杀手".IT评论界老喜欢用这个词了,杀手无处不在.不管是不是杀手,HTML 5引进 ...
- 【原创】使用HTML5+canvas+JavaScript开发的原生中国象棋游戏及源码分享
目前已经实现的功能: V1.0 : 实现棋子的布局,画布及游戏场景的初始化V2.0 : 实现棋子的颜色改变V3.0 :实现所有象棋的走棋规则V4.0 : 实现所有棋子的吃子功能 GItHub源码下载地 ...
- 【JavaScript游戏开发】使用HTML5 canvas开发的网页版中国象棋项目
//V1.0 : 实现棋子的布局,画布及游戏场景的初始化 //V2.0 : 实现棋子的颜色改变 //V3.0 :实现所有象棋的走棋规则 //V4.0 : 实现所有棋子的吃子功能 完整的项目源码已经开源 ...
- git代码库误操作还原记录
先做一些前情提要: 我们项目使用git作为代码管理,同时为了操作更方便,安装了乌龟git(tortoiseGit)工具.以下几乎所有操作都是在乌龟git上进行. 我们的项目是分阶段完成的,在完成上一阶 ...
- snmp数据包分析
今天看了一下snmp数据包的报文格式,用wireshark抓了两个数据包来分析. 先说说snmp get-request的书报包格式吧,get-next-request,get-response,se ...
- Centos6.5搭建bugzilla
一.安装httpd. mod_ssl. mysql-server . mysql .php-mysql . gcc . perl* . mod-perl-devel [root@localhost ~ ...
- kubernetes入门(03)kubernetes的基本概念
一.Pod 在Kubernetes集群中,Pod是创建.部署和调度的基本单位.一个Pod代表着集群中运行的一个进程,它内部封装了一个或多个应用的容器.在同一个Pod内部,多个容器共享存储.网络IP,以 ...
随机推荐
- python 全栈开发,Day36(作业讲解(大文件下载以及进度条展示),socket的更多方法介绍,验证客户端链接的合法性hmac,socketserver)
先来回顾一下昨天的内容 黏包现象粘包现象的成因 : tcp协议的特点 面向流的 为了保证可靠传输 所以有很多优化的机制 无边界 所有在连接建立的基础上传递的数据之间没有界限 收发消息很有可能不完全相 ...
- Hibernate之关联关系映射(一对一主键映射和一对一外键映射)
1:Hibernate的关联关系映射的一对一外键映射: 1.1:第一首先引包,省略 1.2:第二创建实体类: 这里使用用户信息和身份证信息的关系,用户的主键编号既可以做身份证信息的主键又可以做身份证信 ...
- [转] h5上传视频或文件编写
Html5 finally solves an age old problem of being able to upload files while also showing the upload ...
- BZOJ2287【POJ Challenge】消失之物
题解: 1.以前见过类似的,可以cdq分治 当l=r时就是还有一个剩余 这样时间是nmlogn的 空间是mlogn 2.首先我们可以dp出表示出j的方案数 令g[i][j]表示不能选i,表示出j的方案 ...
- Codeforces 1102F Elongated Matrix 状压dp
Elongated Matrix 预处理一下两两之间的最小值, 然后直接dp. #include<bits/stdc++.h> #define LL long long #define f ...
- BZOJ1592 POJ3666 [Usaco2008 Feb]Making the Grade 路面修整 左偏树 可并堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ3666 题目传送门 - BZOJ1592 题意概括 整条路被分成了N段,N个整数A_1, ... , ...
- POJ2065 SETI 高斯消元
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ2065 题意概括 多组数据,首先输入一个T表示数据组数,然后,每次输入一个质数,表示模数,然后,给出一 ...
- Python String 方法详解
官网文档地址:https://docs.python.org/3/library/stdtypes.html#string-methods 官网 公号:软测小生ruancexiaosheng 文档里的 ...
- CentOS下生成密钥对(公钥、私钥)
1.公钥.私钥简述: 假设数据传输方A向数据接收方B传输数据(以A为服务器,B为客户端为例).现在B有一对密钥对(公钥和私钥),B将公钥发送给A,A通过公钥加密后将数据传给B,B收到数据后利用手里的私 ...
- abstract class和interface有什么区别?
含有abstract修饰符的class即为抽象类,abstract 类不能创建的实例对象.含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必 ...