上一章主要用于绘制背景图像如天空、大地,这一章主要是绘制马里奥和绘制图像代码的部分重构,代码有点复杂,大家可以比较提交ID进行查看差异。
本章的提交ID:a338e45b76c1f4aabd06f5642d4abaa17f0d0947、4576245f764d7e0dfbcba1df1775e20fc26552a8
github地址:ainuo5213的超级马里奥
这里对新增目录文件进行解释:
Compositor.js:组合器,将绘制图像的高阶函数进行迭代调用
layer.js:图层模块,创建绘制图像的高阶函数,统一绘制方式,便于管理和维护
spirtes.js:加载外部文件,并返回单位图像样式对象,用于绘制图像
入口文件:
1. 将之前的drawBackground抽离出去,并将每一个Promise放到了Promise.all
2. 将之前绘制图像的部分代码进行重构,移动到了每一个函数里,进行功能聚合
// layers.js
/**
* 绘制背景:天空、大地
* @param {any} background 背景对象,来自于json配置
* @param {any} context canvas上下文对象
* @param {UnitStyleSheet} unitStyleSheet 单元图像样式对象
*/
function drawBackground(background, context, unitStyleSheet) {
background.ranges.forEach(([x1, x2, y1, y2]) => {
for (let x = x1; x < x2; x++) {
for (let y = y1; y < y2; y++) {
unitStyleSheet.drawTile(background.tile, context, x, y);
}
}
})
}
/**
* 创建绘制图像的函数:在回调中绘制图像
* @param {any} backgrounds 背景对象,来自于json配置
* @param {UnitStyleSheet} backgroundSprite 单元图像样式对象
* @returns {Function} 绘制图像的回调
*/
export function createBackgroundLayer(backgrounds, backgroundSprite) {
// 创建临时缓存区,使用闭包在外部绘制图像
const buffer = document.createElement("canvas");
buffer.width = 256;
buffer.height = 240;
// 循环将图像绘制到临时的画布
backgrounds.forEach(background => {
drawBackground(background, buffer.getContext("2d"), backgroundSprite);
});
// 返回绘制图像的回调
return function drawBackgroundLayer(context) {
context.drawImage(buffer, 0, 0);
}
}
/**
* 创建绘制图像的函数:在回调中绘制图像
* @param {UnitStyleSheet} sprite 单元图像样式对象
* @param {
{ x: number, y: number }}} pos 初始位置
* @returns 绘制图像的回调
*/
export function createrSpriteLayer(sprite, pos) {
return function drawSpriteLayer(context) {
for (let i = 0; i < 20; i++) {
sprite.draw('mario', context, pos.x + i * 16, pos.y);
}
}
}
这里将每一个绘制图像的代码移动带了回调函数里,在外部进行组合绘制
// Compositor.js
export class Compositor {
constructor() {
this.layers = [];
}
/**
* 将每个绘制图像的回调全部执行一次
* @param {any} context canvas上下文对象
*/
draw(context) {
this.layers.forEach(layer => {
typeof layer === "function" && layer(context);
})
}
}
// sprites.js
import UnitStyleSheet from "./UnitStyleSheet.js";
import { loadImageAsync } from "./loader.js";
export function loadBackgroundSprites() {
return loadImageAsync("/src/assets/tiles.png")
.then(image => {
const backgroundUnitStyleSheet = new UnitStyleSheet(image, 16, 16);
backgroundUnitStyleSheet.defineTile("ground", 0, 0);
backgroundUnitStyleSheet.defineTile("sky", 3, 23);
return backgroundUnitStyleSheet;
});
}
export function loadMarioSprite() {
return loadImageAsync("/src/assets/characters.png")
.then(image => {
const marioUnitStyleSheet = new UnitStyleSheet(image, 16, 16);
marioUnitStyleSheet.define("mario", 276, 44, 16, 32);
return marioUnitStyleSheet;
});
}
之前将加载外部文件写在了入口文件,那样会造成入口文件过大,而且不利于阅读,这里将其拆分为单独的模块,由入口文件引用