[Day18] Vite 出小蜜蜂~ 位置校正 Position Adjustment!

Day18

接下来再进到分数系统之前,
卡比要先进行位置的校正,使我们更接近原作。

Enemy

首先我们需要校正 Enemy 的位置,
我们将每个整个 Enemy 区块切成 5 * 11 的格子,
而一个格子的长宽为 16

但因为我们的对齐方式是对齐格子的左上,
所以我们要一些调整,让 Enemy 置中对齐。

-- src/characters/Enemy.ts

export type EnemyProps = {
  type: EnemyTypes;
  position: Vector;
  id: number;
+ grid: number;
};
export default function Enemy({
  type,
  id,
  position,
+ grid,
}: EnemyProps): IEnemy {
  const images = EnemyImages[type];

  let current = 0;

+ const height = images[current].length;
+ const width = images[current][0].length;

  return {
    id,
    tags: ["enemy"],
+   position: {
+     ...position,
+     x: position.x + grid / 2 - width / 2,
+   },

    set frame(value) {
      current = value % images.length;

      this.renderer.src = images[current];
    },
    get frame() {
      return current;
    },

    canShoot: false,
    shoot() {
      const { x, y } = this.position;
+     const [w, h] = [width, height];

      return EnemyLaser({
        position: { x: x + w / 2, y: y + h + 1 },
        update(it) {
          it.position.y += 1;
        },
      });
    },

    renderer: {
      type: "graphics",
      src: images[current],
    },

    collider: {
+     size: { x: width, y: height },
    },
  };
}

Game

接下来我们要调整整个 Enemy 的区块在画面上的位置,
我们希望整个区块能对其画面的正中间,
所以我们需要 screen 的 长宽资讯。

移动 INIT_POSITIONpoints 到函式内以取得 screen

并且,我们要接着调整 Enemy 的移动距离,
透过改动 SequentialMovementstep

矫正如下

-- src/scenes/Game.ts

export default function Game(screen: Rectangle): Scene<Container> {
+ const INIT_POSITION = {
+   x: screen.width / 2 - (GRID_SIZE * ROW_WIDTH) / 2,
+   y: 50,
+ };

+ const points: EnemyProps[][] = [
+   "squid",
+   "crab",
+   "crab",
+   "octopus",
+   "octopus",
+ ].map((type, y, list) =>
+   Array.from({ length: ROW_WIDTH }, (_, x) => ({
+     id: (list.length - 1 - y) * ROW_WIDTH + x,
+     type: type as EnemyTypes,
+     position: {
+       x: INIT_POSITION.x + x * GRID_SIZE,
+       y: INIT_POSITION.y + y * GRID_SIZE,
+     },
+     grid: GRID_SIZE,
+   }))
+ );

  let instances: GameObject[] = [
    LaserCannon(screen),
    ...spawn(Enemy, points),
    ...GameHUD(),
  ];

  const update = ap(
    SequentialMovement({
      counts: instances.filter(isEnemy).length,
+     step: { x: 2, y: GRID_SIZE / 2 },
    }),
    RandomlyShoot({
      row: ROW_WIDTH,
      rate: 1000,
    })
  );

  return {
    update(delta) {
      collisionDetect(instances.filter(canCollision).filter(canTransform));

      update(delta, instances);

      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) {
      clear();

      instances
        .filter(canRender)
        .forEach((instance) => render(stage, instance));
    },
  };
}

SequentialMovement

除了改动移动的距离之外,
我们也需要调整移动的方式,让 Enemy 能限制在画面中间移动。

type Props = {
  counts: number;
+ step: Vector;
};
export function SequentialMovement({ counts, step }: Props) {
+ const movement = { x: step.x, y: 0 };
+ let offset = 0;

+ let direction = 1;
  let index = 0;

  return (_: number, instances: GameObject[]) => {
    const enemies = instances.filter(isEnemy);

    let processed = enemies.length > 0;

    while (processed) {
      enemies
        .filter((instance) => instance.id === index)
        .forEach((instance) => {
          instance.position.x += movement.x;
          instance.position.y += movement.y;
          instance.frame += 1;

          processed = false;
        });

      index = (index + 1) % counts;
    }

    if (index === 0) {
+     if (offset === 0) movement.y = 0;

+     offset += direction;
    }

+   if (Math.abs(offset) < 10) return;

+   movement.x *= -direction;
+   if (offset === 10) movement.y = step.y;

+   offset = 0;
+   direction *= -1;
  };
}

关於兔兔们:


<<:  AE自动消除画面动态物件-Day27

>>:  Day 13 Component Lifecycle -2

D18 文件修改页 Modify doc

文件创建後可能要修改标记或是更改上传的档案 只能修改自己发的文件 先看使用者是否登入以及要修改的文件...

30天学会 Python: Day 13-站在巨人的肩上

tags: python, iron_man title: PyDay12 现代的人可以快速得产出...

postman

昨天介绍了API,今天要介绍一个postman的应用程序,它是一个可以让我们检查和实作API的app...

Day01 - 铁人赛我又来罗

避免像去年一样焦头烂额,这次提前至 7 月开始准备铁人赛, 即便提早准备,也不知要写什麽... 只准...

Flutter基础介绍与实作-Day16 Onboarding、Login、Sign Up范例实作(3)

大家昨天消化的怎麽样啊!我们今天就从昨天说的忘记密码页开始吧! 忘记密码页 我们一样先来构思一下忘记...