Files
esengine/docs/physics.md
yhh 8bc06f0476 Merge branch 'master' of https://github.com/esengine/ecs-framework
# Conflicts:
#	extensions/ecs-tween/lib/framework.d.ts
2021-08-04 12:54:18 +08:00

74 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 关于 Physics/Collision
框架中的物理不是一个真实的物理模拟。它只提供了游戏物理。您可以执行一些操作,如检测碰撞器、重叠检查、碰撞检查、扫描测试等。不是一个完整的刚体模拟。
### Colliders 物理系统的根本
没有Collider在物理系统中什么也不会发生。 碰撞器存在于实体类中有几种类型BoxColliderCircleCollider和PolygonCollider。 您可以像这样添加Collider`entity.addComponent(new BoxCollider())`. 将碰撞器添加到Entity时它们会自动添加到SpatialHash中。
### SpatialHash你永远不会用到它但它仍然对你很重要
SpatialHash类是一个隐藏类该类为您的游戏全局管理 `collider`。静态物理类是SpatialHash的公共包装器。 SpatialHash没有设置大小限制用于快速进行碰撞/线投射/重叠检查。例如如果你有一个英雄在世界各地移动而不必检查每个对撞机可能是数百个是否发生碰撞则只需向SpatialHash询问英雄附近的所有collider即可。这大大缩小了您的碰撞检查范围。
SpatialHash有一个可配置的方面它可以极大地影响其性能单元大小。 SpatialHash将空间分成一个网格选择适当的网格大小可以将可能发生的碰撞查询减到最少。默认情况下网格大小为100像素。您可以通过在创建场景之前设置`Physics.SpatialHashCellSize`来更改此设置。选择比您的平均玩家/敌人人数稍大的尺寸通常效果最佳。
### Physics 类
物理类是物理的核心类。 您可以设置一些属性例如前面提到的spatialHashCellSizeraycastsHitTriggers和raycastsStartInColliders。
- linecast从开始到结束投射一条线并返回与layerMask相匹配的碰撞器的第一次命中
- overlapRectangle检查是否有任何collider在矩形区域内
- overlapCircle检查是否有任何collider在圆形区域内
- boxcastBroadphase返回边界与collider.bounds相交的所有碰撞器。 请注意这是一个broadphase检查因此它只检查边界不执行单个碰撞器到碰撞器的检查
会注意到上面提到的layerMask。 layerMask允许您确定与哪些碰撞器碰撞。 每个collider都可以设置其物理层以便在查询物理系统时可以选择仅检索与传入的layerMask匹配的对撞机。 所有物理方法都接受默认为所有图层的图层蒙版参数。 使用此选项可以过滤冲突检查,并通过不执行不必要的冲突检查来使性能保持最佳状态。
### 使用物理系统
射线检测对于检查敌人的视线、探测实体的空间环境、快速移动的子弹等各种事情都非常有用。下面是一个从头到尾投射线条的示例,如果击中某个物体,它只会记录数据:
```ts
const hit = es.Physics.linecast( start, end );
if( hit.collider != null )
console.log( `ray hit ${hit}, entity: {hit.collider.entity}`);
```
我们使用了一些更先进的碰撞/重叠检查方法如Minkowski和、分离轴定理和古老的三角法。这些都被包装在Collider类上的简单易用的方法中。让我们看一些例子。
第一个例子是处理碰撞的最简单方法。deltaMovement是您希望移动实体的量通常是velocity*Time.deltaTime. collidesWithAny方法将检查所有碰撞并调整deltaMovement以解决任何碰撞。
```ts
// 碰撞结果将包含一些非常有用的信息例如被撞的collider表面命中的法线和最小平移矢量MTV。 MTV可用于将碰撞实体直接移动到命中的碰撞器附近。
let collisionResult = null;
// 进行检查以查看entity.getComponent(Collider)(实体上的第一个碰撞器)是否与场景中的任何其他碰撞器发生碰撞。请注意,如果您有多个碰撞器,则可以获取并遍历它们,而不是仅检查第一个碰撞器。
if( entity.getComponent(es.Collider).collidesWithAny( deltaMovement, collisionResult ) )
{
// 记录CollisionResult。 您可能需要使用它来添加一些粒子效果或与您的游戏相关的任何其他内容。
console.log( `collision result: ${collisionResult}` );
}
// 将实体移到新位置。 已经调整了deltaMovement为我们解决冲突。
entity.position = entity.position.add(deltaMovement);
```
如果您需要对碰撞发生时的情况进行更多控制则也可以手动检查是否与其他collider发生碰撞。 请注意执行此操作时deltaMovement不会为您调整。 解决冲突时,您需要考虑最小平移矢量。
```ts
let collisionResult = null;
// 进行检查以查看entity.getComponent<Collider>是否与一些其他Collider发生碰撞
if( entity.getComponent(es.Collider).collidesWith( someOtherCollider, deltaMovement, collisionResult ) )
{
// 将实体移动到与命中Collider相邻的位置然后记录CollisionResult
entity.position = entity.position.add(deltaMovement.sub(collisionResult.minimumTranslationVector));
console.log( `collision result: ${collisionResult}` );
}
```
我们可以使用前面提到的Physics.boxcastBroadphase方法或更具体地讲将自身排除在查询之外的版本使上述示例更进一步。 该方法将为我们提供场景中所有在我们附近的collider然后我们可以使用这些对撞机进行实际的碰撞检查。
```ts
// 在我们自身以外的位置获取可能与之重叠的任何东西
let neighborColliders = es.Physics.boxcastBroadphaseExcludingSelf( entity.getComponent(es.Collider) );
// 遍历并检查每个对撞机是否重叠
for( let collider of neighborColliders )
{
if( entity.getComponent(es.Collider).overlaps( collider ) )
console.log( `我们正在重叠一个collider : ${collider}` );
}
```