延续上一回,我们尝试了 Function Composition
的技巧,
这次来试试看将同样的技巧用於 Enemy
。
这样做的目的是,接下来要导入 Level Design
,
我们可以透过 Level Design
的资料,动态生成 Enemy
的类型跟位置。
首先,在 src/characters
底下新增 src/characters/Enemy.ts
,
将 Crab
,Squid
,Octopus
程序码中共同的部分抽离出来,
并透过一些参数来整合成一份。
程序码如下。
type EnemyTypes = "squid" | "crab" | "octopus";
const EnemyImages: { [key in EnemyTypes]: number[][][] } = {
squid: [
[
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 1, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0, 1],
],
[
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 0, 1, 1, 0, 1, 0],
[1, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 1, 0],
],
],
octopus: [
[
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
],
[
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0],
],
],
crab: [
[
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0],
],
[
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0],
],
],
};
type Enemy = GameObject & Transform & Renderer & Collision & Shooter;
type Props = {
type: EnemyTypes;
position: Vector;
};
export default function Enemy({ type, position }: Props): Enemy {
const images = EnemyImages[type];
let current = 0;
let timePass = 0;
return {
position,
update(delta) {
timePass += delta;
if (timePass > 1000) {
current += 1;
timePass = 0;
this.renderer.src = images[current % images.length];
this.canShoot = true;
}
},
canShoot: false,
shoot() {
const { x, y } = this.position;
const [w, h] = [images[0].length, images.length];
return EnemyLaser({
position: { x: x + w / 2, y: y + h + 1 },
update(it) {
it.position.y += 1;
},
});
},
renderer: {
type: "graphics",
src: images[current % images.length],
},
collider: {
size: { x: images[0].length, y: images.length },
},
};
}
接着,到 src/scenes/Game.ts
调整一下。
export default function Game(screen: Rectangle): Scene<Container> {
let instances: GameObject[] = [
LaserCannon(screen),
+ Enemy({ type: "crab", position: { x: 0, y: 0 } }),
];
return {
update(delta) {
collisionDetect(instances.filter(canCollision).filter(canTransform));
instances.forEach((instance) => {
if (canControl(instance)) {
instance.handleInput(getKeyPressed());
}
if (canShoot(instance) && instance.canShoot) {
requestAnimationFrame(() => {
instances = [...instances, instance.shoot()];
});
instance.canShoot = false;
}
if (instance.destroy) {
requestAnimationFrame(() => {
instances = instances.filter((_instance) => _instance !== instance);
});
return;
}
instance.update?.(delta);
});
},
render(stage) {
instances
.filter(canRender)
.forEach((instance) => render(stage, instance));
},
};
}
这样我们就能根据情境,动态决定要产生何种 Enemy
了。
<<: 从 JavaScript 角度学 Python(13) - 输出入资料
有了新闻类别相关的增删改查後 就要来进行新闻文章的增删改查功能导入 新建好NewsControll...
昨天的崩溃文比平常文章更多点击 笑死 我还没更新.. transaction isolation ...
整体学习 (Ensemble Learning) 今日学习目标 了解整体学习 何谓整体学习? 三种不...
阿修说文解字 先来看一下结论,因为 eql? 及 equal? 的用法与 == 类似,这边也顺便介绍...
from Unsplash 为什麽工程师也要懂产品设计? 前面笔记提过,现代科技秒新分异、资讯爆炸...