用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> ...
随机推荐
- Android播放功能的实现
Android播放语言功能的实现 需要用到的类文件TextToSpeech,此类可以实现播放语言的功能 支持播放的语言1.英语 2.法语 3.德语 4.意语 5.西班牙语 1.实例化此类,添加上下文对 ...
- Git坑换行符自动转换 [转载]
转自https://www.cnblogs.com/zjoch/p/5400251.html 源起 一直想在 GitHub 上发布项目.参与项目,但 Git 这货比较难学啊.买了一本<Git 权 ...
- [转] js对象监听实现
前言 随着前端交互复杂度的提升,各类框架如angular,react,vue等也层出不穷,这些框架一个比较重要的技术点就是数据绑定.数据的监听有较多的实现方案,本文将粗略的描述一番,并对其中一个兼容性 ...
- 安装CentOS 7(转)
转载地址:https://www.cnblogs.com/wcwen1990/p/7630545.html CentOS7安装详解 本文基于vmware workstations进行CentOS7 ...
- ionic2+中修改minSdkVersion的方法
具体方法很简单,直接在config.xml中找到下面这一行 <preference name="android-minSdkVersion" value="17&q ...
- 51Nod1317 相似字符串对 容斥原理 动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1317.html 题目传送门 - 51Nod1317 题意 称一对字符串(A,B)是相似的,当且仅当满 ...
- 删除ELK中的数据。。
直接使用这个命令就行了: curl -XDELETE "http://127.0.0.1:9200/logstash-2017.08.19" 2017.08.19可以动态生成, ...
- Excel表列名称(给定一个正整数,返回它在 Excel 表中相对应的列名称。)
例如, 1 -> A 2 -> B 3 -> C ... 26 -> Z 27 -> AA 28 -> AB ... 示例 1: 输入: 1 输出: "A ...
- 基于C语言的Socket网络编程搭建简易的Web服务器(socket实现的内部原理)
首先编写我们服务器上需要的c文件WebServer.c 涉及到的函数API: int copy(FILE *read_f, FILE * write_f) ----- 文件内容复制的方法 int Do ...
- scrapy 手动编写模板
import scrapy class Tzspider(scrapy.Spider): # spider的名字,唯一 name = 'tz' # 初始url列表 start_urls = ['htt ...