小游戏屏幕虚拟网格 - 像素游戏
我们知道,在平面世界,任何图形,几乎都可以看成是一个有起始点 (x,y)
和宽高 (width,height)
的矩形,不同的只是这个图形在矩形内部是如何绘制的
一般情况下,我们都把屏幕看成是一个起始点为 (0,0)
,宽高为屏幕像素宽高 (screenWidth,screenHeight)
的矩形
如果我们以 1px
横竖划分屏幕,那么每个小方块就是一个 1x1
的小正方形
这也是我们屏幕的网格系统,这种网格系统可以使用一个总长度为 width x height
的数组来表示
像素游戏
随着屏幕的分辨率越来越高,人眼已经看不清 1 x 1
像素的小正方形了,这时候我们就要把屏幕按照更高的像素单位来划分网格,当然,这是虚拟的,实际上我们没法改变屏幕本身的像素单位
例如以 w
个 1px
来划分屏幕,那么屏幕的虚拟高宽就是 (width/w,height/w)
此时我们可以使用一个长度为 (width/w) x (height/w)
的数组来表示这个屏幕
当然了,可能屏幕就会像图中那样,右边和下边不完整,那么数组的长度就是
(width/w +1 ) x (height/w +1)
整体的计算方法就是
// 以 `10px` 像素大小划分屏幕 var unit = 10; var w = Math.ceil(c.width / unit); var h = Math.ceil(c.height / unit); // 数组长度 var size[w*h];
这就是像素游戏划分屏幕的方法
虚拟网格
我们写一个范例,把上图中的虚拟网格给表现出来
game.js
var c = wx.createCanvas(); var ctx = c.getContext('2d'); var unit = 10; var w = Math.ceil(c.width / unit); var h = Math.ceil(c.height / unit); // 数组长度 var pixls = new Array(w * h); var cur = 0; var pos = 0; // 每一个数组的值,创建一个黑白相间的数组 for (var i = 0; i < h; i++) { cur = i % 2 == 0 ? 0 : 1; for (var j = 0; j < w; j++) { // 数组位置位置 pos = (i * w) + j; pixls[pos] = cur; cur = cur == 1 ? 0 : 1; } } function renderVirtualGrid() { ctx.save(); ctx.fillStyle = 'green'; ctx.fillRect(0, 0, c.width, c.height); ctx.fillStyle = "#fff"; pos = 0; for (var i = 0; i < h; i++) { for (var j = 0; j < w; j++) { pos = (i * w) + j; ctx.fillStyle = pixls[pos] == 0 ? "#000" : "#fff"; ctx.fillRect(j * unit, i * unit, unit, unit) } } ctx.textAlign = "center" // 居中对齐 ctx.textBaseline = "middle" //垂直居中绘制 ctx.fillStyle = "#aaa"; ctx.font = "13px Arial" // 字体大小 44 像素 ctx.fillText("简单教程,简单编程", c.width / 2, (c.height - 25)) ctx.fillText(" © 2015-2018 www.twle.cn", c.width / 2, (c.height - 10)); ctx.restore(); } renderVirtualGrid();
在微信中运行效果如下
范例: 贪吃蛇
虚拟网格有什么用呢?
比如做贪吃蛇游戏,小蛇每次只能前进一格子
我们写一个范例来模拟小蛇的移动吧
game.js
var c = wx.createCanvas(); var ctx = c.getContext('2d'); var unit = 10; var w = Math.ceil(c.width / unit); var h = Math.ceil(c.height / unit); // 数组长度 var pixls = new Array(w * h); var cur = 0; var pos = 0; // 每一个数组的值,创建一个黑白相间的数组 for (var i = 0; i < h; i++) { cur = i % 2 == 0 ? 0 : 1; for (var j = 0; j < w; j++) { // 数组位置位置 pos = (i * w) + j; pixls[pos] = cur; cur = cur == 1 ? 0 : 1; } } var tt = null; // 用于保存定时器 // 用来判断是否在游戏中 // 0 表示未开始 // 1 表示游戏进行中 // 2 表示游戏已经结束 var ingame = 0; // 用来保存开始触摸点 var lasted = { x: null, y: null }; // 降帧,每秒 2 帧 var fps = (60 / 2); var fps_s = 0; var game = { v: 1, direction: 1, // 0 1 2 3 分别表示上右下左 boxes:[], draw_boxes: function (ctx) { for (var i = 0; i < this.boxes.length; i++) { this.boxes[i].draw(ctx); } }, hit: function () { var b = this.boxes[0]; var x = (b.x+1) * unit; var y = (b.y+1) * unit; // +1 是因为原点在左上角 return x < 0 || y < 0 || (x+unit) > c.width || (y+unit) > c.height; }, move: function () { // 把数组里底部弹出来,然后在从数组头部插入一个元素 var box = this.boxes[0]; var x = box.x; var y = box.y; switch (game.direction) { case 0: //向上 y--; break; case 1: //向右 x++; break; case 2: //向下 y++; break; case 3: // 向左 x--; } var b = new Box(); b.x = x; b.y = y; this.boxes.unshift(b); this.boxes.pop(); return this; }, add: function () { var box = new Box(); var b2 = this.boxes[0]; var x = b2.x; var y = b2.y; switch (game.direction) { case 0: //向上 y--; break; case 1: //向右 x++; break; case 2: //向下 y++; break; case 3: // 向左 x--; } box.x = x; box.y = y; this.boxes.unshift(box); return this; } } // 蛇 function Box(){ this.x = 5; this.y = 5; this.color = 'black'; this.draw = function (ctx) { ctx.save(); ctx.fillStyle = "#000"; ctx.fillRect(this.x * unit, this.y * unit, unit, unit); } }; function reset_game() { game.v = 1; game.direction = 1; game.boxes = []; var box = new Box(); box.x = 5; box.y = 5; game.boxes.unshift(box); } function reset() { ctx.save(); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, c.width, c.height); ctx.textAlign = "center" // 居中对齐 ctx.textBaseline = "middle" //垂直居中绘制 ctx.fillStyle = "#aaa"; ctx.font = "13px Arial" // 字体大小 44 像素 ctx.fillText("简单教程,简单编程", c.width / 2, (c.height - 25)) ctx.fillText(" © 2015-2018 www.twle.cn", c.width / 2, (c.height - 10)); ctx.restore(); if (ingame == 0) { drawTip("请点击屏幕开始任意位置开始"); } } function drawTip(text) { ctx.save(); ctx.font = "22px Arial" // 字体大小 44 像素 ctx.fillStyle = '#333333'; ctx.textAlign = "center" // 居中对齐 ctx.textBaseline = "middle" //垂直居中绘制 ctx.fillText(text, c.width / 2, c.height / 2); ctx.restore(); } function animate_it() { fps_s++; // 降帧 if (fps_s % fps != 0) { requestAnimationFrame(animate_it); return; } reset(); if (fps_s % (fps * 10) == 0) { game.add(); } else { game.move(); } game.draw_boxes(ctx); if (game.hit()) { ingame = 2; drawTip("你失败了....,请点击画面重新开始"); cancelAnimationFrame(tt); return; } requestAnimationFrame(animate_it); } // 绘制文本 function drawText(ctx, text) { ctx.save(); ctx.font = "14px Arial" // 字体大小 44 像素 ctx.fillText(text, 10, 25); ctx.restore(); } // 触摸开始事件 wx.onTouchStart(function (e) { if (ingame == 0 || ingame == 2) { return; } lasted.x = e.touches[0].clientX lasted.y = e.touches[0].clientY }) // 手指离开屏幕 wx.onTouchEnd(function (e) { console.log("wx.ontouchend:"); if (ingame == 0 || ingame == 2) { ingame = 1; reset_game(); tt = requestAnimationFrame(animate_it); return; } var x = e.touches[0].clientX var y = e.touches[0].clientY var x0 = x - lasted.x; var y0 = y - lasted.y; // 左右滑动 if (Math.abs(x0) > Math.abs(y0)) { game.direction = x0 > 0 ? 1 : 3; } else { // 上下滑动 game.direction = y0 > 0 ? 2 : 0; } lasted.x = null; lasted.y = null; }) reset();
其它
大家还可以对这个小游戏做一些改动,比如
-
绘制边界,就是不使用屏幕边界,而是使用自己绘制的边界
-
加入另一个点,编程贪吃蛇游戏
-
加入如果是反方向可以掉头或者失败,因为自己撞了自己
-
改变每次重新画的局面,而是重绘变化的部分,提高帧率