如何以ECS模式处理冲突?

问题描述 投票:0回答:1

我正在使用打字稿和ECS模式制作游戏。但是我不明白如何处理实体之间的冲突。我的实体Player具有一组组件:

  • LayerComponent-组件,保留要渲染的图层的名称;
  • PositionComponent-保持位置的组件;
  • AppearanceComponent-保留渲染选项的组件;
  • BoxColliderComponent-组件,用于保持碰撞手柄的AABB大小。

也有具有相同组件集的实体Enemy。这些实体在LayerComponent中的值不同。Player中的LayerComponent实体保留Player值,Enemy实体保留Enemy值。

我不知道如何处理这些实体之间的冲突。这些实体不应相互移动。

目前,我已经创建了系统PlayerPosition,该系统可以处理碰撞并阻止实体间的移动用BoxColliderComponent。但是我认为这是错误的,因为必须在自己的系统中处理冲突。

PlayerPosition的代码

import { System } from 'ecs';
import { ecs, EntityType } from 'game';

import Vector2, { IVector2 } from 'services/vector2.service';

import MouseService from 'services/mouse.service';
import ELayers from 'constants/layers';
import Enemy from 'entities/enemy';

interface IIntersect {
  position: IVector2;
  height: number;
  width: number;
}

export default class PlayerPositionSystem extends System<EntityType> {
  readonly ctx: CanvasRenderingContext2D;
  readonly entities: EntityType[] = [];

  private readonly mouse: MouseService = new MouseService();

  constructor(ctx: CanvasRenderingContext2D) {
    super();
    this.ctx = ctx;
  }

  addEntity(entity: EntityType): void {
    if (this.test(entity)) {
      this.entities.push(entity);
    } else {
      console.warn(`The entity '${entity.id}' have no necessary component`);
    }
  }

  test(entity: EntityType): boolean {
    const position = entity.components.position;

    return !!position;
  }

  update(entity: EntityType): void {
    const component = entity.components.position;
    const colliderComponent = entity.components.boxCollider;
    const layerComponent = entity.components.layer;

    if (!component || !colliderComponent || !layerComponent) {
      return;
    }

    if (layerComponent.props.layer !== ELayers.player) {
      return;
    }

    const mouseCoordinates = this.mouse.getMouseCoordinate();
    const { position, velocity } = component.props;

    const distance = mouseCoordinates.distance(position);
    const deltaVector = mouseCoordinates.subtraction(position);
    const inversionDistance = 1 / distance;
    const direction = new Vector2(
      deltaVector.x * inversionDistance,
      deltaVector.y * inversionDistance
    );
    const newPosition = position.addition(
      new Vector2(
        distance > 5 ? direction.x * velocity : 0,
        distance > 5 ? direction.y * velocity : 0
      )
    );

    const currentObject: IIntersect = {
      position: new Vector2(newPosition.x, newPosition.y),
      height: colliderComponent.props.size.y,
      width: colliderComponent.props.size.x,
    };

    for (const object of this.entities) {
      if (object === entity) {
        continue;
      }

      const itemComponents = object.components;
      const itemPosition =
        itemComponents.position && itemComponents.position.props;
      const itemBoxCollider =
        itemComponents.boxCollider && itemComponents.boxCollider.props;

      if (!itemPosition || !itemBoxCollider) {
        continue;
      }

      const item: IIntersect = {
        ...itemPosition,
        height: itemBoxCollider.size.y,
        width: itemBoxCollider.size.x,
      };

      if (this.intersect(currentObject, item)) {
        const itemLayer = object.components.layer;
        if (itemLayer && itemLayer.props.layer === ELayers.enemy) {
          object.remove();
          const canvas = this.ctx.canvas;
          let x = Math.random() * canvas.width - 100;
          x = x < 0 ? 0 : x;
          let y = Math.random() * canvas.height - 100;
          y = y < 0 ? 0 : y;
          ecs.addEntity(Enemy({ velocity: 3, position: new Vector2(x, y) }));
        }

        let x = newPosition.x;
        let y = newPosition.y;

        if (
          this.intersect(
            {
              ...currentObject,
              position: new Vector2(x, position.y),
            },
            item
          )
        ) {
          x = position.x;
        }

        if (
          this.intersect(
            {
              ...currentObject,
              position: new Vector2(position.x, y),
            },
            item
          )
        ) {
          y = position.y;
        }

        newPosition.set(new Vector2(x, y));
      }
    }

    component.setProperties({ position: newPosition });
  }

  intersect(object: IIntersect, object2: IIntersect): boolean {
    const { position: pos1, height: h1, width: w1 } = object;
    const { position: pos2, height: h2, width: w2 } = object2;

    return (
      pos1.x + w1 > pos2.x &&
      pos2.x + w2 > pos1.x &&
      pos1.y + h1 > pos2.y &&
      pos2.y + h2 > pos1.y
    );
  }
}

javascript typescript game-engine entity-component-system
1个回答
0
投票

我不知道是否应该有错误或该代码是否正常工作,所以我假设您的问题严格是关于将碰撞检测系统的代码放在何处:

在这种情况下,您必须考虑碰撞检测系统与运动系统之间的相互作用。大多数情况下,方法将是

1 - Apply movement without taking collisions into account
2 - Detect collisions
3 - Adjust the movement you made depending on the collisions you just detected

因此,由于您的碰撞检测与您的运动系统紧密结合,因此将其保留在其中对我来说很有意义。但是,最好还是将碰撞检测系统隔离开来,所以您可以做的就是将碰撞检测系统与运动系统简单地联系起来,使碰撞系统成为运动系统的“子系统”。 。

另一种选择是确实将它们分开,但是您的碰撞检测系统将需要自己重新调整实体的位置。也许可以,但是这可能会增加代码的复杂性(我猜您将需要在组件中存储更多数据),并且这会打破只有运动系统会改变实体位置的假设(可能是保留是一件好事,但绝对没有必要)。

希望这会有所帮助

© www.soinside.com 2019 - 2024. All rights reserved.