学习canvas做的一个超级简单小游戏

学习canvas做的一个超级简单游戏, 键盘或者鼠标控制人物移动,人物碰撞到鱼就得分,等级到顶则游戏结束。

游戏界面

<!DOCTYPE HTML>
<html>
<head>
	<title>测试学习小游戏</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<style>
		#viewport {
			/*包含canvas的容器*/
			position: relative;
		}
		#viewport canvas {
			/*让canvas可以覆盖*/
			position: absolute;
			/*让canvas背景透明*/
			background-color: transparent;
		}
	</style>
</head>

<body>
	
  <div id="viewport"></div>
  
	<!-- content goes here -->

  </body>

  <script type="text/javascript">
  	
	//游戏画布大小
	var cavs = {  
		width:1200,
		height:800
	}
	// requestAnimationFrame 的浏览器兼容性处理
	var w = window;
	requestAnimationFrame = 
		w.requestAnimationFrame || 
		w.webkitRequestAnimationFrame || 
		w.msRequestAnimationFrame || 
		w.mozRequestAnimationFrame;
	//标记游戏结束
	var isGameOver = false;
	//记录上次屏幕刷新时间
	var then;
	//modifier=刷新间隔时间(intval秒)*speed 表示每次移动距离
	var intval;
	var modifier;
	//通过steptimer计时 英雄每0.5秒走动一次(图片数组里的图片循环切换)
	var stepTimer = 0;
	//expNums 存储每级别升级所需经验
	var expNums = [0,1,2,5,10,20,30];
	
	//骑鱼英雄变宽三倍
	var qiyuWidthNum = 3;
	//骑鱼图片
	var qiyuSrc = [
			["images/qiyu_right.png","images/qiyu_right2.png"],
			["images/qiyu_right.png","images/qiyu_right2.png"],
			["images/qiyu_left.png","images/qiyu_left2.png"],
			["images/qiyu_left.png","images/qiyu_left2.png"]
		];
	//正常图片
	var normalSrc = [
			["images/up.png","images/up2.png"],
			["images/right.png","images/right2.png"],
			["images/down.png","images/down2.png"],
			["images/left5.png","images/left2.png"]
		];

  	/*============================创建画布
	画布分三层 
	背景画游戏背景,基本不刷新画面
	中间层画经验条,有变动才刷新
	前台画英雄和怪物,一直刷新
	*/
	var cav_bg = document.createElement("canvas");
	//后台层画布加上了蓝色边框
	//cav_bg.style="border:1px solid blue";  
	var ctx_bg = cav_bg.getContext("2d");
	cav_bg.width = cavs.width;
	cav_bg.height = cavs.height;
	document.getElementById("viewport").appendChild(cav_bg);

	//中间层画布
	var cav_mg = document.createElement("canvas"); 
	var ctx_mg = cav_mg.getContext("2d");
	cav_mg.width = cavs.width;
	cav_mg.height = cavs.height;
	document.getElementById("viewport").appendChild(cav_mg);

	//前台画布
    var cav_fg = document.createElement("canvas");
	var ctx_fg = cav_fg.getContext("2d");
	cav_fg.width = cavs.width;
	cav_fg.height = cavs.height;
	document.getElementById("viewport").appendChild(cav_fg);

	//==============================游戏对象
	//游戏背景
	var bg = {
		width:cav_bg.width,
		height:cav_bg.height,
		x:0,
		y:0,
		src:"images/fishing.jpg"
	}
	//游戏英雄
	var hero = {
		width:42,   //人物宽度
		height:138, //人物高度
		x:cavs.width/2 - 21, //坐标X
		y:cavs.height*3/4 - 138, //坐标Y

		padding:{  //人物图片脚底到边境距离
			top:18,
			bottom:18,
			left:10,
			right:10
		},    

		speed:250,  //移动速度
		direct:0, //人物行走方向0:向上  1:向右  2:向下  3:向左
		step:0, //行走动作
		//人物图片库
		imgsrc:normalSrc,

		//人物阴影
		shadow:{
			widthNum:1,    //宽度等于人物
			heightNum:10, //高度为人物十分之一
			src:"images/shadow.png"
		},

		isExpChange:true, //经验是否改变了
		lv:1,  //英雄当前等级
		exp:0, //英雄当前经验
		maxLv:expNums.length-1,  //maxLv 英雄最高等级

		isBig:false, //是否变大了
		bigNum:1.5,  //变大倍数
		bigTime:10,   //变大持续时间
		bigBegin:0,  //开始变大的时间

		isJump:false, //是否跳跃中
		jumpHeight:200,//预计跳跃高度
		jumpSpeed:10,  //跳跃速度
		jumpDirect:0, //0:向上跳  2:向下回落
		jumping:0,    //目前跳跃高度

		isAuto:false,    //是否自动寻路中
		targetP:{x:0,y:0} //自动寻路坐标		
	}

	//游戏怪物
	var monster = {
		width:70,
		height:32,
		exp:1,
		bigExpNum:3,  //变大后经验加倍数
		big:false,   //是否变大了
		bigNum:2,  //变大倍数
		x:0,
		y:0,
		src:"images/yu.png"
	}

	//游戏经验条
	var expProcess = {
		r:32,  //半径
		x:50,   //圆心X Y 坐标
		y:50,

		lineWidth:16, //圆环宽度
		lineCap:"butt", //圆环结束断点的样式  butt为平直边缘 round为圆形线帽  square为正方形线帽
		bgColor:"#ccffff", //经验条背景色
		color:"green", //经验条颜色

		fillStyle : "rgb(255, 255, 255)", 
		font : "20px Helvetica",  // 字体和大小
		textAlign : "center",
		textBaseline : "middle", //垂直居中

		showTextType:0 //字符显示内容 0:英雄等级 1:英雄当前经验

	}

	//============================准备背景图片
	var bgReady = false;
	var bgImg = new Image();
	bgImg.onload = function(){
		bgReady = true;
		//背景图片载入完成后启动游戏
		then = Date.now();
		init();
		reset();
		main();
	}
	bgImg.src = bg.src;
	//============================准备英雄图片
	var heroReady = false;
	var heroImg = new Image();
	heroImg.onload = function(){
		heroReady = true;
	}
	heroImg.src = hero.imgsrc[0][0];
	//============================准备英雄阴影图片
	var heroShadowReady = false;
	var heroShadowImg = new Image();
	heroShadowImg.onload = function(){
		heroShadowReady = true;
	}
	heroShadowImg.src = hero.shadow.src;
	//============================准备怪物图片
	var monsterReady = false;
	var monsterImg = new Image();
	monsterImg.onload = function(){
		monsterReady = true;
	}
	monsterImg.src = monster.src;

	//==============================按键处理
	var keysDown = {};
	addEventListener("keydown",function(e){
		keysDown[e.keyCode] = true;
		},false
	);

	addEventListener("keyup",function(e){
		delete keysDown[e.keyCode];
		},false
	);

	//==============================鼠标处理
	cav_fg.addEventListener("click",function(e){
			//判断是否点击在经验进度条内部
			ctx_mg.arc(expProcess.x, expProcess.y, expProcess.r, 0, 2*Math.PI);
			if (ctx_mg.isPointInPath(e.offsetX,e.offsetY))
			{
				//切换经验条内显示文字内容
			   hero.isExpChange = true;
			   if(expProcess.showTextType == 0)
			   {
					expProcess.showTextType = 1;
			   }
			   else
			   {
				   expProcess.showTextType = 0;
			   }
			}
			else
			{
				//英雄开始自动移动到指定点
				hero.isAuto = true;
				hero.targetP.x = e.offsetX - hero.width/2;
				hero.targetP.y = e.offsetY - hero.height + hero.padding.bottom;
			}	
		},false
	);

	//=============================英雄自动行走
	var autoMove = function(){
		var needMove = false;
		if(hero.x < hero.targetP.x-modifier)
		{
			hero.direct = 1;
			hero.x += modifier;

			needMove = true;
		}
		else if(hero.x > hero.targetP.x+modifier)
		{
			hero.direct = 3;
			hero.x -= modifier;

			needMove = true;
		}
		if(hero.y < hero.targetP.y-modifier)
		{
			hero.direct = 2;
			hero.y += modifier;

			needMove = true;
		}
		else if(hero.y > hero.targetP.y+modifier)
		{
			hero.direct = 0;
			hero.y -= modifier;

			needMove = true;
		}
		if(!needMove)
		{
			hero.isAuto = false;
		}
		
	}

	//=============================英雄跳跃
	var Jumping = function(){
		if(hero.jumpDirect==0) //向上跳
		{
			hero.jumping += hero.jumpSpeed;
			if(hero.jumping>=hero.jumpHeight)
			{
				hero.jumpDirect = 2;
			}
		}
		if(hero.jumpDirect==2) //向下落
		{
			hero.jumping -= hero.jumpSpeed;
			if(hero.jumping <= 0)
			{
				hero.jumpDirect = 0;
				hero.isJump = false;
			}
		}
	}

	//=======================游戏结束========
	var gameover = function(){
		ctx_fg.clearRect(0,0,cavs.width,cavs.height); 
		if (monsterReady) {
			var max = 10;  //显示10倍大鱼
			ctx_fg.drawImage(monsterImg, (cavs.width-monster.width*max)/2, (cavs.height-monster.height*max)/2,monster.width*max,monster.height*max);
		}

		//显示游戏结束
			ctx_mg.beginPath();
			ctx_mg.fontSize = "250px"; // 字体大小
			ctx_mg.fillStyle = "red"; 
			ctx_mg.font = "黑体";
			ctx_mg.textAlign = expProcess.textAlign;
			ctx_mg.textBaseline = expProcess.textBaseline;
			ctx_mg.fillText("您的捕鱼数量达到上限,游戏结束!", cavs.width/2, 50);	 // 文字内容和文字坐标
	}

	//=============================刷新怪物
	var reset = function(){
		if(hero.lv <= hero.maxLv)
		{
			//怪物随机出现
			monster.big = (Math.random()>0.8); //20%几率
			if(monster.big) 
			{
				monster.exp = monster.exp*monster.bigExpNum;
				monster.width = monster.width*monster.bigNum;
				monster.height = monster.height*monster.bigNum;
			}
			monster.x = monster.width + (Math.random()*(cavs.width - monster.width*2));
			monster.y = monster.height+ (Math.random()*(cavs.height- monster.height*2)); //(Math.random()*(cavs.height - monster.height*2));
		}
		else
		{
			isGameOver = true;
			setTimeout(gameover,100);
		}
	}

	//===========================更新游戏对象的属性
	var update = function (){
		if(38 in keysDown){ //用户按的是↑
			hero.isAuto = false;
			hero.direct = 0;
			if(hero.y>bg.y) //没有到达画布边界
				hero.y -= modifier;
		}
		if(40 in keysDown){ //用户按的是↓
			hero.isAuto = false;
			hero.direct = 2;
			if(hero.y+hero.height<bg.y+bg.height) //没有到达画布边界
				hero.y += modifier;
		}
		if (37 in keysDown) { // 用户按的是←
			hero.isAuto = false;
			hero.direct = 3;
			if(hero.x>bg.x) //没有到达画布边界
				hero.x -= modifier;
		}
		if (39 in keysDown) { // 用户按的是→
			hero.isAuto = false;
			hero.direct = 1;
			if(hero.x+hero.width<bg.x+bg.width) //没有到达画布边界
				hero.x += modifier;
		}
		if (32 in keysDown) { // 用户按的是空格 左右移动时会跳起
			//hero.isAuto = false;
			hero.isJump = true;
		}

		// 英雄与怪物碰到了么?
		if (  //没有排除边界
			hero.x <= (monster.x + monster.width)
			&& monster.x <= (hero.x + hero.width)
			&& hero.y - hero.jumping <= (monster.y + monster.height)
			&& monster.y <= (hero.y - hero.jumping + hero.height)
		) {
			//增加经验
			hero.isExpChange = true;
			hero.exp += monster.exp;

			//console.dir(hero);
			//console.log("LV:"+hero.lv+" EXP:"+hero.exp);

			if(hero.exp>=expNums[hero.lv])
			{
				hero.exp = hero.exp%expNums[hero.lv];
				hero.lv++;
			}
			if(monster.big) 
			{
				monster.big = false;
				monster.exp = monster.exp/monster.bigExpNum;
				monster.width = monster.width/monster.bigNum;
				monster.height = monster.height/monster.bigNum;

				if(!hero.isBig) //如果人物不在变大状态就变大
				{
					hero.isBig = true;				
					//向左移动人物使人物骑鱼后中心位置不变
					hero.x -= hero.width*(qiyuWidthNum-1)/2;
					//骑鱼变长加速
					hero.width = hero.width*qiyuWidthNum;
					hero.imgsrc = qiyuSrc;
					hero.speed = hero.speed*hero.bigNum;

					//向上移动人物使人物变大后底部高度不变
					hero.y -= hero.height*(hero.bigNum-1);
					//向左边移动人物使人物变大后中心位置不变
					hero.x -= hero.width*(hero.bigNum-1)/2;
					//变大
					hero.width = hero.width*hero.bigNum;
					hero.height = hero.height*hero.bigNum;
					
				}
				//记录变大时间,是变大状态就刷新时间
				hero.bigBegin = Date.now();	
			}
			//hero.isAuto = false;
			reset();
		}
	}

	//======================初始化,画出游戏背景
	var init = function(){
		ctx_bg.clearRect(0,0,cavs.width,cavs.height); 
		//画背景图
		if (bgReady) {
			ctx_bg.drawImage(bgImg, bg.x, bg.y,bg.width,bg.height);
		}

		//绘制完整的圆
		ctx_bg.arc(expProcess.x, expProcess.y, expProcess.r, 0, 2*Math.PI); //绘制的动作	
		ctx_bg.strokeStyle = expProcess.bgColor; //圆环线条的颜色
		ctx_bg.lineWidth = expProcess.lineWidth;	//圆环的粗细
		ctx_bg.stroke();
	}


	// =====================画出人物和怪物
	var render = function () {
		//画前台英雄和怪物
		ctx_fg.clearRect(0,0,cavs.width,cavs.height);  
		if (heroReady) {
			heroImg.src = hero.imgsrc[hero.direct][hero.step];
			ctx_fg.drawImage(heroImg, hero.x, hero.y - hero.jumping ,hero.width,hero.height);
		}
		if (heroShadowReady) {
			heroShadowImg.src = hero.shadow.src;
			ctx_fg.drawImage(heroShadowImg, hero.x, hero.y + hero.height - hero.padding.bottom ,hero.width/hero.shadow.widthNum,hero.height/hero.shadow.heightNum);
		}
		if (monsterReady) {
			ctx_fg.drawImage(monsterImg, monster.x, monster.y,monster.width,monster.height);
		}
		//画中间层的进图条
		if(hero.isExpChange)
		{
			ctx_mg.clearRect(0,0,cavs.width,cavs.height); 
			//绘制进度圆弧
			ctx_mg.beginPath();
			var num = (2 * Math.PI / expNums[hero.lv] * hero.exp)-0.5*Math.PI;
			ctx_mg.strokeStyle = expProcess.color; //圆环线条的颜色
			ctx_mg.lineWidth = expProcess.lineWidth;	//圆环的粗细
			ctx_mg.lineCap = expProcess.lineCap;	//圆环结束断点的样式  butt为平直边缘 round为圆形线帽  square为正方形线帽
			ctx_mg.arc(expProcess.x, expProcess.y, expProcess.r, -0.5*Math.PI, num); 
			ctx_mg.stroke();
			//开始绘制百分比数字
			ctx_mg.beginPath();
			ctx_mg.fontSize = expProcess.fontSize; // 字体大小
			ctx_mg.fillStyle = expProcess.fillStyle; 
			ctx_mg.font = expProcess.font;
			ctx_mg.textAlign = expProcess.textAlign;
			ctx_mg.textBaseline = expProcess.textBaseline;
			ctx_mg.fillText((expProcess.showTextType==0)?("LV"+hero.lv):(hero.exp), expProcess.x, expProcess.y);	 // 文字内容和文字坐标
			//绘制完成清除标记
			hero.isExpChange = false;
		}
	};

	// =================游戏主函数
	var main = function () {
		var now = Date.now();
		intval = (now - then)/1000; 
		//计时器工作,走步
		stepTimer += intval;
		if(stepTimer>100/hero.speed) 
		{
			stepTimer = 0;
			hero.step = (hero.step+1)%hero.imgsrc[hero.direct].length; 
		}
		//计算步距
		modifier = intval*hero.speed;
		//自动移动
		if(hero.isAuto) autoMove();
		//跳跃
		if(hero.isJump) Jumping();
		//变回小人
		if(hero.isBig) {
			if((now - hero.bigBegin)/1000>hero.bigTime) //变身结束
			{
				hero.isBig = false;
				
				//停止骑鱼变窄减速
				hero.width = hero.width/3;
				hero.imgsrc = normalSrc;
				hero.speed = hero.speed/hero.bigNum;
				//向右移动人物使人物骑鱼后中心位置不变
				hero.x += hero.width*(qiyuWidthNum-1)/2;

				//变小
				hero.width = hero.width/hero.bigNum;
				hero.height = hero.height/hero.bigNum;
				//向下移动人物使人物变小后底部高度不变
				hero.y += hero.height*(hero.bigNum-1);
				//向右边移动人物使人物变大后中心位置不变
				hero.x += hero.width*(hero.bigNum-1)/2;
				
			}
		}
		update();
		render();
		then = now;
		// 循环(间隔时间为屏幕刷新时间)
		if(!isGameOver)
		{
			requestAnimationFrame(main);
		}
	};

  </script>
</html>

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值