[Canvas]Bowman

试玩请点此下载并用浏览器打开index.html

这个游戏是弓箭射击敌人,用方向键移动人物,空格键发射箭枝。

图例:

代码:

<!DOCTYPE html>
<html lang="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<head>
     <title>弓箭手打怪物 19.3.8 9:23 by:逆火狂飙 horn19782016@163.com</title>
     
     <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='开始'/>
        </div>
     
        <canvas id="canvas" width="160px" height="160px" >
            出现文字表示你的浏览器不支持HTML5
        </canvas>
     </body>
</html>
<script type="text/javascript">
<!--
var paused=true;
animateBtn.onclick=function(e){
    paused=! paused;
    
    if(paused){
        animateBtn.value="开始";    

    }else{    
        animateBtn.value="停止";
        window.requestAnimationFrame(animate); 
    }
}

var BL=32;// Block length,边长
var ctx;// 绘图环境
var terrain;// 地形
var hero;// 英雄/主角
var arrows;// 箭矢数组
var monsterMng;// 怪物管理者

//------------------------------------
//  初始化函数
//------------------------------------
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();
    arrows=new Array();
    monsterMng=new MonsterMng();
        
    // 响应键盘事件
    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 a=new Arrow(hero.x+BL/2,hero.y+BL/2,hero.direction);
        arrows.push(a);
        //console.log('arrows.length='+arrows.length);
        e.preventDefault();
    }
}

//------------------------------------
// 更新各实例状态
//------------------------------------
function update(){
    for(var i=arrows.length-1;i>-1;i--){
        var arrow=arrows[i];
        var x=parseInt(arrow.x / 32,10);
        var y=parseInt(arrow.y / 32,10);

        // 箭矢射中怪物所在
        if(monsterMng.shoot(x,y)==true){
            //monsterMng.setValue(x,y,2);
            arrows.splice(i,1);
            break;
        }
        
        // 箭矢射中树木房屋等障碍物
        if(terrain.getValue(x,y)!=0){
            arrows.splice(i,1);
        }
    }
    
    var heroX=parseInt(hero.x/BL,10);
    var heroY=parseInt(hero.y/BL,10);
        
    // 看英雄碰到怪物没
    if(monsterMng.isTouched(heroX,heroY)){
        hero.setDead();
    }
    
    // 游戏结束条件
    if(hero.alive==false){ // 英雄死亡
        alert("You lost!");
        init();
    }
    
    if(monsterMng.noMonster()){ // 怪物全清除
        alert("You Win!");
        init();
    }
}

//------------------------------------
// 在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);    // 画地形
    
    // 画怪物
    monsterMng.paint(ctx);
    
    // 画箭矢
    for(var i=0;i<arrows.length;i++){
        var arrow=arrows[i];
        arrow.paint(ctx);
    }
    
    hero.paint(ctx);    // 画英雄
}

//------------------------------------
// 运行动画
//------------------------------------
function animate(){
    if(!paused){
        update();
        draw();
        window.requestAnimationFrame(animate); /// 让浏览器自行决定帧速率
    }
}

//------------------------------------
//  常规函数:角度得到弧度
//------------------------------------
function getRad(degree){
    return degree/180*Math.PI;
}

//------------------------------------
//生成从minNum到maxNum的随机数
//------------------------------------
function randomNum(minNum,maxNum){ 
    switch(arguments.length){ 
        case 1: 
            return parseInt(Math.random()*minNum+1,10); 
        break; 
        case 2: 
            return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10); 
        break; 
            default: 
                return 0; 
            break; 
    } 
}

//---------------------------------------------------地形类定义开始------------------------------------------------------------------->>
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
    
    /*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(
        [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,],
        [0,0,1,1,1,1,1,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,0,],
        [0,0,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,],
        [0,0,0,2,0,0,2,0,0,0,0,0,0,3,0,0,0,0,],
        [0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
        [0,2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,5,0,0,6,6,0,0,0,],
        [0,1,1,1,1,1,1,1,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,],
        [0,0,1,1,1,1,1,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,0,],
        [0,0,2,2,2,2,2,2,2,0,0,1,0,0,0,0,3,0,],
        [0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,3,0,],
        [0,0,0,2,0,0,2,0,0,0,0,0,0,3,0,0,3,0,],
        [0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
        [0,2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,4,0,],
        [0,0,0,0,0,0,0,0,0,0,5,0,0,6,6,0,4,0,],
        [0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,4,0,],
        [0,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,0,0,0,0,0,0,5,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,],
        [0,0,2,2,2,2,2,2,2,0,0,0,0,0,0,0,5,0,],
        [0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,],
        [0,0,0,2,0,0,2,0,0,0,0,0,2,3,0,0,6,0,],
        [0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,6,0,],
        [0,2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,6,0,],
        [0,0,0,0,0,0,0,0,0,0,5,0,0,6,6,0,0,0,],
        [0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,],
        [0,0,1,1,1,1,1,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,0,],
        [0,0,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0,],
        [0,0,0,2,0,0,2,0,0,0,0,0,0,3,0,0,0,0,],
        [0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
        [0,2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,5,0,0,6,6,0,0,0,],
        [0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,],
    );
}
Terrain.prototype={
    files:[],
    maps:[],
    
    // 在ctx画自己
    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;
    },
}
//---------------------------------------------------地形类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------英雄类定义开始------------------------------------------------------------------->>
Hero=function(){
    this.pngs=[
        {left:0,top:10,40,height:40},
        {left:0,top:68,40,height:40},
        {left:0,top:123,40,height:40},
        {left:0,top:180,40,height:40},
    ];
}
Hero.prototype={
    pngs:[],
    x:160,
    y:160,
    xTo:160,
    yTo:160,
    step:32,
    direction:'up',
    alive:true,
    
    setDead:function(){
        //console.log('Hero dead');
        this.alive=false;
    },
    
    paint:function(ctx){
        if(this.alive){
            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);
        }else{
            var img=new Image();
            img.src='skull.png';
            ctx.drawImage(img,this.x,this.y);
        }
    },
    
    move:function(direction){
        if(this.alive){
            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;
            }
        }
    }
}
//---------------------------------------------------英雄类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------怪物类定义开始------------------------------------------------------------------->>
Monster=function(x,y,step,direction){
    this.x=x;
    this.y=y;
    this.step=step;
    this.direction=direction;
    
    this.files=["monster.png",               //  0
                "monsterDead.png",            //  1
                "monsterUpdated.png",        //  2
                "monsterUpdatedDead.png",];    //  3
}
Monster.prototype={
    x:0,            // 横坐标,乘以32等于真正的横坐标
    y:0,            // 纵坐标,乘以32等于真正的纵坐标
    step:1,            // 行走速度
    direction:'',    // 方向
    files:[],        // 显示图片
    alive:true,        // 是否活着
    
    // 步长加倍
    doubleStep:function(){
        this.step*=2;
    },
    
    // 被射中了
    beShooted:function(){
        this.alive=false;
    },
    
    // 移动
    move:function(){
        if(this.alive){
            
        }
    },
    
    turnAround:function(){
        var arr=['left','right','up','down',];
        var newArr=[];
        for(var i=0;i<arr.length;i++){
            if(this.direction!=arr[i]){
                newArr.push(arr[i]);
            }
        }
        console.log("newArr="+newArr);
        var index=randomNum(0,newArr.length-1);
        console.log("index="+index);
        this.direction=newArr[index];
    },
    
    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];
        }
        
        ctx.drawImage(img,this.x,this.y);
    },
    
}
//---------------------------------------------------怪物类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------怪物管理类定义开始------------------------------------------------------------------->>
MonsterMng=function(){
    this.generateMonsters();
}
MonsterMng.prototype={
    monsters:[],
    
    // 生成怪物
    generateMonsters:function(){
        this.monsters=new Array();
        this.monsters.push(new Monster(1*BL,1*BL,1,"right"));
        this.monsters.push(new Monster(1*BL,10*BL,1,"right"));
        this.monsters.push(new Monster(16*BL,16*BL,1,"right"));
        this.monsters.push(new Monster(10*BL,13*BL,1,"right"));
        this.monsters.push(new Monster(10*BL,10*BL,1,"right"));
    },
    
    // 画怪物
    paint:function(ctx){
        for(var i=0;i<this.monsters.length;i++){
            var m=this.monsters[i];
            m.paint(ctx);
        }
    },
    
    // 判断怪物还是否存在
    hasMonster:function(){
        return true;
    },

    // 用箭射击怪物
    shoot:function(arrowX,arrowY){
        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(arrowX,arrowY,monsterX,monsterY);
            if(monsterX==arrowX && monsterY==arrowY && monster.alive==true){
                monster.beShooted();
                //console.log('shooted',arrowX,arrowY,monsterX,monsterY);
                return true;
            }
        }
        
        return false;
    },
    
    // 看英雄是否碰到怪物
    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;
    },
}
//---------------------------------------------------怪物管理类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------箭矢类定义开始------------------------------------------------------------------->>
Arrow=function(x,y,direction){
    this.x=x;
    this.y=y;
    this.direction=direction;
}
Arrow.prototype={
    x:0,
    y:0,
    velocity:1,// 飞行速度
    len:16,// 箭杆长
    direction:'',
    
    // method
    paint:function(ctx){
        var x1=this.x,y1=this.y;
        var leaf=4;//箭头箭羽长
        var x2,y2,x3,y3;
        var angle=getRad(15);
    
        if(this.direction=='up'){
            this.y-=this.velocity;
            y1=this.y-this.len;
            x1=this.x;
            
            y2=y1+leaf*Math.cos(angle);
            x2=x1-leaf*Math.sin(angle);
            y3=y2;
            x3=x1+leaf*Math.sin(angle);
        }
        if(this.direction=='down'){
            this.y+=this.velocity;
            y1=this.y+this.len;
            x1=this.x;
            y2=y1-leaf*Math.cos(angle);
            x2=x1-leaf*Math.sin(angle);
            y3=y2;
            x3=x1+leaf*Math.sin(angle);
        }
        if(this.direction=='left'){
            this.x-=this.velocity;
            x1=this.x-this.len;
            y1=this.y;
            
            x2=x1+leaf*Math.cos(angle);
            y2=y1-leaf*Math.sin(angle);
            x3=x2;
            y3=y1+leaf*Math.sin(angle);
        }
        if(this.direction=='right'){
            this.x+=this.velocity;
            x1=this.x+this.len;
            y1=this.y;
            
            x2=x1-leaf*Math.cos(angle);
            y2=y1-leaf*Math.sin(angle);
            x3=x2;
            y3=y1+leaf*Math.sin(angle);
        }

        ctx.beginPath();
        ctx.lineWidth = 2;
        ctx.strokeStyle = "#ff0000";
        ctx.lineTo(this.x,this.y);
        ctx.lineTo(x1,y1);
        ctx.lineTo(x2,y2);
        ctx.lineTo(x3,y3);
        ctx.lineTo(x1,y1);

        ctx.stroke();
        ctx.closePath();
    },
}
//---------------------------------------------------箭矢类定义结束-------------------------------------------------------------------<<
//-->
</script>

2019年3月8日12点32分

原文地址:https://www.cnblogs.com/heyang78/p/10495066.html