HTML5吃豆豆游戏开发实战(二)主角移动和动画循环设置
接着上一篇讲,在上一篇中呢,我已经使用Canvas绘制出了我们游戏的主角,姑且叫它“小嘴”吧,因为只有嘴巴,嘿嘿,我还添了眼睛。
在这一篇中呢,就实现物体的移动和动画播放(一直张开嘴吧关闭嘴巴的动画,很饥渴的样子)。
1. 要做玩家和游戏的交互,当然要考虑--如何设置按键响应这个问题。
那么如何设置呢?
我们可以通过在body标签里面添加事件来响应用户的操作:
由于我们要用W,A,S,D来控制物体的上下移动,这是按键响应,于是我们选择用onkeydown事件。
onkeydown 事件:事件会在用户按下键盘按键时触发。
(上面的图片解释来自:http://www.w3school.com.cn/jsref/event_onkeydown.asp)
请注意:浏览器差异,IE使用event.KeyCode取回被按下的字符(ASCII),而火狐和欧朋使用event.Which.
2.动画:计时器和游戏循环
动画实际上就是绘制物体、擦掉,再重绘。
我们使用setInterval(functionName,timeInerval)告诉浏览器,每隔一个固定的时间间隔就调用一次给定的函数,直到函数clearInterval( )被调用.如果我们要停止动画播放(比如游戏暂停或者结束),那就调用clearInterval( )函数。
下面是源代码:(可直接复制运行)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>物体移动简单演示</title> </head> <!--注意在body这里添加按键响应事件,引号内为函数名--> <body onkeydown="test()"> <h1>小球上下移动</h1> <canvas id="test" width="1200px" height="600px" style="background-color: white"></canvas> <script type="text/javascript"> //得到画布 var can = document.getElementById("test"); var cxt = can.getContext("2d"); //游戏世界原点坐标 var Ox = 162; var Oy = 0; //窗口宽高 var WinWidth = 900; var WinHeight = 500; //四周边界值 var WinMaxX = Ox + WinWidth; var WinMinX = Ox; var WinMaxY = Oy + WinHeight; var WinMinY = Oy; //小球圆心坐标x,y var x = 30 + Ox; var y = 30; //小球半径 var radius = 20; var dir = 0; var ball = new Ball(x,y,dir,radius,3);//实例化小球对象 var state = 0;//全局变量,控制张嘴闭嘴动画切换 //墙壁数组,大小为3 //注意js里面定义数组并且初始化的方法! //walls按x从小到大排序 var walls = [new Wall(262,200,100,30),new Wall(662,60,30,400),new Wall(762,300,200,30)]; //动画循环 setInterval("drawBall(ball)",100); //setInterval( function(){drawBall(ball),1000}); //小球类 //注意js里面类的定义方法,直接类加函数参数的形式,就相当于定义了,然后里面直接this.xx=xx_,很高效 //参数:x,y坐标,球的方向,球的半径,运动速度 function Ball(x_,y_,dir_,r_,sp_) { this.x = x_; this.y = y_; this.dir = dir_; this.r = r_; //灰色表示还没用 this.sp = sp_; //定义上下左右:0,1,2,3 this.moveUp = function() { this.y -= this.sp; this.dir = 0; } this.moveDown = function() { this.y += this.sp; this.dir = 1; } this.moveLeft = function() { this.x -= this.sp; this.dir = 2; } this.moveRight = function() { this.x += this.sp; this.dir = 3; } //获得小球的坐标 this.getX = function() { return this.x; } this.getY = function() { return this.y; } //获得球的各个方向的边界值 this.getMaxX = function() { return this.x + this.r; } this.getMaxY = function() { return this.y + this.r; } /** * @return {number} */ this.getMinX = function() { return this.x - this.r; } /** * @return {number} */ this.getMinY = function() { return this.y - this.r; } } function Wall(x_,y_,width_,height_) { this.x = x_; this.y = y_; this.width = width_; this.height = height_; this.getX = function() { return this.x; } this.getY = function() { return this.y; } this.getWidth = function() { return this.width; } this.getHeight = function() { return this.height; } } function drawBall(ball_) { FreshWindow(Ox,Oy,WinWidth,WinHeight); switch (ball_.dir) { case 0: drawBall_UpOrDown(ball_,true); break; case 1: drawBall_UpOrDown(ball_,false); break; case 2: drawBall_RightOrLeft(ball_,false); break; case 3: drawBall_RightOrLeft(ball_,true); break; default : break; } } function drawWall(walls_) { // cxt.fillStyle="#000000"; // document.write(walls_[1].x); for(var i=0;i<walls_.length;i++) { cxt.strokeRect(walls_[i].x,walls_[i].y,walls_[i].width,walls_[i].height); } } function FreshWindow(x,y,width,height) { //清理画布 cxt.clearRect(x,y,width,height); cxt.strokeRect(x,y,width,height); drawWall(walls); } //往右/左的样子 function drawBall_RightOrLeft(ball,isRight) { //document.write(state); //画眼睛,眼睛是公共的 //画眼睛-外圈 var eyeX; if(isRight == true) //右 eyeX = ball.x - 5; else eyeX = ball.x + 5;//左 var eyeY = ball.y-8; var eyeR = 6;//目前限定死这个 cxt.beginPath(); cxt.fillStyle="#000000"; cxt.arc(eyeX,eyeY,eyeR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); //画眼睛-眼球 var qiuR = eyeR / 2; cxt.beginPath(); cxt.fillStyle="#FF0000"; cxt.arc(eyeX,eyeY,qiuR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); switch(state) { //张嘴 case 1: //画红球 cxt.beginPath(); cxt.fillStyle="#FF0000"; //嘴巴大小为90° //画圆弧--脸 if(isRight) cxt.arc(ball.x,ball.y,radius,1/4 * Math.PI,3/2 * Math.PI + 1/4 * Math.PI,false); else cxt.arc(ball.x,ball.y,radius,3/4 * Math.PI, Math.PI + 1/4 * Math.PI,true); cxt.stroke(); cxt.closePath(); cxt.beginPath(); //画嘴巴 var ax = 0,ay = 0; var bx = 0,by = 0; var temp = radius * Math.sqrt(2)/2; if(isRight) ax = ball.x + temp; else ax = ball.x - temp; ay = ball.y - temp; bx = ax; by = ball.y + temp; cxt.moveTo(ball.x,ball.y); cxt.lineTo(ax,ay); cxt.moveTo(ball.x,ball.y); cxt.lineTo(bx,by); cxt.closePath(); cxt.stroke(); state = 0; break; //闭嘴 case 0: //画圆弧--脸 cxt.beginPath(); cxt.arc(ball.x,ball.y,radius,0,Math.PI * 2,false); cxt.stroke(); cxt.closePath(); //从圆心到嘴巴末点的连线 cxt.beginPath(); cxt.moveTo(ball.x,ball.y); if(isRight) cxt.lineTo(ball.x + radius,ball.y); else cxt.lineTo(ball.x - radius,ball.y); cxt.stroke(); cxt.closePath(); state = 1; break; default : break; } } //往上/下的样子 function drawBall_UpOrDown(ball,isUp) { //document.write(state); //画眼睛,眼睛是公共的 //画眼睛-外圈 var eyeX = ball.x - 5; var eyeY = ball.y + 8; if(!isUp) { eyeX = ball.x + 5; eyeY = ball.y - 8; } var eyeR = 6;//目前限定死这个 cxt.beginPath(); cxt.fillStyle="#000000"; cxt.arc(eyeX,eyeY,eyeR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); //画眼睛-眼球 var qiuR = eyeR / 2; cxt.beginPath(); cxt.fillStyle="#FF0000"; cxt.arc(eyeX,eyeY,qiuR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); switch(state) { //张嘴 case 1: //画红球 cxt.beginPath(); cxt.fillStyle="#FF0000"; //嘴巴大小为90° //画圆弧--脸 if(!isUp) cxt.arc(ball.x,ball.y,radius,1/4 * Math.PI ,3/4 * Math.PI,true); else cxt.arc(ball.x,ball.y,radius,Math.PI + 1/4 * Math.PI,3/2 * Math.PI+ 1/4 * Math.PI,true); cxt.stroke(); cxt.closePath(); cxt.beginPath(); //画嘴巴 var ax = 0,ay = 0; var bx = 0,by = 0; var temp = radius * Math.sqrt(2)/2; ax = ball.x - temp; ay = ball.y - temp; by = ay; bx = ball.x + temp; if(!isUp) { ax = ball.x + temp; ay = ball.y + temp; by = ay; bx = ball.x - temp; } cxt.moveTo(ball.x,ball.y); cxt.lineTo(ax,ay); cxt.moveTo(ball.x,ball.y); cxt.lineTo(bx,by); cxt.closePath(); cxt.stroke(); state = 0; break; //闭嘴 case 0: //画圆弧--脸 cxt.beginPath(); cxt.arc(ball.x,ball.y,radius,0,Math.PI * 2,false); cxt.stroke(); cxt.closePath(); //从圆心到嘴巴末点的连线 cxt.beginPath(); cxt.moveTo(ball.x,ball.y); if(!isUp) cxt.lineTo(ball.x ,ball.y + radius); else cxt.lineTo(ball.x ,ball.y- radius); cxt.stroke(); cxt.closePath(); state = 1; break; default : break; } } function test() { //清理画布 //cxt.clearRect(0,0,400,300); var code = event.keyCode;//对应字母的ascii //alert(code); switch(code) { //数字是ASCII码,记不住的话可以对照ASCII码表 //上 case 87: if(ball.getMinY() >= WinMinY) ball.moveUp(); break; //下 case 83: if(ball.getMaxY() <= WinMaxY) ball.moveDown(); break; //左 case 65: if(ball.getMinX() >= WinMinX) ball.moveLeft(); break; //右 case 68: if(ball.getMaxX() <= WinMaxX ) ball.moveRight(); break; default :break; } } </script> </body> </html>
注意,给像和我一样的新手一个免费提示,这样写,代码越来越多,越来越乱,整个结构会越来越不清晰,于是,我把JS分割出来,这很有必要。
方法:新建xxx.js,将要移植的代码贴入,然后使用
<script type="text/javascript" src="Ball.js"></script>
将脚本引入即可。
修改过后的工程如下:
1.物体移动demo.html
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>物体移动简单演示</title> </head> <!--注意在body这里添加按键响应事件,引号内为函数名--> <body onkeydown="test()"> <h1>小球上下移动</h1> <canvas id="test" width="1200px" height="600px" style="background-color: white"></canvas> <!--把Ball.js和Wall.js引入本页面--> <script type="text/javascript" src="Ball.js"></script> <script type="text/javascript" src="Wall.js"></script> <script type="text/javascript"> //得到画布 var can = document.getElementById("test"); var cxt = can.getContext("2d"); //游戏世界原点坐标 var Ox = 162; var Oy = 0;dddddd //窗口宽高 var WinWidth = 900; var WinHeight = 500; //四周边界值 var WinMaxX = Ox + WinWidth; var WinMinX = Ox; var WinMaxY = Oy + WinHeight; var WinMinY = Oy; //小球圆心坐标x,y var x = 30 + Ox; var y = 30; //小球半径 var radius = 20; var dir = 0; var ball = new Ball(x,y,dir,radius,3);//实例化小球对象 var state = 0;//全局变量,控制张嘴闭嘴动画切换 //墙壁数组,大小为3 //注意js里面定义数组并且初始化的方法! //walls按x从小到大排序 var walls = [new Wall(262,200,100,30),new Wall(662,60,30,400),new Wall(762,300,200,30)]; //动画循环 setInterval("drawBall(ball)",100); //setInterval( function(){drawBall(ball),1000}); function FreshWindow(x,y,width,height) { //清理画布 cxt.clearRect(x,y,width,height); cxt.strokeRect(x,y,width,height); drawWall(walls); } function test() { //清理画布 //cxt.clearRect(0,0,400,300); var code = event.keyCode;//对应字母的ascii //alert(code); switch(code) { //数字是ASCII码,记不住的话可以对照ASCII码表 //上 case 87: if(ball.getMinY() >= WinMinY) ball.moveUp(); break; //下 case 83: if(ball.getMaxY() <= WinMaxY) ball.moveDown(); break; //左 case 65: if(ball.getMinX() >= WinMinX) ball.moveLeft(); break; //右 case 68: if(ball.getMaxX() <= WinMaxX ) ball.moveRight(); break; default :break; } } </script> </body> </html>
2.Ball.js
//小球类 //注意js里面类的定义方法,直接类加函数参数的形式,就相当于定义了,然后里面直接this.xx=xx_,很高效 //参数:x,y坐标,球的方向,球的半径,运动速度 function Ball(x_,y_,dir_,r_,sp_) { this.x = x_; this.y = y_; this.dir = dir_; this.r = r_; //灰色表示还没用 this.sp = sp_; //定义上下左右:0,1,2,3 this.moveUp = function() { this.y -= this.sp; this.dir = 0; } this.moveDown = function() { this.y += this.sp; this.dir = 1; } this.moveLeft = function() { this.x -= this.sp; this.dir = 2; } this.moveRight = function() { this.x += this.sp; this.dir = 3; } //获得小球的坐标 this.getX = function() { return this.x; } this.getY = function() { return this.y; } //获得球的各个方向的边界值 this.getMaxX = function() { return this.x + this.r; } this.getMaxY = function() { return this.y + this.r; } /** * @return {number} */ this.getMinX = function() { return this.x - this.r; } /** * @return {number} */ this.getMinY = function() { return this.y - this.r; } } function drawBall(ball_) { FreshWindow(Ox,Oy,WinWidth,WinHeight); switch (ball_.dir) { case 0: drawBall_UpOrDown(ball_,true); break; case 1: drawBall_UpOrDown(ball_,false); break; case 2: drawBall_RightOrLeft(ball_,false); break; case 3: drawBall_RightOrLeft(ball_,true); break; default : break; } } //往右/左的样子 function drawBall_RightOrLeft(ball,isRight) { //document.write(state); //画眼睛,眼睛是公共的 //画眼睛-外圈 var eyeX; if(isRight == true) //右 eyeX = ball.x - 5; else eyeX = ball.x + 5;//左 var eyeY = ball.y-8; var eyeR = 6;//目前限定死这个 cxt.beginPath(); cxt.fillStyle="#000000"; cxt.arc(eyeX,eyeY,eyeR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); //画眼睛-眼球 var qiuR = eyeR / 2; cxt.beginPath(); cxt.fillStyle="#FF0000"; cxt.arc(eyeX,eyeY,qiuR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); switch(state) { //张嘴 case 1: //画红球 cxt.beginPath(); cxt.fillStyle="#FF0000"; //嘴巴大小为90° //画圆弧--脸 if(isRight) cxt.arc(ball.x,ball.y,radius,1/4 * Math.PI,3/2 * Math.PI + 1/4 * Math.PI,false); else cxt.arc(ball.x,ball.y,radius,3/4 * Math.PI, Math.PI + 1/4 * Math.PI,true); cxt.stroke(); cxt.closePath(); cxt.beginPath(); //画嘴巴 var ax = 0,ay = 0; var bx = 0,by = 0; var temp = radius * Math.sqrt(2)/2; if(isRight) ax = ball.x + temp; else ax = ball.x - temp; ay = ball.y - temp; bx = ax; by = ball.y + temp; cxt.moveTo(ball.x,ball.y); cxt.lineTo(ax,ay); cxt.moveTo(ball.x,ball.y); cxt.lineTo(bx,by); cxt.closePath(); cxt.stroke(); state = 0; break; //闭嘴 case 0: //画圆弧--脸 cxt.beginPath(); cxt.arc(ball.x,ball.y,radius,0,Math.PI * 2,false); cxt.stroke(); cxt.closePath(); //从圆心到嘴巴末点的连线 cxt.beginPath(); cxt.moveTo(ball.x,ball.y); if(isRight) cxt.lineTo(ball.x + radius,ball.y); else cxt.lineTo(ball.x - radius,ball.y); cxt.stroke(); cxt.closePath(); state = 1; break; default : break; } } //往上/下的样子 function drawBall_UpOrDown(ball,isUp) { //document.write(state); //画眼睛,眼睛是公共的 //画眼睛-外圈 var eyeX = ball.x - 5; var eyeY = ball.y + 8; if(!isUp) { eyeX = ball.x + 5; eyeY = ball.y - 8; } var eyeR = 6;//目前限定死这个 cxt.beginPath(); cxt.fillStyle="#000000"; cxt.arc(eyeX,eyeY,eyeR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); //画眼睛-眼球 var qiuR = eyeR / 2; cxt.beginPath(); cxt.fillStyle="#FF0000"; cxt.arc(eyeX,eyeY,qiuR,0,Math.PI * 2,false); cxt.fill(); cxt.closePath(); switch(state) { //张嘴 case 1: //画红球 cxt.beginPath(); cxt.fillStyle="#FF0000"; //嘴巴大小为90° //画圆弧--脸 if(!isUp) cxt.arc(ball.x,ball.y,radius,1/4 * Math.PI ,3/4 * Math.PI,true); else cxt.arc(ball.x,ball.y,radius,Math.PI + 1/4 * Math.PI,3/2 * Math.PI+ 1/4 * Math.PI,true); cxt.stroke(); cxt.closePath(); cxt.beginPath(); //画嘴巴 var ax = 0,ay = 0; var bx = 0,by = 0; var temp = radius * Math.sqrt(2)/2; ax = ball.x - temp; ay = ball.y - temp; by = ay; bx = ball.x + temp; if(!isUp) { ax = ball.x + temp; ay = ball.y + temp; by = ay; bx = ball.x - temp; } cxt.moveTo(ball.x,ball.y); cxt.lineTo(ax,ay); cxt.moveTo(ball.x,ball.y); cxt.lineTo(bx,by); cxt.closePath(); cxt.stroke(); state = 0; break; //闭嘴 case 0: //画圆弧--脸 cxt.beginPath(); cxt.arc(ball.x,ball.y,radius,0,Math.PI * 2,false); cxt.stroke(); cxt.closePath(); //从圆心到嘴巴末点的连线 cxt.beginPath(); cxt.moveTo(ball.x,ball.y); if(!isUp) cxt.lineTo(ball.x ,ball.y + radius); else cxt.lineTo(ball.x ,ball.y- radius); cxt.stroke(); cxt.closePath(); state = 1; break; default : break; } }
3.Wall.js
function Wall(x_,y_,width_,height_) { this.x = x_; this.y = y_; this.width = width_; this.height = height_; this.getX = function() { return this.x; } this.getY = function() { return this.y; } this.getWidth = function() { return this.width; } this.getHeight = function() { return this.height; } } function drawWall(walls_) { // cxt.fillStyle="#000000"; // document.write(walls_[1].x); for(var i=0;i<walls_.length;i++) { cxt.strokeRect(walls_[i].x,walls_[i].y,walls_[i].width,walls_[i].height); } }
游戏截图:
下一篇,做游戏主角和墙壁的碰撞检测,欢迎交流,谢谢.代码粗糙,希望各位朋友指导小弟,谢谢。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。