iT邦幫忙

2021 iThome 鐵人賽

DAY 1
0
Modern Web

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

[Day8] Vite 出小蜜蜂~撞擊檢測 Collision Detection!

  • 分享至 

  • xImage
  •  

Day8

Collision Detection

接下來我們要讓 Laser 打中敵人時,將敵人消滅。
Laser 要有辦法偵測到目前打中誰,
所以需要一個新的功能來檢測目前哪幾個物件是互相重疊的,
這個就是 撞擊檢測 (Collision Detection)

Collision Detection 根據形狀的不同,實作的演算方式也不同。
這邊卡比用相對簡單的 Rect to Rect 檢測即可。

大致的演算方式如下:

interface Rect {
  x: number;
  y: number;
  w: number;
  h: number;
}

function points({ x, y, w, h }: Rect) {
  return [x, x + w, y, y + h];
}

function hitTest(o1: Rect, o2: Rect) {
  const [a1, a2, a3, a4] = points(o1);
  const [b1, b2, b3, b4] = points(o2);

  return [
    a2 >= b1,
    a1 <= b2,
    a4 >= b3,
    a3 <= b4,
    //
  ].every(Boolean);
}

export function collisionDetect(instances: GameObject[]) {
  for (let i = 0; i < instances.length; i++) {
    for (let j = i + 1; j < instances.length; j++) {
      if (hitTest(o1, o2)) {
        //...
      }
    }
  }
}

Collider

接下來就是要將我們的撞擊邏輯元件化,
我們需要新增一個元件 Collider
用於提供我們撞擊檢測中需要的資料,像是 widthheight

export interface Collision {
  collider: {
    size: Vector;
  };
}

export function canCollision<T extends GameObject>(
  instance: T
): instance is T & Collision {
  return "collider" in instance;
}

接著讓 LaserSquid 有辦法相撞。

function Laser({
  x,
  y,
}: Vector): GameObject & Transform & Renderer & Collision {
  return {
    renderer: {
      type: "graphics",
      src: [[1], [1], [1], [1]],
    },

    position: { x, y },

    update() {
      this.position.y -= 1;
    },

    collider: {
      size: { x: 1, y: 4 },
    },
  };
}
export default function Squid(): GameObject & Transform & Renderer & Collision {
  const images = [image1, image2];

  let current = 0;
  let timePass = 0;

  return {
    position: { x: 0, y: 0 },

    update(delta) {
      timePass += delta;

      if (timePass > 1000) {
        current += 1;
        timePass = 0;

        this.renderer.src = images[current % images.length];
      }
    },

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

    collider: {
      size: { x: image1[0].length, y: image1.length },
    },
  };
}

接著,在 Game 加入 collisionDetect

export default function Game(screen: Rectangle): Scene<Container> {
  let instances: GameObject[] = [LaserCannon(screen), Squid()];

  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;
        }

        instance.update?.(delta);
      });
    },

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

Collision Detect System

Rect 移動到 types

export interface Rect extends Vector {
  w: number;
  h: number;
}

接著,將實作完剛才的 collisionDetect 函式。

export function collisionDetect(instances: (Collision & Transform)[]) {
  for (let i = 0; i < instances.length; i++) {
    for (let j = i + 1; j < instances.length; j++) {
      const A = instances[i];
      const B = instances[j];

      const o1 = {
        x: A.position.x,
        y: A.position.y,
        w: A.collider.size.x,
        h: A.collider.size.y,
      };
      const o2 = {
        x: B.position.x,
        y: B.position.y,
        w: B.collider.size.x,
        h: B.collider.size.y,
      };

      if (hitTest(o1, o2)) {
        console.log("hit");
      }
    }
  }
}

關於兔兔們:


上一篇
[Day7] Vite 出小蜜蜂~Shoot 射擊系統!
下一篇
[Day9] Vite 出小蜜蜂~撞擊行為 Collision Behavior!
系列文
Vite 出小蜜蜂~和卡比一起玩網頁遊戲開發!19
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言