/** * Rigidbody2D Component * 2D 刚体组件 */ import { Component, Property, Serialize, Serializable, ECSComponent } from '@esengine/ecs-framework'; import { RigidbodyType2D, CollisionDetectionMode2D } from '../types/Physics2DTypes'; import type { Vector2 } from '../types/Physics2DTypes'; /** * 刚体约束配置 */ export interface RigidbodyConstraints2D { /** 冻结 X 轴位置 */ freezePositionX: boolean; /** 冻结 Y 轴位置 */ freezePositionY: boolean; /** 冻结旋转 */ freezeRotation: boolean; } /** * 2D 刚体组件 * * 用于给实体添加物理模拟能力。必须与 TransformComponent 配合使用。 * * @example * ```typescript * const entity = scene.createEntity('Player'); * entity.addComponent(TransformComponent); * const rb = entity.addComponent(Rigidbody2DComponent); * rb.bodyType = RigidbodyType2D.Dynamic; * rb.mass = 1; * rb.gravityScale = 1; * ``` */ @ECSComponent('Rigidbody2D') @Serializable({ version: 1, typeId: 'Rigidbody2D' }) export class Rigidbody2DComponent extends Component { // ==================== 基础属性 ==================== /** * 刚体类型 * - Dynamic: 动态刚体,受力和碰撞影响 * - Kinematic: 运动学刚体,手动控制 * - Static: 静态刚体,不移动 */ @Serialize() @Property({ type: 'enum', label: 'Body Type', options: [ { label: 'Dynamic', value: 0 }, { label: 'Kinematic', value: 1 }, { label: 'Static', value: 2 } ] }) public bodyType: RigidbodyType2D = RigidbodyType2D.Dynamic; /** * 质量(kg) * 仅对 Dynamic 刚体有效 */ @Serialize() @Property({ type: 'number', label: 'Mass', min: 0.001, step: 0.1 }) public mass: number = 1; /** * 重力缩放 * 0 = 不受重力影响,1 = 正常重力,-1 = 反重力 */ @Serialize() @Property({ type: 'number', label: 'Gravity Scale', min: -10, max: 10, step: 0.1 }) public gravityScale: number = 1; // ==================== 阻尼 ==================== /** * 线性阻尼 * 值越大,移动速度衰减越快 */ @Serialize() @Property({ type: 'number', label: 'Linear Damping', min: 0, max: 100, step: 0.1 }) public linearDamping: number = 0; /** * 角速度阻尼 * 值越大,旋转速度衰减越快 */ @Serialize() @Property({ type: 'number', label: 'Angular Damping', min: 0, max: 100, step: 0.01 }) public angularDamping: number = 0.05; // ==================== 约束 ==================== /** * 冻结 X 轴位置 */ @Serialize() @Property({ type: 'boolean', label: 'Freeze Position X' }) public freezePositionX: boolean = false; /** * 冻结 Y 轴位置 */ @Serialize() @Property({ type: 'boolean', label: 'Freeze Position Y' }) public freezePositionY: boolean = false; /** * 冻结旋转 */ @Serialize() @Property({ type: 'boolean', label: 'Freeze Rotation' }) public freezeRotation: boolean = false; /** * 运动约束(兼容旧代码) * @deprecated 使用 freezePositionX, freezePositionY, freezeRotation 代替 */ public get constraints(): RigidbodyConstraints2D { return { freezePositionX: this.freezePositionX, freezePositionY: this.freezePositionY, freezeRotation: this.freezeRotation }; } public set constraints(value: RigidbodyConstraints2D) { this.freezePositionX = value.freezePositionX; this.freezePositionY = value.freezePositionY; this.freezeRotation = value.freezeRotation; } // ==================== 碰撞检测 ==================== /** * 碰撞检测模式 * - Discrete: 离散检测,性能好 * - Continuous: 连续检测,防穿透 */ @Serialize() @Property({ type: 'enum', label: 'Collision Detection', options: [ { label: 'Discrete', value: 0 }, { label: 'Continuous', value: 1 } ] }) public collisionDetection: CollisionDetectionMode2D = CollisionDetectionMode2D.Discrete; // ==================== 休眠 ==================== /** * 是否允许休眠 * 休眠的刚体不参与物理计算,提高性能 */ @Serialize() @Property({ type: 'boolean', label: 'Can Sleep' }) public canSleep: boolean = true; /** * 是否处于唤醒状态 */ @Property({ type: 'boolean', label: 'Is Awake', readOnly: true }) public isAwake: boolean = true; // ==================== 运行时状态(不序列化)==================== /** * 当前线速度 */ public velocity: Vector2 = { x: 0, y: 0 }; /** * 当前角速度(弧度/秒) */ public angularVelocity: number = 0; // ==================== 内部状态 ==================== /** * Rapier 刚体句柄 * @internal */ public _bodyHandle: number | null = null; /** * 是否需要同步 Transform 到物理世界 * @internal */ public _needsSync: boolean = true; /** * 上一帧的位置(用于插值) * @internal */ public _previousPosition: Vector2 = { x: 0, y: 0 }; /** * 上一帧的旋转角度 * @internal */ public _previousRotation: number = 0; // ==================== API 方法 ==================== /** * 添加力(在下一个物理步进中应用) * 这是一个标记方法,实际力的应用由 Physics2DSystem 处理 */ public addForce(force: Vector2): void { this._pendingForce.x += force.x; this._pendingForce.y += force.y; } /** * 添加冲量(立即改变速度) */ public addImpulse(impulse: Vector2): void { this._pendingImpulse.x += impulse.x; this._pendingImpulse.y += impulse.y; } /** * 添加扭矩 */ public addTorque(torque: number): void { this._pendingTorque += torque; } /** * 添加角冲量 */ public addAngularImpulse(impulse: number): void { this._pendingAngularImpulse += impulse; } /** * 设置线速度 */ public setVelocity(velocity: Vector2): void { this._targetVelocity = { ...velocity }; this._hasTargetVelocity = true; } /** * 设置角速度 */ public setAngularVelocity(angularVelocity: number): void { this._targetAngularVelocity = angularVelocity; this._hasTargetAngularVelocity = true; } /** * 唤醒刚体 */ public wakeUp(): void { this._shouldWakeUp = true; } /** * 使刚体休眠 */ public sleep(): void { this._shouldSleep = true; } /** * 标记需要重新同步 Transform */ public markNeedsSync(): void { this._needsSync = true; } // ==================== 待处理的力和冲量 ==================== /** @internal */ public _pendingForce: Vector2 = { x: 0, y: 0 }; /** @internal */ public _pendingImpulse: Vector2 = { x: 0, y: 0 }; /** @internal */ public _pendingTorque: number = 0; /** @internal */ public _pendingAngularImpulse: number = 0; /** @internal */ public _targetVelocity: Vector2 = { x: 0, y: 0 }; /** @internal */ public _hasTargetVelocity: boolean = false; /** @internal */ public _targetAngularVelocity: number = 0; /** @internal */ public _hasTargetAngularVelocity: boolean = false; /** @internal */ public _shouldWakeUp: boolean = false; /** @internal */ public _shouldSleep: boolean = false; /** * 清除待处理的力和冲量 * @internal */ public _clearPendingForces(): void { this._pendingForce.x = 0; this._pendingForce.y = 0; this._pendingImpulse.x = 0; this._pendingImpulse.y = 0; this._pendingTorque = 0; this._pendingAngularImpulse = 0; this._hasTargetVelocity = false; this._hasTargetAngularVelocity = false; this._shouldWakeUp = false; this._shouldSleep = false; } public override onRemovedFromEntity(): void { // 清理句柄,实际的物理对象清理由系统处理 this._bodyHandle = null; this._clearPendingForces(); } }