Skip to content

Physical system design

ChenMo edited this page Mar 8, 2022 · 9 revisions

This is the design of physical system.

Multi-backend subcontracting design of physical system

物理引擎分包设计 001

API Design

UML: image

Colliders:

/**
 * Basic class of rigid body collider.
 */
export abstract class Collider extends Component {

  /** The shape of the Collider. */
  get shapes(): Readonly<ColliderShape[]> { }

  /**
   * Add a collider shape.
   * @param shape - The collider shape.
   */
  addShape(shape: ColliderShape): void {}

  /**
   * Remove a collider shape.
   * @param shape - The collider shape.
   */
  removeShape(shape: ColliderShape): void {}

  /**
   * Clear all shape collection.
   */
  clearShapes(): void {}
}

/**
 * A static rigid body collider component that will not move when colliding with a dynamic rigid body collider.
 * @remarks Mostly used for object which always stays at the same place and never moves around.
 */
export class StaticCollider extends Collider {}

/**
 * Basic class of rigid body collider.
 */
export abstract class Collider extends Component {

  /** The shape of the Collider. */
  get shapes(): Readonly<ColliderShape[]> { }

  /**
   * Add a collider shape.
   * @param shape - The collider shape.
   */
  addShape(shape: ColliderShape): void {}

  /**
   * Remove a collider shape.
   * @param shape - The collider shape.
   */
  removeShape(shape: ColliderShape): void {}

  /**
   * Clear all shape collection.
   */
  clearShapes(): void {}
}

/**
 * A static rigid body collider component that will not move when colliding with a dynamic rigid body collider.
 * @remarks Mostly used for object which always stays at the same place and never moves around.
 */
export class StaticCollider extends Collider {}

/**
 * A dynamic rigid body collider component.
 */
export class DynamicCollider extends Collider {
  /** The linear velocity vector of the RigidBody measured in world unit per second. */
  linearVelocity: number;
  /** The angular velocity vector of the RigidBody measured in radians per second. */
  angularVelocity: number;
  /** The linear damping of the RigidBody. */
  linearDamping: number;
  /** The angular damping of the RigidBody. */
  angularDamping: number;
  /** The center of mass relative to the transform's origin. */
  centerOfMass: number;
  /** The diagonal inertia tensor of mass relative to the center of mass. */
  inertiaTensor: Vector3;
  /** The maximum angular velocity of the collider measured in radians per second. (Default 7) range { 0, infinity }. */
  maxAngularVelocity: number;
  /** Maximum velocity of a collider when moving out of penetrating state. */
  maxDepenetrationVelocity: number;
  /** The mass of the RigidBody. */
  mass: number;
  /** The mass-normalized energy threshold, below which objects start going to sleep. */
  sleepThreshold: number;
  /** The solverIterations determines how accurately collider joints and collision contacts are resolved. */
  solverIterations: number;
  /** Controls whether physics will change the rotation of the object. */
  freezeRotation: boolean;
  /** The particular rigid dynamic lock flag. */
  constraints: number;
  /** The colliders' collision detection mode. */
  collisionDetectionMode: number;
  /** Controls whether physics affects the RigidBody. */
  isKinematic: boolean;

  constructor(entity: Entity) {
    super(entity);
  }

  /** apply a force to the DynamicCollider. */
  applyForce(force: Vector3): void {}

  /** apply a torque to the DynamicCollider. */
  applyTorque(torque: Vector3): void {}
}

Shapes:

/**
 * Basic class of collider shape.
 */
export abstract class ColliderShape {
  /** The position of this ColliderShape. */
  position: Vector3;
  /** Whether the ColliderShape is a trigger. */
  isTrigger: boolean;
  /** The physic material of this ColliderShape. */
  material: PhysicsMaterial;
}

/**
 * Box-shaped collider shape.
 */
export class BoxColliderShape extends ColliderShape {
  /** The size of this BoxColliderShape. */
  size: Vector3;
}

/**
 *  Sphere-shaped collider shape.
 */
export class SphereColliderShape extends ColliderShape {
  /** The radius of this SphereColliderShape. */
  radius: number;
}

/**
 * Plane-shaped collider shape.
 */
export class PlaneColliderShape extends ColliderShape {
  /** The rotation of this PlaneColliderShape. */
  rotation: Vector3;
}

/**
 *  Capsule-shaped collider shape.
 */
export class CapsuleColliderShape extends ColliderShape {
  /** The radius of this CapsuleColliderShape. */
  radius: number;
  /** The height of this CapsuleColliderShape. */
  height: number;
  /** The up axis of this CapsuleColliderShape. */
  upAxis: ColliderShapeUpAxis;
}

Script Call back:

export class Script extends Component {
  ..............

  /**
   * Called when the collision enter.
   * @param other - Other ColliderShape
   */
  onTriggerEnter(other: ColliderShape): void {}

  /**
   * Called when the collision stay.
   * @remarks onTriggerStay is called every frame while the collision stay.
   * @param other - Other ColliderShape
   */
  onTriggerStay(other: ColliderShape): void {}

  /**
   * Called when the collision exit.
   * @param other - Other ColliderShape
   */
  onTriggerExit(other: ColliderShape): void {}

  ..............
}

PhysicsManager:

export class PhysicsManager {

  /**
   * Casts a ray through the Scene and returns the first hit.
   * @param ray - The ray
   * @returns Returns true if the ray intersects with a Collider, otherwise false.
   */
  raycast(ray: Ray): Boolean;

  /**
   * Casts a ray through the Scene and returns the first hit.
   * @param ray - The ray
   * @param outHitResult - If true is returned, outHitResult will contain more detailed collision information
   * @returns Returns true if the ray intersects with a Collider, otherwise false.
   */
  raycast(ray: Ray, outHitResult: HitResult): Boolean;

  /**
   * Casts a ray through the Scene and returns the first hit.
   * @param ray - The ray
   * @param distance - The max distance the ray should check
   * @returns Returns true if the ray intersects with a Collider, otherwise false.
   */
  raycast(ray: Ray, distance: number): Boolean;

  /**
   * Casts a ray through the Scene and returns the first hit.
   * @param ray - The ray
   * @param distance - The max distance the ray should check
   * @param outHitResult - If true is returned, outHitResult will contain more detailed collision information
   * @returns Returns true if the ray intersects with a Collider, otherwise false.
   */
  raycast(ray: Ray, distance: number, outHitResult: HitResult): Boolean;

  /**
   * Casts a ray through the Scene and returns the first hit.
   * @param ray - The ray
   * @param distance - The max distance the ray should check
   * @param layerMask - Layer mask that is used to selectively ignore Colliders when casting
   * @returns Returns true if the ray intersects with a Collider, otherwise false.
   */
  raycast(ray: Ray, distance: number, layerMask: Layer): Boolean;

  /**
   * Casts a ray through the Scene and returns the first hit.
   * @param ray - The ray
   * @param distance - The max distance the ray should check
   * @param layerMask - Layer mask that is used to selectively ignore Colliders when casting
   * @param outHitResult - If true is returned, outHitResult will contain more detailed collision information
   * @returns Returns true if the ray intersects with a Collider, otherwise false.
   */
  raycast(ray: Ray, distance: number, layerMask: Layer, outHitResult: HitResult): Boolean;

Auxiliary Class:

/**
 * The up axis of the collider shape.
 */
export enum ColliderShapeUpAxis {
  /** Up axis is X. */
  X,
  /** Up axis is Y. */
  Y,
  /** Up axis is Z. */
  Z
}


/**
 * Describe how to handle with collisions between colliders.
 */
export class PhysicsMaterial {
  /** The bounciness of collider surface. */
  bounciness: number;

  /** The friction coefficient used when already moving. */
  dynamicFriction: number;

  /** The friction coefficient used when an object is lying on a surface. */
  staticFriction: number;

  /** The friction bounce mode.*/
  bounceCombine: PhysicsMaterialCombineMode;

  /** The friction combine mode. */
  frictionCombine: PhysicsMaterialCombineMode;
}

/**
 * Describes how physics materials of the colliders are combined.
 */
export enum PhysicsMaterialCombineMode {
  /** Averages the friction/bounce of the two colliding materials. */
  Average,
  /** Uses the smaller friction/bounce of the two colliding materials. */
  Minimum,
  /** Uses the larger friction/bounce of the two colliding materials. */
  Maximum,
  /** Multiplies the friction/bounce of the two colliding materials. */
  Multiply
}

/**
 * Structure used to get information back from a raycast or a sweep.
 */
export class HitResult {
  /** The collider shape that was hit. */
  colliderShape: ColliderShape = null;
  /** The distance from the origin to the hit point. */
  distance: number = 0;
  /** The hit point of the collider that was hit in world space. */
  point: Vector3 = new Vector3();
  /** The hit normal of the collider that was hit in world space. */
  normal: Vector3 = new Vector3();
}

Physics engine interface:

export interface IColliderShape {
  setPosition(position: Vector3): void;
  setIsTrigger(isTrigger: boolean): void;
  setMaterial(material: IPhysicsMaterial): void;

  setWorldScale(scale: Vector3): void;
}

export interface IBoxColliderShape extends IColliderShape {
  setSize(size: Vector3): void;
}

export interface ICapsuleColliderShape extends ICollider {
  setUpAxis(upAxis: number): void;
  setRadius(radius: number): void;
  setHeight(height: number): void;
}

export interface IPlaneColliderShape extends ICollider {
  setRotation(rotation: Vector3): void;
}

export interface ISphereColliderShape extends ICollider {
  setRadius(radius: number): void;
}

export interface ICollider {
  getWorldTransform(outPosition: Vector3, outRotation: Quaternion): void;
  setWorldTransform(position: Vector3, rotation: Quaternion): void;
  
  addShape(shape: IColliderShape): void;
  removeShape(shape: IColliderShape): void;
  clearShapes(): void;
}

export interface IStaticCollider extends ICollider {}

export interface IDynamicCollider extends ICollider {
  linearVelocity: number;
  angularVelocity: number;
  linearDamping: number;
  angularDamping: number;
  mass: number;
  isKinematic: boolean;

  applyForce(force: Vector3): void;
  applyTorque(torque: Vector3): void;
}

export interface IPhysics {
  createPhysicsManager(): IPhysicsManager;

  createStaticCollider(): IStaticCollider;
  createDynamicCollider(): IDynamicCollider;

  createBoxColliderShape(size: Vector3): IBoxColliderShape;
  createSphereColliderShape(radius: number): ISphereColliderShape;
  createPlaneColliderShape(normal: Vector3,distance:number): IPlaneColliderShape;
  createCapsuleColliderShape(radius: number, height: number): ICapsuleColliderShape;
}

export interface IPhysicsManager {
  addCollider(collider: ICollider): void;
  removeCollider(collider: ICollider): void;
  raycast(
    ray: Ray,
    distance: number,
    layerMask: number,
    outHitResult?: (colliderShapeID: number, distance: number, point: Vector3, normal: Vector3) => void
  ): Boolean;
}

export interface IPhysicsMaterial {
  bounciness: number;
  dynamicFriction: number;
  staticFriction: number;
  bounceCombine: number;
  frictionCombine: number;
}

Design Source Code

https://github.com/GuoLei1990/engine/tree/design/physics
User API Design: packages/core/src/physics
Physics engine interface Design: packages/design/src/physics
Physics engine implement sample: packages/physics-physX and packages/physics-lite