小游戏屏幕虚拟网格 - 像素游戏

我们知道,在平面世界,任何图形,几乎都可以看成是一个有起始点 (x,y) 和宽高 (width,height) 的矩形,不同的只是这个图形在矩形内部是如何绘制的

一般情况下,我们都把屏幕看成是一个起始点为 (0,0),宽高为屏幕像素宽高 (screenWidth,screenHeight) 的矩形

如果我们以 1px 横竖划分屏幕,那么每个小方块就是一个 1x1 的小正方形

这也是我们屏幕的网格系统,这种网格系统可以使用一个总长度为 width x height 的数组来表示

像素游戏

随着屏幕的分辨率越来越高,人眼已经看不清 1 x 1 像素的小正方形了,这时候我们就要把屏幕按照更高的像素单位来划分网格,当然,这是虚拟的,实际上我们没法改变屏幕本身的像素单位

例如以 w1px 来划分屏幕,那么屏幕的虚拟高宽就是 (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();

其它

大家还可以对这个小游戏做一些改动,比如

  1. 绘制边界,就是不使用屏幕边界,而是使用自己绘制的边界

  2. 加入另一个点,编程贪吃蛇游戏

  3. 加入如果是反方向可以掉头或者失败,因为自己撞了自己

  4. 改变每次重新画的局面,而是重绘变化的部分,提高帧率

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.