原生JS超级马里奥(第一天)
Posted ainuo5213
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原生JS超级马里奥(第一天)相关的知识,希望对你有一定的参考价值。
这段时间心血来潮,在网上找了一大堆前端关于游戏制作的博客,诸如飞机大战、魂斗罗等,但是个人思维限制,可能想的不是很多、很全面,最终放弃了个人独立开发的打算。皇天不负有心人,最终在youtube上找到了前端关于超级马里奥的视频,链接见Super Mario In Javascript
打算两周时间,每天花点时间看一到两集,并将所看视频整理出思路和代码供大家参考,当然也会讲解一些自己探寻出来的原理,并写明注释
github链接见:ainuo5213的超级马里奥,可以从该链接获取源码和素材,我争取做到每天更新直至结束,小伙伴们也可以自己再此基础上发挥或重构
第一张实现效果图如下:
目录结构
目录文件讲解:
1-1.json:目前存储关卡的数据,比如背景渲染的一些东西
loader.js:导入配置相关比如图片资源、关卡数据资源等
main.js:入口文件
UnitStyleSheet.js:渲染背景的类文件
Canvas裁剪图片
在此之前我需要先讲解一下canvas裁剪图片的原理,canvas.drawImage,通常传递5个数据,分别是image/video/canvas、x、y、width、height,但是另外还可以传递4个参数,用于裁剪图片,详情见HTML5 canvas drawImage() 方法,举个例子:
const canvas = document.getElementById("screen");
const context = canvas.getContext("2d");
loadImageAsync("/src/assets/tiles.png")
.then(image =>
canvas.getContext("2d")
.drawImage(
image,
0,
0,
16,
16,
0,
0,
16,
16);
);
上述方法描述为:从(0, 0)裁剪一个16x16的图像并放置到(0,0)且16x16的一个方块内,图像显示为
原图:
可以看到我们能够成功将第一个方块剪切出来。
导入配置相关:
// 异步导入图片
export function loadImageAsync(url)
return new Promise(resolve =>
const img = new Image();
img.onload = function ()
resolve(img);
img.src = url;
);
// 异步导入关卡数据
export function loadLevelAsync(name)
return fetch(`/src/levels/$name.json`)
.then(r => r.json());
单位图像样式对象
export default class UnitStyleSheet
/**
* 创建一个图像样式定义对象
* @param htmlImageElement image 背景图像
* @param number width 单位图像宽度
* @param number height 单位图像高度
*/
constructor(image, width, height)
this.image = image;
this.width = width;
this.height = height;
this.tiles = new Map();
/**
* 存储需要裁剪的图片
* @param string name 画布名称
* @param number x 需要裁剪的图片x与单位高度倍数
* @param number y 需要裁剪的图片y与单位高度倍数
*/
define(name, x, y)
const canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
// 裁剪图片,并存储
canvas.getContext("2d")
.drawImage(
this.image,
x * this.width,
y * this.height,
this.width,
this.height,
0,
0,
this.width,
this.height);
this.tiles.set(name, canvas);
/**
* 画图像
* @param string name 画布名称
* @param any context 上下文对象
* @param number x 需要画图像的x坐标
* @param number y 需要画图像的y坐标
*/
draw(name, context, x, y)
const canvas = this.tiles.get(name);
context.drawImage(canvas, x, y, this.width, this.height); // drawImage第一个参数接收类型:图像、视频、画布
/**
* 画单元格
* @param string name 画布名称
* @param any context 上下文对象
* @param number x 需要画图像的x坐标(倍数)
* @param number y 需要画图像的y坐标(倍数)
*/
drawTile(name, context, x, y)
this.draw(name, context, x * this.width, y * this.height)
入口函数:
import UnitStyleSheet from "./UnitStyleSheet.js";
import loadImageAsync, loadLevelAsync from "./loader.js";
// 用于绘制背景,双重循环的x和y依赖于关卡配置文件
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);
)
const canvas = document.getElementById("screen");
const context = canvas.getContext("2d");
loadImageAsync("/src/assets/tiles.png")
.then(image =>
// 创建单位图像样式对象,设置单位长度,并定义裁剪起始位置来裁剪图像
const unitStyleSheet = new UnitStyleSheet(image, 16, 16);
unitStyleSheet.define("ground", 0, 0);
unitStyleSheet.define("sky", 3, 23);
loadLevelAsync('1-1')
.then(level =>
level.backgrounds.forEach(background =>
drawBackground(background, context, unitStyleSheet);
)
);
);
当然油管UP主写的代码很好,思路很清晰,我自己第一次看到也觉得需要好好的理解一番,当然各位小伙伴可以去看看视频,雀食不错。
以上是关于原生JS超级马里奥(第一天)的主要内容,如果未能解决你的问题,请参考以下文章
历史上的今天9 月 13 日:计算机先驱诞生日;第一台装载硬盘的超级计算机;《超级马里奥兄弟》发布