[Day11] Vite 出小蜜蜂~ Enemy!

Day11

延续上一回,我们尝试了 Function Composition 的技巧,
这次来试试看将同样的技巧用於 Enemy

这样做的目的是,接下来要导入 Level Design
我们可以透过 Level Design 的资料,动态生成 Enemy 的类型跟位置。

Base Enemy (Base Function)

首先,在 src/characters 底下新增 src/characters/Enemy.ts
CrabSquidOctopus 程序码中共同的部分抽离出来,
并透过一些参数来整合成一份。

程序码如下。

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) - 输出入资料

>>:  [iT铁人赛Day14]JAVA回圈的跳离范例

.Net Core Web Api_笔记17_api结合ADO.NET资料库操作part5_新闻文章新增_新闻类别元素透过API绑定方式

有了新闻类别相关的增删改查後 就要来进行新闻文章的增删改查功能导入 新建好NewsControll...

【Day 14】MySQL Basics III:Transactions / Locks

昨天的崩溃文比平常文章更多点击 笑死 我还没更新.. transaction isolation ...

[Day 13] 整体学习 (Ensemble Learning)

整体学习 (Ensemble Learning) 今日学习目标 了解整体学习 何谓整体学习? 三种不...

Day 27 Ruby == vs ===

阿修说文解字 先来看一下结论,因为 eql? 及 equal? 的用法与 == 类似,这边也顺便介绍...

全端开发者必懂的「产品设计」——全端开发者内功 IV

from Unsplash 为什麽工程师也要懂产品设计? 前面笔记提过,现代科技秒新分异、资讯爆炸...