暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

HTML5 游戏开发实战 | 人物拼图

432


拼图游戏是指将一幅图片分割成若干拼块并将它们随机打乱顺序,当将所有拼块都放回原位置时,就完成了拼图(游戏结束)。


01

游戏介绍

在“游戏”中,单击滑块选择游戏难易,“容易”为3行3列拼图游戏,中间为一个4行4列拼图游戏,“难”为5行5列拼图游戏。拼块以随机顺序排列,玩家用鼠标单击空白块的四周来交换它们的位置,直到所有拼块都回到原位置。

拼图游戏的运行界面如图7-1所示。


■ 图7-1拼图游戏的运行界面


02

游戏设计的思路

HTML5可以把图片整合到网页中。使用Canvas元素可以在这个空白的画布上填充线条,载入图片文件,甚至动画效果。这里制作拼图游戏用来展示HTML5 Canvas的图片处理能力。

游戏程序首先显示以正确顺序排列的图片缩略图,根据玩家设置的分割数,将图片分割成相应tileCount行列数的拼块,并按顺序编号。动态生成一个大小tileCount×tileCount的数组boardParts,用于存放0、1、2到tileCount×tileCount-1的数,每个数字代表一个拼块。例如,4×4的游戏拼块编号如图7-2所示。


■ 图7-2拼块编号示意图

游戏开始时,随机打乱这个数组boardParts,假如boardParts[0]是5,则在左上角显示编号是5的拼块。根据玩家用鼠标单击的拼块和空白块所在位置,来交换该boardParts数组对应的元素,最后依据元素排列顺序来判断是否已经完成游戏。


03

游戏设计的步骤

1. 游戏页面

<!doctype html>
<html>
<head>
  <title>拼图游戏</title>
  <style>
  .picture {
    border: 1px solid black;
  }
  </style>
</head>
<body>
  <div id="title">
    <h2>拼图游戏</h2>
  </div>
  <div id="slider">
    <form>
      <label>容易</label>
      <input type="range" id="scale" value="4" min="2" max="5" step="1">
      <label></label>
      <img id="source" width="120px" height="120px" src="defa.jpg" >
    </form>
    <br>
  </div>
  <div id="main" class="main">
    <canvas id="puzzle" width="480px" height="480px"></canvas>
  </div>
  <script src="sliding.js"></script>
</body>
</html>

复制


在网页中使用Canvas标记创建画板。

<canvas width="480px" height="480px"></canvas>
复制


Canvas的宽度和高度使用像素为单位。如果这两个属性没有被指定,它们的默认宽度为300px,高度为150px。

网页中< div id="slider">包括了另一个HTML5标记: range input,这个标记<input type="range">可以让用户拖放滑块并选择一个数值。这里设置滑块最小值为3,最大值为5。滑块值为3表明拼图游戏是3行3列的,滑块值为4表明拼图游戏是4行4列的,滑块值为5表明拼图游戏是5行5列的。

<img id="source" width="120px" height="120px" src="defa.jpg" >显示原图defa.jpg的缩小图,供玩家参照移动拼块。

2. sliding.js文件

在页面上画图需要使用Canvas的上下文环境,通过调用getContext()方法获取上下文环境。

var context = document.getElementById('puzzle').getContext('2d');
复制


然后,还需要一个和Canvas相同大小的图片'defa.jpg'。

var img = new Image();
img.src = 'defa.jpg';
img.addEventListener('load', drawTiles, false);//load事件监听,即图片加载完成事件

复制


加入这个'load'事件是确保图片完成加载后,再把图片放入Canvas中。drawTiles()函数绘制打乱的图块。

var boardSize = document.getElementById('puzzle').width; //获取画板(画布)的宽度
var tileCount = document.getElementById('scale').value; //获取滑块的值

复制


boardSize是Canvas的宽度,通过range input设置拼图的数量tileCount,数据范围从3到5(几行几列)。

var tileSize = boardSize  tileCount; //计算出拼块的大小宽度
复制


最后定义3个变量,其中两个Object对象变量,emptyLoc保存空白拼图的位置(emptyLoc.x,emptyLoc.y),clickLoc记录用户单击的位置(clickLoc.x,clickLoc.y)。而一个bool变量solved是指拼图是否完成,所有的拼图都找到正确的位置后,设置为True。

var context = document.getElementById('puzzle').getContext('2d');
var img = new Image();
img.src = 'defa.jpg';
img.addEventListener('load', drawTiles, false); //load事件监听,即图片加载完成事件
var boardSize = document.getElementById('puzzle').width; //获取画板(画布)的宽度
var tileCount = document.getElementById('scale').value; //获取滑块的值
var tileSize = boardSize tileCount;
var clickLoc = new Object; //记录单击鼠标所在的位置
clickLoc.x = 0;
clickLoc.y = 0;
var emptyLoc = new Object; //记录空白拼图的位置
emptyLoc.x = 0;
emptyLoc.y = 0;

var solved = false; //拼图是否完成,false为未完成

复制


下面实现拼块的随机排列——使用一个一维数组存储每个拼块的编号。每个元素代表一个拼块,初始时元素的数组下标与拼块的编号相同,说明位置正确。所以需要打乱数组的元素顺序,实现拼块的随机排列。而数组的元素顺序打乱使用带有排序函数的Array.sort()方法来实现。

var boardParts = new Object();
initBoard(); //初始化拼块,并随机排列
function initBoard() {
    boardParts = new Array(tileCount * tileCount);
    for (var i = 0; i < tileCount * tileCount; i++) {
        boardParts[i] = i;
    }
    shift(); //拼块的随机排列
}
function sortNumber(a,b) {//随机排序函数
    return Math.random() > 0.5 ? -1 : 1;
    //return a-b;
}
function shift() { //拼块的随机排列
    boardParts.sort(sortNumber);
    emptyLoc.x = 0;
    emptyLoc.y = 0;
    solved = false;
}

复制


以上就实现了拼块的随机放置。但是真正显示拼块在屏幕上的是drawTiles()函数。drawTiles()函数用于显示各个拼块,该函数判断是否是空白拼图的位置(emptyLoc.x,emptyLoc.y),不是则调用drawImage()函数绘制相应图块。

drawImage()函数最常用的是传入3个参数:image对象,以及图片相对于画布的x、y坐标。

drawImage(image, x, y);
复制


还可以加入两个参数用于设置图片的宽度和高度。

drawImage(image, x, y, width, height);
复制


最复杂的drawImage()函数有9个参数,按顺序分别为:图片对象、图片x坐标、图片y坐标、图片宽、图片高、目标x坐标、目标y坐标、目标宽和目标高。后4个参数主要是为了截取原图部分用来显示。这里把boardParts记录的拼块显示在(i*tileSize,j*tileSize)处。

//绘制所有拼块
function drawTiles() {
  context.clearRect(0, 0, boardSize, boardSize);
  for (var i = 0; i < tileCount; i++) { //列号
      for (var j = 0; j < tileCount; j++) { //行号
          var n = boardParts[i * tileCount + j];
          //计算出编号为n的拼块在原图的位置坐标(行列号)
          var x = parseInt(n tileCount); //丢弃小数部分, 保留整数部分
          var y = n % tileCount;
          console.log(x + ":" + Math.floor(n tileCount) + ":" + y);
          if (i!= emptyLoc.x||j!= emptyLoc.y||solved==true) {//不是空白拼图的位置且游戏未结束
          //或者if( !(i== emptyLoc.x&&j== emptyLoc.y&&solved==false))可能更容易明白
              //将编号为n的拼块显示在(i * tileSize, j * tileSize)处
              context.drawImage(img, x * tileSize, y * tileSize, tileSize, tileSize,
                   i * tileSize, j * tileSize, tileSize, tileSize);
          }
      }
  }
}

复制


以下是事件定义。

首先为滑块定义触发事件,当它改变了,要重新计算拼块的数量和大小。滑块被移动时触发onchange事件,事件中计算拼块宽度大小,重新初始化画布,显示各个拼块。

document.getElementById('scale').onchange = function() {
  tileCount = this.value;
  tileSize = boardSize tileCount;// 计算拼块宽度大小
  initBoard();//重新初始化拼块,并随机排列
  drawTiles();//显示各个拼块
};

复制


追踪鼠标经过的拼块以及哪个拼块被单击。画板中移动鼠标的onmousemove事件中,计算出鼠标所在网格坐标clickLoc.x,clickLoc.y。

document.getElementById('puzzle').onmousemove = function(e) {
  clickLoc.x = Math.floor((e.pageX - this.offsetLeft) tileSize);
  clickLoc.y = Math.floor((e.pageY - this.offsetTop) tileSize);
};

复制


画布中单击鼠标的onmousemove事件中,计算出鼠标所在网格坐标clickLoc.x,clickLoc.y与空块位置间隔,如果间距为1则移动被单击的拼块。

document.getElementById('puzzle').onclick = function() {
  if (distance(clickLoc.x, clickLoc.y, emptyLoc.x, emptyLoc.y) == 1) {
    slideTile(emptyLoc, clickLoc); //交换被单击的拼块与空块位置
    drawTiles(); //显示各个拼块
  }
  if (solved) {//如果成功
    setTimeout(function() {alert("你成功了!");}, 500);
  }
};
function distance(x1, y1, x2, y2) {
  return Math.abs(x1 - x2) + Math.abs(y1 - y2);
}

复制


注意,一些浏览器会在重画画布之前弹出对话框,为了防止弹出,一定要用延迟。

setTimeout(function() {alert("你成功了!");}, 500);
复制


设置延迟0.5秒后再弹出提示框“你成功了!”。

slideTile(emptyLoc,clickLoc)是移动被单击的拼块clickLoc到空块位置emptyLoc。移动拼图的做法是:交换对应的boardParts元素,然后把单击位置设置成空块位置。

function slideTile(emptyLoc, clickLoc) {
    if (!solved) {
        var t;
        t= boardParts[emptyLoc.x * tileCount+emptyLoc.y];
        boardParts[emptyLoc.x*tileCount+emptyLoc.y]=boardParts[clickLoc.x*tileCount+clickLoc.y];
        boardParts[clickLoc.x * tileCount + clickLoc.y] = t;
        emptyLoc.x = clickLoc.x; //emptyLoc重新记录空白块位置
        emptyLoc.y = clickLoc.y;
        checkSolved(); //检查是否成功
  }
}

复制


一旦拼图移动了,还要检查一下拼图是否全部在正确的位置。checkSolved()函数检查是否成功。如果有一个拼块不正确,函数就会返回False,否则返回True。

function checkSolved() {
  var flag = true;
  for (var i = 0; i < tileCount * tileCount; i++) {
      if (boardParts[i] != i) //判断元素排列顺序
           flag = false;
  }
  solved = flag;
}

复制


至此,完成拼图游戏的设计。


04

参考书籍



HTML5网页游戏设计从基础到开发(第2版·微课视频版)


作者:夏敏捷、尚展垒
定价:69.90元
ISBN:9787302629771

编辑推荐:11个游戏实战案例,提供视频、源代码、教学课件、教学大纲、扩展案例

技术背景
HTML5是 HyperText Markup Language 5 的缩写。HTML5 技术结合了 HTML4.01的相关标准并对其进行革新,符合现代网络发展要求。HTML5是互联网的下一代标准,是构建和呈现互联网内容的一种语言方式,被认为是互联网的核心技术之一。HTML5在2014年10月由万维网联盟(W3C)完成标准制定,仍处于完善之中。然而HTML5已经引起了业内的广泛兴趣,Chrome、Firefox、Opera、Safari等主流浏览器都已经支持HTML5技术,新Edge浏览器率先实现100%支持HTML5。
本书作者长期从事HTML5网页设计教学与应用开发,在长期的工作、学习中,积累了丰富经验和教训,能够了解在学习编程的时候需要什么样的书才能提高HTML5开发能力,以最少的时间投入得到最快的实际应用。
本书主要内容

本书分为基础篇和实战篇。

基础篇包括第1~6章,主要讲解HTML5的基础知识和相关新技术,如JavaScript、Canvas API画图、CSS3和jQuery及其使用技巧;

实战篇包括第7~17章,综合应用前面技术,开发经典的大家耳熟能详的游戏,如人物拼图、扑克翻牌、推箱子、五子棋、黑白棋、俄罗斯方块、贪吃蛇、雷电飞机射击、Flappy Bird、中国象棋。通过本书读者将学会如何利用HTML5和JavaScript、CSS3制作交互式游戏、平台类游戏,学会网页游戏设计。

本书特点
(1)内容全面,代码通用。本书所有案例的源代码通用性强,便于读者直接应用于大部分游戏的开发。
(2)理论夯实,案例丰富。每款游戏案例均提供详细的设计思路、关键技术分析以及具体的解决步骤方案,案例实用性强。






    文章转载自清华计算机学堂,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论