简单的画布打方块小游戏~

终于抽空又写了一个游戏,这次是用画布,一个很简单的小游戏 预览地址  戳这里试玩  用谷歌浏览器哈~
其实多多少少会有bug 我会在接下来抽空慢慢完善它,这里我简单的说一下原理

这个游戏实际上就是画布在极短的时间内不停的对所有元素进行绘制,就像动画片一样。当然如果有更好的方法欢迎告诉我~

html:

[code lang=”html”]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>canvas test</title>

<style type="text/css">
*{
margin:0;
padding:0;
}
</style>

</head>
<body>

<div class="main">
<canvas id="canvas"></canvas>
</div>

<script type="text/javascript" src="js/stick.js"></script>
<script type="text/javascript" src="js/ball.js"></script>
<script type="text/javascript" src="js/sqr.js"></script>
<script type="text/javascript" src="js/main.js"></script>

</body>
</html>
[/code]

以上是html代码,就只有一个canvas元素 作为舞台来进行游戏,可以看到引入了四个js文件 分别是 stick.js,ball.js, sqr.js ,main.js接下来我一一介绍每个js的功能
首先是sqr.js:

[code lang=”js”]
var sqr = function(){ // do draw
this.init()
}

sqr.prototype.level = [[[1],[1],[1],[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]]];

sqr.prototype.chooseCol = [[[1],[4],[5],[9],[4]],
[[5],[1],[3],[6],[0]],
[[7],[6],[2],[7],[2]],
[[6],[9],[4],[6],[3]],
[[2],[7],[0],[5],[4]],
[[0],[5],[7],[4],[1]]];

sqr.prototype.col = function(){ // color lib
var color = ["#02c1f1","#9f5bca","#5bcaa7","#5bca6d","#9fca5b","#c9ca5b","#ca855b","#ca5bbe","#ca5b66","#b4ca5b"]
return color
}

sqr.prototype.init = function(){ // draw sqr
this.x = this.level[0].length;
this.y = this.level.length;
this.w = (c_width / this.x);
this.h = 18;
cxt.save();
cxt.strokeStyle = "#f0f0f0"; // border-color
cxt.lineWidth = 1;
for(var i=0;i<this.y;i++){
for(var j=0;j<this.x;j++){
var n = this.chooseCol[i][j];
var m = this.level[i][j]
var col = this.col()[n];
cxt.beginPath()
cxt.fillStyle = col;
if(m != 0){
cxt.fillRect(this.w*j,this.h*i,this.w,this.h)
cxt.strokeRect(this.w*j,this.h*i,this.w,this.h)
}
cxt.closePath()
}
}
cxt.restore();
}
[/code]

其实 sqr.js 就是画方块的:

[code lang=”js”]
sqr.prototype.level = [[[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],[1]]];
[/code]

这个数组就是方块的初始化状态,是一个5*6的矩形,当然我可以通过将数组内的值置0来完成我想要的各种形状, sqr.prototype.chooseCol 也是一个数组,这个数组和后来的sqr.prototype.col是用来定义方块的颜色的。接下来就是绘制过程了

[code lang=”js”]
cxt.fillRect(this.w*j,this.h*i,this.w,this.h)
cxt.strokeRect(this.w*j,this.h*i,this.w,this.h)
[/code]

熟悉canvas API的人应该知道,cxt是canvas.getContext(‘2d’)的简写,然后strokeRcet和fillRect传入的参数都是 (x,y,w,h),(x,y)是绘制矩形的起始坐标,w和h是矩形的宽和高,这里通过对level内部布局的三维数组循环来布局,其实这里可以将level改写成这样

[code lang=”js”]
sqr.prototype.level = [[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,1]];
[/code]

就少了一个维度的数组了,为什么我不这样呢,因为后面的代码都写好了,我懒得改。。。哈哈,原谅我的懒癌犯了,好了,这里掠过。接下来看stick

[code lang=”js”]
var stick = function(){
this.color = "#f0f0f0";
this.w = 100;
this.h = 10;
this.init()

}

stick.prototype.init = function(){
this.x = p.x – (this.w / 2)
this.draw()

}

stick.prototype.draw = function(){ // draw the stick
var flag = this.bound();
cxt.beginPath();
cxt.fillStyle = this.color;
if(flag == "left"){
this.move(0,c_height-this.h,this.w,this.h)
this.x = 0;
}else if(flag == "right"){
this.move((c_width – this.w),c_height-this.h,this.w,this.h)
this.x = (c_width – this.w);
}else{
this.move(this.x,c_height-this.h,this.w,this.h)
}
}

stick.prototype.bound = function(){
if(this.x < 0){ return "left" }else if(this.x > (c_width – this.w)){
return "right"
}
}

stick.prototype.move = function(x,y,w,h){
cxt.fillRect(x,y,w,h)
cxt.strokeRect(x,y,w,h)
}

[/code]

stick就是下面用来接小球的棍子 这里定义了棍子的颜色 高度 宽度 和棍子碰到画布边缘时不超出的碰撞检测 看代码就ok
接下来的那颗小球,我觉得整个的难点就在这里

[code lang=”js”]
var ball = function(){
this.r = 6;
this.speed = 4;
this.isMove = false
this.color = "#ff0"
this.top = 0
this.init()
}

ball.prototype.init = function(){

this.draw()
this.begin()
}

ball.prototype.draw = function(){
cxt.save()
cxt.beginPath()
cxt.fillStyle = this.color
cxt.strokeStyle = this.color
if(!this.isMove){
cxt.arc(stick.x+(stick.w/2),c_height-(stick.h+this.r),this.r,0,Math.PI * 2, false)
}else{
this.move()
}
cxt.fill()
cxt.stroke()
cxt.closePath()
//console.log(0)
cxt.restore()
}

ball.prototype.move = function(){
var sqrArr = sqr.level;
var n = sqrArr[0].length;
this.x+=this.a;
this.id = Math.ceil((this.x+this.r)/sqr.w – 1) == n ? (n-1) : Math.ceil((this.x+this.r)/sqr.w – 1)
//if(this.speed > 0){
for(var i=sqrArr.length;i>0;i–){
if(sqrArr[i-1][this.id][0]){ // 遇到数组内容为0的方块时直接穿透
this.top = (i-1) * sqr.h
var site = this.id * sqr.w
var f = Math.sqrt( (Math.pow((this.y-this.r-this.top),2) + Math.pow((this.x-site),2)) ) //获取小球圆心+r到方块基点距离
var p = Math.sqrt( (Math.pow(sqr.h,2) + Math.pow((this.x-site),2)) ) //方块基点到小球圆心+r发生碰撞时候的距离
if(f < p){ //碰撞检测
sqr.level[i-1][this.id][0] = 0 // 小球碰撞方块时,方块数组内容置0
this.speed = -this.speed
var m = i == sqrArr.length ? i-1 : i;
if(sqr.level[m][this.id][0] && m < sqrArr.length ){
this.a = -(this.a)
}
break
}

}
}
//}

this.y-=this.speed;
if( (this.x – this.r) < 0 || (this.x + this.r) > c_width){ // 边框反弹
this.a = -(this.a)
}else if(this.y-this.r < this.top){ // 顶部边框反弹 this.speed = -(this.speed) }else if((this.y+this.r) > (c_height-stick.h) && this.x > stick.x && this.x < (stick.x + stick.w) ){ // 球和棍子反弹 this.a = (this.a) + (this.x – (stick.x + stick.w/2))/30 // 判断小球在木棍落点位置,添加加速度 this.speed = -(this.speed) } if(this.y – this.r > c_height + this.r){
play = false;
alert("YOU LOSE")
}
cxt.arc(this.x,this.y,this.r,0,Math.PI * 2, false)

}

ball.prototype.begin = function(){
var root = this
canvas.addEventListener("click",function(e){
if(!root.isMove){
root.x = stick.x+(stick.w/2);
root.y = c_height-(stick.h+root.r);
root.a = (root.x – c_width/2)/50;
}
root.isMove = true
},false)
}

[/code]

卧槽,这个我要怎么说。。。我感觉自己都说不清楚,总之整个的关键在这里

[code lang=”js”]
for(var i=sqrArr.length;i>0;i–){
if(sqrArr[i-1][this.id][0]){ // 遇到数组内容为0的方块时直接穿透
this.top = (i-1) * sqr.h
var site = this.id * sqr.w
var f = Math.sqrt( (Math.pow((this.y-this.r-this.top),2) + Math.pow((this.x-site),2)) ) //获取小球圆心+r到方块基点距离
var p = Math.sqrt( (Math.pow(sqr.h,2) + Math.pow((this.x-site),2)) ) //方块基点到小球圆心+r发生碰撞时候的距离
if(f < p){ //碰撞检测
sqr.level[i-1][this.id][0] = 0 // 小球碰撞方块时,方块数组内容置0
this.speed = -this.speed
var m = i == sqrArr.length ? i-1 : i;
if(sqr.level[m][this.id][0] && m < sqrArr.length ){
this.a = -(this.a)
}
break
}

}
}
[/code]

开始的碰撞检测我只是简单的根据 top 和 left 来做检测,但是后来发现这样的话如果小球碰撞的方块下面还有方块的话就会一起被消掉,直到前几天看了一个教程,里面的碰撞检测是用的勾股定理来算出两点之间的距离小于会碰撞的距离的时候我才豁然开朗,还能这样玩,然后我就写了这个碰撞检测,如下图:

003744ggvmrrfexxgzlfuz

简单的画布打方块小游戏~

如图,根据勾股定理分别算出 两条黄线 d 和 D 的长度(这两个值是一直变化的),当 D 小于 d 时,就说明了方块的小球发生了变化,当然如果方块在小球左边时候也有 D 小于 d 的情况,不过已经在之前的判断里过滤掉了~

最后 就是 main.js 了

[code lang=”js”]
window.onload = function(){
loop()
}

var canvas = document.getElementById("canvas")
var c_width = 400
var c_height = 600
canvas.width = c_width
canvas.height = c_height
var cxt = canvas.getContext("2d")
var delata = 0;
var p = {x:0};
var play = true;
//loop()
function start(){
this.init()
}

start.prototype.init = function(){ // init
this.draw()
}

function setbg(){ // draw background image
var bg = new Image();
bg.src = "../images/bg.jpg";
cxt.drawImage(bg,0,0,c_width,c_height)
}

var sqr = new sqr()
var stick = new stick()
var ball = new ball()

function loop(){ // loop function
setbg()
sqr.init()
stick.init()
ball.init()
if(play){
play = requestAnimationFrame(loop)
}
}

canvas.addEventListener("mousemove",function(e){
p = getPos(canvas,e)
},false)

function getPos(canvas,e){
var rect = canvas.getBoundingClientRect();
return{
x: e.clientX – rect.left
}
}
[/code]

main里其实就是定义了全局变量和一个不断执行的loop函数,然后会一直将 小球 棍子 和方块 以及背景不停的进行重绘,来完成动画的效果,当然可能会影响一些性能,如果大家有更好的方法记得告诉我哦~

嗯,完了~~

发表评论

邮箱地址不会被公开。 必填项已用*标注