接下来卡比要是着操作 LaserCannon,让他可以左右移动。
在上个章节,卡比介绍了 GameLoop。
示意用
while (true) {
processesInput();
update();
render();
}
但是,有个我们还没有实作到部分,也就是 processInput
,
这个环节会负责处理玩家输入的指令,并针对指令产生对应的动作。
各种平台会支援不同的输入硬件,像是 键盘、滑鼠、电玩手把 ...etc。
在这边,卡比会撰写如何使用键盘来操作游戏。
在浏览器,负责接收键盘输入相关的事件叫做 KeyboardEvent,
不同於表单用的 InputEvent
,这个只用来处理当下玩家在键盘做了何种操作,属於比较底层的事件。
而卡比接下来会需要 keydown
跟 keyup
这两个事件,
透过这两个事件来得知玩家按下或放开哪个按键。
document.addEventListener("keydown", (event) => {});
document.addEventListener("keyup", (event) => {});
首先,在 types.ts
增加一个 enum
。
-- src/types.ts
export enum Key {
Left,
Right,
}
然後建立一个新的资料夹 src/systems
,并新建 src/systems/input.ts
。
有一点需要注意的,就是这个事件的触发速度跟 GameLoop
并不同步,
所以我们需要做一些调整,让处理速度跟 GameLoop
一样快。
-- src/systems/input.ts
import { Key } from "../types";
let pressed: Set<Key> = new Set();
document.addEventListener("keydown", (event) => {
if (event.code === "ArrowLeft") {
pressed.add(Key.Left);
}
if (event.code === "ArrowRight") {
pressed.add(Key.Right);
}
});
document.addEventListener("keyup", (event) => {
if (event.code === "ArrowLeft") {
pressed.delete(Key.Left);
}
if (event.code === "ArrowRight") {
pressed.delete(Key.Right);
}
});
export function getKeyPressed() {
return Array.from(pressed);
}
这边我们提供 getKeyPressed
让其他程序码知道当下玩家按下的按键是什麽。
用 Array 或是 Set 来呈现 pressed
是因为玩家可能会同时按下两个以上的按键。
接着,我们要将这个资料传递到需要使用的物件。
新增 handleInput
在 GameObject
介面,
因为不是每个物件都需要处理 handleInput
跟 update
,这边我们采用 optional chaining
。
-- src/types.ts
export interface GameObject {
+ handleInput?(pressed: Key[]): void;
- update(delta: number): void;
+ update?(delta: number): void;
render(app: Application): void;
}
-- src/main.ts
+ import { getKeyPressed } from "./systems/input";
const app = new Application({
width: 20,
height: 20,
resolution: 10,
});
document.querySelector("#app")?.append(app.view);
+ const instance = LaserCannon();
app.ticker.add(() => {
app.stage.removeChildren();
+ instance.handleInput?.(getKeyPressed());
- instance.update(app.ticker.deltaMS);
+ instance.update?.(app.ticker.deltaMS);
instance.render(app.stage);
});
然後我们在 LaserCannon
里面实作 handleInput
这个方法。
-- src/characters/LaserCannon.ts
export default function LaserCannon(): GameObject {
return {
handleInput(pressed) {
if (pressed.includes(Key.Left)) {
console.log("move left");
return;
}
if (pressed.includes(Key.Right)) {
console.log("move right");
return;
}
},
render(app) {
const graphics = new Graphics();
for (let y = 0; y < image.length; y++) {
for (let x = 0; x < image[y].length; x++) {
if (image[y][x] === 0) continue;
graphics.beginFill(0xffffff);
graphics.drawRect(x, y, 1, 1);
graphics.endFill();
}
}
app.stage.addChild(graphics);
},
};
}
赶快试试看按下去的时候,console
会不会印出东西!
接下来就是让 LaserCannon 动起来,
我们需要新增一个参数用来记录每个物件当前的位置。
首先,新增用於记录位置的型别 Vector
。
记得有实作 GameObject 介面的物件都要补上喔!
-- src/types.ts
export type Vector = {
x: number;
y: number;
};
export interface GameObject {
+ position: Vector;
handleInput?(pressed: Key[]): void;
update?(delta: number): void;
render(app: Application): void;
}
接下来在 LaserCannon
这边实作 position
,
并在 handleInput
这边判断,
假设按下的是左键,就往左边移动 1 px,
假设按下的是右键,就往右边移动 1 px,。
-- src/characters/LaserCannon.ts
export default function LaserCannon(): GameObject {
return {
+ position: { x: 0, y: 0 },
handleInput(pressed) {
if (pressed.includes(Key.Left)) {
- console.log("move left");
+ this.position.x -= 1;
return;
}
if (pressed.includes(Key.Right)) {
- console.log("move right");
+ this.position.x += 1;
return;
}
},
render(app) {
const graphics = new Graphics();
for (let y = 0; y < image.length; y++) {
for (let x = 0; x < image[y].length; x++) {
if (image[y][x] === 0) continue;
graphics.beginFill(0xffffff);
graphics.drawRect(x, y, 1, 1);
graphics.endFill();
}
}
app.stage.addChild(graphics);
+ graphics.position.set(this.position.x, this.position.y);
},
};
}
最後,将位置同步到 Graphics
物件上,就可以操作 LaserCannon
啦!
对了,画面太小记得调大一点。
-- src/main.ts
const app = new Application({
- width: 20,
+ width: 80,
- height: 20,
+ height: 80,
- resolution: 10,
+ resolution: 5,
});
LaserCannon
移动范围不要超过我们的画面大小呢?提示 1
function clamp(min: number, max: number, value: number) {
return Math.max(Math.min(max, value), min);
}
提示 2
app.screen
Tensorflow Serving 虽然帮你跑模型,但它并不负责展示网页,或是一些预处理的部分。 ...
网页需要制作时间表、收费表等,都可以使用表格制作。 只要了解表格相关的标签,就能够轻松做出表格了,这...
Metrics - 观察系统的健康指标 系列文章 (1/6) - Metrics 与 Metricb...
哈罗~ 今天是铁人赛的最後一天, 来抢个团队中第一发文的位子XD 之前每几日来个小结, 最後一天就来...
永丰金流收款 API 在目前我们从文件看到的,支援信用卡付款及虚拟帐号 ATM 付款。信用卡付款方式...