吃豆人遊戲開發

hebtu666發表於2019-01-17

這個文章怎麼突然這麼多人看啊。請大家多關注我其他文章啊

 

 

html:


<html>
<head>
<meta charset="utf8">
<title>html5 pacman吃豆人遊戲程式碼</title>

<style>
	body{background-color: #292929}
	*{padding:0;margin:0;}
	.wrapper{
		width: 960px;
		margin:0 auto;
		line-height:36px;
		text-align:center;
		color:#999;
	}
	canvas{display:block;background: #000;}
	.mod-botton{
		height: 32px;
		padding: 15px 0;
		text-align: center;
	}
</style>

</head>
<body><script src="/demos/googlegg.js"></script>

<div class="wrapper">
	<canvas id="canvas" width="960" height="640">不支援畫布</canvas>
	<p>按[空格]暫停或繼續</p>		
</div>
<script src="game.js"></script>
<script src="index.js"></script>

<div style="text-align:center;margin:50px 0; font:normal 14px/24px 'MicroSoft YaHei';">
</div>
</body>
</html>

game.js:

'use strict';
/*
* 小型遊戲引擎
*/

// requestAnimationFrame polyfill
if (!Date.now)
Date.now = function() { return new Date().getTime(); };
(function() {
    'use strict';
    var vendors = ['webkit', 'moz'];
    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
        var vp = vendors[i];
        window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
        window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame'] || window[vp+'CancelRequestAnimationFrame']);
    }
    if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
    || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
        var lastTime = 0;
        window.requestAnimationFrame = function(callback) {
            var now = Date.now();
            var nextTime = Math.max(lastTime + 16, now);
            return setTimeout(function() { callback(lastTime = nextTime); },
            nextTime - now);
        };
        window.cancelAnimationFrame = clearTimeout;
    }
}());

function Game(id,params){
    var _ = this;
    var settings = {
        width:960,						//畫布寬度
        height:640						//畫布高度
    };
    var _extend = function(target,settings,params){
        params = params||{};
        for(var i in settings){
            target[i] = params[i]||settings[i];
        }
        return target;
    };
    _extend(_,settings,params);
    var $canvas = document.getElementById(id);
    $canvas.width = _.width;
    $canvas.height = _.height;
    var _context = $canvas.getContext('2d');	//畫布上下文環境
    var _stages = [];							//佈景物件佇列
    var _events = {};							//事件集合
    var _index=0,								//當前佈景索引
        _hander;  								//幀動畫控制
    //活動物件構造
    var Item = function(params){
        this._params = params||{};
        this._id = 0;               //標誌符
        this._stage = null;         //與所屬佈景繫結
        this._settings = {
            x:0,					//位置座標:橫座標
            y:0,					//位置座標:縱座標
            width:20,				//寬
            height:20,				//高
            type:0,					//物件型別,0表示普通物件(不與地圖繫結),1表示玩家控制物件,2表示程式控制物件
            color:'#F00',			//標識顏色
            status:1,				//物件狀態,0表示未啟用/結束,1表示正常,2表示暫停,3表示臨時,4表示異常
            orientation:0,			//當前定位方向,0表示右,1表示下,2表示左,3表示上
            speed:0,				//移動速度
            //地圖相關
            location:null,			//定位地圖,Map物件
            coord:null,				//如果物件與地圖繫結,需設定地圖座標;若不繫結,則設定位置座標
            path:[],				//NPC自動行走的路徑
            vector:null,			//目標座標
            //佈局相關
            frames:1,				//速度等級,內部計算器times多少幀變化一次
            times:0,				//重新整理畫布計數(用於迴圈動畫狀態判斷)
            timeout:0,				//倒數計時(用於過程動畫狀態判斷)
            control:{},				//控制快取,到達定位點時處理
            update:function(){}, 	//更新引數資訊
            draw:function(){}		//繪製
        };
        _extend(this,this._settings,this._params);
    };
    Item.prototype.bind = function(eventType,callback){
        if(!_events[eventType]){
            _events[eventType] = {};
            $canvas.addEventListener(eventType,function(e){
                var position = _.getPosition(e);
                _stages[_index].items.forEach(function(item){
                    if(Math.abs(position.x-item.x)<item.width/2&&Math.abs(position.y-item.y)<item.height/2){
                        var key = 's'+_index+'i'+item._id;
                        if(_events[eventType][key]){
                            _events[eventType][key](e);
                        }
                    }
                });
                e.preventDefault();
            });
        }
        _events[eventType]['s'+this._stage.index+'i'+this._id] = callback.bind(this);  //繫結作用域
    };
    //地圖物件構造器
    var Map = function(params){
        this._params = params||{};
        this._id = 0;               //標誌符
        this._stage = null;         //與所屬佈景繫結
        this._settings = {
            x:0,					//地圖起點座標
            y:0,
            size:20,				//地圖單元的寬度
            data:[],				//地圖資料
            x_length:0,				//二維陣列x軸長度
            y_length:0,				//二維陣列y軸長度
            frames:1,				//速度等級,內部計算器times多少幀變化一次
            times:0,				//重新整理畫布計數(用於迴圈動畫狀態判斷)
            cache:false,    		//是否靜態(如靜態則設定快取)
            update:function(){},	//更新地圖資料
            draw:function(){},		//繪製地圖
        };
        _extend(this,this._settings,this._params);
    };
    //獲取地圖上某點的值
    Map.prototype.get = function(x,y){
        if(this.data[y]&&typeof this.data[y][x]!='undefined'){
            return this.data[y][x];
        }
        return -1;
    };
    //設定地圖上某點的值
    Map.prototype.set = function(x,y,value){
        if(this.data[y]){
            this.data[y][x] = value;
        }
    };
    //地圖座標轉畫布座標
    Map.prototype.coord2position = function(cx,cy){
        return {
            x:this.x+cx*this.size+this.size/2,
            y:this.y+cy*this.size+this.size/2
        };
    };
    //畫布座標轉地圖座標
    Map.prototype.position2coord = function(x,y){
        var fx = Math.abs(x-this.x)%this.size-this.size/2;
        var fy = Math.abs(y-this.y)%this.size-this.size/2;
        return {
            x:Math.floor((x-this.x)/this.size),
            y:Math.floor((y-this.y)/this.size),
            offset:Math.sqrt(fx*fx+fy*fy)
        };
    };
    //定址演算法
    Map.prototype.finder = function(params){
        var defaults = {
            map:null,
            start:{},
            end:{},
            type:'path'
        };
        var options = _extend({},defaults,params);
        if(options.map[options.start.y][options.start.x]||options.map[options.end.y][options.end.x]){ //當起點或終點設定在牆上
            return [];
        }
        var finded = false;
        var result = [];
        var y_length  = options.map.length;
        var x_length = options.map[0].length;
        var steps = [];     //步驟的對映
        for(var y=y_length;y--;){
            steps[y] = new Array(x_length).fill(0);
        }
        var _getValue = function(x,y){  //獲取地圖上的值
            if(options.map[y]&&typeof options.map[y][x]!='undefined'){
                return options.map[y][x];
            }
            return -1;
        };
        var _next = function(to){ //判定是否可走,可走放入列表
            var value = _getValue(to.x,to.y);
            if(value<1){
                if(value==-1){
                    to.x = (to.x+x_length)%x_length;
                    to.y = (to.y+y_length)%y_length;
                    to.change = 1;
                }
                if(!steps[to.y][to.x]){
                    result.push(to);
                }
            }
        };
        var _render = function(list){//找線路
            var new_list = [];
            var next = function(from,to){
                var value = _getValue(to.x,to.y);
                if(value<1){	//當前點是否可以走
                    if(value==-1){
                        to.x = (to.x+x_length)%x_length;
                        to.y = (to.y+y_length)%y_length;
                        to.change = 1;
                    }
                    if(to.x==options.end.x&&to.y==options.end.y){
                        steps[to.y][to.x] = from;
                        finded = true;
                    }else if(!steps[to.y][to.x]){
                        steps[to.y][to.x] = from;
                        new_list.push(to);
                    }
                }
            };
            list.forEach(function(current){
				next(current,{y:current.y+1,x:current.x});
                next(current,{y:current.y,x:current.x+1});
                next(current,{y:current.y-1,x:current.x});
                next(current,{y:current.y,x:current.x-1});
            });
            if(!finded&&new_list.length){
                _render(new_list);
            }
        };
        _render([options.start]);
        if(finded){
            var current=options.end;
            if(options.type=='path'){
                while(current.x!=options.start.x||current.y!=options.start.y){
                    result.unshift(current);
                    current=steps[current.y][current.x];
                }
            }else if(options.type=='next'){
                _next({x:current.x+1,y:current.y});
                _next({x:current.x,y:current.y+1});
                _next({x:current.x-1,y:current.y});
                _next({x:current.x,y:current.y-1});
            }
        }
        return result;
    };
    //佈景物件構造器
    var Stage = function(params){
        this._params = params||{};
        this._settings = {
            index:0,                        //佈景索引
            status:0,						//佈景狀態,0表示未啟用/結束,1表示正常,2表示暫停,3表示臨時,4表示異常
            maps:[],						//地圖佇列
            audio:[],						//音訊資源
            images:[],						//圖片資源
            items:[],						//物件佇列
            timeout:0,						//倒數計時(用於過程動畫狀態判斷)
            update:function(){}				//嗅探,處理佈局下不同物件的相對關係
        };
        _extend(this,this._settings,this._params);
    };
    //新增物件
    Stage.prototype.createItem = function(options){
        var item = new Item(options);
        //動態屬性
        if(item.location){
            var position = item.location.coord2position(item.coord.x,item.coord.y);
            item.x = position.x;
            item.y = position.y;
        }
        //關係繫結
        item._stage = this;
        item._id = this.items.length;
        this.items.push(item);
        return item;
    };
    //重置物體位置
    Stage.prototype.resetItems = function(){
        this.status = 1;
        this.items.forEach(function(item,index){
            _extend(item,item._settings,item._params);
            if(item.location){
                var position = item.location.coord2position(item.coord.x,item.coord.y);
                item.x = position.x;
                item.y = position.y;
            }
        });
    };
    //獲取物件列表
    Stage.prototype.getItemsByType = function(type){
        return this.items.filter(function(item){
            if(item.type==type){
                return item;
            }
        });
    };
    //新增地圖
    Stage.prototype.createMap = function(options){
        var map = new Map(options);
        //動態屬性
        map.data = JSON.parse(JSON.stringify(map._params.data));
        map.y_length = map.data.length;
        map.x_length = map.data[0].length;
        map.imageData = null;
        //關係繫結
        map._stage = this;
        map._id = this.maps.length;
        this.maps.push(map);
        return map;
    };
    //重置地圖
    Stage.prototype.resetMaps = function(){
        this.status = 1;
        this.maps.forEach(function(map){
            _extend(map,map._settings,map._params);
            map.data = JSON.parse(JSON.stringify(map._params.data));
            map.y_length = map.data.length;
            map.x_length = map.data[0].length;
            map.imageData = null;
        });
    };
    //重置
    Stage.prototype.reset = function(){
        _extend(this,this._settings,this._params);
        this.resetItems();
        this.resetMaps();
    };
    //繫結事件
    Stage.prototype.bind = function(eventType,callback){
        if(!_events[eventType]){
            _events[eventType] = {};
            window.addEventListener(eventType,function(e){
                var key = 's' + _index;
                if(_events[eventType][key]){
                    _events[eventType][key](e);
                }
                e.preventDefault();
            });
        }
        _events[eventType]['s'+this.index] = callback.bind(this);	//繫結事件作用域
    };
    //動畫開始
    this.start = function() {
        var f = 0;		//幀數計算
        var fn = function(){
            var stage = _stages[_index];
            _context.clearRect(0,0,_.width,_.height);		//清除畫布
            _context.fillStyle = '#000000';
            _context.fillRect(0,0,_.width,_.height);
            f++;
            if(stage.timeout){
                stage.timeout--;
            }
            if(stage.update()!=false){		            //update返回false,則不繪製
                stage.maps.forEach(function(map){
                    if(!(f%map.frames)){
                        map.times = f/map.frames;		//計數器
                    }
                    if(map.cache){
                        if(!map.imageData){
                            _context.save();
                            map.draw(_context);
                            map.imageData = _context.getImageData(0,0,_.width,_.height);
                            _context.restore();
                        }else{
                            _context.putImageData(map.imageData,0,0);
                        }
                    }else{
                    	map.update();
                        map.draw(_context);
                    }
                });
                stage.items.forEach(function(item){
                    if(!(f%item.frames)){
                        item.times = f/item.frames;		   //計數器
                    }
                    if(stage.status==1&&item.status!=2){  	//物件及佈景狀態都不處於暫停狀態
                        if(item.location){
                            item.coord = item.location.position2coord(item.x,item.y);
                        }
                        if(item.timeout){
                            item.timeout--;
                        }
                        item.update();
                    }
                    item.draw(_context);
                });
            }
            _hander = requestAnimationFrame(fn);
        };
        _hander = requestAnimationFrame(fn);
    };
    //動畫結束
    this.stop = function(){
        _hander&&cancelAnimationFrame(_hander);
    };
    //事件座標
    this.getPosition = function(e){
        var box = $canvas.getBoundingClientRect();
        return {
            x:e.clientX-box.left*(_.width/box.width),
            y:e.clientY-box.top*(_.height/box.height)
        };
    }
    //建立佈景
    this.createStage = function(options){
        var stage = new Stage(options);
        stage.index = _stages.length;
        _stages.push(stage);
        return stage;
    };
    //指定佈景
    this.setStage = function(index){
        _stages[_index].status = 0;
        _index = index;
        _stages[_index].status = 1;
        return _stages[_index];
    };
    //下個佈景
    this.nextStage = function(){
        if(_index<_stages.length-1){
            return this.setStage(++_index);
        }else{
            throw new Error('unfound new stage.');
        }
    };
    //初始化遊戲引擎
    this.init = function(){
        _index = 0;
        this.start();
    };
}

index.js:

//主程式,業務邏輯
(function(){
	var _DATA = [		//地圖資料
		[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
		[1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1],
		[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],
		[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],
		[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,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,1],
		[1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1],
		[1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1],
		[1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1],
		[1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,0,1,1,1,2,2,1,1,1,0,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,0,1,2,2,2,2,2,2,1,0,1,1,0,1,1,1,1,1,1],
		[0,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0],
		[1,1,1,1,1,1,0,1,1,0,1,2,2,2,2,2,2,1,0,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1],
		[1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1],
		[1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1],
		[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],
		[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],
		[1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1],
		[1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1],
		[1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1],
		[1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1],
		[1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1],
		[1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,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,1],
		[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
	],
	_GOODS = {			//能量豆
		'1,3':1,
		'26,3':1,
		'1,23':1,
		'26,23':1
	},
	_COS = [1,0,-1,0],
	_SIN = [0,1,0,-1],
	_COLOR = ['#F00','#F93','#0CF','#F9C'],//紅,橙,
	_LIFE = 3,
	_SCORE = 0;		//得分

	var game = new Game('canvas');
	//啟動頁
	(function(){
		var stage = game.createStage();
		//logo
		stage.createItem({
			x:game.width/2,
			y:game.height*.45,
			width:100,
			height:100,
			frames:3,
			draw:function(context){
				var t = Math.abs(5-this.times%10);
				context.fillStyle = '#FFE600';
				context.beginPath();
				context.arc(this.x,this.y,this.width/2,t*.04*Math.PI,(2-t*.04)*Math.PI,false);
				context.lineTo(this.x,this.y);
				context.closePath();
				context.fill();
				context.fillStyle = '#000';
				context.beginPath();
				context.arc(this.x+5,this.y-27,7,0,2*Math.PI,false);
				context.closePath();
				context.fill();
			}
		});
		//遊戲名
		stage.createItem({
			x:game.width/2,
			y:game.height*.6,
			draw:function(context){
				context.font = 'bold 42px Helvetica';
				context.textAlign = 'center';
				context.textBaseline = 'middle';
				context.fillStyle = '#FFF';
				context.fillText('Pac-Man',this.x,this.y);
			}
		});
		//版權資訊
		stage.createItem({
			x:game.width-12,
			y:game.height-5,
			draw:function(context){
				context.font = '14px Helvetica';
				context.textAlign = 'right';
				context.textBaseline = 'bottom';
				context.fillStyle = '#AAA';
				context.fillText('© passer-by.com',this.x,this.y);
			}
		});
		//事件繫結
		stage.bind('keydown',function(e){
			switch(e.keyCode){
				case 13:
				case 32:
				game.nextStage();
				break;
			}
		});
	})();
	//遊戲主程式
	(function(){
		var stage,map,beans,player,times;
		stage = game.createStage({
			update:function(){
				var stage = this;
				if(stage.status==1){								//場景正常執行
					items.forEach(function(item){
						if(map&&!map.get(item.coord.x,item.coord.y)&&!map.get(player.coord.x,player.coord.y)){
							var dx = item.x-player.x;
							var dy = item.y-player.y;
							if(dx*dx+dy*dy<750&&item.status!=4){		//物體檢測
								if(item.status==3){
									item.status = 4;
									_SCORE += 10;
								}else{
									stage.status = 3;
									stage.timeout = 30;
								}
							}
						}
					});
					if(JSON.stringify(beans.data).indexOf(0)<0){	//當沒有物品的時候,進入結束畫面
						game.nextStage();
					}
				}else if(stage.status==3){		//場景臨時狀態
					if(!stage.timeout){
						_LIFE--;
						if(_LIFE){
							stage.resetItems();
						}else{
							game.nextStage();
							return false;
						}
					}
				}
			}
		});
		//繪製地圖
		map = stage.createMap({
			x:60,
			y:10,
			data:_DATA,
			cache:true,
			draw:function(context){
				context.lineWidth = 2;
				for(var j=0; j<this.y_length; j++){
					for(var i=0; i<this.x_length; i++){
						var value = this.get(i,j);
						if(value){
							var code = [0,0,0,0];
							if(this.get(i+1,j)&&!(this.get(i+1,j-1)&&this.get(i+1,j+1)&&this.get(i,j-1)&&this.get(i,j+1))){
								code[0]=1;
							}
							if(this.get(i,j+1)&&!(this.get(i-1,j+1)&&this.get(i+1,j+1)&&this.get(i-1,j)&&this.get(i+1,j))){
								code[1]=1;
							}
							if(this.get(i-1,j)&&!(this.get(i-1,j-1)&&this.get(i-1,j+1)&&this.get(i,j-1)&&this.get(i,j+1))){
								code[2]=1;
							}
							if(this.get(i,j-1)&&!(this.get(i-1,j-1)&&this.get(i+1,j-1)&&this.get(i-1,j)&&this.get(i+1,j))){
								code[3]=1;
							}
							if(code.indexOf(1)>-1){
								context.strokeStyle=value==2?"#FFF":"#09C";
								var pos = this.coord2position(i,j);
								switch(code.join('')){
									case '1100':
										context.beginPath();
										context.arc(pos.x+this.size/2,pos.y+this.size/2,this.size/2,Math.PI,1.5*Math.PI,false);
										context.stroke();
										context.closePath();
										break;
									case '0110':
										context.beginPath();
										context.arc(pos.x-this.size/2,pos.y+this.size/2,this.size/2,1.5*Math.PI,2*Math.PI,false);
										context.stroke();
										context.closePath();
										break;
									case '0011':
										context.beginPath();
										context.arc(pos.x-this.size/2,pos.y-this.size/2,this.size/2,0,.5*Math.PI,false);
										context.stroke();
										context.closePath();
										break;
									case '1001':
										context.beginPath();
										context.arc(pos.x+this.size/2,pos.y-this.size/2,this.size/2,.5*Math.PI,1*Math.PI,false);
										context.stroke();
										context.closePath();
										break;
									default:
										var dist = this.size/2;
										code.forEach(function(v,index){
											if(v){
												context.beginPath();
												context.moveTo(pos.x,pos.y);
												context.lineTo(pos.x-_COS[index]*dist,pos.y-_SIN[index]*dist);
												context.stroke();
												context.closePath();							
											}
										});
								}
							}
						}
					}
				}
			}
		});
		//物品地圖
		beans = stage.createMap({
			x:60,
			y:10,
			data:_DATA,
			frames:8,
			draw:function(context){
				for(var j=0; j<this.y_length; j++){
					for(var i=0; i<this.x_length; i++){
						if(!this.get(i,j)){
							var pos = this.coord2position(i,j);
							context.fillStyle = "#F5F5DC";
							if(_GOODS[i+','+j]){
								context.beginPath();
								context.arc(pos.x,pos.y,3+this.times%2,0,2*Math.PI,true);
								context.fill();
								context.closePath();
							}else{
								context.fillRect(pos.x-2,pos.y-2,4,4);
							}
						}
					}
				}
			}
		});
		//得分
		stage.createItem({
			x:690,
			y:100,
			draw:function(context){
				context.font = 'bold 28px Helvetica';
				context.textAlign = 'left';
				context.textBaseline = 'bottom';
				context.fillStyle = '#C33';
				context.fillText('SCORE',this.x,this.y);
				context.font = '28px Helvetica';
				context.textAlign = 'left';
				context.textBaseline = 'top';
				context.fillStyle = '#FFF';
				context.fillText(_SCORE,this.x+12,this.y);
			}
		});
		//狀態文字
		stage.createItem({
			x:690,
			y:320,
			frames:25,
			draw:function(context){
				if(stage.status==2&&this.times%2){
					context.font = '24px Helvetica';
					context.textAlign = 'left';
					context.textBaseline = 'center';
					context.fillStyle = '#09F';
					context.fillText('PAUSE',this.x,this.y);
				}
			}
		});
		//生命值
		stage.createItem({
			x:705,
			y:540,
			width:30,
			height:30,
			draw:function(context){
				for(var i=0;i<_LIFE-1;i++){
					var x=this.x+40*i,y=this.y;
					context.fillStyle = '#FFE600';
					context.beginPath();
					context.arc(x,y,this.width/2,.15*Math.PI,-.15*Math.PI,false);
					context.lineTo(x,y);
					context.closePath();
					context.fill();
				}
			}
		});
		//NPC
		for(var i=0;i<4;i++){
			stage.createItem({
				width:30,
				height:30,
				orientation:3,
				color:_COLOR[i],
				location:map,
				coord:{x:12+i,y:14},
				vector:{x:12+i,y:14},
				type:2,
				frames:10,
				speed:1,
				timeout:Math.floor(Math.random()*120),
				update:function(){
					var new_map;
					if(this.status==3&&!this.timeout){
						this.status = 1;
					}
					if(!this.coord.offset){			//到達座標中心時計算
						if(this.status==1){
							if(!this.timeout){		//定時器
								new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0));
								var id = this._id;
								items.forEach(function(item){
									if(item._id!=id&&item.status==1){	//NPC將其它所有還處於正常狀態的NPC當成一堵牆
										new_map[item.coord.y][item.coord.x]=1;
									}
								});
								this.path = map.finder({
									map:new_map,
									start:this.coord,
									end:player.coord
								});
								if(this.path.length){
									this.vector = this.path[0];
								}
							}
						}else if(this.status==3){
							new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0));
							var id = this._id;
							items.forEach(function(item){
								if(item._id!=id){
									new_map[item.coord.y][item.coord.x]=1;
								}
							});
							this.path = map.finder({
								map:new_map,
								start:player.coord,
								end:this.coord,
								type:'next'
							});
							if(this.path.length){
								this.vector = this.path[Math.floor(Math.random()*this.path.length)];
							}
						}else if(this.status==4){
							new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0));
							this.path = map.finder({
								map:new_map,
								start:this.coord,
								end:this._params.coord
							});
							if(this.path.length){
								this.vector = this.path[0];
							}else{
								this.status = 1;
							}
						}
						//是否轉變方向
						if(this.vector.change){
							this.coord.x = this.vector.x;
							this.coord.y = this.vector.y;
							var pos = map.coord2position(this.coord.x,this.coord.y);
							this.x = pos.x;
							this.y = pos.y;
						}
						//方向判定
						if(this.vector.x>this.coord.x){
							this.orientation = 0;
						}else if(this.vector.x<this.coord.x){
							this.orientation = 2;
						}else if(this.vector.y>this.coord.y){
							this.orientation = 1;
						}else if(this.vector.y<this.coord.y){
							this.orientation = 3;
						}
					}
					this.x += this.speed*_COS[this.orientation];
					this.y += this.speed*_SIN[this.orientation];
				},
				draw:function(context){
					var isSick = false;
					if(this.status==3){
						isSick = this.timeout>80||this.times%2?true:false;
					}
					if(this.status!=4){
						context.fillStyle = isSick?'#BABABA':this.color;
						context.beginPath();
						context.arc(this.x,this.y,this.width*.5,0,Math.PI,true);
						switch(this.times%2){
							case 0:
							context.lineTo(this.x-this.width*.5,this.y+this.height*.4);
							context.quadraticCurveTo(this.x-this.width*.4,this.y+this.height*.5,this.x-this.width*.2,this.y+this.height*.3);
							context.quadraticCurveTo(this.x,this.y+this.height*.5,this.x+this.width*.2,this.y+this.height*.3);
							context.quadraticCurveTo(this.x+this.width*.4,this.y+this.height*.5,this.x+this.width*.5,this.y+this.height*.4);
							break;
							case 1:
							context.lineTo(this.x-this.width*.5,this.y+this.height*.3);
							context.quadraticCurveTo(this.x-this.width*.25,this.y+this.height*.5,this.x,this.y+this.height*.3);
							context.quadraticCurveTo(this.x+this.width*.25,this.y+this.height*.5,this.x+this.width*.5,this.y+this.height*.3);
							break;
						}
						context.fill();
						context.closePath();
					}
					context.fillStyle = '#FFF';
					if(isSick){
						context.beginPath();
						context.arc(this.x-this.width*.15,this.y-this.height*.21,this.width*.08,0,2*Math.PI,false);
						context.arc(this.x+this.width*.15,this.y-this.height*.21,this.width*.08,0,2*Math.PI,false);
						context.fill();
						context.closePath();
					}else{
						context.beginPath();
						context.arc(this.x-this.width*.15,this.y-this.height*.21,this.width*.12,0,2*Math.PI,false);
						context.arc(this.x+this.width*.15,this.y-this.height*.21,this.width*.12,0,2*Math.PI,false);
						context.fill();
						context.closePath();
						context.fillStyle = '#000';
						context.beginPath();
						context.arc(this.x-this.width*(.15-.04*_COS[this.orientation]),this.y-this.height*(.21-.04*_SIN[this.orientation]),this.width*.07,0,2*Math.PI,false);
						context.arc(this.x+this.width*(.15+.04*_COS[this.orientation]),this.y-this.height*(.21-.04*_SIN[this.orientation]),this.width*.07,0,2*Math.PI,false);
						context.fill();
						context.closePath();
					}
				}
			});
		}
		items = stage.getItemsByType(2);
		//主角
		player = stage.createItem({
			width:30,
			height:30,
			type:1,
			location:map,
			coord:{x:13.5,y:23},
			orientation:2,
			speed:2,
			frames:10,
			update:function(){
				var coord = this.coord;
				if(!coord.offset){
					if(this.control.orientation!='undefined'){
						if(!map.get(coord.x+_COS[this.control.orientation],coord.y+_SIN[this.control.orientation])){
							this.orientation = this.control.orientation;
						}
					}
					this.control = {};
					var value = map.get(coord.x+_COS[this.orientation],coord.y+_SIN[this.orientation]);
					if(value==0){
						this.x += this.speed*_COS[this.orientation];
						this.y += this.speed*_SIN[this.orientation];
					}else if(value<0){
						this.x -= map.size*(map.x_length-1)*_COS[this.orientation];
						this.y -= map.size*(map.y_length-1)*_SIN[this.orientation];
					}
				}else{
					if(!beans.get(this.coord.x,this.coord.y)){	//吃豆
						_SCORE++;
						beans.set(this.coord.x,this.coord.y,1);
						if(_GOODS[this.coord.x+','+this.coord.y]){	//吃到能量豆
							items.forEach(function(item){
								if(item.status==1||item.status==3){	//如果NPC為正常狀態,則置為臨時狀態
									item.timeout = 450;
									item.status = 3;
								}
							});
						}
					}
					this.x += this.speed*_COS[this.orientation];
					this.y += this.speed*_SIN[this.orientation];
				}
			},
			draw:function(context){
				context.fillStyle = '#FFE600';
				context.beginPath();
				if(stage.status!=3){	//玩家正常狀態
					if(this.times%2){
						context.arc(this.x,this.y,this.width/2,(.5*this.orientation+.20)*Math.PI,(.5*this.orientation-.20)*Math.PI,false);
					}else{
						context.arc(this.x,this.y,this.width/2,(.5*this.orientation+.01)*Math.PI,(.5*this.orientation-.01)*Math.PI,false);
					}
				}else{	//玩家被吃
					if(stage.timeout) {
						context.arc(this.x,this.y,this.width/2,(.5*this.orientation+1-.02*stage.timeout)*Math.PI,(.5*this.orientation-1+.02*stage.timeout)*Math.PI,false);
					}
				}
				context.lineTo(this.x,this.y);
				context.closePath();
				context.fill();
			}
		});
		//事件繫結
		stage.bind('keydown',function(e){
			switch(e.keyCode){
				case 13: //回車
				case 32: //空格
				this.status = this.status==2?1:2;
				break;
				case 39: //右
				player.control = {orientation:0};
				break;
				case 40: //下
				player.control = {orientation:1};
				break;
				case 37: //左
				player.control = {orientation:2};
				break;
				case 38: //上
				player.control = {orientation:3};
				break;
			}
		});
	})();
	//結束畫面
	(function(){
		var stage = game.createStage();
		//遊戲結束
		stage.createItem({
			x:game.width/2,
			y:game.height*.35,
			draw:function(context){
				context.fillStyle = '#FFF';
				context.font = 'bold 48px Helvetica';
				context.textAlign = 'center';
				context.textBaseline = 'middle';
				context.fillText('GAME OVER',this.x,this.y);
			}
		});
		//記分
		stage.createItem({
			x:game.width/2,
			y:game.height*.5,
			draw:function(context){
				context.fillStyle = '#FFF';
				context.font = '20px Helvetica';
				context.textAlign = 'center';
				context.textBaseline = 'middle';
				context.fillText('FINAL SCORE: '+(_SCORE+50*Math.max(_LIFE-1,0)),this.x,this.y);
			}
		});
		//事件繫結
		stage.bind('keydown',function(e){
			switch(e.keyCode){
				case 13: //回車
				case 32: //空格
				_SCORE = 0;
				_LIFE = 3;
				var st = game.setStage(1);
				st.reset();
				break;
			}
		});
	})();
	game.init();
})();

 

相關文章