原生JS超级马里奥(第五天)
上一章讲的是键盘事件监听,实现马里奥的跳跃功能,本章讲述马里奥和大地(砖块)的碰撞检测,代码有点多,原视频长度差不多1个小时20分钟,大家可以看原视频进行理解,边看边敲,我在这里写的代码和注释仅我自己的理解,
本章的提交937989dbe9a60259cc55965db7eb4019cffba6c3、c19d8d5b85120b606eba0a149e4df0685a13dcfd
github地址:ainuo5213的超级马里奥
本节目录:
目录文件讲解:
1. traits/GoTrait.js:改变马里奥x方向的速度,当按下左键和右键的时候改变速度方向以此达到马里奥行走的效果
2. Math.js:原Velocity.js,改名为Math.js,存储一些关于数学相关的数据,例如矩阵(Matrix)、矢量(Vector)
3. Level.js:游戏的关卡对象,内部含有实体数据、碰撞检测、tile格子和更新方法等
4. setupKeyboard.js:抽离原本在入口文件中对于事件监听的代码到新的模块,并加入左右键的事件监听
5. TileCollider.js:碰撞检测类,用于检测某个实体和tile格子中大地格子(砖块)之间的碰撞
6. TileResolver.js:获取tile格子数据的辅助类,通过各种手段获取tile格子及其相关的数据
本节实现效果
入口文件变动
入口文件拆分了原本事件监听到单独的模块,加入了碰撞检测layer的挂载,然后添加了对于鼠标左键和鼠标移动的事件监听处理
玛丽奥创建模块变动
马里奥创建模块中,马里奥去除了速度的特征(因为要检测碰撞改变速度)、加入了移动的特征
加载模块变动
加载模块改动了加载关卡数据方法,在其中 加入了layer的创建和tile格子矩阵数据的初始化
图层模块变动
图层模块将原本绘制背景的方法去除,改动创建背景图层方法,将其中循环绘制背景的代码去掉,改为循环tile矩阵数据进行背景的绘制。
键盘状态模块改动
键盘状态模块将原本使用keyCode方式改为了code
实体模块改动
实体模块将原本Velocity对象改为了Victor对象
新增Go特征
import { Trait } from '../Entity.js'export class GoTrait extends Trait {constructor() {super('go');// 时长this.dir = 0;// 行走速度this.speed = 6000;}// 更新马里奥的x方向的速度(含当前马里奥的方向)update = (entity, deltaTime) => {entity.vel.x = this.speed * this.dir * deltaTime;}
}
新增Level对象
level对象包含整个游戏中所有数据,包括碰撞检测、实体列表、背景tile格子等数据
import { Compositor } from "./Compositor.js"
import TileCollider from "./TileCollider.js"
import { Matrix } from "./Math.js"export class Level {constructor() {// 重力加速度this.gravity = 2000;// 组合器this.compositor = new Compositor();// 实体列表this.entities = new Set();// tile格子this.tiles = new Matrix();// tile格子碰撞检测类this.tileCollider = new TileCollider(this.tiles);}update = deltaTime => {// 循环每一个实体,调用其更新方法后,再更新其位置信息,进行碰撞检测(碰撞检测内部会更改实体的位置,如果实体真的发生了碰撞的话)this.entities.forEach(entity => {entity.update(deltaTime);entity.pos.x += entity.vel.x * deltaTime;this.tileCollider.checkX(entity);entity.pos.y += entity.vel.y * deltaTime;this.tileCollider.checkY(entity);entity.vel.y += this.gravity * deltaTime;});}
}
新增Math对象
修改原本的Velocity.js为Math对象,在内部新增了Matrix对象,用于记录背景tile格子数据
// 矢量对象
export class Vector {constructor(x, y) {this.x = x;this.y = y;}set(x, y) {this.x = x;this.y = y;}
}// 矩阵数据,是个二维数组,存储渲背景(sky和ground)的每个单元格(16x16)的数据,数据例子:
// [
// [{ tile: 'groud'}, { tile: 'sky' }], []
// ]
export class Matrix {constructor() {this.grid = [];}forEach = callback => {this.grid.forEach((column, x) => {column.forEach((tile, y) => {callback(x, y, tile);})});}get = (x, y) => {const col = this.grid[x];if (col !== undefined) {return col[y];}return undefined;}set = (x, y, value) => {if (this.grid[x] === undefined) {this.grid[x] = [];}this.grid[x][y] = value;}
}
键盘监听事件处理抽离
将原本位于入口文件中的事件监听移到了单独的模块,用来内聚事件监听
import { KeyboardState, CODE_SPACE, STATE_KEYDOWN, CODE_RIGHT, CODE_LEFT } from './KeyboardState.js'function setupKeyboard(mario) {// 监听键盘的空格事件const keyboard = new KeyboardState();// 添加键盘空格键的映射,并设置一个回调来使马里奥跳跃(设置y方向的速度)keyboard.addMapping(CODE_SPACE, keyState => {if (keyState === STATE_KEYDOWN) {mario.jump.start();}else {mario.jump.cancel();}});// 左键和右键控制马里奥x方向速度的方向keyboard.addMapping(CODE_RIGHT, keyState => {mario.go.dir = keyState;});keyboard.addMapping(CODE_LEFT, keyState => {mario.go.dir = -keyState;});return keyboard;
}export default setupKeyboard;
马里奥碰撞检测类
马里奥碰撞检测类用于对马里奥移动过程中检测马里奥是否和砖块碰撞,如果碰撞了就改动马里奥的位置
import TileResolver from "./TileResolver.js"export default class TileCollider {constructor(tiles) {this.tiles = tiles;this.tileResolver = new TileResolver(tiles);}/*** 马里奥Y方向的碰撞检测* @param {Entity} entity 马里奥实体*/checkY = entity => {// 这里不检测马里奥当前这个格子,做一个优化let y;if (entity.vel.y > 0) {y = entity.pos.y + entity.size.y} else if (entity.vel.y < 0) {y = entity.pos.y} else {return;}// 找马里奥当前所在的格子范围,然后对每个格子进行碰撞检测:即马里奥y方向的高度和马里奥自己的高度与匹配出的格子高度对比const matches = this.tileResolver.searchByRange(entity.pos.x,entity.pos.x + entity.size.x,y,y);matches.forEach(match => {if (!match) {return;}// 跳过非大地的tileif (match.tile.name !== "ground") {return;}if (entity.vel.y > 0) {if (entity.pos.y + entity.size.y > match.y1) {entity.pos.y = match.y1 - entity.size.y;entity.vel.y = 0;}} else if (entity.vel.y < 0) {if (entity.pos.y < match.y2) {entity.pos.y = match.y2;entity.vel.y = 0;}}});}checkX = entity => {// 原理同checkYlet x;if (entity.vel.x > 0) {x = entity.pos.x + entity.size.x} else if (entity.vel.x < 0) {x = entity.pos.x}else {return;}const matches = this.tileResolver.searchByRange(x,x,entity.pos.y,entity.pos.y + entity.size.y);matches.forEach(match => {if (!match) {return false;}if (match.tile.name !== "ground") {return false;}if (entity.vel.x > 0) {if (entity.pos.x + entity.size.x > match.x1) {entity.pos.x = match.x1 - entity.size.x;entity.vel.x = 0;}} else if (entity.vel.x < 0) {if (entity.pos.x < match.x2) {entity.pos.x = match.x2;entity.vel.x = 0;}}});}
}
格子矩阵数据获取辅助类
该类用于通过各种手段获取某个格子的矩阵数据和相关其他数据,例如始末位置等
export default class TileResolver {constructor(matrix, tileSize = 16) {this.matrix = matrix;this.tileSize = tileSize;}// 获取当前x或y位置所在的格子索引toIndex = pos => {return Math.floor(pos / this.tileSize);}// 将x和x1或y和y1两个位置,转化为其之间的相隔的格子索引。例如toIndex(17, 33) => [1, 2]toIndexRange = (pos1, pos2) => {const posMax = Math.ceil(pos2 / this.tileSize) * this.tileSize;const range = [];let pos = pos1;do {range.push(this.toIndex(pos));pos += this.tileSize;} while (pos < posMax);return range;}// 通过格子所在的索引获取tile数据,包括tile、当前格子所在位置始末(含x和y方向)getByIndex = (indexX, indexY) => {const tile = this.matrix.get(indexX, indexY);if (tile) {const y1 = indexY * this.tileSize;const y2 = y1 + this.tileSize;const x1 = indexX * this.tileSize;const x2 = x1 + this.tileSize;return {tile,x1,x2,y1,y2,}}}// 通过位置找tile数据,现将其转换为格子所在的索引,再找出格子的tile和当前格子的位置始末数据searchByPosition = (positionX, positionY) => {const indexX = this.toIndex(positionX);const indexY = this.toIndex(positionY);return this.getByIndex(indexX, indexY);}// 通过传入格子所在的始末位置数据找格子所在tile数据范围,并形成格子始末位置所对应的始末tile数据范围数组searchByRange = (x1, x2, y1, y2) => {const mathes = [];this.toIndexRange(x1, x2).forEach(indexX => {this.toIndexRange(y1, y2).forEach(indexY => {const match = this.getByIndex(indexX, indexY);if (match) {mathes.push(match);}});});return mathes;}
}
今天代码有点多,各位可以看原视频更好理解,代码仅作参考
原生JS超级马里奥(第五天)相关推荐
- 原生JS超级马里奥(第三天)
上一章主要是绘制马里奥和绘制图像代码的部分重构 本章主要是用于时间控制,实现马里奥每次弹跳的定点和弹跳起点一致,对源代码拆分比较多,各位可以跟着原作者视频一起敲 本章的提交ID:90dc4d0a02c ...
- 原生JS超级马里奥(第一天)
这段时间心血来潮,在网上找了一大堆前端关于游戏制作的博客,诸如飞机大战.魂斗罗等,但是个人思维限制,可能想的不是很多.很全面,最终放弃了个人独立开发的打算.皇天不负有心人,最终在youtube上找到了 ...
- 原生JS超级马里奥(第四天)
上一章讲的是马里奥速度改变的事件控制,本章改动较多,例如将马里奥位置改变拆分为一个一个的特征类,还有键盘事件监听,实现马里奥的跳跃功能,各位可以跟着原作者视频一起敲 本章的提交ID:e5caf33c8 ...
- 原生JS超级马里奥(第八天)
上一章讲述马里奥前进和后退动画以及其他一些动画,本章讲述马里奥跳跃动作和刹车的动作 本章的提交ID:493f38d8628e5beaa23c72ff6c38f76e46fa5b3a github地址: ...
- 原生JS超级马里奥(第九天)
上一章讲述马里奥跳跃动作和刹车的动作,本章对Json文件改动较多,我会一一讲解,还涉及到了部分代码重构 本章的提交ID:7419c4a70c772a45795d20ceb6559c1e1e8d8e3a ...
- 游戏感:虚拟感觉的游戏设计师指南——第十五章 超级马里奥64
这是一本游戏设计方面的好书 转自天:天之虹的博客:http://blog.sina.com.cn/jackiechueng 感谢天之虹的无私奉献 Word版可到本人的资源中下载 第十五章超级马里奥64 ...
- 前端原生js实现图片轮播效果,超级简单,备注详细
原生js实现简单轮播图,效果如下 纯生js实现轮播图 链接: link. 图片: 我们可以通过左右两边的箭头来播放图片,在我们的鼠标放在图片上时,自动播放结束,转化为手动播放,可以通过小圆点来点击切换 ...
- gdiplus判断一个点是否在圆弧线上_面试前必读!!!原生JS补给(上)
前言 你盼世界,我盼望你无bug.Hello 大家好!我是霖呆呆! 几个月前看过一遍三元大佬的<(建议收藏)原生JS灵魂之问, 请问你能接得住几个?>[¹]系列,当时是利用上下班公交的时间 ...
- 机器学习原来这么有趣!第二章:用机器学习制作超级马里奥的关卡
第一章:全世界最简单的机器学习入门指南 https://blog.csdn.net/wskzgz/article/details/89917343 第二章:用机器学习制作超级马里奥的关卡 https: ...
最新文章
- 有了Windows Defender应用程序防护功能,再也不担心电脑免遭恶意***
- [MIPS汇编语言]对于数的输入和输出
- Word中更新交叉引用
- Redis基础知识之—— hset 和hsetnx 的区别
- 在rancher服务器页面添加Host
- [luogu3676]小清新数据结构题
- oracle is null效率,Oracle查询优化之is null和is not null优化
- javascript-阻止默认行为发生
- 17.立体匹配——动态规划公式(Dynamic Programming Formulation),二维网格上的相干立体_4
- Javascript、jQuery 操作select控件大全(新增、修改、删除、选中、清空、判断存在等)(转)...
- c语言游戏小型程序代码,C语言小游戏源码
- 常用的图像标注工具汇总
- 能否构成三角形的条件代码_初中阶段数学三角形相关知识点汇总,超全
- 期货和股票平仓时成本计价的区别(期货和股票平仓时成本计价的区别是什么)
- Win7 每天定时关机
- origin双y轴数据散点图显示
- 【看表情包学Linux】环境安装 | 安装XShell | 基本的账号管理
- 【宇麦科技】群晖NAS套件之Drive的安装与使用(一),保姆级教程来喽!
- 9款免费且超实用的响应式网页测试工具
- 1252:走迷宫 2021-01-05