本篇主要讲解,如何利用原生的 JavaScript

来实现一个简单的拼图小游戏。

线上体验地址:拼图

一、游戏的基础逻辑

想用一门语言来开发游戏,必须先了解如何使用这门语言来实现一些基础逻辑,比如图像的绘制、交互的处理、定时器等。

1、图形绘制

图形绘制是一切的基础,这里使用 JavaScript 在 canvas 上进行绘制。即先在 html 中创建 canvas 元素,然后在 JavaScript 中,通过 id 拿到这个元素,并且通过 canvas 拿到对应的上下文环境 context ,为后续的绘图做好准备。

<canvas id="background" width="450px" height="450px"></canvas>
var background = document.getElementById("background");
var context = background.getContext('2d');

通过 context 的 drawImage 方法可以绘制图片,这里进行了相应的封装:

注:这里要等图片加载完毕后再进行绘制,即在 onload 中去调用 drawImage 方法,否则会绘制失败。

var drawImageItem = function(index, position) {
var img = new Image();
img.src = './image/dog_0' + String(index+1) + '.jpg';
img.onload = () => {
var rect = rectForPosition(position);
context.drawImage(img, rect[0], rect[1], rect[2], rect[3]);
}
}

在绘制图片之后,我们还需要去动态刷新视图,否则 canvas 就只是一张静态的图片。如果是简单的图形刷新,只需在原来的位置重新绘制,进行覆盖即可。但有时候我们只需要将原来已存在的图形清除掉,而不需要绘制新图案。比如在拼图游戏中,将一个方块移动到另一个位置后,需要清空原来的位置。

通过 context 的 clearRect 方法可以达到清除的目的。以下是清除 canvas 的某个区域的代码:

var originRect = rectForPosition(origin);
context.clearRect(originRect[0], originRect[1], originRect[2], originRect[3]);

2、事件处理

有了图形的绘制后,我们还需要处理玩家的输入事件,然后根据输入事件,来决定什么时候刷新视图。输入事件可以分为 3 种:在手机上有触屏事件;在 PC 上,有鼠标和键盘事件。

JavaScript 中对触屏和鼠标点击的监听是一样的,都是通过 canvas 的 onclick 事件进行回调,具体如下:

// 屏幕点击
background.onclick = function(e) {
};

我们可以通过 e.offsetX 、 e.offsetY 来获取触控点在 canvas 中的位置。

注: canvas 的坐标原点在左上角,即左上角的坐标是 (0, 0) 。

键盘的按键点击则是通过 document 的 onkeyup 、 onkeydown 等事件进行回调。 onkeyup 是指按键的抬起事件, onkeydown 是指按键的按下事件。我们可以通过 keyCode 知道当前具体是哪一个按键,然后根据不同的按键去处理不同的逻辑,如下:

if (event.keyCode == '37') {  // 左
// do something
} else if (event.keyCode == '38') { // 上
// do something
} else if (event.keyCode == '39') { // 右
// do something
} else if (event.keyCode == '40') { // 下
// do something
}

3、定时器

有时候,除了在玩家输入的时候需要去刷新视图,还需要每隔一段时间定时去刷新视图。比如在一个贪吃蛇游戏中,就需要每隔一段时间就去刷新蛇的位置。

这个时候我们就需要一个定时器,让它每隔一段时间去执行一段刷新视图的代码。我们通过 setInterval 方法来实现定时器功能:

setInterval("run()", 100);

上面这段代码表示每隔 100 毫秒,去执行一次 run 方法。

二、拼图的基础逻辑

有了游戏的基础逻辑,下面来看一下如何实现拼图的逻辑。

1、生成随机序列

因为不是任意序列都可以通过平移的方式来还原,所以我们不能简单地生成一个随机序列。比如 1、0、2、3、4、5、6、7、8 这个序列,无论怎么平移,都不可能还原。

这里采取的做法是:预先设置了 4 个可还原的序列,先从这 4 个序列中随机选取一个,然后再对序列进行模拟平移若干步骤。以此来尽可能地保证初始序列的多样性,也保证了序列的可还原性。具体代码如下:

var setupRandomPosition = function() {
var list1 = [4, 3, 2, 8, 0, 7, 5, 6, 1];
var list2 = [2, 0, 5, 6, 8, 7, 3, 1, 4];
var list3 = [3, 7, 2, 4, 1, 6, 8, 0, 5];
var list4 = [3, 2, 4, 1, 7, 6, 5, 0, 8];
var lists = [list1, list2, list3, list4]; imageIndexForPosition = lists[parseInt(Math.random() * 4)]; // 获取空位位置
var emptyPosition = 0;
for (var i = imageIndexForPosition.length - 1; i >= 0; i--) {
if (imageIndexForPosition[i] == lastIndex()) {
emptyPosition = i;
break;
}
}
background.emptyPosition = emptyPosition; // 随机移动次数
var times = 10;
while (times--) {
// 获取随机数,决定空位哪个位置进行移动
var direction = parseInt(Math.random() * 4); var target = -1;
if (direction == 0) {
target = topOfPosition(emptyPosition); // 上
} else if (direction == 1) {
target = leftOfPosition(emptyPosition); // 左
} else if (direction == 2) {
target = rightOfPosition(emptyPosition); // 右
} else if (direction == 3) {
target = bottomOfPosition(emptyPosition); // 下
}
if (target < 0 || target > lastIndex()) { // 位置不合法,继续下一次循环
continue;
}
var result = moveImageIfCanAtPosition(target);
if (result >= 0) { // 如果移动成功,更新空位的位置
emptyPosition = target;
}
}
}

2、判断是否可以移动方块

在保存顺序的时候,是用 0~8 这 9 个数字来保存,而空白的方块是数字 8 的位置。所以判断可以移动的唯一条件是,目标位置的值是否为 8。代码如下:

var isPositionEmpty = function(position) {
if (position < 0 || position > lastIndex()) {
return false;
}
if (imageIndexForPosition[position] == lastIndex()) {
return true;
} else {
return false;
}
}

上面 lastIndex() 的值为 8。

3、实现方块移动

方块移动的实现很简单,先将旧位置的图形清除,然后在新的位置绘制。

var refreshImagePositions = function(origin, target) {
var originRect = rectForPosition(origin);
context.clearRect(originRect[0], originRect[1], originRect[2], originRect[3]);
drawImageItem(imageIndexForPosition[target], target);
}

4、检查是否完成

检查图案是否已经还原,只需要对数组进行一次遍历,看是否有序即可。

var checkIfFinish = function() {
for (var index = 0; index < imageIndexForPosition.length; index++) {
if (index != imageIndexForPosition[index]) {
return false;
}
}
return true;
}

5、交互事件屏蔽

当图案还原之后,我们不希望玩家还能通过键盘或鼠标来移动方块,这个时候就需要对交互事件进行屏蔽。

只需要一个标志位就可以达到这个目的:

// 屏幕点击
background.onclick = function(e) {
if (isFinish) {
return;
} // do something
}; // 键盘按钮事件
document.onkeyup = function(event) {
if (isFinish) {
return;
} // do something
}

当图案还原之后,标志位 isFinish 会被置为 true ,然后在屏幕点击和键盘按钮响应事件的开始处添加判断,如果已经结束,则不继续走方块移动的逻辑。

原文链接:https://www.jianshu.com/p/e53...

用 JavaScript 实现简单拼图游戏的更多相关文章

  1. JavaScript写一个拼图游戏

    拼图游戏的代码400行, 有点多了, 在线DEMO的地址是:打开: 因为使用canvas,所以某些浏览器是不支持的: you know: 为什么要用canvas(⊙o⊙)?  因为图片是一整张jpg或 ...

  2. atitit.html5 拼图游戏的解决之道.

    atitit.html5 拼图游戏的解决之道. 1. 拼图游戏的操作(点击法and 拖动法) 1 1. 支持键盘上.下.左.右键移动: 1 2. 支持点击空白模块中的上下左右箭头移动: 1 3. 支持 ...

  3. 拼图游戏js

    实现算法: 1. JavaScript动态生成拼图:通过生成16个div,且除最后一个div不使用背景图片以外,其他div都设置拼图图片为背景.然后通过调整background-position来实现 ...

  4. JavaScript拼图游戏

    今天是2016年最后一天上班了.最近几天都比较休闲,有时间空闲下来写写文档之类的. 2016过得真是快.感觉没做什么就过去了.想到之前想坚持每个月写一写博客都没坚持到.希望2017年可以吧. 无聊之余 ...

  5. 利用Vue.js实现拼图游戏

    之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/luozhihao/p/5 ...

  6. Vue.js实现拼图游戏

    Vue.js实现拼图游戏 之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/ ...

  7. 一款html拼图游戏详解

    本文是爱编程原创翻译,转载请看清文末的转载要求,谢谢合作! 游戏介绍 这篇文章是献给web游戏开发者用简单的开发工具开发一款游戏.此文介绍了用html.css.javascript只需简单和几个步骤开 ...

  8. C#实现拼图游戏

    C#实现<拼图游戏> (下) 原理篇   前言:在 http://www.cnblogs.com/labixiaohei/p/6698887.html 程序设计 之 C#实现<拼图游 ...

  9. SDL制作拼图游戏

    看完教程第三集后,好像自己能用这个来写一个简单的拼图游戏,第一次写出个带界面的游戏,好有成就感. 图片是自己慢慢截左上部分8个脸. #include <stdio.h> #include ...

随机推荐

  1. 【模板】最大流模板(dinic)

    题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. 输入输出格式 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向边的个数.源点序号.汇点序号. 接下来M行每行 ...

  2. 《黑白团团队》第八次团队作业:Alpha冲刺 第五天

    项目 内容 作业课程地址 任课教师首页链接 作业要求 团队项目 填写团队名称 黑白团团队 填写具体目标 认真负责,完成项目 团队项目Github仓库地址链接. 第五天 日期:2019/6/19 成员 ...

  3. 驱动中的IO访问

    1,内存空间与IO空间 1)I/O 映射方式(I/O-mapped) 典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间 ...

  4. 使用SeaJS,require加载Jquery的时候总是为null

    这个问题困扰了我两天,使用别人的例子.官网down下来的example都没有问题.但是放到自己项目里就 var $=require("jquery") 为null. 后来发现,jq ...

  5. 使用动态代理实现dao接口

    使用动态代理实现dao接口的实现类 MyBatis允许只声明一个dao接口,而无需写dao实现类的方式实现数据库操作.前提是必须保证Mapper文件中的<mapper>标签的namespa ...

  6. Shell编程入门(第二版)(上)

    简单的示例Shell程序 示例1. #!/bin/bash #This is to show what a shell script looks like echo "Our first e ...

  7. Android ViewGroup拦截触摸事件具体解释

    前言 在自己定义ViewGroup中.有时候须要实现触摸事件拦截.比方ListView下拉刷新就是典型的触摸事件拦截的样例. 触摸事件拦截就是在触摸事件被parent view拦截,而不会分发给其ch ...

  8. 6.设置ListView的Item的高度无效

    问题: 设置ListView的Item的高度无效. 解决方式: 设置ListView的Item的minHeight属性.

  9. 使用android.graphics.Path类自绘制PopupWindow背景

    PopupWindow简单介绍 PopupWindow是悬浮在当前activity上的一个容器,用它能够展示随意的内容. PopupWindow跟位置有关的API有以下几个: showAsDropDo ...

  10. void型指针

    void型指针,表示这个指针指向的内存中的数据的类型要由用户来指定. 比方内存分配函数malloc函数返回的指针就是void *型. 用户在使用这个指针的时候.要进行强制类型转换,也就是显式说明该指针 ...