iT邦幫忙

2021 iThome 鐵人賽

DAY 1
0
Modern Web

Vite 出小蜜蜂~和卡比一起玩網頁遊戲開發!系列 第 18

[Day17] Vite 出小蜜蜂~ 介面 (HUD)!

Day17

開始做 介面 (HUD)
接下來都是用之前有實作過的技巧!

Render

因為要調整 Text 的位置,我們需要調整一下 render

function Text(instance: Renderer & Transform) {
  if (instance.renderer.type !== "text") return;

  const src = instance.renderer.src;

+ const text = new _Text(src, {
    fontFamily: "VT323",
    fontSize: 16,
    fill: 0xffffff,
  });

+ const { x, y } = instance.position;
+ text.position.set(x, y);

+ return text;
}

export function render(stage: Container, instance: GameObject & Renderer) {
  let child: DisplayObject | undefined;

  if (instance.renderer.type === "graphics" && canTransform(instance)) {
    child = Graphics(graphics, instance);
  }

+ if (instance.renderer.type === "text" && canTransform(instance)) {
    child = Text(instance);
  }

  child && stage.addChild(child);
}

Base Function

透過定義 Base Function 根據 props 的不同產生不同 Text

type IText = GameObject & Renderer & Transform;

type TextProps = {
  src: string;
  position: Vector;
};
function Text({ src, position }: TextProps): IText {
  return {
    renderer: {
      type: "text",
      src,
    },
    position,
  };
}

Score Component

透過 Array 下去做 Group

type ScoreProps = {
  title: string;
  value?: number;
  x: number;
};
function Score({ title, value, x }: ScoreProps): IText[] {
  return [
    Text({ src: title, position: { x, y: 0 } }),
    Text({
      src: value ? String(value).padStart(4, "0") : "",
      position: { x: x + 12, y: 12 },
    }),
  ];
}

Mount

組合這些畫面元件。

export default function GameHUD(): IText[] {
  return [
    ...Score({ title: "SCORE<1>", value: 70, x: 10 }),
    ...Score({ title: "HI-SCORE", value: 880, x: 90 }),
    ...Score({ title: "SCORE<2>", x: 160 }),
  ];
}

調整一下就完成了。

-- src/scenes/Game.ts

export default function Game(screen: Rectangle): Scene<Container> {
  let instances: GameObject[] = [
    LaserCannon(screen),
    ...spawn(Enemy, points),
+   ...GameHUD(),
  ];

  const update = ap(
    SequentialMovement({
      counts: instances.filter(isEnemy).length,
      step: 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));
    },
  };
}

Optimization

這時應該會再度遇到記憶體問題,我們要將每次產生出來的 PIXI.Text 釋放掉。

const graphics = new _Graphics();
let pools: DisplayObject[] = [];

export function clear() {
  pools.forEach((obj) => obj.destroy());
  pools = [];

  graphics.clear();
}
function Text(instance: Renderer & Transform) {
  if (instance.renderer.type !== "text") return;

  const src = instance.renderer.src;

  const text = new _Text(src, {
    fontFamily: "VT323",
    fontSize: 16,
    fill: 0xffffff,
  });

  const { x, y } = instance.position;

  text.position.set(x, y);

+ pools.push(text);

  return text;
}

這樣就不會因為 PIXI.Text 而產生記憶體問題。

關於兔兔們:


上一篇
[Day16] Vite 出小蜜蜂~ Text 文字!
下一篇
[Day18] Vite 出小蜜蜂~ 位置校正 Position Adjustment!
系列文
Vite 出小蜜蜂~和卡比一起玩網頁遊戲開發!19

尚未有邦友留言

立即登入留言