接着上一篇讲,在上一篇中呢,我已经使用Canvas绘制出了我们游戏的主角,姑且叫它“小嘴”吧,因为只有嘴巴,嘿嘿,我还添了眼睛。
在这一篇中呢,就实现物体的移动和动画播放(一直张开嘴吧关闭嘴巴的动画,很饥渴的样子)。
1. 要做玩家和游戏的交互,当然要考虑--如何设置按键响应这个问题。
那么如何设置呢?
我们可以通过在body标签里面添加事件来响应用户的操作:
由于我们要用W,A,S,D来控制物体的上下移动,这是按键响应,于是我们选择用onkeydown事件。
onkeydown 事件:事件会在用户按下键盘按键时触发。
(上面的图片解释来自:/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 οnkeydοwn="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,yvar 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,3this.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);elsecxt.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;elseax = 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);elsecxt.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);elsecxt.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);elsecxt.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 οnkeydοwn="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,yvar 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,3this.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);elsecxt.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;elseax = 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);elsecxt.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);elsecxt.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);elsecxt.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);}}
游戏截图:
下一篇,做游戏主角和墙壁的碰撞检测,欢迎交流,谢谢.代码粗糙,希望各位朋友指导小弟,谢谢。