[Day8] Vite 出小蜜蜂~撞击检测 Collision Detection!

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

关於兔兔们:


<<:  [Day10] Google Cloud Platform 简介

>>:  修改word 作者属性

【D10】市场热络程度:每日市场成交资讯

前言 常见的市场热络程度,也有用成交金额判断,当大家踊跃交易的时候,容易价格抬升。试想如果,大家预期...

Day13-JDK堆内存快照工具-jmap(三)自动导出内存映像文件

前言 接续着上篇,这篇要说明的是如何自动导出hprof文件,针对自动导出又有分两种状况:已是运行中的...

DAY16 MongoDB Explain 与 Index 建议

DAY16 MongoDB Explain 与 Index 建议 MongoDB explain -...

DAY08 - [CSS+RWD] 图文交错排版,资料不打结!

今日文章目录 应用情境 事前准备 CSS 说明 参考资料 应用情境 针对重复性的资料流中,指定其中...

Day28 用python写UI-聊聊Treeview(二)

今天的范例也有结合昨天的程序码,要聊聊直接在介面上做增减,等不及的话就赶快往下滑吧~ ♠♣今天的文章...