我正在使用打字稿和ECS模式制作游戏。但是我不明白如何处理实体之间的冲突。我的实体Player
具有一组组件:
也有具有相同组件集的实体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
);
}
}
我不知道是否应该有错误或该代码是否正常工作,所以我假设您的问题严格是关于将碰撞检测系统的代码放在何处:
在这种情况下,您必须考虑碰撞检测系统与运动系统之间的相互作用。大多数情况下,方法将是
1 - Apply movement without taking collisions into account
2 - Detect collisions
3 - Adjust the movement you made depending on the collisions you just detected
因此,由于您的碰撞检测与您的运动系统紧密结合,因此将其保留在其中对我来说很有意义。但是,最好还是将碰撞检测系统隔离开来,所以您可以做的就是将碰撞检测系统与运动系统简单地联系起来,使碰撞系统成为运动系统的“子系统”。 。
另一种选择是确实将它们分开,但是您的碰撞检测系统将需要自己重新调整实体的位置。也许可以,但是这可能会增加代码的复杂性(我猜您将需要在组件中存储更多数据),并且这会打破只有运动系统会改变实体位置的假设(可能是保留是一件好事,但绝对没有必要)。
希望这会有所帮助