用canvas写个接水管小游戏
声明:本文为原创文章,如需转载,请注明来源WAxes,谢谢!
过年的十八天假期迷迷糊糊一下子就过去了(LZ还是实习生,鉴于大学最后一个寒假了,所以就多请了好多天假),又要返工上班了。这是年后的第一篇博客了。其实就是水一下,毕竟不能冷落博客太久啊。
这两天刚好抱着玩的心情写了个接水管游戏,本来用css3更容易写。。。就旋转那些东西,不过因为LZ比较喜欢canvas的写法,所以还是用了canvas来写。
先上个DEMO吧:接水管 玩法很简单:点击水管块就可以旋转水管,连通水管后点击上面的水阀,然后就可以了。可能还有bug,LZ暂时没去测试了。
因为代码也很简单,重点也不多,就先说下游戏思路:
【游戏逻辑】
游戏逻辑很简单,就是水管总共有4个面,给四个面一个代号:0,1,2,3;然后水管有两个口,一个进水口,一个出水口,就可以用0,1,2,3来表示水管的两个口(如果想写更复杂的,多个口的,也一个样,一个进水口,多个出水口,用数组保存就行了。我写的这个比较简单,只有两个口,所以后面判断连通性也比较容易。),保存好水管块的进出口数据后,当水管旋转时,相应改变水管进出口参数就行了。
然后就是连通性判断:水管只有两个口的话就很简单了。一般有一个最初的进水口,然后就从那个水管块开始,先获取出水口,然后再遍历当前水管块周围的水管块的进水口是否跟当前水管块的出水口相连通(被遍历的水管块的进水口和出水口都要进行判断,因为水管可以旋转,也就是说进水口和出水口在一开始是无法确定的,当确定了连通关系后,就可以确定水管的进水口和出水口了,之后的循环也一样),如果对的上。就说明连通了。然后就跳到那个水管块,再进行那个水管块周围的水管块的遍历,就这样重复遍历,直到最后一个也就是出水的那个水管块也连通了,就说明整条水管连通了。
上面的是只有两个口的水管连通性判断,如果游戏中有三个口的水管甚至四个口的水管,判断方式就没那么简单了。我写的是两个口的,不过也想了一下多口的判断,大概就是找到水管的进水口,因为是多个出水口,所以就得一个一个来,对每一条线路进行判断,每当跳转到一个新的水管块,就把之前的那个水管块保存为新水管块的父类水管。然后对新水管的出水口进行循环判断,如果没有发现有连通的水管,则跳转至父类水管,并将新水管块加入“此路不通”列表,再次遍历父类水管块的其他出水口进行判断,如此循环,当循环到父类为最初进水口的那个水管块的时候,就说明水管没有连通路线,当然,如果循环到新水管块为出口水管时,就说明连通了。
楼主比较懒,所以就只写了两个口的,没写多个口的了。
【代码部分】
首先定义水管块对象:
var Box = function(center , style , Gateway , angle , coordinate){
this.center = center;
this.style = style;
this.angle = angle;
this.endangle = angle;
this.Gateway = Gateway;
this.coordinate = coordinate;
}
Box.prototype = {
constructor:Box,
draw:function(){
this.setHole(); if(this.angle!==this.endangle){
canclick = false;
this.rotate();
} var ext = [
{x:0,y:-boxWidth/2},
{x:boxWidth/2,y:0},
{x:0,y:boxWidth/2},
{x:-boxWidth/2,y:0},
]
ctx.save();
ctx.translate(this.center.x , this.center.y);
ctx.rotate(this.angle);
//画管道
switch(this.style){
case 0:break;
case 1:ctx.drawImage(document.getElementById("pipe1") , -boxWidth/2 , -boxWidth/2 , boxWidth , boxWidth);
break;
case 2:ctx.drawImage(document.getElementById("pipe2") , -boxWidth/2 , -boxWidth/2 , boxWidth , boxWidth);
break;
case 3:ctx.drawImage(document.getElementById("start") , -boxWidth/2 , -boxWidth/2 , boxWidth , boxWidth);
break;
}
ctx.restore();
},
rotate:function(){
if(Math.abs(this.endangle-this.angle)<=0.01){
canclick = true;
this.endangle = this.endangle>=2*Math.PI?0:this.endangle;
this.angle = this.endangle; if(this.style===3){
var result = connectPipes();
if(result){
// alert('成功连通')
connectSuccess = true;
}
else {
alert("游戏失败")
window.location.reload();
}
}
}
else {
this.angle += (this.endangle-this.angle)*0.2;
}
},
setHole:function(){
if(this.style===1){
var zl = this.endangle/(0.5*Math.PI);
var initHole1 = 0 , initHole2 = 2;
this.inHole = (initHole1+zl)>=4?((initHole1+zl)-4):(initHole1+zl);
this.outHole = (initHole2+zl)>=4?((initHole2+zl)-4):(initHole2+zl);
}
else if(this.style===2){
var zl = this.endangle/(0.5*Math.PI);
var initHole1 = 1 , initHole2 = 2;
this.inHole = (initHole1+zl)>=4?((initHole1+zl)-4):(initHole1+zl);
this.outHole = (initHole2+zl)>=4?((initHole2+zl)-4):(initHole2+zl);
}
}
}
对象主要属性包括:水管位置,水管种类(主要就两个,1为直线的,2为九十度折角的),水管是否可以旋转的判定,水管旋转的初始角度,以及水管所处位置的行和列。
如何让水管点击后旋转,其实也很简单,就是先把画布平移到水管块中心,然后旋转,然后再把水管画出来,就旋转好了。
然后就是判断水管连通性的代码:
//判断水管连通性
function connectPipes(){
var index = 0;
while(1){
var result = getHole(boxes[index]);
if(boxes[index+result.nextBox]){
if(result.hole===boxes[index+result.nextBox].inHole){
index = index+result.nextBox;
}
else if(result.hole===boxes[index+result.nextBox].outHole){
var num = boxes[index+result.nextBox].inHole;
boxes[index+result.nextBox].inHole = result.hole;
boxes[index+result.nextBox].outHole = num;
index = index+result.nextBox;
}
else {
break;
}
}
else {
break;
}
}
if(index===boxes.length-1) return true;
else return false;
} function getHole(box){
var hole="0";
var nextBox=0;
switch(box.outHole){
case 0 : hole = 2;
nextBox = -cols;
break;
case 1 : hole = 3;
if(box.coordinate.cols===cols-1){
nextBox = 1000000;
}
else nextBox = 1;
break;
case 2 : hole = 0;
nextBox = cols;
break;
case 3 : hole = 1;
if(box.coordinate.cols===0){
nextBox = 1000000;
}
else nextBox = -1;
break;
}
return {hole:hole , nextBox:nextBox};
}
逻辑之前已经说过了,而且代码也比较简单,就不解释了,getHole是返回当前水管块的出水口如果要连通,需要的进水口的参数以及水管在水管数组里的位置。
然后就是路径,如何保证一定有条成功的路呢?因为如果管道全部随机的话,可能会陷入死路。所以,我就干脆定好几条正确的路径,每次刷新页面就取其中一条,其他水管块也添加一些随机出来的水管作干扰。所以,我就专门用一个data.js文件来存放所有的路径,路径文件代码如下:
//水管路径
var allPath = [
[
{rows:0,cols:0,style:1},
{rows:1,cols:0,style:1},
{rows:2,cols:0,style:1},
{rows:3,cols:0,style:2},
{rows:3,cols:1,style:2},
{rows:2,cols:1,style:2},
{rows:2,cols:2,style:1},
{rows:2,cols:3,style:1},
{rows:2,cols:4,style:1},
{rows:2,cols:5,style:1},
{rows:2,cols:6,style:1},
{rows:2,cols:7,style:2},
{rows:3,cols:7,style:1},
], [
{rows:0,cols:0,style:1},
{rows:1,cols:0,style:1},
{rows:2,cols:0,style:2},
{rows:2,cols:1,style:2},
{rows:1,cols:1,style:2},
{rows:1,cols:2,style:2},
{rows:2,cols:2,style:2},
{rows:2,cols:3,style:1},
{rows:2,cols:4,style:1},
{rows:2,cols:5,style:1},
{rows:2,cols:6,style:2},
{rows:1,cols:6,style:2},
{rows:1,cols:7,style:2},
{rows:2,cols:7,style:1},
{rows:3,cols:7,style:1},
], [
{rows:0,cols:0,style:1},
{rows:1,cols:0,style:1},
{rows:2,cols:0,style:1},
{rows:3,cols:0,style:2},
{rows:3,cols:1,style:2},
{rows:2,cols:1,style:1},
{rows:1,cols:1,style:2},
{rows:1,cols:2,style:1},
{rows:1,cols:3,style:1},
{rows:1,cols:4,style:1},
{rows:1,cols:5,style:2},
{rows:1,cols:5,style:2},
{rows:2,cols:5,style:2},
{rows:2,cols:6,style:1},
{rows:2,cols:7,style:2},
{rows:3,cols:7,style:1},
], ]
路径的每一个点包含的参数就是行列位置以及水管的类型,如果想游戏更多变化,可以再多加几条路径,我就只弄了三条啦:下面是取路径然后生成相对应的水管,同时把水管的旋转角度也随机。
var n = getRandom(0 , allPath.length-1);
var path = allPath[n];
path.foreach(function(){
var index = this.rows*cols + this.cols;
if((this.rows==0&&this.cols==0)||(this.rows==(rows-1)&&this.cols==(cols-1))){
boxes[index] = new Box({x:(this.cols*boxWidth)+boxWidth/2+jiange , y:(this.rows*boxWidth)+boxWidth/2+marginTop} , this.style , true , 0 , {rows:this.rows , cols:this.cols});
}
else if(this.rows>0&&this.rows<3&&this.cols>2&&this.cols<5){
boxes[index] = new Box({x:(this.cols*boxWidth)+boxWidth/2+jiange , y:(this.rows*boxWidth)+boxWidth/2+marginTop} , this.style , true , 0.5*Math.PI , {rows:this.rows , cols:this.cols});
}
else{
boxes[index] = new Box({x:(this.cols*boxWidth)+boxWidth/2+jiange , y:(this.rows*boxWidth)+boxWidth/2+marginTop} , this.style , false , parseInt(getRandom(0,3))*0.5*Math.PI , {rows:this.rows , cols:this.cols});
}
});
然后就木有啦。。。。
源码地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Game-demo/connectPipe
用canvas写个接水管小游戏的更多相关文章
- canvas写个简单的小游戏
之前在HTML5 Canvas属性和方法汇总一文中,介绍过Canvas的各种属性以及方法的说明,并列举了自己写的一些Canvas demo,接下来开始写一个简单的小游戏吧,有多简单,这么说吧,代码不到 ...
- 每个人都可以用C语言写的推箱子小游戏!今天你就可以写出属于自己项目~
C语言,作为大多数人的第一门编程语言,重要性不言而喻,很多编程习惯,逻辑方式在此时就已经形成了.这个是我在大一学习 C语言 后写的推箱子小游戏,自己的逻辑能力得到了提升,在这里同大家分享这个推箱子小游 ...
- html5+Canvas实现酷炫的小游戏
最近除了做业务,也在尝试学习h5和移动端,在这个过程中,学到了很多,利用h5和canvas做了一个爱心鱼的小游戏.点这里去玩一下 PS: 貌似有点闪屏,亲测多刷新两下就好了==.代码在本地跑都不会闪, ...
- python写的battle ship小游戏 - 1.0
最近学python,这是今天写的一个小游戏. from random import randint class Board(object): board = [] def __init__(self, ...
- 10分钟用scratch写一个大鱼吃小鱼的小游戏
第一次给张江小朋友教Scratch课程之前,还在担心一些概念能不能向小朋友解释清楚,可上完课发现,我严重低估了小朋友的聪明程度,发现现在的孩子相比较自己8.9岁的时候,简直聪明太多倍了. 所以总结了半 ...
- 发个2012年用java写的一个控制台小游戏
时间是把杀狗刀 突然发现了12年用java写的控制台玩的一个文字游戏,有兴趣的可以下载试试哈汪~ 里面难点当时确实遇到过,在计算倒计时的时候用了多线程,当时还写了好久才搞定.很怀念那个时间虽然不会做游 ...
- linux下用gtk+写比赛赌博GUI小游戏
游戏界面全部由gtk的GUI完成,没有使用openGL之类的高端货. 游戏玩法就是8位选手比赛跑步,你可以在赛前赌哪位选手会赢,如果输了cash会被扣除,反之cash会增加. 无聊写了3个选项:小数时 ...
- python Tkinter 写一个弹球的小游戏
#!usr/bin/python #-*- coding:utf-8 -*- from Tkinter import * import Tkinter import random import tim ...
- js+canvas 一只一担小游戏
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
随机推荐
- JavaScript 高级程序设计第二版
20.4 部署 20.4.1 构建 构建过程始于在源控制中定义用于存储文件的逻辑结构.最好避免使用一个文件存放所有的JavaScript,遵循以下面向对象语言中的典型模式:将每个对象或自定义了类别分别 ...
- map映射巧用 A-B Problems
A-B problem Description 大家都非常熟悉 A+B Problem! 题目看多了也有审美疲劳,于是我舍弃了,改用 A-B problem! 题目是这样的:给出一串数以及一个数字 C ...
- 改变input type="file" 文字、样式等
<div class="tac"> <input type="file" id="browsefile" class=&q ...
- [转]搭建Hadoop伪分布式环境
https://my.oschina.net/MyHeaven1987/blog/1821509 http://hadoop.apache.org/docs/current/hadoop-projec ...
- C# 之 反射性能优化2
问题回顾 在上篇博客中,我介绍了优化反射的第一个步骤:用委托调用代替直接反射调用. 然而,那只是反射优化过程的开始,因为新的问题出现了:如何保存大量的委托? 如果我们将委托保存在字典集合中,会发现这种 ...
- Fiddler的安装与使用
Fiddler是位于客户端和服务器端之间的代理,也是目前最常用的抓包工具之一 .它能够记录客户端和服务器之间的所有 请求,可以针对特定的请求,分析请求数据.设置断点.调试web应用.修改请求的数据,甚 ...
- YII框架增删改查常用语句
//实例化db $db = new \yii\db\Query(); //插入 $db->createCommand()->insert('user', [ 'name' => 't ...
- bitset里面一些函数的用法
- P1101 单词方阵 简单dfs
题目描述 给一n \times nn×n的字母方阵,内可能蕴含多个“yizhong”单词.单词在方阵中是沿着同一方向连续摆放的.摆放可沿着 88 个方向的任一方向,同一单词摆放时不再改变方向,单词与单 ...
- Ubuntu 硬盘分区只读,重新挂载为读写分区之后,文件依然创建出错
原因: 分区只读,可能是windows没有正常关机,或者使用了混合休眠模式. 解决方案: sudo mount -o remount,rw /dev/sdaX 若重新挂载后,创建文件以及文件夹失败: ...