diff --git a/.gitmodules b/.gitmodules index dade3a79..8d608a8a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,3 @@ [submodule "extensions/behaviourTree-ai"] path = extensions/behaviourTree-ai url = https://github.com/esengine/BehaviourTree-ai -[submodule "extensions/ecs-tween"] - path = extensions/ecs-tween - url = https://github.com/esengine/ecs-tween diff --git a/README.md b/README.md index e5fb16fe..3fd6405b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ecs-framework 的目标是成为功能强大的框架。它为您构建游戏提 - AABB,圆和多边形碰撞/触发检测 - 高效的协程,可在多个帧或动画定时中分解大型任务(Core.startCoroutine) - 通过Astar和广度优先搜索提供寻路支持,以查找图块地图或您自己的自定义格式 ( 参见 https://github.com/esengine/ecs-astar ) -- tween系统。任何number / Vector / 矩形/字段或属性都可以tween。 (参见 https://github.com/esengine/ecs-tween) +- tween系统。任何number / Vector / 矩形/字段或属性都可以tween。 - 针对核心事件的优化的事件发射器(发射器类),您也可以将其添加到自己的任何类中 - 延迟和重复任务的调度程序(核心调度方法) @@ -269,6 +269,9 @@ Debug类提供日志记录。 Insist类提供各种断言条件。 您可以在 #### [laya-demo](https://github.com/esengine/ecs-laya-demo) #### [egret-demo](https://github.com/esengine/ecs-egret-demo) +### 渲染集成框架 +#### [cocos-framework](https://github.com/esengine/cocos-framework) + ## 扩展库 #### [基于ecs-framework开发的astar/BreadthFirst/Dijkstra/GOAP目标导向计划 路径寻找库](https://github.com/esengine/ecs-astar) diff --git a/extensions/ecs-tween/lib/framework.d.ts b/extensions/ecs-tween/lib/framework.d.ts new file mode 100644 index 00000000..94b14d9b --- /dev/null +++ b/extensions/ecs-tween/lib/framework.d.ts @@ -0,0 +1,6514 @@ +declare module es { + /** + * 全局核心类 + */ + class Core { + /** + * 核心发射器。只发出核心级别的事件 + */ + static emitter: Emitter; + static paused: boolean; + /** + * 是否启用调试渲染 + */ + static debugRenderEndabled: boolean; + /** + * 简化对内部类的全局内容实例的访问 + */ + private static _instance; + /** + * 用于确定是否应该使用EntitySystems + */ + static entitySystemsEnabled: boolean; + /** + * 是否正在debug模式 + * 仅允许在create时进行更改 + */ + readonly debug: boolean; + _nextScene: Scene; + /** + * 用于凝聚GraphicsDeviceReset事件 + */ + _graphicsDeviceChangeTimer: ITimer; + /** + * 全局访问系统 + */ + _globalManagers: GlobalManager[]; + _coroutineManager: CoroutineManager; + _timerManager: TimerManager; + private constructor(); + /** + * 提供对单例/游戏实例的访问 + * @constructor + */ + static readonly Instance: Core; + _frameCounterElapsedTime: number; + _frameCounter: number; + _totalMemory: number; + _titleMemory: (totalMemory: number, frameCounter: number) => void; + _scene: Scene; + /** + * 当前活动的场景。注意,如果设置了该设置,在更新结束之前场景实际上不会改变 + */ + /** + * 当前活动的场景。注意,如果设置了该设置,在更新结束之前场景实际上不会改变 + * @param value + */ + static scene: Scene; + /** + * 默认实现创建核心 + */ + static create(debug?: boolean): Core; + /** + * 添加一个全局管理器对象,它的更新方法将调用场景前的每一帧。 + * @param manager + */ + static registerGlobalManager(manager: es.GlobalManager): void; + /** + * 删除全局管理器对象 + * @param manager + */ + static unregisterGlobalManager(manager: es.GlobalManager): void; + /** + * 获取类型为T的全局管理器 + * @param type + */ + static getGlobalManager(type: new (...args: any[]) => T): T; + /** + * 启动一个coroutine。Coroutine可以将number延时几秒或延时到其他startCoroutine.Yielding + * null将使coroutine在下一帧被执行。 + * @param enumerator + */ + static startCoroutine(enumerator: any): ICoroutine; + /** + * 调度一个一次性或重复的计时器,该计时器将调用已传递的动作 + * @param timeInSeconds + * @param repeats + * @param context + * @param onTime + */ + static schedule(timeInSeconds: number, repeats: boolean, context: any, onTime: (timer: ITimer) => void): Timer; + startDebugDraw(): void; + /** + * 在一个场景结束后,下一个场景开始之前调用 + */ + onSceneChanged(): void; + protected initialize(): void; + protected update(currentTime?: number): Promise; + } +} +declare module es { + enum LogType { + error = 0, + warn = 1, + log = 2, + info = 3, + trace = 4 + } + class Debug { + static warnIf(condition: boolean, format: string, ...args: any[]): void; + static warn(format: string, ...args: any[]): void; + static error(format: string, ...args: any[]): void; + static log(type: LogType, format: string, ...args: any[]): void; + } +} +declare module es { + /** + * 我们在这里存储了各种系统的默认颜色,如对撞机调试渲染、Debug.drawText等。 + * 命名方式尽可能采用CLASS-THING,以明确它的使用位置 + */ + class DebugDefault { + static debugText: number; + static colliderBounds: number; + static colliderEdge: number; + static colliderPosition: number; + static colliderCenter: number; + static renderableBounds: number; + static renderableCenter: number; + static verletParticle: number; + static verletConstraintEdge: number; + } +} +declare module es { + class Insist { + static fail(message?: string, ...args: any[]): void; + static isTrue(condition: boolean, message?: string, ...args: any[]): void; + static isFalse(condition: boolean, message?: string, ...args: any[]): void; + static isNull(obj: any, message?: string, ...args: any[]): void; + static isNotNull(obj: any, message?: string, ...args: any[]): void; + static areEqual(first: any, second: any, message: string, ...args: any[]): void; + static areNotEqual(first: any, second: any, message: string, ...args: any[]): void; + } +} +declare module es { + /** + * 执行顺序 + * - onAddedToEntity + * - OnEnabled + * + * 删除执行顺序 + * - onRemovedFromEntity + */ + abstract class Component { + static _idGenerator: number; + /** + * 此组件的唯一标识 + */ + readonly id: number; + /** + * 此组件附加的实体 + */ + entity: Entity; + constructor(); + /** + * 快速访问 this.entity.transform + */ + readonly transform: Transform; + private _enabled; + /** + * 如果组件和实体都已启用,则为。当启用该组件时,将调用该组件的生命周期方法。状态的改变会导致调用onEnabled/onDisable。 + */ + /** + * 如果组件和实体都已启用,则为。当启用该组件时,将调用该组件的生命周期方法。状态的改变会导致调用onEnabled/onDisable。 + * @param value + */ + enabled: boolean; + private _updateOrder; + /** 更新此实体上组件的顺序 */ + /** 更新此实体上组件的顺序 */ + updateOrder: number; + /** + * 当此组件已分配其实体,但尚未添加到实体的活动组件列表时调用。有用的东西,如物理组件,需要访问转换来修改碰撞体的属性。 + */ + initialize(): void; + /** + * 在提交所有挂起的组件更改后,将该组件添加到场景时调用。此时,设置了实体字段和实体。场景也设定好了。 + */ + onAddedToEntity(): void; + /** + * 当此组件从其实体中移除时调用。在这里做所有的清理工作。 + */ + onRemovedFromEntity(): void; + /** + * 当实体的位置改变时调用。这允许组件知道它们由于父实体的移动而移动了。 + * @param comp + */ + onEntityTransformChanged(comp: ComponentTransform): void; + debugRender(batcher: IBatcher): void; + /** + *当父实体或此组件启用时调用 + */ + onEnabled(): void; + /** + * 禁用父实体或此组件时调用 + */ + onDisabled(): void; + setEnabled(isEnabled: boolean): this; + setUpdateOrder(updateOrder: number): this; + } +} +declare module es { + class ComponentType { + static INDEX: number; + private index_; + private type_; + constructor(type: Class, index?: number); + getName(): string; + getIndex(): number; + toString(): string; + } +} +declare module es { + enum CoreEvents { + /** + * 当场景发生变化时触发 + */ + sceneChanged = 0, + /** + * 每帧更新事件 + */ + frameUpdated = 1, + /** + * 当渲染发生时触发 + */ + renderChanged = 2 + } +} +declare module es { + class EntityComparer implements IComparer { + compare(self: Entity, other: Entity): number; + } + class Entity implements IEqualityComparable { + static entityComparer: IComparer; + /** + * 当前实体所属的场景 + */ + scene: Scene; + /** + * 实体名称。用于在场景范围内搜索实体 + */ + name: string; + /** + * 此实体的唯一标识 + */ + readonly id: number; + /** + * 封装实体的位置/旋转/缩放,并允许设置一个高层结构 + */ + readonly transform: Transform; + /** + * 当前附加到此实体的所有组件的列表 + */ + readonly components: ComponentList; + /** + * 指定应该调用这个entity update方法的频率。1表示每一帧,2表示每一帧,以此类推 + */ + updateInterval: number; + componentBits: Bits; + constructor(name: string, id: number); + _isDestroyed: boolean; + /** + * 如果调用了destroy,那么在下一次处理实体之前这将一直为true + */ + readonly isDestroyed: boolean; + private _tag; + /** + * 你可以随意使用。稍后可以使用它来查询场景中具有特定标记的所有实体 + */ + /** + * 你可以随意使用。稍后可以使用它来查询场景中具有特定标记的所有实体 + * @param value + */ + tag: number; + private _enabled; + /** + * 启用/禁用实体。当禁用碰撞器从物理系统和组件中移除时,方法将不会被调用 + */ + /** + * 启用/禁用实体。当禁用碰撞器从物理系统和组件中移除时,方法将不会被调用 + * @param value + */ + enabled: boolean; + private _updateOrder; + /** + * 更新此实体的顺序。updateOrder还用于对scene.entities上的标签列表进行排序 + */ + /** + * 更新此实体的顺序。updateOrder还用于对scene.entities上的标签列表进行排序 + * @param value + */ + updateOrder: number; + parent: Transform; + readonly childCount: number; + position: Vector2; + localPosition: Vector2; + rotation: number; + rotationDegrees: number; + localRotation: number; + localRotationDegrees: number; + scale: Vector2; + localScale: Vector2; + readonly worldInverseTransform: Matrix2D; + readonly localToWorldTransform: Matrix2D; + readonly worldToLocalTransform: Matrix2D; + onTransformChanged(comp: ComponentTransform): void; + setParent(parent: Entity): any; + setParent(parent: Transform): any; + setPosition(x: number, y: number): this; + setLocalPosition(localPosition: Vector2): this; + setRotation(radians: number): this; + setRotationDegrees(degrees: number): this; + setLocalRotation(radians: number): this; + setLocalRotationDegrees(degrees: number): this; + setScale(scale: number): any; + setScale(scale: Vector2): any; + setLocalScale(scale: number): any; + setLocalScale(scale: Vector2): any; + /** + * 设置实体的标记 + * @param tag + */ + setTag(tag: number): Entity; + /** + * 设置实体的启用状态。当禁用碰撞器从物理系统和组件中移除时,方法将不会被调用 + * @param isEnabled + */ + setEnabled(isEnabled: boolean): this; + /** + * 设置此实体的更新顺序。updateOrder还用于对scene.entities上的标签列表进行排序 + * @param updateOrder + */ + setUpdateOrder(updateOrder: number): this; + /** + * 从场景中删除实体并销毁所有子元素 + */ + destroy(): void; + /** + * 将实体从场景中分离。下面的生命周期方法将被调用在组件上:OnRemovedFromEntity + */ + detachFromScene(): void; + /** + * 将一个先前分离的实体附加到一个新的场景 + * @param newScene + */ + attachToScene(newScene: Scene): void; + /** + * 在提交了所有挂起的实体更改后,将此实体添加到场景时调用 + */ + onAddedToScene(): void; + /** + * 当此实体从场景中删除时调用 + */ + onRemovedFromScene(): void; + /** + * 每帧进行调用进行更新组件 + */ + update(): void; + debugRender(batcher: IBatcher): void; + /** + * 创建组件的新实例。返回实例组件 + * @param componentType + */ + createComponent(componentType: new (...args: any[]) => T): T; + /** + * 将组件添加到组件列表中。返回组件。 + * @param component + */ + addComponent(component: T): T; + /** + * 获取类型T的第一个组件并返回它。如果没有找到组件,则返回null。 + * @param type + */ + getComponent(type: new (...args: any[]) => T): T; + /** + * 获取类型T的第一个并已加入场景的组件并返回它。如果没有找到组件,则返回null。 + * @param type + * @returns + */ + getComponentInScene(type: new (...args: any[]) => T): T; + /** + * 尝试获取T类型的组件。如果未找到任何组件,则返回false + * @param type + * @param outComponent + * @returns + */ + tryGetComponent(type: new (...args: any[]) => T, outComponent: Ref): boolean; + /** + * 检查实体是否具有该组件 + * @param type + */ + hasComponent(type: new (...args: any[]) => T): boolean; + /** + * 获取类型T的第一个组件并返回它。如果没有找到组件,将创建组件。 + * @param type + */ + getOrCreateComponent(type: new (...args: any[]) => T): T; + /** + * 获取typeName类型的所有组件,但不使用列表分配 + * @param typeName + * @param componentList + */ + getComponents(typeName: any, componentList?: any): any[]; + /** + * 从组件列表中删除组件 + * @param component + */ + removeComponent(component: Component): void; + /** + * 从组件列表中删除类型为T的第一个组件 + * @param type + */ + removeComponentForType(type: any): boolean; + /** + * 从实体中删除所有组件 + */ + removeAllComponents(): void; + tweenPositionTo(to: Vector2, duration?: number): ITween; + tweenLocalPositionTo(to: Vector2, duration?: number): ITween; + tweenScaleTo(to: Vector2, duration?: number): any; + tweenScaleTo(to: number, duration?: number): any; + tweenLocalScaleTo(to: Vector2, duration?: any): any; + tweenLocalScaleTo(to: number, duration?: any): any; + tweenRotationDegreesTo(to: number, duration?: number): TransformVector2Tween; + tweenLocalRotationDegreesTo(to: number, duration?: number): TransformVector2Tween; + compareTo(other: Entity): number; + equals(other: Entity): boolean; + getHashCode(): number; + toString(): string; + } +} +declare module es { + /** 2d 向量 */ + class Vector2 implements IEquatable { + x: number; + y: number; + /** + * 从两个值构造一个带有X和Y的二维向量。 + * @param x 二维空间中的x坐标 + * @param y 二维空间的y坐标 + */ + constructor(x?: number, y?: number); + static readonly zero: Vector2; + static readonly one: Vector2; + static readonly unitX: Vector2; + static readonly unitY: Vector2; + static readonly up: Vector2; + static readonly down: Vector2; + static readonly left: Vector2; + static readonly right: Vector2; + /** + * + * @param value1 + * @param value2 + */ + static add(value1: Vector2, value2: Vector2): Vector2; + /** + * + * @param value1 + * @param value2 + */ + static divide(value1: Vector2, value2: Vector2): Vector2; + static divideScaler(value1: Vector2, value2: number): Vector2; + /** + * 返回两个向量之间距离的平方 + * @param value1 + * @param value2 + */ + static sqrDistance(value1: Vector2, value2: Vector2): number; + /** + * 将指定的值限制在一个范围内 + * @param value1 + * @param min + * @param max + */ + static clamp(value1: Vector2, min: Vector2, max: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含指定向量的线性插值 + * @param value1 第一个向量 + * @param value2 第二个向量 + * @param amount 加权值(0.0-1.0之间) + * @returns 指定向量的线性插值结果 + */ + static lerp(value1: Vector2, value2: Vector2, amount: number): Vector2; + /** + * 创建一个新的Vector2,其中包含指定矢量的线性插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + static lerpPrecise(value1: Vector2, value2: Vector2, amount: number): Vector2; + /** + * 创建一个新的Vector2,该Vector2包含了通过指定的Matrix进行的二维向量变换。 + * @param position + * @param matrix + */ + static transform(position: Vector2, matrix: Matrix2D): Vector2; + /** + * 创建一个新的Vector2,其中包含由指定的Matrix转换的指定法线 + * @param normal + * @param matrix + */ + static transformNormal(normal: Vector2, matrix: Matrix): Vector2; + /** + * 返回两个向量之间的距离 + * @param value1 + * @param value2 + * @returns 两个向量之间的距离 + */ + static distance(vec1: Vector2, vec2: Vector2): number; + /** + * 返回两个向量之间的角度,单位是度数 + * @param from + * @param to + */ + static angle(from: Vector2, to: Vector2): number; + /** + * 创建一个包含指定向量反转的新Vector2 + * @param value + * @returns 矢量反演的结果 + */ + static negate(value: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含给定矢量和法线的反射矢量 + * @param vector + * @param normal + * @returns + */ + static reflect(vector: Vector2, normal: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含指定矢量的三次插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + static smoothStep(value1: Vector2, value2: Vector2, amount: number): Vector2; + setTo(x: number, y: number): void; + negate(): Vector2; + /** + * + * @param value + */ + add(v: Vector2): Vector2; + addEqual(v: Vector2): Vector2; + /** + * + * @param value + */ + divide(value: Vector2): Vector2; + divideScaler(value: number): Vector2; + /** + * + * @param value + */ + multiply(value: Vector2): Vector2; + /** + * + * @param value + * @returns + */ + multiplyScaler(value: number): Vector2; + /** + * 从当前Vector2减去一个Vector2 + * @param value 要减去的Vector2 + * @returns 当前Vector2 + */ + sub(value: Vector2): Vector2; + subEqual(v: Vector2): Vector2; + dot(v: Vector2): number; + /** + * + * @param size + * @returns + */ + scale(size: number): Vector2; + scaleEqual(size: number): Vector2; + transform(matrix: Matrix2D): Vector2; + normalize(): Vector2; + /** + * 将这个Vector2变成一个方向相同的单位向量 + */ + normalizeEqual(): Vector2; + magnitude(): number; + distance(v?: Vector2): number; + /** + * 返回该Vector2的平方长度 + * @returns 这个Vector2的平方长度 + */ + lengthSquared(): number; + /** + * 四舍五入X和Y值 + */ + round(): Vector2; + /** + * 返回以自己为中心点的左右角,单位为度 + * @param left + * @param right + */ + angleBetween(left: Vector2, right: Vector2): number; + /** + * 比较当前实例是否等于指定的对象 + * @param other 要比较的对象 + * @returns 如果实例相同true 否则false + */ + equals(other: Vector2, tolerance?: number): boolean; + isValid(): boolean; + /** + * 创建一个新的Vector2,其中包含来自两个向量的最小值 + * @param value1 + * @param value2 + * @returns + */ + static min(value1: Vector2, value2: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含两个向量的最大值 + * @param value1 + * @param value2 + * @returns + */ + static max(value1: Vector2, value2: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number): Vector2; + static unsignedAngle(from: Vector2, to: Vector2, round?: boolean): number; + clone(): Vector2; + } +} +declare module es { + /** 场景 */ + class Scene { + camera: ICamera; + /** 这个场景中的实体列表 */ + readonly entities: EntityList; + readonly renderableComponents: RenderableComponentList; + /** 管理所有实体处理器 */ + readonly entityProcessors: EntityProcessorList; + readonly _sceneComponents: SceneComponent[]; + _renderers: Renderer[]; + readonly identifierPool: IdentifierPool; + private _didSceneBegin; + constructor(); + /** + * 在场景子类中重写这个,然后在这里进行加载。 + * 在场景设置好之后,但在调用begin之前,从contructor中调用这个函数 + */ + initialize(): void; + /** + * 当Core将这个场景设置为活动场景时,这个将被调用 + */ + onStart(): void; + /** + * 在场景子类中重写这个,并在这里做任何必要的卸载。 + * 当Core把这个场景从活动槽中移除时,这个被调用。 + */ + unload(): void; + begin(): void; + end(): void; + update(): void; + render(): void; + addRenderer(renderer: T): T; + getRenderer(type: new (...args: any[]) => T): T; + removeRenderer(renderer: Renderer): void; + /** + * 向组件列表添加并返回SceneComponent + * @param component + */ + addSceneComponent(component: T): T; + /** + * 获取类型为T的第一个SceneComponent并返回它。如果没有找到组件,则返回null。 + * @param type + */ + getSceneComponent(type: any): T; + /** + * 获取类型为T的第一个SceneComponent并返回它。如果没有找到SceneComponent,则将创建SceneComponent。 + * @param type + */ + getOrCreateSceneComponent(type: any): T; + /** + * 从SceneComponents列表中删除一个SceneComponent + * @param component + */ + removeSceneComponent(component: SceneComponent): void; + /** + * 将实体添加到此场景,并返回它 + * @param name + */ + createEntity(name: string): Entity; + /** + * 在场景的实体列表中添加一个实体 + * @param entity + */ + addEntity(entity: Entity): Entity; + /** + * 从场景中删除所有实体 + */ + destroyAllEntities(): void; + /** + * 搜索并返回第一个具有名称的实体 + * @param name + */ + findEntity(name: string): Entity; + findEntityById(id: number): Entity; + /** + * 返回具有给定标记的所有实体 + * @param tag + */ + findEntitiesWithTag(tag: number): Entity[]; + /** + * 返回提一个具有该标记的实体 + * @param tag + * @returns + */ + findEntityWithTag(tag: number): Entity; + /** + * 返回第一个启用加载的类型为T的组件 + * @param type + */ + findComponentOfType(type: new (...args: any[]) => T): T; + /** + * 返回类型为T的所有已启用已加载组件的列表 + * @param type + */ + findComponentsOfType(type: new (...args: any[]) => T): T[]; + /** + * 返回场景中包含特定组件的实体列表 + * @param type + * @returns + */ + findEntitiesOfComponent(...types: any[]): Entity[]; + /** + * 在场景中添加一个EntitySystem处理器 + * @param processor 处理器 + */ + addEntityProcessor(processor: EntitySystem): EntitySystem; + /** + * 从场景中删除EntitySystem处理器 + * @param processor + */ + removeEntityProcessor(processor: EntitySystem): void; + /** + * 获取EntitySystem处理器 + */ + getEntityProcessor(type: new (...args: any[]) => T): T; + } +} +declare module es { + enum ComponentTransform { + position = 0, + scale = 1, + rotation = 2 + } + enum DirtyType { + clean = 0, + positionDirty = 1, + scaleDirty = 2, + rotationDirty = 4 + } + class Transform { + /** 与此转换关联的实体 */ + readonly entity: Entity; + hierarchyDirty: DirtyType; + _localDirty: boolean; + _localPositionDirty: boolean; + _localScaleDirty: boolean; + _localRotationDirty: boolean; + _positionDirty: boolean; + _worldToLocalDirty: boolean; + _worldInverseDirty: boolean; + /** + * 值会根据位置、旋转和比例自动重新计算 + */ + _localTransform: Matrix2D; + /** + * 值将自动从本地和父矩阵重新计算。 + */ + _worldTransform: Matrix2D; + _rotationMatrix: Matrix2D; + _translationMatrix: Matrix2D; + _scaleMatrix: Matrix2D; + _children: Transform[]; + constructor(entity: Entity); + /** + * 这个转换的所有子元素 + */ + readonly childCount: number; + /** + * 变换在世界空间的旋转度 + */ + /** + * 变换在世界空间的旋转度 + * @param value + */ + rotationDegrees: number; + /** + * 旋转相对于父变换旋转的角度 + */ + /** + * 旋转相对于父变换旋转的角度 + * @param value + */ + localRotationDegrees: number; + readonly localToWorldTransform: Matrix2D; + _parent: Transform; + /** + * 获取此转换的父转换 + */ + /** + * 设置此转换的父转换 + * @param value + */ + parent: Transform; + _worldToLocalTransform: Matrix2D; + readonly worldToLocalTransform: Matrix2D; + _worldInverseTransform: Matrix2D; + readonly worldInverseTransform: Matrix2D; + _position: Vector2; + /** + * 变换在世界空间中的位置 + */ + /** + * 变换在世界空间中的位置 + * @param value + */ + position: Vector2; + _scale: Vector2; + /** + * 变换在世界空间的缩放 + */ + /** + * 变换在世界空间的缩放 + * @param value + */ + scale: Vector2; + _rotation: number; + /** + * 在世界空间中以弧度旋转的变换 + */ + /** + * 变换在世界空间的旋转度 + * @param value + */ + rotation: number; + _localPosition: Vector2; + /** + * 转换相对于父转换的位置。如果转换没有父元素,则与transform.position相同 + */ + /** + * 转换相对于父转换的位置。如果转换没有父元素,则与transform.position相同 + * @param value + */ + localPosition: Vector2; + _localScale: Vector2; + /** + * 转换相对于父元素的比例。如果转换没有父元素,则与transform.scale相同 + */ + /** + * 转换相对于父元素的比例。如果转换没有父元素,则与transform.scale相同 + * @param value + */ + localScale: Vector2; + _localRotation: number; + /** + * 相对于父变换的旋转,变换的旋转。如果转换没有父元素,则与transform.rotation相同 + */ + /** + * 相对于父变换的旋转,变换的旋转。如果转换没有父元素,则与transform.rotation相同 + * @param value + */ + localRotation: number; + /** + * 返回在索引处的转换子元素 + * @param index + */ + getChild(index: number): Transform; + /** + * 设置此转换的父转换 + * @param parent + */ + setParent(parent: Transform): Transform; + /** + * 设置转换在世界空间中的位置 + * @param x + * @param y + */ + setPosition(x: number, y: number): Transform; + /** + * 设置转换相对于父转换的位置。如果转换没有父元素,则与transform.position相同 + * @param localPosition + */ + setLocalPosition(localPosition: Vector2): Transform; + /** + * 设置变换在世界空间的旋转度 + * @param radians + */ + setRotation(radians: number): Transform; + /** + * 设置变换在世界空间的旋转度 + * @param degrees + */ + setRotationDegrees(degrees: number): Transform; + /** + * 旋转精灵的顶部,使其朝向位置 + * @param pos + */ + lookAt(pos: Vector2): void; + /** + * 相对于父变换的旋转设置变换的旋转。如果转换没有父元素,则与transform.rotation相同 + * @param radians + */ + setLocalRotation(radians: number): this; + /** + * 相对于父变换的旋转设置变换的旋转。如果转换没有父元素,则与transform.rotation相同 + * @param degrees + */ + setLocalRotationDegrees(degrees: number): Transform; + /** + * 设置变换在世界空间中的缩放 + * @param scale + */ + setScale(scale: Vector2): Transform; + /** + * 设置转换相对于父对象的比例。如果转换没有父元素,则与transform.scale相同 + * @param scale + */ + setLocalScale(scale: Vector2): Transform; + /** + * 对精灵坐标进行四舍五入 + */ + roundPosition(): void; + updateTransform(): void; + setDirty(dirtyFlagType: DirtyType): void; + /** + * 从另一个transform属性进行拷贝 + * @param transform + */ + copyFrom(transform: Transform): void; + toString(): string; + } +} +declare module es { + /** + * 接口,当添加到一个Component时,只要Component和实体被启用,它就会在每个框架中调用更新方法。 + */ + interface IUpdatable { + enabled: boolean; + updateOrder: number; + update(): any; + } + /** + * 用于比较组件更新排序 + */ + class IUpdatableComparer implements IComparer { + compare(a: IUpdatable, b: IUpdatable): number; + } + var isIUpdatable: (props: any) => props is IUpdatable; +} +declare module es { + class SceneComponent implements IComparer { + /** + * 这个场景组件被附加到的场景 + */ + scene: Scene; + /** + * 如果启用了SceneComponent,则为true。状态的改变会导致调用onEnabled/onDisable。 + */ + /** + * 如果启用了SceneComponent,则为true。状态的改变会导致调用onEnabled/onDisable。 + * @param value + */ + enabled: boolean; + /** + * 更新此场景中SceneComponents的顺序 + */ + updateOrder: number; + _enabled: boolean; + /** + * 在启用此SceneComponent时调用 + */ + onEnabled(): void; + /** + * 当禁用此SceneComponent时调用 + */ + onDisabled(): void; + /** + * 当该SceneComponent从场景中移除时调用 + */ + onRemovedFromScene(): void; + /** + * 在实体更新之前每一帧调用 + */ + update(): void; + /** + * 启用/禁用这个SceneComponent + * @param isEnabled + */ + setEnabled(isEnabled: boolean): SceneComponent; + /** + * 设置SceneComponent的updateOrder并触发某种SceneComponent + * @param updateOrder + */ + setUpdateOrder(updateOrder: number): this; + compare(other: SceneComponent): number; + } +} +declare module es { + /** + * 请注意,这不是一个完整的、多迭代的物理系统!它可以用于简单的、街机风格的物理。 + * 这可以用于简单的,街机风格的物理学 + */ + class ArcadeRigidbody extends Component implements IUpdatable { + /** 这个刚体的质量。质量为0,则是一个不可移动的物体 */ + mass: number; + /** + * 0-1范围,其中0为无反弹,1为完全反射。 + */ + readonly elasticity: number; + elasticiy: number; + /** + * 0 - 1范围。0表示没有摩擦力,1表示物体会停止在原地 + */ + friction: number; + /** + * 0-9的范围。当发生碰撞时,沿碰撞面做直线运动时,如果其平方幅度小于glue摩擦力,则将碰撞设置为上限 + */ + glue: number; + /** + * 如果为真,则每一帧都会考虑到Physics.gravity + */ + shouldUseGravity: boolean; + /** + * 该刚体的速度 + */ + velocity: Vector2; + /** + * 质量为0的刚体被认为是不可移动的。改变速度和碰撞对它们没有影响 + */ + readonly isImmovable: boolean; + _mass: number; + _elasticity: number; + _friction: number; + _glue: number; + _inverseMass: number; + _collider: Collider; + constructor(); + /** + * 这个刚体的质量。质量为0,则是一个不可移动的物体 + * @param mass + */ + setMass(mass: number): ArcadeRigidbody; + /** + * 0-1范围,其中0为无反弹,1为完全反射。 + * @param value + */ + setElasticity(value: number): ArcadeRigidbody; + /** + * 0 - 1范围。0表示没有摩擦力,1表示物体会停止在原地 + * @param value + */ + setFriction(value: number): ArcadeRigidbody; + /** + * 0-9的范围。当发生碰撞时,沿碰撞面做直线运动时,如果其平方幅度小于glue摩擦力,则将碰撞设置为上限 + * @param value + */ + setGlue(value: number): ArcadeRigidbody; + setVelocity(velocity: Vector2): ArcadeRigidbody; + /** + * 用刚体的质量给刚体加上一个瞬间的力脉冲。力是一个加速度,单位是每秒像素每秒。将力乘以100000,使数值使用更合理 + * @param force + */ + addImpulse(force: Vector2): void; + onAddedToEntity(): void; + update(): void; + /** + * 将两个重叠的刚体分开。也处理其中一个不可移动的情况 + * @param other + * @param minimumTranslationVector + */ + processOverlap(other: ArcadeRigidbody, minimumTranslationVector: Vector2): void; + /** + * 处理两个非重叠的刚体的碰撞。新的速度将根据情况分配给每个刚体 + * @param other + * @param minimumTranslationVector + */ + processCollision(other: ArcadeRigidbody, minimumTranslationVector: Vector2): void; + /** + * 给定两个物体和MTV之间的相对速度,本方法修改相对速度,使其成为碰撞响应 + * @param relativeVelocity + * @param minimumTranslationVector + * @param responseVelocity + */ + calculateResponseVelocity(relativeVelocity: Vector2, minimumTranslationVector: Vector2): Vector2; + } +} +declare module es { + class CharacterCollisionState2D { + right: boolean; + left: boolean; + above: boolean; + below: boolean; + becameGroundedThisFrame: boolean; + wasGroundedLastFrame: boolean; + movingDownSlope: boolean; + slopeAngle: number; + hasCollision(): boolean; + reset(): void; + toString(): string; + } + class CharacterController implements ITriggerListener { + onControllerCollidedEvent: ObservableT; + onTriggerEnterEvent: ObservableT; + onTriggerExitEvent: ObservableT; + /** + * 如果为 true,则在垂直移动单帧时将忽略平台的一种方式 + */ + ignoreOneWayPlatformsTime: number; + supportSlopedOneWayPlatforms: boolean; + ignoredColliders: Set; + /** + * 定义距离碰撞射线的边缘有多远。 + * 如果使用 0 范围进行投射,则通常会导致不需要的光线击中(例如,直接从表面水平投射的足部碰撞器可能会导致击中) + */ + skinWidth: number; + /** + * CC2D 可以爬升的最大坡度角 + */ + slopeLimit: number; + /** + * 构成跳跃的帧之间垂直运动变化的阈值 + */ + jumpingThreshold: number; + /** + * 基于斜率乘以速度的曲线(负 = 下坡和正 = 上坡) + */ + slopeSpeedMultiplier: AnimCurve; + totalHorizontalRays: number; + totalVerticalRays: number; + collisionState: CharacterCollisionState2D; + velocity: Vector2; + readonly isGrounded: boolean; + readonly raycastHitsThisFrame: RaycastHit[]; + constructor(player: Entity, skinWidth?: number, platformMask?: number, onewayPlatformMask?: number, triggerMask?: number); + onTriggerEnter(other: Collider, local: Collider): void; + onTriggerExit(other: Collider, local: Collider): void; + /** + * 尝试将角色移动到位置 + deltaMovement。 任何挡路的碰撞器都会在遇到时导致运动停止 + * @param deltaMovement + * @param deltaTime + */ + move(deltaMovement: Vector2, deltaTime: number): void; + /** + * 直接向下移动直到接地 + * @param maxDistance + */ + warpToGrounded(maxDistance?: number): void; + /** + * 这应该在您必须在运行时修改 BoxCollider2D 的任何时候调用。 + * 它将重新计算用于碰撞检测的光线之间的距离。 + * 它也用于 skinWidth setter,以防在运行时更改。 + */ + recalculateDistanceBetweenRays(): void; + /** + * 将 raycastOrigins 重置为由 skinWidth 插入的框碰撞器的当前范围。 + * 插入它是为了避免从直接接触另一个碰撞器的位置投射光线,从而导致不稳定的法线数据。 + */ + private primeRaycastOrigins; + /** + * 我们必须在这方面使用一些技巧。 + * 光线必须从我们的碰撞器(skinWidth)内部的一小段距离投射,以避免零距离光线会得到错误的法线。 + * 由于这个小偏移,我们必须增加光线距离 skinWidth 然后记住在实际移动玩家之前从 deltaMovement 中删除 skinWidth + * @param deltaMovement + * @returns + */ + private moveHorizontally; + private moveVertically; + /** + * 检查 BoxCollider2D 下的中心点是否存在坡度。 + * 如果找到一个,则调整 deltaMovement 以便玩家保持接地,并考虑slopeSpeedModifier 以加快移动速度。 + * @param deltaMovement + * @returns + */ + private handleVerticalSlope; + /** + * 如果我们要上坡,则处理调整 deltaMovement + * @param deltaMovement + * @param angle + * @returns + */ + private handleHorizontalSlope; + private _player; + private _collider; + private _skinWidth; + private _triggerHelper; + /** + * 这用于计算为检查坡度而投射的向下光线。 + * 我们使用有点随意的值 75 度来计算检查斜率的射线的长度。 + */ + private _slopeLimitTangent; + private readonly kSkinWidthFloatFudgeFactor; + /** + * 我们的光线投射原点角的支架(TR、TL、BR、BL) + */ + private _raycastOrigins; + /** + * 存储我们在移动过程中命中的光线投射 + */ + private _raycastHit; + /** + * 存储此帧发生的任何光线投射命中。 + * 我们必须存储它们,以防我们遇到水平和垂直移动的碰撞,以便我们可以在设置所有碰撞状态后发送事件 + */ + private _raycastHitsThisFrame; + private _verticalDistanceBetweenRays; + private _horizontalDistanceBetweenRays; + /** + * 我们使用这个标志来标记我们正在爬坡的情况,我们修改了 delta.y 以允许爬升。 + * 原因是,如果我们到达斜坡的尽头,我们可以进行调整以保持接地 + */ + private _isGoingUpSlope; + private _isWarpingToGround; + private platformMask; + private triggerMask; + private oneWayPlatformMask; + private readonly rayOriginSkinMutiplier; + } +} +declare module es { + /** + * 当添加到组件时,每当实体上的冲突器与另一个组件重叠/退出时,将调用这些方法。 + * ITriggerListener方法将在实现接口的触发器实体上的任何组件上调用。 + * 注意,这个接口只与Mover类一起工作 + */ + interface ITriggerListener { + /** + * 当碰撞器与触发碰撞器相交时调用。这是在触发碰撞器和触发碰撞器上调用的。 + * 移动必须由Mover/ProjectileMover方法处理,以使其自动工作。 + * @param other + * @param local + */ + onTriggerEnter(other: Collider, local: Collider): any; + /** + * 当另一个碰撞器离开触发碰撞器时调用 + * @param other + * @param local + */ + onTriggerExit(other: Collider, local: Collider): any; + } + class TriggerListenerHelper { + static getITriggerListener(entity: Entity, components: ITriggerListener[]): ITriggerListener[]; + } + var isITriggerListener: (props: any) => props is ITriggerListener; +} +declare module es { + /** + * 辅助类说明了一种处理移动的方法,它考虑了包括触发器在内的所有冲突。 + * ITriggerListener接口用于管理对移动过程中违反的任何触发器的回调。 + * 一个物体只能通过移动器移动。要正确报告触发器的move方法。 + * + * 请注意,多个移动者相互交互将多次调用ITriggerListener。 + */ + class Mover extends Component { + private _triggerHelper; + onAddedToEntity(): void; + /** + * 计算修改运动矢量的运动,以考虑移动时可能发生的碰撞 + * @param motion + * @param collisionResult + */ + calculateMovement(motion: Vector2, collisionResult: CollisionResult): boolean; + /** + * 将calculatemomovement应用到实体并更新triggerHelper + * @param motion + */ + applyMovement(motion: Vector2): void; + /** + * 通过调用calculateMovement和applyMovement来移动考虑碰撞的实体; + * @param motion + * @param collisionResult + */ + move(motion: Vector2, collisionResult: CollisionResult): boolean; + } +} +declare module es { + /** + * 移动时考虑到碰撞,只用于向任何ITriggerListeners报告。 + * 物体总是会全量移动,所以如果需要的话,由调用者在撞击时销毁它。 + */ + class ProjectileMover extends Component { + private _tempTriggerList; + private _collider; + onAddedToEntity(): void; + /** + * 在考虑到碰撞的情况下移动实体 + * @param motion + */ + move(motion: Vector2): boolean; + private notifyTriggerListeners; + } +} +declare module es { + abstract class Collider extends Component { + static readonly lateSortOrder: number; + castSortOrder: number; + /** + * 对撞机的基本形状 + */ + shape: Shape; + /** + * 如果这个碰撞器是一个触发器,它将不会引起碰撞,但它仍然会触发事件 + */ + isTrigger: boolean; + /** + * 在处理冲突时,physicsLayer可以用作过滤器。Flags类有帮助位掩码的方法 + */ + physicsLayer: Ref; + /** + * 碰撞器在使用移动器移动时应该碰撞的层 + * 默认为所有层 + */ + collidesWithLayers: Ref; + /** + * 如果为true,碰撞器将根据附加的变换缩放和旋转 + */ + shouldColliderScaleAndRotateWithTransform: boolean; + /** + * 这个对撞机在物理系统注册时的边界。 + * 存储这个允许我们始终能够安全地从物理系统中移除对撞机,即使它在试图移除它之前已经被移动了。 + */ + registeredPhysicsBounds: Rectangle; + protected _colliderRequiresAutoSizing: boolean; + _localOffsetLength: number; + _isPositionDirty: boolean; + _isRotationDirty: boolean; + /** + * 标记来跟踪我们的实体是否被添加到场景中 + */ + protected _isParentEntityAddedToScene: any; + /** + * 标记来记录我们是否注册了物理系统 + */ + protected _isColliderRegistered: any; + /** + * 镖师碰撞器的绝对位置 + */ + readonly absolutePosition: Vector2; + /** + * 封装变换。如果碰撞器没和实体一起旋转 则返回transform.rotation + */ + readonly rotation: number; + readonly bounds: Rectangle; + protected _localOffset: Vector2; + /** + * 将localOffset添加到实体。获取碰撞器几何图形的最终位置。 + * 允许向一个实体添加多个碰撞器并分别定位,还允许你设置缩放/旋转 + */ + /** + * 将localOffset添加到实体。获取碰撞器几何图形的最终位置。 + * 允许向一个实体添加多个碰撞器并分别定位,还允许你设置缩放/旋转 + * @param value + */ + localOffset: Vector2; + /** + * 将localOffset添加到实体。获取碰撞器的最终位置。 + * 这允许您向一个实体添加多个碰撞器并分别定位它们。 + * @param offset + */ + setLocalOffset(offset: Vector2): Collider; + /** + * 如果为true,碰撞器将根据附加的变换缩放和旋转 + * @param shouldColliderScaleAndRotationWithTransform + */ + setShouldColliderScaleAndRotateWithTransform(shouldColliderScaleAndRotationWithTransform: boolean): Collider; + onAddedToEntity(): void; + onRemovedFromEntity(): void; + onEntityTransformChanged(comp: ComponentTransform): void; + onEnabled(): void; + onDisabled(): void; + /** + * 父实体会在不同的时间调用它(当添加到场景,启用,等等) + */ + registerColliderWithPhysicsSystem(): void; + /** + * 父实体会在不同的时候调用它(从场景中移除,禁用,等等) + */ + unregisterColliderWithPhysicsSystem(): void; + /** + * 检查这个形状是否与物理系统中的其他对撞机重叠 + * @param other + */ + overlaps(other: Collider): boolean; + /** + * 检查这个与运动应用的碰撞器(移动向量)是否与碰撞器碰撞。如果是这样,将返回true,并且结果将填充碰撞数据。 + * @param collider + * @param motion + * @param result + */ + collidesWith(collider: Collider, motion: Vector2, result?: CollisionResult): boolean; + /** + * 检查这个对撞机是否与对撞机发生碰撞。如果碰撞,则返回true,结果将被填充 + * @param collider + * @param result + */ + collidesWithNonMotion(collider: Collider, result?: CollisionResult): boolean; + /** + * 检查此碰撞器是否已应用运动(增量运动矢量)与任何碰撞器发生碰撞。 + * 如果是这样,则将返回true,并且将使用碰撞数据填充结果。 运动将设置为碰撞器在碰撞之前可以行进的最大距离。 + * @param motion + * @param result + */ + collidesWithAny(motion: Vector2, result: CollisionResult): boolean; + /** + * 检查此碰撞器是否与场景中的其他碰撞器碰撞。它相交的第一个碰撞器将在碰撞结果中返回碰撞数据。 + * @param result + */ + collidesWithAnyNonMotion(result?: CollisionResult): boolean; + } +} +declare module es { + class BoxCollider extends Collider { + /** + * 创建一个BoxCollider,并使用x/y组件作为局部Offset + * @param x + * @param y + * @param width + * @param height + */ + constructor(x?: number, y?: number, width?: number, height?: number); + width: number; + height: number; + /** + * 设置BoxCollider的大小 + * @param width + * @param height + */ + setSize(width: number, height: number): this; + /** + * 设置BoxCollider的宽度 + * @param width + */ + setWidth(width: number): BoxCollider; + /** + * 设置BoxCollider的高度 + * @param height + */ + setHeight(height: number): void; + debugRender(batcher: IBatcher): void; + toString(): string; + } +} +declare module es { + class CircleCollider extends Collider { + /** + * 创建一个具有半径的CircleCollider。 + * 请注意,当指定半径时,如果在实体上使用RenderableComponent,您将需要设置原点来对齐CircleCollider。 + * 例如,如果RenderableComponent有一个0,0的原点,并且创建了一个半径为1.5f * renderable.width的CircleCollider,你可以通过设置originNormalied为中心除以缩放尺寸来偏移原点 + * + * @param radius + */ + constructor(radius?: number); + radius: number; + /** + * 设置圆的半径 + * @param radius + */ + setRadius(radius: number): CircleCollider; + debugRender(batcher: IBatcher): void; + toString(): string; + } +} +declare module es { + /** + * 多边形应该以顺时针方式定义 + */ + class PolygonCollider extends Collider { + /** + * 如果这些点没有居中,它们将以localOffset的差异为居中。 + * @param points + */ + constructor(points: Vector2[]); + } +} +declare module es { + interface IRenderable { + enabled: boolean; + renderLayer: number; + isVisibleFromCamera(camera: ICamera): boolean; + render(batcher: IBatcher, camera: ICamera): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + abstract class RenderableComponent extends es.Component implements IRenderable { + getwidth(): number; + getheight(): number; + protected _bounds: es.Rectangle; + getbounds(): es.Rectangle; + readonly bounds: Rectangle; + protected _areBoundsDirty: boolean; + color: Color; + renderLayer: number; + protected _renderLayer: number; + onEntityTransformChanged(comp: ComponentTransform): void; + localOffset: es.Vector2; + setLocalOffset(offset: es.Vector2): this; + isVisible: boolean; + debugRenderEnabled: boolean; + protected _isVisible: boolean; + protected _localOffset: es.Vector2; + abstract render(batcher: IBatcher, camera: ICamera): void; + protected onBecameVisible(): void; + protected onBecameInvisible(): void; + setRenderLayer(renderLayer: number): RenderableComponent; + isVisibleFromCamera(cam: ICamera): boolean; + debugRender(batcher: IBatcher): void; + tweenColorTo(to: Color, duration: number): RenderableColorTween; + } +} +declare module es { + interface Map { + clear(): void; + containsKey(key: any): boolean; + containsValue(value: any): boolean; + get(key: any): V; + isEmpty(): boolean; + put(key: any, value: any): void; + remove(key: any): V; + size(): number; + values(): V[]; + } + class HashMap implements Map { + private map_; + private keys_; + constructor(); + clear(): void; + values(): V[]; + contains(value: any): boolean; + containsKey(key: any): boolean; + containsValue(value: any): boolean; + get(key: K): V; + isEmpty(): boolean; + keys(): K[]; + /** + * if key is a string, use as is, else use key.id_ or key.name + */ + put(key: any, value: any): void; + remove(key: any): V; + size(): number; + } +} +declare module es { + /** + * 追踪实体的子集,但不实现任何排序或迭代。 + */ + abstract class EntitySystem { + private _entities; + constructor(matcher?: Matcher); + private _scene; + /** + * 这个系统所属的场景 + */ + scene: Scene; + private _matcher; + readonly matcher: Matcher; + private _startTime; + private _endTime; + private _useTime; + /** 获取系统在当前帧所消耗的时间 仅在debug模式下生效 */ + readonly useTime: number; + initialize(): void; + onChanged(entity: Entity): void; + add(entity: Entity): void; + onAdded(entity: Entity): void; + remove(entity: Entity): void; + onRemoved(entity: Entity): void; + update(): void; + lateUpdate(): void; + /** + * 在系统处理开始前调用 + * 在下一个系统开始处理或新的处理回合开始之前(以先到者为准),使用此方法创建的任何实体都不会激活 + */ + protected begin(): void; + protected process(entities: Entity[]): void; + protected lateProcess(entities: Entity[]): void; + /** + * 系统处理完毕后调用 + */ + protected end(): void; + /** + * 系统是否需要处理 + * + * 在启用系统时有用,但仅偶尔需要处理 + * 这只影响处理,不影响事件或订阅列表 + * @returns 如果系统应该处理,则为true,如果不处理则为false。 + */ + protected checkProcessing(): boolean; + } +} +declare module es { + /** + * 追踪每个实体的冷却时间,当实体的计时器耗尽时进行处理 + * + * 一个示例系统将是ExpirationSystem,该系统将在特定生存期后删除实体。 + * 你不必运行会为每个实体递减timeLeft值的系统 + * 而只需使用此系统在寿命最短的实体时在将来执行 + * 然后重置系统在未来的某一个最短命实体的时间运行 + * + * 另一个例子是一个动画系统 + * 你知道什么时候你必须对某个实体进行动画制作,比如300毫秒内。 + * 所以你可以设置系统以300毫秒为单位运行来执行动画 + * + * 这将在某些情况下节省CPU周期 + */ + abstract class DelayedIteratingSystem extends EntitySystem { + /** + * 一个实体应被处理的时间 + */ + private delay; + /** + * 如果系统正在运行,并倒计时延迟 + */ + private running; + /** + * 倒计时 + */ + private acc; + constructor(matcher: Matcher); + protected process(entities: Entity[]): void; + protected checkProcessing(): boolean; + /** + * 只有当提供的延迟比系统当前计划执行的时间短时,才会重新启动系统。 + * 如果系统已经停止(不运行),那么提供的延迟将被用来重新启动系统,无论其值如何 + * 如果系统已经在倒计时,并且提供的延迟大于剩余时间,系统将忽略它。 + * 如果提供的延迟时间短于剩余时间,系统将重新启动,以提供的延迟时间运行。 + * @param offeredDelay + */ + offerDelay(offeredDelay: number): void; + /** + * 处理本系统感兴趣的实体 + * 从实体定义的延迟中抽象出accumulativeDelta + * @param entity + * @param accumulatedDelta 本系统最后一次执行后的delta时间 + */ + protected abstract processDelta(entity: Entity, accumulatedDelta: number): any; + protected abstract processExpired(entity: Entity): any; + /** + * 返回该实体处理前的延迟时间 + * @param entity + */ + protected abstract getRemainingDelay(entity: Entity): number; + /** + * 获取系统被命令处理实体后的初始延迟 + */ + getInitialTimeDelay(): number; + /** + * 获取系统计划运行前的时间 + * 如果系统没有运行,则返回零 + */ + getRemainingTimeUntilProcessing(): number; + /** + * 检查系统是否正在倒计时处理 + */ + isRunning(): boolean; + /** + * 停止系统运行,中止当前倒计时 + */ + stop(): void; + } +} +declare module es { + /** + * 基本实体处理系统。将其用作处理具有特定组件的许多实体的基础 + * + * 按实体引用遍历实体订阅成员实体的系统 + * 当你需要处理与Matcher相匹配的实体,并且你更喜欢使用Entity的时候,可以使用这个功能。 + */ + abstract class EntityProcessingSystem extends EntitySystem { + constructor(matcher: Matcher); + /** + * 处理特定的实体 + * @param entity + */ + abstract processEntity(entity: Entity): any; + lateProcessEntity(entity: Entity): void; + /** + * 遍历这个系统的所有实体并逐个处理它们 + * @param entities + */ + protected process(entities: Entity[]): void; + protected lateProcess(entities: Entity[]): void; + } +} +declare module es { + /** + * 实体系统以一定的时间间隔进行处理 + */ + abstract class IntervalSystem extends EntitySystem { + /** + * 累积增量以跟踪间隔 + */ + protected acc: number; + /** + * 更新之间需要等待多长时间 + */ + private readonly interval; + private intervalDelta; + constructor(matcher: Matcher, interval: number); + protected checkProcessing(): boolean; + /** + * 获取本系统上次处理后的实际delta值 + */ + protected getIntervalDelta(): number; + } +} +declare module es { + /** + * 每x个ticks处理一个实体的子集 + * + * 典型的用法是每隔一定的时间间隔重新生成弹药或生命值 + * 而无需在每个游戏循环中都进行 + * 而是每100毫秒一次或每秒 + */ + abstract class IntervalIteratingSystem extends IntervalSystem { + constructor(matcher: Matcher, interval: number); + /** + * 处理本系统感兴趣的实体 + * @param entity + */ + abstract processEntity(entity: Entity): any; + protected process(entities: Entity[]): void; + } +} +declare module es { + /** + * JobSystem使用实体的子集调用Execute(entities),并在指定数量的线程上分配工作负载。 + */ + abstract class JobSystem extends EntitySystem { + readonly _threads: number; + readonly _jobs: Job[]; + readonly _executeStr: string; + constructor(matcher: Matcher, threads: number); + protected process(entities: Entity[]): void; + private queueOnThread; + /** + * 当操作完成时,改变的值需要用户进行手动传递 + * 由于worker数据无法共享,所以这块需要特殊处理 + * @example this.test = job[0].context.test; + * @param job + */ + protected abstract resetJob(job: Job): any; + /** + * 对指定实体进行多线程操作 + * @param entity + */ + protected abstract execute(entity: Entity): any; + } + class Job { + entities: Entity[]; + from: number; + to: number; + worker: Worker; + execute: string; + err: string; + context: any; + set(entities: Entity[], from: number, to: number, execute: string, context: any): void; + } +} +declare module es { + abstract class PassiveSystem extends EntitySystem { + onChanged(entity: Entity): void; + protected process(entities: Entity[]): void; + } +} +/** 用于协调其他系统的通用系统基类 */ +declare module es { + abstract class ProcessingSystem extends EntitySystem { + onChanged(entity: Entity): void; + /** 处理我们的系统 每帧调用 */ + abstract processSystem(): any; + protected process(entities: Entity[]): void; + } +} +declare module es { + class Bits { + private _bit; + set(index: number, value: number): void; + get(index: number): number; + } +} +declare module es { + class ComponentList { + /** + * 组件列表的全局updateOrder排序 + */ + static compareUpdatableOrder: IUpdatableComparer; + _entity: Entity; + /** + * 添加到实体的组件列表 + */ + _components: Component[]; + /** + * 所有需要更新的组件列表 + */ + _updatableComponents: IUpdatable[]; + /** + * 添加到此框架的组件列表。用来对组件进行分组,这样我们就可以同时进行加工 + */ + _componentsToAdd: { + [index: number]: Component; + }; + /** + * 标记要删除此框架的组件列表。用来对组件进行分组,这样我们就可以同时进行加工 + */ + _componentsToRemove: { + [index: number]: Component; + }; + _componentsToAddList: Component[]; + _componentsToRemoveList: Component[]; + _tempBufferList: Component[]; + /** + * 用于确定是否需要对该框架中的组件进行排序的标志 + */ + _isComponentListUnsorted: boolean; + private componentsByType; + private componentsToAddByType; + constructor(entity: Entity); + readonly count: number; + readonly buffer: Component[]; + markEntityListUnsorted(): void; + add(component: Component): void; + remove(component: Component): void; + /** + * 立即从组件列表中删除所有组件 + */ + removeAllComponents(): void; + deregisterAllComponents(): void; + registerAllComponents(): void; + private decreaseBits; + private addBits; + /** + * 处理任何需要删除或添加的组件 + */ + updateLists(): void; + handleRemove(component: Component): void; + private removeComponentsByType; + private addComponentsByType; + private removeComponentsToAddByType; + private addComponentsToAddByType; + /** + * 获取类型T的第一个组件并返回它 + * 可以选择跳过检查未初始化的组件(尚未调用onAddedToEntity方法的组件) + * 如果没有找到组件,则返回null。 + * @param type + * @param onlyReturnInitializedComponents + */ + getComponent(type: any, onlyReturnInitializedComponents: boolean): T; + /** + * 获取T类型的所有组件,但不使用列表分配 + * @param typeName + * @param components + */ + getComponents(typeName: any, components?: any[]): any[]; + update(): void; + onEntityTransformChanged(comp: ComponentTransform): void; + onEntityEnabled(): void; + onEntityDisabled(): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + class ComponentTypeFactory { + private componentTypes_; + private componentTypeCount_; + types: Bag; + constructor(); + getTypeFor(c: any): ComponentType; + getIndexFor(c: any): number; + } +} +declare module es { + class ComponentTypeManager { + private static _componentTypesMask; + static add(type: any): void; + static getIndexFor(type: any): number; + } +} +declare module es { + class EntityList { + scene: Scene; + /** + * 场景中添加的实体列表 + */ + _entities: Entity[]; + /** + * 本帧添加的实体列表。用于对实体进行分组,以便我们可以同时处理它们 + */ + _entitiesToAdded: { + [index: number]: Entity; + }; + /** + * 本帧被标记为删除的实体列表。用于对实体进行分组,以便我们可以同时处理它们 + */ + _entitiesToRemove: { + [index: number]: Entity; + }; + _entitiesToAddedList: Entity[]; + _entitiesToRemoveList: Entity[]; + /** + * 标志,用于确定我们是否需要在这一帧中对实体进行排序 + */ + _isEntityListUnsorted: boolean; + /** + * 通过标签跟踪实体,便于检索 + */ + _entityDict: Map>; + _unsortedTags: Set; + constructor(scene: Scene); + readonly count: number; + readonly buffer: Entity[]; + markEntityListUnsorted(): void; + markTagUnsorted(tag: number): void; + /** + * 将一个实体添加到列表中。所有的生命周期方法将在下一帧中被调用 + * @param entity + */ + add(entity: Entity): void; + /** + * 从列表中删除一个实体。所有的生命周期方法将在下一帧中被调用 + * @param entity + */ + remove(entity: Entity): void; + /** + * 从实体列表中删除所有实体 + */ + removeAllEntities(): void; + /** + * 检查实体目前是否由这个EntityList管理 + * @param entity + */ + contains(entity: Entity): boolean; + getTagList(tag: number): Set; + addToTagList(entity: Entity): void; + removeFromTagList(entity: Entity): void; + update(): void; + updateLists(): void; + /** + * 返回第一个找到的名字为name的实体。如果没有找到则返回null + * @param name + */ + findEntity(name: string): Entity; + /** + * + * @param id + * @returns + */ + findEntityById(id: number): Entity; + /** + * 返回带有标签的所有实体的列表。如果没有实体有标签,则返回一个空列表。 + * 返回的List可以通过ListPool.free放回池中 + * @param tag + */ + entitiesWithTag(tag: number): Entity[]; + /** + * 返回第一个找到该tag的实体 + * @param tag + * @returns + */ + entityWithTag(tag: number): Entity; + /** + * 返回在场景中找到的第一个T类型的组件。 + * @param type + */ + findComponentOfType(type: any): T; + /** + * 返回在场景中找到的所有T类型的组件。 + * 返回的List可以通过ListPool.free放回池中。 + * @param type + */ + findComponentsOfType(type: any): T[]; + /** + * 返回场景中包含特定组件的实体列表 + * @param types + * @returns + */ + findEntitesOfComponent(...types: any[]): Entity[]; + } +} +declare module es { + class EntityProcessorList { + _processors: EntitySystem[]; + add(processor: EntitySystem): void; + remove(processor: EntitySystem): void; + onComponentAdded(entity: Entity): void; + onComponentRemoved(entity: Entity): void; + onEntityAdded(entity: Entity): void; + onEntityRemoved(entity: Entity): void; + begin(): void; + update(): void; + lateUpdate(): void; + end(): void; + getProcessor(type: new (...args: any[]) => T): T; + protected notifyEntityChanged(entity: Entity): void; + protected removeFromProcessors(entity: Entity): void; + } +} +declare module es { + class HashHelpers { + static readonly hashCollisionThreshold: number; + static readonly hashPrime: number; + /** + * 用来作为哈希表大小的质数表。 + * 一个典型的调整大小的算法会在这个数组中选取比之前容量大两倍的最小质数。 + * 假设我们的Hashtable当前的容量为x,并且添加了足够多的元素,因此需要进行大小调整。 + * 调整大小首先计算2x,然后在表中找到第一个大于2x的质数,即如果质数的顺序是p_1,p_2,...,p_i,...,则找到p_n,使p_n-1 < 2x < p_n。 + * 双倍对于保持哈希特操作的渐近复杂度是很重要的,比如添加。 + * 拥有一个质数可以保证双倍哈希不会导致无限循环。 IE,你的哈希函数将是h1(key)+i*h2(key),0 <= i < size.h2和size必须是相对质数。 + */ + static readonly primes: number[]; + /** + * 这是比Array.MaxArrayLength小的最大质数 + */ + static readonly maxPrimeArrayLength: number; + static isPrime(candidate: number): boolean; + static getPrime(min: number): number; + /** + * + * @param oldSize + * @returns 返回要增长的哈希特表的大小 + */ + static expandPrime(oldSize: number): number; + static getHashCode(str: any): number; + } +} +declare module es { + class IdentifierPool { + private ids; + private nextAvailableId_; + constructor(); + checkOut(): number; + checkIn(id: number): void; + } +} +declare module es { + class Matcher { + protected allSet: (new (...args: any[]) => Component)[]; + protected exclusionSet: (new (...args: any[]) => Component)[]; + protected oneSet: (new (...args: any[]) => Component)[]; + static empty(): Matcher; + getAllSet(): (new (...args: any[]) => Component)[]; + getExclusionSet(): (new (...args: any[]) => Component)[]; + getOneSet(): (new (...args: any[]) => Component)[]; + isInterestedEntity(e: Entity): boolean; + isInterested(components: Bits): boolean; + all(...types: any[]): Matcher; + exclude(...types: any[]): this; + one(...types: any[]): this; + } +} +declare module es { + class RenderableComponentList { + private _components; + private _componentsByRenderLayer; + private _unsortedRenderLayers; + private _componentsNeedSort; + readonly count: number; + get(index: number): IRenderable; + add(component: IRenderable): void; + remove(component: IRenderable): void; + updateRenderableRenderLayer(component: IRenderable, oldRenderLayer: number, newRenderLayer: number): void; + setRenderLayerNeedsComponentSort(renderLayer: number): void; + setNeedsComponentSort(): void; + private addToRenderLayerList; + componentsWithRenderLayer(renderLayer: number): IRenderable[]; + updateLists(): void; + } +} +declare class StringUtils { + /** + * 特殊符号字符串 + */ + private static specialSigns; + /** + * 匹配中文字符 + * @param str 需要匹配的字符串 + * @return + */ + static matchChineseWord(str: string): string[]; + /** + * 去除字符串左端的空白字符 + * @param target 目标字符串 + * @return + */ + static lTrim(target: string): string; + /** + * 去除字符串右端的空白字符 + * @param target 目标字符串 + * @return + */ + static rTrim(target: string): string; + /** + * 返回一个去除2段空白字符的字符串 + * @param target + * @return 返回一个去除2段空白字符的字符串 + */ + static trim(target: string): string; + /** + * 返回该字符是否为空白字符 + * @param str + * @return 返回该字符是否为空白字符 + */ + static isWhiteSpace(str: string): boolean; + /** + * 返回执行替换后的字符串 + * @param mainStr 待查找字符串 + * @param targetStr 目标字符串 + * @param replaceStr 替换字符串 + * @param caseMark 是否忽略大小写 + * @return 返回执行替换后的字符串 + */ + static replaceMatch(mainStr: string, targetStr: string, replaceStr: string, caseMark?: boolean): string; + /** + * 用html实体换掉字符窜中的特殊字符 + * @param str 需要替换的字符串 + * @param reversion 是否翻转替换:将转义符号替换为正常的符号 + * @return 换掉特殊字符后的字符串 + */ + static htmlSpecialChars(str: string, reversion?: boolean): string; + /** + * 给数字字符前面添 "0" + * + * @param str 要进行处理的字符串 + * @param width 处理后字符串的长度, + * 如果str.length >= width,将不做任何处理直接返回原始的str。 + * @return + * + */ + static zfill(str: string, width?: number): string; + /** + * 翻转字符串 + * @param str 字符串 + * @return 翻转后的字符串 + */ + static reverse(str: string): string; + /** + * 截断某段字符串 + * @param str 目标字符串 + * @param start 需要截断的起始索引 + * @param en 截断长度 + * @param order 顺序,true从字符串头部开始计算,false从字符串尾巴开始结算。 + * @return 截断后的字符串 + */ + static cutOff(str: string, start: number, len: number, order?: boolean): string; + /** + * {0} 字符替换 + */ + static strReplace(str: string, rStr: string[]): string; + static format(str: string, ...args: any[]): string; +} +declare module es { + /** 提供帧定时信息 */ + class Time { + /** 游戏运行的总时间 */ + static totalTime: number; + /** deltaTime的未缩放版本。不受时间尺度的影响 */ + static unscaledDeltaTime: number; + /** 前一帧到当前帧的时间增量,按时间刻度进行缩放 */ + static deltaTime: number; + /** 时间刻度缩放 */ + static timeScale: number; + /** DeltaTime可以为的最大值 */ + static maxDeltaTime: number; + /** 已传递的帧总数 */ + static frameCount: number; + /** 自场景加载以来的总时间 */ + static timeSinceSceneLoad: number; + private static _lastTime; + static update(currentTime: number): void; + static sceneChanged(): void; + /** + * 允许在间隔检查。只应该使用高于delta的间隔值,否则它将始终返回true。 + * @param interval + */ + static checkEvery(interval: number): boolean; + } +} +declare class TimeUtils { + /** + * 计算月份ID + * @param d 指定计算日期 + * @returns 月ID + */ + static monthId(d?: Date): number; + /** + * 计算日期ID + * @param d 指定计算日期 + * @returns 日期ID + */ + static dateId(t?: Date): number; + /** + * 计算周ID + * @param d 指定计算日期 + * @returns 周ID + */ + static weekId(d?: Date, first?: boolean): number; + /** + * 计算俩日期时间差,如果a比b小,返回负数 + */ + static diffDay(a: Date, b: Date, fixOne?: boolean): number; + /** + * 获取本周一 凌晨时间 + */ + static getFirstDayOfWeek(d?: Date): Date; + /** + * 获取当日凌晨时间 + */ + static getFirstOfDay(d?: Date): Date; + /** + * 获取次日凌晨时间 + */ + static getNextFirstOfDay(d?: Date): Date; + /** + * @returns 2018-12-12 + */ + static formatDate(date: Date): string; + /** + * @returns 2018-12-12 12:12:12 + */ + static formatDateTime(date: Date): string; + /** + * @returns s 2018-12-12 或者 2018-12-12 12:12:12 + */ + static parseDate(s: string): Date; + /** + * 秒数转换为时间形式。 + * @param time 秒数 + * @param partition 分隔符 + * @param showHour 是否显示小时 + * @return 返回一个以分隔符分割的时, 分, 秒 + * + * 比如: time = 4351; secondToTime(time)返回字符串01:12:31; + */ + static secondToTime(time?: number, partition?: string, showHour?: boolean): string; + /** + * 时间形式转换为毫秒数。 + * @param time 以指定分隔符分割的时间字符串 + * @param partition 分隔符 + * @return 毫秒数显示的字符串 + * @throws Error Exception + * + * 用法1 trace(MillisecondTransform.timeToMillisecond("00:60:00")) + * 输出 3600000 + * + * + * 用法2 trace(MillisecondTransform.timeToMillisecond("00.60.00",".")) + * 输出 3600000 + */ + static timeToMillisecond(time: string, partition?: string): string; +} +declare module es { + /** + * 开辟一个新线程 + * 注意:它无法获得主线程中的上下文 + */ + class WorkerUtils { + /** 正在执行的队列 */ + private static readonly pendingJobs; + private static jobIdGen; + /** + * 创建一个worker + * @param doFunc worker所能做的事情 + */ + static makeWorker(doFunc: Function): Worker; + static workerMessage(worker: Worker): (...message: any[]) => Promise<{}>; + } +} +declare module es { + class Graphics { + static instance: Graphics; + batcher: IBatcher; + constructor(batcher: IBatcher); + } +} +declare module es { + class Color { + /** + * 红色通道 + */ + r: number; + /** + * 绿色通道 + */ + g: number; + /** + * 蓝色通道 + */ + b: number; + /** + * 透明度通道 (仅0-1之间) + */ + a: number; + /** + * 色调 + */ + h: number; + /** + * 饱和 + */ + s: number; + /** + * 亮度 + */ + l: number; + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + constructor(r: number, g: number, b: number, a?: number); + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + static fromRGB(r: number, g: number, b: number, a?: number): Color; + /** + * 从十六进制字符串创建一个新的 Color 实例 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + static createFromHex(hex: string): Color; + /** + * 从 hsl 值创建一个新的 Color 实例 + * + * @param h 色调表示 [0-1] + * @param s 饱和度表示为 [0-1] + * @param l 亮度表示 [0-1] + * @param a 透明度表示 [0-1] + */ + static fromHSL(h: number, s: number, l: number, a?: number): Color; + /** + * 将当前颜色调亮指定的量 + * + * @param factor + */ + lighten(factor?: number): Color; + /** + * 将当前颜色变暗指定的量 + * + * @param factor + */ + darken(factor?: number): Color; + /** + * 使当前颜色饱和指定的量 + * + * @param factor + */ + saturate(factor?: number): Color; + /** + * 按指定量降低当前颜色的饱和度 + * + * @param factor + */ + desaturate(factor?: number): Color; + /** + * 将一种颜色乘以另一种颜色,得到更深的颜色 + * + * @param color + */ + mulitiply(color: Color): Color; + /** + * 筛选另一种颜色,导致颜色较浅 + * + * @param color + */ + screen(color: Color): Color; + /** + * 反转当前颜色 + */ + invert(): Color; + /** + * 将当前颜色与另一个颜色平均 + * + * @param color + */ + average(color: Color): Color; + /** + * 返回颜色的 CSS 字符串表示形式。 + * + * @param format + */ + toString(format?: 'rgb' | 'hsl' | 'hex'): string; + /** + * 返回颜色分量的十六进制值 + * @param c + * @see https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + */ + private _componentToHex; + /** + *返回颜色的十六进制表示 + */ + toHex(): string; + /** + * 从十六进制字符串设置颜色 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + fromHex(hex: string): void; + /** + * 返回颜色的 RGBA 表示 + */ + toRGBA(): string; + /** + * 返回颜色的 HSLA 表示 + */ + toHSLA(): string; + /** + * 返回颜色的 CSS 字符串表示形式 + */ + fillStyle(): string; + /** + * 返回当前颜色的克隆 + */ + clone(): Color; + /** + * Black (#000000) + */ + static Black: Color; + /** + * White (#FFFFFF) + */ + static White: Color; + /** + * Gray (#808080) + */ + static Gray: Color; + /** + * Light gray (#D3D3D3) + */ + static LightGray: Color; + /** + * Dark gray (#A9A9A9) + */ + static DarkGray: Color; + /** + * Yellow (#FFFF00) + */ + static Yellow: Color; + /** + * Orange (#FFA500) + */ + static Orange: Color; + /** + * Red (#FF0000) + */ + static Red: Color; + /** + * Vermillion (#FF5B31) + */ + static Vermillion: Color; + /** + * Rose (#FF007F) + */ + static Rose: Color; + /** + * Magenta (#FF00FF) + */ + static Magenta: Color; + /** + * Violet (#7F00FF) + */ + static Violet: Color; + /** + * Blue (#0000FF) + */ + static Blue: Color; + /** + * Azure (#007FFF) + */ + static Azure: Color; + /** + * Cyan (#00FFFF) + */ + static Cyan: Color; + /** + * Viridian (#59978F) + */ + static Viridian: Color; + /** + * Green (#00FF00) + */ + static Green: Color; + /** + * Chartreuse (#7FFF00) + */ + static Chartreuse: Color; + /** + * Transparent (#FFFFFF00) + */ + static Transparent: Color; + } +} +declare module es { + interface IBatcher { + begin(cam: ICamera): any; + end(): any; + drawPoints(points: Vector2[], color: Color, thickness?: number): any; + drawPolygon(poisition: Vector2, points: Vector2[], color: Color, closePoly: boolean, thickness?: number): any; + drawHollowRect(x: number, y: number, width: number, height: number, color: Color, thickness?: number): any; + drawCircle(position: Vector2, raidus: number, color: Color, thickness?: number): any; + drawCircleLow(position: es.Vector2, radius: number, color: Color, thickness?: number, resolution?: number): any; + drawRect(x: number, y: number, width: number, height: number, color: Color): any; + drawLine(start: Vector2, end: Vector2, color: Color, thickness: number): any; + drawPixel(position: Vector2, color: Color, size?: number): any; + } +} +declare module es { + interface ICamera extends Component { + bounds: Rectangle; + } +} +declare module es { + abstract class Renderer { + camera: ICamera; + readonly renderOrder: number; + shouldDebugRender: boolean; + protected renderDirty: boolean; + constructor(renderOrder: number, camera: ICamera); + onAddedToScene(scene: es.Scene): void; + unload(): void; + protected beginRender(cam: ICamera): void; + protected endRender(): void; + protected onRenderChanged(): void; + abstract render(scene: Scene): void; + protected renderAfterStateCheck(renderable: IRenderable, cam: ICamera): void; + protected debugRender(scene: Scene): void; + } +} +declare module es { + class DefaultRenderer extends Renderer { + constructor(renderOrder?: number, camera?: ICamera); + render(scene: Scene): void; + } +} +declare module es { + /** + * 三次方和二次方贝塞尔帮助器(cubic and quadratic bezier helper) + */ + class Bezier { + /** + * 求解二次曲折线 + * @param p0 + * @param p1 + * @param p2 + * @param t + */ + static getPoint(p0: Vector2, p1: Vector2, p2: Vector2, t: number): Vector2; + /** + * 求解一个立方体曲率 + * @param start + * @param firstControlPoint + * @param secondControlPoint + * @param end + * @param t + */ + static getPointThree(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2, end: Vector2, t: number): Vector2; + /** + * 得到二次贝塞尔函数的一阶导数 + * @param p0 + * @param p1 + * @param p2 + * @param t + */ + static getFirstDerivative(p0: Vector2, p1: Vector2, p2: Vector2, t: number): Vector2; + /** + * 得到一个三次贝塞尔函数的一阶导数 + * @param start + * @param firstControlPoint + * @param secondControlPoint + * @param end + * @param t + */ + static getFirstDerivativeThree(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2, end: Vector2, t: number): Vector2; + /** + * 递归地细分bezier曲线,直到满足距离校正 + * 在这种算法中,平面切片的点要比曲面切片少。返回完成后应返回到ListPool的合并列表。 + * @param start + * @param firstCtrlPoint + * @param secondCtrlPoint + * @param end + * @param distanceTolerance + */ + static getOptimizedDrawingPoints(start: Vector2, firstCtrlPoint: Vector2, secondCtrlPoint: Vector2, end: Vector2, distanceTolerance?: number): Vector2[]; + /** + * 递归地细分bezier曲线,直到满足距离校正。在这种算法中,平面切片的点要比曲面切片少。 + * @param start + * @param firstCtrlPoint + * @param secondCtrlPoint + * @param end + * @param points + * @param distanceTolerance + */ + private static recursiveGetOptimizedDrawingPoints; + } +} +declare module es { + /** + * 提供了一系列立方贝塞尔点,并提供了帮助方法来访问贝塞尔 + */ + class BezierSpline { + _points: Vector2[]; + _curveCount: number; + /** + * 在这个过程中,t被修改为在曲线段的范围内。 + * @param t + */ + pointIndexAtTime(t: number): { + time: number; + range: number; + }; + /** + * 设置一个控制点,考虑到这是否是一个共享点,如果是,则适当调整 + * @param index + * @param point + */ + setControlPoint(index: number, point: Vector2): void; + /** + * 得到时间t的贝塞尔曲线上的点 + * @param t + */ + getPointAtTime(t: number): Vector2; + /** + * 得到贝塞尔在时间t的速度(第一导数) + * @param t + */ + getVelocityAtTime(t: number): Vector2; + /** + * 得到时间t时贝塞尔的方向(归一化第一导数) + * @param t + */ + getDirectionAtTime(t: number): Vector2; + /** + * 在贝塞尔曲线上添加一条曲线 + * @param start + * @param firstControlPoint + * @param secondControlPoint + * @param end + */ + addCurve(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2, end: Vector2): void; + /** + * 重置bezier,移除所有点 + */ + reset(): void; + /** + * 将splitine分解成totalSegments部分,并返回使用线条绘制所需的所有点 + * @param totalSegments + */ + getDrawingPoints(totalSegments: number): Vector2[]; + } +} +declare module es { + /** + * 帮助处理位掩码的实用程序类 + * 除了isFlagSet之外,所有方法都期望flag参数是一个非移位的标志 + * 允许您使用普通的(0、1、2、3等)来设置/取消您的标记 + */ + class Flags { + /** + * 检查位标志是否已在数值中设置 + * 检查期望标志是否已经移位 + * @param self + * @param flag + */ + static isFlagSet(self: number, flag: number): boolean; + /** + * 检查位标志是否在数值中设置 + * @param self + * @param flag + */ + static isUnshiftedFlagSet(self: number, flag: number): boolean; + /** + * 设置数值标志位,移除所有已经设置的标志 + * @param self + * @param flag + */ + static setFlagExclusive(self: Ref, flag: number): void; + /** + * 设置标志位 + * @param self + * @param flag + */ + static setFlag(self: Ref, flag: number): void; + /** + * 取消标志位 + * @param self + * @param flag + */ + static unsetFlag(self: Ref, flag: number): void; + /** + * 反转数值集合位 + * @param self + */ + static invertFlags(self: Ref): void; + } +} +declare module es { + class MathHelper { + static readonly Epsilon: number; + static readonly Rad2Deg: number; + static readonly Deg2Rad: number; + /** + * 表示pi除以2的值(1.57079637) + */ + static readonly PiOver2: number; + /** + * 将弧度转换成角度。 + * @param radians 用弧度表示的角 + */ + static toDegrees(radians: number): number; + /** + * 将角度转换为弧度 + * @param degrees + */ + static toRadians(degrees: number): number; + /** + * 返回由给定三角形和两个归一化重心(面积)坐标定义的点的一个轴的笛卡尔坐标 + * @param value1 + * @param value2 + * @param value3 + * @param amount1 + * @param amount2 + */ + static barycentric(value1: number, value2: number, value3: number, amount1: number, amount2: number): number; + /** + * 使用指定位置执行Catmull-Rom插值 + * @param value1 + * @param value2 + * @param value3 + * @param value4 + * @param amount + */ + static catmullRom(value1: number, value2: number, value3: number, value4: number, amount: number): number; + /** + * 将值(在leftMin-leftMax范围内)映射到一个在rightMin-rightMax范围内的值 + * @param value + * @param leftMin + * @param leftMax + * @param rightMin + * @param rightMax + */ + static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number): number; + /** + * 将值从任意范围映射到0到1范围 + * @param value + * @param min + * @param max + * @returns + */ + static map01(value: number, min: number, max: number): number; + /** + * 将值从某个任意范围映射到1到0范围 + * 这相当于map01的取反 + * @param value + * @param min + * @param max + * @returns + */ + static map10(value: number, min: number, max: number): number; + /** + * 使用三次方程在两个值之间进行插值 + * @param value1 + * @param value2 + * @param amount + */ + static smoothStep(value1: number, value2: number, amount: number): number; + /** + * 将给定角度减小到π到-π之间的值 + * @param angle + */ + static wrapAngle(angle: number): number; + /** + * 确定值是否以2为底 + * @param value + * @returns + */ + static isPowerOfTwo(value: number): boolean; + static lerp(from: number, to: number, t: number): number; + static betterLerp(a: number, b: number, t: number, epsilon: number): number; + /** + * 使度数的角度在a和b之间 + * 用于处理360度环绕 + * @param a + * @param b + * @param t + * @returns + */ + static lerpAngle(a: number, b: number, t: number): number; + /** + * 使弧度的角度在a和b之间 + * @param a + * @param b + * @param t + * @returns + */ + static lerpAngleRadians(a: number, b: number, t: number): number; + /** + * 循环t使其不大于长度且不小于0 + * @param t + * @param length + * @returns + */ + static pingPong(t: number, length: number): number; + /** + * 如果value> = threshold返回其符号,否则返回0 + * @param value + * @param threshold + * @returns + */ + static signThreshold(value: number, threshold: number): number; + static inverseLerp(from: number, to: number, t: number): number; + /** + * 在两个值之间线性插值 + * 此方法是MathHelper.Lerp的效率较低,更精确的版本。 + */ + static lerpPrecise(value1: number, value2: number, amount: number): number; + static clamp(value: number, min: number, max: number): number; + static snap(value: number, increment: number): number; + /** + * 给定圆心、半径和角度,得到圆周上的一个点。0度是3点钟。 + * @param circleCenter + * @param radius + * @param angleInDegrees + */ + static pointOnCirlce(circleCenter: Vector2, radius: number, angleInDegrees: number): Vector2; + /** + * 如果值为偶数,返回true + * @param value + */ + static isEven(value: number): boolean; + /** + * 如果值是奇数,则返回true + * @param value + * @returns + */ + static isOdd(value: number): boolean; + /** + * 将值四舍五入并返回它和四舍五入后的数值 + * @param value + * @param roundedAmount + * @returns + */ + static roundWithRoundedAmount(value: number, roundedAmount: Ref): number; + /** + * 数值限定在0-1之间 + * @param value + */ + static clamp01(value: number): number; + static angleBetweenVectors(from: Vector2, to: Vector2): number; + static angleToVector(angleRadians: number, length: number): Vector2; + /** + * 增加t并确保它总是大于或等于0并且小于长度 + * @param t + * @param length + */ + static incrementWithWrap(t: number, length: number): number; + /** + * 递减t并确保其始终大于或等于0且小于长度 + * @param t + * @param length + * @returns + */ + static decrementWithWrap(t: number, length: number): number; + /** + * 返回sqrt(x * x + y * y) + * @param x + * @param y + * @returns + */ + static hypotenuse(x: number, y: number): number; + static closestPowerOfTwoGreaterThan(x: number): number; + /** + * 以roundToNearest为步长,将值舍入到最接近的数字。例如:在125中找到127到最近的5个结果 + * @param value + * @param roundToNearest + */ + static roundToNearest(value: number, roundToNearest: number): number; + /** + * 检查传递的值是否在某个阈值之下。对于小规模、精确的比较很有用 + * @param value + * @param ep + */ + static withinEpsilon(value: number, ep?: number): boolean; + /** + * 由上移量向上移。start可以小于或大于end。例如:开始是2,结束是10,移位是4,结果是6 + * @param start + * @param end + * @param shift + */ + static approach(start: number, end: number, shift: number): number; + /** + * 通过偏移量钳位结果并选择最短路径,将起始角度向终止角度移动,起始值可以小于或大于终止值。 + * 示例1:开始是30,结束是100,移位是25,结果为55 + * 示例2:开始是340,结束是30,移位是25,结果是5(365换为5) + * @param start + * @param end + * @param shift + * @returns + */ + static approachAngle(start: number, end: number, shift: number): number; + /** + * 将 Vector 投影到另一个 Vector 上 + * @param other + */ + static project(self: Vector2, other: Vector2): Vector2; + /** + * 通过将偏移量(全部以弧度为单位)夹住结果并选择最短路径,起始角度朝向终止角度。 + * 起始值可以小于或大于终止值。 + * 此方法的工作方式与“角度”方法非常相似,唯一的区别是使用弧度代替度,并以2 * Pi代替360。 + * @param start + * @param end + * @param shift + * @returns + */ + static approachAngleRadians(start: number, end: number, shift: number): number; + /** + * 使用可接受的检查公差检查两个值是否大致相同 + * @param value1 + * @param value2 + * @param tolerance + * @returns + */ + static approximately(value1: number, value2: number, tolerance?: number): boolean; + /** + * 计算两个给定角之间的最短差值(度数) + * @param current + * @param target + */ + static deltaAngle(current: number, target: number): number; + /** + * 检查值是否介于最小值/最大值(包括最小值/最大值)之间 + * @param value + * @param min + * @param max + * @returns + */ + static between(value: number, min: number, max: number): boolean; + /** + * 计算以弧度为单位的两个给定角度之间的最短差 + * @param current + * @param target + * @returns + */ + static deltaAngleRadians(current: number, target: number): number; + /** + * 循环t,使其永远不大于长度,永远不小于0 + * @param t + * @param length + */ + static repeat(t: number, length: number): number; + static floorToInt(f: number): number; + /** + * 将值绕一圈移动的助手 + * @param position + * @param speed + * @returns + */ + static rotateAround(position: Vector2, speed: number): Vector2; + /** + * 旋转是相对于当前位置而不是总旋转。 + * 例如,如果您当前处于90度并且想要旋转到135度,则可以使用45度而不是135度的角度 + * @param point + * @param center + * @param angleIndegrees + */ + static rotateAround2(point: Vector2, center: Vector2, angleIndegrees: number): Vector2; + /** + * 根据圆的中心,半径和角度在圆的圆周上得到一个点。 0度是3点钟方向 + * @param circleCenter + * @param radius + * @param angleInDegrees + */ + static pointOnCircle(circleCenter: Vector2, radius: number, angleInDegrees: number): Vector2; + /** + * 根据圆的中心,半径和角度在圆的圆周上得到一个点。 0弧度是3点钟方向 + * @param circleCenter + * @param radius + * @param angleInRadians + * @returns + */ + static pointOnCircleRadians(circleCenter: Vector2, radius: number, angleInRadians: number): Vector2; + /** + * lissajou曲线 + * @param xFrequency + * @param yFrequency + * @param xMagnitude + * @param yMagnitude + * @param phase + * @returns + */ + static lissajou(xFrequency?: number, yFrequency?: number, xMagnitude?: number, yMagnitude?: number, phase?: number): Vector2; + /** + * lissajou曲线的阻尼形式,其振荡随时间在0和最大幅度之间。 + * 为获得最佳效果,阻尼应在0到1之间。 + * 振荡间隔是动画循环的一半完成的时间(以秒为单位)。 + * @param xFrequency + * @param yFrequency + * @param xMagnitude + * @param yMagnitude + * @param phase + * @param damping + * @param oscillationInterval + * @returns + */ + static lissajouDamped(xFrequency?: number, yFrequency?: number, xMagnitude?: number, yMagnitude?: number, phase?: number, damping?: number, oscillationInterval?: number): Vector2; + /** + * 执行Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + static hermite(value1: number, tangent1: number, value2: number, tangent2: number, amount: number): any; + /** + * 此函数用于确保数不是NaN或无穷大 + * @param x + * @returns + */ + static isValid(x: number): boolean; + static smoothDamp(current: number, target: number, currentVelocity: number, smoothTime: number, maxSpeed: number, deltaTime: number): { + value: number; + currentVelocity: number; + }; + static smoothDampVector(current: Vector2, target: Vector2, currentVelocity: Vector2, smoothTime: number, maxSpeed: number, deltaTime: number): Vector2; + /** + * 将值(在 leftMin - leftMax 范围内)映射到 rightMin - rightMax 范围内的值 + * @param value + * @param leftMin + * @param leftMax + * @param rightMin + * @param rightMax + * @returns + */ + static mapMinMax(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: any): number; + static fromAngle(angle: number): Vector2; + } +} +declare module es { + /** + * 代表右手4x4浮点矩阵,可以存储平移、比例和旋转信息 + */ + class Matrix { + private static identity; + static readonly Identity: Matrix; + m11: number; + m12: number; + m13: number; + m14: number; + m21: number; + m22: number; + m23: number; + m24: number; + m31: number; + m32: number; + m33: number; + m34: number; + m41: number; + m42: number; + m43: number; + m44: number; + constructor(m11?: any, m12?: any, m13?: any, m14?: any, m21?: any, m22?: any, m23?: any, m24?: any, m31?: any, m32?: any, m33?: any, m34?: any, m41?: any, m42?: any, m43?: any, m44?: any); + /** + * 为自定义的正交视图创建一个新的投影矩阵 + * @param left + * @param right + * @param top + * @param zFarPlane + * @param result + */ + static createOrthographicOffCenter(left: number, right: number, bottom: number, top: number, zNearPlane: number, zFarPlane: number, result?: Matrix): void; + static createTranslation(position: Vector2, result: Matrix): void; + static createRotationZ(radians: number, result: Matrix): void; + /** + * 创建一个新的矩阵,其中包含两个矩阵的乘法。 + * @param matrix1 + * @param matrix2 + * @param result + */ + static multiply(matrix1: Matrix, matrix2: Matrix, result?: Matrix): void; + } +} +declare module es { + /** + * 表示右手3 * 3的浮点矩阵,可以存储平移、缩放和旋转信息。 + */ + class Matrix2D implements IEquatable { + m11: number; + m12: number; + m21: number; + m22: number; + m31: number; + m32: number; + /** + * 返回标识矩阵 + */ + static readonly identity: Matrix2D; + setIdentity(): Matrix2D; + setValues(m11: number, m12: number, m21: number, m22: number, m31: number, m32: number): Matrix2D; + /** + * 储存在该矩阵中的位置 + */ + translation: Vector2; + /** + * 以弧度为单位的旋转,存储在这个矩阵中 + */ + rotation: number; + /** + * 矩阵中存储的旋转度数 + */ + rotationDegrees: number; + /** + * 储存在这个矩阵中的缩放 + */ + scale: Vector2; + /** + * 创建一个新的围绕Z轴的旋转矩阵2D + * @param radians + */ + static createRotation(radians: number, result: Matrix2D): void; + static createRotationOut(radians: number, result: Matrix2D): void; + /** + * 创建一个新的缩放矩阵2D + * @param xScale + * @param yScale + */ + static createScale(xScale: number, yScale: number, result: Matrix2D): void; + static createScaleOut(xScale: number, yScale: number, result: Matrix2D): void; + /** + * 创建一个新的平移矩阵2D + * @param xPosition + * @param yPosition + */ + static createTranslation(xPosition: number, yPosition: number, result: Matrix2D): Matrix2D; + static createTranslationOut(position: Vector2, result: Matrix2D): void; + static invert(matrix: Matrix2D): Matrix2D; + /** + * 创建一个新的matrix, 它包含两个矩阵的和。 + * @param matrix + */ + add(matrix: Matrix2D): Matrix2D; + substract(matrix: Matrix2D): Matrix2D; + divide(matrix: Matrix2D): Matrix2D; + multiply(matrix: Matrix2D): Matrix2D; + static multiply(matrix1: Matrix2D, matrix2: Matrix2D, result: Matrix2D): void; + determinant(): number; + /** + * 创建一个新的Matrix2D,包含指定矩阵中的线性插值。 + * @param matrix1 + * @param matrix2 + * @param amount + */ + static lerp(matrix1: Matrix2D, matrix2: Matrix2D, amount: number): Matrix2D; + /** + * 交换矩阵的行和列 + * @param matrix + */ + static transpose(matrix: Matrix2D): Matrix2D; + mutiplyTranslation(x: number, y: number): Matrix2D; + /** + * 比较当前实例是否等于指定的Matrix2D + * @param other + */ + equals(other: Matrix2D): boolean; + static toMatrix(mat: Matrix2D): Matrix; + toString(): string; + } +} +declare module es { + class MatrixHelper { + /** + * 创建一个新的Matrix2D,其中包含两个矩阵的和 + * @param matrix1 + * @param matrix2 + */ + static add(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D; + /** + * 将一个Matrix2D的元素除以另一个矩阵的元素 + * @param matrix1 + * @param matrix2 + */ + static divide(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D; + /** + * 创建一个新的Matrix2D,包含两个矩阵的乘法 + * @param matrix1 + * @param matrix2 + */ + static mutiply(matrix1: Matrix2D, matrix2: Matrix2D | number): Matrix2D; + /** + * 创建一个新的Matrix2D,包含一个矩阵与另一个矩阵的减法。 + * @param matrix1 + * @param matrix2 + */ + static subtract(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D; + } +} +declare module es { + class Rectangle implements IEquatable { + /** + * 该矩形的左上角的x坐标 + */ + x: number; + /** + * 该矩形的左上角的y坐标 + */ + y: number; + /** + * 该矩形的宽度 + */ + width: number; + /** + * 该矩形的高度 + */ + height: number; + /** + * 返回X=0, Y=0, Width=0, Height=0的矩形 + */ + static readonly empty: Rectangle; + /** + * 返回一个Number.Min/Max值的矩形 + */ + static readonly maxRect: Rectangle; + /** + * 返回此矩形左边缘的X坐标 + */ + readonly left: number; + /** + * 返回此矩形右边缘的X坐标 + */ + readonly right: number; + /** + * 返回此矩形顶边的y坐标 + */ + readonly top: number; + /** + * 返回此矩形底边的y坐标 + */ + readonly bottom: number; + /** + * 获取矩形的最大点,即右下角 + */ + readonly max: Vector2; + /** + * 这个矩形的宽和高是否为0,位置是否为(0,0) + */ + isEmpty(): boolean; + /** 这个矩形的左上角坐标 */ + location: Vector2; + /** + * 这个矩形的宽-高坐标 + */ + size: Vector2; + /** + * 位于这个矩形中心的一个点 + * 如果 "宽度 "或 "高度 "是奇数,则中心点将向下舍入 + */ + readonly center: Vector2; + _tempMat: Matrix2D; + _transformMat: Matrix2D; + /** + * 创建一个新的Rectanglestruct实例,指定位置、宽度和高度。 + * @param x 创建的矩形的左上角的X坐标 + * @param y 创建的矩形的左上角的y坐标 + * @param width 创建的矩形的宽度 + * @param height 创建的矩形的高度 + */ + constructor(x?: number, y?: number, width?: number, height?: number); + /** + * 创建一个给定最小/最大点(左上角,右下角)的矩形 + * @param minX + * @param minY + * @param maxX + * @param maxY + */ + static fromMinMax(minX: number, minY: number, maxX: number, maxY: number): Rectangle; + /** + * 给定多边形的点,计算边界 + * @param points + * @returns 来自多边形的点 + */ + static rectEncompassingPoints(points: Vector2[]): Rectangle; + /** + * 获取指定边缘的位置 + * @param edge + */ + getSide(edge: Edge): number; + /** + * 获取所提供的坐标是否在这个矩形的范围内 + * @param x 检查封堵点的X坐标 + * @param y 检查封堵点的Y坐标 + */ + contains(x: number, y: number): boolean; + /** + * 按指定的水平和垂直方向调整此矩形的边缘 + * @param horizontalAmount 调整左、右边缘的值 + * @param verticalAmount 调整上、下边缘的值 + */ + inflate(horizontalAmount: number, verticalAmount: number): void; + /** + * 获取其他矩形是否与这个矩形相交 + * @param value 另一个用于测试的矩形 + */ + intersects(value: Rectangle): boolean; + rayIntersects(ray: Ray2D): { + intersected: boolean; + distance: number; + }; + /** + * 获取所提供的矩形是否在此矩形的边界内 + * @param value + */ + containsRect(value: Rectangle): boolean; + getHalfSize(): Vector2; + getClosestPointOnBoundsToOrigin(): Vector2; + /** + * 返回离给定点最近的点 + * @param point 矩形上离点最近的点 + */ + getClosestPointOnRectangleToPoint(point: Vector2): Vector2; + /** + * 获取矩形边界上与给定点最近的点 + * @param point + * @param edgeNormal + * @returns 矩形边框上离点最近的点 + */ + getClosestPointOnRectangleBorderToPoint(point: Vector2, edgeNormal: Vector2): Vector2; + /** + * 创建一个新的RectangleF,该RectangleF包含两个其他矩形的重叠区域 + * @param value1 + * @param value2 + * @returns 将两个矩形的重叠区域作为输出参数 + */ + static intersect(value1: Rectangle, value2: Rectangle): Rectangle; + /** + * 改变这个矩形的位置 + * @param offsetX 要添加到这个矩形的X坐标 + * @param offsetY 要添加到这个矩形的y坐标 + */ + offset(offsetX: number, offsetY: number): void; + /** + * 创建一个完全包含两个其他矩形的新矩形 + * @param value1 + * @param value2 + */ + static union(value1: Rectangle, value2: Rectangle): Rectangle; + /** + * 在矩形重叠的地方创建一个新的矩形 + * @param value1 + * @param value2 + */ + static overlap(value1: Rectangle, value2: Rectangle): Rectangle; + calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number): void; + /** + * 返回一个横跨当前矩形和提供的三角形位置的矩形 + * @param deltaX + * @param deltaY + */ + getSweptBroadphaseBounds(deltaX: number, deltaY: number): Rectangle; + /** + * 如果发生碰撞,返回true + * moveX和moveY将返回b1为避免碰撞而必须移动的移动量 + * @param other + * @param moveX + * @param moveY + */ + collisionCheck(other: Rectangle, moveX: Ref, moveY: Ref): boolean; + /** + * 计算两个矩形之间有符号的交点深度 + * @param rectA + * @param rectB + * @returns 两个相交的矩形之间的重叠量。 + * 这些深度值可以是负值,取决于矩形/相交的哪些边。 + * 这允许调用者确定正确的推送对象的方向,以解决碰撞问题。 + * 如果矩形不相交,则返回Vector2.Zero + */ + static getIntersectionDepth(rectA: Rectangle, rectB: Rectangle): Vector2; + /** + * 比较当前实例是否等于指定的矩形 + * @param other + */ + equals(other: Rectangle): boolean; + /** + * 获取这个矩形的哈希码 + */ + getHashCode(): number; + clone(): Rectangle; + } +} +declare module es { + /** + * 它存储值,直到累计的总数大于1。一旦超过1,该值将在调用update时添加到amount中 + * 一般用法如下: + * + * let deltaMove = this.velocity * es.Time.deltaTime; + * deltaMove.x = this._x.update(deltaMove.x); + * deltaMove.y = this._y.update(deltaMove.y); + */ + class SubpixelFloat { + remainder: number; + /** + * 以amount递增余数,将值截断,存储新的余数并将amount设置为当前值 + * @param amount + */ + update(amount: number): number; + /** + * 将余数重置为0 + */ + reset(): void; + } +} +declare module es { + class SubpixelVector2 { + _x: SubpixelFloat; + _y: SubpixelFloat; + /** + * 以数量递增s/y余数,将值截断为整数,存储新的余数并将amount设置为当前值 + * @param amount + */ + update(amount: Vector2): void; + /** + * 将余数重置为0 + */ + reset(): void; + } +} +declare module es { + /** + * 移动器使用的帮助器类,用于管理触发器碰撞器交互并调用itriggerlistener + */ + class ColliderTriggerHelper { + private _entity; + /** 存储当前帧中发生的所有活动交点对 */ + private _activeTriggerIntersections; + /** 存储前一帧的交点对,这样我们就可以在移动这一帧后检测到退出 */ + private _previousTriggerIntersections; + private _tempTriggerList; + constructor(entity: Entity); + /** + * update应该在实体被移动后被调用,它将处理任何与Colllider重叠的ITriggerListeners。 + * 它将处理任何与Collider重叠的ITriggerListeners。 + */ + update(): void; + private checkForExitedColliders; + private notifyTriggerListeners; + } +} +declare module es { + enum PointSectors { + center = 0, + top = 1, + bottom = 2, + topLeft = 9, + topRight = 5, + left = 8, + right = 4, + bottomLeft = 10, + bottomRight = 6 + } + class Collisions { + static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): boolean; + static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection?: Vector2): boolean; + static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2; + static circleToCircle(circleCenter1: Vector2, circleRadius1: number, circleCenter2: Vector2, circleRadius2: number): boolean; + static circleToLine(circleCenter: Vector2, radius: number, lineFrom: Vector2, lineTo: Vector2): boolean; + static circleToPoint(circleCenter: Vector2, radius: number, point: Vector2): boolean; + static rectToCircle(rect: Rectangle, cPosition: Vector2, cRadius: number): boolean; + static rectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2): boolean; + static rectToPoint(rX: number, rY: number, rW: number, rH: number, point: Vector2): boolean; + /** + * 位标志和帮助使用Cohen–Sutherland算法 + * + * 位标志: + * 1001 1000 1010 + * 0001 0000 0010 + * 0101 0100 0110 + * @param rX + * @param rY + * @param rW + * @param rH + * @param point + */ + static getSector(rX: number, rY: number, rW: number, rH: number, point: Vector2): PointSectors; + } +} +declare module es { + class RaycastHit { + /** + * 对撞机被射线击中 + */ + collider: Collider; + /** + * 撞击发生时沿射线的距离。 + */ + fraction: number; + /** + * 从射线原点到碰撞点的距离 + */ + distance: number; + /** + * 世界空间中光线击中对撞机表面的点 + */ + point: Vector2; + /** + * 被射线击中的表面的法向量 + */ + normal: Vector2; + /** + * 用于执行转换的质心。使其接触的形状的位置。 + */ + centroid: Vector2; + constructor(collider?: Collider, fraction?: number, distance?: number, point?: Vector2, normal?: Vector2); + setAllValues(collider: Collider, fraction: number, distance: number, point: Vector2, normal: Vector2): void; + setValues(fraction: number, distance: number, point: Vector2, normal: Vector2): void; + reset(): void; + clone(): RaycastHit; + toString(): string; + } +} +declare module es { + class Physics { + static _spatialHash: SpatialHash; + /** 用于在全局范围内存储重力值的方便字段 */ + static gravity: Vector2; + /** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */ + static spatialHashCellSize: number; + /** 接受layerMask的所有方法的默认值 */ + static readonly allLayers: number; + /** + * raycast是否检测配置为触发器的碰撞器 + */ + static raycastsHitTriggers: boolean; + /** + * 在碰撞器中开始的射线/直线是否强制转换检测到那些碰撞器 + */ + static raycastsStartInColliders: boolean; + static debugRender: boolean; + /** + * 我们保留它以避免在每次raycast发生时分配它 + */ + static _hitArray: RaycastHit[]; + /** + * 避免重叠检查和形状投射的分配 + */ + static _colliderArray: Collider[]; + static reset(): void; + /** + * 从SpatialHash中移除所有碰撞器 + */ + static clear(): void; + static debugDraw(secondsToDisplay: any): void; + /** + * 检查是否有对撞机落在一个圆形区域内。返回遇到的第一个对撞机 + * @param center + * @param radius + * @param layerMask + */ + static overlapCircle(center: Vector2, radius: number, layerMask?: number): Collider; + /** + * 获取所有落在指定圆圈内的碰撞器 + * @param center + * @param randius + * @param results + * @param layerMask + */ + static overlapCircleAll(center: Vector2, radius: number, results: Collider[], layerMask?: number): number; + /** + * 返回所有碰撞器与边界相交的碰撞器。bounds。请注意,这是一个broadphase检查,所以它只检查边界,不做单个碰撞到碰撞器的检查! + * @param rect + * @param layerMask + */ + static boxcastBroadphase(rect: Rectangle, layerMask?: number): Collider[]; + /** + * 返回所有被边界交错的碰撞器,但不包括传入的碰撞器(self)。 + * 如果你想为其他查询自己创建扫描边界,这个方法很有用 + * @param collider + * @param rect + * @param layerMask + */ + static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask?: number): Collider[]; + /** + * 返回所有边界与 collider.bounds 相交的碰撞器,但不包括传入的碰撞器(self) + * @param collider + * @param layerMask + */ + static boxcastBroadphaseExcludingSelfNonRect(collider: Collider, layerMask?: number): Collider[]; + /** + * 返回所有被 collider.bounds 扩展为包含 deltaX/deltaY 的碰撞器,但不包括传入的碰撞器(self) + * @param collider + * @param deltaX + * @param deltaY + * @param layerMask + */ + static boxcastBroadphaseExcludingSelfDelta(collider: Collider, deltaX: number, deltaY: number, layerMask?: number): Collider[]; + /** + * 将对撞机添加到物理系统中 + * @param collider + */ + static addCollider(collider: Collider): void; + /** + * 从物理系统中移除对撞机 + * @param collider + */ + static removeCollider(collider: Collider): void; + /** + * 更新物理系统中对撞机的位置。这实际上只是移除然后重新添加带有新边界的碰撞器 + * @param collider + */ + static updateCollider(collider: Collider): void; + /** + * 返回与layerMask匹配的碰撞器的第一次命中 + * @param start + * @param end + * @param layerMask + */ + static linecast(start: Vector2, end: Vector2, layerMask?: number, ignoredColliders?: Set): RaycastHit; + /** + * 通过空间散列强制执行一行,并用该行命中的任何碰撞器填充hits数组 + * @param start + * @param end + * @param hits + * @param layerMask + */ + static linecastAll(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask?: number, ignoredColliders?: Set): number; + /** + * 检查是否有对撞机落在一个矩形区域中 + * @param rect + * @param layerMask + */ + static overlapRectangle(rect: Rectangle, layerMask?: number): Collider; + /** + * 获取所有在指定矩形范围内的碰撞器 + * @param rect + * @param results + * @param layerMask + */ + static overlapRectangleAll(rect: Rectangle, results: Collider[], layerMask?: number): number; + } +} +declare module es { + /** + * 不是真正的射线(射线只有开始和方向),作为一条线和射线。 + */ + class Ray2D { + readonly start: Vector2; + readonly direction: Vector2; + readonly end: Vector2; + constructor(pos: Vector2, end: Vector2); + private _start; + private _direction; + private _end; + } +} +declare module es { + class SpatialHash { + gridBounds: Rectangle; + _raycastParser: RaycastResultParser; + /** + * 散列中每个单元格的大小 + */ + _cellSize: number; + /** + * 1除以单元格大小。缓存结果,因为它被大量使用。 + */ + _inverseCellSize: number; + /** + * 重叠检查缓存框 + */ + _overlapTestBox: Box; + /** + * 重叠检查缓存圈 + */ + _overlapTestCircle: Circle; + /** + * 保存所有数据的字典 + */ + _cellDict: NumberDictionary; + /** + * 用于返回冲突信息的共享HashSet + */ + _tempHashSet: Set; + constructor(cellSize?: number); + /** + * 将对象添加到SpatialHash + * @param collider + */ + register(collider: Collider): void; + /** + * 从SpatialHash中删除对象 + * @param collider + */ + remove(collider: Collider): void; + /** + * 使用蛮力方法从SpatialHash中删除对象 + * @param obj + */ + removeWithBruteForce(obj: Collider): void; + clear(): void; + debugDraw(secondsToDisplay: number): void; + private debugDrawCellDetails; + /** + * 返回边框与单元格相交的所有对象 + * @param bounds + * @param excludeCollider + * @param layerMask + */ + aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Collider[]; + /** + * 通过空间散列投掷一条线,并将该线碰到的任何碰撞器填入碰撞数组 + * https://github.com/francisengelmann/fast_voxel_traversal/blob/master/main.cpp + * http://www.cse.yorku.ca/~amana/research/grid.pdf + * @param start + * @param end + * @param hits + * @param layerMask + */ + linecast(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number, ignoredColliders: Set): number; + /** + * 获取所有在指定矩形范围内的碰撞器 + * @param rect + * @param results + * @param layerMask + */ + overlapRectangle(rect: Rectangle, results: Collider[], layerMask: number): number; + /** + * 获取所有落在指定圆圈内的碰撞器 + * @param circleCenter + * @param radius + * @param results + * @param layerMask + */ + overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask: any): number; + /** + * 获取单元格的x,y值作为世界空间的x,y值 + * @param x + * @param y + */ + cellCoords(x: number, y: number): Vector2; + /** + * 获取世界空间x,y值的单元格。 + * 如果单元格为空且createCellIfEmpty为true,则会创建一个新的单元格 + * @param x + * @param y + * @param createCellIfEmpty + */ + cellAtPosition(x: number, y: number, createCellIfEmpty?: boolean): Collider[]; + } + class NumberDictionary { + _store: Map; + add(x: number, y: number, list: T[]): void; + /** + * 使用蛮力方法从字典存储列表中移除碰撞器 + * @param obj + */ + remove(obj: T): void; + tryGetValue(x: number, y: number): T[]; + getKey(x: number, y: number): string; + /** + * 清除字典数据 + */ + clear(): void; + } + class RaycastResultParser { + hitCounter: number; + static compareRaycastHits: (a: RaycastHit, b: RaycastHit) => number; + _hits: RaycastHit[]; + _tempHit: RaycastHit; + _checkedColliders: Collider[]; + _cellHits: RaycastHit[]; + _ray: Ray2D; + _layerMask: number; + private _ignoredColliders; + start(ray: Ray2D, hits: RaycastHit[], layerMask: number, ignoredColliders: Set): void; + /** + * 如果hits数组被填充,返回true。单元格不能为空! + * @param cellX + * @param cellY + * @param cell + */ + checkRayIntersection(cellX: number, cellY: number, cell: Collider[]): boolean; + reset(): void; + } +} +declare module es { + abstract class Shape { + /** + * 有一个单独的位置字段可以让我们改变形状的位置来进行碰撞检查,而不是改变entity.position。 + * 触发碰撞器/边界/散列更新的位置。 + * 内部字段 + */ + position: Vector2; + /** + * 这不是中心。这个值不一定是物体的中心。对撞机更准确。 + * 应用任何转换旋转的localOffset + * 内部字段 + */ + center: Vector2; + /** 缓存的形状边界 内部字段 */ + bounds: Rectangle; + abstract recalculateBounds(collider: Collider): any; + abstract overlaps(other: Shape): boolean; + abstract collidesWithShape(other: Shape, collisionResult: CollisionResult): boolean; + abstract collidesWithLine(start: Vector2, end: Vector2, hit: RaycastHit): boolean; + abstract containsPoint(point: Vector2): any; + abstract pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean; + } +} +declare module es { + /** + * 多边形 + */ + class Polygon extends Shape { + /** + * 组成多边形的点 + * 保持顺时针与凸边形 + */ + points: Vector2[]; + _areEdgeNormalsDirty: boolean; + /** + * 多边形的原始数据 + */ + _originalPoints: Vector2[]; + _polygonCenter: Vector2; + /** + * 用于优化未旋转box碰撞 + */ + isBox: boolean; + isUnrotated: boolean; + /** + * 从点构造一个多边形 + * 多边形应该以顺时针方式指定 不能重复第一个/最后一个点,它们以0 0为中心 + * @param points + * @param isBox + */ + constructor(points: Vector2[], isBox?: boolean); + create(vertCount: number, radius: number): void; + _edgeNormals: Vector2[]; + /** + * 边缘法线用于SAT碰撞检测。缓存它们用于避免squareRoots + * box只有两个边缘 因为其他两边是平行的 + */ + readonly edgeNormals: Vector2[]; + /** + * 重置点并重新计算中心和边缘法线 + * @param points + */ + setPoints(points: Vector2[]): void; + /** + * 重新计算多边形中心 + * 如果点数改变必须调用该方法 + */ + recalculateCenterAndEdgeNormals(): void; + /** + * 建立多边形边缘法线 + * 它们仅由edgeNormals getter惰性创建和更新 + */ + buildEdgeNormals(): void; + /** + * 建立一个对称的多边形(六边形,八角形,n角形)并返回点 + * @param vertCount + * @param radius + */ + static buildSymmetricalPolygon(vertCount: number, radius: number): any[]; + /** + * 重定位多边形的点 + * @param points + */ + static recenterPolygonVerts(points: Vector2[]): void; + /** + * 找到多边形的中心。注意,这对于正则多边形是准确的。不规则多边形没有中心。 + * @param points + */ + static findPolygonCenter(points: Vector2[]): Vector2; + /** + * 不知道辅助顶点,所以取每个顶点,如果你知道辅助顶点,执行climbing算法 + * @param points + * @param direction + */ + static getFarthestPointInDirection(points: Vector2[], direction: Vector2): Vector2; + /** + * 迭代多边形的所有边,并得到任意边上离点最近的点。 + * 通过最近点的平方距离和它所在的边的法线返回。 + * 点应该在多边形的空间中(点-多边形.位置) + * @param points + * @param point + * @param distanceSquared + * @param edgeNormal + */ + static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { + distanceSquared: number; + edgeNormal: Vector2; + closestPoint: Vector2; + }; + /** + * 旋转原始点并复制旋转的值到旋转的点 + * @param radians + * @param originalPoints + * @param rotatedPoints + */ + static rotatePolygonVerts(radians: number, originalPoints: Vector2[], rotatedPoints: Vector2[]): void; + recalculateBounds(collider: Collider): void; + overlaps(other: Shape): any; + collidesWithShape(other: Shape, result: CollisionResult): boolean; + collidesWithLine(start: es.Vector2, end: es.Vector2, hit: es.RaycastHit): boolean; + /** + * 本质上,这个算法所做的就是从一个点发射一条射线。 + * 如果它与奇数条多边形边相交,我们就知道它在多边形内部。 + * @param point + */ + containsPoint(point: Vector2): boolean; + pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean; + } +} +declare module es { + /** + * 多边形的特殊情况。在进行SAT碰撞检查时,我们只需要检查2个轴而不是8个轴 + */ + class Box extends Polygon { + width: number; + height: number; + constructor(width: number, height: number); + /** + * 在一个盒子的形状中建立多边形需要的点的帮助方法 + * @param width + * @param height + */ + private static buildBox; + /** + * 更新框点,重新计算中心,设置宽度/高度 + * @param width + * @param height + */ + updateBox(width: number, height: number): void; + overlaps(other: Shape): any; + collidesWithShape(other: Shape, result: CollisionResult): boolean; + containsPoint(point: Vector2): boolean; + pointCollidesWithShape(point: es.Vector2, result: es.CollisionResult): boolean; + } +} +declare module es { + class Circle extends Shape { + radius: number; + _originalRadius: number; + constructor(radius: number); + recalculateBounds(collider: Collider): void; + overlaps(other: Shape): any; + collidesWithShape(other: Shape, result: CollisionResult): boolean; + collidesWithLine(start: Vector2, end: Vector2, hit: RaycastHit): boolean; + getPointAlongEdge(angle: number): Vector2; + /** + * 获取所提供的点是否在此范围内 + * @param point + */ + containsPoint(point: Vector2): boolean; + pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean; + } +} +declare module es { + class CollisionResult { + /** + * 与之相撞的对撞机 + */ + collider: Collider; + /** + * 被形状击中的表面的法向量 + */ + normal: Vector2; + /** + * 应用于第一个形状以推入形状的转换 + */ + minimumTranslationVector: Vector2; + /** + * 不是所有冲突类型都使用!在依赖这个字段之前,请检查ShapeCollisions切割类! + */ + point: Vector2; + reset(): void; + cloneTo(cr: CollisionResult): void; + /** + * 改变最小平移向量,如果没有相同方向上的运动,它将移除平移的x分量。 + * @param deltaMovement + */ + removeHorizontalTranslation(deltaMovement: Vector2): void; + invertResult(): void; + toString(): string; + } +} +declare module es { + class RealtimeCollisions { + static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: number): boolean; + /** + * 支持函数,返回索引为n的矩形vert + * @param b + * @param n + */ + static corner(b: Rectangle, n: number): Vector2; + /** + * 检查圆是否与方框重叠,并返回point交点 + * @param cirlce + * @param box + * @param point + */ + static testCircleBox(cirlce: Circle, box: Box, point: Vector2): boolean; + } +} +declare module es { + class ShapeCollisionsBox { + static boxToBox(first: Box, second: Box, result: CollisionResult): boolean; + /** + * 用second检查被deltaMovement移动的框的结果 + * @param first + * @param second + * @param movement + * @param hit + */ + static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean; + private static minkowskiDifference; + } +} +declare module es { + class ShapeCollisionsCircle { + static circleToCircleCast(first: Circle, second: Circle, deltaMovement: Vector2, hit: RaycastHit): boolean; + static circleToCircle(first: Circle, second: Circle, result?: CollisionResult): boolean; + /** + * 适用于中心在框内的圆,也适用于与框外中心重合的圆。 + * @param circle + * @param box + * @param result + */ + static circleToBox(circle: Circle, box: Box, result?: CollisionResult): boolean; + static circleToPolygon(circle: Circle, polygon: Polygon, result?: CollisionResult): boolean; + static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2; + } +} +declare module es { + class ShapeCollisionsLine { + static lineToPoly(start: Vector2, end: Vector2, polygon: Polygon, hit?: RaycastHit): boolean; + static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2): boolean; + static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean; + } +} +declare module es { + class ShapeCollisionsPoint { + static pointToCircle(point: Vector2, circle: Circle, result: CollisionResult): boolean; + static pointToBox(point: Vector2, box: Box, result?: CollisionResult): boolean; + static pointToPoly(point: Vector2, poly: Polygon, result?: CollisionResult): boolean; + } +} +declare module es { + class ShapeCollisionsPolygon { + /** + * 检查两个多边形之间的碰撞 + * @param first + * @param second + * @param result + */ + static polygonToPolygon(first: Polygon, second: Polygon, result: CollisionResult): boolean; + /** + * 计算一个多边形在一个轴上的投影,并返回一个[min,max]区间 + * @param axis + * @param polygon + * @param min + * @param max + */ + static getInterval(axis: Vector2, polygon: Polygon): { + min: number; + max: number; + }; + /** + * 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的 + * @param minA + * @param maxA + * @param minB + * @param maxB + */ + static intervalDistance(minA: number, maxA: number, minB: number, maxB: number): number; + } +} +declare module es { + class Particle { + position: Vector2; + lastPosition: Vector2; + mass: number; + radius: number; + collidesWithColliders: boolean; + isPinned: boolean; + acceleration: Vector2; + pinnedPosition: Vector2; + constructor(position: { + x: number; + y: number; + }); + applyForce(force: Vector2): void; + pin(): Particle; + pinTo(position: Vector2): Particle; + unpin(): Particle; + } +} +declare module es { + class VerletWorld { + gravity: Vector2; + constraintIterations: number; + maximumStepIterations: number; + simulationBounds: Rectangle; + allowDragging: boolean; + selectionRadiusSquared: number; + _draggedParticle: Particle; + _composites: Composite[]; + static _colliders: Collider[]; + _tempCircle: Circle; + _leftOverTime: number; + _fixedDeltaTime: number; + _iterationSteps: number; + _fixedDeltaTimeSq: number; + onHandleDrag: Function; + constructor(simulationBounds?: Rectangle); + update(): void; + constrainParticleToBounds(p: Particle): void; + handleCollisions(p: Particle, collidesWithLayers: number): void; + updateTiming(): void; + addComposite(composite: T): T; + removeComposite(composite: Composite): void; + handleDragging(): void; + getNearestParticle(position: Vector2): Particle; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + class Composite { + friction: Vector2; + drawParticles: boolean; + drawConstraints: boolean; + collidesWithLayers: number; + particles: Particle[]; + _constraints: Constraint[]; + addParticle(particle: Particle): Particle; + removeParticle(particle: Particle): void; + removeAll(): void; + addConstraint(constraint: T): T; + removeConstraint(constraint: Constraint): void; + applyForce(force: Vector2): void; + solveConstraints(): void; + updateParticles(deltaTimeSquared: number, gravity: Vector2): void; + handleConstraintCollisions(): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + class Ball extends Composite { + constructor(position: Vector2, radius?: number); + } +} +declare module es { + class VerletBox extends es.Composite { + constructor(center: es.Vector2, width: number, height: number, borderStiffness?: number, diagonalStiffness?: number); + } +} +declare module es { + class Cloth extends Composite { + constructor(topLeftPosition: Vector2, width: number, height: number, segments?: number, stiffness?: number, tearSensitivity?: number, connectHorizontalParticles?: boolean); + } +} +declare module es { + class LineSegments extends Composite { + constructor(vertices: Vector2[], stiffness: number); + pinParticleAtIndex(index: number): LineSegments; + } +} +declare module es { + class Ragdoll extends Composite { + constructor(x: number, y: number, bodyHeight: number); + } +} +declare module es { + class Tire extends Composite { + constructor(origin: Vector2, radius: number, segments: number, spokeStiffness?: number, treadStiffness?: number); + } +} +declare module es { + abstract class Constraint { + composite: Composite; + collidesWithColliders: boolean; + abstract solve(): void; + handleCollisions(collidesWithLayers: number): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + class AngleConstraint extends Constraint { + stiffness: number; + angleInRadius: number; + _particleA: Particle; + _centerParticle: Particle; + _particleC: Particle; + constructor(a: Particle, center: Particle, c: Particle, stiffness: number); + angleBetweenParticles(): number; + solve(): void; + } +} +declare module es { + class DistanceConstraint extends Constraint { + stiffness: number; + restingDistance: number; + tearSensitivity: number; + shouldApproximateCollisionsWithPoints: boolean; + totalPointsToApproximateCollisionsWith: number; + _particleOne: Particle; + _particleTwo: Particle; + static _polygon: Polygon; + constructor(first: Particle, second: Particle, stiffness: number, distance?: number); + static create(a: Particle, center: Particle, c: Particle, stiffness: number, angleInDegrees: number): DistanceConstraint; + setTearSensitivity(tearSensitivity: number): this; + setCollidesWithColliders(collidesWithColliders: boolean): this; + setShouldApproximateCollisionsWithPoints(shouldApproximateCollisionsWithPoints: boolean): this; + solve(): void; + handleCollisions(collidesWithLayers: number): void; + approximateCollisionsWithPoints(collidesWithLayers: number): void; + preparePolygonForCollisionChecks(midPoint: Vector2): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + /** + * AbstractTweenable作为你可能想做的任何可以执行的自定义类的基础。 + * 这些类不同于ITweens,因为他们没有实现ITweenT接口。 + * 它只是说一个AbstractTweenable不仅仅是将一个值从开始移动到结束。 + * 它可以做任何需要每帧执行的事情。 + */ + abstract class AbstractTweenable implements ITweenable { + protected _isPaused: boolean; + /** + * abstractTweenable在完成后往往会被保留下来。 + * 这个标志可以让它们在内部知道自己当前是否被TweenManager盯上了,以便在必要时可以重新添加自己。 + */ + protected _isCurrentlyManagedByTweenManager: boolean; + abstract tick(): boolean; + recycleSelf(): void; + isRunning(): boolean; + start(): void; + pause(): void; + resume(): void; + stop(bringToCompletion?: boolean): void; + } +} +declare module es { + class PropertyTweens { + static NumberPropertyTo(self: any, memberName: string, to: number, duration: number): ITween; + static Vector2PropertyTo(self: any, memeberName: string, to: Vector2, duration: number): ITween; + } +} +declare module es { + enum LoopType { + none = 0, + restartFromBeginning = 1, + pingpong = 2 + } + enum TweenState { + running = 0, + paused = 1, + complete = 2 + } + abstract class Tween implements ITweenable, ITween { + protected _target: ITweenTarget; + protected _isFromValueOverridden: boolean; + protected _fromValue: T; + protected _toValue: T; + protected _easeType: EaseType; + protected _shouldRecycleTween: boolean; + protected _isRelative: boolean; + protected _completionHandler: (tween: ITween) => void; + protected _loopCompleteHandler: (tween: ITween) => void; + protected _nextTween: ITweenable; + protected _tweenState: TweenState; + private _isTimeScaleIndependent; + protected _delay: number; + protected _duration: number; + protected _timeScale: number; + protected _elapsedTime: number; + protected _loopType: LoopType; + protected _loops: number; + protected _delayBetweenLoops: number; + private _isRunningInReverse; + context: any; + setEaseType(easeType: EaseType): ITween; + setDelay(delay: number): ITween; + setDuration(duration: number): ITween; + setTimeScale(timeSclae: number): ITween; + setIsTimeScaleIndependent(): ITween; + setCompletionHandler(completeHandler: (tween: ITween) => void): ITween; + setLoops(loopType: LoopType, loops?: number, delayBetweenLoops?: number): ITween; + setLoopCompletionHanlder(loopCompleteHandler: (tween: ITween) => void): ITween; + setFrom(from: T): ITween; + prepareForReuse(from: T, to: T, duration: number): ITween; + setRecycleTween(shouldRecycleTween: boolean): ITween; + abstract setIsRelative(): ITween; + setContext(context: any): ITween; + setNextTween(nextTween: ITweenable): ITween; + tick(): boolean; + recycleSelf(): void; + isRunning(): boolean; + start(): void; + pause(): void; + resume(): void; + stop(bringToCompletion?: boolean): void; + jumpToElapsedTime(elapsedTime: any): void; + /** + * 反转当前的tween,如果是向前走,就会向后走,反之亦然 + */ + reverseTween(): void; + /** + * 当通过StartCoroutine调用时,这将一直持续到tween完成 + */ + waitForCompletion(): IterableIterator; + getTargetObject(): any; + private resetState; + /** + * 将所有状态重置为默认值,并根据传入的参数设置初始状态。 + * 这个方法作为一个切入点,这样Tween子类就可以调用它,这样tweens就可以被回收。 + * 当回收时,构造函数不会再被调用,所以这个方法封装了构造函数要做的事情 + * @param target + * @param to + * @param duration + */ + initialize(target: ITweenTarget, to: T, duration: number): void; + /** + * 处理循环逻辑 + * @param elapsedTimeExcess + */ + private handleLooping; + protected abstract updateValue(): any; + } +} +declare module es { + class NumberTween extends Tween { + static create(): NumberTween; + constructor(target?: ITweenTarget, to?: number, duration?: number); + setIsRelative(): ITween; + protected updateValue(): void; + recycleSelf(): void; + } + class Vector2Tween extends Tween { + static create(): Vector2Tween; + constructor(target?: ITweenTarget, to?: Vector2, duration?: number); + setIsRelative(): ITween; + protected updateValue(): void; + recycleSelf(): void; + } + class RectangleTween extends Tween { + static create(): RectangleTween; + constructor(target?: ITweenTarget, to?: Rectangle, duration?: number); + setIsRelative(): ITween; + protected updateValue(): void; + recycleSelf(): void; + } + class ColorTween extends Tween { + static create(): ColorTween; + constructor(target?: ITweenTarget, to?: Color, duration?: number); + setIsRelative(): this; + protected updateValue(): void; + } +} +declare module es { + class RenderableColorTween extends ColorTween implements ITweenTarget { + _renderable: RenderableComponent; + setTweenedValue(value: Color): void; + getTweenedValue(): Color; + getTargetObject(): RenderableComponent; + updateValue(): void; + setTarget(renderable: RenderableComponent): void; + recycleSelf(): void; + } +} +declare module es { + class TransformSpringTween extends AbstractTweenable { + readonly targetType: TransformTargetType; + private _transform; + private _targetType; + private _targetValue; + private _velocity; + /** + * 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + */ + dampingRatio: number; + /** + * 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定角频率 + */ + angularFrequency: number; + constructor(transform: Transform, targetType: TransformTargetType, targetValue: Vector2); + /** + * 你可以在任何时候调用setTargetValue来重置目标值到一个新的Vector2。 + * 如果你没有调用start来添加spring tween,它会为你调用 + * @param targetValue + */ + setTargetValue(targetValue: Vector2): void; + /** + * lambda应该是振荡幅度减少50%时的理想持续时间 + * @param lambda + */ + updateDampingRatioWithHalfLife(lambda: number): void; + tick(): boolean; + private setTweenedValue; + private getCurrentValueOfTweenedTargetType; + } +} +declare module es { + /** + * 对任何与Transform相关的属性tweens都是有用的枚举 + */ + enum TransformTargetType { + position = 0, + localPosition = 1, + scale = 2, + localScale = 3, + rotationDegrees = 4, + localRotationDegrees = 5 + } + /** + * 这是一个特殊的情况,因为Transform是迄今为止最被ween的对象。 + * 我们将Tween和ITweenTarget封装在一个单一的、可缓存的类中 + */ + class TransformVector2Tween extends Vector2Tween implements ITweenTarget { + private _transform; + private _targetType; + setTweenedValue(value: Vector2): void; + getTweenedValue(): Vector2; + getTargetObject(): Transform; + setTargetAndType(transform: Transform, targetType: TransformTargetType): void; + protected updateValue(): void; + recycleSelf(): void; + } +} +declare module es { + enum EaseType { + linear = 0, + sineIn = 1, + sineOut = 2, + sineInOut = 3, + quadIn = 4, + quadOut = 5, + quadInOut = 6, + quintIn = 7, + quintOut = 8, + quintInOut = 9, + cubicIn = 10, + cubicOut = 11, + cubicInOut = 12, + quartIn = 13, + quartOut = 14, + quartInOut = 15, + expoIn = 16, + expoOut = 17, + expoInOut = 18, + circleIn = 19, + circleOut = 20, + circleInOut = 21, + elasticIn = 22, + elasticOut = 23, + elasticInOut = 24, + punch = 25, + backIn = 26, + backOut = 27, + backInOut = 28, + bounceIn = 29, + bounceOut = 30, + bounceInOut = 31 + } + /** + * 助手的一个方法,它接收一个EaseType,并通过给定的持续时间和时间参数来应用该Ease方程。 + * 我们这样做是为了避免传来传去的Funcs为垃圾收集器制造大量垃圾 + */ + class EaseHelper { + /** + * 返回 easeType 的相反 EaseType + * @param easeType + */ + static oppositeEaseType(easeType: EaseType): EaseType.linear | EaseType.sineIn | EaseType.sineOut | EaseType.sineInOut | EaseType.quadIn | EaseType.quadOut | EaseType.quadInOut | EaseType.quintIn | EaseType.quintOut | EaseType.quintInOut | EaseType.cubicIn | EaseType.cubicOut | EaseType.cubicInOut | EaseType.quartIn | EaseType.quartInOut | EaseType.expoIn | EaseType.expoOut | EaseType.expoInOut | EaseType.circleIn | EaseType.circleOut | EaseType.circleInOut | EaseType.elasticIn | EaseType.elasticOut | EaseType.elasticInOut | EaseType.punch | EaseType.backIn | EaseType.backOut | EaseType.backInOut | EaseType.bounceIn | EaseType.bounceOut | EaseType.bounceInOut; + static ease(easeType: EaseType, t: number, duration: number): number; + } +} +declare module es { + class GlobalManager { + _enabled: boolean; + /** + * 如果true则启用了GlobalManager。 + * 状态的改变会导致调用OnEnabled/OnDisable + */ + /** + * 如果true则启用了GlobalManager。 + * 状态的改变会导致调用OnEnabled/OnDisable + * @param value + */ + enabled: boolean; + /** + * 启用/禁用这个GlobalManager + * @param isEnabled + */ + setEnabled(isEnabled: boolean): void; + /** + * 此GlobalManager启用时调用 + */ + onEnabled(): void; + /** + * 此GlobalManager禁用时调用 + */ + onDisabled(): void; + /** + * 在frame .update之前调用每一帧 + */ + update(): void; + } +} +declare module es { + class TweenManager extends GlobalManager { + static defaultEaseType: EaseType; + /** + * 如果为真,当加载新关卡时,活动的tween列表将被清除 + */ + static removeAllTweensOnLevelLoad: boolean; + /** + * 这里支持各种类型的自动缓存。请 + * 注意,只有在使用扩展方法启动tweens时,或者在做自定义tweens时从缓存中获取tween时,缓存才会起作用。 + * 关于如何获取缓存的tween,请参见扩展方法的实现 + */ + static cacheNumberTweens: boolean; + static cacheVector2Tweens: boolean; + static cacheColorTweens: boolean; + static cacheRectTweens: boolean; + /** + * 当前所有活跃用户的内部列表 + */ + private _activeTweens; + private _tempTweens; + /** + * 标志表示tween更新循环正在运行 + */ + private _isUpdating; + /** + * 便于暴露一个静态的API以方便访问 + */ + private static _instance; + constructor(); + update(): void; + /** + * 将一个tween添加到活动tweens列表中 + * @param tween + */ + static addTween(tween: ITweenable): void; + /** + * 从当前的tweens列表中删除一个tween + * @param tween + */ + static removeTween(tween: ITweenable): void; + /** + * 停止所有的tween并选择地把他们全部完成 + * @param bringToCompletion + */ + static stopAllTweens(bringToCompletion?: boolean): void; + /** + * 返回具有特定上下文的所有tweens。 + * Tweens以ITweenable的形式返回,因为这就是TweenManager所知道的所有内容 + * @param context + */ + static allTweensWithContext(context: any): ITweenable[]; + /** + * 停止所有给定上下文的tweens + * @param context + * @param bringToCompletion + */ + static stopAllTweensWithContext(context: any, bringToCompletion?: boolean): void; + /** + * 返回具有特定目标的所有tweens。 + * Tweens以ITweenControl的形式返回,因为TweenManager只知道这些 + * @param target + */ + static allTweenWithTarget(target: any): ITweenable[]; + /** + * 停止所有具有TweenManager知道的特定目标的tweens + * @param target + * @param bringToCompletion + */ + static stopAllTweensWithTarget(target: any, bringToCompletion?: boolean): void; + } +} +declare module es { + /** + * 标准缓和方程通过将b和c参数(起始值和变化值)用0和1替换,然后进行简化。 + * 这样做的目的是为了让我们可以得到一个0 - 1之间的原始值(除了弹性/反弹故意超过界限),然后用这个值来lerp任何东西 + */ + module Easing { + class Linear { + static easeNone(t: number, d: number): number; + } + class Quadratic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Back { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Bounce { + static easeOut(t: number, d: number): number; + static easeIn(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Circular { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Cubic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Elastic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + static punch(t: number, d: number): number; + } + class Exponential { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Quartic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Quintic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Sinusoidal { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + } +} +declare module es { + /** + * 一系列静态方法来处理所有常见的tween类型结构,以及它们的unclamped lerps.unclamped lerps对于超过0-1范围的bounce、elastic或其他tweens是必需的 + */ + class Lerps { + static lerp(from: Color, to: Color, t: number): any; + static lerp(from: number, to: number, t: number): any; + static lerp(from: Rectangle, to: Rectangle, t: number): any; + static lerp(from: Vector2, to: Vector2, t: number): any; + static angleLerp(from: Vector2, to: Vector2, t: number): Vector2; + static ease(easeType: EaseType, from: Rectangle, to: Rectangle, t: number, duration: number): any; + static ease(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number): any; + static ease(easeType: EaseType, from: number, to: number, t: number, duration: number): any; + static ease(easeType: EaseType, from: Color, to: Color, t: number, duration: number): any; + static easeAngle(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number): Vector2; + /** + * 使用半隐式欧拉方法。速度较慢,但总是很稳定。见 + * http://allenchou.net/2015/04/game-math-more-on-numeric-springing/ + * @param currentValue + * @param targetValue + * @param velocity Velocity的引用。如果在两次调用之间改变targetValue,请务必将其重置为0 + * @param dampingRatio 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + * @param angularFrequency 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定 + */ + static fastSpring(currentValue: Vector2, targetValue: Vector2, velocity: Vector2, dampingRatio: number, angularFrequency: number): Vector2; + } +} +declare module es { + /** + * 一系列强类型、可链式的方法来设置各种tween属性 + */ + interface ITween extends ITweenControl { + /** + * 设置该tween的易用性类型 + * @param easeType + */ + setEaseType(easeType: EaseType): ITween; + /** + * 设置启动tween前的延迟 + * @param delay + */ + setDelay(delay: number): ITween; + /** + * 设置tween的持续时间 + * @param duration + */ + setDuration(duration: number): ITween; + /** + * 设置这个tween使用的timeScale。 + * TimeScale将与Time.deltaTime/Time.unscaledDeltaTime相乘,从而得到tween实际使用的delta时间 + * @param timeScale + */ + setTimeScale(timeScale: number): ITween; + /** + * 设置tween使用Time.unscaledDeltaTime代替Time.deltaTime + */ + setIsTimeScaleIndependent(): ITween; + /** + * 设置当tween完成时应该调用的动作 + * @param completionHandler + */ + setCompletionHandler(completionHandler: (tween: ITween) => void): ITween; + /** + * 设置tween的循环类型。一个pingpong循环意味着从开始-结束-开始 + * @param loopType + * @param loops + * @param delayBetweenLoops + */ + setLoops(loopType: LoopType, loops: number, delayBetweenLoops: number): ITween; + /** + * 设置tween的起始位置 + * @param from + */ + setFrom(from: T): ITween; + /** + * 通过重置tween的from/to值和持续时间,为重复使用tween做准备。 + * @param from + * @param to + * @param duration + */ + prepareForReuse(from: T, to: T, duration: number): ITween; + /** + * 如果为true(默认值),tween将在使用后被回收。 + * 如果在TweenManager类中进行了配置,所有的Tween子类都有自己相关的自动缓存 + * @param shouldRecycleTween + */ + setRecycleTween(shouldRecycleTween: boolean): ITween; + /** + * 帮助程序,只是将tween的to值设置为相对于其当前值的+从使tween + */ + setIsRelative(): ITween; + /** + * 允许你通过tween.context.context来设置任何可检索的对象引用。 + * 这对于避免完成处理程序方法的闭包分配是很方便的。 + * 你也可以在TweenManager中搜索具有特定上下文的所有tweens + * @param context + */ + setContext(context: any): ITween; + /** + * 允许你添加一个tween,这个tween完成后会被运行。 + * 注意 nextTween 必须是一个 ITweenable! 同时注意,所有的ITweenT都是ITweenable + * @param nextTween + */ + setNextTween(nextTween: ITweenable): ITween; + } +} +declare module es { + /** + * 更多具体的Tween播放控制在这里 + */ + interface ITweenControl extends ITweenable { + /** + * 当使用匿名方法时,您可以在任何回调(如完成处理程序)中使用该属性来避免分配 + */ + context: any; + /** + * 将tween扭曲为elapsedTime,并将其限制在0和duration之间,无论tween对象是暂停、完成还是运行,都会立即更新 + * @param elapsedTime 所用时间 + */ + jumpToElapsedTime(elapsedTime: number): any; + /** + * 当从StartCoroutine调用时,它将直到tween完成 + */ + waitForCompletion(): any; + /** + * 获取tween的目标,如果TweenTargets不一定都是一个对象,则为null,它的唯一真正用途是让TweenManager按目标查找tweens的列表 + */ + getTargetObject(): any; + } +} +declare module es { + /** + * 任何想要被weened的对象都需要实现这个功能。 + * TweenManager内部喜欢做一个简单的对象来实现这个接口,并存储一个对被tweened对象的引用 + */ + interface ITweenTarget { + /** + * 在你选择的对象上设置最终的tweened值 + * @param value + */ + setTweenedValue(value: T): any; + getTweenedValue(): T; + /** + * 获取tween的目标,如果TweenTargets不一定都是一个对象,则为null,它的唯一真正用途是让TweenManager按目标查找tweens的列表 + */ + getTargetObject(): any; + } +} +declare module es { + interface ITweenable { + /** + * 就像内部的Update一样,每一帧都被TweenManager调用 + */ + tick(): boolean; + /** + * 当一个tween被移除时,由TweenManager调用。子 + * 类可以选择自己回收。子类应该首先在其实现中检查_shouldRecycleTween bool! + */ + recycleSelf(): any; + /** + * 检查是否有tween在运行 + */ + isRunning(): boolean; + /** + * 启动tween + */ + start(): any; + /** + * 暂停 + */ + pause(): any; + /** + * 暂停后恢复tween + */ + resume(): any; + /** + * 停止tween,并可选择将其完成 + * @param bringToCompletion + */ + stop(bringToCompletion: boolean): any; + } +} +declare module es { + interface IAnimFrame { + t: number; + value: number; + } + class AnimCurve { + readonly points: IAnimFrame[]; + constructor(points: IAnimFrame[]); + lerp(t: number): number; + _points: IAnimFrame[]; + } +} +declare module es { + /** + * 用于包装事件的一个小类 + */ + class FuncPack { + /** 函数 */ + func: Function; + /** 上下文 */ + context: any; + constructor(func: Function, context: any); + } + /** + * 用于事件管理 + */ + class Emitter { + private _messageTable; + constructor(); + /** + * 开始监听项 + * @param eventType 监听类型 + * @param handler 监听函数 + * @param context 监听上下文 + */ + addObserver(eventType: T, handler: Function, context: any): void; + /** + * 移除监听项 + * @param eventType 事件类型 + * @param handler 事件函数 + */ + removeObserver(eventType: T, handler: Function): void; + /** + * 触发该事件 + * @param eventType 事件类型 + * @param data 事件数据 + */ + emit(eventType: T, ...data: any[]): void; + } +} +declare module es { + enum Edge { + top = 0, + bottom = 1, + left = 2, + right = 3 + } +} +declare module es { + class EqualityComparer implements IEqualityComparer { + static default(): EqualityComparer; + protected constructor(); + equals(x: T, y: T): boolean; + getHashCode(o: T): number; + private _getHashCodeForNumber; + private _getHashCodeForString; + private forOwn; + } +} +declare module es { + class Hash { + /** + * 从一个字节数组中计算一个哈希值 + * @param data + */ + static computeHash(...data: number[]): number; + } +} +declare module es { + interface IComparer { + compare(x: T, y: T): number; + } +} +declare module es { + /** + * 对象声明自己的平等方法和Hashcode的生成 + */ + interface IEqualityComparable { + /** + * 确定另一个对象是否等于这个实例 + * @param other + */ + equals(other: any): boolean; + /** + * 生成对象的哈希码 + */ + getHashCode(): number; + } +} +declare module es { + /** + * 为确定对象的哈希码和两个项目是否相等提供接口 + */ + interface IEqualityComparer { + /** + * 判断两个对象是否相等 + * @param x + * @param y + */ + equals(x: T, y: T): boolean; + /** + * 生成对象的哈希码 + * @param value + */ + getHashCode(value: T): number; + } +} +declare module es { + /** + * 实现该接口用于判定两个对象是否相等的快速接口 + */ + interface IEquatable { + equals(other: T): boolean; + } +} +declare module es { + interface IListener { + caller: object; + callback: Function; + } + interface IObservable { + addListener(caller: object, callback: Function): any; + removeListener(caller: object, callback: Function): any; + clearListener(): any; + clearListenerWithCaller(caller: object): any; + } + class Observable implements IObservable { + constructor(); + addListener(caller: object, callback: Function): void; + removeListener(caller: object, callback: Function): void; + clearListener(): void; + clearListenerWithCaller(caller: object): void; + notify(...args: any[]): void; + private _listeners; + } + class ObservableT extends Observable { + addListener(caller: object, callback: (arg: T) => void): void; + removeListener(caller: object, callback: (arg: T) => void): void; + notify(arg: T): void; + } + class ObservableTT extends Observable { + addListener(caller: object, callback: (arg1: T, arg2: R) => void): void; + removeListener(caller: object, callback: (arg: T, arg2: R) => void): void; + notify(arg1: T, arg2: R): void; + } + class Command implements IObservable { + constructor(caller: object, action: Function); + bindAction(caller: object, action: Function): void; + dispatch(...args: any[]): void; + addListener(caller: object, callback: Function): void; + removeListener(caller: object, callback: Function): void; + clearListener(): void; + clearListenerWithCaller(caller: object): void; + private _onExec; + private _caller; + private _action; + } + class ValueChangeCommand implements IObservable { + constructor(value: T); + readonly onValueChange: Observable; + value: T; + dispatch(value: T): void; + addListener(caller: object, callback: Function): void; + removeListener(caller: object, callback: Function): void; + clearListener(): void; + clearListenerWithCaller(caller: object): void; + private _onValueChange; + private _value; + } +} +declare module es { + class Ref { + value: T; + constructor(value: T); + } +} +declare module es { + class Screen { + static width: number; + static height: number; + static readonly size: Vector2; + static readonly center: Vector2; + } +} +declare module es { + /** + * 管理数值的简单助手类。它存储值,直到累计的总数大于1。一旦超过1,该值将在调用update时添加到amount中。 + */ + class SubpixelNumber { + remainder: number; + /** + * 以amount递增余数,将值截断为int,存储新的余数并将amount设置为当前值。 + * @param amount + */ + update(amount: number): number; + /** + * 将余数重置为0。当一个物体与一个不可移动的物体碰撞时有用。 + * 在这种情况下,您将希望将亚像素余数归零,因为它是空的和无效的碰撞。 + */ + reset(): void; + } +} +declare module es { + /** + * 简单的剪耳三角测量器,最终的三角形将出现在triangleIndices列表中。 + */ + class Triangulator { + /** + * 上次三角函数调用中使用的点列表的三角列表条目索引 + */ + triangleIndices: number[]; + private _triPrev; + private _triNext; + static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean; + /** + * 计算一个三角形列表,该列表完全覆盖给定点集所包含的区域。如果点不是CCW,则将arePointsCCW参数传递为false + * @param points 定义封闭路径的点列表 + * @param arePointsCCW + */ + triangulate(points: Vector2[], arePointsCCW?: boolean): void; + private initialize; + } +} +declare module es { + class UUID { + static randomUUID(): string; + } +} +declare module es { + interface Class extends Function { + } + function getClassName(klass: any): string; +} +declare namespace es { + /** + * 记录时间的持续时间,一些设计灵感来自物理秒表。 + */ + class Stopwatch { + private readonly getSystemTime; + /** + * 秒表启动的系统时间。 + * undefined,如果秒表尚未启动,或已复位。 + */ + private _startSystemTime; + /** + * 秒表停止的系统时间。 + * undefined,如果秒表目前没有停止,尚未开始,或已复位。 + */ + private _stopSystemTime; + /** 自上次复位以来,秒表已停止的系统时间总数。 */ + private _stopDuration; + /** + * 用秒表计时,当前等待的切片开始的时间。 + * undefined,如果秒表尚未启动,或已复位。 + */ + private _pendingSliceStartStopwatchTime; + /** + * 记录自上次复位以来所有已完成切片的结果。 + */ + private _completeSlices; + constructor(getSystemTime?: GetTimeFunc); + getState(): State; + isIdle(): boolean; + isRunning(): boolean; + isStopped(): boolean; + /** + * + */ + slice(): Slice; + /** + * 获取自上次复位以来该秒表已完成/记录的所有片的列表。 + */ + getCompletedSlices(): Slice[]; + /** + * 获取自上次重置以来该秒表已完成/记录的所有片的列表,以及当前挂起的片。 + */ + getCompletedAndPendingSlices(): Slice[]; + /** + * 获取关于这个秒表当前挂起的切片的详细信息。 + */ + getPendingSlice(): Slice; + /** + * 获取当前秒表时间。这是这个秒表自上次复位以来运行的系统时间总数。 + */ + getTime(): number; + /** + * 完全重置这个秒表到它的初始状态。清除所有记录的运行持续时间、切片等。 + */ + reset(): void; + /** + * 开始(或继续)运行秒表。 + * @param forceReset + */ + start(forceReset?: boolean): void; + /** + * + * @param recordPendingSlice + */ + stop(recordPendingSlice?: boolean): number; + /** + * 计算指定秒表时间的当前挂起片。 + * @param endStopwatchTime + */ + private calculatePendingSlice; + /** + * 计算指定系统时间的当前秒表时间。 + * @param endSystemTime + */ + private caculateStopwatchTime; + /** + * 获取与当前秒表时间等效的系统时间。 + * 如果该秒表当前停止,则返回该秒表停止时的系统时间。 + */ + private getSystemTimeOfCurrentStopwatchTime; + /** + * 结束/记录当前挂起的片的私有实现。 + * @param endStopwatchTime + */ + private recordPendingSlice; + } + /** + * 返回某个系统的“当前时间”的函数。 + * 惟一的要求是,对该函数的每次调用都必须返回一个大于或等于前一次对该函数的调用的数字。 + */ + type GetTimeFunc = () => number; + enum State { + /** 秒表尚未启动,或已复位。 */ + IDLE = "IDLE", + /** 秒表正在运行。 */ + RUNNING = "RUNNING", + /** 秒表以前还在跑,但现在已经停了。 */ + STOPPED = "STOPPED" + } + function setDefaultSystemTimeGetter(systemTimeGetter?: GetTimeFunc): void; + /** + * 由秒表记录的单个“薄片”的测量值 + */ + interface Slice { + /** 秒表显示的时间在这一片开始的时候。 */ + readonly startTime: number; + /** 秒表在这片片尾的时间。 */ + readonly endTime: number; + /** 该切片的运行时间 */ + readonly duration: number; + } +} +declare module es { + class Bag implements ImmutableBag { + size_: number; + length: number; + private array; + constructor(capacity?: number); + removeAt(index: number): E; + remove(e: E): boolean; + removeLast(): E; + contains(e: E): boolean; + removeAll(bag: ImmutableBag): boolean; + get(index: number): E; + safeGet(index: number): E; + size(): number; + getCapacity(): number; + isIndexWithinBounds(index: number): boolean; + isEmpty(): boolean; + add(e: E): void; + set(index: number, e: E): void; + grow(newCapacity?: number): void; + ensureCapacity(index: number): void; + clear(): void; + addAll(items: ImmutableBag): void; + } +} +declare module es { + /** + * 创建这个字典的原因只有一个: + * 我需要一个能让我直接以数组的形式对值进行迭代的字典,而不需要生成一个数组或使用迭代器。 + * 对于这个目标是比标准字典快N倍。 + * Faster dictionary在大部分操作上也比标准字典快,但差别可以忽略不计。 + * 唯一较慢的操作是在添加时调整内存大小,因为与标准数组相比,这个实现需要使用两个单独的数组。 + */ + class FasterDictionary { + _values: TValue[]; + _valuesInfo: FastNode[]; + _buckets: number[]; + _freeValueCellIndex: number; + _collisions: number; + constructor(size?: number); + getValuesArray(count: { + value: number; + }): TValue[]; + readonly valuesArray: TValue[]; + readonly count: number; + add(key: TKey, value: TValue): void; + addValue(key: TKey, value: TValue, indexSet: { + value: number; + }): boolean; + remove(key: TKey): boolean; + trim(): void; + clear(): void; + fastClear(): void; + containsKey(key: TKey): boolean; + tryGetValue(key: TKey): TValue; + tryFindIndex(key: TKey, findIndex: { + value: number; + }): boolean; + getDirectValue(index: number): TValue; + getIndex(key: TKey): number; + static updateLinkedList(index: number, valuesInfo: FastNode[]): void; + static hash(key: any): number; + static reduce(x: number, n: number): number; + } + class FastNode { + readonly key: any; + readonly hashcode: number; + previous: number; + next: number; + constructor(key: any, hash: number, previousNode?: number); + } +} +declare module es { + interface ImmutableBag { + get(index: number): E; + size(): number; + isEmpty(): boolean; + contains(e: E): boolean; + } +} +declare module es { + class Node { + element: T; + next: Node; + constructor(element: T, next?: Node); + } + interface equalsFnType { + (a: T, b: T): boolean; + } + function defaultEquals(a: T, b: T): boolean; + class LinkedList { + protected count: number; + protected next: any; + protected equalsFn: equalsFnType; + protected head: Node; + constructor(equalsFn?: typeof defaultEquals); + push(element: T): void; + removeAt(index: number): T; + getElementAt(index: number): Node; + insert(element: T, index: number): boolean; + indexOf(element: T): number; + remove(element: T): void; + clear(): void; + size(): number; + isEmpty(): boolean; + getHead(): Node; + toString(): string; + } +} +declare module es { + /** + * 可以用于列表池的简单类 + */ + class ListPool { + private static readonly _objectQueue; + /** + * 预热缓存,使用最大的cacheCount对象填充缓存 + * @param cacheCount + */ + static warmCache(cacheCount: number): void; + /** + * 将缓存修剪为cacheCount项目 + * @param cacheCount + */ + static trimCache(cacheCount: any): void; + /** + * 清除缓存 + */ + static clearCache(): void; + /** + * 如果可以的话,从堆栈中弹出一个项 + */ + static obtain(): T[]; + /** + * 将项推回堆栈 + * @param obj + */ + static free(obj: Array): void; + } +} +declare module es { + /** + * 用于管理一对对象的简单DTO + */ + class Pair implements IEqualityComparable { + first: T; + second: T; + constructor(first: T, second: T); + clear(): void; + equals(other: Pair): boolean; + getHashCode(): number; + } +} +declare module es { + /** + * 用于池任何对象 + */ + class Pool { + private static _objectQueue; + /** + * 预热缓存,使用最大的cacheCount对象填充缓存 + * @param type + * @param cacheCount + */ + static warmCache(type: new (...args: any[]) => T, cacheCount: number): void; + /** + * 将缓存修剪为cacheCount项目 + * @param cacheCount + */ + static trimCache(cacheCount: number): void; + /** + * 清除缓存 + */ + static clearCache(): void; + /** + * 如果可以的话,从堆栈中弹出一个项 + */ + static obtain(type: new (...args: any[]) => T): T; + /** + * 将项推回堆栈 + * @param obj + */ + static free(obj: T): void; + } + interface IPoolable { + /** + * 重置对象以供重用。对象引用应该为空,字段可以设置为默认值 + */ + reset(): any; + } + var isIPoolable: (props: any) => props is IPoolable; +} +declare module es { + interface ISet { + add(item: T): boolean; + remove(item: T): boolean; + contains(item: T): boolean; + getCount(): number; + clear(): void; + toArray(): Array; + /** + * 从当前集合中删除指定集合中的所有元素 + * @param other + */ + exceptWith(other: Array): void; + /** + * 修改当前Set对象,使其只包含该对象和指定数组中的元素 + * @param other + */ + intersectWith(other: Array): void; + /** + * 修改当前的集合对象,使其包含所有存在于自身、指定集合中的元素,或者两者都包含 + * @param other + */ + unionWith(other: Array): void; + isSubsetOf(other: Array): boolean; + isSupersetOf(other: Array): boolean; + overlaps(other: Array): boolean; + setEquals(other: Array): boolean; + } + abstract class Set implements ISet { + protected buckets: T[][]; + protected count: number; + constructor(source?: Array); + abstract getHashCode(item: T): number; + abstract areEqual(value1: T, value2: T): boolean; + add(item: T): boolean; + remove(item: T): boolean; + contains(item: T): boolean; + getCount(): number; + clear(): void; + toArray(): T[]; + /** + * 从当前集合中删除指定集合中的所有元素 + * @param other + */ + exceptWith(other: Array): void; + /** + * 修改当前Set对象,使其只包含该对象和指定数组中的元素 + * @param other + */ + intersectWith(other: Array): void; + unionWith(other: Array): void; + /** + * 确定当前集合是否为指定集合或数组的子集 + * @param other + */ + isSubsetOf(other: Array): boolean; + /** + * 确定当前不可变排序集是否为指定集合的超集 + * @param other + */ + isSupersetOf(other: Array): boolean; + overlaps(other: Array): boolean; + setEquals(other: Array): boolean; + private buildInternalBuckets; + private bucketsContains; + } + class HashSet extends Set { + constructor(source?: Array); + getHashCode(item: T): number; + areEqual(value1: T, value2: T): boolean; + } +} +declare module es { + /** + * startCoroutine返回的接口,它提供了中途停止coroutine的能力。 + */ + interface ICoroutine { + /** + * 停止Coroutine + */ + stop(): any; + /** + * 设置Coroutine是否应该使用deltaTime或unscaledDeltaTime进行计时 + * @param useUnscaledDeltaTime + */ + setUseUnscaledDeltaTime(useUnscaledDeltaTime: boolean): ICoroutine; + } + class Coroutine { + /** + * 导致Coroutine在指定的时间内暂停。在Coroutine.waitForSeconds的基础上,在Coroutine中使用Yield + * @param seconds + */ + static waitForSeconds(seconds: number): WaitForSeconds; + } + /** + * 帮助类,用于当一个coroutine想要暂停一段时间时。返回Coroutine.waitForSeconds返回其中一个 + */ + class WaitForSeconds { + static waiter: WaitForSeconds; + waitTime: number; + wait(seconds: number): WaitForSeconds; + } +} +declare module es { + /** + * CoroutineManager用于隐藏Coroutine所需数据的内部类 + */ + class CoroutineImpl implements ICoroutine, IPoolable { + enumerator: any; + /** + * 每当产生一个延迟,它就会被添加到跟踪延迟的waitTimer中 + */ + waitTimer: number; + isDone: boolean; + waitForCoroutine: CoroutineImpl; + useUnscaledDeltaTime: boolean; + stop(): void; + setUseUnscaledDeltaTime(useUnscaledDeltaTime: boolean): this; + prepareForUse(): void; + reset(): void; + } + class CoroutineManager extends GlobalManager { + /** + * 标志来跟踪我们何时处于更新循环中。 + * 如果在更新循环中启动了一个新的coroutine,我们必须将它贴在shouldRunNextFrame列表中,以避免在迭代时修改一个数组 + */ + _isInUpdate: boolean; + _unblockedCoroutines: CoroutineImpl[]; + _shouldRunNextFrame: CoroutineImpl[]; + /** + * 将IEnumerator添加到CoroutineManager中 + * Coroutine在每一帧调用Update之前被执行 + * @param enumerator + */ + startCoroutine(enumerator: any): CoroutineImpl; + update(): void; + /** + * 勾选一个coroutine,如果该coroutine应该在下一帧继续运行,则返回true。本方法会将完成的coroutine放回Pool + * @param coroutine + */ + tickCoroutine(coroutine: CoroutineImpl): boolean; + } +} +declare module es { + class MaxRectsBinPack { + binWidth: number; + binHeight: number; + allowRotations: boolean; + usedRectangles: Rectangle[]; + freeRectangles: Rectangle[]; + constructor(width: number, height: number, rotations?: boolean); + init(width: number, height: number, rotations?: boolean): void; + insert(width: number, height: number): Rectangle; + findPositionForNewNodeBestAreaFit(width: number, height: number, bestAreaFit: Ref, bestShortSideFit: Ref): Rectangle; + splitFreeNode(freeNode: Rectangle, usedNode: Rectangle): boolean; + pruneFreeList(): void; + isContainedIn(a: Rectangle, b: Rectangle): boolean; + } +} +declare module es { + class ArrayUtils { + /** + * 执行冒泡排序 + * @param ary + */ + static bubbleSort(ary: number[]): void; + /** + * 执行插入排序 + * @param ary + */ + static insertionSort(ary: number[]): void; + /** + * 执行二分搜索 + * @param ary 搜索的数组(必须排序过) + * @param value 需要搜索的值 + * @returns 返回匹配结果的数组索引 + */ + static binarySearch(ary: number[], value: number): number; + /** + * 返回匹配项的索引 + * @param ary + * @param num + */ + static findElementIndex(ary: any[], num: any): any; + /** + * 返回数组中最大值的索引 + * @param ary + */ + static getMaxElementIndex(ary: number[]): number; + /** + * 返回数组中最小值的索引 + * @param ary + */ + static getMinElementIndex(ary: number[]): number; + /** + * 返回一个"唯一性"数组 + * @param ary 需要唯一性的数组 + * @returns 唯一性的数组 + * + * @tutorial + * 比如: [1, 2, 2, 3, 4] + * 返回: [1, 2, 3, 4] + */ + static getUniqueAry(ary: number[]): number[]; + /** + * 返回2个数组中不同的部分 + * 比如数组A = [1, 2, 3, 4, 6] + * 数组B = [0, 2, 1, 3, 4] + * 返回[6, 0] + * @param aryA + * @param aryB + * @return + */ + static getDifferAry(aryA: number[], aryB: number[]): number[]; + /** + * 交换数组元素 + * @param array 目标数组 + * @param index1 交换后的索引 + * @param index2 交换前的索引 + */ + static swap(array: any[], index1: number, index2: number): void; + /** + * 清除列表 + * @param ary + */ + static clearList(ary: any[]): void; + /** + * 克隆一个数组 + * @param ary 需要克隆的数组 + * @return 克隆的数组 + */ + static cloneList(ary: any[]): any[]; + /** + * 判断2个数组是否相同 + * @param ary1 数组1 + * @param ary2 数组2 + */ + static equals(ary1: number[], ary2: number[]): Boolean; + /** + * 根据索引插入元素,索引和索引后的元素都向后移动一位 + * @param ary + * @param index 插入索引 + * @param value 插入的元素 + * @returns 插入的元素 未插入则返回空 + */ + static insert(ary: any[], index: number, value: any): any; + /** + * 打乱数组 Fisher–Yates shuffle + * @param list + */ + static shuffle(list: T[]): void; + /** + * 如果项目已经在列表中,返回false,如果成功添加,返回true + * @param list + * @param item + */ + static addIfNotPresent(list: T[], item: T): boolean; + /** + * 返回列表中的最后一项。列表中至少应该有一个项目 + * @param list + */ + static lastItem(list: T[]): T; + /** + * 从列表中随机获取一个项目。不清空检查列表! + * @param list + */ + static randomItem(list: T[]): T; + /** + * 从列表中随机获取物品。不清空检查列表,也不验证列表数是否大于项目数。返回的List可以通过ListPool.free放回池中 + * @param list + * @param itemCount 从列表中返回的随机项目的数量 + */ + static randomItems(list: T[], itemCount: number): T[]; + } +} +declare module es { + class Base64Utils { + private static _keyStr; + /** + * 判断是否原生支持Base64位解析 + */ + static readonly nativeBase64: boolean; + /** + * 解码 + * @param input + */ + static decode(input: string): string; + /** + * 编码 + * @param input + */ + static encode(input: string): string; + /** + * 解析Base64格式数据 + * @param input + * @param bytes + */ + static decodeBase64AsArray(input: string, bytes: number): Uint32Array; + /** + * 暂时不支持 + * @param data + * @param decoded + * @param compression + * @private + */ + static decompress(data: string, decoded: any, compression: string): any; + /** + * 解析csv数据 + * @param input + */ + static decodeCSV(input: string): Array; + } +} +declare module es { + class EdgeExt { + static oppositeEdge(self: Edge): Edge; + /** + * 如果边是右或左,则返回true + * @param self + */ + static isHorizontal(self: Edge): boolean; + /** + * 如果边是顶部或底部,则返回true + * @param self + */ + static isVertical(self: Edge): boolean; + } +} +declare module es { + class NumberExtension { + static toNumber(value: any): number; + } +} +declare module es { + class RandomUtils { + /** + * 在 start 与 stop之间取一个随机整数,可以用step指定间隔, 但不包括较大的端点(start与stop较大的一个) + * 如 + * this.randrange(1, 10, 3) + * 则返回的可能是 1 或 4 或 7 , 注意 这里面不会返回10,因为是10是大端点 + * + * @param start + * @param stop + * @param step + * @return 假设 start < stop, [start, stop) 区间内的随机整数 + * + */ + static randrange(start: number, stop: number, step?: number): number; + /** + * 返回a 到 b之间的随机整数,包括 a 和 b + * @param a + * @param b + * @return [a, b] 之间的随机整数 + * + */ + static randint(a: number, b: number): number; + /** + * 返回 a - b之间的随机数,不包括 Math.max(a, b) + * @param a + * @param b + * @return 假设 a < b, [a, b) + */ + static randnum(a: number, b: number): number; + /** + * 打乱数组 + * @param array + * @return + */ + static shuffle(array: any[]): any[]; + /** + * 从序列中随机取一个元素 + * @param sequence 可以是 数组、 vector,等只要是有length属性,并且可以用数字索引获取元素的对象, + * 另外,字符串也是允许的。 + * @return 序列中的某一个元素 + * + */ + static choice(sequence: any): any; + /** + * 对列表中的元素进行随机采æ ? + *
+         * this.sample([1, 2, 3, 4, 5],  3)  // Choose 3 elements
+         * [4, 1, 5]
+         * 
+ * @param sequence + * @param num + * @return + * + */ + static sample(sequence: any[], num: number): any[]; + /** + * 返回 0.0 - 1.0 之间的随机数,等同于 Math.random() + * @return Math.random() + * + */ + static random(): number; + /** + * 计算概率 + * @param chance 概率 + * @return + */ + static boolean(chance?: number): boolean; + private static _randomCompare; + } +} +declare module es { + class RectangleExt { + /** + * 获取指定边的位置 + * @param rect + * @param edge + */ + static getSide(rect: Rectangle, edge: Edge): number; + /** + * 计算两个矩形的并集。结果将是一个包含其他两个的矩形。 + * @param first + * @param point + */ + static union(first: Rectangle, point: Vector2): Rectangle; + static getHalfRect(rect: Rectangle, edge: Edge): Rectangle; + /** + * 获取矩形的一部分,其宽度/高度的大小位于矩形的边缘,但仍然包含在其中。 + * @param rect + * @param edge + * @param size + */ + static getRectEdgePortion(rect: Rectangle, edge: Edge, size?: number): Rectangle; + static expandSide(rect: Rectangle, edge: Edge, amount: number): void; + static contract(rect: Rectangle, horizontalAmount: any, verticalAmount: any): void; + /** + * 给定多边形的点,计算其边界 + * @param points + */ + static boundsFromPolygonVector(points: Vector2[]): Rectangle; + /** + * 创建一个给定最小/最大点(左上角,右下角)的矩形 + * @param min + * @param max + */ + static fromMinMaxVector(min: Vector2, max: Vector2): Rectangle; + /** + * 返回一个跨越当前边界和提供的delta位置的Bounds + * @param rect + * @param deltaX + * @param deltaY + */ + static getSweptBroadphaseBounds(rect: Rectangle, deltaX: number, deltaY: number): Rectangle; + /** + * 如果矩形发生碰撞,返回true + * moveX和moveY将返回b1为避免碰撞而必须移动的移动量 + * @param rect + * @param other + * @param moveX + * @param moveY + */ + collisionCheck(rect: Rectangle, other: Rectangle, moveX: Ref, moveY: Ref): boolean; + /** + * 计算两个矩形之间有符号的交点深度 + * @param rectA + * @param rectB + * @returns 两个相交的矩形之间的重叠量。 + * 这些深度值可以是负值,取决于矩形相交的边。 + * 这允许调用者确定正确的推送对象的方向,以解决碰撞问题。 + * 如果矩形不相交,则返回Vector2.zero。 + */ + static getIntersectionDepth(rectA: Rectangle, rectB: Rectangle): Vector2; + static getClosestPointOnBoundsToOrigin(rect: Rectangle): Vector2; + /** + * 将Rectangle中或上的最接近点返回给定点 + * @param rect + * @param point + */ + static getClosestPointOnRectangleToPoint(rect: Rectangle, point: Vector2): Vector2; + /** + * 获取矩形边界上与给定点最接近的点 + * @param rect + * @param point + */ + static getClosestPointOnRectangleBorderToPoint(rect: Rectangle, point: Vector2): Vector2; + static getMax(rect: Rectangle): Vector2; + /** + * 以Vector2的形式获取矩形的中心点 + * @param rect + * @returns + */ + static getCenter(rect: Rectangle): Vector2; + /** + * 给定多边形的点即可计算边界 + * @param points + */ + static boundsFromPolygonPoints(points: Vector2[]): Rectangle; + static calculateBounds(rect: Rectangle, parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number): void; + /** + * 缩放矩形 + * @param rect + * @param scale + */ + static scale(rect: Rectangle, scale: Vector2): void; + static translate(rect: Rectangle, vec: Vector2): void; + } +} +declare module es { + class TextureUtils { + static premultiplyAlpha(pixels: number[]): void; + } +} +declare module es { + class TypeUtils { + static getType(obj: any): any; + } +} +declare module es { + class Vector2Ext { + /** + * 检查三角形是CCW还是CW + * @param a + * @param center + * @param c + */ + static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2): boolean; + static halfVector(): Vector2; + /** + * 计算二维伪叉乘点(Perp(u), v) + * @param u + * @param v + */ + static cross(u: Vector2, v: Vector2): number; + /** + * 返回垂直于传入向量的向量 + * @param first + * @param second + */ + static perpendicular(first: Vector2, second: Vector2): Vector2; + /** + * 将x/y值翻转,并将y反转,得到垂直于x/y的值 + * @param original + */ + static perpendicularFlip(original: Vector2): Vector2; + /** + * 返回两个向量之间的角度,单位为度 + * @param from + * @param to + */ + static angle(from: Vector2, to: Vector2): number; + /** + * 返回以自度为中心的左右角度 + * @param self + * @param left + * @param right + */ + static angleBetween(self: Vector2, left: Vector2, right: Vector2): number; + /** + * 给定两条直线(ab和cd),求交点 + * @param a + * @param b + * @param c + * @param d + * @param intersection + */ + static getRayIntersection(a: Vector2, b: Vector2, c: Vector2, d: Vector2, intersection?: Vector2): boolean; + /** + * Vector2的临时解决方案 + * 标准化把向量弄乱了 + * @param vec + */ + static normalize(vec: Vector2): void; + /** + * 通过指定的矩阵对Vector2的数组中的向量应用变换,并将结果放置在另一个数组中。 + * @param sourceArray + * @param sourceIndex + * @param matrix + * @param destinationArray + * @param destinationIndex + * @param length + */ + static transformA(sourceArray: Vector2[], sourceIndex: number, matrix: Matrix2D, destinationArray: Vector2[], destinationIndex: number, length: number): void; + /** + * 创建一个新的Vector2,该Vector2包含了通过指定的Matrix进行的二维向量变换 + * @param position + * @param matrix + * @param result + */ + static transformR(position: Vector2, matrix: Matrix2D, result?: Vector2): void; + /** + * 通过指定的矩阵对Vector2的数组中的所有向量应用变换,并将结果放到另一个数组中。 + * @param sourceArray + * @param matrix + * @param destinationArray + */ + static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]): void; + static round(vec: Vector2): Vector2; + } +} +declare module es { + class Enumerable { + /** + * 在指定范围内生成一个整数序列。 + */ + static range(start: number, count: number): List; + /** + * 生成包含一个重复值的序列。 + */ + static repeat(element: T, count: number): List; + } +} +declare module es { + /** + * 检查传递的参数是否为对象 + */ + const isObj: (x: T) => boolean; + /** + * 创建一个否定谓词结果的函数 + */ + const negate: (pred: (...args: T[]) => boolean) => (...args: T[]) => boolean; + /** + * 比较器助手 + */ + const composeComparers: (previousComparer: (a: T, b: T) => number, currentComparer: (a: T, b: T) => number) => (a: T, b: T) => number; + const keyComparer: (_keySelector: (key: T) => string, descending?: boolean) => (a: T, b: T) => number; +} +declare module es { + type PredicateType = (value?: T, index?: number, list?: T[]) => boolean; + class List { + protected _elements: T[]; + /** + * 默认为列表的元素 + */ + constructor(elements?: T[]); + /** + * 在列表的末尾添加一个对象。 + */ + add(element: T): void; + /** + * 将一个对象追加到列表的末尾。 + */ + append(element: T): void; + /** + * 在列表的开头添加一个对象。 + */ + prepend(element: T): void; + /** + * 将指定集合的元素添加到列表的末尾。 + */ + addRange(elements: T[]): void; + /** + * 对序列应用累加器函数。 + */ + aggregate(accumulator: (accum: U, value?: T, index?: number, list?: T[]) => any, initialValue?: U): any; + /** + * 确定序列的所有元素是否满足一个条件。 + */ + all(predicate: PredicateType): boolean; + /** + * 确定序列是否包含任何元素。 + */ + any(): boolean; + any(predicate: PredicateType): boolean; + /** + * 计算通过对输入序列的每个元素调用转换函数获得的一系列数值的平均值。 + */ + average(): number; + average(transform: (value?: T, index?: number, list?: T[]) => any): number; + /** + * 将序列的元素转换为指定的类型。 + */ + cast(): List; + /** + * 从列表中删除所有元素。 + */ + clear(): void; + /** + * 连接两个序列。 + */ + concat(list: List): List; + /** + * 确定一个元素是否在列表中。 + */ + contains(element: T): boolean; + /** + * 返回序列中元素的数量。 + */ + count(): number; + count(predicate: PredicateType): number; + /** + * 返回指定序列的元素,或者如果序列为空,则返回单例集合中类型参数的默认值。 + */ + defaultIfEmpty(defaultValue?: T): List; + /** + * 根据指定的键选择器从序列中返回不同的元素。 + */ + distinctBy(keySelector: (key: T) => string | number): List; + /** + * 返回序列中指定索引处的元素。 + */ + elementAt(index: number): T; + /** + * 返回序列中指定索引处的元素,如果索引超出范围,则返回默认值。 + */ + elementAtOrDefault(index: number): T | null; + /** + * 通过使用默认的相等比较器来比较值,生成两个序列的差值集。 + */ + except(source: List): List; + /** + * 返回序列的第一个元素。 + */ + first(): T; + first(predicate: PredicateType): T; + /** + * 返回序列的第一个元素,如果序列不包含元素,则返回默认值。 + */ + firstOrDefault(): T; + firstOrDefault(predicate: PredicateType): T; + /** + * 对列表中的每个元素执行指定的操作。 + */ + forEach(action: (value?: T, index?: number, list?: T[]) => any): void; + /** + * 根据指定的键选择器函数对序列中的元素进行分组。 + */ + groupBy(grouper: (key: T) => string | number, mapper?: (element: T) => TResult): { + [key: string]: TResult[]; + }; + /** + * 根据键的相等将两个序列的元素关联起来,并将结果分组。默认的相等比较器用于比较键。 + */ + groupJoin(list: List, key1: (k: T) => any, key2: (k: U) => any, result: (first: T, second: List) => R): List; + /** + * 返回列表中某个元素第一次出现的索引。 + */ + indexOf(element: T): number; + /** + * 向列表中插入一个元素在指定索引处。 + */ + insert(index: number, element: T): void | Error; + /** + * 通过使用默认的相等比较器来比较值,生成两个序列的交集集。 + */ + intersect(source: List): List; + /** + * 基于匹配的键将两个序列的元素关联起来。默认的相等比较器用于比较键。 + */ + join(list: List, key1: (key: T) => any, key2: (key: U) => any, result: (first: T, second: U) => R): List; + /** + * 返回序列的最后一个元素。 + */ + last(): T; + last(predicate: PredicateType): T; + /** + * 返回序列的最后一个元素,如果序列不包含元素,则返回默认值。 + */ + lastOrDefault(): T; + lastOrDefault(predicate: PredicateType): T; + /** + * 返回泛型序列中的最大值。 + */ + max(): number; + max(selector: (value: T, index: number, array: T[]) => number): number; + /** + * 返回泛型序列中的最小值。 + */ + min(): number; + min(selector: (value: T, index: number, array: T[]) => number): number; + /** + * 根据指定的类型筛选序列中的元素。 + */ + ofType(type: any): List; + /** + * 根据键按升序对序列中的元素进行排序。 + */ + orderBy(keySelector: (key: T) => any, comparer?: (a: T, b: T) => number): List; + /** + * 根据键值降序对序列中的元素进行排序。 + */ + orderByDescending(keySelector: (key: T) => any, comparer?: (a: T, b: T) => number): List; + /** + * 按键按升序对序列中的元素执行后续排序。 + */ + thenBy(keySelector: (key: T) => any): List; + /** + * 根据键值按降序对序列中的元素执行后续排序。 + */ + thenByDescending(keySelector: (key: T) => any): List; + /** + * 从列表中删除第一个出现的特定对象。 + */ + remove(element: T): boolean; + /** + * 删除与指定谓词定义的条件匹配的所有元素。 + */ + removeAll(predicate: PredicateType): List; + /** + * 删除列表指定索引处的元素。 + */ + removeAt(index: number): void; + /** + * 颠倒整个列表中元素的顺序。 + */ + reverse(): List; + /** + * 将序列中的每个元素投射到一个新形式中。 + */ + select(selector: (element: T, index: number) => TOut): List; + /** + * 将序列的每个元素投影到一个列表中。并将得到的序列扁平化为一个序列。 + */ + selectMany>(selector: (element: T, index: number) => TOut): TOut; + /** + * 通过使用默认的相等比较器对元素的类型进行比较,确定两个序列是否相等。 + */ + sequenceEqual(list: List): boolean; + /** + * 返回序列中唯一的元素,如果序列中没有恰好一个元素,则抛出异常。 + */ + single(predicate?: PredicateType): T; + /** + * 返回序列中唯一的元素,如果序列为空,则返回默认值;如果序列中有多个元素,此方法将抛出异常。 + */ + singleOrDefault(predicate?: PredicateType): T; + /** + * 绕过序列中指定数量的元素,然后返回剩余的元素。 + */ + skip(amount: number): List; + /** + * 省略序列中最后指定数量的元素,然后返回剩余的元素。 + */ + skipLast(amount: number): List; + /** + * 只要指定条件为真,就绕过序列中的元素,然后返回剩余的元素。 + */ + skipWhile(predicate: PredicateType): List; + /** + * 计算通过对输入序列的每个元素调用转换函数获得的数值序列的和。 + */ + sum(): number; + sum(transform: (value?: T, index?: number, list?: T[]) => number): number; + /** + * 从序列的开始返回指定数量的连续元素。 + */ + take(amount: number): List; + /** + * 从序列的末尾返回指定数目的连续元素。 + */ + takeLast(amount: number): List; + /** + * 返回序列中的元素,只要指定的条件为真。 + */ + takeWhile(predicate: PredicateType): List; + /** + * 复制列表中的元素到一个新数组。 + */ + toArray(): T[]; + /** + * 创建一个从List< T>根据指定的键选择器函数。 + */ + toDictionary(key: (key: T) => TKey): List<{ + Key: TKey; + Value: T; + }>; + toDictionary(key: (key: T) => TKey, value: (value: T) => TValue): List<{ + Key: TKey; + Value: T | TValue; + }>; + /** + * 创建一个Set从一个Enumerable.List< T>。 + */ + toSet(): Set; + /** + * 创建一个List< T>从一个Enumerable.List< T>。 + */ + toList(): List; + /** + * 创建一个查找,TElement>从一个IEnumerable< T>根据指定的键选择器和元素选择器函数。 + */ + toLookup(keySelector: (key: T) => string | number, elementSelector: (element: T) => TResult): { + [key: string]: TResult[]; + }; + /** + * 基于谓词过滤一系列值。 + */ + where(predicate: PredicateType): List; + /** + * 将指定的函数应用于两个序列的对应元素,生成结果序列。 + */ + zip(list: List, result: (first: T, second: U) => TOut): List; + } + /** + * 表示已排序的序列。该类的方法是通过使用延迟执行来实现的。 + * 即时返回值是一个存储执行操作所需的所有信息的对象。 + * 在通过调用对象的ToDictionary、ToLookup、ToList或ToArray方法枚举对象之前,不会执行由该方法表示的查询 + */ + class OrderedList extends List { + private _comparer; + constructor(elements: T[], _comparer: (a: T, b: T) => number); + /** + * 按键按升序对序列中的元素执行后续排序。 + * @override + */ + thenBy(keySelector: (key: T) => any): List; + /** + * 根据键值按降序对序列中的元素执行后续排序。 + * @override + */ + thenByDescending(keySelector: (key: T) => any): List; + } +} +declare module es { + /** + * 一段的终点 + */ + class EndPoint { + /** 该部分的位置 */ + position: Vector2; + /** 如果这个端点是一个段的起始点或终点(每个段只有一个起始点和一个终点) */ + begin: boolean; + /** 该端点所属的段 */ + segment: Segment; + /** 端点相对于能见度测试位置的角度 */ + angle: number; + constructor(); + } + class EndPointComparer implements IComparer { + /** + * 按角度对点进行排序的比较功能 + * @param a + * @param b + */ + compare(a: EndPoint, b: EndPoint): 0 | 1 | -1; + } +} +declare module es { + /** + * 表示可见性网格中的遮挡线段 + */ + class Segment { + /** + * 该部分的第一个终点 + */ + p1: EndPoint; + /** + * 该部分的第二个终点 + */ + p2: EndPoint; + constructor(); + } +} +declare module es { + /** + * 类,它可以计算出一个网格,表示从给定的一组遮挡物的原点可以看到哪些区域。使用方法如下。 + * + * - 调用 begin + * - 添加任何遮挡物 + * - 调用end来获取可见度多边形。当调用end时,所有的内部存储都会被清空。 + */ + class VisibilityComputer { + /** + * 在近似圆的时候要用到的线的总数。只需要一个180度的半球,所以这将是近似该半球的线段数 + */ + lineCountForCircleApproximation: number; + _radius: number; + _origin: Vector2; + _isSpotLight: boolean; + _spotStartAngle: number; + _spotEndAngle: number; + _endPoints: EndPoint[]; + _segments: Segment[]; + _radialComparer: EndPointComparer; + static _cornerCache: Vector2[]; + static _openSegments: LinkedList; + constructor(origin?: Vector2, radius?: number); + /** + * 增加了一个对撞机作为PolyLight的遮蔽器 + * @param collider + */ + addColliderOccluder(collider: Collider): void; + /** + * 增加了一个圆形的遮挡器 + * @param position + * @param radius + */ + addCircleOccluder(position: Vector2, radius: number): void; + /** + * 增加一个线型遮挡器 + * @param p1 + * @param p2 + */ + addLineOccluder(p1: Vector2, p2: Vector2): void; + /** + * 增加一个方形的遮挡器 + * @param bounds + */ + addSquareOccluder(bounds: Rectangle): void; + /** + * 添加一个段,第一个点在可视化中显示,但第二个点不显示。 + * 每个端点都是两个段的一部分,但我们希望只显示一次 + * @param p1 + * @param p2 + */ + addSegment(p1: Vector2, p2: Vector2): void; + /** + * 移除所有的遮挡物 + */ + clearOccluders(): void; + /** + * 为计算机计算当前的聚光做好准备 + * @param origin + * @param radius + */ + begin(origin: Vector2, radius: number): void; + /** + * 计算可见性多边形,并返回三角形扇形的顶点(减去中心顶点)。返回的数组来自ListPool + */ + end(): Vector2[]; + addTriangle(triangles: Vector2[], angle1: number, angle2: number, segment: Segment): void; + /** + * 计算直线p1-p2与p3-p4的交点 + * @param p1 + * @param p2 + * @param p3 + * @param p4 + */ + static lineLineIntersection(p1: Vector2, p2: Vector2, p3: Vector2, p4: Vector2): Vector2; + static between(value: number, min: number, max: number): boolean; + /** + * 辅助函数,用于沿外周线构建分段,以限制光的半径。 + */ + loadRectangleBoundaries(): void; + /** + * 助手:我们知道a段在b的前面吗?实现不反对称(也就是说,isSegmentInFrontOf(a, b) != (!isSegmentInFrontOf(b, a)))。 + * 另外要注意的是,在可见性算法中,它只需要在有限的一组情况下工作,我不认为它能处理所有的情况。 + * 见http://www.redblobgames.com/articles/visibility/segment-sorting.html + * @param a + * @param b + * @param relativeTo + */ + isSegmentInFrontOf(a: Segment, b: Segment, relativeTo: Vector2): boolean; + /** + * 返回略微缩短的向量:p * (1 - f) + q * f + * @param p + * @param q + * @param f + */ + static interpolate(p: Vector2, q: Vector2, f: number): Vector2; + /** + * 返回点是否在直线p1-p2的 "左边"。 + * @param p1 + * @param p2 + * @param point + */ + static isLeftOf(p1: Vector2, p2: Vector2, point: Vector2): boolean; + /** + * 处理片段,以便我们稍后对它们进行分类 + */ + updateSegments(): void; + } +} +declare module es { + interface ITimer { + context: any; + /** + * 调用stop以停止此计时器再次运行。这对非重复计时器没有影响。 + */ + stop(): any; + /** + * 将计时器的运行时间重置为0 + */ + reset(): any; + /** + * 返回投向T的上下文,作为方便 + */ + getContext(): T; + } +} +declare module es { + /** + * 私有类隐藏ITimer的实现 + */ + class Timer implements ITimer { + context: any; + _timeInSeconds: number; + _repeats: boolean; + _onTime: (timer: ITimer) => void; + _isDone: boolean; + _elapsedTime: number; + getContext(): T; + reset(): void; + stop(): void; + tick(): boolean; + initialize(timeInsSeconds: number, repeats: boolean, context: any, onTime: (timer: ITimer) => void): void; + /** + * 空出对象引用,以便在js需要时GC可以清理它们的引用 + */ + unload(): void; + } +} +declare module es { + /** + * 允许动作的延迟和重复执行 + */ + class TimerManager extends GlobalManager { + _timers: Timer[]; + update(): void; + /** + * 调度一个一次性或重复的计时器,该计时器将调用已传递的动作 + * @param timeInSeconds + * @param repeats + * @param context + * @param onTime + */ + schedule(timeInSeconds: number, repeats: boolean, context: any, onTime: (timer: ITimer) => void): Timer; + } +} diff --git a/source/bin/framework.d.ts b/source/bin/framework.d.ts index ac49d37b..94b14d9b 100644 --- a/source/bin/framework.d.ts +++ b/source/bin/framework.d.ts @@ -193,7 +193,8 @@ declare module es { * 当实体的位置改变时调用。这允许组件知道它们由于父实体的移动而移动了。 * @param comp */ - onEntityTransformChanged(comp: transform.Component): void; + onEntityTransformChanged(comp: ComponentTransform): void; + debugRender(batcher: IBatcher): void; /** *当父实体或此组件启用时调用 */ @@ -226,7 +227,11 @@ declare module es { /** * 每帧更新事件 */ - frameUpdated = 1 + frameUpdated = 1, + /** + * 当渲染发生时触发 + */ + renderChanged = 2 } } declare module es { @@ -306,7 +311,7 @@ declare module es { readonly worldInverseTransform: Matrix2D; readonly localToWorldTransform: Matrix2D; readonly worldToLocalTransform: Matrix2D; - onTransformChanged(comp: transform.Component): void; + onTransformChanged(comp: ComponentTransform): void; setParent(parent: Entity): any; setParent(parent: Transform): any; setPosition(x: number, y: number): this; @@ -359,6 +364,7 @@ declare module es { * 每帧进行调用进行更新组件 */ update(): void; + debugRender(batcher: IBatcher): void; /** * 创建组件的新实例。返回实例组件 * @param componentType @@ -417,6 +423,14 @@ declare module es { * 从实体中删除所有组件 */ removeAllComponents(): void; + tweenPositionTo(to: Vector2, duration?: number): ITween; + tweenLocalPositionTo(to: Vector2, duration?: number): ITween; + tweenScaleTo(to: Vector2, duration?: number): any; + tweenScaleTo(to: number, duration?: number): any; + tweenLocalScaleTo(to: Vector2, duration?: any): any; + tweenLocalScaleTo(to: number, duration?: any): any; + tweenRotationDegreesTo(to: number, duration?: number): TransformVector2Tween; + tweenLocalRotationDegreesTo(to: number, duration?: number): TransformVector2Tween; compareTo(other: Entity): number; equals(other: Entity): boolean; getHashCode(): number; @@ -438,6 +452,10 @@ declare module es { static readonly one: Vector2; static readonly unitX: Vector2; static readonly unitY: Vector2; + static readonly up: Vector2; + static readonly down: Vector2; + static readonly left: Vector2; + static readonly right: Vector2; /** * * @param value1 @@ -450,43 +468,13 @@ declare module es { * @param value2 */ static divide(value1: Vector2, value2: Vector2): Vector2; - /** - * - * @param value1 - * @param value2 - */ - static multiply(value1: Vector2, value2: Vector2): Vector2; - /** - * - * @param value1 - * @param value2 - * @returns - */ - static multiplyScaler(value1: Vector2, value2: number): Vector2; - /** - * - * @param value1 - * @param value2 - */ - static subtract(value1: Vector2, value2: Vector2): Vector2; - /** - * 创建一个新的Vector2 - * 它包含来自另一个向量的标准化值。 - * @param value - */ - static normalize(value: Vector2): Vector2; - /** - * 返回两个向量的点积 - * @param value1 - * @param value2 - */ - static dot(value1: Vector2, value2: Vector2): number; + static divideScaler(value1: Vector2, value2: number): Vector2; /** * 返回两个向量之间距离的平方 * @param value1 * @param value2 */ - static distanceSquared(value1: Vector2, value2: Vector2): number; + static sqrDistance(value1: Vector2, value2: Vector2): number; /** * 将指定的值限制在一个范围内 * @param value1 @@ -528,7 +516,7 @@ declare module es { * @param value2 * @returns 两个向量之间的距离 */ - static distance(value1: Vector2, value2: Vector2): number; + static distance(vec1: Vector2, vec2: Vector2): number; /** * 返回两个向量之间的角度,单位是度数 * @param from @@ -556,16 +544,20 @@ declare module es { * @returns */ static smoothStep(value1: Vector2, value2: Vector2, amount: number): Vector2; + setTo(x: number, y: number): void; + negate(): Vector2; /** * * @param value */ - add(value: Vector2): Vector2; + add(v: Vector2): Vector2; + addEqual(v: Vector2): Vector2; /** * * @param value */ divide(value: Vector2): Vector2; + divideScaler(value: number): Vector2; /** * * @param value @@ -582,13 +574,24 @@ declare module es { * @param value 要减去的Vector2 * @returns 当前Vector2 */ - subtract(value: Vector2): this; + sub(value: Vector2): Vector2; + subEqual(v: Vector2): Vector2; + dot(v: Vector2): number; + /** + * + * @param size + * @returns + */ + scale(size: number): Vector2; + scaleEqual(size: number): Vector2; + transform(matrix: Matrix2D): Vector2; + normalize(): Vector2; /** * 将这个Vector2变成一个方向相同的单位向量 */ - normalize(): void; - /** 返回它的长度 */ - length(): number; + normalizeEqual(): Vector2; + magnitude(): number; + distance(v?: Vector2): number; /** * 返回该Vector2的平方长度 * @returns 这个Vector2的平方长度 @@ -609,7 +612,7 @@ declare module es { * @param other 要比较的对象 * @returns 如果实例相同true 否则false */ - equals(other: Vector2 | object): boolean; + equals(other: Vector2, tolerance?: number): boolean; isValid(): boolean; /** * 创建一个新的Vector2,其中包含来自两个向量的最小值 @@ -635,21 +638,21 @@ declare module es { * @returns */ static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number): Vector2; + static unsignedAngle(from: Vector2, to: Vector2, round?: boolean): number; clone(): Vector2; } } declare module es { /** 场景 */ class Scene { - /** - * 这个场景中的实体列表 - */ + camera: ICamera; + /** 这个场景中的实体列表 */ readonly entities: EntityList; - /** - * 管理所有实体处理器 - */ + readonly renderableComponents: RenderableComponentList; + /** 管理所有实体处理器 */ readonly entityProcessors: EntityProcessorList; readonly _sceneComponents: SceneComponent[]; + _renderers: Renderer[]; readonly identifierPool: IdentifierPool; private _didSceneBegin; constructor(); @@ -670,6 +673,10 @@ declare module es { begin(): void; end(): void; update(): void; + render(): void; + addRenderer(renderer: T): T; + getRenderer(type: new (...args: any[]) => T): T; + removeRenderer(renderer: Renderer): void; /** * 向组件列表添加并返回SceneComponent * @param component @@ -753,14 +760,12 @@ declare module es { getEntityProcessor(type: new (...args: any[]) => T): T; } } -declare module transform { - enum Component { +declare module es { + enum ComponentTransform { position = 0, scale = 1, rotation = 2 } -} -declare module es { enum DirtyType { clean = 0, positionDirty = 1, @@ -1074,6 +1079,7 @@ declare module es { * @param value */ setGlue(value: number): ArcadeRigidbody; + setVelocity(velocity: Vector2): ArcadeRigidbody; /** * 用刚体的质量给刚体加上一个瞬间的力脉冲。力是一个加速度,单位是每秒像素每秒。将力乘以100000,使数值使用更合理 * @param force @@ -1099,7 +1105,139 @@ declare module es { * @param minimumTranslationVector * @param responseVelocity */ - calculateResponseVelocity(relativeVelocity: Vector2, minimumTranslationVector: Vector2, responseVelocity?: Vector2): void; + calculateResponseVelocity(relativeVelocity: Vector2, minimumTranslationVector: Vector2): Vector2; + } +} +declare module es { + class CharacterCollisionState2D { + right: boolean; + left: boolean; + above: boolean; + below: boolean; + becameGroundedThisFrame: boolean; + wasGroundedLastFrame: boolean; + movingDownSlope: boolean; + slopeAngle: number; + hasCollision(): boolean; + reset(): void; + toString(): string; + } + class CharacterController implements ITriggerListener { + onControllerCollidedEvent: ObservableT; + onTriggerEnterEvent: ObservableT; + onTriggerExitEvent: ObservableT; + /** + * 如果为 true,则在垂直移动单帧时将忽略平台的一种方式 + */ + ignoreOneWayPlatformsTime: number; + supportSlopedOneWayPlatforms: boolean; + ignoredColliders: Set; + /** + * 定义距离碰撞射线的边缘有多远。 + * 如果使用 0 范围进行投射,则通常会导致不需要的光线击中(例如,直接从表面水平投射的足部碰撞器可能会导致击中) + */ + skinWidth: number; + /** + * CC2D 可以爬升的最大坡度角 + */ + slopeLimit: number; + /** + * 构成跳跃的帧之间垂直运动变化的阈值 + */ + jumpingThreshold: number; + /** + * 基于斜率乘以速度的曲线(负 = 下坡和正 = 上坡) + */ + slopeSpeedMultiplier: AnimCurve; + totalHorizontalRays: number; + totalVerticalRays: number; + collisionState: CharacterCollisionState2D; + velocity: Vector2; + readonly isGrounded: boolean; + readonly raycastHitsThisFrame: RaycastHit[]; + constructor(player: Entity, skinWidth?: number, platformMask?: number, onewayPlatformMask?: number, triggerMask?: number); + onTriggerEnter(other: Collider, local: Collider): void; + onTriggerExit(other: Collider, local: Collider): void; + /** + * 尝试将角色移动到位置 + deltaMovement。 任何挡路的碰撞器都会在遇到时导致运动停止 + * @param deltaMovement + * @param deltaTime + */ + move(deltaMovement: Vector2, deltaTime: number): void; + /** + * 直接向下移动直到接地 + * @param maxDistance + */ + warpToGrounded(maxDistance?: number): void; + /** + * 这应该在您必须在运行时修改 BoxCollider2D 的任何时候调用。 + * 它将重新计算用于碰撞检测的光线之间的距离。 + * 它也用于 skinWidth setter,以防在运行时更改。 + */ + recalculateDistanceBetweenRays(): void; + /** + * 将 raycastOrigins 重置为由 skinWidth 插入的框碰撞器的当前范围。 + * 插入它是为了避免从直接接触另一个碰撞器的位置投射光线,从而导致不稳定的法线数据。 + */ + private primeRaycastOrigins; + /** + * 我们必须在这方面使用一些技巧。 + * 光线必须从我们的碰撞器(skinWidth)内部的一小段距离投射,以避免零距离光线会得到错误的法线。 + * 由于这个小偏移,我们必须增加光线距离 skinWidth 然后记住在实际移动玩家之前从 deltaMovement 中删除 skinWidth + * @param deltaMovement + * @returns + */ + private moveHorizontally; + private moveVertically; + /** + * 检查 BoxCollider2D 下的中心点是否存在坡度。 + * 如果找到一个,则调整 deltaMovement 以便玩家保持接地,并考虑slopeSpeedModifier 以加快移动速度。 + * @param deltaMovement + * @returns + */ + private handleVerticalSlope; + /** + * 如果我们要上坡,则处理调整 deltaMovement + * @param deltaMovement + * @param angle + * @returns + */ + private handleHorizontalSlope; + private _player; + private _collider; + private _skinWidth; + private _triggerHelper; + /** + * 这用于计算为检查坡度而投射的向下光线。 + * 我们使用有点随意的值 75 度来计算检查斜率的射线的长度。 + */ + private _slopeLimitTangent; + private readonly kSkinWidthFloatFudgeFactor; + /** + * 我们的光线投射原点角的支架(TR、TL、BR、BL) + */ + private _raycastOrigins; + /** + * 存储我们在移动过程中命中的光线投射 + */ + private _raycastHit; + /** + * 存储此帧发生的任何光线投射命中。 + * 我们必须存储它们,以防我们遇到水平和垂直移动的碰撞,以便我们可以在设置所有碰撞状态后发送事件 + */ + private _raycastHitsThisFrame; + private _verticalDistanceBetweenRays; + private _horizontalDistanceBetweenRays; + /** + * 我们使用这个标志来标记我们正在爬坡的情况,我们修改了 delta.y 以允许爬升。 + * 原因是,如果我们到达斜坡的尽头,我们可以进行调整以保持接地 + */ + private _isGoingUpSlope; + private _isWarpingToGround; + private platformMask; + private triggerMask; + private oneWayPlatformMask; + private readonly rayOriginSkinMutiplier; } } declare module es { @@ -1176,7 +1314,9 @@ declare module es { } } declare module es { - class Collider extends Component { + abstract class Collider extends Component { + static readonly lateSortOrder: number; + castSortOrder: number; /** * 对撞机的基本形状 */ @@ -1248,7 +1388,7 @@ declare module es { setShouldColliderScaleAndRotateWithTransform(shouldColliderScaleAndRotationWithTransform: boolean): Collider; onAddedToEntity(): void; onRemovedFromEntity(): void; - onEntityTransformChanged(comp: transform.Component): void; + onEntityTransformChanged(comp: ComponentTransform): void; onEnabled(): void; onDisabled(): void; /** @@ -1300,7 +1440,7 @@ declare module es { * @param width * @param height */ - constructor(x: number, y: number, width: number, height: number); + constructor(x?: number, y?: number, width?: number, height?: number); width: number; height: number; /** @@ -1319,6 +1459,7 @@ declare module es { * @param height */ setHeight(height: number): void; + debugRender(batcher: IBatcher): void; toString(): string; } } @@ -1331,13 +1472,14 @@ declare module es { * * @param radius */ - constructor(radius: number); + constructor(radius?: number); radius: number; /** * 设置圆的半径 * @param radius */ setRadius(radius: number): CircleCollider; + debugRender(batcher: IBatcher): void; toString(): string; } } @@ -1353,6 +1495,42 @@ declare module es { constructor(points: Vector2[]); } } +declare module es { + interface IRenderable { + enabled: boolean; + renderLayer: number; + isVisibleFromCamera(camera: ICamera): boolean; + render(batcher: IBatcher, camera: ICamera): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + abstract class RenderableComponent extends es.Component implements IRenderable { + getwidth(): number; + getheight(): number; + protected _bounds: es.Rectangle; + getbounds(): es.Rectangle; + readonly bounds: Rectangle; + protected _areBoundsDirty: boolean; + color: Color; + renderLayer: number; + protected _renderLayer: number; + onEntityTransformChanged(comp: ComponentTransform): void; + localOffset: es.Vector2; + setLocalOffset(offset: es.Vector2): this; + isVisible: boolean; + debugRenderEnabled: boolean; + protected _isVisible: boolean; + protected _localOffset: es.Vector2; + abstract render(batcher: IBatcher, camera: ICamera): void; + protected onBecameVisible(): void; + protected onBecameInvisible(): void; + setRenderLayer(renderLayer: number): RenderableComponent; + isVisibleFromCamera(cam: ICamera): boolean; + debugRender(batcher: IBatcher): void; + tweenColorTo(to: Color, duration: number): RenderableColorTween; + } +} declare module es { interface Map { clear(): void; @@ -1698,9 +1876,10 @@ declare module es { */ getComponents(typeName: any, components?: any[]): any[]; update(): void; - onEntityTransformChanged(comp: transform.Component): void; + onEntityTransformChanged(comp: ComponentTransform): void; onEntityEnabled(): void; onEntityDisabled(): void; + debugRender(batcher: IBatcher): void; } } declare module es { @@ -1892,6 +2071,24 @@ declare module es { one(...types: any[]): this; } } +declare module es { + class RenderableComponentList { + private _components; + private _componentsByRenderLayer; + private _unsortedRenderLayers; + private _componentsNeedSort; + readonly count: number; + get(index: number): IRenderable; + add(component: IRenderable): void; + remove(component: IRenderable): void; + updateRenderableRenderLayer(component: IRenderable, oldRenderLayer: number, newRenderLayer: number): void; + setRenderLayerNeedsComponentSort(renderLayer: number): void; + setNeedsComponentSort(): void; + private addToRenderLayerList; + componentsWithRenderLayer(renderLayer: number): IRenderable[]; + updateLists(): void; + } +} declare class StringUtils { /** * 特殊符号字符串 @@ -2091,6 +2288,280 @@ declare module es { static workerMessage(worker: Worker): (...message: any[]) => Promise<{}>; } } +declare module es { + class Graphics { + static instance: Graphics; + batcher: IBatcher; + constructor(batcher: IBatcher); + } +} +declare module es { + class Color { + /** + * 红色通道 + */ + r: number; + /** + * 绿色通道 + */ + g: number; + /** + * 蓝色通道 + */ + b: number; + /** + * 透明度通道 (仅0-1之间) + */ + a: number; + /** + * 色调 + */ + h: number; + /** + * 饱和 + */ + s: number; + /** + * 亮度 + */ + l: number; + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + constructor(r: number, g: number, b: number, a?: number); + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + static fromRGB(r: number, g: number, b: number, a?: number): Color; + /** + * 从十六进制字符串创建一个新的 Color 实例 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + static createFromHex(hex: string): Color; + /** + * 从 hsl 值创建一个新的 Color 实例 + * + * @param h 色调表示 [0-1] + * @param s 饱和度表示为 [0-1] + * @param l 亮度表示 [0-1] + * @param a 透明度表示 [0-1] + */ + static fromHSL(h: number, s: number, l: number, a?: number): Color; + /** + * 将当前颜色调亮指定的量 + * + * @param factor + */ + lighten(factor?: number): Color; + /** + * 将当前颜色变暗指定的量 + * + * @param factor + */ + darken(factor?: number): Color; + /** + * 使当前颜色饱和指定的量 + * + * @param factor + */ + saturate(factor?: number): Color; + /** + * 按指定量降低当前颜色的饱和度 + * + * @param factor + */ + desaturate(factor?: number): Color; + /** + * 将一种颜色乘以另一种颜色,得到更深的颜色 + * + * @param color + */ + mulitiply(color: Color): Color; + /** + * 筛选另一种颜色,导致颜色较浅 + * + * @param color + */ + screen(color: Color): Color; + /** + * 反转当前颜色 + */ + invert(): Color; + /** + * 将当前颜色与另一个颜色平均 + * + * @param color + */ + average(color: Color): Color; + /** + * 返回颜色的 CSS 字符串表示形式。 + * + * @param format + */ + toString(format?: 'rgb' | 'hsl' | 'hex'): string; + /** + * 返回颜色分量的十六进制值 + * @param c + * @see https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + */ + private _componentToHex; + /** + *返回颜色的十六进制表示 + */ + toHex(): string; + /** + * 从十六进制字符串设置颜色 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + fromHex(hex: string): void; + /** + * 返回颜色的 RGBA 表示 + */ + toRGBA(): string; + /** + * 返回颜色的 HSLA 表示 + */ + toHSLA(): string; + /** + * 返回颜色的 CSS 字符串表示形式 + */ + fillStyle(): string; + /** + * 返回当前颜色的克隆 + */ + clone(): Color; + /** + * Black (#000000) + */ + static Black: Color; + /** + * White (#FFFFFF) + */ + static White: Color; + /** + * Gray (#808080) + */ + static Gray: Color; + /** + * Light gray (#D3D3D3) + */ + static LightGray: Color; + /** + * Dark gray (#A9A9A9) + */ + static DarkGray: Color; + /** + * Yellow (#FFFF00) + */ + static Yellow: Color; + /** + * Orange (#FFA500) + */ + static Orange: Color; + /** + * Red (#FF0000) + */ + static Red: Color; + /** + * Vermillion (#FF5B31) + */ + static Vermillion: Color; + /** + * Rose (#FF007F) + */ + static Rose: Color; + /** + * Magenta (#FF00FF) + */ + static Magenta: Color; + /** + * Violet (#7F00FF) + */ + static Violet: Color; + /** + * Blue (#0000FF) + */ + static Blue: Color; + /** + * Azure (#007FFF) + */ + static Azure: Color; + /** + * Cyan (#00FFFF) + */ + static Cyan: Color; + /** + * Viridian (#59978F) + */ + static Viridian: Color; + /** + * Green (#00FF00) + */ + static Green: Color; + /** + * Chartreuse (#7FFF00) + */ + static Chartreuse: Color; + /** + * Transparent (#FFFFFF00) + */ + static Transparent: Color; + } +} +declare module es { + interface IBatcher { + begin(cam: ICamera): any; + end(): any; + drawPoints(points: Vector2[], color: Color, thickness?: number): any; + drawPolygon(poisition: Vector2, points: Vector2[], color: Color, closePoly: boolean, thickness?: number): any; + drawHollowRect(x: number, y: number, width: number, height: number, color: Color, thickness?: number): any; + drawCircle(position: Vector2, raidus: number, color: Color, thickness?: number): any; + drawCircleLow(position: es.Vector2, radius: number, color: Color, thickness?: number, resolution?: number): any; + drawRect(x: number, y: number, width: number, height: number, color: Color): any; + drawLine(start: Vector2, end: Vector2, color: Color, thickness: number): any; + drawPixel(position: Vector2, color: Color, size?: number): any; + } +} +declare module es { + interface ICamera extends Component { + bounds: Rectangle; + } +} +declare module es { + abstract class Renderer { + camera: ICamera; + readonly renderOrder: number; + shouldDebugRender: boolean; + protected renderDirty: boolean; + constructor(renderOrder: number, camera: ICamera); + onAddedToScene(scene: es.Scene): void; + unload(): void; + protected beginRender(cam: ICamera): void; + protected endRender(): void; + protected onRenderChanged(): void; + abstract render(scene: Scene): void; + protected renderAfterStateCheck(renderable: IRenderable, cam: ICamera): void; + protected debugRender(scene: Scene): void; + } +} +declare module es { + class DefaultRenderer extends Renderer { + constructor(renderOrder?: number, camera?: ICamera); + render(scene: Scene): void; + } +} declare module es { /** * 三次方和二次方贝塞尔帮助器(cubic and quadratic bezier helper) @@ -2163,7 +2634,10 @@ declare module es { * 在这个过程中,t被修改为在曲线段的范围内。 * @param t */ - pointIndexAtTime(t: Ref): number; + pointIndexAtTime(t: number): { + time: number; + range: number; + }; /** * 设置一个控制点,考虑到这是否是一个共享点,如果是,则适当调整 * @param index @@ -2331,6 +2805,7 @@ declare module es { */ static isPowerOfTwo(value: number): boolean; static lerp(from: number, to: number, t: number): number; + static betterLerp(a: number, b: number, t: number, epsilon: number): number; /** * 使度数的角度在a和b之间 * 用于处理360度环绕 @@ -2452,6 +2927,11 @@ declare module es { * @returns */ static approachAngle(start: number, end: number, shift: number): number; + /** + * 将 Vector 投影到另一个 Vector 上 + * @param other + */ + static project(self: Vector2, other: Vector2): Vector2; /** * 通过将偏移量(全部以弧度为单位)夹住结果并选择最短路径,起始角度朝向终止角度。 * 起始值可以小于或大于终止值。 @@ -2497,6 +2977,7 @@ declare module es { * @param length */ static repeat(t: number, length: number): number; + static floorToInt(f: number): number; /** * 将值绕一圈移动的助手 * @param position @@ -2567,6 +3048,22 @@ declare module es { * @returns */ static isValid(x: number): boolean; + static smoothDamp(current: number, target: number, currentVelocity: number, smoothTime: number, maxSpeed: number, deltaTime: number): { + value: number; + currentVelocity: number; + }; + static smoothDampVector(current: Vector2, target: Vector2, currentVelocity: Vector2, smoothTime: number, maxSpeed: number, deltaTime: number): Vector2; + /** + * 将值(在 leftMin - leftMax 范围内)映射到 rightMin - rightMax 范围内的值 + * @param value + * @param leftMin + * @param leftMax + * @param rightMin + * @param rightMax + * @returns + */ + static mapMinMax(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: any): number; + static fromAngle(angle: number): Vector2; } } declare module es { @@ -2574,6 +3071,8 @@ declare module es { * 代表右手4x4浮点矩阵,可以存储平移、比例和旋转信息 */ class Matrix { + private static identity; + static readonly Identity: Matrix; m11: number; m12: number; m13: number; @@ -2590,6 +3089,7 @@ declare module es { m42: number; m43: number; m44: number; + constructor(m11?: any, m12?: any, m13?: any, m14?: any, m21?: any, m22?: any, m23?: any, m24?: any, m31?: any, m32?: any, m33?: any, m34?: any, m41?: any, m42?: any, m43?: any, m44?: any); /** * 为自定义的正交视图创建一个新的投影矩阵 * @param left @@ -2599,6 +3099,8 @@ declare module es { * @param result */ static createOrthographicOffCenter(left: number, right: number, bottom: number, top: number, zNearPlane: number, zFarPlane: number, result?: Matrix): void; + static createTranslation(position: Vector2, result: Matrix): void; + static createRotationZ(radians: number, result: Matrix): void; /** * 创建一个新的矩阵,其中包含两个矩阵的乘法。 * @param matrix1 @@ -2623,6 +3125,8 @@ declare module es { * 返回标识矩阵 */ static readonly identity: Matrix2D; + setIdentity(): Matrix2D; + setValues(m11: number, m12: number, m21: number, m22: number, m31: number, m32: number): Matrix2D; /** * 储存在该矩阵中的位置 */ @@ -2639,33 +3143,26 @@ declare module es { * 储存在这个矩阵中的缩放 */ scale: Vector2; - /** - * 构建一个矩阵 - * @param m11 - * @param m12 - * @param m21 - * @param m22 - * @param m31 - * @param m32 - */ - constructor(m11: number, m12: number, m21: number, m22: number, m31: number, m32: number); /** * 创建一个新的围绕Z轴的旋转矩阵2D * @param radians */ - static createRotation(radians: number): Matrix2D; + static createRotation(radians: number, result: Matrix2D): void; + static createRotationOut(radians: number, result: Matrix2D): void; /** * 创建一个新的缩放矩阵2D * @param xScale * @param yScale */ - static createScale(xScale: number, yScale: number): Matrix2D; + static createScale(xScale: number, yScale: number, result: Matrix2D): void; + static createScaleOut(xScale: number, yScale: number, result: Matrix2D): void; /** * 创建一个新的平移矩阵2D * @param xPosition * @param yPosition */ - static createTranslation(xPosition: number, yPosition: number): Matrix2D; + static createTranslation(xPosition: number, yPosition: number, result: Matrix2D): Matrix2D; + static createTranslationOut(position: Vector2, result: Matrix2D): void; static invert(matrix: Matrix2D): Matrix2D; /** * 创建一个新的matrix, 它包含两个矩阵的和。 @@ -2675,6 +3172,7 @@ declare module es { substract(matrix: Matrix2D): Matrix2D; divide(matrix: Matrix2D): Matrix2D; multiply(matrix: Matrix2D): Matrix2D; + static multiply(matrix1: Matrix2D, matrix2: Matrix2D, result: Matrix2D): void; determinant(): number; /** * 创建一个新的Matrix2D,包含指定矩阵中的线性插值。 @@ -2833,7 +3331,10 @@ declare module es { * @param value 另一个用于测试的矩形 */ intersects(value: Rectangle): boolean; - rayIntersects(ray: Ray2D, distance: Ref): boolean; + rayIntersects(ray: Ray2D): { + intersected: boolean; + distance: number; + }; /** * 获取所提供的矩形是否在此矩形的边界内 * @param value @@ -3038,9 +3539,10 @@ declare module es { */ centroid: Vector2; constructor(collider?: Collider, fraction?: number, distance?: number, point?: Vector2, normal?: Vector2); - setValues(collider: Collider, fraction: number, distance: number, point: Vector2): void; - setValuesNonCollider(fraction: number, distance: number, point: Vector2, normal: Vector2): void; + setAllValues(collider: Collider, fraction: number, distance: number, point: Vector2, normal: Vector2): void; + setValues(fraction: number, distance: number, point: Vector2, normal: Vector2): void; reset(): void; + clone(): RaycastHit; toString(): string; } } @@ -3061,6 +3563,7 @@ declare module es { * 在碰撞器中开始的射线/直线是否强制转换检测到那些碰撞器 */ static raycastsStartInColliders: boolean; + static debugRender: boolean; /** * 我们保留它以避免在每次raycast发生时分配它 */ @@ -3074,6 +3577,7 @@ declare module es { * 从SpatialHash中移除所有碰撞器 */ static clear(): void; + static debugDraw(secondsToDisplay: any): void; /** * 检查是否有对撞机落在一个圆形区域内。返回遇到的第一个对撞机 * @param center @@ -3088,13 +3592,13 @@ declare module es { * @param results * @param layerMask */ - static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask?: number): number; + static overlapCircleAll(center: Vector2, radius: number, results: Collider[], layerMask?: number): number; /** * 返回所有碰撞器与边界相交的碰撞器。bounds。请注意,这是一个broadphase检查,所以它只检查边界,不做单个碰撞到碰撞器的检查! * @param rect * @param layerMask */ - static boxcastBroadphase(rect: Rectangle, layerMask?: number): Set; + static boxcastBroadphase(rect: Rectangle, layerMask?: number): Collider[]; /** * 返回所有被边界交错的碰撞器,但不包括传入的碰撞器(self)。 * 如果你想为其他查询自己创建扫描边界,这个方法很有用 @@ -3102,13 +3606,13 @@ declare module es { * @param rect * @param layerMask */ - static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask?: number): Set; + static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask?: number): Collider[]; /** * 返回所有边界与 collider.bounds 相交的碰撞器,但不包括传入的碰撞器(self) * @param collider * @param layerMask */ - static boxcastBroadphaseExcludingSelfNonRect(collider: Collider, layerMask?: number): Set; + static boxcastBroadphaseExcludingSelfNonRect(collider: Collider, layerMask?: number): Collider[]; /** * 返回所有被 collider.bounds 扩展为包含 deltaX/deltaY 的碰撞器,但不包括传入的碰撞器(self) * @param collider @@ -3116,7 +3620,7 @@ declare module es { * @param deltaY * @param layerMask */ - static boxcastBroadphaseExcludingSelfDelta(collider: Collider, deltaX: number, deltaY: number, layerMask?: number): Set; + static boxcastBroadphaseExcludingSelfDelta(collider: Collider, deltaX: number, deltaY: number, layerMask?: number): Collider[]; /** * 将对撞机添加到物理系统中 * @param collider @@ -3138,7 +3642,7 @@ declare module es { * @param end * @param layerMask */ - static linecast(start: Vector2, end: Vector2, layerMask?: number): RaycastHit; + static linecast(start: Vector2, end: Vector2, layerMask?: number, ignoredColliders?: Set): RaycastHit; /** * 通过空间散列强制执行一行,并用该行命中的任何碰撞器填充hits数组 * @param start @@ -3146,7 +3650,7 @@ declare module es { * @param hits * @param layerMask */ - static linecastAll(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask?: number): number; + static linecastAll(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask?: number, ignoredColliders?: Set): number; /** * 检查是否有对撞机落在一个矩形区域中 * @param rect @@ -3167,10 +3671,13 @@ declare module es { * 不是真正的射线(射线只有开始和方向),作为一条线和射线。 */ class Ray2D { - start: Vector2; - end: Vector2; - direction: Vector2; - constructor(position: Vector2, end: Vector2); + readonly start: Vector2; + readonly direction: Vector2; + readonly end: Vector2; + constructor(pos: Vector2, end: Vector2); + private _start; + private _direction; + private _end; } } declare module es { @@ -3196,7 +3703,7 @@ declare module es { /** * 保存所有数据的字典 */ - _cellDict: NumberDictionary; + _cellDict: NumberDictionary; /** * 用于返回冲突信息的共享HashSet */ @@ -3218,13 +3725,15 @@ declare module es { */ removeWithBruteForce(obj: Collider): void; clear(): void; + debugDraw(secondsToDisplay: number): void; + private debugDrawCellDetails; /** * 返回边框与单元格相交的所有对象 * @param bounds * @param excludeCollider * @param layerMask */ - aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Set; + aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Collider[]; /** * 通过空间散列投掷一条线,并将该线碰到的任何碰撞器填入碰撞数组 * https://github.com/francisengelmann/fast_voxel_traversal/blob/master/main.cpp @@ -3234,7 +3743,7 @@ declare module es { * @param hits * @param layerMask */ - linecast(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number): number; + linecast(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number, ignoredColliders: Set): number; /** * 获取所有在指定矩形范围内的碰撞器 * @param rect @@ -3265,20 +3774,16 @@ declare module es { */ cellAtPosition(x: number, y: number, createCellIfEmpty?: boolean): Collider[]; } - /** - * 包装一个Unit32,列表碰撞器字典 - * 它的主要目的是将int、int x、y坐标散列到单个Uint32键中,使用O(1)查找。 - */ - class NumberDictionary { - _store: Map; - add(x: number, y: number, list: Collider[]): void; + class NumberDictionary { + _store: Map; + add(x: number, y: number, list: T[]): void; /** * 使用蛮力方法从字典存储列表中移除碰撞器 * @param obj */ - remove(obj: Collider): void; - tryGetValue(x: number, y: number): Collider[]; - getKey(x: number, y: number): number; + remove(obj: T): void; + tryGetValue(x: number, y: number): T[]; + getKey(x: number, y: number): string; /** * 清除字典数据 */ @@ -3293,7 +3798,8 @@ declare module es { _cellHits: RaycastHit[]; _ray: Ray2D; _layerMask: number; - start(ray: Ray2D, hits: RaycastHit[], layerMask: number): void; + private _ignoredColliders; + start(ray: Ray2D, hits: RaycastHit[], layerMask: number, ignoredColliders: Set): void; /** * 如果hits数组被填充,返回true。单元格不能为空! * @param cellX @@ -3356,6 +3862,7 @@ declare module es { * @param isBox */ constructor(points: Vector2[], isBox?: boolean); + create(vertCount: number, radius: number): void; _edgeNormals: Vector2[]; /** * 边缘法线用于SAT碰撞检测。缓存它们用于避免squareRoots @@ -3408,7 +3915,11 @@ declare module es { * @param distanceSquared * @param edgeNormal */ - static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2, distanceSquared: Ref, edgeNormal: Vector2): Vector2; + static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { + distanceSquared: number; + edgeNormal: Vector2; + closestPoint: Vector2; + }; /** * 旋转原始点并复制旋转的值到旋转的点 * @param radians @@ -3464,6 +3975,7 @@ declare module es { overlaps(other: Shape): any; collidesWithShape(other: Shape, result: CollisionResult): boolean; collidesWithLine(start: Vector2, end: Vector2, hit: RaycastHit): boolean; + getPointAlongEdge(angle: number): Vector2; /** * 获取所提供的点是否在此范围内 * @param point @@ -3490,18 +4002,20 @@ declare module es { * 不是所有冲突类型都使用!在依赖这个字段之前,请检查ShapeCollisions切割类! */ point: Vector2; + reset(): void; + cloneTo(cr: CollisionResult): void; /** * 改变最小平移向量,如果没有相同方向上的运动,它将移除平移的x分量。 * @param deltaMovement */ - removeHorizontal(deltaMovement: Vector2): void; - invertResult(): this; + removeHorizontalTranslation(deltaMovement: Vector2): void; + invertResult(): void; toString(): string; } } declare module es { class RealtimeCollisions { - static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: Ref): boolean; + static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: number): boolean; /** * 支持函数,返回索引为n的矩形vert * @param b @@ -3533,6 +4047,7 @@ declare module es { } declare module es { class ShapeCollisionsCircle { + static circleToCircleCast(first: Circle, second: Circle, deltaMovement: Vector2, hit: RaycastHit): boolean; static circleToCircle(first: Circle, second: Circle, result?: CollisionResult): boolean; /** * 适用于中心在框内的圆,也适用于与框外中心重合的圆。 @@ -3575,7 +4090,10 @@ declare module es { * @param min * @param max */ - static getInterval(axis: Vector2, polygon: Polygon, min: Ref, max: Ref): void; + static getInterval(axis: Vector2, polygon: Polygon): { + min: number; + max: number; + }; /** * 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的 * @param minA @@ -3583,7 +4101,768 @@ declare module es { * @param minB * @param maxB */ - static intervalDistance(minA: number, maxA: number, minB: number, maxB: any): number; + static intervalDistance(minA: number, maxA: number, minB: number, maxB: number): number; + } +} +declare module es { + class Particle { + position: Vector2; + lastPosition: Vector2; + mass: number; + radius: number; + collidesWithColliders: boolean; + isPinned: boolean; + acceleration: Vector2; + pinnedPosition: Vector2; + constructor(position: { + x: number; + y: number; + }); + applyForce(force: Vector2): void; + pin(): Particle; + pinTo(position: Vector2): Particle; + unpin(): Particle; + } +} +declare module es { + class VerletWorld { + gravity: Vector2; + constraintIterations: number; + maximumStepIterations: number; + simulationBounds: Rectangle; + allowDragging: boolean; + selectionRadiusSquared: number; + _draggedParticle: Particle; + _composites: Composite[]; + static _colliders: Collider[]; + _tempCircle: Circle; + _leftOverTime: number; + _fixedDeltaTime: number; + _iterationSteps: number; + _fixedDeltaTimeSq: number; + onHandleDrag: Function; + constructor(simulationBounds?: Rectangle); + update(): void; + constrainParticleToBounds(p: Particle): void; + handleCollisions(p: Particle, collidesWithLayers: number): void; + updateTiming(): void; + addComposite(composite: T): T; + removeComposite(composite: Composite): void; + handleDragging(): void; + getNearestParticle(position: Vector2): Particle; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + class Composite { + friction: Vector2; + drawParticles: boolean; + drawConstraints: boolean; + collidesWithLayers: number; + particles: Particle[]; + _constraints: Constraint[]; + addParticle(particle: Particle): Particle; + removeParticle(particle: Particle): void; + removeAll(): void; + addConstraint(constraint: T): T; + removeConstraint(constraint: Constraint): void; + applyForce(force: Vector2): void; + solveConstraints(): void; + updateParticles(deltaTimeSquared: number, gravity: Vector2): void; + handleConstraintCollisions(): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + class Ball extends Composite { + constructor(position: Vector2, radius?: number); + } +} +declare module es { + class VerletBox extends es.Composite { + constructor(center: es.Vector2, width: number, height: number, borderStiffness?: number, diagonalStiffness?: number); + } +} +declare module es { + class Cloth extends Composite { + constructor(topLeftPosition: Vector2, width: number, height: number, segments?: number, stiffness?: number, tearSensitivity?: number, connectHorizontalParticles?: boolean); + } +} +declare module es { + class LineSegments extends Composite { + constructor(vertices: Vector2[], stiffness: number); + pinParticleAtIndex(index: number): LineSegments; + } +} +declare module es { + class Ragdoll extends Composite { + constructor(x: number, y: number, bodyHeight: number); + } +} +declare module es { + class Tire extends Composite { + constructor(origin: Vector2, radius: number, segments: number, spokeStiffness?: number, treadStiffness?: number); + } +} +declare module es { + abstract class Constraint { + composite: Composite; + collidesWithColliders: boolean; + abstract solve(): void; + handleCollisions(collidesWithLayers: number): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + class AngleConstraint extends Constraint { + stiffness: number; + angleInRadius: number; + _particleA: Particle; + _centerParticle: Particle; + _particleC: Particle; + constructor(a: Particle, center: Particle, c: Particle, stiffness: number); + angleBetweenParticles(): number; + solve(): void; + } +} +declare module es { + class DistanceConstraint extends Constraint { + stiffness: number; + restingDistance: number; + tearSensitivity: number; + shouldApproximateCollisionsWithPoints: boolean; + totalPointsToApproximateCollisionsWith: number; + _particleOne: Particle; + _particleTwo: Particle; + static _polygon: Polygon; + constructor(first: Particle, second: Particle, stiffness: number, distance?: number); + static create(a: Particle, center: Particle, c: Particle, stiffness: number, angleInDegrees: number): DistanceConstraint; + setTearSensitivity(tearSensitivity: number): this; + setCollidesWithColliders(collidesWithColliders: boolean): this; + setShouldApproximateCollisionsWithPoints(shouldApproximateCollisionsWithPoints: boolean): this; + solve(): void; + handleCollisions(collidesWithLayers: number): void; + approximateCollisionsWithPoints(collidesWithLayers: number): void; + preparePolygonForCollisionChecks(midPoint: Vector2): void; + debugRender(batcher: IBatcher): void; + } +} +declare module es { + /** + * AbstractTweenable作为你可能想做的任何可以执行的自定义类的基础。 + * 这些类不同于ITweens,因为他们没有实现ITweenT接口。 + * 它只是说一个AbstractTweenable不仅仅是将一个值从开始移动到结束。 + * 它可以做任何需要每帧执行的事情。 + */ + abstract class AbstractTweenable implements ITweenable { + protected _isPaused: boolean; + /** + * abstractTweenable在完成后往往会被保留下来。 + * 这个标志可以让它们在内部知道自己当前是否被TweenManager盯上了,以便在必要时可以重新添加自己。 + */ + protected _isCurrentlyManagedByTweenManager: boolean; + abstract tick(): boolean; + recycleSelf(): void; + isRunning(): boolean; + start(): void; + pause(): void; + resume(): void; + stop(bringToCompletion?: boolean): void; + } +} +declare module es { + class PropertyTweens { + static NumberPropertyTo(self: any, memberName: string, to: number, duration: number): ITween; + static Vector2PropertyTo(self: any, memeberName: string, to: Vector2, duration: number): ITween; + } +} +declare module es { + enum LoopType { + none = 0, + restartFromBeginning = 1, + pingpong = 2 + } + enum TweenState { + running = 0, + paused = 1, + complete = 2 + } + abstract class Tween implements ITweenable, ITween { + protected _target: ITweenTarget; + protected _isFromValueOverridden: boolean; + protected _fromValue: T; + protected _toValue: T; + protected _easeType: EaseType; + protected _shouldRecycleTween: boolean; + protected _isRelative: boolean; + protected _completionHandler: (tween: ITween) => void; + protected _loopCompleteHandler: (tween: ITween) => void; + protected _nextTween: ITweenable; + protected _tweenState: TweenState; + private _isTimeScaleIndependent; + protected _delay: number; + protected _duration: number; + protected _timeScale: number; + protected _elapsedTime: number; + protected _loopType: LoopType; + protected _loops: number; + protected _delayBetweenLoops: number; + private _isRunningInReverse; + context: any; + setEaseType(easeType: EaseType): ITween; + setDelay(delay: number): ITween; + setDuration(duration: number): ITween; + setTimeScale(timeSclae: number): ITween; + setIsTimeScaleIndependent(): ITween; + setCompletionHandler(completeHandler: (tween: ITween) => void): ITween; + setLoops(loopType: LoopType, loops?: number, delayBetweenLoops?: number): ITween; + setLoopCompletionHanlder(loopCompleteHandler: (tween: ITween) => void): ITween; + setFrom(from: T): ITween; + prepareForReuse(from: T, to: T, duration: number): ITween; + setRecycleTween(shouldRecycleTween: boolean): ITween; + abstract setIsRelative(): ITween; + setContext(context: any): ITween; + setNextTween(nextTween: ITweenable): ITween; + tick(): boolean; + recycleSelf(): void; + isRunning(): boolean; + start(): void; + pause(): void; + resume(): void; + stop(bringToCompletion?: boolean): void; + jumpToElapsedTime(elapsedTime: any): void; + /** + * 反转当前的tween,如果是向前走,就会向后走,反之亦然 + */ + reverseTween(): void; + /** + * 当通过StartCoroutine调用时,这将一直持续到tween完成 + */ + waitForCompletion(): IterableIterator; + getTargetObject(): any; + private resetState; + /** + * 将所有状态重置为默认值,并根据传入的参数设置初始状态。 + * 这个方法作为一个切入点,这样Tween子类就可以调用它,这样tweens就可以被回收。 + * 当回收时,构造函数不会再被调用,所以这个方法封装了构造函数要做的事情 + * @param target + * @param to + * @param duration + */ + initialize(target: ITweenTarget, to: T, duration: number): void; + /** + * 处理循环逻辑 + * @param elapsedTimeExcess + */ + private handleLooping; + protected abstract updateValue(): any; + } +} +declare module es { + class NumberTween extends Tween { + static create(): NumberTween; + constructor(target?: ITweenTarget, to?: number, duration?: number); + setIsRelative(): ITween; + protected updateValue(): void; + recycleSelf(): void; + } + class Vector2Tween extends Tween { + static create(): Vector2Tween; + constructor(target?: ITweenTarget, to?: Vector2, duration?: number); + setIsRelative(): ITween; + protected updateValue(): void; + recycleSelf(): void; + } + class RectangleTween extends Tween { + static create(): RectangleTween; + constructor(target?: ITweenTarget, to?: Rectangle, duration?: number); + setIsRelative(): ITween; + protected updateValue(): void; + recycleSelf(): void; + } + class ColorTween extends Tween { + static create(): ColorTween; + constructor(target?: ITweenTarget, to?: Color, duration?: number); + setIsRelative(): this; + protected updateValue(): void; + } +} +declare module es { + class RenderableColorTween extends ColorTween implements ITweenTarget { + _renderable: RenderableComponent; + setTweenedValue(value: Color): void; + getTweenedValue(): Color; + getTargetObject(): RenderableComponent; + updateValue(): void; + setTarget(renderable: RenderableComponent): void; + recycleSelf(): void; + } +} +declare module es { + class TransformSpringTween extends AbstractTweenable { + readonly targetType: TransformTargetType; + private _transform; + private _targetType; + private _targetValue; + private _velocity; + /** + * 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + */ + dampingRatio: number; + /** + * 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定角频率 + */ + angularFrequency: number; + constructor(transform: Transform, targetType: TransformTargetType, targetValue: Vector2); + /** + * 你可以在任何时候调用setTargetValue来重置目标值到一个新的Vector2。 + * 如果你没有调用start来添加spring tween,它会为你调用 + * @param targetValue + */ + setTargetValue(targetValue: Vector2): void; + /** + * lambda应该是振荡幅度减少50%时的理想持续时间 + * @param lambda + */ + updateDampingRatioWithHalfLife(lambda: number): void; + tick(): boolean; + private setTweenedValue; + private getCurrentValueOfTweenedTargetType; + } +} +declare module es { + /** + * 对任何与Transform相关的属性tweens都是有用的枚举 + */ + enum TransformTargetType { + position = 0, + localPosition = 1, + scale = 2, + localScale = 3, + rotationDegrees = 4, + localRotationDegrees = 5 + } + /** + * 这是一个特殊的情况,因为Transform是迄今为止最被ween的对象。 + * 我们将Tween和ITweenTarget封装在一个单一的、可缓存的类中 + */ + class TransformVector2Tween extends Vector2Tween implements ITweenTarget { + private _transform; + private _targetType; + setTweenedValue(value: Vector2): void; + getTweenedValue(): Vector2; + getTargetObject(): Transform; + setTargetAndType(transform: Transform, targetType: TransformTargetType): void; + protected updateValue(): void; + recycleSelf(): void; + } +} +declare module es { + enum EaseType { + linear = 0, + sineIn = 1, + sineOut = 2, + sineInOut = 3, + quadIn = 4, + quadOut = 5, + quadInOut = 6, + quintIn = 7, + quintOut = 8, + quintInOut = 9, + cubicIn = 10, + cubicOut = 11, + cubicInOut = 12, + quartIn = 13, + quartOut = 14, + quartInOut = 15, + expoIn = 16, + expoOut = 17, + expoInOut = 18, + circleIn = 19, + circleOut = 20, + circleInOut = 21, + elasticIn = 22, + elasticOut = 23, + elasticInOut = 24, + punch = 25, + backIn = 26, + backOut = 27, + backInOut = 28, + bounceIn = 29, + bounceOut = 30, + bounceInOut = 31 + } + /** + * 助手的一个方法,它接收一个EaseType,并通过给定的持续时间和时间参数来应用该Ease方程。 + * 我们这样做是为了避免传来传去的Funcs为垃圾收集器制造大量垃圾 + */ + class EaseHelper { + /** + * 返回 easeType 的相反 EaseType + * @param easeType + */ + static oppositeEaseType(easeType: EaseType): EaseType.linear | EaseType.sineIn | EaseType.sineOut | EaseType.sineInOut | EaseType.quadIn | EaseType.quadOut | EaseType.quadInOut | EaseType.quintIn | EaseType.quintOut | EaseType.quintInOut | EaseType.cubicIn | EaseType.cubicOut | EaseType.cubicInOut | EaseType.quartIn | EaseType.quartInOut | EaseType.expoIn | EaseType.expoOut | EaseType.expoInOut | EaseType.circleIn | EaseType.circleOut | EaseType.circleInOut | EaseType.elasticIn | EaseType.elasticOut | EaseType.elasticInOut | EaseType.punch | EaseType.backIn | EaseType.backOut | EaseType.backInOut | EaseType.bounceIn | EaseType.bounceOut | EaseType.bounceInOut; + static ease(easeType: EaseType, t: number, duration: number): number; + } +} +declare module es { + class GlobalManager { + _enabled: boolean; + /** + * 如果true则启用了GlobalManager。 + * 状态的改变会导致调用OnEnabled/OnDisable + */ + /** + * 如果true则启用了GlobalManager。 + * 状态的改变会导致调用OnEnabled/OnDisable + * @param value + */ + enabled: boolean; + /** + * 启用/禁用这个GlobalManager + * @param isEnabled + */ + setEnabled(isEnabled: boolean): void; + /** + * 此GlobalManager启用时调用 + */ + onEnabled(): void; + /** + * 此GlobalManager禁用时调用 + */ + onDisabled(): void; + /** + * 在frame .update之前调用每一帧 + */ + update(): void; + } +} +declare module es { + class TweenManager extends GlobalManager { + static defaultEaseType: EaseType; + /** + * 如果为真,当加载新关卡时,活动的tween列表将被清除 + */ + static removeAllTweensOnLevelLoad: boolean; + /** + * 这里支持各种类型的自动缓存。请 + * 注意,只有在使用扩展方法启动tweens时,或者在做自定义tweens时从缓存中获取tween时,缓存才会起作用。 + * 关于如何获取缓存的tween,请参见扩展方法的实现 + */ + static cacheNumberTweens: boolean; + static cacheVector2Tweens: boolean; + static cacheColorTweens: boolean; + static cacheRectTweens: boolean; + /** + * 当前所有活跃用户的内部列表 + */ + private _activeTweens; + private _tempTweens; + /** + * 标志表示tween更新循环正在运行 + */ + private _isUpdating; + /** + * 便于暴露一个静态的API以方便访问 + */ + private static _instance; + constructor(); + update(): void; + /** + * 将一个tween添加到活动tweens列表中 + * @param tween + */ + static addTween(tween: ITweenable): void; + /** + * 从当前的tweens列表中删除一个tween + * @param tween + */ + static removeTween(tween: ITweenable): void; + /** + * 停止所有的tween并选择地把他们全部完成 + * @param bringToCompletion + */ + static stopAllTweens(bringToCompletion?: boolean): void; + /** + * 返回具有特定上下文的所有tweens。 + * Tweens以ITweenable的形式返回,因为这就是TweenManager所知道的所有内容 + * @param context + */ + static allTweensWithContext(context: any): ITweenable[]; + /** + * 停止所有给定上下文的tweens + * @param context + * @param bringToCompletion + */ + static stopAllTweensWithContext(context: any, bringToCompletion?: boolean): void; + /** + * 返回具有特定目标的所有tweens。 + * Tweens以ITweenControl的形式返回,因为TweenManager只知道这些 + * @param target + */ + static allTweenWithTarget(target: any): ITweenable[]; + /** + * 停止所有具有TweenManager知道的特定目标的tweens + * @param target + * @param bringToCompletion + */ + static stopAllTweensWithTarget(target: any, bringToCompletion?: boolean): void; + } +} +declare module es { + /** + * 标准缓和方程通过将b和c参数(起始值和变化值)用0和1替换,然后进行简化。 + * 这样做的目的是为了让我们可以得到一个0 - 1之间的原始值(除了弹性/反弹故意超过界限),然后用这个值来lerp任何东西 + */ + module Easing { + class Linear { + static easeNone(t: number, d: number): number; + } + class Quadratic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Back { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Bounce { + static easeOut(t: number, d: number): number; + static easeIn(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Circular { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Cubic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Elastic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + static punch(t: number, d: number): number; + } + class Exponential { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Quartic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Quintic { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + class Sinusoidal { + static easeIn(t: number, d: number): number; + static easeOut(t: number, d: number): number; + static easeInOut(t: number, d: number): number; + } + } +} +declare module es { + /** + * 一系列静态方法来处理所有常见的tween类型结构,以及它们的unclamped lerps.unclamped lerps对于超过0-1范围的bounce、elastic或其他tweens是必需的 + */ + class Lerps { + static lerp(from: Color, to: Color, t: number): any; + static lerp(from: number, to: number, t: number): any; + static lerp(from: Rectangle, to: Rectangle, t: number): any; + static lerp(from: Vector2, to: Vector2, t: number): any; + static angleLerp(from: Vector2, to: Vector2, t: number): Vector2; + static ease(easeType: EaseType, from: Rectangle, to: Rectangle, t: number, duration: number): any; + static ease(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number): any; + static ease(easeType: EaseType, from: number, to: number, t: number, duration: number): any; + static ease(easeType: EaseType, from: Color, to: Color, t: number, duration: number): any; + static easeAngle(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number): Vector2; + /** + * 使用半隐式欧拉方法。速度较慢,但总是很稳定。见 + * http://allenchou.net/2015/04/game-math-more-on-numeric-springing/ + * @param currentValue + * @param targetValue + * @param velocity Velocity的引用。如果在两次调用之间改变targetValue,请务必将其重置为0 + * @param dampingRatio 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + * @param angularFrequency 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定 + */ + static fastSpring(currentValue: Vector2, targetValue: Vector2, velocity: Vector2, dampingRatio: number, angularFrequency: number): Vector2; + } +} +declare module es { + /** + * 一系列强类型、可链式的方法来设置各种tween属性 + */ + interface ITween extends ITweenControl { + /** + * 设置该tween的易用性类型 + * @param easeType + */ + setEaseType(easeType: EaseType): ITween; + /** + * 设置启动tween前的延迟 + * @param delay + */ + setDelay(delay: number): ITween; + /** + * 设置tween的持续时间 + * @param duration + */ + setDuration(duration: number): ITween; + /** + * 设置这个tween使用的timeScale。 + * TimeScale将与Time.deltaTime/Time.unscaledDeltaTime相乘,从而得到tween实际使用的delta时间 + * @param timeScale + */ + setTimeScale(timeScale: number): ITween; + /** + * 设置tween使用Time.unscaledDeltaTime代替Time.deltaTime + */ + setIsTimeScaleIndependent(): ITween; + /** + * 设置当tween完成时应该调用的动作 + * @param completionHandler + */ + setCompletionHandler(completionHandler: (tween: ITween) => void): ITween; + /** + * 设置tween的循环类型。一个pingpong循环意味着从开始-结束-开始 + * @param loopType + * @param loops + * @param delayBetweenLoops + */ + setLoops(loopType: LoopType, loops: number, delayBetweenLoops: number): ITween; + /** + * 设置tween的起始位置 + * @param from + */ + setFrom(from: T): ITween; + /** + * 通过重置tween的from/to值和持续时间,为重复使用tween做准备。 + * @param from + * @param to + * @param duration + */ + prepareForReuse(from: T, to: T, duration: number): ITween; + /** + * 如果为true(默认值),tween将在使用后被回收。 + * 如果在TweenManager类中进行了配置,所有的Tween子类都有自己相关的自动缓存 + * @param shouldRecycleTween + */ + setRecycleTween(shouldRecycleTween: boolean): ITween; + /** + * 帮助程序,只是将tween的to值设置为相对于其当前值的+从使tween + */ + setIsRelative(): ITween; + /** + * 允许你通过tween.context.context来设置任何可检索的对象引用。 + * 这对于避免完成处理程序方法的闭包分配是很方便的。 + * 你也可以在TweenManager中搜索具有特定上下文的所有tweens + * @param context + */ + setContext(context: any): ITween; + /** + * 允许你添加一个tween,这个tween完成后会被运行。 + * 注意 nextTween 必须是一个 ITweenable! 同时注意,所有的ITweenT都是ITweenable + * @param nextTween + */ + setNextTween(nextTween: ITweenable): ITween; + } +} +declare module es { + /** + * 更多具体的Tween播放控制在这里 + */ + interface ITweenControl extends ITweenable { + /** + * 当使用匿名方法时,您可以在任何回调(如完成处理程序)中使用该属性来避免分配 + */ + context: any; + /** + * 将tween扭曲为elapsedTime,并将其限制在0和duration之间,无论tween对象是暂停、完成还是运行,都会立即更新 + * @param elapsedTime 所用时间 + */ + jumpToElapsedTime(elapsedTime: number): any; + /** + * 当从StartCoroutine调用时,它将直到tween完成 + */ + waitForCompletion(): any; + /** + * 获取tween的目标,如果TweenTargets不一定都是一个对象,则为null,它的唯一真正用途是让TweenManager按目标查找tweens的列表 + */ + getTargetObject(): any; + } +} +declare module es { + /** + * 任何想要被weened的对象都需要实现这个功能。 + * TweenManager内部喜欢做一个简单的对象来实现这个接口,并存储一个对被tweened对象的引用 + */ + interface ITweenTarget { + /** + * 在你选择的对象上设置最终的tweened值 + * @param value + */ + setTweenedValue(value: T): any; + getTweenedValue(): T; + /** + * 获取tween的目标,如果TweenTargets不一定都是一个对象,则为null,它的唯一真正用途是让TweenManager按目标查找tweens的列表 + */ + getTargetObject(): any; + } +} +declare module es { + interface ITweenable { + /** + * 就像内部的Update一样,每一帧都被TweenManager调用 + */ + tick(): boolean; + /** + * 当一个tween被移除时,由TweenManager调用。子 + * 类可以选择自己回收。子类应该首先在其实现中检查_shouldRecycleTween bool! + */ + recycleSelf(): any; + /** + * 检查是否有tween在运行 + */ + isRunning(): boolean; + /** + * 启动tween + */ + start(): any; + /** + * 暂停 + */ + pause(): any; + /** + * 暂停后恢复tween + */ + resume(): any; + /** + * 停止tween,并可选择将其完成 + * @param bringToCompletion + */ + stop(bringToCompletion: boolean): any; + } +} +declare module es { + interface IAnimFrame { + t: number; + value: number; + } + class AnimCurve { + readonly points: IAnimFrame[]; + constructor(points: IAnimFrame[]); + lerp(t: number): number; + _points: IAnimFrame[]; } } declare module es { @@ -3643,38 +4922,6 @@ declare module es { private forOwn; } } -declare module es { - class GlobalManager { - _enabled: boolean; - /** - * 如果true则启用了GlobalManager。 - * 状态的改变会导致调用OnEnabled/OnDisable - */ - /** - * 如果true则启用了GlobalManager。 - * 状态的改变会导致调用OnEnabled/OnDisable - * @param value - */ - enabled: boolean; - /** - * 启用/禁用这个GlobalManager - * @param isEnabled - */ - setEnabled(isEnabled: boolean): void; - /** - * 此GlobalManager启用时调用 - */ - onEnabled(): void; - /** - * 此GlobalManager禁用时调用 - */ - onDisabled(): void; - /** - * 在frame .update之前调用每一帧 - */ - update(): void; - } -} declare module es { class Hash { /** @@ -3731,6 +4978,61 @@ declare module es { equals(other: T): boolean; } } +declare module es { + interface IListener { + caller: object; + callback: Function; + } + interface IObservable { + addListener(caller: object, callback: Function): any; + removeListener(caller: object, callback: Function): any; + clearListener(): any; + clearListenerWithCaller(caller: object): any; + } + class Observable implements IObservable { + constructor(); + addListener(caller: object, callback: Function): void; + removeListener(caller: object, callback: Function): void; + clearListener(): void; + clearListenerWithCaller(caller: object): void; + notify(...args: any[]): void; + private _listeners; + } + class ObservableT extends Observable { + addListener(caller: object, callback: (arg: T) => void): void; + removeListener(caller: object, callback: (arg: T) => void): void; + notify(arg: T): void; + } + class ObservableTT extends Observable { + addListener(caller: object, callback: (arg1: T, arg2: R) => void): void; + removeListener(caller: object, callback: (arg: T, arg2: R) => void): void; + notify(arg1: T, arg2: R): void; + } + class Command implements IObservable { + constructor(caller: object, action: Function); + bindAction(caller: object, action: Function): void; + dispatch(...args: any[]): void; + addListener(caller: object, callback: Function): void; + removeListener(caller: object, callback: Function): void; + clearListener(): void; + clearListenerWithCaller(caller: object): void; + private _onExec; + private _caller; + private _action; + } + class ValueChangeCommand implements IObservable { + constructor(value: T); + readonly onValueChange: Observable; + value: T; + dispatch(value: T): void; + addListener(caller: object, callback: Function): void; + removeListener(caller: object, callback: Function): void; + clearListener(): void; + clearListenerWithCaller(caller: object): void; + private _onValueChange; + private _value; + } +} declare module es { class Ref { value: T; @@ -4589,6 +5891,7 @@ declare module es { * @param points */ static boundsFromPolygonPoints(points: Vector2[]): Rectangle; + static calculateBounds(rect: Rectangle, parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number): void; /** * 缩放矩形 * @param rect diff --git a/source/bin/framework.js b/source/bin/framework.js index 6fcbecd9..11b7be84 100644 --- a/source/bin/framework.js +++ b/source/bin/framework.js @@ -96,6 +96,7 @@ var es; Core.emitter = new es.Emitter(); Core.emitter.addObserver(es.CoreEvents.frameUpdated, this.update, this); Core.registerGlobalManager(this._coroutineManager); + Core.registerGlobalManager(new es.TweenManager()); Core.registerGlobalManager(this._timerManager); Core.entitySystemsEnabled = enableEntitySystems; this.debug = debug; @@ -170,9 +171,10 @@ var es; * @param type */ Core.getGlobalManager = function (type) { - for (var i = 0; i < this._instance._globalManagers.length; i++) { - if (this._instance._globalManagers[i] instanceof type) - return this._instance._globalManagers[i]; + for (var i = 0, s = Core._instance._globalManagers.length; i < s; ++i) { + var manager = Core._instance._globalManagers[i]; + if (manager instanceof type) + return manager; } return null; }; @@ -511,6 +513,7 @@ var es; */ Component.prototype.onEntityTransformChanged = function (comp) { }; + Component.prototype.debugRender = function (batcher) { }; /** *当父实体或此组件启用时调用 */ @@ -583,6 +586,10 @@ var es; * 每帧更新事件 */ CoreEvents[CoreEvents["frameUpdated"] = 1] = "frameUpdated"; + /** + * 当渲染发生时触发 + */ + CoreEvents[CoreEvents["renderChanged"] = 2] = "renderChanged"; })(CoreEvents = es.CoreEvents || (es.CoreEvents = {})); })(es || (es = {})); var es; @@ -835,7 +842,7 @@ var es; this.transform.setScale(scale); } else { - this.transform.setScale(new es.Vector2(scale)); + this.transform.setScale(new es.Vector2(scale, scale)); } return this; }; @@ -844,7 +851,7 @@ var es; this.transform.setLocalScale(scale); } else { - this.transform.setLocalScale(new es.Vector2(scale)); + this.transform.setLocalScale(new es.Vector2(scale, scale)); } return this; }; @@ -945,6 +952,11 @@ var es; Entity.prototype.update = function () { this.components.update(); }; + Entity.prototype.debugRender = function (batcher) { + if (!batcher) + return; + this.components.debugRender(batcher); + }; /** * 创建组件的新实例。返回实例组件 * @param componentType @@ -1042,6 +1054,54 @@ var es; this.removeComponent(this.components.buffer[i]); } }; + Entity.prototype.tweenPositionTo = function (to, duration) { + if (duration === void 0) { duration = 0.3; } + var tween = es.Pool.obtain(es.TransformVector2Tween); + tween.setTargetAndType(this.transform, es.TransformTargetType.position); + tween.initialize(tween, to, duration); + return tween; + }; + Entity.prototype.tweenLocalPositionTo = function (to, duration) { + if (duration === void 0) { duration = 0.3; } + var tween = es.Pool.obtain(es.TransformVector2Tween); + tween.setTargetAndType(this.transform, es.TransformTargetType.localPosition); + tween.initialize(tween, to, duration); + return tween; + }; + Entity.prototype.tweenScaleTo = function (to, duration) { + if (duration === void 0) { duration = 0.3; } + if (typeof (to) == 'number') { + return this.tweenScaleTo(new es.Vector2(to, to), duration); + } + var tween = es.Pool.obtain(es.TransformVector2Tween); + tween.setTargetAndType(this.transform, es.TransformTargetType.scale); + tween.initialize(tween, to, duration); + return tween; + }; + Entity.prototype.tweenLocalScaleTo = function (to, duration) { + if (duration === void 0) { duration = 0.3; } + if (typeof (to) == 'number') { + return this.tweenLocalScaleTo(new es.Vector2(to, to), duration); + } + var tween = es.Pool.obtain(es.TransformVector2Tween); + tween.setTargetAndType(this.transform, es.TransformTargetType.localScale); + tween.initialize(tween, to, duration); + return tween; + }; + Entity.prototype.tweenRotationDegreesTo = function (to, duration) { + if (duration === void 0) { duration = 0.3; } + var tween = es.Pool.obtain(es.TransformVector2Tween); + tween.setTargetAndType(this.transform, es.TransformTargetType.rotationDegrees); + tween.initialize(tween, new es.Vector2(to, to), duration); + return tween; + }; + Entity.prototype.tweenLocalRotationDegreesTo = function (to, duration) { + if (duration === void 0) { duration = 0.3; } + var tween = es.Pool.obtain(es.TransformVector2Tween); + tween.setTargetAndType(this.transform, es.TransformTargetType.localRotationDegrees); + tween.initialize(tween, new es.Vector2(to, to), duration); + return tween; + }; Entity.prototype.compareTo = function (other) { var compare = this._updateOrder - other._updateOrder; if (compare == 0) @@ -1072,10 +1132,12 @@ var es; * @param y 二维空间的y坐标 */ function Vector2(x, y) { + if (x === void 0) { x = 0; } + if (y === void 0) { y = 0; } this.x = 0; this.y = 0; - this.x = x ? x : 0; - this.y = y != undefined ? y : this.x; + this.x = x; + this.y = y; } Object.defineProperty(Vector2, "zero", { get: function () { @@ -1105,6 +1167,34 @@ var es; enumerable: true, configurable: true }); + Object.defineProperty(Vector2, "up", { + get: function () { + return new Vector2(0, -1); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Vector2, "down", { + get: function () { + return new Vector2(0, 1); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Vector2, "left", { + get: function () { + return new Vector2(-1, 0); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Vector2, "right", { + get: function () { + return new Vector2(1, 0); + }, + enumerable: true, + configurable: true + }); /** * * @param value1 @@ -1127,68 +1217,19 @@ var es; result.y = value1.y / value2.y; return result; }; - /** - * - * @param value1 - * @param value2 - */ - Vector2.multiply = function (value1, value2) { - var result = new Vector2(0, 0); - result.x = value1.x * value2.x; - result.y = value1.y * value2.y; + Vector2.divideScaler = function (value1, value2) { + var result = Vector2.zero; + result.x = value1.x / value2; + result.y = value1.y / value2; return result; }; - /** - * - * @param value1 - * @param value2 - * @returns - */ - Vector2.multiplyScaler = function (value1, value2) { - var result = new Vector2(0, 0); - result.x = value1.x * value2; - result.y = value1.x * value2; - return result; - }; - /** - * - * @param value1 - * @param value2 - */ - Vector2.subtract = function (value1, value2) { - var result = new Vector2(0, 0); - result.x = value1.x - value2.x; - result.y = value1.y - value2.y; - return result; - }; - /** - * 创建一个新的Vector2 - * 它包含来自另一个向量的标准化值。 - * @param value - */ - Vector2.normalize = function (value) { - var nValue = new Vector2(value.x, value.y); - var val = 1 / Math.sqrt((nValue.x * nValue.x) + (nValue.y * nValue.y)); - nValue.x *= val; - nValue.y *= val; - return nValue; - }; - /** - * 返回两个向量的点积 - * @param value1 - * @param value2 - */ - Vector2.dot = function (value1, value2) { - return (value1.x * value2.x) + (value1.y * value2.y); - }; /** * 返回两个向量之间距离的平方 * @param value1 * @param value2 */ - Vector2.distanceSquared = function (value1, value2) { - var v1 = value1.x - value2.x, v2 = value1.y - value2.y; - return (v1 * v1) + (v2 * v2); + Vector2.sqrDistance = function (value1, value2) { + return Math.pow(value1.x - value2.x, 2) + Math.pow(value1.y - value2.y, 2); }; /** * 将指定的值限制在一个范围内 @@ -1241,9 +1282,8 @@ var es; * @param value2 * @returns 两个向量之间的距离 */ - Vector2.distance = function (value1, value2) { - var v1 = value1.x - value2.x, v2 = value1.y - value2.y; - return Math.sqrt((v1 * v1) + (v2 * v2)); + Vector2.distance = function (vec1, vec2) { + return Math.sqrt(Math.pow(vec1.x - vec2.x, 2) + Math.pow(vec1.y - vec2.y, 2)); }; /** * 返回两个向量之间的角度,单位是度数 @@ -1251,9 +1291,9 @@ var es; * @param to */ Vector2.angle = function (from, to) { - from = Vector2.normalize(from); - to = Vector2.normalize(to); - return Math.acos(es.MathHelper.clamp(Vector2.dot(from, to), -1, 1)) * es.MathHelper.Rad2Deg; + from = from.normalize(); + to = to.normalize(); + return Math.acos(es.MathHelper.clamp(from.dot(to), -1, 1)) * es.MathHelper.Rad2Deg; }; /** * 创建一个包含指定向量反转的新Vector2 @@ -1272,7 +1312,7 @@ var es; * @returns */ Vector2.reflect = function (vector, normal) { - var result = new Vector2(); + var result = es.Vector2.zero; var val = 2 * ((vector.x * normal.x) + (vector.y * normal.y)); result.x = vector.x - (normal.x * val); result.y = vector.y - (normal.y * val); @@ -1288,13 +1328,23 @@ var es; Vector2.smoothStep = function (value1, value2, amount) { return new Vector2(es.MathHelper.smoothStep(value1.x, value2.x, amount), es.MathHelper.smoothStep(value1.y, value2.y, amount)); }; + Vector2.prototype.setTo = function (x, y) { + this.x = x; + this.y = y; + }; + Vector2.prototype.negate = function () { + return this.scale(-1); + }; /** * * @param value */ - Vector2.prototype.add = function (value) { - this.x += value.x; - this.y += value.y; + Vector2.prototype.add = function (v) { + return new Vector2(this.x + v.x, this.y + v.y); + }; + Vector2.prototype.addEqual = function (v) { + this.x += v.x; + this.y += v.y; return this; }; /** @@ -1302,18 +1352,17 @@ var es; * @param value */ Vector2.prototype.divide = function (value) { - this.x /= value.x; - this.y /= value.y; - return this; + return new Vector2(this.x / value.x, this.y / value.y); + }; + Vector2.prototype.divideScaler = function (value) { + return new Vector2(this.x / value, this.y / value); }; /** * * @param value */ Vector2.prototype.multiply = function (value) { - this.x *= value.x; - this.y *= value.y; - return this; + return new Vector2(value.x * this.x, value.y * this.y); }; /** * @@ -1330,22 +1379,64 @@ var es; * @param value 要减去的Vector2 * @returns 当前Vector2 */ - Vector2.prototype.subtract = function (value) { - this.x -= value.x; - this.y -= value.y; + Vector2.prototype.sub = function (value) { + return new Vector2(this.x - value.x, this.y - value.y); + }; + Vector2.prototype.subEqual = function (v) { + this.x -= v.x; + this.y -= v.y; return this; }; + Vector2.prototype.dot = function (v) { + return this.x * v.x + this.y * v.y; + }; + /** + * + * @param size + * @returns + */ + Vector2.prototype.scale = function (size) { + return new Vector2(this.x * size, this.y * size); + }; + Vector2.prototype.scaleEqual = function (size) { + this.x *= size; + this.y *= size; + return this; + }; + Vector2.prototype.transform = function (matrix) { + return new Vector2(this.x * matrix.m11 + this.y * matrix.m21 + matrix.m31, this.x * matrix.m12 + this.y * matrix.m22 + matrix.m32); + }; + Vector2.prototype.normalize = function () { + var d = this.distance(); + if (d > 0) { + return new Vector2(this.x / d, this.y / d); + } + else { + return new Vector2(0, 1); + } + }; /** * 将这个Vector2变成一个方向相同的单位向量 */ - Vector2.prototype.normalize = function () { - var val = 1 / Math.sqrt((this.x * this.x) + (this.y * this.y)); - this.x *= val; - this.y *= val; + Vector2.prototype.normalizeEqual = function () { + var d = this.distance(); + if (d > 0) { + this.setTo(this.x / d, this.y / d); + return this; + } + else { + this.setTo(0, 1); + return this; + } }; - /** 返回它的长度 */ - Vector2.prototype.length = function () { - return Math.sqrt((this.x * this.x) + (this.y * this.y)); + Vector2.prototype.magnitude = function () { + return this.distance(); + }; + Vector2.prototype.distance = function (v) { + if (!v) { + v = Vector2.zero; + } + return Math.sqrt(Math.pow(this.x - v.x, 2) + Math.pow(this.y - v.y, 2)); }; /** * 返回该Vector2的平方长度 @@ -1366,8 +1457,8 @@ var es; * @param right */ Vector2.prototype.angleBetween = function (left, right) { - var one = Vector2.subtract(left, this); - var two = Vector2.subtract(right, this); + var one = left.sub(this); + var two = right.sub(this); return es.Vector2Ext.angle(one, two); }; /** @@ -1375,11 +1466,9 @@ var es; * @param other 要比较的对象 * @returns 如果实例相同true 否则false */ - Vector2.prototype.equals = function (other) { - if (other instanceof Vector2) { - return other.x == this.x && other.y == this.y; - } - return false; + Vector2.prototype.equals = function (other, tolerance) { + if (tolerance === void 0) { tolerance = 0.001; } + return Math.abs(this.x - other.x) <= tolerance && Math.abs(this.y - other.y) <= tolerance; }; Vector2.prototype.isValid = function () { return es.MathHelper.isValid(this.x) && es.MathHelper.isValid(this.y); @@ -1414,6 +1503,13 @@ var es; Vector2.hermite = function (value1, tangent1, value2, tangent2, amount) { return new Vector2(es.MathHelper.hermite(value1.x, tangent1.x, value2.x, tangent2.x, amount), es.MathHelper.hermite(value1.y, tangent1.y, value2.y, tangent2.y, amount)); }; + Vector2.unsignedAngle = function (from, to, round) { + if (round === void 0) { round = true; } + from.normalizeEqual(); + to.normalizeEqual(); + var angle = Math.acos(es.MathHelper.clamp(from.dot(to), -1, 1)) * es.MathHelper.Rad2Deg; + return round ? Math.round(angle) : angle; + }; Vector2.prototype.clone = function () { return new Vector2(this.x, this.y); }; @@ -1429,7 +1525,9 @@ var es; var Scene = /** @class */ (function () { function Scene() { this._sceneComponents = []; + this._renderers = []; this.entities = new es.EntityList(this); + this.renderableComponents = new es.RenderableComponentList(); this.entityProcessors = new es.EntityProcessorList(); this.identifierPool = new es.IdentifierPool(); this.initialize(); @@ -1452,6 +1550,9 @@ var es; Scene.prototype.unload = function () { }; Scene.prototype.begin = function () { + if (this._renderers.length == 0) { + this.addRenderer(new es.DefaultRenderer()); + } es.Physics.reset(); if (this.entityProcessors != null) this.entityProcessors.begin(); @@ -1460,11 +1561,14 @@ var es; }; Scene.prototype.end = function () { this._didSceneBegin = false; + for (var i = 0; i < this._renderers.length; i++) + this._renderers[i].unload(); this.entities.removeAllEntities(); for (var i = 0; i < this._sceneComponents.length; i++) { this._sceneComponents[i].onRemovedFromScene(); } this._sceneComponents.length = 0; + this.camera = null; es.Physics.clear(); if (this.entityProcessors) this.entityProcessors.end(); @@ -1484,6 +1588,30 @@ var es; this.entities.update(); if (this.entityProcessors != null) this.entityProcessors.lateUpdate(); + this.renderableComponents.updateLists(); + this.render(); + }; + Scene.prototype.render = function () { + for (var i = 0; i < this._renderers.length; i++) { + this._renderers[i].render(this); + } + }; + Scene.prototype.addRenderer = function (renderer) { + this._renderers.push(renderer); + this._renderers.sort(function (self, other) { return self.renderOrder - other.renderOrder; }); + renderer.onAddedToScene(this); + return renderer; + }; + Scene.prototype.getRenderer = function (type) { + for (var i = 0; i < this._renderers.length; i++) { + if (this._renderers[i] instanceof type) + return this._renderers[i]; + } + return null; + }; + Scene.prototype.removeRenderer = function (renderer) { + new es.List(this._renderers).remove(renderer); + renderer.unload(); }; /** * 向组件列表添加并返回SceneComponent @@ -1634,17 +1762,14 @@ var es; }()); es.Scene = Scene; })(es || (es = {})); -var transform; -(function (transform) { - var Component; - (function (Component) { - Component[Component["position"] = 0] = "position"; - Component[Component["scale"] = 1] = "scale"; - Component[Component["rotation"] = 2] = "rotation"; - })(Component = transform.Component || (transform.Component = {})); -})(transform || (transform = {})); var es; (function (es) { + var ComponentTransform; + (function (ComponentTransform) { + ComponentTransform[ComponentTransform["position"] = 0] = "position"; + ComponentTransform[ComponentTransform["scale"] = 1] = "scale"; + ComponentTransform[ComponentTransform["rotation"] = 2] = "rotation"; + })(ComponentTransform = es.ComponentTransform || (es.ComponentTransform = {})); var DirtyType; (function (DirtyType) { DirtyType[DirtyType["clean"] = 0] = "clean"; @@ -1654,12 +1779,17 @@ var es; })(DirtyType = es.DirtyType || (es.DirtyType = {})); var Transform = /** @class */ (function () { function Transform(entity) { + /** + * 值会根据位置、旋转和比例自动重新计算 + */ + this._localTransform = es.Matrix2D.identity; /** * 值将自动从本地和父矩阵重新计算。 */ this._worldTransform = es.Matrix2D.identity; this._rotationMatrix = es.Matrix2D.identity; this._translationMatrix = es.Matrix2D.identity; + this._scaleMatrix = es.Matrix2D.identity; this._children = []; this._worldToLocalTransform = es.Matrix2D.identity; this._worldInverseTransform = es.Matrix2D.identity; @@ -1744,7 +1874,7 @@ var es; Object.defineProperty(Transform.prototype, "worldToLocalTransform", { get: function () { if (this._worldToLocalDirty) { - if (!this.parent) { + if (this.parent == null) { this._worldToLocalTransform = es.Matrix2D.identity; } else { @@ -1900,15 +2030,16 @@ var es; * @param parent */ Transform.prototype.setParent = function (parent) { + var _this = this; if (this._parent == parent) return this; if (this._parent != null) { - var children = new es.List(this._parent._children); - children.remove(this); + var index = this._parent._children.findIndex(function (t) { return t == _this; }); + if (index != -1) + this._parent._children.splice(index, 1); } if (parent != null) { - var children = new es.List(parent._children); - children.add(this); + parent._children.push(this); } this._parent = parent; this.setDirty(DirtyType.positionDirty); @@ -1925,7 +2056,7 @@ var es; return this; this._position = position; if (this.parent != null) { - this.localPosition = es.Vector2.transform(this._position, this._worldToLocalTransform); + this.localPosition = es.Vector2.transform(this._position, this.worldToLocalTransform); } else { this.localPosition = position; @@ -1951,7 +2082,7 @@ var es; */ Transform.prototype.setRotation = function (radians) { this._rotation = radians; - if (this.parent) { + if (this.parent != null) { this.localRotation = this.parent.rotation + radians; } else { @@ -1972,8 +2103,8 @@ var es; */ Transform.prototype.lookAt = function (pos) { var sign = this.position.x > pos.x ? -1 : 1; - var vectorToAlignTo = es.Vector2.normalize(es.Vector2.subtract(this.position, pos)); - this.rotation = sign * Math.acos(es.Vector2.dot(vectorToAlignTo, es.Vector2.unitY)); + var vectorToAlignTo = this.position.sub(pos).normalize(); + this.rotation = sign * Math.acos(vectorToAlignTo.dot(es.Vector2.unitY)); }; /** * 相对于父变换的旋转设置变换的旋转。如果转换没有父元素,则与transform.rotation相同 @@ -1998,7 +2129,7 @@ var es; */ Transform.prototype.setScale = function (scale) { this._scale = scale; - if (this.parent) { + if (this.parent != null) { this.localScale = es.Vector2.divide(scale, this.parent._scale); } else { @@ -2028,19 +2159,19 @@ var es; this.parent.updateTransform(); if (this._localDirty) { if (this._localPositionDirty) { - this._translationMatrix = es.Matrix2D.createTranslation(this._localPosition.x, this._localPosition.y); + es.Matrix2D.createTranslation(this._localPosition.x, this._localPosition.y, this._translationMatrix); this._localPositionDirty = false; } if (this._localRotationDirty) { - this._rotationMatrix = es.Matrix2D.createRotation(this._localRotation); + es.Matrix2D.createRotation(this._localRotation, this._rotationMatrix); this._localRotationDirty = false; } if (this._localScaleDirty) { - this._scaleMatrix = es.Matrix2D.createScale(this._localScale.x, this._localScale.y); + es.Matrix2D.createScale(this._localScale.x, this._localScale.y, this._scaleMatrix); this._localScaleDirty = false; } - this._localTransform = this._scaleMatrix.multiply(this._rotationMatrix); - this._localTransform = this._localTransform.multiply(this._translationMatrix); + es.Matrix2D.multiply(this._scaleMatrix, this._rotationMatrix, this._localTransform); + es.Matrix2D.multiply(this._localTransform, this._translationMatrix, this._localTransform); if (this.parent == null) { this._worldTransform = this._localTransform; this._rotation = this._localRotation; @@ -2050,9 +2181,10 @@ var es; this._localDirty = false; } if (this.parent != null) { - this._worldTransform = this._localTransform.multiply(this.parent._worldTransform); + es.Matrix2D.multiply(this._localTransform, this.parent._worldTransform, this._worldTransform); this._rotation = this._localRotation + this.parent._rotation; - this._scale = es.Vector2.multiply(this.parent._scale, this._localScale); + this._scale = this.parent._scale.multiply(this._localScale); + ; this._worldInverseDirty = true; } this._worldToLocalDirty = true; @@ -2064,14 +2196,14 @@ var es; if ((this.hierarchyDirty & dirtyFlagType) == 0) { this.hierarchyDirty |= dirtyFlagType; switch (dirtyFlagType) { - case es.DirtyType.positionDirty: - this.entity.onTransformChanged(transform.Component.position); + case DirtyType.positionDirty: + this.entity.onTransformChanged(ComponentTransform.position); break; - case es.DirtyType.rotationDirty: - this.entity.onTransformChanged(transform.Component.rotation); + case DirtyType.rotationDirty: + this.entity.onTransformChanged(ComponentTransform.rotation); break; - case es.DirtyType.scaleDirty: - this.entity.onTransformChanged(transform.Component.scale); + case DirtyType.scaleDirty: + this.entity.onTransformChanged(ComponentTransform.scale); break; } // 告诉子项发生了变换 @@ -2084,8 +2216,8 @@ var es; * @param transform */ Transform.prototype.copyFrom = function (transform) { - this._position = transform.position; - this._localPosition = transform._localPosition; + this._position = transform.position.clone(); + this._localPosition = transform._localPosition.clone(); this._rotation = transform._rotation; this._localRotation = transform._localRotation; this._scale = transform._scale; @@ -2214,7 +2346,7 @@ var es; /** * 该刚体的速度 */ - _this.velocity = new es.Vector2(); + _this.velocity = es.Vector2.zero; _this._mass = 10; _this._elasticity = 0.5; _this._friction = 0.5; @@ -2323,18 +2455,28 @@ var es; this._glue = es.MathHelper.clamp(value, 0, 10); return this; }; + ArcadeRigidbody.prototype.setVelocity = function (velocity) { + this.velocity = velocity; + return this; + }; /** * 用刚体的质量给刚体加上一个瞬间的力脉冲。力是一个加速度,单位是每秒像素每秒。将力乘以100000,使数值使用更合理 * @param force */ ArcadeRigidbody.prototype.addImpulse = function (force) { if (!this.isImmovable) { - this.velocity = this.velocity.add(es.Vector2.multiplyScaler(force, 100000) - .multiplyScaler(this._inverseMass * es.Time.deltaTime * es.Time.deltaTime)); + this.velocity.addEqual(force.scale(100000 * (this._inverseMass * (es.Time.deltaTime * es.Time.deltaTime)))); } }; ArcadeRigidbody.prototype.onAddedToEntity = function () { - this._collider = this.entity.getComponent(es.Collider); + this._collider = null; + for (var i = 0; i < this.entity.components.buffer.length; i++) { + var component = this.entity.components.buffer[i]; + if (component instanceof es.Collider) { + this._collider = component; + break; + } + } es.Debug.warnIf(this._collider == null, "ArcadeRigidbody 没有 Collider。ArcadeRigidbody需要一个Collider!"); }; ArcadeRigidbody.prototype.update = function () { @@ -2344,14 +2486,16 @@ var es; return; } if (this.shouldUseGravity) - this.velocity = this.velocity.add(es.Vector2.multiplyScaler(es.Physics.gravity, es.Time.deltaTime)); - this.entity.transform.position = this.entity.transform.position.add(es.Vector2.multiplyScaler(this.velocity, es.Time.deltaTime)); + this.velocity.addEqual(es.Physics.gravity.scale(es.Time.deltaTime)); + this.entity.position = this.entity.position.add(this.velocity.scale(es.Time.deltaTime)); var collisionResult = new es.CollisionResult(); // 捞取我们在新的位置上可能会碰撞到的任何东西 - var neighbors = es.Physics.boxcastBroadphaseExcludingSelfNonRect(this._collider, this._collider.collidesWithLayers.value); + var neighbors = es.Physics.boxcastBroadphaseExcludingSelf(this._collider, this._collider.bounds, this._collider.collidesWithLayers.value); try { for (var neighbors_1 = __values(neighbors), neighbors_1_1 = neighbors_1.next(); !neighbors_1_1.done; neighbors_1_1 = neighbors_1.next()) { var neighbor = neighbors_1_1.value; + if (!neighbor) + continue; // 如果邻近的对撞机是同一个实体,则忽略它 if (neighbor.entity.equals(this.entity)) { continue; @@ -2365,10 +2509,9 @@ var es; } else { // 没有ArcadeRigidbody,所以我们假设它是不动的,只移动我们自己的 - this.entity.transform.position = this.entity.transform.position.subtract(collisionResult.minimumTranslationVector); - var relativeVelocity = this.velocity.clone(); - this.calculateResponseVelocity(relativeVelocity, collisionResult.minimumTranslationVector, relativeVelocity); - this.velocity = this.velocity.add(relativeVelocity); + this.entity.position = this.entity.position.sub(collisionResult.minimumTranslationVector); + var relativeVelocity = this.calculateResponseVelocity(this.velocity, collisionResult.minimumTranslationVector); + this.velocity.addEqual(relativeVelocity); } } } @@ -2388,14 +2531,14 @@ var es; */ ArcadeRigidbody.prototype.processOverlap = function (other, minimumTranslationVector) { if (this.isImmovable) { - other.entity.transform.position = other.entity.transform.position.add(minimumTranslationVector); + other.entity.position = other.entity.position.add(minimumTranslationVector); } else if (other.isImmovable) { - this.entity.transform.position = this.entity.transform.position.subtract(minimumTranslationVector); + this.entity.position = this.entity.position.sub(minimumTranslationVector); } else { - this.entity.transform.position = this.entity.transform.position.subtract(es.Vector2.multiplyScaler(minimumTranslationVector, 0.5)); - other.entity.transform.position = other.entity.transform.position.add(es.Vector2.multiplyScaler(minimumTranslationVector, 0.5)); + this.entity.position = this.entity.position.sub(minimumTranslationVector.scale(0.5)); + other.entity.position = other.entity.position.add(minimumTranslationVector.scale(0.5)); } }; /** @@ -2407,14 +2550,14 @@ var es; // 我们计算两个相撞物体的响应。 // 计算的基础是沿碰撞表面法线反射的物体的相对速度。 // 然后,响应的一部分会根据质量加到每个物体上 - var relativeVelocity = es.Vector2.subtract(this.velocity, other.velocity); - this.calculateResponseVelocity(relativeVelocity, minimumTranslationVector, relativeVelocity); + var relativeVelocity = this.velocity.sub(other.velocity); + relativeVelocity = this.calculateResponseVelocity(relativeVelocity, minimumTranslationVector); // 现在,我们使用质量来线性缩放两个刚体上的响应 var totalinverseMass = this._inverseMass + other._inverseMass; var ourResponseFraction = this._inverseMass / totalinverseMass; var otherResponseFraction = other._inverseMass / totalinverseMass; - this.velocity = this.velocity.add(es.Vector2.multiplyScaler(relativeVelocity, ourResponseFraction)); - other.velocity = other.velocity.subtract(es.Vector2.multiplyScaler(relativeVelocity, otherResponseFraction)); + this.velocity = this.velocity.add(relativeVelocity.scale(ourResponseFraction)); + other.velocity = other.velocity.sub(relativeVelocity.scale(otherResponseFraction)); }; /** * 给定两个物体和MTV之间的相对速度,本方法修改相对速度,使其成为碰撞响应 @@ -2422,16 +2565,15 @@ var es; * @param minimumTranslationVector * @param responseVelocity */ - ArcadeRigidbody.prototype.calculateResponseVelocity = function (relativeVelocity, minimumTranslationVector, responseVelocity) { - if (responseVelocity === void 0) { responseVelocity = new es.Vector2(); } + ArcadeRigidbody.prototype.calculateResponseVelocity = function (relativeVelocity, minimumTranslationVector) { // 首先,我们得到反方向的归一化MTV:表面法线 - var inverseMTV = es.Vector2.multiplyScaler(minimumTranslationVector, -1); - var normal = es.Vector2.normalize(inverseMTV); + var inverseMTV = minimumTranslationVector.scale(-1); + var normal = inverseMTV.normalize(); // 速度是沿碰撞法线和碰撞平面分解的。 // 弹性将影响沿法线的响应(法线速度分量),摩擦力将影响速度的切向分量(切向速度分量) - var n = es.Vector2.dot(relativeVelocity, normal); - var normalVelocityComponent = es.Vector2.multiplyScaler(normal, n); - var tangentialVelocityComponent = es.Vector2.subtract(relativeVelocity, normalVelocityComponent); + var n = relativeVelocity.dot(normal); + var normalVelocityComponent = normal.scale(n); + var tangentialVelocityComponent = relativeVelocity.sub(normalVelocityComponent); if (n > 0) normalVelocityComponent = es.Vector2.zero; // 如果切向分量的平方幅度小于glue,那么我们就把摩擦力提升到最大 @@ -2439,14 +2581,430 @@ var es; if (tangentialVelocityComponent.lengthSquared() < this._glue) coefficientOfFriction = 1.01; // 弹性影响速度的法向分量,摩擦力影响速度的切向分量 - responseVelocity = es.Vector2.multiplyScaler(normalVelocityComponent, -(1 + this._elasticity)) - .subtract(es.Vector2.multiplyScaler(tangentialVelocityComponent, coefficientOfFriction)); + return normalVelocityComponent + .scale(1 + this._elasticity) + .sub(tangentialVelocityComponent.scale(coefficientOfFriction)) + .scale(-1); }; return ArcadeRigidbody; }(es.Component)); es.ArcadeRigidbody = ArcadeRigidbody; })(es || (es = {})); var es; +(function (es) { + var CharacterRaycastOrigins = /** @class */ (function () { + function CharacterRaycastOrigins() { + this.topLeft = es.Vector2.zero; + this.bottomRight = es.Vector2.zero; + this.bottomLeft = es.Vector2.zero; + } + return CharacterRaycastOrigins; + }()); + var CharacterCollisionState2D = /** @class */ (function () { + function CharacterCollisionState2D() { + this.right = false; + this.left = false; + this.above = false; + this.below = false; + this.becameGroundedThisFrame = false; + this.wasGroundedLastFrame = false; + this.movingDownSlope = false; + this.slopeAngle = 0; + } + CharacterCollisionState2D.prototype.hasCollision = function () { + return this.below || this.right || this.left || this.above; + }; + CharacterCollisionState2D.prototype.reset = function () { + this.right = this.left = false; + this.above = this.below = false; + this.becameGroundedThisFrame = this.movingDownSlope = false; + this.slopeAngle = 0; + }; + CharacterCollisionState2D.prototype.toString = function () { + return "[CharacterCollisionState2D] r: " + this.right + ", l: " + this.left + ", a: " + this.above + ", b: " + this.below + ", movingDownSlope: " + this.movingDownSlope + ", angle: " + this.slopeAngle + ", wasGroundedLastFrame: " + this.wasGroundedLastFrame + ", becameGroundedThisFrame: " + this.becameGroundedThisFrame; + }; + return CharacterCollisionState2D; + }()); + var CharacterController = /** @class */ (function () { + function CharacterController(player, skinWidth, platformMask, onewayPlatformMask, triggerMask) { + if (platformMask === void 0) { platformMask = -1; } + if (onewayPlatformMask === void 0) { onewayPlatformMask = -1; } + if (triggerMask === void 0) { triggerMask = -1; } + this.ignoredColliders = new Set(); + /** + * CC2D 可以爬升的最大坡度角 + */ + this.slopeLimit = 30; + /** + * 构成跳跃的帧之间垂直运动变化的阈值 + */ + this.jumpingThreshold = -7; + this.totalHorizontalRays = 5; + this.totalVerticalRays = 3; + this.collisionState = new CharacterCollisionState2D(); + this.velocity = new es.Vector2(0, 0); + this._skinWidth = 0.02; + this.kSkinWidthFloatFudgeFactor = 0.001; + /** + * 我们的光线投射原点角的支架(TR、TL、BR、BL) + */ + this._raycastOrigins = new CharacterRaycastOrigins(); + /** + * 存储我们在移动过程中命中的光线投射 + */ + this._raycastHit = new es.RaycastHit(); + /** + * 我们使用这个标志来标记我们正在爬坡的情况,我们修改了 delta.y 以允许爬升。 + * 原因是,如果我们到达斜坡的尽头,我们可以进行调整以保持接地 + */ + this._isGoingUpSlope = false; + this._isWarpingToGround = true; + this.platformMask = -1; + this.triggerMask = -1; + this.oneWayPlatformMask = -1; + this.rayOriginSkinMutiplier = 4; + this.onTriggerEnterEvent = new es.ObservableT(); + this.onTriggerExitEvent = new es.ObservableT(); + this.onControllerCollidedEvent = new es.ObservableT(); + this.platformMask = platformMask; + this.oneWayPlatformMask = onewayPlatformMask; + this.triggerMask = triggerMask; + // 将我们的单向平台添加到我们的普通平台掩码中,以便我们可以从上方降落 + this.platformMask |= this.oneWayPlatformMask; + this._player = player; + var collider = null; + for (var i = 0; i < this._player.components.buffer.length; i++) { + var component = this._player.components.buffer[i]; + if (component instanceof es.Collider) { + collider = component; + break; + } + } + collider.isTrigger = false; + if (collider instanceof es.BoxCollider) { + this._collider = collider; + } + else { + throw new Error('player collider must be box'); + } + // 在这里,我们触发了具有主体的 setter 的属性 + this.skinWidth = skinWidth || collider.width * 0.05; + this._slopeLimitTangent = Math.tan(75 * es.MathHelper.Deg2Rad); + this._triggerHelper = new es.ColliderTriggerHelper(this._player); + // 我们想设置我们的 CC2D 忽略所有碰撞层,除了我们的 triggerMask + for (var i = 0; i < 32; i++) { + // 查看我们的 triggerMask 是否包含此层,如果不包含则忽略它 + if ((this.triggerMask & (1 << i)) === 0) { + es.Flags.unsetFlag(this._collider.collidesWithLayers, i); + } + } + } + Object.defineProperty(CharacterController.prototype, "skinWidth", { + /** + * 定义距离碰撞射线的边缘有多远。 + * 如果使用 0 范围进行投射,则通常会导致不需要的光线击中(例如,直接从表面水平投射的足部碰撞器可能会导致击中) + */ + get: function () { + return this._skinWidth; + }, + set: function (value) { + this._skinWidth = value; + this.recalculateDistanceBetweenRays(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CharacterController.prototype, "isGrounded", { + get: function () { + return this.collisionState.below; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CharacterController.prototype, "raycastHitsThisFrame", { + get: function () { + return this._raycastHitsThisFrame; + }, + enumerable: true, + configurable: true + }); + CharacterController.prototype.onTriggerEnter = function (other, local) { + this.onTriggerEnterEvent.notify(other); + }; + CharacterController.prototype.onTriggerExit = function (other, local) { + this.onTriggerExitEvent.notify(other); + }; + /** + * 尝试将角色移动到位置 + deltaMovement。 任何挡路的碰撞器都会在遇到时导致运动停止 + * @param deltaMovement + * @param deltaTime + */ + CharacterController.prototype.move = function (deltaMovement, deltaTime) { + this.collisionState.wasGroundedLastFrame = this.collisionState.below; + this.collisionState.reset(); + this._raycastHitsThisFrame = []; + this._isGoingUpSlope = false; + this.primeRaycastOrigins(); + if (deltaMovement.y > 0 && this.collisionState.wasGroundedLastFrame) { + deltaMovement = this.handleVerticalSlope(deltaMovement); + } + if (deltaMovement.x !== 0) { + deltaMovement = this.moveHorizontally(deltaMovement); + } + if (deltaMovement.y !== 0) { + deltaMovement = this.moveVertically(deltaMovement); + } + this._player.setPosition(this._player.position.x + deltaMovement.x, this._player.position.y + deltaMovement.y); + if (deltaTime > 0) { + this.velocity.x = deltaMovement.x / deltaTime; + this.velocity.y = deltaMovement.y / deltaTime; + } + if (!this.collisionState.wasGroundedLastFrame && + this.collisionState.below) { + this.collisionState.becameGroundedThisFrame = true; + } + if (this._isGoingUpSlope) { + this.velocity.y = 0; + } + if (!this._isWarpingToGround) { + this._triggerHelper.update(); + } + for (var i = 0; i < this._raycastHitsThisFrame.length; i++) { + this.onControllerCollidedEvent.notify(this._raycastHitsThisFrame[i]); + } + if (this.ignoreOneWayPlatformsTime > 0) { + this.ignoreOneWayPlatformsTime -= deltaTime; + } + }; + /** + * 直接向下移动直到接地 + * @param maxDistance + */ + CharacterController.prototype.warpToGrounded = function (maxDistance) { + if (maxDistance === void 0) { maxDistance = 1000; } + this.ignoreOneWayPlatformsTime = 0; + this._isWarpingToGround = true; + var delta = 0; + do { + delta += 1; + this.move(new es.Vector2(0, 1), 0.02); + if (delta > maxDistance) { + break; + } + } while (!this.isGrounded); + this._isWarpingToGround = false; + }; + /** + * 这应该在您必须在运行时修改 BoxCollider2D 的任何时候调用。 + * 它将重新计算用于碰撞检测的光线之间的距离。 + * 它也用于 skinWidth setter,以防在运行时更改。 + */ + CharacterController.prototype.recalculateDistanceBetweenRays = function () { + var colliderUsableHeight = this._collider.height * Math.abs(this._player.scale.y) - + 2 * this._skinWidth; + this._verticalDistanceBetweenRays = + colliderUsableHeight / (this.totalHorizontalRays - 1); + var colliderUsableWidth = this._collider.width * Math.abs(this._player.scale.x) - + 2 * this._skinWidth; + this._horizontalDistanceBetweenRays = + colliderUsableWidth / (this.totalVerticalRays - 1); + }; + /** + * 将 raycastOrigins 重置为由 skinWidth 插入的框碰撞器的当前范围。 + * 插入它是为了避免从直接接触另一个碰撞器的位置投射光线,从而导致不稳定的法线数据。 + */ + CharacterController.prototype.primeRaycastOrigins = function () { + var rect = this._collider.bounds; + this._raycastOrigins.topLeft = new es.Vector2(rect.x + this._skinWidth, rect.y + this._skinWidth); + this._raycastOrigins.bottomRight = new es.Vector2(rect.right - this._skinWidth, rect.bottom - this._skinWidth); + this._raycastOrigins.bottomLeft = new es.Vector2(rect.x + this._skinWidth, rect.bottom - this._skinWidth); + }; + /** + * 我们必须在这方面使用一些技巧。 + * 光线必须从我们的碰撞器(skinWidth)内部的一小段距离投射,以避免零距离光线会得到错误的法线。 + * 由于这个小偏移,我们必须增加光线距离 skinWidth 然后记住在实际移动玩家之前从 deltaMovement 中删除 skinWidth + * @param deltaMovement + * @returns + */ + CharacterController.prototype.moveHorizontally = function (deltaMovement) { + var isGoingRight = deltaMovement.x > 0; + var rayDistance = Math.abs(deltaMovement.x) + + this._skinWidth * this.rayOriginSkinMutiplier; + var rayDirection = isGoingRight ? es.Vector2.right : es.Vector2.left; + var initialRayOriginY = this._raycastOrigins.bottomLeft.y; + var initialRayOriginX = isGoingRight + ? this._raycastOrigins.bottomRight.x - + this._skinWidth * (this.rayOriginSkinMutiplier - 1) + : this._raycastOrigins.bottomLeft.x + + this._skinWidth * (this.rayOriginSkinMutiplier - 1); + for (var i = 0; i < this.totalHorizontalRays; i++) { + var ray = new es.Vector2(initialRayOriginX, initialRayOriginY - i * this._verticalDistanceBetweenRays); + // 如果我们接地,我们将只在第一条射线(底部)上包含 oneWayPlatforms。 + // 允许我们走上倾斜的 oneWayPlatforms + if (i === 0 && + this.supportSlopedOneWayPlatforms && + this.collisionState.wasGroundedLastFrame) { + this._raycastHit = es.Physics.linecast(ray, ray.add(rayDirection.scaleEqual(rayDistance)), this.platformMask, this.ignoredColliders); + } + else { + this._raycastHit = es.Physics.linecast(ray, ray.add(rayDirection.scaleEqual(rayDistance)), this.platformMask & ~this.oneWayPlatformMask, this.ignoredColliders); + } + if (this._raycastHit.collider) { + if (i === 0 && + this.handleHorizontalSlope(deltaMovement, es.Vector2.unsignedAngle(this._raycastHit.normal, es.Vector2.up))) { + this._raycastHitsThisFrame.push(this._raycastHit); + break; + } + deltaMovement.x = this._raycastHit.point.x - ray.x; + rayDistance = Math.abs(deltaMovement.x); + if (isGoingRight) { + deltaMovement.x -= this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.right = true; + } + else { + deltaMovement.x += this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.left = true; + } + this._raycastHitsThisFrame.push(this._raycastHit); + if (rayDistance < + this._skinWidth * this.rayOriginSkinMutiplier + + this.kSkinWidthFloatFudgeFactor) { + break; + } + } + } + return deltaMovement; + }; + CharacterController.prototype.moveVertically = function (deltaMovement) { + var isGoingUp = deltaMovement.y < 0; + var rayDistance = Math.abs(deltaMovement.y) + + this._skinWidth * this.rayOriginSkinMutiplier; + var rayDirection = isGoingUp ? es.Vector2.up : es.Vector2.down; + var initialRayOriginX = this._raycastOrigins.topLeft.x; + var initialRayOriginY = isGoingUp + ? this._raycastOrigins.topLeft.y + + this._skinWidth * (this.rayOriginSkinMutiplier - 1) + : this._raycastOrigins.bottomLeft.y - + this._skinWidth * (this.rayOriginSkinMutiplier - 1); + initialRayOriginX += deltaMovement.x; + var mask = this.platformMask; + if (isGoingUp || this.ignoreOneWayPlatformsTime > 0) { + mask &= ~this.oneWayPlatformMask; + } + for (var i = 0; i < this.totalVerticalRays; i++) { + var rayStart = new es.Vector2(initialRayOriginX + i * this._horizontalDistanceBetweenRays, initialRayOriginY); + this._raycastHit = es.Physics.linecast(rayStart, rayStart.add(rayDirection.scaleEqual(rayDistance)), mask, this.ignoredColliders); + if (this._raycastHit.collider) { + deltaMovement.y = this._raycastHit.point.y - rayStart.y; + rayDistance = Math.abs(deltaMovement.y); + if (isGoingUp) { + deltaMovement.y += this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.above = true; + } + else { + deltaMovement.y -= this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.below = true; + } + this._raycastHitsThisFrame.push(this._raycastHit); + if (!isGoingUp && deltaMovement.y < -0.00001) { + this._isGoingUpSlope = true; + } + if (rayDistance < + this._skinWidth * this.rayOriginSkinMutiplier + + this.kSkinWidthFloatFudgeFactor) { + break; + } + } + } + return deltaMovement; + }; + /** + * 检查 BoxCollider2D 下的中心点是否存在坡度。 + * 如果找到一个,则调整 deltaMovement 以便玩家保持接地,并考虑slopeSpeedModifier 以加快移动速度。 + * @param deltaMovement + * @returns + */ + CharacterController.prototype.handleVerticalSlope = function (deltaMovement) { + var centerOfCollider = (this._raycastOrigins.bottomLeft.x + + this._raycastOrigins.bottomRight.x) * + 0.5; + var rayDirection = es.Vector2.down; + var slopeCheckRayDistance = this._slopeLimitTangent * + (this._raycastOrigins.bottomRight.x - centerOfCollider); + var slopeRay = new es.Vector2(centerOfCollider, this._raycastOrigins.bottomLeft.y); + this._raycastHit = es.Physics.linecast(slopeRay, slopeRay.add(rayDirection.scaleEqual(slopeCheckRayDistance)), this.platformMask, this.ignoredColliders); + if (this._raycastHit.collider) { + var angle = es.Vector2.unsignedAngle(this._raycastHit.normal, es.Vector2.up); + if (angle === 0) { + return deltaMovement; + } + var isMovingDownSlope = Math.sign(this._raycastHit.normal.x) === Math.sign(deltaMovement.x); + if (isMovingDownSlope) { + var slopeModifier = this.slopeSpeedMultiplier + ? this.slopeSpeedMultiplier.lerp(-angle) + : 1; + deltaMovement.y += + this._raycastHit.point.y - slopeRay.y - this.skinWidth; + deltaMovement.x *= slopeModifier; + this.collisionState.movingDownSlope = true; + this.collisionState.slopeAngle = angle; + } + } + return deltaMovement; + }; + /** + * 如果我们要上坡,则处理调整 deltaMovement + * @param deltaMovement + * @param angle + * @returns + */ + CharacterController.prototype.handleHorizontalSlope = function (deltaMovement, angle) { + if (Math.round(angle) === 90) { + return false; + } + if (angle < this.slopeLimit) { + if (deltaMovement.y > this.jumpingThreshold) { + var slopeModifier = this.slopeSpeedMultiplier + ? this.slopeSpeedMultiplier.lerp(angle) + : 1; + deltaMovement.x *= slopeModifier; + deltaMovement.y = Math.abs(Math.tan(angle * es.MathHelper.Deg2Rad) * deltaMovement.x); + var isGoingRight = deltaMovement.x > 0; + var ray = isGoingRight + ? this._raycastOrigins.bottomRight + : this._raycastOrigins.bottomLeft; + var raycastHit = null; + if (this.supportSlopedOneWayPlatforms && + this.collisionState.wasGroundedLastFrame) { + raycastHit = es.Physics.linecast(ray, ray.add(deltaMovement), this.platformMask, this.ignoredColliders); + } + else { + raycastHit = es.Physics.linecast(ray, ray.add(deltaMovement), this.platformMask & ~this.oneWayPlatformMask, this.ignoredColliders); + } + if (raycastHit.collider) { + deltaMovement.x = raycastHit.point.x - ray.x; + deltaMovement.y = raycastHit.point.y - ray.y; + if (isGoingRight) { + deltaMovement.x -= this._skinWidth; + } + else { + deltaMovement.x += this._skinWidth; + } + } + this._isGoingUpSlope = true; + this.collisionState.below = true; + } + } + else { + deltaMovement.x = 0; + } + return true; + }; + return CharacterController; + }()); + es.CharacterController = CharacterController; +})(es || (es = {})); +var es; (function (es) { var TriggerListenerHelper = /** @class */ (function () { function TriggerListenerHelper() { @@ -2504,39 +3062,63 @@ var es; * @param collisionResult */ Mover.prototype.calculateMovement = function (motion, collisionResult) { - if (this.entity.getComponent(es.Collider) == null || this._triggerHelper == null) { + var e_3, _a; + var collider = null; + for (var i = 0; i < this.entity.components.buffer.length; i++) { + var component = this.entity.components.buffer[i]; + if (component instanceof es.Collider) { + collider = component; + break; + } + } + if (collider == null || this._triggerHelper == null) { return false; } // 移动所有的非触发碰撞器并获得最近的碰撞 - var colliders = this.entity.getComponents(es.Collider); - var _loop_1 = function (i) { - var collider = colliders[i]; + var colliders = []; + for (var i = 0; i < this.entity.components.buffer.length; i++) { + var component = this.entity.components.buffer[i]; + if (component instanceof es.Collider) { + colliders.push(component); + } + } + for (var i = 0; i < colliders.length; i++) { + var collider_1 = colliders[i]; // 不检测触发器 在我们移动后会重新访问它 - if (collider.isTrigger) - return "continue"; + if (collider_1.isTrigger) + continue; // 获取我们在新位置可能发生碰撞的任何东西 - var bounds = collider.bounds.clone(); + var bounds = collider_1.bounds.clone(); bounds.x += motion.x; bounds.y += motion.y; - var neighbors = es.Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers.value); - neighbors.forEach(function (value) { - var neighbor = value; - // 不检测触发器 - if (neighbor.isTrigger) - return; - var _internalcollisionResult = new es.CollisionResult(); - if (collider.collidesWith(neighbor, motion, _internalcollisionResult)) { - // 如果碰撞 则退回之前的移动量 - motion.subtract(_internalcollisionResult.minimumTranslationVector); - // 如果我们碰到多个对象,为了简单起见,只取第一个。 - if (_internalcollisionResult.collider != null) { - collisionResult = _internalcollisionResult; + var neighbors = es.Physics.boxcastBroadphaseExcludingSelf(collider_1, bounds, collider_1.collidesWithLayers.value); + try { + for (var neighbors_2 = __values(neighbors), neighbors_2_1 = neighbors_2.next(); !neighbors_2_1.done; neighbors_2_1 = neighbors_2.next()) { + var neighbor = neighbors_2_1.value; + // 不检测触发器 + if (neighbor.isTrigger) + return; + var _internalcollisionResult = new es.CollisionResult(); + if (collider_1.collidesWith(neighbor, motion, _internalcollisionResult)) { + // 如果碰撞 则退回之前的移动量 + motion.sub(_internalcollisionResult.minimumTranslationVector); + // 如果我们碰到多个对象,为了简单起见,只取第一个。 + if (_internalcollisionResult.collider != null) { + collisionResult.collider = _internalcollisionResult.collider; + collisionResult.minimumTranslationVector = _internalcollisionResult.minimumTranslationVector; + collisionResult.normal = _internalcollisionResult.normal; + collisionResult.point = _internalcollisionResult.point; + } } } - }); - }; - for (var i = 0; i < colliders.length; i++) { - _loop_1(i); + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (neighbors_2_1 && !neighbors_2_1.done && (_a = neighbors_2.return)) _a.call(neighbors_2); + } + finally { if (e_3) throw e_3.error; } + } } es.ListPool.free(colliders); return collisionResult.collider != null; @@ -2580,7 +3162,15 @@ var es; return _this; } ProjectileMover.prototype.onAddedToEntity = function () { - this._collider = this.entity.getComponent(es.Collider); + var collider = null; + for (var i = 0; i < this.entity.components.buffer.length; i++) { + var component = this.entity.components.buffer[i]; + if (component instanceof es.Collider) { + collider = component; + break; + } + } + this._collider = collider; es.Debug.warnIf(this._collider == null, "ProjectileMover没有Collider。ProjectilMover需要一个Collider!"); }; /** @@ -2588,7 +3178,7 @@ var es; * @param motion */ ProjectileMover.prototype.move = function (motion) { - var e_3, _a; + var e_4, _a; if (this._collider == null) return false; var didCollide = false; @@ -2597,20 +3187,20 @@ var es; // 获取任何可能在新位置发生碰撞的东西 var neighbors = es.Physics.boxcastBroadphase(this._collider.bounds, this._collider.collidesWithLayers.value); try { - for (var neighbors_2 = __values(neighbors), neighbors_2_1 = neighbors_2.next(); !neighbors_2_1.done; neighbors_2_1 = neighbors_2.next()) { - var neighbor = neighbors_2_1.value; + for (var neighbors_3 = __values(neighbors), neighbors_3_1 = neighbors_3.next(); !neighbors_3_1.done; neighbors_3_1 = neighbors_3.next()) { + var neighbor = neighbors_3_1.value; if (this._collider.overlaps(neighbor) && neighbor.enabled) { didCollide = true; this.notifyTriggerListeners(this._collider, neighbor); } } } - catch (e_3_1) { e_3 = { error: e_3_1 }; } + catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { - if (neighbors_2_1 && !neighbors_2_1.done && (_a = neighbors_2.return)) _a.call(neighbors_2); + if (neighbors_3_1 && !neighbors_3_1.done && (_a = neighbors_3.return)) _a.call(neighbors_3); } - finally { if (e_3) throw e_3.error; } + finally { if (e_4) throw e_4.error; } } return didCollide; }; @@ -2638,6 +3228,7 @@ var es; __extends(Collider, _super); function Collider() { var _this = _super !== null && _super.apply(this, arguments) || this; + _this.castSortOrder = 0; /** * 如果这个碰撞器是一个触发器,它将不会引起碰撞,但它仍然会触发事件 */ @@ -2725,8 +3316,8 @@ var es; Collider.prototype.setLocalOffset = function (offset) { if (!this._localOffset.equals(offset)) { this.unregisterColliderWithPhysicsSystem(); - this._localOffset = offset; - this._localOffsetLength = this._localOffset.length(); + this._localOffset.setTo(offset.x, offset.y); + this._localOffsetLength = this._localOffset.magnitude(); this._isPositionDirty = true; this.registerColliderWithPhysicsSystem(); } @@ -2742,6 +3333,30 @@ var es; return this; }; Collider.prototype.onAddedToEntity = function () { + if (this._colliderRequiresAutoSizing) { + var renderable = null; + for (var i = 0; i < this.entity.components.buffer.length; i++) { + var component = this.entity.components.buffer[i]; + if (component instanceof es.RenderableComponent) { + renderable = component; + break; + } + } + if (renderable != null) { + var renderableBounds = renderable.bounds.clone(); + var width = renderableBounds.width / this.entity.transform.scale.x; + var height = renderableBounds.height / this.entity.transform.scale.y; + if (this instanceof es.CircleCollider) { + this.radius = Math.max(width, height) * 0.5; + this.localOffset = renderableBounds.center.sub(this.entity.transform.position); + } + else if (this instanceof es.BoxCollider) { + this.width = width; + this.height = height; + this.localOffset = renderableBounds.center.sub(this.entity.transform.position); + } + } + } this._isParentEntityAddedToScene = true; this.registerColliderWithPhysicsSystem(); }; @@ -2751,13 +3366,13 @@ var es; }; Collider.prototype.onEntityTransformChanged = function (comp) { switch (comp) { - case transform.Component.position: + case es.ComponentTransform.position: this._isPositionDirty = true; break; - case transform.Component.scale: + case es.ComponentTransform.scale: this._isPositionDirty = true; break; - case transform.Component.rotation: + case es.ComponentTransform.rotation: this._isRotationDirty = true; break; } @@ -2806,8 +3421,8 @@ var es; Collider.prototype.collidesWith = function (collider, motion, result) { if (result === void 0) { result = new es.CollisionResult(); } // 改变形状的位置,使它在移动后的位置,这样我们可以检查重叠 - var oldPosition = this.entity.position.clone(); - this.entity.position = es.Vector2.add(this.entity.position, motion); + var oldPosition = this.entity.position; + this.entity.position = this.entity.position.add(motion); var didCollide = this.shape.collidesWithShape(collider.shape, result); if (didCollide) result.collider = collider; @@ -2826,6 +3441,7 @@ var es; result.collider = collider; return true; } + result.collider = null; return false; }; /** @@ -2835,7 +3451,7 @@ var es; * @param result */ Collider.prototype.collidesWithAny = function (motion, result) { - var e_4, _a; + var e_5, _a; // 在我们的新位置上获取我们可能会碰到的任何东西 var colliderBounds = this.bounds.clone(); colliderBounds.x += motion.x; @@ -2845,45 +3461,16 @@ var es; var oldPosition = this.shape.position.clone(); this.shape.position = es.Vector2.add(this.shape.position, motion); var didCollide = false; - try { - for (var neighbors_3 = __values(neighbors), neighbors_3_1 = neighbors_3.next(); !neighbors_3_1.done; neighbors_3_1 = neighbors_3.next()) { - var neighbor = neighbors_3_1.value; - if (neighbor.isTrigger) - continue; - if (this.collidesWithNonMotion(neighbor, result)) { - motion = es.Vector2.subtract(motion, result.minimumTranslationVector); - this.shape.position = es.Vector2.subtract(this.shape.position, result.minimumTranslationVector); - didCollide = true; - } - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (neighbors_3_1 && !neighbors_3_1.done && (_a = neighbors_3.return)) _a.call(neighbors_3); - } - finally { if (e_4) throw e_4.error; } - } - // 将形状位置返回到检查之前的位置 - this.shape.position = oldPosition; - return didCollide; - }; - /** - * 检查此碰撞器是否与场景中的其他碰撞器碰撞。它相交的第一个碰撞器将在碰撞结果中返回碰撞数据。 - * @param result - */ - Collider.prototype.collidesWithAnyNonMotion = function (result) { - if (result === void 0) { result = new es.CollisionResult(); } - var e_5, _a; - // 在我们的新位置上获取我们可能会碰到的任何东西 - var neighbors = es.Physics.boxcastBroadphaseExcludingSelfNonRect(this, this.collidesWithLayers.value); try { for (var neighbors_4 = __values(neighbors), neighbors_4_1 = neighbors_4.next(); !neighbors_4_1.done; neighbors_4_1 = neighbors_4.next()) { var neighbor = neighbors_4_1.value; if (neighbor.isTrigger) continue; - if (this.collidesWithNonMotion(neighbor, result)) - return true; + if (this.collidesWithNonMotion(neighbor, result)) { + motion = motion.sub(result.minimumTranslationVector); + this.shape.position = this.shape.position.sub(result.minimumTranslationVector); + didCollide = true; + } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } @@ -2893,8 +3480,38 @@ var es; } finally { if (e_5) throw e_5.error; } } + // 将形状位置返回到检查之前的位置 + this.shape.position = oldPosition.clone(); + return didCollide; + }; + /** + * 检查此碰撞器是否与场景中的其他碰撞器碰撞。它相交的第一个碰撞器将在碰撞结果中返回碰撞数据。 + * @param result + */ + Collider.prototype.collidesWithAnyNonMotion = function (result) { + if (result === void 0) { result = new es.CollisionResult(); } + var e_6, _a; + // 在我们的新位置上获取我们可能会碰到的任何东西 + var neighbors = es.Physics.boxcastBroadphaseExcludingSelfNonRect(this, this.collidesWithLayers.value); + try { + for (var neighbors_5 = __values(neighbors), neighbors_5_1 = neighbors_5.next(); !neighbors_5_1.done; neighbors_5_1 = neighbors_5.next()) { + var neighbor = neighbors_5_1.value; + if (neighbor.isTrigger) + continue; + if (this.collidesWithNonMotion(neighbor, result)) + return true; + } + } + catch (e_6_1) { e_6 = { error: e_6_1 }; } + finally { + try { + if (neighbors_5_1 && !neighbors_5_1.done && (_a = neighbors_5.return)) _a.call(neighbors_5); + } + finally { if (e_6) throw e_6.error; } + } return false; }; + Collider.lateSortOrder = 999; return Collider; }(es.Component)); es.Collider = Collider; @@ -2913,8 +3530,17 @@ var es; * @param height */ function BoxCollider(x, y, width, height) { + if (x === void 0) { x = 0; } + if (y === void 0) { y = 0; } + if (width === void 0) { width = 1; } + if (height === void 0) { height = 1; } var _this = _super.call(this) || this; - _this._localOffset = new es.Vector2(x + width / 2, y + height / 2); + if (width == 1 && height == 1) { + _this._colliderRequiresAutoSizing = true; + } + else { + _this._localOffset = new es.Vector2(x + width / 2, y + height / 2); + } _this.shape = new es.Box(width, height); return _this; } @@ -2966,7 +3592,7 @@ var es; // 更新框,改变边界,如果我们需要更新物理系统中的边界 box.updateBox(width, box.height); this._isPositionDirty = true; - if (this.entity && this._isParentEntityAddedToScene) + if (this.entity != null && this._isParentEntityAddedToScene) es.Physics.updateCollider(this); } return this; @@ -2986,6 +3612,17 @@ var es; es.Physics.updateCollider(this); } }; + BoxCollider.prototype.debugRender = function (batcher) { + var poly = this.shape; + batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new es.Color(76, 76, 76, 76), 2); + batcher.end(); + batcher.drawPolygon(this.shape.position, poly.points, new es.Color(139, 0, 0, 255), true, 2); + batcher.end(); + batcher.drawPixel(this.entity.position, new es.Color(255, 255, 0), 4); + batcher.end(); + batcher.drawPixel(es.Vector2.add(this.transform.position, this.shape.center), new es.Color(255, 0, 0), 2); + batcher.end(); + }; BoxCollider.prototype.toString = function () { return "[BoxCollider: bounds: " + this.bounds + "]"; }; @@ -3005,8 +3642,12 @@ var es; * @param radius */ function CircleCollider(radius) { + if (radius === void 0) { radius = 1; } var _this = _super.call(this) || this; _this.shape = new es.Circle(radius); + if (radius == 1) { + _this._colliderRequiresAutoSizing = true; + } return _this; } Object.defineProperty(CircleCollider.prototype, "radius", { @@ -3035,6 +3676,16 @@ var es; } return this; }; + CircleCollider.prototype.debugRender = function (batcher) { + batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new es.Color(76, 76, 76, 76), 2); + batcher.end(); + batcher.drawCircle(this.shape.position, this.radius, new es.Color(139, 0, 0), 2); + batcher.end(); + batcher.drawPixel(this.entity.transform.position, new es.Color(255, 255, 0), 4); + batcher.end(); + batcher.drawPixel(this.shape.position, new es.Color(255, 0, 0), 2); + batcher.end(); + }; CircleCollider.prototype.toString = function () { return "[CircleCollider: bounds: " + this.bounds + ", radius: " + this.shape.radius + "]"; }; @@ -3057,10 +3708,9 @@ var es; var _this = _super.call(this) || this; // 第一点和最后一点决不能相同。我们想要一个开放的多边形 var isPolygonClosed = points[0] == points[points.length - 1]; - var linqPoints = new es.List(points); // 最后一个移除 if (isPolygonClosed) - linqPoints.remove(linqPoints.last()); + points = points.slice(0, points.length - 1); var center = es.Polygon.findPolygonCenter(points); _this.setLocalOffset(center); es.Polygon.recenterPolygonVerts(points); @@ -3072,6 +3722,134 @@ var es; es.PolygonCollider = PolygonCollider; })(es || (es = {})); var es; +(function (es) { + var RenderableComponent = /** @class */ (function (_super) { + __extends(RenderableComponent, _super); + function RenderableComponent() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this._bounds = new es.Rectangle(); + _this._areBoundsDirty = true; + _this.color = es.Color.White; + _this._renderLayer = 0; + _this.debugRenderEnabled = true; + _this._isVisible = false; + _this._localOffset = new es.Vector2(); + return _this; + } + RenderableComponent.prototype.getwidth = function () { + return this.bounds.width; + }; + RenderableComponent.prototype.getheight = function () { + return this.bounds.height; + }; + RenderableComponent.prototype.getbounds = function () { + if (this._areBoundsDirty) { + this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, new es.Vector2(this.getwidth() / 2, this.getheight() / 2), this.entity.transform.scale, this.entity.transform.rotation, this.getwidth(), this.getheight()); + this._areBoundsDirty = false; + } + return this._bounds; + }; + Object.defineProperty(RenderableComponent.prototype, "bounds", { + get: function () { + return this.getbounds(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(RenderableComponent.prototype, "renderLayer", { + get: function () { + return this._renderLayer; + }, + set: function (value) { + this.setRenderLayer(value); + }, + enumerable: true, + configurable: true + }); + RenderableComponent.prototype.onEntityTransformChanged = function (comp) { + this._areBoundsDirty = true; + }; + Object.defineProperty(RenderableComponent.prototype, "localOffset", { + get: function () { + return this._localOffset; + }, + set: function (value) { + this.setLocalOffset(value); + }, + enumerable: true, + configurable: true + }); + RenderableComponent.prototype.setLocalOffset = function (offset) { + if (!this._localOffset.equals(offset)) { + this._localOffset = offset; + this._areBoundsDirty = true; + } + return this; + }; + Object.defineProperty(RenderableComponent.prototype, "isVisible", { + get: function () { + return this._isVisible; + }, + set: function (value) { + if (this._isVisible != value) { + this._isVisible = value; + if (this._isVisible) { + this.onBecameVisible(); + } + else { + this.onBecameInvisible(); + } + } + }, + enumerable: true, + configurable: true + }); + RenderableComponent.prototype.onBecameVisible = function () { + }; + RenderableComponent.prototype.onBecameInvisible = function () { + }; + RenderableComponent.prototype.setRenderLayer = function (renderLayer) { + if (renderLayer != this._renderLayer) { + var oldRenderLayer = this._renderLayer; + this._renderLayer = renderLayer; + if (this.entity != null && this.entity.scene != null) + es.Core.scene.renderableComponents.updateRenderableRenderLayer(this, oldRenderLayer, this._renderLayer); + } + return this; + }; + RenderableComponent.prototype.isVisibleFromCamera = function (cam) { + this.isVisible = cam.bounds.intersects(this.bounds); + return this.isVisible; + }; + RenderableComponent.prototype.debugRender = function (batcher) { + if (!this.debugRenderEnabled) + return; + var collider = null; + for (var i = 0; i < this.entity.components.buffer.length; i++) { + var component = this.entity.components.buffer[i]; + if (component instanceof es.Collider) { + collider = component; + break; + } + } + if (collider == null) { + batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new es.Color(255, 255, 0)); + batcher.end(); + } + batcher.drawPixel(es.Vector2.add(this.entity.transform.position, this._localOffset), new es.Color(153, 50, 204), 4); + batcher.end(); + }; + RenderableComponent.prototype.tweenColorTo = function (to, duration) { + var tween = es.Pool.obtain(es.RenderableColorTween); + tween.setTarget(this); + tween.initialize(tween, to, duration); + return tween; + }; + return RenderableComponent; + }(es.Component)); + es.RenderableComponent = RenderableComponent; +})(es || (es = {})); +var es; (function (es) { function decode(key) { switch (typeof key) { @@ -3517,7 +4295,7 @@ var es; var _this = this; var remainder = entities.length & this._threads; var slice = entities.length / this._threads + (remainder == 0 ? 0 : 1); - var _loop_2 = function (t) { + var _loop_1 = function (t) { var from = t * slice; var to = from + slice; if (to > entities.length) { @@ -3540,7 +4318,7 @@ var es; }; var this_1 = this; for (var t = 0; t < this._threads; t++) { - _loop_2(t); + _loop_1(t); } }; JobSystem.prototype.queueOnThread = function () { @@ -3701,15 +4479,15 @@ var es; for (var i = 0, s = this._components.length; i < s; ++i) { this.handleRemove(this._components[i]); } - this.componentsByType.clear(); - this.componentsToAddByType.clear(); - this._components.length = 0; - this._updatableComponents.length = 0; - this._componentsToAdd = {}; - this._componentsToRemove = {}; - this._componentsToAddList.length = 0; - this._componentsToRemoveList.length = 0; } + this.componentsByType.clear(); + this.componentsToAddByType.clear(); + this._components.length = 0; + this._updatableComponents.length = 0; + this._componentsToAdd = {}; + this._componentsToRemove = {}; + this._componentsToAddList.length = 0; + this._componentsToRemoveList.length = 0; }; ComponentList.prototype.deregisterAllComponents = function () { if (this._components.length > 0) { @@ -3717,6 +4495,8 @@ var es; var component = this._components[i]; if (!component) continue; + if (component instanceof es.RenderableComponent) + this._entity.scene.renderableComponents.remove(component); // 处理IUpdatable if (es.isIUpdatable(component)) new es.List(this._updatableComponents).remove(component); @@ -3729,6 +4509,8 @@ var es; if (this._components.length > 0) { for (var i = 0, s = this._components.length; i < s; ++i) { var component = this._components[i]; + if (component instanceof es.RenderableComponent) + this._entity.scene.renderableComponents.remove(component); if (es.isIUpdatable(component)) this._updatableComponents.push(component); this.addBits(component); @@ -3751,7 +4533,7 @@ var es; */ ComponentList.prototype.updateLists = function () { if (this._componentsToRemoveList.length > 0) { - var _loop_3 = function (i, l) { + var _loop_2 = function (i, l) { var component = this_2._componentsToRemoveList[i]; this_2.handleRemove(component); var index = this_2._components.findIndex(function (c) { return c.id == component.id; }); @@ -3761,7 +4543,7 @@ var es; }; var this_2 = this; for (var i = 0, l = this._componentsToRemoveList.length; i < l; ++i) { - _loop_3(i, l); + _loop_2(i, l); } this._componentsToRemove = {}; this._componentsToRemoveList.length = 0; @@ -3769,6 +4551,8 @@ var es; if (this._componentsToAddList.length > 0) { for (var i = 0, l = this._componentsToAddList.length; i < l; ++i) { var component = this._componentsToAddList[i]; + if (component instanceof es.RenderableComponent) + this._entity.scene.renderableComponents.add(component); if (es.isIUpdatable(component)) this._updatableComponents.push(component); this.addBits(component); @@ -3797,6 +4581,8 @@ var es; } }; ComponentList.prototype.handleRemove = function (component) { + if (component instanceof es.RenderableComponent) + this._entity.scene.renderableComponents.remove(component); if (es.isIUpdatable(component) && this._updatableComponents.length > 0) { var index = this._updatableComponents.findIndex(function (c) { return c.id == component.id; }); if (index != -1) @@ -3908,6 +4694,15 @@ var es; this._components[i].onDisabled(); } }; + ComponentList.prototype.debugRender = function (batcher) { + if (!batcher) + return; + for (var i = 0; i < this._components.length; i++) { + if (this._components[i].enabled) { + this._components[i].debugRender(batcher); + } + } + }; /** * 组件列表的全局updateOrder排序 */ @@ -4091,7 +4886,7 @@ var es; }; EntityList.prototype.updateLists = function () { if (this._entitiesToRemoveList.length > 0) { - var _loop_4 = function (i, s) { + var _loop_3 = function (i, s) { var entity = this_3._entitiesToRemoveList[i]; this_3.removeFromTagList(entity); // 处理常规实体列表 @@ -4104,7 +4899,7 @@ var es; }; var this_3 = this; for (var i = 0, s = this._entitiesToRemoveList.length; i < s; ++i) { - _loop_4(i, s); + _loop_3(i, s); } this._entitiesToRemove = {}; this._entitiesToRemoveList.length = 0; @@ -4167,7 +4962,7 @@ var es; * @param tag */ EntityList.prototype.entitiesWithTag = function (tag) { - var e_6, _a; + var e_7, _a; var list = this.getTagList(tag); var returnList = es.ListPool.obtain(); if (list.size > 0) { @@ -4177,12 +4972,12 @@ var es; returnList.push(entity); } } - catch (e_6_1) { e_6 = { error: e_6_1 }; } + catch (e_7_1) { e_7 = { error: e_7_1 }; } finally { try { if (list_1_1 && !list_1_1.done && (_a = list_1.return)) _a.call(list_1); } - finally { if (e_6) throw e_6.error; } + finally { if (e_7) throw e_7.error; } } } return returnList; @@ -4193,7 +4988,7 @@ var es; * @returns */ EntityList.prototype.entityWithTag = function (tag) { - var e_7, _a; + var e_8, _a; var list = this.getTagList(tag); if (list.size > 0) { try { @@ -4202,12 +4997,12 @@ var es; return entity; } } - catch (e_7_1) { e_7 = { error: e_7_1 }; } + catch (e_8_1) { e_8 = { error: e_8_1 }; } finally { try { if (list_2_1 && !list_2_1.done && (_a = list_2.return)) _a.call(list_2); } - finally { if (e_7) throw e_7.error; } + finally { if (e_8) throw e_8.error; } } } return null; @@ -4580,6 +5375,82 @@ var es; }()); es.Matcher = Matcher; })(es || (es = {})); +var es; +(function (es) { + var RenderableComponentList = /** @class */ (function () { + function RenderableComponentList() { + this._components = []; + this._componentsByRenderLayer = new Map(); + this._unsortedRenderLayers = []; + this._componentsNeedSort = true; + } + Object.defineProperty(RenderableComponentList.prototype, "count", { + get: function () { + return this._components.length; + }, + enumerable: true, + configurable: true + }); + RenderableComponentList.prototype.get = function (index) { + return this._components[index]; + }; + RenderableComponentList.prototype.add = function (component) { + this._components.push(component); + this.addToRenderLayerList(component, component.renderLayer); + }; + RenderableComponentList.prototype.remove = function (component) { + new es.List(this._components).remove(component); + new es.List(this._componentsByRenderLayer.get(component.renderLayer)).remove(component); + }; + RenderableComponentList.prototype.updateRenderableRenderLayer = function (component, oldRenderLayer, newRenderLayer) { + if (this._componentsByRenderLayer.has(oldRenderLayer) && new es.List(this._componentsByRenderLayer.get(oldRenderLayer)).contains(component)) { + new es.List(this._componentsByRenderLayer.get(oldRenderLayer)).remove(component); + this.addToRenderLayerList(component, newRenderLayer); + } + }; + RenderableComponentList.prototype.setRenderLayerNeedsComponentSort = function (renderLayer) { + var unsortedRenderLayersList = new es.List(this._unsortedRenderLayers); + if (!unsortedRenderLayersList.contains(renderLayer)) + unsortedRenderLayersList.add(renderLayer); + this._componentsNeedSort = true; + }; + RenderableComponentList.prototype.setNeedsComponentSort = function () { + this._componentsNeedSort = true; + }; + RenderableComponentList.prototype.addToRenderLayerList = function (component, renderLayer) { + var list = this.componentsWithRenderLayer(renderLayer); + es.Insist.isFalse(!!list.find(function (c) { return c == component; }), "组件renderLayer列表已包含此组件"); + list.push(component); + var unsortedRenderLayersList = new es.List(this._unsortedRenderLayers); + if (!unsortedRenderLayersList.contains(renderLayer)) + unsortedRenderLayersList.add(renderLayer); + this._componentsNeedSort = true; + }; + RenderableComponentList.prototype.componentsWithRenderLayer = function (renderLayer) { + if (!this._componentsByRenderLayer.get(renderLayer)) { + this._componentsByRenderLayer.set(renderLayer, []); + } + return this._componentsByRenderLayer.get(renderLayer); + }; + RenderableComponentList.prototype.updateLists = function () { + if (this._componentsNeedSort) { + this._components.sort(function (self, other) { return other.renderLayer - self.renderLayer; }); + this._componentsNeedSort = false; + } + if (this._unsortedRenderLayers.length > 0) { + for (var i = 0, count = this._unsortedRenderLayers.length; i < count; i++) { + var renderLayerComponents = this._componentsByRenderLayer.get(this._unsortedRenderLayers[i]); + if (renderLayerComponents) { + renderLayerComponents.sort(function (self, other) { return other.renderLayer - self.renderLayer; }); + } + this._unsortedRenderLayers.length = 0; + } + } + }; + return RenderableComponentList; + }()); + es.RenderableComponentList = RenderableComponentList; +})(es || (es = {})); var StringUtils = /** @class */ (function () { function StringUtils() { } @@ -5118,6 +5989,490 @@ var es; es.WorkerUtils = WorkerUtils; })(es || (es = {})); var es; +(function (es) { + var Graphics = /** @class */ (function () { + function Graphics(batcher) { + this.batcher = batcher; + } + return Graphics; + }()); + es.Graphics = Graphics; +})(es || (es = {})); +var es; +(function (es) { + var Color = /** @class */ (function () { + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + function Color(r, g, b, a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a != null ? a : 1; + } + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + Color.fromRGB = function (r, g, b, a) { + return new Color(r, g, b, a); + }; + /** + * 从十六进制字符串创建一个新的 Color 实例 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + Color.createFromHex = function (hex) { + var color = new Color(1, 1, 1); + color.fromHex(hex); + return color; + }; + /** + * 从 hsl 值创建一个新的 Color 实例 + * + * @param h 色调表示 [0-1] + * @param s 饱和度表示为 [0-1] + * @param l 亮度表示 [0-1] + * @param a 透明度表示 [0-1] + */ + Color.fromHSL = function (h, s, l, a) { + if (a === void 0) { a = 1.0; } + var temp = new HSLColor(h, s, l, a); + return temp.toRGBA(); + }; + /** + * 将当前颜色调亮指定的量 + * + * @param factor + */ + Color.prototype.lighten = function (factor) { + if (factor === void 0) { factor = 0.1; } + var temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.l += temp.l * factor; + return temp.toRGBA(); + }; + /** + * 将当前颜色变暗指定的量 + * + * @param factor + */ + Color.prototype.darken = function (factor) { + if (factor === void 0) { factor = 0.1; } + var temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.l -= temp.l * factor; + return temp.toRGBA(); + }; + /** + * 使当前颜色饱和指定的量 + * + * @param factor + */ + Color.prototype.saturate = function (factor) { + if (factor === void 0) { factor = 0.1; } + var temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.s += temp.s * factor; + return temp.toRGBA(); + }; + /** + * 按指定量降低当前颜色的饱和度 + * + * @param factor + */ + Color.prototype.desaturate = function (factor) { + if (factor === void 0) { factor = 0.1; } + var temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.s -= temp.s * factor; + return temp.toRGBA(); + }; + /** + * 将一种颜色乘以另一种颜色,得到更深的颜色 + * + * @param color + */ + Color.prototype.mulitiply = function (color) { + var newR = (((color.r / 255) * this.r) / 255) * 255; + var newG = (((color.g / 255) * this.g) / 255) * 255; + var newB = (((color.b / 255) * this.b) / 255) * 255; + var newA = color.a * this.a; + return new Color(newR, newG, newB, newA); + }; + /** + * 筛选另一种颜色,导致颜色较浅 + * + * @param color + */ + Color.prototype.screen = function (color) { + var color1 = color.invert(); + var color2 = color.invert(); + return color1.mulitiply(color2).invert(); + }; + /** + * 反转当前颜色 + */ + Color.prototype.invert = function () { + return new Color(255 - this.r, 255 - this.g, 255 - this.b, 1.0 - this.a); + }; + /** + * 将当前颜色与另一个颜色平均 + * + * @param color + */ + Color.prototype.average = function (color) { + var newR = (color.r + this.r) / 2; + var newG = (color.g + this.g) / 2; + var newB = (color.b + this.b) / 2; + var newA = (color.a + this.a) / 2; + return new Color(newR, newG, newB, newA); + }; + /** + * 返回颜色的 CSS 字符串表示形式。 + * + * @param format + */ + Color.prototype.toString = function (format) { + if (format === void 0) { format = 'rgb'; } + switch (format) { + case 'rgb': + return this.toRGBA(); + case 'hsl': + return this.toHSLA(); + case 'hex': + return this.toHex(); + default: + throw new Error('Invalid Color format'); + } + }; + /** + * 返回颜色分量的十六进制值 + * @param c + * @see https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + */ + Color.prototype._componentToHex = function (c) { + var hex = c.toString(16); + return hex.length === 1 ? '0' + hex : hex; + }; + /** + *返回颜色的十六进制表示 + */ + Color.prototype.toHex = function () { + return ('#' + + this._componentToHex(this.r) + + this._componentToHex(this.g) + + this._componentToHex(this.b) + + this._componentToHex(this.a)); + }; + /** + * 从十六进制字符串设置颜色 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + Color.prototype.fromHex = function (hex) { + var hexRegEx = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i; + var match = hex.match(hexRegEx); + if (match) { + var r = parseInt(match[1], 16); + var g = parseInt(match[2], 16); + var b = parseInt(match[3], 16); + var a = 1; + if (match[4]) { + a = parseInt(match[4], 16) / 255; + } + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + else { + throw new Error('Invalid hex string: ' + hex); + } + }; + /** + * 返回颜色的 RGBA 表示 + */ + Color.prototype.toRGBA = function () { + var result = String(this.r.toFixed(0)) + + ', ' + + String(this.g.toFixed(0)) + + ', ' + + String(this.b.toFixed(0)); + if (this.a !== undefined || this.a != null) { + return 'rgba(' + result + ', ' + String(this.a) + ')'; + } + return 'rgb(' + result + ')'; + }; + /** + * 返回颜色的 HSLA 表示 + */ + Color.prototype.toHSLA = function () { + return HSLColor.fromRGBA(this.r, this.g, this.b, this.a).toString(); + }; + /** + * 返回颜色的 CSS 字符串表示形式 + */ + Color.prototype.fillStyle = function () { + return this.toString(); + }; + /** + * 返回当前颜色的克隆 + */ + Color.prototype.clone = function () { + return new Color(this.r, this.g, this.b, this.a); + }; + /** + * Black (#000000) + */ + Color.Black = Color.createFromHex('#000000'); + /** + * White (#FFFFFF) + */ + Color.White = Color.createFromHex('#FFFFFF'); + /** + * Gray (#808080) + */ + Color.Gray = Color.createFromHex('#808080'); + /** + * Light gray (#D3D3D3) + */ + Color.LightGray = Color.createFromHex('#D3D3D3'); + /** + * Dark gray (#A9A9A9) + */ + Color.DarkGray = Color.createFromHex('#A9A9A9'); + /** + * Yellow (#FFFF00) + */ + Color.Yellow = Color.createFromHex('#FFFF00'); + /** + * Orange (#FFA500) + */ + Color.Orange = Color.createFromHex('#FFA500'); + /** + * Red (#FF0000) + */ + Color.Red = Color.createFromHex('#FF0000'); + /** + * Vermillion (#FF5B31) + */ + Color.Vermillion = Color.createFromHex('#FF5B31'); + /** + * Rose (#FF007F) + */ + Color.Rose = Color.createFromHex('#FF007F'); + /** + * Magenta (#FF00FF) + */ + Color.Magenta = Color.createFromHex('#FF00FF'); + /** + * Violet (#7F00FF) + */ + Color.Violet = Color.createFromHex('#7F00FF'); + /** + * Blue (#0000FF) + */ + Color.Blue = Color.createFromHex('#0000FF'); + /** + * Azure (#007FFF) + */ + Color.Azure = Color.createFromHex('#007FFF'); + /** + * Cyan (#00FFFF) + */ + Color.Cyan = Color.createFromHex('#00FFFF'); + /** + * Viridian (#59978F) + */ + Color.Viridian = Color.createFromHex('#59978F'); + /** + * Green (#00FF00) + */ + Color.Green = Color.createFromHex('#00FF00'); + /** + * Chartreuse (#7FFF00) + */ + Color.Chartreuse = Color.createFromHex('#7FFF00'); + /** + * Transparent (#FFFFFF00) + */ + Color.Transparent = Color.createFromHex('#FFFFFF00'); + return Color; + }()); + es.Color = Color; + /** + * 内部 HSL 颜色表示 + * + * http://en.wikipedia.org/wiki/HSL_and_HSV + * http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c + */ + var HSLColor = /** @class */ (function () { + function HSLColor(h, s, l, a) { + this.h = h; + this.s = s; + this.l = l; + this.a = a; + } + HSLColor.hue2rgb = function (p, q, t) { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; + }; + HSLColor.fromRGBA = function (r, g, b, a) { + r /= 255; + g /= 255; + b /= 255; + var max = Math.max(r, g, b); + var min = Math.min(r, g, b); + var h = (max + min) / 2; + var s = h; + var l = h; + if (max === min) { + h = s = 0; // achromatic + } + else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + return new HSLColor(h, s, l, a); + }; + HSLColor.prototype.toRGBA = function () { + var r; + var g; + var b; + if (this.s === 0) { + r = g = b = this.l; // achromatic + } + else { + var q = this.l < 0.5 + ? this.l * (1 + this.s) + : this.l + this.s - this.l * this.s; + var p = 2 * this.l - q; + r = HSLColor.hue2rgb(p, q, this.h + 1 / 3); + g = HSLColor.hue2rgb(p, q, this.h); + b = HSLColor.hue2rgb(p, q, this.h - 1 / 3); + } + return new Color(r * 255, g * 255, b * 255, this.a); + }; + HSLColor.prototype.toString = function () { + var h = this.h.toFixed(0); + var s = this.s.toFixed(0); + var l = this.l.toFixed(0); + var a = this.a.toFixed(0); + return "hsla(" + h + ", " + s + ", " + l + ", " + a + ")"; + }; + return HSLColor; + }()); +})(es || (es = {})); +var es; +(function (es) { + var Renderer = /** @class */ (function () { + function Renderer(renderOrder, camera) { + this.renderOrder = 0; + this.shouldDebugRender = true; + this.renderDirty = true; + this.renderOrder = renderOrder; + this.camera = camera; + es.Core.emitter.addObserver(es.CoreEvents.renderChanged, this.onRenderChanged, this); + } + Renderer.prototype.onAddedToScene = function (scene) { }; + Renderer.prototype.unload = function () { }; + Renderer.prototype.beginRender = function (cam) { + if (!es.Graphics.instance) + return; + es.Graphics.instance.batcher.begin(cam); + }; + Renderer.prototype.endRender = function () { + if (!es.Graphics.instance) + return; + es.Graphics.instance.batcher.end(); + }; + Renderer.prototype.onRenderChanged = function () { + this.renderDirty = true; + }; + Renderer.prototype.renderAfterStateCheck = function (renderable, cam) { + if (!es.Graphics.instance) + return; + renderable.render(es.Graphics.instance.batcher, cam); + }; + Renderer.prototype.debugRender = function (scene) { + if (!es.Graphics.instance) + return; + es.Physics.debugDraw(2); + for (var i = 0; i < scene.entities.count; i++) { + var entity = scene.entities.buffer[i]; + if (entity.enabled) { + entity.debugRender(es.Graphics.instance.batcher); + } + } + }; + return Renderer; + }()); + es.Renderer = Renderer; +})(es || (es = {})); +/// +var es; +/// +(function (es) { + var DefaultRenderer = /** @class */ (function (_super) { + __extends(DefaultRenderer, _super); + function DefaultRenderer(renderOrder, camera) { + if (renderOrder === void 0) { renderOrder = 0; } + if (camera === void 0) { camera = null; } + return _super.call(this, renderOrder, camera) || this; + } + DefaultRenderer.prototype.render = function (scene) { + if (!this.renderDirty) + return; + this.renderDirty = false; + var cam = this.camera ? this.camera : scene.camera; + this.beginRender(cam); + for (var i = 0; i < scene.renderableComponents.count; i++) { + var renderable = scene.renderableComponents.get(i); + if (renderable.enabled && renderable.isVisibleFromCamera(scene.camera)) + this.renderAfterStateCheck(renderable, cam); + } + if (this.shouldDebugRender && es.Core.debugRenderEndabled) { + this.debugRender(scene); + } + this.endRender(); + }; + return DefaultRenderer; + }(es.Renderer)); + es.DefaultRenderer = DefaultRenderer; +})(es || (es = {})); +var es; (function (es) { /** * 三次方和二次方贝塞尔帮助器(cubic and quadratic bezier helper) @@ -5135,9 +6490,9 @@ var es; Bezier.getPoint = function (p0, p1, p2, t) { t = es.MathHelper.clamp01(t); var oneMinusT = 1 - t; - return new es.Vector2(oneMinusT * oneMinusT).multiply(p0) - .add(new es.Vector2(2 * oneMinusT * t).multiply(p1)) - .add(new es.Vector2(t * t).multiply(p2)); + return p0.scale(oneMinusT * oneMinusT) + .addEqual(p1.scale(2 * oneMinusT * t)) + .addEqual(p2.scale(t * t)); }; /** * 求解一个立方体曲率 @@ -5150,10 +6505,10 @@ var es; Bezier.getPointThree = function (start, firstControlPoint, secondControlPoint, end, t) { t = es.MathHelper.clamp01(t); var oneMinusT = 1 - t; - return new es.Vector2(oneMinusT * oneMinusT * oneMinusT).multiply(start) - .add(new es.Vector2(3 * oneMinusT * oneMinusT * t).multiply(firstControlPoint)) - .add(new es.Vector2(3 * oneMinusT * t * t).multiply(secondControlPoint)) - .add(new es.Vector2(t * t * t).multiply(end)); + return start.scale(oneMinusT * oneMinusT * oneMinusT) + .addEqual(firstControlPoint.scale(3 * oneMinusT * oneMinusT * t)) + .addEqual(secondControlPoint.scale(3 * oneMinusT * t * t)) + .addEqual(end.scale(t * t * t)); }; /** * 得到二次贝塞尔函数的一阶导数 @@ -5163,8 +6518,8 @@ var es; * @param t */ Bezier.getFirstDerivative = function (p0, p1, p2, t) { - return new es.Vector2(2 * (1 - t)).multiply(es.Vector2.subtract(p1, p0)) - .add(new es.Vector2(2 * t).multiply(es.Vector2.subtract(p2, p1))); + return p1.sub(p0).scale(2 * (1 - t)) + .addEqual(p2.sub(p1).scale(2 * t)); }; /** * 得到一个三次贝塞尔函数的一阶导数 @@ -5177,9 +6532,9 @@ var es; Bezier.getFirstDerivativeThree = function (start, firstControlPoint, secondControlPoint, end, t) { t = es.MathHelper.clamp01(t); var oneMunusT = 1 - t; - return new es.Vector2(3 * oneMunusT * oneMunusT).multiply(es.Vector2.subtract(firstControlPoint, start)) - .add(new es.Vector2(6 * oneMunusT * t).multiply(es.Vector2.subtract(secondControlPoint, firstControlPoint))) - .add(new es.Vector2(3 * t * t).multiply(es.Vector2.subtract(end, secondControlPoint))); + return firstControlPoint.sub(start).scale(3 * oneMunusT * oneMunusT) + .addEqual(secondControlPoint.sub(firstControlPoint).scale(6 * oneMunusT * t)) + .addEqual(end.sub(secondControlPoint).scale(3 * t * t)); }; /** * 递归地细分bezier曲线,直到满足距离校正 @@ -5209,16 +6564,16 @@ var es; */ Bezier.recursiveGetOptimizedDrawingPoints = function (start, firstCtrlPoint, secondCtrlPoint, end, points, distanceTolerance) { // 计算线段的所有中点 - var pt12 = es.Vector2.divide(es.Vector2.add(start, firstCtrlPoint), new es.Vector2(2)); - var pt23 = es.Vector2.divide(es.Vector2.add(firstCtrlPoint, secondCtrlPoint), new es.Vector2(2)); - var pt34 = es.Vector2.divide(es.Vector2.add(secondCtrlPoint, end), new es.Vector2(2)); + var pt12 = es.Vector2.divideScaler(start.add(firstCtrlPoint), 2); + var pt23 = es.Vector2.divideScaler(firstCtrlPoint.add(secondCtrlPoint), 2); + var pt34 = es.Vector2.divideScaler(secondCtrlPoint.add(end), 2); // 计算新半直线的中点 - var pt123 = es.Vector2.divide(es.Vector2.add(pt12, pt23), new es.Vector2(2)); - var pt234 = es.Vector2.divide(es.Vector2.add(pt23, pt34), new es.Vector2(2)); + var pt123 = es.Vector2.divideScaler(pt12.add(pt23), 2); + var pt234 = es.Vector2.divideScaler(pt23.add(pt34), 2); // 最后再细分最后两个中点。如果我们满足我们的距离公差,这将是我们使用的最后一点。 - var pt1234 = es.Vector2.divide(es.Vector2.add(pt123, pt234), new es.Vector2(2)); + var pt1234 = es.Vector2.divideScaler(pt123.add(pt234), 2); // 试着用一条直线来近似整个三次曲线 - var deltaLine = es.Vector2.subtract(end, start); + var deltaLine = end.sub(start); var d2 = Math.abs(((firstCtrlPoint.x, end.x) * deltaLine.y - (firstCtrlPoint.y - end.y) * deltaLine.x)); var d3 = Math.abs(((secondCtrlPoint.x - end.x) * deltaLine.y - (secondCtrlPoint.y - end.y) * deltaLine.x)); if ((d2 + d3) * (d2 + d3) < distanceTolerance * (deltaLine.x * deltaLine.x + deltaLine.y * deltaLine.y)) { @@ -5248,18 +6603,19 @@ var es; * @param t */ BezierSpline.prototype.pointIndexAtTime = function (t) { - var i = 0; - if (t.value >= 1) { - t.value = 1; - i = this._points.length - 4; + var res = { time: 0, range: 0 }; + if (t >= 1) { + t = 1; + res.range = this._points.length - 4; } else { - t.value = es.MathHelper.clamp01(t.value) * this._curveCount; - i = ~~t; - t.value -= i; - i *= 3; + t = es.MathHelper.clamp01(t) * this._curveCount; + res.range = Math.floor(t); + t -= res.range; + res.range *= 3; } - return i; + res.time = t; + return res; }; /** * 设置一个控制点,考虑到这是否是一个共享点,如果是,则适当调整 @@ -5268,11 +6624,11 @@ var es; */ BezierSpline.prototype.setControlPoint = function (index, point) { if (index % 3 == 0) { - var delta = es.Vector2.subtract(point, this._points[index]); + var delta = point.sub(this._points[index]); if (index > 0) - this._points[index - 1].add(delta); + this._points[index - 1].addEqual(delta); if (index + 1 < this._points.length) - this._points[index + 1].add(delta); + this._points[index + 1].addEqual(delta); } this._points[index] = point; }; @@ -5281,7 +6637,8 @@ var es; * @param t */ BezierSpline.prototype.getPointAtTime = function (t) { - var i = this.pointIndexAtTime(new es.Ref(t)); + var res = this.pointIndexAtTime(t); + var i = res.range; return es.Bezier.getPointThree(this._points[i], this._points[i + 1], this._points[i + 2], this._points[i + 3], t); }; /** @@ -5289,7 +6646,8 @@ var es; * @param t */ BezierSpline.prototype.getVelocityAtTime = function (t) { - var i = this.pointIndexAtTime(new es.Ref(t)); + var res = this.pointIndexAtTime(t); + var i = res.range; return es.Bezier.getFirstDerivativeThree(this._points[i], this._points[i + 1], this._points[i + 2], this._points[i + 3], t); }; /** @@ -5297,7 +6655,7 @@ var es; * @param t */ BezierSpline.prototype.getDirectionAtTime = function (t) { - return es.Vector2.normalize(this.getVelocityAtTime(t)); + return this.getVelocityAtTime(t).normalize(); }; /** * 在贝塞尔曲线上添加一条曲线 @@ -5515,6 +6873,9 @@ var es; MathHelper.lerp = function (from, to, t) { return from + (to - from) * this.clamp01(t); }; + MathHelper.betterLerp = function (a, b, t, epsilon) { + return Math.abs(a - b) < epsilon ? b : MathHelper.lerp(a, b, t); + }; /** * 使度数的角度在a和b之间 * 用于处理360度环绕 @@ -5733,6 +7094,15 @@ var es; return end; return this.repeat(this.approach(start, start + deltaAngle, shift), 360); }; + /** + * 将 Vector 投影到另一个 Vector 上 + * @param other + */ + MathHelper.project = function (self, other) { + var amt = self.dot(other) / other.lengthSquared(); + var vec = other.scale(amt); + return vec; + }; /** * 通过将偏移量(全部以弧度为单位)夹住结果并选择最短路径,起始角度朝向终止角度。 * 起始值可以小于或大于终止值。 @@ -5800,6 +7170,9 @@ var es; MathHelper.repeat = function (t, length) { return t - Math.floor(t / length) * length; }; + MathHelper.floorToInt = function (f) { + return Math.trunc(Math.floor(f)); + }; /** * 将值绕一圈移动的助手 * @param position @@ -5926,7 +7299,52 @@ var es; if (Number.isNaN(x)) { return false; } - return !Number.isFinite(x); + return x !== Infinity; + }; + MathHelper.smoothDamp = function (current, target, currentVelocity, smoothTime, maxSpeed, deltaTime) { + smoothTime = Math.max(0.0001, smoothTime); + var num = 2 / smoothTime; + var num2 = num * deltaTime; + var num3 = 1 / + (1 + (num2 + (0.48 * (num2 * num2) + 0.235 * (num2 * (num2 * num2))))); + var num4 = current - target; + var num5 = target; + var num6 = maxSpeed * smoothTime; + num4 = this.clamp(num4, num6 * -1, num6); + target = current - num4; + var num7 = (currentVelocity + num * num4) * deltaTime; + currentVelocity = (currentVelocity - num * num7) * num3; + var num8 = target + (num4 + num7) * num3; + if (num5 - current > 0 === num8 > num5) { + num8 = num5; + currentVelocity = (num8 - num5) / deltaTime; + } + return { value: num8, currentVelocity: currentVelocity }; + }; + MathHelper.smoothDampVector = function (current, target, currentVelocity, smoothTime, maxSpeed, deltaTime) { + var v = es.Vector2.zero; + var resX = this.smoothDamp(current.x, target.x, currentVelocity.x, smoothTime, maxSpeed, deltaTime); + v.x = resX.value; + currentVelocity.x = resX.currentVelocity; + var resY = this.smoothDamp(current.y, target.y, currentVelocity.y, smoothTime, maxSpeed, deltaTime); + v.y = resY.value; + currentVelocity.y = resY.currentVelocity; + return v; + }; + /** + * 将值(在 leftMin - leftMax 范围内)映射到 rightMin - rightMax 范围内的值 + * @param value + * @param leftMin + * @param leftMax + * @param rightMin + * @param rightMax + * @returns + */ + MathHelper.mapMinMax = function (value, leftMin, leftMax, rightMin, rightMax) { + return rightMin + ((MathHelper.clamp(value, leftMin, leftMax) - leftMin) * (rightMax - rightMin)) / (leftMax - leftMin); + }; + MathHelper.fromAngle = function (angle) { + return new es.Vector2(Math.cos(angle), Math.sin(angle)).normalizeEqual(); }; MathHelper.Epsilon = 0.00001; MathHelper.Rad2Deg = 57.29578; @@ -5945,8 +7363,31 @@ var es; * 代表右手4x4浮点矩阵,可以存储平移、比例和旋转信息 */ var Matrix = /** @class */ (function () { - function Matrix() { + function Matrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44) { + this.m11 = m11; + this.m12 = m12; + this.m13 = m13; + this.m14 = m14; + this.m21 = m21; + this.m22 = m22; + this.m23 = m23; + this.m24 = m24; + this.m31 = m31; + this.m32 = m32; + this.m33 = m33; + this.m34 = m34; + this.m41 = m41; + this.m42 = m42; + this.m43 = m43; + this.m44 = m44; } + Object.defineProperty(Matrix, "Identity", { + get: function () { + return this.identity; + }, + enumerable: true, + configurable: true + }); /** * 为自定义的正交视图创建一个新的投影矩阵 * @param left @@ -5974,6 +7415,33 @@ var es; result.m43 = zNearPlane / (zNearPlane - zFarPlane); result.m44 = 1; }; + Matrix.createTranslation = function (position, result) { + result.m11 = 1; + result.m12 = 0; + result.m13 = 0; + result.m14 = 0; + result.m21 = 0; + result.m22 = 1; + result.m23 = 0; + result.m24 = 0; + result.m31 = 0; + result.m32 = 0; + result.m33 = 1; + result.m34 = 0; + result.m41 = position.x; + result.m42 = position.y; + result.m43 = 0; + result.m44 = 1; + }; + Matrix.createRotationZ = function (radians, result) { + result = Matrix.Identity; + var val1 = Math.cos(radians); + var val2 = Math.sin(radians); + result.m11 = val1; + result.m12 = val2; + result.m21 = -val2; + result.m22 = val1; + }; /** * 创建一个新的矩阵,其中包含两个矩阵的乘法。 * @param matrix1 @@ -6015,6 +7483,7 @@ var es; result.m43 = m43; result.m44 = m44; }; + Matrix.identity = new Matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return Matrix; }()); es.Matrix = Matrix; @@ -6025,39 +7494,36 @@ var es; * 表示右手3 * 3的浮点矩阵,可以存储平移、缩放和旋转信息。 */ var Matrix2D = /** @class */ (function () { - /** - * 构建一个矩阵 - * @param m11 - * @param m12 - * @param m21 - * @param m22 - * @param m31 - * @param m32 - */ - function Matrix2D(m11, m12, m21, m22, m31, m32) { + function Matrix2D() { this.m11 = 0; // x 缩放 this.m12 = 0; this.m21 = 0; this.m22 = 0; this.m31 = 0; this.m32 = 0; - this.m11 = m11; - this.m12 = m12; - this.m21 = m21; - this.m22 = m22; - this.m31 = m31; - this.m32 = m32; } Object.defineProperty(Matrix2D, "identity", { /** * 返回标识矩阵 */ get: function () { - return new Matrix2D(1, 0, 0, 1, 0, 0); + return new Matrix2D().setIdentity(); }, enumerable: true, configurable: true }); + Matrix2D.prototype.setIdentity = function () { + return this.setValues(1, 0, 0, 1, 0, 0); + }; + Matrix2D.prototype.setValues = function (m11, m12, m21, m22, m31, m32) { + this.m11 = m11; + this.m12 = m12; + this.m21 = m21; + this.m22 = m22; + this.m31 = m31; + this.m32 = m32; + return this; + }; Object.defineProperty(Matrix2D.prototype, "translation", { /** * 储存在该矩阵中的位置 @@ -6121,38 +7587,50 @@ var es; * 创建一个新的围绕Z轴的旋转矩阵2D * @param radians */ - Matrix2D.createRotation = function (radians) { - var result = this.identity; + Matrix2D.createRotation = function (radians, result) { + result.setIdentity(); + var val1 = Math.cos(radians); + var val2 = Math.sin(radians); + result.m11 = val1; + result.m12 = val2; + result.m21 = val2 * -1; + result.m22 = val1; + }; + Matrix2D.createRotationOut = function (radians, result) { var val1 = Math.cos(radians); var val2 = Math.sin(radians); result.m11 = val1; result.m12 = val2; result.m21 = -val2; result.m22 = val1; - return result; }; /** * 创建一个新的缩放矩阵2D * @param xScale * @param yScale */ - Matrix2D.createScale = function (xScale, yScale) { - var result = this.identity; + Matrix2D.createScale = function (xScale, yScale, result) { + result.m11 = xScale; + result.m12 = 0; + result.m21 = 0; + result.m22 = yScale; + result.m31 = 0; + result.m32 = 0; + }; + Matrix2D.createScaleOut = function (xScale, yScale, result) { result.m11 = xScale; result.m12 = 0; result.m21 = 0; result.m22 = yScale; result.m31 = 0; result.m32 = 0; - return result; }; /** * 创建一个新的平移矩阵2D * @param xPosition * @param yPosition */ - Matrix2D.createTranslation = function (xPosition, yPosition) { - var result = this.identity; + Matrix2D.createTranslation = function (xPosition, yPosition, result) { result.m11 = 1; result.m12 = 0; result.m21 = 0; @@ -6161,6 +7639,14 @@ var es; result.m32 = yPosition; return result; }; + Matrix2D.createTranslationOut = function (position, result) { + result.m11 = 1; + result.m12 = 0; + result.m21 = 0; + result.m22 = 1; + result.m31 = position.x; + result.m32 = position.y; + }; Matrix2D.invert = function (matrix) { var det = 1 / matrix.determinant(); var result = this.identity; @@ -6218,6 +7704,20 @@ var es; this.m32 = m32; return this; }; + Matrix2D.multiply = function (matrix1, matrix2, result) { + var m11 = (matrix1.m11 * matrix2.m11) + (matrix1.m12 * matrix2.m21); + var m12 = (matrix1.m11 * matrix2.m12) + (matrix1.m12 * matrix2.m22); + var m21 = (matrix1.m21 * matrix2.m11) + (matrix1.m22 * matrix2.m21); + var m22 = (matrix1.m21 * matrix2.m12) + (matrix1.m22 * matrix2.m22); + var m31 = (matrix1.m31 * matrix2.m11) + (matrix1.m32 * matrix2.m21) + matrix2.m31; + var m32 = (matrix1.m31 * matrix2.m12) + (matrix1.m32 * matrix2.m22) + matrix2.m32; + result.m11 = m11; + result.m12 = m12; + result.m21 = m21; + result.m22 = m22; + result.m31 = m31; + result.m32 = m32; + }; Matrix2D.prototype.determinant = function () { return this.m11 * this.m22 - this.m12 * this.m21; }; @@ -6251,7 +7751,8 @@ var es; return ret; }; Matrix2D.prototype.mutiplyTranslation = function (x, y) { - var trans = Matrix2D.createTranslation(x, y); + var trans = new Matrix2D(); + Matrix2D.createTranslation(x, y, trans); return es.MatrixHelper.mutiply(this, trans); }; /** @@ -6404,6 +7905,9 @@ var es; * 该矩形的高度 */ this.height = 0; + // temp 用于计算边界的矩阵 + this._tempMat = new es.Matrix2D(); + this._transformMat = new es.Matrix2D(); this.x = x; this.y = y; this.width = width; @@ -6604,12 +8108,12 @@ var es; value.top < this.bottom && this.top < value.bottom; }; - Rectangle.prototype.rayIntersects = function (ray, distance) { - distance.value = 0; + Rectangle.prototype.rayIntersects = function (ray) { + var res = { intersected: false, distance: 0 }; var maxValue = Number.MAX_VALUE; if (Math.abs(ray.direction.x) < 1E-06) { if ((ray.start.x < this.x) || (ray.start.x > this.x + this.width)) - return false; + return res; } else { var num11 = 1 / ray.direction.x; @@ -6620,14 +8124,14 @@ var es; num8 = num7; num7 = num14; } - distance.value = Math.max(num8, distance.value); + res.distance = Math.max(num8, res.distance); maxValue = Math.min(num7, maxValue); - if (distance.value > maxValue) - return false; + if (res.distance > maxValue) + return res; } - if (Math.abs(ray.direction.y) < 1E-06) { + if (Math.abs(ray.direction.y) < 1e-06) { if ((ray.start.y < this.y) || (ray.start.y > this.y + this.height)) - return false; + return res; } else { var num10 = 1 / ray.direction.y; @@ -6638,12 +8142,13 @@ var es; num6 = num5; num5 = num13; } - distance.value = Math.max(num6, distance.value); + res.distance = Math.max(num6, res.distance); maxValue = Math.max(num5, maxValue); - if (distance.value > maxValue) - return false; + if (res.distance > maxValue) + return res; } - return true; + res.intersected = true; + return res; }; /** * 获取所提供的矩形是否在此矩形的边界内 @@ -6684,7 +8189,7 @@ var es; */ Rectangle.prototype.getClosestPointOnRectangleToPoint = function (point) { // 对于每条轴,如果点在框外,就把它限制在框内,否则就不要管它 - var res = new es.Vector2(); + var res = es.Vector2.zero; res.x = es.MathHelper.clamp(point.x, this.left, this.right); res.y = es.MathHelper.clamp(point.y, this.top, this.bottom); return res; @@ -6697,7 +8202,7 @@ var es; */ Rectangle.prototype.getClosestPointOnRectangleBorderToPoint = function (point, edgeNormal) { // 对于每条轴,如果点在框外,就把它限制在框内,否则就不要管它 - var res = new es.Vector2(); + var res = es.Vector2.zero; res.x = es.MathHelper.clamp(point.x, this.left, this.right); res.y = es.MathHelper.clamp(point.y, this.top, this.bottom); // 如果点在矩形内,我们需要将res推到边界上,因为它将在矩形内 @@ -6785,22 +8290,22 @@ var es; }; Rectangle.prototype.calculateBounds = function (parentPosition, position, origin, scale, rotation, width, height) { if (rotation == 0) { - this.x = parentPosition.x + position.x - origin.x * scale.x; - this.y = parentPosition.y + position.y - origin.y * scale.y; - this.width = width * scale.x; - this.height = height * scale.y; + this.x = Math.trunc(parentPosition.x + position.x - origin.x * scale.x); + this.y = Math.trunc(parentPosition.y + position.y - origin.y * scale.y); + this.width = Math.trunc(width * scale.x); + this.height = Math.trunc(height * scale.y); } else { // 我们需要找到我们的绝对最小/最大值,并据此创建边界 var worldPosX = parentPosition.x + position.x; var worldPosY = parentPosition.y + position.y; // 考虑到原点,将参考点设置为世界参考 - this._transformMat = es.Matrix2D.createTranslation(-worldPosX - origin.x, -worldPosY - origin.y); - this._tempMat = es.Matrix2D.createScale(scale.x, scale.y); + es.Matrix2D.createTranslation(-worldPosX - origin.x, -worldPosY - origin.y, this._transformMat); + es.Matrix2D.createScale(scale.x, scale.y, this._tempMat); this._transformMat = this._transformMat.multiply(this._tempMat); - this._tempMat = es.Matrix2D.createRotation(rotation); + es.Matrix2D.createRotation(rotation, this._tempMat); this._transformMat = this._transformMat.multiply(this._tempMat); - this._tempMat = es.Matrix2D.createTranslation(worldPosX, worldPosY); + es.Matrix2D.createTranslation(worldPosX, worldPosY, this._tempMat); this._transformMat = this._transformMat.multiply(this._tempMat); // TODO: 我们可以把世界变换留在矩阵中,避免在世界空间中得到所有的四个角 var topLeft = new es.Vector2(worldPosX, worldPosY); @@ -6812,13 +8317,13 @@ var es; es.Vector2Ext.transformR(bottomLeft, this._transformMat, bottomLeft); es.Vector2Ext.transformR(bottomRight, this._transformMat, bottomRight); // 找出最小值和最大值,这样我们就可以计算出我们的边界框。 - var minX = Math.min(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x); - var maxX = Math.max(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x); - var minY = Math.min(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y); - var maxY = Math.max(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y); + var minX = Math.trunc(Math.min(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + var maxX = Math.trunc(Math.max(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + var minY = Math.trunc(Math.min(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); + var maxY = Math.trunc(Math.max(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); this.location = new es.Vector2(minX, minY); - this.width = maxX - minX; - this.height = maxY - minY; + this.width = Math.trunc(maxX - minX); + this.height = Math.trunc(maxY - minY); } }; /** @@ -6902,7 +8407,7 @@ var es; * 获取这个矩形的哈希码 */ Rectangle.prototype.getHashCode = function () { - return (this.x ^ this.y ^ this.width ^ this.height); + return (Math.trunc(this.x) ^ Math.trunc(this.y) ^ Math.trunc(this.width) ^ Math.trunc(this.height)); }; Rectangle.prototype.clone = function () { return new Rectangle(this.x, this.y, this.width, this.height); @@ -6931,7 +8436,7 @@ var es; */ SubpixelFloat.prototype.update = function (amount) { this.remainder += amount; - var motion = Math.floor(Math.trunc(this.remainder)); + var motion = Math.trunc(this.remainder); this.remainder -= motion; amount = motion; return amount; @@ -6991,13 +8496,15 @@ var es; * 它将处理任何与Collider重叠的ITriggerListeners。 */ ColliderTriggerHelper.prototype.update = function () { + var e_9, _a; + var lateColliders = []; // 对所有实体.colliders进行重叠检查,这些实体.colliders是触发器,与所有宽相碰撞器,无论是否触发器。 // 任何重叠都会导致触发事件 var colliders = this._entity.getComponents(es.Collider); for (var i = 0; i < colliders.length; i++) { var collider = colliders[i]; - var neighbors = es.Physics.boxcastBroadphase(collider.bounds, collider.collidesWithLayers); - for (var j = 0; j < neighbors.size; j++) { + var neighbors = es.Physics.boxcastBroadphaseExcludingSelf(collider.bounds, collider.collidesWithLayers); + for (var j = 0; j < neighbors.length; j++) { var neighbor = neighbors[j]; // 我们至少需要一个碰撞器作为触发器 if (!collider.isTrigger && !neighbor.isTrigger) @@ -7007,13 +8514,31 @@ var es; // 如果我们的某一个集合中已经有了这个对子(前一个或当前的触发交叉点),就不要调用输入事件了 var shouldReportTriggerEvent = !this._activeTriggerIntersections.contains(pair) && !this._previousTriggerIntersections.contains(pair); - if (shouldReportTriggerEvent) - this.notifyTriggerListeners(pair, true); + if (shouldReportTriggerEvent) { + if (neighbor.castSortOrder >= es.Collider.lateSortOrder) { + lateColliders.push(pair); + } + else { + this.notifyTriggerListeners(pair, true); + } + } this._activeTriggerIntersections.add(pair); } } } - es.ListPool.free(colliders); + try { + for (var lateColliders_1 = __values(lateColliders), lateColliders_1_1 = lateColliders_1.next(); !lateColliders_1_1.done; lateColliders_1_1 = lateColliders_1.next()) { + var pair = lateColliders_1_1.value; + this.notifyTriggerListeners(pair, true); + } + } + catch (e_9_1) { e_9 = { error: e_9_1 }; } + finally { + try { + if (lateColliders_1_1 && !lateColliders_1_1.done && (_a = lateColliders_1.return)) _a.call(lateColliders_1); + } + finally { if (e_9) throw e_9.error; } + } this.checkForExitedColliders(); }; ColliderTriggerHelper.prototype.checkForExitedColliders = function () { @@ -7074,58 +8599,60 @@ var es; function Collisions() { } Collisions.lineToLine = function (a1, a2, b1, b2) { - var b = es.Vector2.subtract(a2, a1); - var d = es.Vector2.subtract(b2, b1); + var b = a2.sub(a1); + var d = b2.sub(b1); var bDotDPerp = b.x * d.y - b.y * d.x; // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 if (bDotDPerp == 0) return false; - var c = es.Vector2.subtract(b1, a1); + var c = b1.sub(a1); var t = (c.x * d.y - c.y * d.x) / bDotDPerp; - if (t < 0 || t > 1) + if (t < 0 || t > 1) { return false; + } var u = (c.x * b.y - c.y * b.x) / bDotDPerp; - if (u < 0 || u > 1) + if (u < 0 || u > 1) { return false; + } return true; }; Collisions.lineToLineIntersection = function (a1, a2, b1, b2, intersection) { - if (intersection === void 0) { intersection = new es.Vector2(); } + if (intersection === void 0) { intersection = es.Vector2.zero; } intersection.x = 0; intersection.y = 0; - var b = es.Vector2.subtract(a2, a1); - var d = es.Vector2.subtract(b2, b1); + var b = a2.sub(a1); + var d = b2.sub(b1); var bDotDPerp = b.x * d.y - b.y * d.x; // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 if (bDotDPerp == 0) return false; - var c = es.Vector2.subtract(b1, a1); + var c = b1.sub(a1); var t = (c.x * d.y - c.y * d.x) / bDotDPerp; if (t < 0 || t > 1) return false; var u = (c.x * b.y - c.y * b.x) / bDotDPerp; if (u < 0 || u > 1) return false; - var temp = es.Vector2.add(a1, new es.Vector2(t * b.x, t * b.y)); + var temp = a1.add(b.scale(t)); intersection.x = temp.x; intersection.y = temp.y; return true; }; Collisions.closestPointOnLine = function (lineA, lineB, closestTo) { - var v = es.Vector2.subtract(lineB, lineA); - var w = es.Vector2.subtract(closestTo, lineA); - var t = es.Vector2.dot(w, v) / es.Vector2.dot(v, v); + var v = lineB.sub(lineA); + var w = closestTo.sub(lineA); + var t = w.dot(v) / v.dot(v); t = es.MathHelper.clamp(t, 0, 1); - return es.Vector2.add(lineA, new es.Vector2(v.x * t, v.y * t)); + return lineA.add(v.scale(t)); }; Collisions.circleToCircle = function (circleCenter1, circleRadius1, circleCenter2, circleRadius2) { - return es.Vector2.distanceSquared(circleCenter1, circleCenter2) < (circleRadius1 + circleRadius2) * (circleRadius1 + circleRadius2); + return es.Vector2.sqrDistance(circleCenter1, circleCenter2) < (circleRadius1 + circleRadius2) * (circleRadius1 + circleRadius2); }; Collisions.circleToLine = function (circleCenter, radius, lineFrom, lineTo) { - return es.Vector2.distanceSquared(circleCenter, this.closestPointOnLine(lineFrom, lineTo, circleCenter)) < radius * radius; + return es.Vector2.sqrDistance(circleCenter, this.closestPointOnLine(lineFrom, lineTo, circleCenter)) < radius * radius; }; Collisions.circleToPoint = function (circleCenter, radius, point) { - return es.Vector2.distanceSquared(circleCenter, point) < radius * radius; + return es.Vector2.sqrDistance(circleCenter, point) < radius * radius; }; Collisions.rectToCircle = function (rect, cPosition, cRadius) { // 检查矩形是否包含圆的中心点 @@ -7135,25 +8662,25 @@ var es; var edgeFrom; var edgeTo; var sector = this.getSector(rect.x, rect.y, rect.width, rect.height, cPosition); - if ((sector & PointSectors.top) != 0) { + if ((sector & PointSectors.top) !== 0) { edgeFrom = new es.Vector2(rect.x, rect.y); edgeTo = new es.Vector2(rect.x + rect.width, rect.y); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) return true; } - if ((sector & PointSectors.bottom) != 0) { + if ((sector & PointSectors.bottom) !== 0) { edgeFrom = new es.Vector2(rect.x, rect.y + rect.width); edgeTo = new es.Vector2(rect.x + rect.width, rect.y + rect.height); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) return true; } - if ((sector & PointSectors.left) != 0) { + if ((sector & PointSectors.left) !== 0) { edgeFrom = new es.Vector2(rect.x, rect.y); edgeTo = new es.Vector2(rect.x, rect.y + rect.height); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) return true; } - if ((sector & PointSectors.right) != 0) { + if ((sector & PointSectors.right) !== 0) { edgeFrom = new es.Vector2(rect.x + rect.width, rect.y); edgeTo = new es.Vector2(rect.x + rect.width, rect.y + rect.height); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) @@ -7260,13 +8787,14 @@ var es; this.point = point; this.centroid = es.Vector2.zero; } - RaycastHit.prototype.setValues = function (collider, fraction, distance, point) { + RaycastHit.prototype.setAllValues = function (collider, fraction, distance, point, normal) { this.collider = collider; this.fraction = fraction; this.distance = distance; this.point = point; + this.normal = normal; }; - RaycastHit.prototype.setValuesNonCollider = function (fraction, distance, point, normal) { + RaycastHit.prototype.setValues = function (fraction, distance, point, normal) { this.fraction = fraction; this.distance = distance; this.point = point; @@ -7276,6 +8804,11 @@ var es; this.collider = null; this.fraction = this.distance = 0; }; + RaycastHit.prototype.clone = function () { + var hit = new RaycastHit(); + hit.setAllValues(this.collider, this.fraction, this.distance, this.point, this.normal); + return hit; + }; RaycastHit.prototype.toString = function () { return "[RaycastHit] fraction: " + this.fraction + ", distance: " + this.distance + ", normal: " + this.normal + ", centroid: " + this.centroid + ", point: " + this.point; }; @@ -7301,6 +8834,10 @@ var es; Physics.clear = function () { this._spatialHash.clear(); }; + Physics.debugDraw = function (secondsToDisplay) { + if (this.debugRender) + this._spatialHash.debugDraw(secondsToDisplay); + }; /** * 检查是否有对撞机落在一个圆形区域内。返回遇到的第一个对撞机 * @param center @@ -7320,13 +8857,9 @@ var es; * @param results * @param layerMask */ - Physics.overlapCircleAll = function (center, randius, results, layerMask) { - if (layerMask === void 0) { layerMask = -1; } - if (results.length == 0) { - console.warn("传入了一个空的结果数组。不会返回任何结果"); - return; - } - return this._spatialHash.overlapCircle(center, randius, results, layerMask); + Physics.overlapCircleAll = function (center, radius, results, layerMask) { + if (layerMask === void 0) { layerMask = this.allLayers; } + return this._spatialHash.overlapCircle(center, radius, results, layerMask); }; /** * 返回所有碰撞器与边界相交的碰撞器。bounds。请注意,这是一个broadphase检查,所以它只检查边界,不做单个碰撞到碰撞器的检查! @@ -7355,7 +8888,7 @@ var es; */ Physics.boxcastBroadphaseExcludingSelfNonRect = function (collider, layerMask) { if (layerMask === void 0) { layerMask = this.allLayers; } - var bounds = collider.bounds.clone(); + var bounds = collider.bounds; return this._spatialHash.aabbBroadphase(bounds, collider, layerMask); }; /** @@ -7367,7 +8900,7 @@ var es; */ Physics.boxcastBroadphaseExcludingSelfDelta = function (collider, deltaX, deltaY, layerMask) { if (layerMask === void 0) { layerMask = Physics.allLayers; } - var colliderBounds = collider.bounds.clone(); + var colliderBounds = collider.bounds; var sweptBounds = colliderBounds.getSweptBroadphaseBounds(deltaX, deltaY); return this._spatialHash.aabbBroadphase(sweptBounds, collider, layerMask); }; @@ -7399,11 +8932,14 @@ var es; * @param end * @param layerMask */ - Physics.linecast = function (start, end, layerMask) { - if (layerMask === void 0) { layerMask = Physics.allLayers; } + Physics.linecast = function (start, end, layerMask, ignoredColliders) { + if (layerMask === void 0) { layerMask = this.allLayers; } + if (ignoredColliders === void 0) { ignoredColliders = null; } this._hitArray[0].reset(); this.linecastAll(start, end, this._hitArray, layerMask); - return this._hitArray[0]; + this._hitArray[0].reset(); + Physics.linecastAll(start, end, this._hitArray, layerMask, ignoredColliders); + return this._hitArray[0].clone(); }; /** * 通过空间散列强制执行一行,并用该行命中的任何碰撞器填充hits数组 @@ -7412,13 +8948,10 @@ var es; * @param hits * @param layerMask */ - Physics.linecastAll = function (start, end, hits, layerMask) { - if (layerMask === void 0) { layerMask = Physics.allLayers; } - if (hits.length == 0) { - console.warn("传入了一个空的hits数组。没有点击会被返回"); - return 0; - } - return this._spatialHash.linecast(start, end, hits, layerMask); + Physics.linecastAll = function (start, end, hits, layerMask, ignoredColliders) { + if (layerMask === void 0) { layerMask = this.allLayers; } + if (ignoredColliders === void 0) { ignoredColliders = null; } + return this._spatialHash.linecast(start, end, hits, layerMask, ignoredColliders); }; /** * 检查是否有对撞机落在一个矩形区域中 @@ -7446,7 +8979,7 @@ var es; return this._spatialHash.overlapRectangle(rect, results, layerMask); }; /** 用于在全局范围内存储重力值的方便字段 */ - Physics.gravity = new es.Vector2(0, 300); + Physics.gravity = new es.Vector2(0, -300); /** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */ Physics.spatialHashCellSize = 100; /** 接受layerMask的所有方法的默认值 */ @@ -7459,6 +8992,7 @@ var es; * 在碰撞器中开始的射线/直线是否强制转换检测到那些碰撞器 */ Physics.raycastsStartInColliders = false; + Physics.debugRender = false; /** * 我们保留它以避免在每次raycast发生时分配它 */ @@ -7481,11 +9015,32 @@ var es; * 不是真正的射线(射线只有开始和方向),作为一条线和射线。 */ var Ray2D = /** @class */ (function () { - function Ray2D(position, end) { - this.start = position; - this.end = end; - this.direction = es.Vector2.subtract(this.end, this.start); + function Ray2D(pos, end) { + this._start = pos.clone(); + this._end = end.clone(); + this._direction = this._end.sub(this._start); } + Object.defineProperty(Ray2D.prototype, "start", { + get: function () { + return this._start; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Ray2D.prototype, "direction", { + get: function () { + return this._direction; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Ray2D.prototype, "end", { + get: function () { + return this._end; + }, + enumerable: true, + configurable: true + }); return Ray2D; }()); es.Ray2D = Ray2D; @@ -7568,6 +9123,20 @@ var es; SpatialHash.prototype.clear = function () { this._cellDict.clear(); }; + SpatialHash.prototype.debugDraw = function (secondsToDisplay) { + for (var x = this.gridBounds.x; x <= this.gridBounds.right; x++) { + for (var y = this.gridBounds.y; y <= this.gridBounds.bottom; y++) { + var cell = this.cellAtPosition(x, y); + if (cell != null && cell.length > 0) + this.debugDrawCellDetails(x, y, secondsToDisplay); + } + } + }; + SpatialHash.prototype.debugDrawCellDetails = function (x, y, secondsToDisplay) { + if (secondsToDisplay === void 0) { secondsToDisplay = 0.5; } + es.Graphics.instance.batcher.drawHollowRect(x * this._cellSize, y * this._cellSize, this._cellSize, this._cellSize, new es.Color(255, 0, 0), secondsToDisplay); + es.Graphics.instance.batcher.end(); + }; /** * 返回边框与单元格相交的所有对象 * @param bounds @@ -7581,7 +9150,7 @@ var es; for (var x = p1.x; x <= p2.x; x++) { for (var y = p1.y; y <= p2.y; y++) { var cell = this.cellAtPosition(x, y); - if (cell == null) + if (!cell) continue; // 当cell不为空。循环并取回所有碰撞器 for (var i = 0; i < cell.length; i++) { @@ -7595,7 +9164,7 @@ var es; } } } - return this._tempHashSet; + return Array.from(this._tempHashSet); }; /** * 通过空间散列投掷一条线,并将该线碰到的任何碰撞器填入碰撞数组 @@ -7606,9 +9175,9 @@ var es; * @param hits * @param layerMask */ - SpatialHash.prototype.linecast = function (start, end, hits, layerMask) { + SpatialHash.prototype.linecast = function (start, end, hits, layerMask, ignoredColliders) { var ray = new es.Ray2D(start, end); - this._raycastParser.start(ray, hits, layerMask); + this._raycastParser.start(ray, hits, layerMask, ignoredColliders); // 获取我们的起始/结束位置,与我们的网格在同一空间内 var currentCell = this.cellCoords(start.x, start.y); var lastCell = this.cellCoords(end.x, end.y); @@ -7635,17 +9204,17 @@ var es; var tDeltaY = ray.direction.y != 0 ? this._cellSize / (ray.direction.y * stepY) : Number.MAX_VALUE; // 开始遍历并返回交叉单元格。 var cell = this.cellAtPosition(currentCell.x, currentCell.y); - if (cell && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)) { + if (cell != null && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)) { this._raycastParser.reset(); return this._raycastParser.hitCounter; } while (currentCell.x != lastCell.x || currentCell.y != lastCell.y) { if (tMaxX < tMaxY) { - currentCell.x = Math.floor(es.MathHelper.approach(currentCell.x, lastCell.x, Math.abs(stepX))); + currentCell.x = es.MathHelper.approach(currentCell.x, lastCell.x, Math.abs(stepX)); tMaxX += tDeltaX; } else { - currentCell.y = Math.floor(es.MathHelper.approach(currentCell.y, lastCell.y, Math.abs(stepY))); + currentCell.y = es.MathHelper.approach(currentCell.y, lastCell.y, Math.abs(stepY)); tMaxY += tDeltaY; } cell = this.cellAtPosition(currentCell.x, currentCell.y); @@ -7665,9 +9234,9 @@ var es; * @param layerMask */ SpatialHash.prototype.overlapRectangle = function (rect, results, layerMask) { - var e_8, _a; + var e_10, _a; this._overlapTestBox.updateBox(rect.width, rect.height); - this._overlapTestBox.position = rect.location; + this._overlapTestBox.position = rect.location.clone(); var resultCounter = 0; var potentials = this.aabbBroadphase(rect, null, layerMask); try { @@ -7696,12 +9265,12 @@ var es; return resultCounter; } } - catch (e_8_1) { e_8 = { error: e_8_1 }; } + catch (e_10_1) { e_10 = { error: e_10_1 }; } finally { try { if (potentials_1_1 && !potentials_1_1.done && (_a = potentials_1.return)) _a.call(potentials_1); } - finally { if (e_8) throw e_8.error; } + finally { if (e_10) throw e_10.error; } } return resultCounter; }; @@ -7713,7 +9282,7 @@ var es; * @param layerMask */ SpatialHash.prototype.overlapCircle = function (circleCenter, radius, results, layerMask) { - var e_9, _a; + var e_11, _a; var bounds = new es.Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2); this._overlapTestCircle.radius = radius; this._overlapTestCircle.position = circleCenter; @@ -7723,8 +9292,10 @@ var es; for (var potentials_2 = __values(potentials), potentials_2_1 = potentials_2.next(); !potentials_2_1.done; potentials_2_1 = potentials_2.next()) { var collider = potentials_2_1.value; if (collider instanceof es.BoxCollider) { - results[resultCounter] = collider; - resultCounter++; + if (collider.shape.overlaps(this._overlapTestCircle)) { + results[resultCounter] = collider; + resultCounter++; + } } else if (collider instanceof es.CircleCollider) { if (collider.shape.overlaps(this._overlapTestCircle)) { @@ -7742,16 +9313,16 @@ var es; throw new Error("对这个对撞机类型的overlapCircle没有实现!"); } // 如果我们所有的结果数据有了则返回 - if (resultCounter == results.length) + if (resultCounter === results.length) return resultCounter; } } - catch (e_9_1) { e_9 = { error: e_9_1 }; } + catch (e_11_1) { e_11 = { error: e_11_1 }; } finally { try { if (potentials_2_1 && !potentials_2_1.done && (_a = potentials_2.return)) _a.call(potentials_2); } - finally { if (e_9) throw e_9.error; } + finally { if (e_11) throw e_11.error; } } return resultCounter; }; @@ -7784,10 +9355,6 @@ var es; return SpatialHash; }()); es.SpatialHash = SpatialHash; - /** - * 包装一个Unit32,列表碰撞器字典 - * 它的主要目的是将int、int x、y坐标散列到单个Uint32键中,使用O(1)查找。 - */ var NumberDictionary = /** @class */ (function () { function NumberDictionary() { this._store = new Map(); @@ -7810,7 +9377,7 @@ var es; return this._store.get(this.getKey(x, y)); }; NumberDictionary.prototype.getKey = function (x, y) { - return x << 16 | (y >>> 0); + return x + "_" + y; }; /** * 清除字典数据 @@ -7827,10 +9394,11 @@ var es; this._checkedColliders = []; this._cellHits = []; } - RaycastResultParser.prototype.start = function (ray, hits, layerMask) { + RaycastResultParser.prototype.start = function (ray, hits, layerMask, ignoredColliders) { this._ray = ray; this._hits = hits; this._layerMask = layerMask; + this._ignoredColliders = ignoredColliders; this.hitCounter = 0; }; /** @@ -7840,7 +9408,6 @@ var es; * @param cell */ RaycastResultParser.prototype.checkRayIntersection = function (cellX, cellY, cell) { - var fraction = new es.Ref(0); for (var i = 0; i < cell.length; i++) { var potential = cell[i]; // 管理我们已经处理过的碰撞器 @@ -7853,11 +9420,15 @@ var es; // 确保碰撞器在图层蒙版上 if (!es.Flags.isFlagSet(this._layerMask, potential.physicsLayer.value)) continue; + if (this._ignoredColliders && this._ignoredColliders.has(potential)) { + continue; + } // TODO: rayIntersects的性能够吗?需要测试它。Collisions.rectToLine可能更快 // TODO: 如果边界检查返回更多数据,我们就不需要为BoxCollider检查做任何事情 // 在做形状测试之前先做一个边界检查 - var colliderBounds = potential.bounds.clone(); - if (colliderBounds.rayIntersects(this._ray, fraction) && fraction.value <= 1) { + var colliderBounds = potential.bounds; + var res = colliderBounds.rayIntersects(this._ray); + if (res.intersected && res.distance <= 1) { if (potential.shape.collidesWithLine(this._ray.start, this._ray.end, this._tempHit)) { // 检查一下,我们应该排除这些射线,射线cast是否在碰撞器中开始 if (!es.Physics.raycastsStartInColliders && potential.shape.containsPoint(this._ray.start)) @@ -7868,7 +9439,7 @@ var es; } } } - if (this._cellHits.length == 0) + if (this._cellHits.length === 0) return false; // 所有处理单元完成。对结果进行排序并将命中结果打包到结果数组中 this._cellHits.sort(RaycastResultParser.compareRaycastHits); @@ -7876,7 +9447,7 @@ var es; this._hits[this.hitCounter] = this._cellHits[i]; // 增加命中计数器,如果它已经达到数组大小的限制,我们就完成了 this.hitCounter++; - if (this.hitCounter == this._hits.length) + if (this.hitCounter === this._hits.length) return true; } return false; @@ -7885,9 +9456,15 @@ var es; this._hits = null; this._checkedColliders.length = 0; this._cellHits.length = 0; + this._ignoredColliders = null; }; RaycastResultParser.compareRaycastHits = function (a, b) { - return a.distance - b.distance; + if (a.distance !== b.distance) { + return a.distance - b.distance; + } + else { + return a.collider.castSortOrder - b.collider.castSortOrder; + } }; return RaycastResultParser; }()); @@ -7925,6 +9502,9 @@ var es; _this.isBox = isBox; return _this; } + Polygon.prototype.create = function (vertCount, radius) { + Polygon.buildSymmetricalPolygon(vertCount, radius); + }; Object.defineProperty(Polygon.prototype, "edgeNormals", { /** * 边缘法线用于SAT碰撞检测。缓存它们用于避免squareRoots @@ -7943,9 +9523,13 @@ var es; * @param points */ Polygon.prototype.setPoints = function (points) { + var _this = this; this.points = points; this.recalculateCenterAndEdgeNormals(); - this._originalPoints = this.points.slice(); + this._originalPoints = []; + this.points.forEach(function (p) { + _this._originalPoints.push(p.clone()); + }); }; /** * 重新计算多边形中心 @@ -7996,7 +9580,7 @@ var es; Polygon.recenterPolygonVerts = function (points) { var center = this.findPolygonCenter(points); for (var i = 0; i < points.length; i++) - points[i] = es.Vector2.subtract(points[i], center); + points[i] = points[i].sub(center); }; /** * 找到多边形的中心。注意,这对于正则多边形是准确的。不规则多边形没有中心。 @@ -8017,9 +9601,9 @@ var es; */ Polygon.getFarthestPointInDirection = function (points, direction) { var index = 0; - var maxDot = es.Vector2.dot(points[index], direction); + var maxDot = points[index].dot(direction); for (var i = 1; i < points.length; i++) { - var dot = es.Vector2.dot(points[i], direction); + var dot = points[i].dot(direction); if (dot > maxDot) { maxDot = dot; index = i; @@ -8036,29 +9620,30 @@ var es; * @param distanceSquared * @param edgeNormal */ - Polygon.getClosestPointOnPolygonToPoint = function (points, point, distanceSquared, edgeNormal) { - distanceSquared.value = Number.MAX_VALUE; - edgeNormal.x = 0; - edgeNormal.y = 0; - var closestPoint = es.Vector2.zero; + Polygon.getClosestPointOnPolygonToPoint = function (points, point) { + var res = { + distanceSquared: Number.MAX_VALUE, + edgeNormal: es.Vector2.zero, + closestPoint: es.Vector2.zero, + }; var tempDistanceSquared = 0; for (var i = 0; i < points.length; i++) { var j = i + 1; - if (j == points.length) + if (j === points.length) j = 0; var closest = es.ShapeCollisionsCircle.closestPointOnLine(points[i], points[j], point); - tempDistanceSquared = es.Vector2.distanceSquared(point, closest); - if (tempDistanceSquared < distanceSquared.value) { - distanceSquared.value = tempDistanceSquared; - closestPoint = closest; + tempDistanceSquared = es.Vector2.sqrDistance(point, closest); + if (tempDistanceSquared < res.distanceSquared) { + res.distanceSquared = tempDistanceSquared; + res.closestPoint = closest; // 求直线的法线 - var line = es.Vector2.subtract(points[j], points[i]); - edgeNormal.x = -line.y; - edgeNormal.y = line.x; + var line = points[j].sub(points[i]); + res.edgeNormal.x = line.y; + res.edgeNormal.y = -line.x; } } - es.Vector2Ext.normalize(edgeNormal); - return closestPoint; + res.edgeNormal = res.edgeNormal.normalize(); + return res; }; /** * 旋转原始点并复制旋转的值到旋转的点 @@ -8075,41 +9660,47 @@ var es; } }; Polygon.prototype.recalculateBounds = function (collider) { + var _this = this; // 如果我们没有旋转或不关心TRS我们使用localOffset作为中心,我们会从那开始 - this.center = collider.localOffset.clone(); + this.center = collider.localOffset; if (collider.shouldColliderScaleAndRotateWithTransform) { var hasUnitScale = true; - var tempMat = void 0; - var combinedMatrix = es.Matrix2D.createTranslation(-this._polygonCenter.x, -this._polygonCenter.y); + var tempMat = new es.Matrix2D(); + var combinedMatrix_1 = new es.Matrix2D(); + es.Matrix2D.createTranslation(this._polygonCenter.x * -1, this._polygonCenter.y * -1, combinedMatrix_1); if (!collider.entity.transform.scale.equals(es.Vector2.one)) { - tempMat = es.Matrix2D.createScale(collider.entity.transform.scale.x, collider.entity.transform.scale.y); - combinedMatrix = combinedMatrix.multiply(tempMat); + es.Matrix2D.createScale(collider.entity.scale.x, collider.entity.scale.y, tempMat); + es.Matrix2D.multiply(combinedMatrix_1, tempMat, combinedMatrix_1); hasUnitScale = false; // 缩放偏移量并将其设置为中心。如果我们有旋转,它会在下面重置 - this.center = es.Vector2.multiply(collider.localOffset, collider.entity.transform.scale); + var scaledOffset = new es.Vector2(collider.localOffset.x * collider.entity.scale.x, collider.localOffset.y * collider.entity.scale.y); + this.center = scaledOffset; } if (collider.entity.transform.rotation != 0) { - tempMat = es.Matrix2D.createRotation(collider.entity.transform.rotation); - combinedMatrix = combinedMatrix.multiply(tempMat); + es.Matrix2D.createRotation(es.MathHelper.Deg2Rad * collider.entity.rotation, tempMat); + es.Matrix2D.multiply(combinedMatrix_1, tempMat, combinedMatrix_1); // 为了处理偏移原点的旋转我们只需要将圆心在(0,0)附近移动 // 我们的偏移使角度为0我们还需要处理这里的比例所以我们先对偏移进行缩放以得到合适的长度。 var offsetAngle = Math.atan2(collider.localOffset.y * collider.entity.transform.scale.y, collider.localOffset.x * collider.entity.transform.scale.x) * es.MathHelper.Rad2Deg; var offsetLength = hasUnitScale ? collider._localOffsetLength : - es.Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length(); + collider.localOffset.multiply(collider.entity.transform.scale).magnitude(); this.center = es.MathHelper.pointOnCirlce(es.Vector2.zero, offsetLength, collider.entity.transform.rotationDegrees + offsetAngle); } - tempMat = es.Matrix2D.createTranslation(this._polygonCenter.x, this._polygonCenter.y); - combinedMatrix = combinedMatrix.multiply(tempMat); + es.Matrix2D.createTranslation(this._polygonCenter.x, this._polygonCenter.y, tempMat); + es.Matrix2D.multiply(combinedMatrix_1, tempMat, combinedMatrix_1); // 最后变换原始点 - es.Vector2Ext.transform(this._originalPoints, combinedMatrix, this.points); + this.points = []; + this._originalPoints.forEach(function (p) { + _this.points.push(p.transform(combinedMatrix_1)); + }); this.isUnrotated = collider.entity.transform.rotation == 0; // 如果旋转的话,我们只需要重建边的法线 if (collider._isRotationDirty) this._areEdgeNormalsDirty = true; } - this.position = es.Vector2.add(collider.entity.transform.position, this.center); + this.position = collider.transform.position.add(this.center); this.bounds = es.Rectangle.rectEncompassingPoints(this.points); - this.bounds.location = es.Vector2.add(this.bounds.location, this.position); + this.bounds.location = this.bounds.location.add(this.position); }; Polygon.prototype.overlaps = function (other) { var result = new es.CollisionResult(); @@ -8147,10 +9738,10 @@ var es; */ Polygon.prototype.containsPoint = function (point) { // 将点归一化到多边形坐标空间中 - point.subtract(this.position); + point = point.sub(this.position); var isInside = false; for (var i = 0, j = this.points.length - 1; i < this.points.length; j = i++) { - if (((this.points[i].y > point.y) != (this.points[j].y > point.y)) && + if (((this.points[i].y > point.y) !== (this.points[j].y > point.y)) && (point.x < (this.points[j].x - this.points[i].x) * (point.y - this.points[i].y) / (this.points[j].y - this.points[i].y) + this.points[i].x)) { isInside = !isInside; @@ -8264,17 +9855,17 @@ var es; if (collider.shouldColliderScaleAndRotateWithTransform) { // 我们只将直线缩放为一个圆,所以我们将使用最大值 var scale = collider.entity.transform.scale; - var hasUnitScale = scale.x == 1 && scale.y == 1; + var hasUnitScale = scale.x === 1 && scale.y === 1; var maxScale = Math.max(scale.x, scale.y); this.radius = this._originalRadius * maxScale; - if (collider.entity.transform.rotation != 0) { + if (collider.entity.transform.rotation !== 0) { // 为了处理偏移原点的旋转,我们只需要将圆心围绕(0,0)在一个圆上移动,我们的偏移量就是0角 var offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * es.MathHelper.Rad2Deg; - var offsetLength = hasUnitScale ? collider._localOffsetLength : es.Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length(); - this.center = es.MathHelper.pointOnCirlce(es.Vector2.zero, offsetLength, collider.entity.transform.rotationDegrees + offsetAngle); + var offsetLength = hasUnitScale ? collider._localOffsetLength : collider.localOffset.multiply(collider.entity.transform.scale).magnitude(); + this.center = es.MathHelper.pointOnCirlce(es.Vector2.zero, offsetLength, collider.entity.transform.rotation + offsetAngle); } } - this.position = es.Vector2.add(collider.entity.transform.position, this.center); + this.position = collider.transform.position.add(this.center); this.bounds = new es.Rectangle(this.position.x - this.radius, this.position.y - this.radius, this.radius * 2, this.radius * 2); }; Circle.prototype.overlaps = function (other) { @@ -8302,12 +9893,15 @@ var es; Circle.prototype.collidesWithLine = function (start, end, hit) { return es.ShapeCollisionsLine.lineToCircle(start, end, this, hit); }; + Circle.prototype.getPointAlongEdge = function (angle) { + return new es.Vector2(this.position.x + this.radius * Math.cos(angle), this.position.y + this.radius * Math.sin(angle)); + }; /** * 获取所提供的点是否在此范围内 * @param point */ Circle.prototype.containsPoint = function (point) { - return (es.Vector2.subtract(point, this.position)).lengthSquared() <= this.radius * this.radius; + return (point.sub(this.position)).lengthSquared() <= this.radius * this.radius; }; Circle.prototype.pointCollidesWithShape = function (point, result) { return es.ShapeCollisionsPoint.pointToCircle(point, this, result); @@ -8333,14 +9927,33 @@ var es; */ this.point = es.Vector2.zero; } + CollisionResult.prototype.reset = function () { + this.collider = null; + this.normal.setTo(0, 0); + this.minimumTranslationVector.setTo(0, 0); + if (this.point) { + this.point.setTo(0, 0); + } + }; + CollisionResult.prototype.cloneTo = function (cr) { + cr.collider = this.collider; + cr.normal.setTo(this.normal.x, this.normal.y); + cr.minimumTranslationVector.setTo(this.minimumTranslationVector.x, this.minimumTranslationVector.y); + if (this.point) { + if (!cr.point) { + cr.point = new es.Vector2(0, 0); + } + cr.point.setTo(this.point.x, this.point.y); + } + }; /** * 改变最小平移向量,如果没有相同方向上的运动,它将移除平移的x分量。 * @param deltaMovement */ - CollisionResult.prototype.removeHorizontal = function (deltaMovement) { + CollisionResult.prototype.removeHorizontalTranslation = function (deltaMovement) { // 检查是否需要横向移动,如果需要,移除并固定响应 - if (Math.sign(this.normal.x) != Math.sign(deltaMovement.x) || (deltaMovement.x == 0 && this.normal.x != 0)) { - var responseDistance = this.minimumTranslationVector.length(); + if (Math.sign(this.normal.x) !== Math.sign(deltaMovement.x) || (deltaMovement.x === 0 && this.normal.x !== 0)) { + var responseDistance = this.minimumTranslationVector.magnitude(); var fix = responseDistance / this.normal.y; // 检查一些边界情况。因为我们除以法线 使得x == 1和一个非常小的y这将导致一个巨大的固定值 if (Math.abs(this.normal.x) != 1 && Math.abs(fix) < Math.abs(deltaMovement.y * 3)) { @@ -8349,9 +9962,8 @@ var es; } }; CollisionResult.prototype.invertResult = function () { - this.minimumTranslationVector = es.Vector2.negate(this.minimumTranslationVector); - this.normal = es.Vector2.negate(this.normal); - return this; + this.minimumTranslationVector = this.minimumTranslationVector.negate(); + this.normal = this.normal.negate(); }; CollisionResult.prototype.toString = function () { return "[CollisionResult] normal: " + this.normal + ", minimumTranslationVector: " + this.minimumTranslationVector; @@ -8367,14 +9979,15 @@ var es; } RealtimeCollisions.intersectMovingCircleBox = function (s, b, movement, time) { // 计算将b按球面半径r扩大后的AABB - var e = b.bounds.clone(); + var e = b.bounds; e.inflate(s.radius, s.radius); // 将射线与展开的矩形e相交,如果射线错过了e,则以无交点退出,否则得到交点p和时间t作为结果。 - var ray = new es.Ray2D(es.Vector2.subtract(s.position, movement), s.position); - if (!e.rayIntersects(ray, time) && time.value > 1) + var ray = new es.Ray2D(s.position.sub(movement), s.position); + var res = e.rayIntersects(ray); + if (!res.intersected && res.distance > 1) return false; // 求交点 - var point = es.Vector2.add(ray.start, es.Vector2.multiply(ray.direction, new es.Vector2(time.value))); + var point = ray.start.add(ray.direction.scale(time)); // 计算交点p位于b的哪个最小面和最大面之外。注意,u和v不能有相同的位集,它们之间必须至少有一个位集。 var u, v = 0; if (point.x < b.bounds.left) @@ -8406,7 +10019,7 @@ var es; * @param n */ RealtimeCollisions.corner = function (b, n) { - var p = new es.Vector2(); + var p = es.Vector2.zero; p.x = (n & 1) == 0 ? b.right : b.left; p.y = (n & 1) == 0 ? b.bottom : b.top; return p; @@ -8421,8 +10034,8 @@ var es; // 找出离球心最近的点 point = box.bounds.getClosestPointOnRectangleToPoint(cirlce.position); // 圆和方块相交,如果圆心到点的距离小于圆的半径,则圆和方块相交 - var v = es.Vector2.subtract(point, cirlce.position); - var dist = es.Vector2.dot(v, v); + var v = point.sub(cirlce.position); + var dist = v.dot(v); return dist <= cirlce.radius * cirlce.radius; }; return RealtimeCollisions; @@ -8441,8 +10054,8 @@ var es; result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin(); if (result.minimumTranslationVector.equals(es.Vector2.zero)) return false; - result.normal = new es.Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y); - result.normal.normalize(); + result.normal = result.minimumTranslationVector.scale(-1); + result.normal = result.normal.normalize(); return true; } return false; @@ -8462,22 +10075,22 @@ var es; var mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin(); if (mtv.equals(es.Vector2.zero)) return false; - hit.normal = new es.Vector2(-mtv.x); - hit.normal.normalize(); + hit.normal = new es.Vector2(-mtv.x, -mtv.y); + hit.normal = hit.normal.normalize(); hit.distance = 0; hit.fraction = 0; return true; } else { // 射线投射移动矢量 - var ray = new es.Ray2D(es.Vector2.zero, new es.Vector2(-movement.x)); - var fraction = new es.Ref(0); - if (minkowskiDiff.rayIntersects(ray, fraction) && fraction.value <= 1) { - hit.fraction = fraction.value; - hit.distance = movement.length() * fraction.value; - hit.normal = new es.Vector2(-movement.x, -movement.y); - hit.normal.normalize(); - hit.centroid = es.Vector2.add(first.bounds.center, es.Vector2.multiply(movement, new es.Vector2(fraction.value))); + var ray = new es.Ray2D(es.Vector2.zero, movement.scale(-1)); + var res = minkowskiDiff.rayIntersects(ray); + if (res.intersected && res.distance <= 1) { + hit.fraction = res.distance; + hit.distance = movement.magnitude() * res.distance; + hit.normal = movement.scale(-1); + hit.normal = hit.normal.normalize(); + hit.centroid = first.bounds.center.add(movement.scale(res.distance)); return true; } } @@ -8486,9 +10099,9 @@ var es; ShapeCollisionsBox.minkowskiDifference = function (first, second) { // 我们需要第一个框的左上角 // 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。 - var positionOffset = es.Vector2.subtract(first.position, es.Vector2.add(first.bounds.location, new es.Vector2(first.bounds.size.x / 2, first.bounds.size.y / 2))); - var topLeft = es.Vector2.subtract(es.Vector2.add(first.bounds.location, positionOffset), second.bounds.max); - var fullSize = es.Vector2.add(first.bounds.size, second.bounds.size); + var positionOffset = first.position.sub(first.bounds.center); + var topLeft = first.bounds.location.add(positionOffset.sub(second.bounds.max)); + var fullSize = first.bounds.size.add(second.bounds.size); return new es.Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y); }; return ShapeCollisionsBox; @@ -8500,20 +10113,38 @@ var es; var ShapeCollisionsCircle = /** @class */ (function () { function ShapeCollisionsCircle() { } + ShapeCollisionsCircle.circleToCircleCast = function (first, second, deltaMovement, hit) { + var endPointOfCast = first.position.add(deltaMovement); + var d = this.closestPointOnLine(first.position, endPointOfCast, second.position); + var closestDistanceSquared = es.Vector2.sqrDistance(second.position, d); + var sumOfRadiiSquared = (first.radius + second.radius) * (first.radius + second.radius); + if (closestDistanceSquared <= sumOfRadiiSquared) { + var normalizedDeltaMovement = deltaMovement.normalize(); + if (d === endPointOfCast) { + endPointOfCast = first.position.add(deltaMovement.add(normalizedDeltaMovement.scale(second.radius))); + d = this.closestPointOnLine(first.position, endPointOfCast, second.position); + closestDistanceSquared = es.Vector2.sqrDistance(second.position, d); + } + var backDist = Math.sqrt(sumOfRadiiSquared - closestDistanceSquared); + hit.centroid = d.sub(normalizedDeltaMovement.scale(backDist)); + hit.normal = hit.centroid.sub(second.position).normalize(); + hit.fraction = (hit.centroid.x - first.position.x) / deltaMovement.x; + hit.distance = es.Vector2.distance(first.position, hit.centroid); + hit.point = second.position.add(hit.normal.scale(second.radius)); + return true; + } + return false; + }; ShapeCollisionsCircle.circleToCircle = function (first, second, result) { if (result === void 0) { result = new es.CollisionResult(); } - var distanceSquared = es.Vector2.distanceSquared(first.position, second.position); + var distanceSquared = es.Vector2.sqrDistance(first.position, second.position); var sumOfRadii = first.radius + second.radius; var collided = distanceSquared < sumOfRadii * sumOfRadii; if (collided) { - result.normal = es.Vector2.normalize(es.Vector2.subtract(first.position, second.position)); + result.normal = first.position.sub(second.position).normalize(); var depth = sumOfRadii - Math.sqrt(distanceSquared); - result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(-depth), result.normal); - result.point = es.Vector2.add(second.position, es.Vector2.multiply(result.normal, new es.Vector2(second.radius))); - // 这可以得到实际的碰撞点,可能有用也可能没用,所以我们暂时把它留在这里 - // let collisionPointX = ((first.position.x * second.radius) + (second.position.x * first.radius)) / sumOfRadii; - // let collisionPointY = ((first.position.y * second.radius) + (second.position.y * first.radius)) / sumOfRadii; - // result.point = new Vector2(collisionPointX, collisionPointY); + result.minimumTranslationVector = result.normal.scale(-depth); + result.point = second.position.add(result.normal.scale(second.radius)); return true; } return false; @@ -8529,23 +10160,23 @@ var es; var closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position, result.normal); // 先处理中心在盒子里的圆,如果我们是包含的, 它的成本更低, if (box.containsPoint(circle.position)) { - result.point = closestPointOnBounds.clone(); + result.point = closestPointOnBounds; // 计算MTV。找出安全的、非碰撞的位置,并从中得到MTV - var safePlace = es.Vector2.add(closestPointOnBounds, es.Vector2.multiply(result.normal, new es.Vector2(circle.radius))); - result.minimumTranslationVector = es.Vector2.subtract(circle.position, safePlace); + var safePlace = closestPointOnBounds.add(result.normal.scale(circle.radius)); + result.minimumTranslationVector = circle.position.sub(safePlace); return true; } - var sqrDistance = es.Vector2.distanceSquared(closestPointOnBounds, circle.position); + var sqrDistance = es.Vector2.sqrDistance(closestPointOnBounds, circle.position); // 看框上的点距圆的半径是否小于圆的半径 if (sqrDistance == 0) { - result.minimumTranslationVector = es.Vector2.multiply(result.normal, new es.Vector2(circle.radius)); + result.minimumTranslationVector = result.normal.scale(circle.radius); } else if (sqrDistance <= circle.radius * circle.radius) { - result.normal = es.Vector2.subtract(circle.position, closestPointOnBounds); - var depth = result.normal.length() - circle.radius; + result.normal = circle.position.sub(closestPointOnBounds); + var depth = result.normal.magnitude() - circle.radius; result.point = closestPointOnBounds; - es.Vector2Ext.normalize(result.normal); - result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(depth), result.normal); + result.normal = result.normal.normalize(); + result.minimumTranslationVector = result.normal.scale(depth); return true; } return false; @@ -8553,42 +10184,43 @@ var es; ShapeCollisionsCircle.circleToPolygon = function (circle, polygon, result) { if (result === void 0) { result = new es.CollisionResult(); } // 圆圈在多边形中的位置坐标 - var poly2Circle = es.Vector2.subtract(circle.position, polygon.position); + var poly2Circle = circle.position.sub(polygon.position); // 首先,我们需要找到从圆到多边形的最近距离 - var distanceSquared = new es.Ref(0); - var closestPoint = es.Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle, distanceSquared, result.normal); + var res = es.Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle); + result.normal = res.edgeNormal; // 确保距离的平方小于半径的平方,否则我们不会相撞。 // 请注意,如果圆完全包含在多边形中,距离可能大于半径。 // 正因为如此,我们还要确保圆的位置不在多边形内。 var circleCenterInsidePoly = polygon.containsPoint(circle.position); - if (distanceSquared.value > circle.radius * circle.radius && !circleCenterInsidePoly) + if (res.distanceSquared > circle.radius * circle.radius && !circleCenterInsidePoly) return false; // 算出MTV。我们要注意处理完全包含在多边形中的圆或包含其中心的圆 var mtv; if (circleCenterInsidePoly) { - mtv = es.Vector2.multiply(result.normal, new es.Vector2(Math.sqrt(distanceSquared.value) - circle.radius)); + mtv = result.normal.scale(Math.sqrt(res.distanceSquared) - circle.radius); } else { // 如果我们没有距离,这意味着圆心在多边形的边缘上。只需根据它的半径移动它 - if (distanceSquared.value == 0) { - mtv = new es.Vector2(result.normal.x * circle.radius, result.normal.y * circle.radius); + if (res.distanceSquared === 0) { + mtv = result.normal.scale(circle.radius); } else { - var distance = Math.sqrt(distanceSquared.value); - mtv = es.Vector2.subtract(new es.Vector2(-1), es.Vector2.subtract(poly2Circle, closestPoint)) - .multiply(new es.Vector2((circle.radius - distance) / distance)); + var distance = Math.sqrt(res.distanceSquared); + mtv = poly2Circle + .sub(res.closestPoint) + .scale(((circle.radius - distance) / distance) * -1); } } result.minimumTranslationVector = mtv; - result.point = es.Vector2.add(closestPoint, polygon.position); + result.point = res.closestPoint.add(polygon.position); return true; }; ShapeCollisionsCircle.closestPointOnLine = function (lineA, lineB, closestTo) { - var v = es.Vector2.subtract(lineB, lineA); - var w = es.Vector2.subtract(closestTo, lineA); - var t = es.Vector2.dot(w, v) / es.Vector2.dot(v, v); + var v = lineB.sub(lineA); + var w = closestTo.sub(lineA); + var t = w.dot(v) / v.dot(v); t = es.MathHelper.clamp(t, 0, 1); - return es.Vector2.add(lineA, es.Vector2.multiply(v, new es.Vector2(t))); + return lineA.add(v.scaleEqual(t)); }; return ShapeCollisionsCircle; }()); @@ -8609,15 +10241,15 @@ var es; var edge1 = es.Vector2.add(polygon.position, polygon.points[j]); var edge2 = es.Vector2.add(polygon.position, polygon.points[i]); var intersection = es.Vector2.zero; - if (this.lineToLine(edge1, edge2, start, end, intersection)) { + if (ShapeCollisionsLine.lineToLine(edge1, edge2, start, end, intersection)) { hasIntersection = true; // TODO: 这是得到分数的正确和最有效的方法吗? // 先检查x分数。如果是NaN,就用y代替 var distanceFraction = (intersection.x - start.x) / (end.x - start.x); - if (Number.isNaN(distanceFraction) || Number.isFinite(distanceFraction)) + if (Number.isNaN(distanceFraction) || Math.abs(distanceFraction) == Infinity) distanceFraction = (intersection.y - start.y) / (end.y - start.y); if (distanceFraction < fraction) { - var edge = es.Vector2.subtract(edge2, edge1); + var edge = edge2.sub(edge1); normal = new es.Vector2(edge.y, -edge.x); fraction = distanceFraction; intersectionPoint = intersection; @@ -8625,37 +10257,39 @@ var es; } } if (hasIntersection) { - normal.normalize(); + normal = normal.normalize(); var distance = es.Vector2.distance(start, intersectionPoint); - hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal); + hit.setValues(fraction, distance, intersectionPoint, normal); return true; } return false; }; ShapeCollisionsLine.lineToLine = function (a1, a2, b1, b2, intersection) { - var b = es.Vector2.subtract(a2, a1); - var d = es.Vector2.subtract(b2, b1); + var b = a2.sub(a1); + var d = b2.sub(b1); var bDotDPerp = b.x * d.y - b.y * d.x; // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 if (bDotDPerp == 0) return false; - var c = es.Vector2.subtract(b1, a1); + var c = b1.sub(a1); var t = (c.x * d.y - c.y * d.x) / bDotDPerp; if (t < 0 || t > 1) return false; var u = (c.x * b.y - c.y * b.x) / bDotDPerp; if (u < 0 || u > 1) return false; - intersection = es.Vector2.add(a1, es.Vector2.multiply(new es.Vector2(t), b)); + var r = a1.add(b.scale(t)); + intersection.x = r.x; + intersection.y = r.y; return true; }; ShapeCollisionsLine.lineToCircle = function (start, end, s, hit) { // 计算这里的长度并分别对d进行标准化,因为如果我们命中了我们需要它来得到分数 var lineLength = es.Vector2.distance(start, end); - var d = es.Vector2.divide(es.Vector2.subtract(end, start), new es.Vector2(lineLength)); - var m = es.Vector2.subtract(start, s.position); - var b = es.Vector2.dot(m, d); - var c = es.Vector2.dot(m, m) - s.radius * s.radius; + var d = es.Vector2.divideScaler(end.sub(start), lineLength); + var m = start.sub(s.position); + var b = m.dot(d); + var c = m.dot(m) - s.radius * s.radius; // 如果r的原点在s之外,(c>0)和r指向s (b>0) 则返回 if (c > 0 && b > 0) return false; @@ -8668,9 +10302,9 @@ var es; // 如果分数为负数,射线从圈内开始, if (hit.fraction < 0) hit.fraction = 0; - hit.point = es.Vector2.add(start, es.Vector2.multiply(new es.Vector2(hit.fraction), d)); + hit.point = start.add(d.scale(hit.fraction)); hit.distance = es.Vector2.distance(start, hit.point); - hit.normal = es.Vector2.normalize(es.Vector2.subtract(hit.point, s.position)); + hit.normal = hit.point.sub(s.position).normalize(); hit.fraction = hit.distance / lineLength; return true; }; @@ -8684,14 +10318,15 @@ var es; function ShapeCollisionsPoint() { } ShapeCollisionsPoint.pointToCircle = function (point, circle, result) { - var distanceSquared = es.Vector2.distanceSquared(point, circle.position); + var distanceSquared = es.Vector2.sqrDistance(point, circle.position); var sumOfRadii = 1 + circle.radius; var collided = distanceSquared < sumOfRadii * sumOfRadii; if (collided) { - result.normal = es.Vector2.normalize(es.Vector2.subtract(point, circle.position)); + result.normal = point.sub(circle.position).normalize(); var depth = sumOfRadii - Math.sqrt(distanceSquared); - result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(-depth, -depth), result.normal); - result.point = es.Vector2.add(circle.position, es.Vector2.multiply(result.normal, new es.Vector2(circle.radius, circle.radius))); + result.minimumTranslationVector = result.normal.scale(-depth); + ; + result.point = circle.position.add(result.normal.scale(circle.radius)); return true; } return false; @@ -8701,7 +10336,7 @@ var es; if (box.containsPoint(point)) { // 在方框的空间里找到点 result.point = box.bounds.getClosestPointOnRectangleBorderToPoint(point, result.normal); - result.minimumTranslationVector = es.Vector2.subtract(point, result.point); + result.minimumTranslationVector = point.sub(result.point); return true; } return false; @@ -8709,10 +10344,10 @@ var es; ShapeCollisionsPoint.pointToPoly = function (point, poly, result) { if (result === void 0) { result = new es.CollisionResult(); } if (poly.containsPoint(point)) { - var distanceSquared = new es.Ref(0); - var closestPoint = es.Polygon.getClosestPointOnPolygonToPoint(poly.points, es.Vector2.subtract(point, poly.position), distanceSquared, result.normal); - result.minimumTranslationVector = new es.Vector2(result.normal.x * Math.sqrt(distanceSquared.value), result.normal.y * Math.sqrt(distanceSquared.value)); - result.point = es.Vector2.add(closestPoint, poly.position); + var res = es.Polygon.getClosestPointOnPolygonToPoint(poly.points, point.sub(poly.position)); + result.normal = res.edgeNormal; + result.minimumTranslationVector = result.normal.scale(Math.sqrt(res.distanceSquared)); + result.point = res.closestPoint.sub(poly.position); return true; } return false; @@ -8734,36 +10369,27 @@ var es; */ ShapeCollisionsPolygon.polygonToPolygon = function (first, second, result) { var isIntersecting = true; - var firstEdges = first.edgeNormals.slice(); - var secondEdges = second.edgeNormals.slice(); + var firstEdges = first.edgeNormals; + var secondEdges = second.edgeNormals; var minIntervalDistance = Number.POSITIVE_INFINITY; - var translationAxis = new es.Vector2(); - var polygonOffset = es.Vector2.subtract(first.position, second.position); + var translationAxis = es.Vector2.zero; + var polygonOffset = first.position.sub(second.position); var axis; // 循环穿过两个多边形的所有边 for (var edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) { // 1. 找出当前多边形是否相交 // 多边形的归一化轴垂直于缓存给我们的当前边 - if (edgeIndex < firstEdges.length) { - axis = firstEdges[edgeIndex]; - } - else { - axis = secondEdges[edgeIndex - firstEdges.length]; - } + axis = edgeIndex < firstEdges.length ? firstEdges[edgeIndex] : secondEdges[edgeIndex - firstEdges.length]; // 求多边形在当前轴上的投影 - var minA = new es.Ref(0); - var minB = new es.Ref(0); - var maxA = new es.Ref(0); - var maxB = new es.Ref(0); var intervalDist = 0; - this.getInterval(axis, first, minA, maxA); - this.getInterval(axis, second, minB, maxB); + var _a = this.getInterval(axis, first), minA = _a.min, maxA = _a.max; + var _b = this.getInterval(axis, second), minB = _b.min, maxB = _b.max; // 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。 - var relativeIntervalOffset = es.Vector2.dot(polygonOffset, axis); - minA.value += relativeIntervalOffset; - maxA.value += relativeIntervalOffset; + var relativeIntervalOffset = polygonOffset.dot(axis); + minA += relativeIntervalOffset; + maxA += relativeIntervalOffset; // 检查多边形投影是否正在相交 - intervalDist = this.intervalDistance(minA.value, maxA.value, minB.value, maxB.value); + intervalDist = this.intervalDistance(minA, maxA, minB, maxB); if (intervalDist > 0) isIntersecting = false; // 对于多对多数据类型转换,添加一个Vector2?参数称为deltaMovement。为了提高速度,我们这里不使用它 @@ -8775,14 +10401,14 @@ var es; intervalDist = Math.abs(intervalDist); if (intervalDist < minIntervalDistance) { minIntervalDistance = intervalDist; - translationAxis = axis; - if (es.Vector2.dot(translationAxis, polygonOffset) < 0) - translationAxis = new es.Vector2(-translationAxis.x, -translationAxis.y); + translationAxis.setTo(axis.x, axis.y); + if (translationAxis.dot(polygonOffset) < 0) + translationAxis = translationAxis.scale(-1); } } // 利用最小平移向量对多边形进行推入。 result.normal = translationAxis; - result.minimumTranslationVector = new es.Vector2(-translationAxis.x * minIntervalDistance, -translationAxis.y * minIntervalDistance); + result.minimumTranslationVector = translationAxis.scale(-minIntervalDistance); return true; }; /** @@ -8792,18 +10418,22 @@ var es; * @param min * @param max */ - ShapeCollisionsPolygon.getInterval = function (axis, polygon, min, max) { - var dot = es.Vector2.dot(polygon.points[0], axis); - min.value = max.value = dot; + ShapeCollisionsPolygon.getInterval = function (axis, polygon) { + var res = { min: 0, max: 0 }; + var dot; + dot = polygon.points[0].dot(axis); + res.max = dot; + res.min = dot; for (var i = 1; i < polygon.points.length; i++) { - dot = es.Vector2.dot(polygon.points[i], axis); - if (dot < min.value) { - min.value = dot; + dot = polygon.points[i].dot(axis); + if (dot < res.min) { + res.min = dot; } - else if (dot > max.value) { - max.value = dot; + else if (dot > res.max) { + res.max = dot; } } + return res; }; /** * 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的 @@ -8822,6 +10452,1983 @@ var es; es.ShapeCollisionsPolygon = ShapeCollisionsPolygon; })(es || (es = {})); var es; +(function (es) { + var Particle = /** @class */ (function () { + function Particle(position) { + this.position = es.Vector2.zero; + this.lastPosition = es.Vector2.zero; + this.mass = 1; + this.radius = 0; + this.collidesWithColliders = true; + this.isPinned = false; + this.acceleration = es.Vector2.zero; + this.pinnedPosition = es.Vector2.zero; + this.position = new es.Vector2(position.x, position.y); + this.lastPosition = new es.Vector2(position.x, position.y); + } + Particle.prototype.applyForce = function (force) { + this.acceleration = this.acceleration.add(force.divideScaler(this.mass)); + }; + Particle.prototype.pin = function () { + this.isPinned = true; + this.pinnedPosition = this.position; + return this; + }; + Particle.prototype.pinTo = function (position) { + this.isPinned = true; + this.pinnedPosition = position; + this.position = this.pinnedPosition; + return this; + }; + Particle.prototype.unpin = function () { + this.isPinned = false; + return this; + }; + return Particle; + }()); + es.Particle = Particle; +})(es || (es = {})); +var es; +(function (es) { + var VerletWorld = /** @class */ (function () { + function VerletWorld(simulationBounds) { + if (simulationBounds === void 0) { simulationBounds = null; } + this.gravity = new es.Vector2(0, -980); + this.constraintIterations = 3; + this.maximumStepIterations = 5; + this.allowDragging = true; + this.selectionRadiusSquared = 20 * 20; + this._composites = []; + this._tempCircle = new es.Circle(1); + this._leftOverTime = 0; + this._fixedDeltaTime = 1 / 60; + this._iterationSteps = 0; + this._fixedDeltaTimeSq = 0; + this.simulationBounds = simulationBounds; + this._fixedDeltaTimeSq = Math.pow(this._fixedDeltaTime, 2); + } + VerletWorld.prototype.update = function () { + this.updateTiming(); + if (this.allowDragging) + this.handleDragging(); + for (var iteration = 1; iteration <= this._iterationSteps; iteration++) { + for (var i = this._composites.length - 1; i >= 0; i--) { + var composite = this._composites[i]; + for (var s = 0; s < this.constraintIterations; s++) + composite.solveConstraints(); + composite.updateParticles(this._fixedDeltaTimeSq, this.gravity); + composite.handleConstraintCollisions(); + for (var j = 0; j < composite.particles.length; j++) { + var p = composite.particles[j]; + if (this.simulationBounds) { + this.constrainParticleToBounds(p); + } + if (p.collidesWithColliders) + this.handleCollisions(p, composite.collidesWithLayers); + } + } + } + }; + VerletWorld.prototype.constrainParticleToBounds = function (p) { + var tempPos = p.position; + var bounds = this.simulationBounds; + if (p.radius == 0) { + if (tempPos.y > bounds.height) + tempPos.y = bounds.height; + else if (tempPos.y < bounds.y) + tempPos.y = bounds.y; + if (tempPos.x < bounds.x) + tempPos.x = bounds.x; + else if (tempPos.x > bounds.width) + tempPos.x = bounds.width; + } + else { + if (tempPos.y < bounds.y + p.radius) + tempPos.y = 2 * (bounds.y + p.radius) - tempPos.y; + if (tempPos.y > bounds.height - p.radius) + tempPos.y = 2 * (bounds.height - p.radius) - tempPos.y; + if (tempPos.x > bounds.width - p.radius) + tempPos.x = 2 * (bounds.width - p.radius) - tempPos.x; + if (tempPos.x < bounds.x + p.radius) + tempPos.x = 2 * (bounds.x + p.radius) - tempPos.x; + } + p.position = tempPos; + }; + VerletWorld.prototype.handleCollisions = function (p, collidesWithLayers) { + var collidedCount = es.Physics.overlapCircleAll(p.position, p.radius, VerletWorld._colliders, collidesWithLayers); + for (var i = 0; i < collidedCount; i++) { + var collider = VerletWorld._colliders[i]; + if (collider.isTrigger) + continue; + var collisionResult = new es.CollisionResult(); + if (p.radius < 2) { + if (collider.shape.pointCollidesWithShape(p.position, collisionResult)) { + p.position = p.position.sub(collisionResult.minimumTranslationVector); + } + } + else { + this._tempCircle.radius = p.radius; + this._tempCircle.position = p.position; + if (this._tempCircle.collidesWithShape(collider.shape, collisionResult)) { + p.position = p.position.sub(collisionResult.minimumTranslationVector); + } + } + } + }; + VerletWorld.prototype.updateTiming = function () { + this._leftOverTime += es.Time.deltaTime; + this._iterationSteps = Math.trunc(this._leftOverTime / this._fixedDeltaTime); + this._leftOverTime -= this._iterationSteps * this._fixedDeltaTime; + this._iterationSteps = Math.min(this._iterationSteps, this.maximumStepIterations); + }; + VerletWorld.prototype.addComposite = function (composite) { + this._composites.push(composite); + return composite; + }; + VerletWorld.prototype.removeComposite = function (composite) { + var index = this._composites.indexOf(composite); + this._composites.splice(index, 1); + }; + VerletWorld.prototype.handleDragging = function () { + if (this.onHandleDrag) + this.onHandleDrag(); + }; + VerletWorld.prototype.getNearestParticle = function (position) { + var nearestSquaredDistance = this.selectionRadiusSquared; + var particle = null; + for (var j = 0; j < this._composites.length; j++) { + var particles = this._composites[j].particles; + for (var i = 0; i < particles.length; i++) { + var p = particles[i]; + var squaredDistanceToParticle = es.Vector2.sqrDistance(p.position, position); + if (squaredDistanceToParticle <= this.selectionRadiusSquared && + (particle == null || squaredDistanceToParticle < nearestSquaredDistance)) { + particle = p; + nearestSquaredDistance = squaredDistanceToParticle; + } + } + } + return particle; + }; + VerletWorld.prototype.debugRender = function (batcher) { + for (var i = 0; i < this._composites.length; i++) { + this._composites[i].debugRender(batcher); + } + if (this.allowDragging) { + if (this._draggedParticle != null) { + batcher.drawCircle(this._draggedParticle.position, 8, es.Color.White); + } + } + }; + VerletWorld._colliders = []; + return VerletWorld; + }()); + es.VerletWorld = VerletWorld; +})(es || (es = {})); +var es; +(function (es) { + var Composite = /** @class */ (function () { + function Composite() { + this.friction = new es.Vector2(0.98, 1); + this.drawParticles = true; + this.drawConstraints = true; + this.collidesWithLayers = es.Physics.allLayers; + this.particles = []; + this._constraints = []; + } + Composite.prototype.addParticle = function (particle) { + this.particles.push(particle); + return particle; + }; + Composite.prototype.removeParticle = function (particle) { + var index = this.particles.indexOf(particle); + this.particles.splice(index, 1); + }; + Composite.prototype.removeAll = function () { + this.particles.length = 0; + this._constraints.length = 0; + }; + Composite.prototype.addConstraint = function (constraint) { + this._constraints.push(constraint); + constraint.composite = this; + return constraint; + }; + Composite.prototype.removeConstraint = function (constraint) { + var index = this._constraints.indexOf(constraint); + this._constraints.splice(index, 1); + }; + Composite.prototype.applyForce = function (force) { + for (var j = 0; j < this.particles.length; j++) + this.particles[j].applyForce(force); + }; + Composite.prototype.solveConstraints = function () { + for (var i = this._constraints.length - 1; i >= 0; i--) + this._constraints[i].solve(); + }; + Composite.prototype.updateParticles = function (deltaTimeSquared, gravity) { + for (var j = 0; j < this.particles.length; j++) { + var p = this.particles[j]; + if (p.isPinned) { + p.position = p.pinnedPosition; + continue; + } + p.applyForce(gravity.scale(p.mass)); + var vel = p.position.sub(p.lastPosition).multiply(this.friction); + var nextPos = p.position.add(vel).add(p.acceleration.scale(0.5 * deltaTimeSquared)); + p.lastPosition = p.position; + p.position = nextPos; + p.acceleration.x = p.acceleration.y = 0; + } + }; + Composite.prototype.handleConstraintCollisions = function () { + for (var i = this._constraints.length - 1; i >= 0; i--) { + if (this._constraints[i].collidesWithColliders) + this._constraints[i].handleCollisions(this.collidesWithLayers); + } + }; + Composite.prototype.debugRender = function (batcher) { + if (this.drawConstraints) { + for (var i = 0; i < this._constraints.length; i++) + this._constraints[i].debugRender(batcher); + } + if (this.drawParticles) { + for (var i = 0; i < this.particles.length; i++) { + if (this.particles[i].radius == 0) + batcher.drawPixel(this.particles[i].position, new es.Color(220, 52, 94), 4); + else + batcher.drawCircleLow(this.particles[i].position, this.particles[i].radius, new es.Color(220, 52, 94), 1, 4); + } + } + }; + return Composite; + }()); + es.Composite = Composite; +})(es || (es = {})); +/// +var es; +/// +(function (es) { + var Ball = /** @class */ (function (_super) { + __extends(Ball, _super); + function Ball(position, radius) { + if (radius === void 0) { radius = 10; } + var _this = _super.call(this) || this; + _this.addParticle(new es.Particle(position)).radius = radius; + return _this; + } + return Ball; + }(es.Composite)); + es.Ball = Ball; +})(es || (es = {})); +/// +var es; +/// +(function (es) { + var VerletBox = /** @class */ (function (_super) { + __extends(VerletBox, _super); + function VerletBox(center, width, height, borderStiffness, diagonalStiffness) { + if (borderStiffness === void 0) { borderStiffness = 0.2; } + if (diagonalStiffness === void 0) { diagonalStiffness = 0.5; } + var _this = _super.call(this) || this; + var tl = _this.addParticle(new es.Particle(center.add(new es.Vector2(-width / 2, -height / 2)))); + var tr = _this.addParticle(new es.Particle(center.add(new es.Vector2(width / 2, -height / 2)))); + var br = _this.addParticle(new es.Particle(center.add(new es.Vector2(width / 2, height / 2)))); + var bl = _this.addParticle(new es.Particle(center.add(new es.Vector2(-width / 2, height / 2)))); + _this.addConstraint(new es.DistanceConstraint(tl, tr, borderStiffness)); + _this.addConstraint(new es.DistanceConstraint(tr, br, borderStiffness)); + _this.addConstraint(new es.DistanceConstraint(br, bl, borderStiffness)); + _this.addConstraint(new es.DistanceConstraint(bl, tl, borderStiffness)); + _this.addConstraint(new es.DistanceConstraint(tl, br, diagonalStiffness)) + .setCollidesWithColliders(false); + _this.addConstraint(new es.DistanceConstraint(bl, tr, diagonalStiffness)) + .setCollidesWithColliders(false); + return _this; + } + return VerletBox; + }(es.Composite)); + es.VerletBox = VerletBox; +})(es || (es = {})); +var es; +(function (es) { + var Cloth = /** @class */ (function (_super) { + __extends(Cloth, _super); + function Cloth(topLeftPosition, width, height, segments, stiffness, tearSensitivity, connectHorizontalParticles) { + if (segments === void 0) { segments = 20; } + if (stiffness === void 0) { stiffness = 0.25; } + if (tearSensitivity === void 0) { tearSensitivity = 5; } + if (connectHorizontalParticles === void 0) { connectHorizontalParticles = true; } + var _this = _super.call(this) || this; + var xStride = width / segments; + var yStride = height / segments; + for (var y = 0; y < segments; y++) { + for (var x = 0; x < segments; x++) { + var px = topLeftPosition.x + x * xStride; + var py = topLeftPosition.y + y + yStride; + var particle = _this.addParticle(new es.Particle(new es.Vector2(px, py))); + if (connectHorizontalParticles && x > 0) + _this.addConstraint(new es.DistanceConstraint(_this.particles[y * segments + x], _this.particles[y * segments + x - 1], stiffness)) + .setTearSensitivity(tearSensitivity) + .setCollidesWithColliders(false); + if (y > 0) + _this.addConstraint(new es.DistanceConstraint(_this.particles[y * segments + x], _this.particles[(y - 1) * segments + x], stiffness)) + .setTearSensitivity(tearSensitivity) + .setCollidesWithColliders(false); + if (y == 0) + particle.pin(); + } + } + return _this; + } + return Cloth; + }(es.Composite)); + es.Cloth = Cloth; +})(es || (es = {})); +var es; +(function (es) { + var LineSegments = /** @class */ (function (_super) { + __extends(LineSegments, _super); + function LineSegments(vertices, stiffness) { + var _this = _super.call(this) || this; + for (var i = 0; i < vertices.length; i++) { + var p = new es.Particle(vertices[i]); + _this.addParticle(p); + if (i > 0) + _this.addConstraint(new es.DistanceConstraint(_this.particles[i], _this.particles[i - 1], stiffness)); + } + return _this; + } + LineSegments.prototype.pinParticleAtIndex = function (index) { + this.particles[index].pin(); + return this; + }; + return LineSegments; + }(es.Composite)); + es.LineSegments = LineSegments; +})(es || (es = {})); +var es; +(function (es) { + var Ragdoll = /** @class */ (function (_super) { + __extends(Ragdoll, _super); + function Ragdoll(x, y, bodyHeight) { + var _this = _super.call(this) || this; + var headLength = bodyHeight / 7.5; + var head = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + head.radius = headLength * 0.75; + head.mass = 4; + var shoulder = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + shoulder.mass = 26; + _this.addConstraint(new es.DistanceConstraint(head, shoulder, 1, 5 / 4 * headLength)); + var elbowLeft = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + var elbowRight = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + elbowLeft.mass = 2; + elbowRight.mass = 2; + _this.addConstraint(new es.DistanceConstraint(elbowLeft, shoulder, 1, headLength * 3 / 2)); + _this.addConstraint(new es.DistanceConstraint(elbowRight, shoulder, 1, headLength * 3 / 2)); + var handLeft = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + var handRight = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + handLeft.mass = 2; + handRight.mass = 2; + _this.addConstraint(new es.DistanceConstraint(handLeft, elbowLeft, 1, headLength * 2)); + _this.addConstraint(new es.DistanceConstraint(handRight, elbowRight, 1, headLength * 2)); + var pelvis = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + pelvis.mass = 15; + _this.addConstraint(new es.DistanceConstraint(pelvis, shoulder, 0.8, headLength * 3.5)); + _this.addConstraint(new es.DistanceConstraint(pelvis, head, 0.02, bodyHeight * 2)) + .setCollidesWithColliders(false); + var kneeLeft = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + var kneeRight = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + kneeLeft.mass = 10; + kneeRight.mass = 10; + _this.addConstraint(new es.DistanceConstraint(kneeLeft, pelvis, 1, headLength * 2)); + _this.addConstraint(new es.DistanceConstraint(kneeRight, pelvis, 1, headLength * 2)); + var footLeft = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + var footRight = _this.addParticle(new es.Particle({ x: x + es.RandomUtils.randint(-5, 5), y: y + es.RandomUtils.randint(-5, 5) })); + footLeft.mass = 5; + footRight.mass = 5; + _this.addConstraint(new es.DistanceConstraint(footLeft, kneeLeft, 1, headLength * 2)); + _this.addConstraint(new es.DistanceConstraint(footRight, kneeRight, 1, headLength * 2)); + _this.addConstraint(new es.DistanceConstraint(footLeft, shoulder, 0.001, bodyHeight * 2)) + .setCollidesWithColliders(false); + _this.addConstraint(new es.DistanceConstraint(footLeft, shoulder, 0.001, bodyHeight * 2)) + .setCollidesWithColliders(false); + return _this; + } + return Ragdoll; + }(es.Composite)); + es.Ragdoll = Ragdoll; +})(es || (es = {})); +var es; +(function (es) { + var Tire = /** @class */ (function (_super) { + __extends(Tire, _super); + function Tire(origin, radius, segments, spokeStiffness, treadStiffness) { + if (spokeStiffness === void 0) { spokeStiffness = 1; } + if (treadStiffness === void 0) { treadStiffness = 1; } + var _this = _super.call(this) || this; + var stride = 2 * Math.PI / segments; + for (var i = 0; i < segments; i++) { + var theta = i * stride; + _this.addParticle(new es.Particle(new es.Vector2(origin.x + Math.cos(theta) * radius, origin.y + Math.sin(theta) * radius))); + } + var centerParticle = _this.addParticle(new es.Particle(origin)); + for (var i = 0; i < segments; i++) { + _this.addConstraint(new es.DistanceConstraint(_this.particles[i], _this.particles[(i + 1) % segments], treadStiffness)); + _this.addConstraint(new es.DistanceConstraint(_this.particles[i], centerParticle, spokeStiffness)) + .setCollidesWithColliders(false); + _this.addConstraint(new es.DistanceConstraint(_this.particles[i], _this.particles[(i + 5) % segments], treadStiffness)); + } + return _this; + } + return Tire; + }(es.Composite)); + es.Tire = Tire; +})(es || (es = {})); +var es; +(function (es) { + var Constraint = /** @class */ (function () { + function Constraint() { + this.collidesWithColliders = true; + } + Constraint.prototype.handleCollisions = function (collidesWithLayers) { + }; + Constraint.prototype.debugRender = function (batcher) { + }; + return Constraint; + }()); + es.Constraint = Constraint; +})(es || (es = {})); +/// +var es; +/// +(function (es) { + var AngleConstraint = /** @class */ (function (_super) { + __extends(AngleConstraint, _super); + function AngleConstraint(a, center, c, stiffness) { + var _this = _super.call(this) || this; + _this.stiffness = 0; + _this.angleInRadius = 0; + _this._particleA = a; + _this._centerParticle = center; + _this._particleC = c; + _this.stiffness = stiffness; + _this.collidesWithColliders = false; + _this.angleInRadius = _this.angleBetweenParticles(); + return _this; + } + AngleConstraint.prototype.angleBetweenParticles = function () { + var first = this._particleA.position.sub(this._centerParticle.position); + var second = this._particleC.position.sub(this._centerParticle.position); + return Math.atan2(first.x * second.y - first.y * second.x, first.x * second.x + first.y * second.y); + }; + AngleConstraint.prototype.solve = function () { + var angleBetween = this.angleBetweenParticles(); + var diff = angleBetween - this.angleInRadius; + if (diff <= -Math.PI) + diff += 2 * Math.PI; + else if (diff >= Math.PI) + diff -= 2 * Math.PI; + diff *= this.stiffness; + this._particleA.position = es.MathHelper.rotateAround2(this._particleA.position, this._centerParticle.position, diff); + this._particleC.position = es.MathHelper.rotateAround2(this._particleC.position, this._centerParticle.position, -diff); + this._centerParticle.position = es.MathHelper.rotateAround2(this._centerParticle.position, this._particleA.position, diff); + this._centerParticle.position = es.MathHelper.rotateAround2(this._centerParticle.position, this._particleC.position, -diff); + }; + return AngleConstraint; + }(es.Constraint)); + es.AngleConstraint = AngleConstraint; +})(es || (es = {})); +var es; +(function (es) { + var DistanceConstraint = /** @class */ (function (_super) { + __extends(DistanceConstraint, _super); + function DistanceConstraint(first, second, stiffness, distance) { + if (distance === void 0) { distance = -1; } + var _this = _super.call(this) || this; + _this.stiffness = 0; + _this.restingDistance = 0; + _this.tearSensitivity = Number.POSITIVE_INFINITY; + _this.shouldApproximateCollisionsWithPoints = false; + _this.totalPointsToApproximateCollisionsWith = 5; + DistanceConstraint._polygon.create(2, 1); + _this._particleOne = first; + _this._particleTwo = second; + _this.stiffness = stiffness; + if (distance > -1) + _this.restingDistance = distance; + else + _this.restingDistance = first.position.distance(second.position); + return _this; + } + DistanceConstraint.create = function (a, center, c, stiffness, angleInDegrees) { + var aToCenter = a.position.distance(center.position); + var cToCenter = c.position.distance(center.position); + var distance = Math.sqrt(aToCenter * aToCenter + cToCenter * cToCenter - (2 * aToCenter * cToCenter * Math.cos(angleInDegrees * es.MathHelper.Deg2Rad))); + return new DistanceConstraint(a, c, stiffness, distance); + }; + DistanceConstraint.prototype.setTearSensitivity = function (tearSensitivity) { + this.tearSensitivity = tearSensitivity; + return this; + }; + DistanceConstraint.prototype.setCollidesWithColliders = function (collidesWithColliders) { + this.collidesWithColliders = collidesWithColliders; + return this; + }; + DistanceConstraint.prototype.setShouldApproximateCollisionsWithPoints = function (shouldApproximateCollisionsWithPoints) { + this.shouldApproximateCollisionsWithPoints = shouldApproximateCollisionsWithPoints; + return this; + }; + DistanceConstraint.prototype.solve = function () { + var diff = this._particleOne.position.sub(this._particleTwo.position); + var d = diff.distance(); + var difference = (this.restingDistance - d) / d; + if (d / this.restingDistance > this.tearSensitivity) { + this.composite.removeConstraint(this); + return; + } + var im1 = 1 / this._particleOne.mass; + var im2 = 1 / this._particleTwo.mass; + var scalarP1 = (im1 / (im1 + im2)) * this.stiffness; + var scalarP2 = this.stiffness - scalarP1; + this._particleOne.position = this._particleOne.position.add(diff.scale(scalarP1 * difference)); + this._particleTwo.position = this._particleTwo.position.sub(diff.scale(scalarP2 * difference)); + }; + DistanceConstraint.prototype.handleCollisions = function (collidesWithLayers) { + if (this.shouldApproximateCollisionsWithPoints) { + this.approximateCollisionsWithPoints(collidesWithLayers); + return; + } + var minX = Math.min(this._particleOne.position.x, this._particleTwo.position.x); + var maxX = Math.max(this._particleOne.position.x, this._particleTwo.position.x); + var minY = Math.min(this._particleOne.position.y, this._particleTwo.position.y); + var maxY = Math.max(this._particleOne.position.y, this._particleTwo.position.y); + DistanceConstraint._polygon.bounds = es.Rectangle.fromMinMax(minX, minY, maxX, maxY); + var midPoint = es.Vector2.zero; + this.preparePolygonForCollisionChecks(midPoint); + var colliders = es.Physics.boxcastBroadphase(DistanceConstraint._polygon.bounds, collidesWithLayers); + for (var i = 0; i < colliders.length; i++) { + var collider = colliders[i]; + var result = new es.CollisionResult(); + if (DistanceConstraint._polygon.collidesWithShape(collider.shape, result)) { + this._particleOne.position = this._particleOne.position.sub(result.minimumTranslationVector); + this._particleTwo.position = this._particleTwo.position.sub(result.minimumTranslationVector); + } + } + }; + DistanceConstraint.prototype.approximateCollisionsWithPoints = function (collidesWithLayers) { + var pt; + for (var j = 0; j < this.totalPointsToApproximateCollisionsWith - 1; j++) { + pt = es.Vector2.lerp(this._particleOne.position, this._particleTwo.position, (j + 1) / this.totalPointsToApproximateCollisionsWith); + var collidedCount = es.Physics.overlapCircleAll(pt, 3, es.VerletWorld._colliders, collidesWithLayers); + for (var i = 0; i < collidedCount; i++) { + var collider = es.VerletWorld._colliders[i]; + var collisionResult = new es.CollisionResult(); + if (collider.shape.pointCollidesWithShape(pt, collisionResult)) { + this._particleOne.position = this._particleOne.position.sub(collisionResult.minimumTranslationVector); + this._particleTwo.position = this._particleTwo.position.sub(collisionResult.minimumTranslationVector); + } + } + } + }; + DistanceConstraint.prototype.preparePolygonForCollisionChecks = function (midPoint) { + var tempMidPoint = es.Vector2.lerp(this._particleOne.position, this._particleTwo.position, 0.5); + midPoint.setTo(tempMidPoint.x, tempMidPoint.y); + DistanceConstraint._polygon.position = midPoint; + DistanceConstraint._polygon.points[0] = this._particleOne.position.sub(DistanceConstraint._polygon.position); + DistanceConstraint._polygon.points[1] = this._particleTwo.position.sub(DistanceConstraint._polygon.position); + DistanceConstraint._polygon.recalculateCenterAndEdgeNormals(); + }; + DistanceConstraint.prototype.debugRender = function (batcher) { + batcher.drawLine(this._particleOne.position, this._particleTwo.position, new es.Color(67, 62, 54), 1); + }; + DistanceConstraint._polygon = new es.Polygon([]); + return DistanceConstraint; + }(es.Constraint)); + es.DistanceConstraint = DistanceConstraint; +})(es || (es = {})); +var es; +(function (es) { + /** + * AbstractTweenable作为你可能想做的任何可以执行的自定义类的基础。 + * 这些类不同于ITweens,因为他们没有实现ITweenT接口。 + * 它只是说一个AbstractTweenable不仅仅是将一个值从开始移动到结束。 + * 它可以做任何需要每帧执行的事情。 + */ + var AbstractTweenable = /** @class */ (function () { + function AbstractTweenable() { + } + AbstractTweenable.prototype.recycleSelf = function () { + }; + AbstractTweenable.prototype.isRunning = function () { + return this._isCurrentlyManagedByTweenManager && !this._isPaused; + }; + AbstractTweenable.prototype.start = function () { + if (this._isCurrentlyManagedByTweenManager) { + this._isPaused = false; + return; + } + es.TweenManager.addTween(this); + this._isCurrentlyManagedByTweenManager = true; + this._isPaused = false; + }; + AbstractTweenable.prototype.pause = function () { + this._isPaused = true; + }; + AbstractTweenable.prototype.resume = function () { + this._isPaused = false; + }; + AbstractTweenable.prototype.stop = function (bringToCompletion) { + if (bringToCompletion === void 0) { bringToCompletion = false; } + es.TweenManager.removeTween(this); + this._isCurrentlyManagedByTweenManager = false; + this._isPaused = true; + }; + return AbstractTweenable; + }()); + es.AbstractTweenable = AbstractTweenable; +})(es || (es = {})); +var es; +(function (es) { + /** + * 通用ITweenTarget用于所有属性tweens。 + */ + var PropertyTarget = /** @class */ (function () { + function PropertyTarget(target, propertyName) { + this._target = target; + this._propertyName = propertyName; + } + PropertyTarget.prototype.getTargetObject = function () { + return this._target; + }; + PropertyTarget.prototype.setTweenedValue = function (value) { + this._target[this._propertyName] = value; + }; + PropertyTarget.prototype.getTweenedValue = function () { + return this._target[this._propertyName]; + }; + return PropertyTarget; + }()); + var PropertyTweens = /** @class */ (function () { + function PropertyTweens() { + } + PropertyTweens.NumberPropertyTo = function (self, memberName, to, duration) { + var tweenTarget = new PropertyTarget(self, memberName); + var tween = es.TweenManager.cacheNumberTweens ? es.Pool.obtain(es.NumberTween) : new es.NumberTween(); + tween.initialize(tweenTarget, to, duration); + return tween; + }; + PropertyTweens.Vector2PropertyTo = function (self, memeberName, to, duration) { + var tweenTarget = new PropertyTarget(self, memeberName); + var tween = es.TweenManager.cacheVector2Tweens ? es.Pool.obtain(es.Vector2Tween) : new es.Vector2Tween(); + tween.initialize(tweenTarget, to, duration); + return tween; + }; + return PropertyTweens; + }()); + es.PropertyTweens = PropertyTweens; +})(es || (es = {})); +var es; +(function (es) { + var LoopType; + (function (LoopType) { + LoopType[LoopType["none"] = 0] = "none"; + LoopType[LoopType["restartFromBeginning"] = 1] = "restartFromBeginning"; + LoopType[LoopType["pingpong"] = 2] = "pingpong"; + })(LoopType = es.LoopType || (es.LoopType = {})); + var TweenState; + (function (TweenState) { + TweenState[TweenState["running"] = 0] = "running"; + TweenState[TweenState["paused"] = 1] = "paused"; + TweenState[TweenState["complete"] = 2] = "complete"; + })(TweenState = es.TweenState || (es.TweenState = {})); + var Tween = /** @class */ (function () { + function Tween() { + this._shouldRecycleTween = true; + this._tweenState = TweenState.complete; + this._timeScale = 1; + } + Tween.prototype.setEaseType = function (easeType) { + this._easeType = easeType; + return this; + }; + Tween.prototype.setDelay = function (delay) { + this._delay = delay; + this._elapsedTime = -this._delay; + return this; + }; + Tween.prototype.setDuration = function (duration) { + this._duration = duration; + return this; + }; + Tween.prototype.setTimeScale = function (timeSclae) { + this._timeScale = timeSclae; + return this; + }; + Tween.prototype.setIsTimeScaleIndependent = function () { + this._isTimeScaleIndependent = true; + return this; + }; + Tween.prototype.setCompletionHandler = function (completeHandler) { + this._completionHandler = completeHandler; + return this; + }; + Tween.prototype.setLoops = function (loopType, loops, delayBetweenLoops) { + if (loops === void 0) { loops = 1; } + if (delayBetweenLoops === void 0) { delayBetweenLoops = 0; } + this._loopType = loopType; + this._delayBetweenLoops = delayBetweenLoops; + if (loops < 0) + loops = -1; + if (loopType == LoopType.pingpong) + loops = loops * 2; + this._loops = loops; + return this; + }; + Tween.prototype.setLoopCompletionHanlder = function (loopCompleteHandler) { + this._loopCompleteHandler = loopCompleteHandler; + return this; + }; + Tween.prototype.setFrom = function (from) { + this._isFromValueOverridden = true; + this._fromValue = from; + return this; + }; + Tween.prototype.prepareForReuse = function (from, to, duration) { + this.initialize(this._target, to, duration); + return this; + }; + Tween.prototype.setRecycleTween = function (shouldRecycleTween) { + this._shouldRecycleTween = shouldRecycleTween; + return this; + }; + Tween.prototype.setContext = function (context) { + this.context = context; + return this; + }; + Tween.prototype.setNextTween = function (nextTween) { + this._nextTween = nextTween; + return this; + }; + Tween.prototype.tick = function () { + if (this._tweenState == TweenState.paused) + return false; + // 当我们进行循环时,我们会在0和持续时间之间限制数值 + var elapsedTimeExcess = 0; + if (!this._isRunningInReverse && this._elapsedTime >= this._duration) { + elapsedTimeExcess = this._elapsedTime - this._duration; + this._elapsedTime = this._duration; + this._tweenState = TweenState.complete; + } + else if (this._isRunningInReverse && this._elapsedTime <= 0) { + elapsedTimeExcess = 0 - this._elapsedTime; + this._elapsedTime = 0; + this._tweenState = TweenState.complete; + } + // 当我们延迟开始tween的时候,经过的时间会是负数,所以不要更新这个值。 + if (this._elapsedTime >= 0 && this._elapsedTime <= this._duration) { + this.updateValue(); + } + // 如果我们有一个loopType,并且我们是Complete(意味着我们达到了0或持续时间)处理循环。 + // handleLooping将采取任何多余的elapsedTime,并将其因子化,并在必要时调用udpateValue来保持tween的完美准确性 + if (this._loopType != LoopType.none && this._tweenState == TweenState.complete && this._loops != 0) { + this.handleLooping(elapsedTimeExcess); + } + var deltaTime = this._isTimeScaleIndependent ? es.Time.unscaledDeltaTime : es.Time.deltaTime; + deltaTime *= this._timeScale; + // 我们需要减去deltaTime + if (this._isRunningInReverse) + this._elapsedTime -= deltaTime; + else + this._elapsedTime += deltaTime; + if (this._tweenState == TweenState.complete) { + this._completionHandler && this._completionHandler(this); + // 如果我们有一个nextTween,把它添加到TweenManager中,这样它就可以开始运行了 + if (this._nextTween != null) { + this._nextTween.start(); + this._nextTween = null; + } + return true; + } + return false; + }; + Tween.prototype.recycleSelf = function () { + if (this._shouldRecycleTween) { + this._target = null; + this._nextTween = null; + } + }; + Tween.prototype.isRunning = function () { + return this._tweenState == TweenState.running; + }; + Tween.prototype.start = function () { + if (!this._isFromValueOverridden) + this._fromValue = this._target.getTargetObject(); + if (this._tweenState == TweenState.complete) { + this._tweenState = TweenState.running; + es.TweenManager.addTween(this); + } + }; + Tween.prototype.pause = function () { + this._tweenState = TweenState.paused; + }; + Tween.prototype.resume = function () { + this._tweenState = TweenState.running; + }; + Tween.prototype.stop = function (bringToCompletion) { + if (bringToCompletion === void 0) { bringToCompletion = false; } + this._tweenState = TweenState.complete; + if (bringToCompletion) { + // 如果我们逆向运行,我们在0处结束,否则我们进入持续时间 + this._elapsedTime = this._isRunningInReverse ? 0 : this._duration; + this._loopType = LoopType.none; + this._loops = 0; + // TweenManager将在下一个tick上进行删除处理 + } + else { + es.TweenManager.removeTween(this); + } + }; + Tween.prototype.jumpToElapsedTime = function (elapsedTime) { + this._elapsedTime = es.MathHelper.clamp(elapsedTime, 0, this._duration); + this.updateValue(); + }; + /** + * 反转当前的tween,如果是向前走,就会向后走,反之亦然 + */ + Tween.prototype.reverseTween = function () { + this._isRunningInReverse = !this._isRunningInReverse; + }; + /** + * 当通过StartCoroutine调用时,这将一直持续到tween完成 + */ + Tween.prototype.waitForCompletion = function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(this._tweenState != TweenState.complete)) return [3 /*break*/, 2]; + return [4 /*yield*/, null]; + case 1: + _a.sent(); + return [3 /*break*/, 0]; + case 2: return [2 /*return*/]; + } + }); + }; + Tween.prototype.getTargetObject = function () { + return this._target.getTargetObject(); + }; + Tween.prototype.resetState = function () { + this.context = null; + this._completionHandler = this._loopCompleteHandler = null; + this._isFromValueOverridden = false; + this._isTimeScaleIndependent = false; + this._tweenState = TweenState.complete; + // TODO: 我认为在没有得到用户同意的情况下,我们绝对不应该从_shouldRecycleTween=false。需要研究和思考 + // this._shouldRecycleTween = true; + this._isRelative = false; + this._easeType = es.TweenManager.defaultEaseType; + if (this._nextTween != null) { + this._nextTween.recycleSelf(); + this._nextTween = null; + } + this._delay = 0; + this._duration = 0; + this._timeScale = 1; + this._elapsedTime = 0; + this._loopType = LoopType.none; + this._delayBetweenLoops = 0; + this._loops = 0; + this._isRunningInReverse = false; + }; + /** + * 将所有状态重置为默认值,并根据传入的参数设置初始状态。 + * 这个方法作为一个切入点,这样Tween子类就可以调用它,这样tweens就可以被回收。 + * 当回收时,构造函数不会再被调用,所以这个方法封装了构造函数要做的事情 + * @param target + * @param to + * @param duration + */ + Tween.prototype.initialize = function (target, to, duration) { + // 重置状态,以防我们被回收 + this.resetState(); + this._target = target; + this._toValue = to; + this._duration = duration; + }; + /** + * 处理循环逻辑 + * @param elapsedTimeExcess + */ + Tween.prototype.handleLooping = function (elapsedTimeExcess) { + this._loops--; + if (this._loopType == LoopType.pingpong) { + this.reverseTween(); + } + if (this._loopType == LoopType.restartFromBeginning || this._loops % 2 == 0) { + this._loopCompleteHandler && this._completionHandler(this); + } + // 如果我们还有循环要处理,就把我们的状态重置为Running,这样我们就可以继续处理它们了 + if (this._loops != 0) { + this._tweenState = TweenState.running; + // 现在,我们需要设置我们的经过时间,并考虑到我们的elapsedTimeExcess + if (this._loopType == LoopType.restartFromBeginning) { + this._elapsedTime = elapsedTimeExcess - this._delayBetweenLoops; + } + else { + if (this._isRunningInReverse) + this._elapsedTime += this._delayBetweenLoops - elapsedTimeExcess; + else + this._elapsedTime = elapsedTimeExcess - this._delayBetweenLoops; + } + // 如果我们有一个elapsedTimeExcess,并且没有delayBetweenLoops,则更新该值 + if (this._delayBetweenLoops == 0 && elapsedTimeExcess > 0) { + this.updateValue(); + } + } + }; + return Tween; + }()); + es.Tween = Tween; +})(es || (es = {})); +/// +var es; +/// +(function (es) { + var NumberTween = /** @class */ (function (_super) { + __extends(NumberTween, _super); + function NumberTween(target, to, duration) { + var _this = _super.call(this) || this; + _this.initialize(target, to, duration); + return _this; + } + NumberTween.create = function () { + return es.TweenManager.cacheNumberTweens ? es.Pool.obtain(NumberTween) : new NumberTween(); + }; + NumberTween.prototype.setIsRelative = function () { + this._isRelative = true; + this._toValue += this._fromValue; + return this; + }; + NumberTween.prototype.updateValue = function () { + this._target.setTweenedValue(es.Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + }; + NumberTween.prototype.recycleSelf = function () { + _super.prototype.recycleSelf.call(this); + if (this._shouldRecycleTween && es.TweenManager.cacheNumberTweens) + es.Pool.free(this); + }; + return NumberTween; + }(es.Tween)); + es.NumberTween = NumberTween; + var Vector2Tween = /** @class */ (function (_super) { + __extends(Vector2Tween, _super); + function Vector2Tween(target, to, duration) { + var _this = _super.call(this) || this; + _this.initialize(target, to, duration); + return _this; + } + Vector2Tween.create = function () { + return es.TweenManager.cacheVector2Tweens ? es.Pool.obtain(Vector2Tween) : new Vector2Tween(); + }; + Vector2Tween.prototype.setIsRelative = function () { + this._isRelative = true; + this._toValue.add(this._fromValue); + return this; + }; + Vector2Tween.prototype.updateValue = function () { + this._target.setTweenedValue(es.Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + }; + Vector2Tween.prototype.recycleSelf = function () { + _super.prototype.recycleSelf.call(this); + if (this._shouldRecycleTween && es.TweenManager.cacheVector2Tweens) + es.Pool.free(this); + }; + return Vector2Tween; + }(es.Tween)); + es.Vector2Tween = Vector2Tween; + var RectangleTween = /** @class */ (function (_super) { + __extends(RectangleTween, _super); + function RectangleTween(target, to, duration) { + var _this = _super.call(this) || this; + _this.initialize(target, to, duration); + return _this; + } + RectangleTween.create = function () { + return es.TweenManager.cacheRectTweens ? es.Pool.obtain(RectangleTween) : new RectangleTween(); + }; + RectangleTween.prototype.setIsRelative = function () { + this._isRelative = true; + this._toValue = new es.Rectangle(this._toValue.x + this._fromValue.x, this._toValue.y + this._fromValue.y, this._toValue.width + this._fromValue.width, this._toValue.height + this._fromValue.height); + return this; + }; + RectangleTween.prototype.updateValue = function () { + this._target.setTweenedValue(es.Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + }; + RectangleTween.prototype.recycleSelf = function () { + _super.prototype.recycleSelf.call(this); + if (this._shouldRecycleTween && es.TweenManager.cacheRectTweens) + es.Pool.free(this); + }; + return RectangleTween; + }(es.Tween)); + es.RectangleTween = RectangleTween; + var ColorTween = /** @class */ (function (_super) { + __extends(ColorTween, _super); + function ColorTween(target, to, duration) { + var _this = _super.call(this) || this; + _this.initialize(target, to, duration); + return _this; + } + ColorTween.create = function () { + return es.TweenManager.cacheColorTweens ? es.Pool.obtain(ColorTween) : new ColorTween(); + }; + ColorTween.prototype.setIsRelative = function () { + this._isRelative = true; + this._toValue.r += this._fromValue.r; + this._toValue.g += this._fromValue.g; + this._toValue.b += this._fromValue.b; + this._toValue.a += this._fromValue.a; + return this; + }; + ColorTween.prototype.updateValue = function () { + this._target.setTweenedValue(es.Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + }; + return ColorTween; + }(es.Tween)); + es.ColorTween = ColorTween; +})(es || (es = {})); +/// +var es; +/// +(function (es) { + var RenderableColorTween = /** @class */ (function (_super) { + __extends(RenderableColorTween, _super); + function RenderableColorTween() { + return _super !== null && _super.apply(this, arguments) || this; + } + RenderableColorTween.prototype.setTweenedValue = function (value) { + this._renderable.color = value; + }; + RenderableColorTween.prototype.getTweenedValue = function () { + return this._renderable.color; + }; + RenderableColorTween.prototype.getTargetObject = function () { + return this._renderable; + }; + RenderableColorTween.prototype.updateValue = function () { + this.setTweenedValue(es.Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + }; + RenderableColorTween.prototype.setTarget = function (renderable) { + this._renderable = renderable; + }; + RenderableColorTween.prototype.recycleSelf = function () { + if (this._shouldRecycleTween) { + this._renderable = null; + this._target = null; + this._nextTween = null; + } + if (this._shouldRecycleTween && es.TweenManager.cacheColorTweens) { + es.Pool.free(this); + } + }; + return RenderableColorTween; + }(es.ColorTween)); + es.RenderableColorTween = RenderableColorTween; +})(es || (es = {})); +var es; +(function (es) { + var TransformSpringTween = /** @class */ (function (_super) { + __extends(TransformSpringTween, _super); + function TransformSpringTween(transform, targetType, targetValue) { + var _this = _super.call(this) || this; + // 阻尼比(dampingRatio)和角频率(angularFrequency)的配置是公开的,以便于在设计时进行调整 + /** + * 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + */ + _this.dampingRatio = 0.23; + /** + * 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定角频率 + */ + _this.angularFrequency = 25; + _this._transform = transform; + _this._targetType = targetType; + _this.setTargetValue(targetValue); + return _this; + } + Object.defineProperty(TransformSpringTween.prototype, "targetType", { + get: function () { + return this._targetType; + }, + enumerable: true, + configurable: true + }); + /** + * 你可以在任何时候调用setTargetValue来重置目标值到一个新的Vector2。 + * 如果你没有调用start来添加spring tween,它会为你调用 + * @param targetValue + */ + TransformSpringTween.prototype.setTargetValue = function (targetValue) { + this._velocity = es.Vector2.zero; + this._targetValue = targetValue; + if (!this._isCurrentlyManagedByTweenManager) + this.start(); + }; + /** + * lambda应该是振荡幅度减少50%时的理想持续时间 + * @param lambda + */ + TransformSpringTween.prototype.updateDampingRatioWithHalfLife = function (lambda) { + this.dampingRatio = (-lambda / this.angularFrequency) * Math.log(0.5); + }; + TransformSpringTween.prototype.tick = function () { + if (!this._isPaused) + this.setTweenedValue(es.Lerps.fastSpring(this.getCurrentValueOfTweenedTargetType(), this._targetValue, this._velocity, this.dampingRatio, this.angularFrequency)); + return false; + }; + TransformSpringTween.prototype.setTweenedValue = function (value) { + switch (this._targetType) { + case es.TransformTargetType.position: + this._transform.position = value; + break; + case es.TransformTargetType.localPosition: + this._transform.localPosition = value; + break; + case es.TransformTargetType.scale: + this._transform.scale = value; + break; + case es.TransformTargetType.localScale: + this._transform.localScale = value; + break; + case es.TransformTargetType.rotationDegrees: + this._transform.rotationDegrees = value.x; + case es.TransformTargetType.localRotationDegrees: + this._transform.localRotationDegrees = value.x; + break; + } + }; + TransformSpringTween.prototype.getCurrentValueOfTweenedTargetType = function () { + switch (this._targetType) { + case es.TransformTargetType.position: + return this._transform.position; + case es.TransformTargetType.localPosition: + return this._transform.localPosition; + case es.TransformTargetType.scale: + return this._transform.scale; + case es.TransformTargetType.localScale: + return this._transform.localScale; + case es.TransformTargetType.rotationDegrees: + return new es.Vector2(this._transform.rotationDegrees); + case es.TransformTargetType.localRotationDegrees: + return new es.Vector2(this._transform.localRotationDegrees, 0); + default: + return es.Vector2.zero; + } + }; + return TransformSpringTween; + }(es.AbstractTweenable)); + es.TransformSpringTween = TransformSpringTween; +})(es || (es = {})); +/// +var es; +/// +(function (es) { + /** + * 对任何与Transform相关的属性tweens都是有用的枚举 + */ + var TransformTargetType; + (function (TransformTargetType) { + TransformTargetType[TransformTargetType["position"] = 0] = "position"; + TransformTargetType[TransformTargetType["localPosition"] = 1] = "localPosition"; + TransformTargetType[TransformTargetType["scale"] = 2] = "scale"; + TransformTargetType[TransformTargetType["localScale"] = 3] = "localScale"; + TransformTargetType[TransformTargetType["rotationDegrees"] = 4] = "rotationDegrees"; + TransformTargetType[TransformTargetType["localRotationDegrees"] = 5] = "localRotationDegrees"; + })(TransformTargetType = es.TransformTargetType || (es.TransformTargetType = {})); + /** + * 这是一个特殊的情况,因为Transform是迄今为止最被ween的对象。 + * 我们将Tween和ITweenTarget封装在一个单一的、可缓存的类中 + */ + var TransformVector2Tween = /** @class */ (function (_super) { + __extends(TransformVector2Tween, _super); + function TransformVector2Tween() { + return _super !== null && _super.apply(this, arguments) || this; + } + TransformVector2Tween.prototype.setTweenedValue = function (value) { + switch (this._targetType) { + case TransformTargetType.position: + this._transform.position = value; + break; + case TransformTargetType.localPosition: + this._transform.localPosition = value; + break; + case TransformTargetType.scale: + this._transform.scale = value; + break; + case TransformTargetType.localScale: + this._transform.localScale = value; + break; + case TransformTargetType.rotationDegrees: + this._transform.rotationDegrees = value.x; + case TransformTargetType.localRotationDegrees: + this._transform.localRotationDegrees = value.x; + break; + } + }; + TransformVector2Tween.prototype.getTweenedValue = function () { + switch (this._targetType) { + case TransformTargetType.position: + return this._transform.position; + case TransformTargetType.localPosition: + return this._transform.localPosition; + case TransformTargetType.scale: + return this._transform.scale; + case TransformTargetType.localScale: + return this._transform.localScale; + case TransformTargetType.rotationDegrees: + return new es.Vector2(this._transform.rotationDegrees, this._transform.rotationDegrees); + case TransformTargetType.localRotationDegrees: + return new es.Vector2(this._transform.localRotationDegrees, 0); + } + }; + TransformVector2Tween.prototype.getTargetObject = function () { + return this._transform; + }; + TransformVector2Tween.prototype.setTargetAndType = function (transform, targetType) { + this._transform = transform; + this._targetType = targetType; + }; + TransformVector2Tween.prototype.updateValue = function () { + // 非相对角勒普的特殊情况,使他们采取尽可能短的旋转 + if ((this._targetType == TransformTargetType.rotationDegrees || + this._targetType == TransformTargetType.localRotationDegrees) && !this._isRelative) { + this.setTweenedValue(es.Lerps.easeAngle(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } + else { + this.setTweenedValue(es.Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } + }; + TransformVector2Tween.prototype.recycleSelf = function () { + if (this._shouldRecycleTween) { + this._target = null; + this._nextTween = null; + this._transform = null; + es.Pool.free(this); + } + }; + return TransformVector2Tween; + }(es.Vector2Tween)); + es.TransformVector2Tween = TransformVector2Tween; +})(es || (es = {})); +var es; +(function (es) { + var EaseType; + (function (EaseType) { + EaseType[EaseType["linear"] = 0] = "linear"; + EaseType[EaseType["sineIn"] = 1] = "sineIn"; + EaseType[EaseType["sineOut"] = 2] = "sineOut"; + EaseType[EaseType["sineInOut"] = 3] = "sineInOut"; + EaseType[EaseType["quadIn"] = 4] = "quadIn"; + EaseType[EaseType["quadOut"] = 5] = "quadOut"; + EaseType[EaseType["quadInOut"] = 6] = "quadInOut"; + EaseType[EaseType["quintIn"] = 7] = "quintIn"; + EaseType[EaseType["quintOut"] = 8] = "quintOut"; + EaseType[EaseType["quintInOut"] = 9] = "quintInOut"; + EaseType[EaseType["cubicIn"] = 10] = "cubicIn"; + EaseType[EaseType["cubicOut"] = 11] = "cubicOut"; + EaseType[EaseType["cubicInOut"] = 12] = "cubicInOut"; + EaseType[EaseType["quartIn"] = 13] = "quartIn"; + EaseType[EaseType["quartOut"] = 14] = "quartOut"; + EaseType[EaseType["quartInOut"] = 15] = "quartInOut"; + EaseType[EaseType["expoIn"] = 16] = "expoIn"; + EaseType[EaseType["expoOut"] = 17] = "expoOut"; + EaseType[EaseType["expoInOut"] = 18] = "expoInOut"; + EaseType[EaseType["circleIn"] = 19] = "circleIn"; + EaseType[EaseType["circleOut"] = 20] = "circleOut"; + EaseType[EaseType["circleInOut"] = 21] = "circleInOut"; + EaseType[EaseType["elasticIn"] = 22] = "elasticIn"; + EaseType[EaseType["elasticOut"] = 23] = "elasticOut"; + EaseType[EaseType["elasticInOut"] = 24] = "elasticInOut"; + EaseType[EaseType["punch"] = 25] = "punch"; + EaseType[EaseType["backIn"] = 26] = "backIn"; + EaseType[EaseType["backOut"] = 27] = "backOut"; + EaseType[EaseType["backInOut"] = 28] = "backInOut"; + EaseType[EaseType["bounceIn"] = 29] = "bounceIn"; + EaseType[EaseType["bounceOut"] = 30] = "bounceOut"; + EaseType[EaseType["bounceInOut"] = 31] = "bounceInOut"; + })(EaseType = es.EaseType || (es.EaseType = {})); + /** + * 助手的一个方法,它接收一个EaseType,并通过给定的持续时间和时间参数来应用该Ease方程。 + * 我们这样做是为了避免传来传去的Funcs为垃圾收集器制造大量垃圾 + */ + var EaseHelper = /** @class */ (function () { + function EaseHelper() { + } + /** + * 返回 easeType 的相反 EaseType + * @param easeType + */ + EaseHelper.oppositeEaseType = function (easeType) { + switch (easeType) { + case EaseType.linear: + return easeType; + case EaseType.backIn: + return EaseType.backOut; + case EaseType.backOut: + return EaseType.backIn; + case EaseType.backInOut: + return easeType; + case EaseType.bounceIn: + return EaseType.bounceOut; + case EaseType.bounceOut: + return EaseType.bounceIn; + case EaseType.bounceInOut: + return easeType; + case EaseType.circleIn: + return EaseType.circleOut; + case EaseType.circleOut: + return EaseType.circleIn; + case EaseType.circleInOut: + return easeType; + case EaseType.cubicIn: + return EaseType.cubicOut; + case EaseType.cubicOut: + return EaseType.cubicIn; + case EaseType.circleInOut: + return easeType; + case EaseType.punch: + return easeType; + case EaseType.expoIn: + return EaseType.expoOut; + case EaseType.expoOut: + return EaseType.expoIn; + case EaseType.expoInOut: + return easeType; + case EaseType.quadIn: + return EaseType.quadOut; + case EaseType.quadOut: + return EaseType.quadIn; + case EaseType.quadInOut: + return easeType; + case EaseType.quartIn: + return EaseType.quadOut; + case EaseType.quartOut: + return EaseType.quartIn; + case EaseType.quadInOut: + return easeType; + case EaseType.sineIn: + return EaseType.sineOut; + case EaseType.sineOut: + return EaseType.sineIn; + case EaseType.sineInOut: + return easeType; + default: + return easeType; + } + }; + EaseHelper.ease = function (easeType, t, duration) { + switch (easeType) { + case EaseType.linear: + return es.Easing.Linear.easeNone(t, duration); + case EaseType.backIn: + return es.Easing.Back.easeIn(t, duration); + case EaseType.backOut: + return es.Easing.Back.easeOut(t, duration); + case EaseType.backInOut: + return es.Easing.Back.easeInOut(t, duration); + case EaseType.bounceIn: + return es.Easing.Bounce.easeIn(t, duration); + case EaseType.bounceOut: + return es.Easing.Bounce.easeOut(t, duration); + case EaseType.bounceInOut: + return es.Easing.Bounce.easeInOut(t, duration); + case EaseType.circleIn: + return es.Easing.Circular.easeIn(t, duration); + case EaseType.circleOut: + return es.Easing.Circular.easeOut(t, duration); + case EaseType.circleInOut: + return es.Easing.Circular.easeInOut(t, duration); + case EaseType.cubicIn: + return es.Easing.Cubic.easeIn(t, duration); + case EaseType.cubicOut: + return es.Easing.Cubic.easeOut(t, duration); + case EaseType.cubicInOut: + return es.Easing.Cubic.easeInOut(t, duration); + case EaseType.elasticIn: + return es.Easing.Elastic.easeIn(t, duration); + case EaseType.elasticOut: + return es.Easing.Elastic.easeOut(t, duration); + case EaseType.elasticInOut: + return es.Easing.Elastic.easeInOut(t, duration); + case EaseType.punch: + return es.Easing.Elastic.punch(t, duration); + case EaseType.expoIn: + return es.Easing.Exponential.easeIn(t, duration); + case EaseType.expoOut: + return es.Easing.Exponential.easeOut(t, duration); + case EaseType.expoInOut: + return es.Easing.Exponential.easeInOut(t, duration); + case EaseType.quadIn: + return es.Easing.Quadratic.easeIn(t, duration); + case EaseType.quadOut: + return es.Easing.Quadratic.easeOut(t, duration); + case EaseType.quadInOut: + return es.Easing.Quadratic.easeInOut(t, duration); + case EaseType.quadIn: + return es.Easing.Quadratic.easeIn(t, duration); + case EaseType.quadOut: + return es.Easing.Quadratic.easeOut(t, duration); + case EaseType.quadInOut: + return es.Easing.Quadratic.easeInOut(t, duration); + case EaseType.quintIn: + return es.Easing.Quintic.easeIn(t, duration); + case EaseType.quintOut: + return es.Easing.Quintic.easeOut(t, duration); + case EaseType.quintInOut: + return es.Easing.Quintic.easeInOut(t, duration); + case EaseType.sineIn: + return es.Easing.Sinusoidal.easeIn(t, duration); + case EaseType.sineOut: + return es.Easing.Sinusoidal.easeOut(t, duration); + case EaseType.sineInOut: + return es.Easing.Sinusoidal.easeInOut(t, duration); + default: + return es.Easing.Linear.easeNone(t, duration); + } + }; + return EaseHelper; + }()); + es.EaseHelper = EaseHelper; +})(es || (es = {})); +var es; +(function (es) { + var GlobalManager = /** @class */ (function () { + function GlobalManager() { + } + Object.defineProperty(GlobalManager.prototype, "enabled", { + /** + * 如果true则启用了GlobalManager。 + * 状态的改变会导致调用OnEnabled/OnDisable + */ + get: function () { + return this._enabled; + }, + /** + * 如果true则启用了GlobalManager。 + * 状态的改变会导致调用OnEnabled/OnDisable + * @param value + */ + set: function (value) { + this.setEnabled(value); + }, + enumerable: true, + configurable: true + }); + /** + * 启用/禁用这个GlobalManager + * @param isEnabled + */ + GlobalManager.prototype.setEnabled = function (isEnabled) { + if (this._enabled != isEnabled) { + this._enabled = isEnabled; + if (this._enabled) { + this.onEnabled(); + } + else { + this.onDisabled(); + } + } + }; + /** + * 此GlobalManager启用时调用 + */ + GlobalManager.prototype.onEnabled = function () { + }; + /** + * 此GlobalManager禁用时调用 + */ + GlobalManager.prototype.onDisabled = function () { + }; + /** + * 在frame .update之前调用每一帧 + */ + GlobalManager.prototype.update = function () { + }; + return GlobalManager; + }()); + es.GlobalManager = GlobalManager; +})(es || (es = {})); +/// +/// +var es; +/// +/// +(function (es) { + var TweenManager = /** @class */ (function (_super) { + __extends(TweenManager, _super); + function TweenManager() { + var _this = _super.call(this) || this; + /** + * 当前所有活跃用户的内部列表 + */ + _this._activeTweens = []; + _this._tempTweens = []; + TweenManager._instance = _this; + return _this; + } + TweenManager.prototype.update = function () { + this._isUpdating = true; + // 反向循环,这样我们就可以把完成的weens删除了 + for (var i = this._activeTweens.length - 1; i >= 0; --i) { + var tween = this._activeTweens[i]; + if (tween.tick()) + this._tempTweens.push(tween); + } + this._isUpdating = false; + for (var i = 0; i < this._tempTweens.length; i++) { + this._tempTweens[i].recycleSelf(); + new es.List(this._activeTweens).remove(this._tempTweens[i]); + } + this._tempTweens.length = 0; + }; + /** + * 将一个tween添加到活动tweens列表中 + * @param tween + */ + TweenManager.addTween = function (tween) { + TweenManager._instance._activeTweens.push(tween); + }; + /** + * 从当前的tweens列表中删除一个tween + * @param tween + */ + TweenManager.removeTween = function (tween) { + if (TweenManager._instance._isUpdating) { + TweenManager._instance._tempTweens.push(tween); + } + else { + tween.recycleSelf(); + new es.List(TweenManager._instance._activeTweens).remove(tween); + } + }; + /** + * 停止所有的tween并选择地把他们全部完成 + * @param bringToCompletion + */ + TweenManager.stopAllTweens = function (bringToCompletion) { + if (bringToCompletion === void 0) { bringToCompletion = false; } + for (var i = TweenManager._instance._activeTweens.length - 1; i >= 0; --i) + TweenManager._instance._activeTweens[i].stop(bringToCompletion); + }; + /** + * 返回具有特定上下文的所有tweens。 + * Tweens以ITweenable的形式返回,因为这就是TweenManager所知道的所有内容 + * @param context + */ + TweenManager.allTweensWithContext = function (context) { + var foundTweens = []; + for (var i = 0; i < TweenManager._instance._activeTweens.length; i++) { + if (TweenManager._instance._activeTweens[i].context == context) + foundTweens.push(TweenManager._instance._activeTweens[i]); + } + return foundTweens; + }; + /** + * 停止所有给定上下文的tweens + * @param context + * @param bringToCompletion + */ + TweenManager.stopAllTweensWithContext = function (context, bringToCompletion) { + if (bringToCompletion === void 0) { bringToCompletion = false; } + for (var i = TweenManager._instance._activeTweens.length - 1; i >= 0; --i) { + if (TweenManager._instance._activeTweens[i].context == context) + TweenManager._instance._activeTweens[i].stop(bringToCompletion); + } + }; + /** + * 返回具有特定目标的所有tweens。 + * Tweens以ITweenControl的形式返回,因为TweenManager只知道这些 + * @param target + */ + TweenManager.allTweenWithTarget = function (target) { + var foundTweens = []; + for (var i = 0; i < TweenManager._instance._activeTweens.length; i++) { + if (TweenManager._instance._activeTweens[i]) { + var tweenControl = TweenManager._instance._activeTweens[i]; + if (tweenControl.getTargetObject() == target) + foundTweens.push(TweenManager._instance._activeTweens[i]); + } + } + return foundTweens; + }; + /** + * 停止所有具有TweenManager知道的特定目标的tweens + * @param target + * @param bringToCompletion + */ + TweenManager.stopAllTweensWithTarget = function (target, bringToCompletion) { + if (bringToCompletion === void 0) { bringToCompletion = false; } + for (var i = TweenManager._instance._activeTweens.length - 1; i >= 0; --i) { + if (TweenManager._instance._activeTweens[i]) { + var tweenControl = TweenManager._instance._activeTweens[i]; + if (tweenControl.getTargetObject() == target) + tweenControl.stop(bringToCompletion); + } + } + }; + TweenManager.defaultEaseType = es.EaseType.quartIn; + /** + * 如果为真,当加载新关卡时,活动的tween列表将被清除 + */ + TweenManager.removeAllTweensOnLevelLoad = false; + /** + * 这里支持各种类型的自动缓存。请 + * 注意,只有在使用扩展方法启动tweens时,或者在做自定义tweens时从缓存中获取tween时,缓存才会起作用。 + * 关于如何获取缓存的tween,请参见扩展方法的实现 + */ + TweenManager.cacheNumberTweens = true; + TweenManager.cacheVector2Tweens = true; + TweenManager.cacheColorTweens = true; + TweenManager.cacheRectTweens = false; + return TweenManager; + }(es.GlobalManager)); + es.TweenManager = TweenManager; +})(es || (es = {})); +var es; +(function (es) { + /** + * 标准缓和方程通过将b和c参数(起始值和变化值)用0和1替换,然后进行简化。 + * 这样做的目的是为了让我们可以得到一个0 - 1之间的原始值(除了弹性/反弹故意超过界限),然后用这个值来lerp任何东西 + */ + var Easing; + (function (Easing) { + var Linear = /** @class */ (function () { + function Linear() { + } + Linear.easeNone = function (t, d) { + return t / d; + }; + return Linear; + }()); + Easing.Linear = Linear; + var Quadratic = /** @class */ (function () { + function Quadratic() { + } + Quadratic.easeIn = function (t, d) { + return (t /= d) * t; + }; + Quadratic.easeOut = function (t, d) { + return -1 * (t /= d) * (t - 2); + }; + Quadratic.easeInOut = function (t, d) { + if ((t /= d / 2) < 1) + return 0.5 * t * t; + return -0.5 * ((--t) * (t - 2) - 1); + }; + return Quadratic; + }()); + Easing.Quadratic = Quadratic; + var Back = /** @class */ (function () { + function Back() { + } + Back.easeIn = function (t, d) { + return (t /= d) * t * ((1.70158 + 1) * t - 1.70158); + }; + Back.easeOut = function (t, d) { + return ((t = t / d - 1) * t * ((1.70158 + 1) * t + 1.70158) + 1); + }; + Back.easeInOut = function (t, d) { + var s = 1.70158; + if ((t /= d / 2) < 1) { + return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); + }; + return Back; + }()); + Easing.Back = Back; + var Bounce = /** @class */ (function () { + function Bounce() { + } + Bounce.easeOut = function (t, d) { + if ((t /= d) < (1 / 2.75)) { + return (7.5625 * t * t); + } + else if (t < (2 / 2.75)) { + return (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); + } + else if (t < (2.5 / 2.75)) { + return (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); + } + else { + return (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); + } + }; + Bounce.easeIn = function (t, d) { + return 1 - this.easeOut(d - t, d); + }; + Bounce.easeInOut = function (t, d) { + if (t < d / 2) + return this.easeIn(t * 2, d) * 0.5; + else + return this.easeOut(t * 2 - d, d) * 0.5 + 1 * 0.5; + }; + return Bounce; + }()); + Easing.Bounce = Bounce; + var Circular = /** @class */ (function () { + function Circular() { + } + Circular.easeIn = function (t, d) { + return -(Math.sqrt(1 - (t /= d) * t) - 1); + }; + Circular.easeOut = function (t, d) { + return Math.sqrt(1 - (t = t / d - 1) * t); + }; + Circular.easeInOut = function (t, d) { + if ((t /= d / 2) < 1) + return -0.5 * (Math.sqrt(1 - t * t) - 1); + return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); + }; + return Circular; + }()); + Easing.Circular = Circular; + var Cubic = /** @class */ (function () { + function Cubic() { + } + Cubic.easeIn = function (t, d) { + return (t /= d) * t * t; + }; + Cubic.easeOut = function (t, d) { + return ((t = t / d - 1) * t * t + 1); + }; + Cubic.easeInOut = function (t, d) { + if ((t /= d / 2) < 1) + return 0.5 * t * t * t; + return 0.5 * ((t -= 2) * t * t + 2); + }; + return Cubic; + }()); + Easing.Cubic = Cubic; + var Elastic = /** @class */ (function () { + function Elastic() { + } + Elastic.easeIn = function (t, d) { + if (t == 0) + return 0; + if ((t /= d) == 1) + return 1; + var p = d * 0.3; + var s = p / 4; + return -(1 * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)); + }; + Elastic.easeOut = function (t, d) { + if (t == 0) + return 0; + if ((t /= d) == 1) + return 1; + var p = d * 0.3; + var s = p / 4; + return (1 * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + 1); + }; + Elastic.easeInOut = function (t, d) { + if (t == 0) + return 0; + if ((t /= d / 2) == 2) + return 1; + var p = d * (0.3 * 1.5); + var s = p / 4; + if (t < 1) + return -0.5 * (Math.pow(2, 10 * (t -= 1)) * Math.sin(t * d - s) * (2 * Math.PI) / p); + return (Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + 1); + }; + Elastic.punch = function (t, d) { + if (t == 0) + return 0; + if ((t /= d) == 1) + return 0; + var p = 0.3; + return (Math.pow(2, -10 * t) * Math.sin(t * (2 * Math.PI) / p)); + }; + return Elastic; + }()); + Easing.Elastic = Elastic; + var Exponential = /** @class */ (function () { + function Exponential() { + } + Exponential.easeIn = function (t, d) { + return (t == 0) ? 0 : Math.pow(2, 10 * (t / d - 1)); + }; + Exponential.easeOut = function (t, d) { + return t == d ? 1 : (-Math.pow(2, -10 * t / d) + 1); + }; + Exponential.easeInOut = function (t, d) { + if (t == 0) + return 0; + if (t == d) + return 1; + if ((t /= d / 2) < 1) { + return 0.5 * Math.pow(2, 10 * (t - 1)); + } + return 0.5 * (-Math.pow(2, -10 * --t) + 2); + }; + return Exponential; + }()); + Easing.Exponential = Exponential; + var Quartic = /** @class */ (function () { + function Quartic() { + } + Quartic.easeIn = function (t, d) { + return (t /= d) * t * t * t; + }; + Quartic.easeOut = function (t, d) { + return -1 * ((t = t / d - 1) * t * t * t - 1); + }; + Quartic.easeInOut = function (t, d) { + t /= d / 2; + if (t < 1) + return 0.5 * t * t * t * t; + t -= 2; + return -0.5 * (t * t * t * t - 2); + }; + return Quartic; + }()); + Easing.Quartic = Quartic; + var Quintic = /** @class */ (function () { + function Quintic() { + } + Quintic.easeIn = function (t, d) { + return (t /= d) * t * t * t * t; + }; + Quintic.easeOut = function (t, d) { + return ((t = t / d - 1) * t * t * t * t + 1); + }; + Quintic.easeInOut = function (t, d) { + if ((t /= d / 2) < 1) + return 0.5 * t * t * t * t * t; + return 0.5 * ((t -= 2) * t * t * t * t + 2); + }; + return Quintic; + }()); + Easing.Quintic = Quintic; + var Sinusoidal = /** @class */ (function () { + function Sinusoidal() { + } + Sinusoidal.easeIn = function (t, d) { + return -1 * Math.cos(t / d * (Math.PI / 2)) + 1; + }; + Sinusoidal.easeOut = function (t, d) { + return Math.sin(t / d * (Math.PI / 2)); + }; + Sinusoidal.easeInOut = function (t, d) { + return -0.5 * (Math.cos(Math.PI * t / d) - 1); + }; + return Sinusoidal; + }()); + Easing.Sinusoidal = Sinusoidal; + })(Easing = es.Easing || (es.Easing = {})); +})(es || (es = {})); +var es; +(function (es) { + /** + * 一系列静态方法来处理所有常见的tween类型结构,以及它们的unclamped lerps.unclamped lerps对于超过0-1范围的bounce、elastic或其他tweens是必需的 + */ + var Lerps = /** @class */ (function () { + function Lerps() { + } + Lerps.lerp = function (from, to, t) { + if (typeof (from) == "number" && typeof (to) == "number") { + return from + (to - from) * t; + } + if (from instanceof es.Color && to instanceof es.Color) { + var t255 = t * 255; + return new es.Color(from.r + (to.r - from.r) * t255 / 255, from.g + (to.g - from.g) * t255 / 255, from.b + (to.b - from.b) * t255 / 255, from.a + (to.a - from.a) * t255 / 255); + } + if (from instanceof es.Rectangle && to instanceof es.Rectangle) { + return new es.Rectangle((from.x + (to.x - from.x) * t), (from.y + (to.x - from.y) * t), (from.width + (to.width - from.width) * t), (from.height + (to.height - from.height) * t)); + } + if (from instanceof es.Vector2 && to instanceof es.Vector2) { + return new es.Vector2(from.x + (to.x - from.x) * t, from.y + (to.y - from.y) * t); + } + }; + Lerps.angleLerp = function (from, to, t) { + // 我们计算这个lerp的最短角差 + var toMinusFrom = new es.Vector2(es.MathHelper.deltaAngle(from.x, to.x), es.MathHelper.deltaAngle(from.y, to.y)); + return new es.Vector2(from.x + toMinusFrom.x * t, from.y + toMinusFrom.y * t); + }; + Lerps.ease = function (easeType, from, to, t, duration) { + if (typeof (from) == 'number' && typeof (to) == "number") { + return this.lerp(from, to, es.EaseHelper.ease(easeType, t, duration)); + } + if (from instanceof es.Vector2 && to instanceof es.Vector2) { + return this.lerp(from, to, es.EaseHelper.ease(easeType, t, duration)); + } + if (from instanceof es.Rectangle && to instanceof es.Rectangle) { + return this.lerp(from, to, es.EaseHelper.ease(easeType, t, duration)); + } + if (from instanceof es.Color && to instanceof es.Color) { + return this.lerp(from, to, es.EaseHelper.ease(easeType, t, duration)); + } + }; + Lerps.easeAngle = function (easeType, from, to, t, duration) { + return this.angleLerp(from, to, es.EaseHelper.ease(easeType, t, duration)); + }; + /** + * 使用半隐式欧拉方法。速度较慢,但总是很稳定。见 + * http://allenchou.net/2015/04/game-math-more-on-numeric-springing/ + * @param currentValue + * @param targetValue + * @param velocity Velocity的引用。如果在两次调用之间改变targetValue,请务必将其重置为0 + * @param dampingRatio 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + * @param angularFrequency 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定 + */ + Lerps.fastSpring = function (currentValue, targetValue, velocity, dampingRatio, angularFrequency) { + velocity.add(velocity.scale(-2 * es.Time.deltaTime * dampingRatio * angularFrequency) + .add(targetValue.sub(currentValue).scale(es.Time.deltaTime * angularFrequency * angularFrequency))); + currentValue.add(velocity.scale(es.Time.deltaTime)); + return currentValue; + }; + return Lerps; + }()); + es.Lerps = Lerps; +})(es || (es = {})); +var es; +(function (es) { + var AnimCurve = /** @class */ (function () { + function AnimCurve(points) { + if (points.length < 2) { + throw new Error('curve length must be >= 2'); + } + points.sort(function (a, b) { + return a.t - b.t; + }); + if (points[0].t !== 0) { + throw new Error('curve must start with 0'); + } + if (points[points.length - 1].t !== 1) { + throw new Error('curve must end with 1'); + } + this._points = points; + } + Object.defineProperty(AnimCurve.prototype, "points", { + get: function () { + return this._points; + }, + enumerable: true, + configurable: true + }); + AnimCurve.prototype.lerp = function (t) { + for (var i = 1; i < this._points.length; i++) { + if (t <= this._points[i].t) { + var m = es.MathHelper.map01(t, this._points[i - 1].t, this._points[i].t); + return es.MathHelper.lerp(this._points[i - 1].value, this._points[i].value, m); + } + } + throw new Error('should never be here'); + }; + return AnimCurve; + }()); + es.AnimCurve = AnimCurve; +})(es || (es = {})); +var es; (function (es) { /** * 用于包装事件的一个小类 @@ -8957,64 +12564,6 @@ var es; es.EqualityComparer = EqualityComparer; })(es || (es = {})); var es; -(function (es) { - var GlobalManager = /** @class */ (function () { - function GlobalManager() { - } - Object.defineProperty(GlobalManager.prototype, "enabled", { - /** - * 如果true则启用了GlobalManager。 - * 状态的改变会导致调用OnEnabled/OnDisable - */ - get: function () { - return this._enabled; - }, - /** - * 如果true则启用了GlobalManager。 - * 状态的改变会导致调用OnEnabled/OnDisable - * @param value - */ - set: function (value) { - this.setEnabled(value); - }, - enumerable: true, - configurable: true - }); - /** - * 启用/禁用这个GlobalManager - * @param isEnabled - */ - GlobalManager.prototype.setEnabled = function (isEnabled) { - if (this._enabled != isEnabled) { - this._enabled = isEnabled; - if (this._enabled) { - this.onEnabled(); - } - else { - this.onDisabled(); - } - } - }; - /** - * 此GlobalManager启用时调用 - */ - GlobalManager.prototype.onEnabled = function () { - }; - /** - * 此GlobalManager禁用时调用 - */ - GlobalManager.prototype.onDisabled = function () { - }; - /** - * 在frame .update之前调用每一帧 - */ - GlobalManager.prototype.update = function () { - }; - return GlobalManager; - }()); - es.GlobalManager = GlobalManager; -})(es || (es = {})); -var es; (function (es) { var Hash = /** @class */ (function () { function Hash() { @@ -9044,6 +12593,177 @@ var es; es.Hash = Hash; })(es || (es = {})); var es; +(function (es) { + var Observable = /** @class */ (function () { + function Observable() { + this._listeners = []; + } + Observable.prototype.addListener = function (caller, callback) { + if (this._listeners.findIndex(function (listener) { + return listener.callback === callback && listener.caller === caller; + }) === -1) { + this._listeners.push({ caller: caller, callback: callback }); + } + }; + Observable.prototype.removeListener = function (caller, callback) { + var index = this._listeners.findIndex(function (listener) { return listener.callback === callback && listener.caller === caller; }); + if (index >= 0) { + this._listeners.splice(index, 1); + } + }; + Observable.prototype.clearListener = function () { + this._listeners = []; + }; + Observable.prototype.clearListenerWithCaller = function (caller) { + for (var i = this._listeners.length - 1; i >= 0; i--) { + var listener = this._listeners[i]; + if (listener.caller === caller) { + this._listeners.splice(i, 1); + } + } + }; + Observable.prototype.notify = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var _a; + for (var i = this._listeners.length - 1; i >= 0; i--) { + var listener = this._listeners[i]; + if (listener.caller) { + (_a = listener.callback).call.apply(_a, __spread([listener.caller], args)); + } + else { + listener.callback.apply(listener, __spread(args)); + } + } + }; + return Observable; + }()); + es.Observable = Observable; + var ObservableT = /** @class */ (function (_super) { + __extends(ObservableT, _super); + function ObservableT() { + return _super !== null && _super.apply(this, arguments) || this; + } + ObservableT.prototype.addListener = function (caller, callback) { + _super.prototype.addListener.call(this, caller, callback); + }; + ObservableT.prototype.removeListener = function (caller, callback) { + _super.prototype.removeListener.call(this, caller, callback); + }; + ObservableT.prototype.notify = function (arg) { + _super.prototype.notify.call(this, arg); + }; + return ObservableT; + }(Observable)); + es.ObservableT = ObservableT; + var ObservableTT = /** @class */ (function (_super) { + __extends(ObservableTT, _super); + function ObservableTT() { + return _super !== null && _super.apply(this, arguments) || this; + } + ObservableTT.prototype.addListener = function (caller, callback) { + _super.prototype.addListener.call(this, caller, callback); + }; + ObservableTT.prototype.removeListener = function (caller, callback) { + _super.prototype.removeListener.call(this, caller, callback); + }; + ObservableTT.prototype.notify = function (arg1, arg2) { + _super.prototype.notify.call(this, arg1, arg2); + }; + return ObservableTT; + }(Observable)); + es.ObservableTT = ObservableTT; + var Command = /** @class */ (function () { + function Command(caller, action) { + this.bindAction(caller, action); + this._onExec = new Observable(); + } + Command.prototype.bindAction = function (caller, action) { + this._caller = caller; + this._action = action; + }; + Command.prototype.dispatch = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var _a; + if (this._action) { + if (this._caller) { + (_a = this._action).call.apply(_a, __spread([this._caller], args)); + } + else { + this._action.apply(this, __spread(args)); + } + this._onExec.notify(); + } + else { + console.warn('command not bind with an action'); + } + }; + Command.prototype.addListener = function (caller, callback) { + this._onExec.addListener(caller, callback); + }; + Command.prototype.removeListener = function (caller, callback) { + this._onExec.removeListener(caller, callback); + }; + Command.prototype.clearListener = function () { + this._onExec.clearListener(); + }; + Command.prototype.clearListenerWithCaller = function (caller) { + this._onExec.clearListenerWithCaller(caller); + }; + return Command; + }()); + es.Command = Command; + var ValueChangeCommand = /** @class */ (function () { + function ValueChangeCommand(value) { + this._onValueChange = new Observable(); + this._value = value; + } + Object.defineProperty(ValueChangeCommand.prototype, "onValueChange", { + get: function () { + return this._onValueChange; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(ValueChangeCommand.prototype, "value", { + get: function () { + return this._value; + }, + set: function (newValue) { + this._value = newValue; + }, + enumerable: true, + configurable: true + }); + ValueChangeCommand.prototype.dispatch = function (value) { + if (value !== this._value) { + var oldValue = this._value; + this._value = value; + this._onValueChange.notify(this._value, oldValue); + } + }; + ValueChangeCommand.prototype.addListener = function (caller, callback) { + this._onValueChange.addListener(caller, callback); + }; + ValueChangeCommand.prototype.removeListener = function (caller, callback) { + this._onValueChange.removeListener(caller, callback); + }; + ValueChangeCommand.prototype.clearListener = function () { + this._onValueChange.clearListener(); + }; + ValueChangeCommand.prototype.clearListenerWithCaller = function (caller) { + this._onValueChange.clearListenerWithCaller(caller); + }; + return ValueChangeCommand; + }()); + es.ValueChangeCommand = ValueChangeCommand; +})(es || (es = {})); +var es; (function (es) { var Ref = /** @class */ (function () { function Ref(value) { @@ -9121,13 +12841,13 @@ var es; } Triangulator.testPointTriangle = function (point, a, b, c) { // 如果点在AB的右边,那么外边的三角形是 - if (es.Vector2Ext.cross(es.Vector2.subtract(point, a), es.Vector2.subtract(b, a)) < 0) + if (es.Vector2Ext.cross(point.sub(a), b.sub(a)) < 0) return false; // 如果点在BC的右边,则在三角形的外侧 - if (es.Vector2Ext.cross(es.Vector2.subtract(point, b), es.Vector2.subtract(c, b)) < 0) + if (es.Vector2Ext.cross(point.sub(b), c.sub(b)) < 0) return false; // 如果点在ca的右边,则在三角形的外面 - if (es.Vector2Ext.cross(es.Vector2.subtract(point, c), es.Vector2.subtract(a, c)) < 0) + if (es.Vector2Ext.cross(point.sub(c), a.sub(c)) < 0) return false; // 点在三角形上 return true; @@ -11721,7 +15441,7 @@ var es; */ RectangleExt.getClosestPointOnRectangleToPoint = function (rect, point) { // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 - var res = new es.Vector2(); + var res = es.Vector2.zero; res.x = es.MathHelper.clamp(point.x, rect.left, rect.right); res.y = es.MathHelper.clamp(point.y, rect.top, rect.bottom); return res; @@ -11733,9 +15453,9 @@ var es; */ RectangleExt.getClosestPointOnRectangleBorderToPoint = function (rect, point) { // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 - var res = new es.Vector2(); - res.x = es.MathHelper.clamp(point.x, rect.left, rect.right); - res.y = es.MathHelper.clamp(point.y, rect.top, rect.bottom); + var res = es.Vector2.zero; + res.x = es.MathHelper.clamp(Math.trunc(point.x), rect.left, rect.right); + res.y = es.MathHelper.clamp(Math.trunc(point.y), rect.top, rect.bottom); // 如果点在矩形内,我们需要将res推到边框,因为它将在矩形内 if (rect.contains(res.x, res.y)) { var dl = rect.x - rect.left; @@ -11786,7 +15506,47 @@ var es; if (pt.y > maxY) maxY = pt.y; } - return this.fromMinMaxVector(new es.Vector2(minX, minY), new es.Vector2(maxX, maxY)); + return this.fromMinMaxVector(new es.Vector2(Math.trunc(minX), Math.trunc(minY)), new es.Vector2(Math.trunc(maxX), Math.trunc(maxY))); + }; + RectangleExt.calculateBounds = function (rect, parentPosition, position, origin, scale, rotation, width, height) { + if (rotation == 0) { + rect.x = Math.trunc(parentPosition.x + position.x - origin.x * scale.x); + rect.y = Math.trunc(parentPosition.y + position.y - origin.y * scale.y); + rect.width = Math.trunc(width * scale.x); + rect.height = Math.trunc(height * scale.y); + } + else { + // 我们需要找到我们的绝对最小/最大值,并据此创建边界 + var worldPosX = parentPosition.x + position.x; + var worldPosY = parentPosition.y + position.y; + var tempMat = void 0; + // 考虑到原点,将参考点设置为世界参考 + var transformMatrix = new es.Matrix2D(); + es.Matrix2D.createTranslation(-worldPosX - origin.x, -worldPosY - origin.y, transformMatrix); + es.Matrix2D.createScale(scale.x, scale.y, tempMat); + transformMatrix = transformMatrix.multiply(tempMat); + es.Matrix2D.createRotation(rotation, tempMat); + transformMatrix = transformMatrix.multiply(tempMat); + es.Matrix2D.createTranslation(worldPosX, worldPosY, tempMat); + transformMatrix = transformMatrix.multiply(tempMat); + // TODO: 我们可以把世界变换留在矩阵中,避免在世界空间中得到所有的四个角 + var topLeft = new es.Vector2(worldPosX, worldPosY); + var topRight = new es.Vector2(worldPosX + width, worldPosY); + var bottomLeft = new es.Vector2(worldPosX, worldPosY + height); + var bottomRight = new es.Vector2(worldPosX + width, worldPosY + height); + es.Vector2Ext.transformR(topLeft, transformMatrix, topLeft); + es.Vector2Ext.transformR(topRight, transformMatrix, topRight); + es.Vector2Ext.transformR(bottomLeft, transformMatrix, bottomLeft); + es.Vector2Ext.transformR(bottomRight, transformMatrix, bottomRight); + // 找出最小值和最大值,这样我们就可以计算出我们的边界框。 + var minX = Math.trunc(Math.min(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + var maxX = Math.trunc(Math.max(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + var minY = Math.trunc(Math.min(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); + var maxY = Math.trunc(Math.max(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); + rect.location = new es.Vector2(minX, minY); + rect.width = Math.trunc(maxX - minX); + rect.height = Math.trunc(maxY - minY); + } }; /** * 缩放矩形 @@ -11794,13 +15554,13 @@ var es; * @param scale */ RectangleExt.scale = function (rect, scale) { - rect.x = rect.x * scale.x; - rect.y = rect.y * scale.y; - rect.width = rect.width * scale.x; - rect.height = rect.height * scale.y; + rect.x = Math.trunc(rect.x * scale.x); + rect.y = Math.trunc(rect.y * scale.y); + rect.width = Math.trunc(rect.width * scale.x); + rect.height = Math.trunc(rect.height * scale.y); }; RectangleExt.translate = function (rect, vec) { - rect.location.add(vec); + rect.location.addEqual(vec); }; return RectangleExt; }()); @@ -11850,7 +15610,7 @@ var es; * @param c */ Vector2Ext.isTriangleCCW = function (a, center, c) { - return this.cross(es.Vector2.subtract(center, a), es.Vector2.subtract(c, center)) < 0; + return this.cross(center.sub(a), c.sub(center)) < 0; }; Vector2Ext.halfVector = function () { return new es.Vector2(0.5, 0.5); @@ -11886,7 +15646,7 @@ var es; Vector2Ext.angle = function (from, to) { this.normalize(from); this.normalize(to); - return Math.acos(es.MathHelper.clamp(es.Vector2.dot(from, to), -1, 1)) * es.MathHelper.Rad2Deg; + return Math.acos(es.MathHelper.clamp(from.dot(to), -1, 1)) * es.MathHelper.Rad2Deg; }; /** * 返回以自度为中心的左右角度 @@ -11895,8 +15655,8 @@ var es; * @param right */ Vector2Ext.angleBetween = function (self, left, right) { - var one = es.Vector2.subtract(left, self); - var two = es.Vector2.subtract(right, self); + var one = left.sub(self); + var two = right.sub(self); return this.angle(one, two); }; /** @@ -11908,7 +15668,7 @@ var es; * @param intersection */ Vector2Ext.getRayIntersection = function (a, b, c, d, intersection) { - if (intersection === void 0) { intersection = new es.Vector2(); } + if (intersection === void 0) { intersection = es.Vector2.zero; } var dy1 = b.y - a.y; var dx1 = b.x - a.x; var dy2 = d.y - c.y; @@ -11932,7 +15692,7 @@ var es; Vector2Ext.normalize = function (vec) { var magnitude = Math.sqrt((vec.x * vec.x) + (vec.y * vec.y)); if (magnitude > es.MathHelper.Epsilon) { - vec.divide(new es.Vector2(magnitude)); + vec.divideScaler(magnitude); } else { vec.x = vec.y = 0; @@ -11963,7 +15723,7 @@ var es; * @param result */ Vector2Ext.transformR = function (position, matrix, result) { - if (result === void 0) { result = new es.Vector2(); } + if (result === void 0) { result = es.Vector2.zero; } var x = (position.x * matrix.m11) + (position.y * matrix.m21) + matrix.m31; var y = (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32; result.x = x; @@ -12449,7 +16209,7 @@ var es; * 创建一个Set从一个Enumerable.List< T>。 */ List.prototype.toSet = function () { - var e_10, _a; + var e_12, _a; var result = new Set(); try { for (var _b = __values(this._elements), _c = _b.next(); !_c.done; _c = _b.next()) { @@ -12457,12 +16217,12 @@ var es; result.add(x); } } - catch (e_10_1) { e_10 = { error: e_10_1 }; } + catch (e_12_1) { e_12 = { error: e_12_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } - finally { if (e_10) throw e_10.error; } + finally { if (e_12) throw e_12.error; } } return result; }; @@ -12638,13 +16398,13 @@ var es; * @param radius */ VisibilityComputer.prototype.addCircleOccluder = function (position, radius) { - var dirToCircle = es.Vector2.subtract(position, this._origin); + var dirToCircle = position.sub(this._origin); var angle = Math.atan2(dirToCircle.y, dirToCircle.x); var stepSize = Math.PI / this.lineCountForCircleApproximation; var startAngle = angle + es.MathHelper.PiOver2; - var lastPt = es.MathHelper.angleToVector(startAngle, radius).add(position); + var lastPt = es.MathHelper.angleToVector(startAngle, radius).addEqual(position); for (var i = 1; i < this.lineCountForCircleApproximation; i++) { - var nextPt = es.MathHelper.angleToVector(startAngle + i * stepSize, radius).add(position); + var nextPt = es.MathHelper.angleToVector(startAngle + i * stepSize, radius).addEqual(position); this.addLineOccluder(lastPt, nextPt); lastPt = nextPt; } @@ -12711,7 +16471,7 @@ var es; * 计算可见性多边形,并返回三角形扇形的顶点(减去中心顶点)。返回的数组来自ListPool */ VisibilityComputer.prototype.end = function () { - var e_11, _a; + var e_13, _a; var output = es.ListPool.obtain(); this.updateSegments(); this._endPoints.sort(this._radialComparer.compare); @@ -12750,12 +16510,12 @@ var es; } } } - catch (e_11_1) { e_11 = { error: e_11_1 }; } + catch (e_13_1) { e_13 = { error: e_13_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } - finally { if (e_11) throw e_11.error; } + finally { if (e_13) throw e_13.error; } } } VisibilityComputer._openSegments.clear(); @@ -12871,7 +16631,7 @@ var es; * 处理片段,以便我们稍后对它们进行分类 */ VisibilityComputer.prototype.updateSegments = function () { - var e_12, _a; + var e_14, _a; try { for (var _b = __values(this._segments), _c = _b.next(); !_c.done; _c = _b.next()) { var segment = _c.value; @@ -12889,12 +16649,12 @@ var es; segment.p2.begin = !segment.p1.begin; } } - catch (e_12_1) { e_12 = { error: e_12_1 }; } + catch (e_14_1) { e_14 = { error: e_14_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } - finally { if (e_12) throw e_12.error; } + finally { if (e_14) throw e_14.error; } } // 如果我们有一个聚光灯,我们需要存储前两个段的角度。 // 这些是光斑的边界,我们将用它们来过滤它们之外的任何顶点。 diff --git a/source/bin/framework.min.js b/source/bin/framework.min.js index a52896ab..c65062ea 100644 --- a/source/bin/framework.min.js +++ b/source/bin/framework.min.js @@ -1 +1 @@ -window.es={};var __awaiter=this&&this.__awaiter||function(t,e,n,i){return new(n||(n=Promise))(function(r,o){function s(t){try{c(i.next(t))}catch(t){o(t)}}function a(t){try{c(i.throw(t))}catch(t){o(t)}}function c(t){t.done?r(t.value):new n(function(e){e(t.value)}).then(s,a)}c((i=i.apply(t,e||[])).next())})},__generator=this&&this.__generator||function(t,e){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]0)&&!(i=o.next()).done;)s.push(i.value)}catch(t){r={error:t}}finally{try{i&&!i.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return s},__spread=this&&this.__spread||function(){for(var t=[],e=0;e=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}}},transform;!function(t){var e=function(){function e(n,i){void 0===n&&(n=!0),void 0===i&&(i=!0),this._globalManagers=[],this._coroutineManager=new t.CoroutineManager,this._timerManager=new t.TimerManager,this._frameCounterElapsedTime=0,this._frameCounter=0,this._totalMemory=0,e._instance=this,e.emitter=new t.Emitter,e.emitter.addObserver(t.CoreEvents.frameUpdated,this.update,this),e.registerGlobalManager(this._coroutineManager),e.registerGlobalManager(this._timerManager),e.entitySystemsEnabled=i,this.debug=n,this.initialize()}return Object.defineProperty(e,"Instance",{get:function(){return this._instance},enumerable:!0,configurable:!0}),Object.defineProperty(e,"scene",{get:function(){return this._instance?this._instance._scene:null},set:function(e){t.Insist.isNotNull(e,"场景不能为空"),null==this._instance._scene?(this._instance._scene=e,this._instance.onSceneChanged(),this._instance._scene.begin()):this._instance._nextScene=e},enumerable:!0,configurable:!0}),e.create=function(e){return void 0===e&&(e=!0),null==this._instance&&(this._instance=new t.Core(e)),this._instance},e.registerGlobalManager=function(t){this._instance._globalManagers.push(t),t.enabled=!0},e.unregisterGlobalManager=function(e){new t.List(this._instance._globalManagers).remove(e),e.enabled=!1},e.getGlobalManager=function(t){for(var e=0;e=1)){var e=window.performance.memory;null!=e&&(this._totalMemory=Number((e.totalJSHeapSize/1048576).toFixed(2))),this._titleMemory&&this._titleMemory(this._totalMemory,this._frameCounter),this._frameCounter=0,this._frameCounterElapsedTime-=1}},e.prototype.onSceneChanged=function(){t.Time.sceneChanged()},e.prototype.initialize=function(){},e.prototype.update=function(n){return void 0===n&&(n=-1),__awaiter(this,void 0,void 0,function(){var i;return __generator(this,function(r){if(e.paused)return[2];if(t.Time.update(n),null!=this._scene){for(i=this._globalManagers.length-1;i>=0;i--)this._globalManagers[i].enabled&&this._globalManagers[i].update();this._scene.update(),null!=this._nextScene&&(this._scene.end(),this._scene=this._nextScene,this._nextScene=null,this.onSceneChanged(),this._scene.begin())}return this.startDebugDraw(),[2]})})},e.paused=!1,e.debugRenderEndabled=!1,e}();t.Core=e}(es||(es={})),function(t){var e;!function(t){t[t.error=0]="error",t[t.warn=1]="warn",t[t.log=2]="log",t[t.info=3]="info",t[t.trace=4]="trace"}(e=t.LogType||(t.LogType={}));var n=function(){function t(){}return t.warnIf=function(t,n){for(var i=[],r=2;r=0;t--){this.transform.getChild(t).entity.destroy()}},n.prototype.detachFromScene=function(){this.scene.entities.remove(this),this.components.deregisterAllComponents();for(var t=0;tn.x?t.x:n.x,t.y>n.y?t.y:n.y)},e.hermite=function(n,i,r,o,s){return new e(t.MathHelper.hermite(n.x,i.x,r.x,o.x,s),t.MathHelper.hermite(n.y,i.y,r.y,o.y,s))},e.prototype.clone=function(){return new e(this.x,this.y)},e}();t.Vector2=e}(es||(es={})),function(t){var e=function(){function e(){this._sceneComponents=[],this.entities=new t.EntityList(this),this.entityProcessors=new t.EntityProcessorList,this.identifierPool=new t.IdentifierPool,this.initialize()}return e.prototype.initialize=function(){},e.prototype.onStart=function(){},e.prototype.unload=function(){},e.prototype.begin=function(){t.Physics.reset(),null!=this.entityProcessors&&this.entityProcessors.begin(),this._didSceneBegin=!0,this.onStart()},e.prototype.end=function(){this._didSceneBegin=!1,this.entities.removeAllEntities();for(var e=0;e=0;t--)this._sceneComponents[t].enabled&&this._sceneComponents[t].update();null!=this.entityProcessors&&this.entityProcessors.update(),this.entities.update(),null!=this.entityProcessors&&this.entityProcessors.lateUpdate()},e.prototype.addSceneComponent=function(t){return t.scene=this,t.onEnabled(),this._sceneComponents.push(t),this._sceneComponents.sort(t.compare),t},e.prototype.getSceneComponent=function(t){for(var e=0;ee.x?-1:1,i=t.Vector2.normalize(t.Vector2.subtract(this.position,e));this.rotation=n*Math.acos(t.Vector2.dot(i,t.Vector2.unitY))},n.prototype.setLocalRotation=function(t){return this._localRotation=t,this._localDirty=this._positionDirty=this._localPositionDirty=this._localRotationDirty=this._localScaleDirty=!0,this.setDirty(e.rotationDirty),this},n.prototype.setLocalRotationDegrees=function(e){return this.setLocalRotation(t.MathHelper.toRadians(e))},n.prototype.setScale=function(e){return this._scale=e,this.parent?this.localScale=t.Vector2.divide(e,this.parent._scale):this.localScale=e,this},n.prototype.setLocalScale=function(t){return this._localScale=t,this._localDirty=this._positionDirty=this._localScaleDirty=!0,this.setDirty(e.scaleDirty),this},n.prototype.roundPosition=function(){this.position=t.Vector2Ext.round(this._position)},n.prototype.updateTransform=function(){this.hierarchyDirty!=e.clean&&(null!=this.parent&&this.parent.updateTransform(),this._localDirty&&(this._localPositionDirty&&(this._translationMatrix=t.Matrix2D.createTranslation(this._localPosition.x,this._localPosition.y),this._localPositionDirty=!1),this._localRotationDirty&&(this._rotationMatrix=t.Matrix2D.createRotation(this._localRotation),this._localRotationDirty=!1),this._localScaleDirty&&(this._scaleMatrix=t.Matrix2D.createScale(this._localScale.x,this._localScale.y),this._localScaleDirty=!1),this._localTransform=this._scaleMatrix.multiply(this._rotationMatrix),this._localTransform=this._localTransform.multiply(this._translationMatrix),null==this.parent&&(this._worldTransform=this._localTransform,this._rotation=this._localRotation,this._scale=this._localScale,this._worldInverseDirty=!0),this._localDirty=!1),null!=this.parent&&(this._worldTransform=this._localTransform.multiply(this.parent._worldTransform),this._rotation=this._localRotation+this.parent._rotation,this._scale=t.Vector2.multiply(this.parent._scale,this._localScale),this._worldInverseDirty=!0),this._worldToLocalDirty=!0,this._positionDirty=!0,this.hierarchyDirty=e.clean)},n.prototype.setDirty=function(e){if(0==(this.hierarchyDirty&e)){switch(this.hierarchyDirty|=e,e){case t.DirtyType.positionDirty:this.entity.onTransformChanged(transform.Component.position);break;case t.DirtyType.rotationDirty:this.entity.onTransformChanged(transform.Component.rotation);break;case t.DirtyType.scaleDirty:this.entity.onTransformChanged(transform.Component.scale)}for(var n=0;n1e-4?this._inverseMass=1/this._mass:this._inverseMass=0,this},n.prototype.setElasticity=function(e){return this._elasticity=t.MathHelper.clamp01(e),this},n.prototype.setFriction=function(e){return this._friction=t.MathHelper.clamp01(e),this},n.prototype.setGlue=function(e){return this._glue=t.MathHelper.clamp(e,0,10),this},n.prototype.addImpulse=function(e){this.isImmovable||(this.velocity=this.velocity.add(t.Vector2.multiplyScaler(e,1e5).multiplyScaler(this._inverseMass*t.Time.deltaTime*t.Time.deltaTime)))},n.prototype.onAddedToEntity=function(){this._collider=this.entity.getComponent(t.Collider),t.Debug.warnIf(null==this._collider,"ArcadeRigidbody 没有 Collider。ArcadeRigidbody需要一个Collider!")},n.prototype.update=function(){var e,i;if(this.isImmovable||null==this._collider)this.velocity=t.Vector2.zero;else{this.shouldUseGravity&&(this.velocity=this.velocity.add(t.Vector2.multiplyScaler(t.Physics.gravity,t.Time.deltaTime))),this.entity.transform.position=this.entity.transform.position.add(t.Vector2.multiplyScaler(this.velocity,t.Time.deltaTime));var r=new t.CollisionResult,o=t.Physics.boxcastBroadphaseExcludingSelfNonRect(this._collider,this._collider.collidesWithLayers.value);try{for(var s=__values(o),a=s.next();!a.done;a=s.next()){var c=a.value;if(!c.entity.equals(this.entity)&&this._collider.collidesWithNonMotion(c,r)){var h=c.entity.getComponent(n);if(null!=h)this.processOverlap(h,r.minimumTranslationVector),this.processCollision(h,r.minimumTranslationVector);else{this.entity.transform.position=this.entity.transform.position.subtract(r.minimumTranslationVector);var u=this.velocity.clone();this.calculateResponseVelocity(u,r.minimumTranslationVector,u),this.velocity=this.velocity.add(u)}}}}catch(t){e={error:t}}finally{try{a&&!a.done&&(i=s.return)&&i.call(s)}finally{if(e)throw e.error}}}},n.prototype.processOverlap=function(e,n){this.isImmovable?e.entity.transform.position=e.entity.transform.position.add(n):e.isImmovable?this.entity.transform.position=this.entity.transform.position.subtract(n):(this.entity.transform.position=this.entity.transform.position.subtract(t.Vector2.multiplyScaler(n,.5)),e.entity.transform.position=e.entity.transform.position.add(t.Vector2.multiplyScaler(n,.5)))},n.prototype.processCollision=function(e,n){var i=t.Vector2.subtract(this.velocity,e.velocity);this.calculateResponseVelocity(i,n,i);var r=this._inverseMass+e._inverseMass,o=this._inverseMass/r,s=e._inverseMass/r;this.velocity=this.velocity.add(t.Vector2.multiplyScaler(i,o)),e.velocity=e.velocity.subtract(t.Vector2.multiplyScaler(i,s))},n.prototype.calculateResponseVelocity=function(e,n,i){void 0===i&&(i=new t.Vector2);var r=t.Vector2.multiplyScaler(n,-1),o=t.Vector2.normalize(r),s=t.Vector2.dot(e,o),a=t.Vector2.multiplyScaler(o,s),c=t.Vector2.subtract(e,a);s>0&&(a=t.Vector2.zero);var h=this._friction;c.lengthSquared()n;n++){var i=t[n];this.processDelta(i,this.acc);var r=this.getRemainingDelay(i);r<=0?this.processExpired(i):this.offerDelay(r)}this.acc=0}else this.stop()},n.prototype.checkProcessing=function(){return!!this.running&&(this.acc+=t.Time.deltaTime,this.acc>=this.delay)},n.prototype.offerDelay=function(t){this.running?this.delay=Math.min(this.delay,t):(this.running=!0,this.delay=t)},n.prototype.getInitialTimeDelay=function(){return this.delay},n.prototype.getRemainingTimeUntilProcessing=function(){return this.running?this.delay-this.acc:0},n.prototype.isRunning=function(){return this.running},n.prototype.stop=function(){this.running=!1,this.acc=0},n}(t.EntitySystem);t.DelayedIteratingSystem=e}(es||(es={})),function(t){var e=function(t){function e(e){return t.call(this,e)||this}return __extends(e,t),e.prototype.lateProcessEntity=function(t){},e.prototype.process=function(t){if(0!=t.length)for(var e=0,n=t.length;e=this.interval&&(this.acc-=this.interval,this.intervalDelta=this.acc-this.intervalDelta,!0)},n.prototype.getIntervalDelta=function(){return this.interval+this.intervalDelta},n}(t.EntitySystem);t.IntervalSystem=e}(es||(es={})),function(t){var e=function(t){function e(e,n){return t.call(this,e,n)||this}return __extends(e,t),e.prototype.process=function(t){var e=this;t.forEach(function(t){return e.processEntity(t)})},e}(t.IntervalSystem);t.IntervalIteratingSystem=e}(es||(es={})),function(es){var JobSystem=function(_super){function JobSystem(t,e){var n=_super.call(this,t)||this;n._threads=e,n._jobs=new Array(e);for(var i=0;it.length&&(s=t.length);var a=o._jobs[n];if(a.set(t,r,s,o._executeStr,o),r!=s){var c=es.WorkerUtils.makeWorker(o.queueOnThread);es.WorkerUtils.workerMessage(c)(a).then(function(t){var n=t;e.resetJob(n),c.terminate()}).catch(function(t){a.err=t,c.terminate()})}},o=this,s=0;s-1?eval("(function(){return "+v+" })()"):v}),i=job.from;i0){for(var t=0,e=this._components.length;t0)for(var e=0,n=this._components.length;e0)for(var e=0,n=this._components.length;e0){for(var e=function(t,e){var i=n._componentsToRemoveList[t];n.handleRemove(i);var r=n._components.findIndex(function(t){return t.id==i.id});-1!=r&&n._components.splice(r,1),n.removeComponentsByType(i)},n=this,i=0,r=this._componentsToRemoveList.length;i0){for(i=0,r=this._componentsToAddList.length;i0){for(i=0,r=this._tempBufferList.length;i0){var n=this._updatableComponents.findIndex(function(t){return t.id==e.id});-1!=n&&this._updatableComponents.splice(n,1)}this.decreaseBits(e),this._entity.scene.entityProcessors.onComponentRemoved(this._entity),e.onRemovedFromEntity(),e.entity=null},e.prototype.removeComponentsByType=function(e){var n=this.componentsByType.get(t.TypeUtils.getType(e)),i=n.findIndex(function(t){return t.id==e.id});-1!=i&&n.splice(i,1)},e.prototype.addComponentsByType=function(e){var n=this.componentsByType.get(t.TypeUtils.getType(e));n||(n=[]),n.push(e),this.componentsByType.set(t.TypeUtils.getType(e),n)},e.prototype.removeComponentsToAddByType=function(e){var n=this.componentsToAddByType.get(t.TypeUtils.getType(e)),i=n.findIndex(function(t){return t.id==e.id});-1!=i&&n.splice(i,1)},e.prototype.addComponentsToAddByType=function(e){var n=this.componentsToAddByType.get(t.TypeUtils.getType(e));n||(n=[]),n.push(e),this.componentsToAddByType.set(t.TypeUtils.getType(e),n)},e.prototype.getComponent=function(t,e){var n=this.componentsByType.get(t);if(n&&n.length>0)return n[0];if(!e){var i=this.componentsToAddByType.get(t);if(i&&i.length>0)return i[0]}return null},e.prototype.getComponents=function(t,e){e||(e=[]);var n=this.componentsByType.get(t);n&&(e=e.concat(n));var i=this.componentsToAddByType.get(t);return i&&(e=e.concat(i)),e},e.prototype.update=function(){if(this.updateLists(),this._updatableComponents.length>0)for(var t=0,e=this._updatableComponents.length;t0)for(var e=0,n=this._components.length;e0)for(e=0,n=this._componentsToAddList.length;e0)for(var t=0,e=this._components.length;t0)for(var t=0,e=this._components.length;t0){for(var t=function(t,n){var i=e._entitiesToRemoveList[t];e.removeFromTagList(i);var r=e._entities.findIndex(function(t){return t.id==i.id});-1!=r&&e._entities.splice(r,1),i.onRemovedFromScene(),i.scene=null,e.scene.entityProcessors.onEntityRemoved(i)},e=this,n=0,i=this._entitiesToRemoveList.length;n0){for(n=0,i=this._entitiesToAddedList.length;n0)for(var e=0,n=this._entities.length;e0)for(e=0,n=this._entitiesToAddedList.length;e0)for(var e=0,n=this._entities.length;e0)try{for(var s=__values(r),a=s.next();!a.done;a=s.next()){var c=a.value;o.push(c)}}catch(t){n={error:t}}finally{try{a&&!a.done&&(i=s.return)&&i.call(s)}finally{if(n)throw n.error}}return o},e.prototype.entityWithTag=function(t){var e,n,i=this.getTagList(t);if(i.size>0)try{for(var r=__values(i),o=r.next();!o.done;o=r.next()){return o.value}}catch(t){e={error:t}}finally{try{o&&!o.done&&(n=r.return)&&n.call(r)}finally{if(e)throw e.error}}return null},e.prototype.findComponentOfType=function(t){if(this._entities.length>0)for(var e=0,n=this._entities.length;e0)for(e=0;e0)for(var i=0,r=this._entities.length;i0)for(i=0,r=this._entitiesToAddedList.length;i0)for(var i=0,r=this._entities.length;i0)for(var s=0,a=t.length;s0)for(i=0,r=this._entitiesToAddedList.length;i0)for(s=0,a=t.length;s=t)return n}for(e=1|t;ethis.maxPrimeArrayLength&&this.maxPrimeArrayLength>t?this.maxPrimeArrayLength:this.getPrime(e)},t.getHashCode=function(t){var e,n=0;if(0==(e="object"==typeof t?JSON.stringify(t):t.toString()).length)return n;for(var i=0;i0?this.ids.removeLast():this.nextAvailableId_++},e.prototype.checkIn=function(t){this.ids.add(t)},e}();t.IdentifierPool=e}(es||(es={})),function(t){var e=function(){function e(){this.allSet=[],this.exclusionSet=[],this.oneSet=[]}return e.empty=function(){return new e},e.prototype.getAllSet=function(){return this.allSet},e.prototype.getExclusionSet=function(){return this.exclusionSet},e.prototype.getOneSet=function(){return this.oneSet},e.prototype.isInterestedEntity=function(t){return this.isInterested(t.componentBits)},e.prototype.isInterested=function(e){if(0!=this.allSet.length)for(var n=0,i=this.allSet.length;n=e)return t;var i=!1;"-"==t.substr(0,1)&&(i=!0,t=t.substr(1));for(var r=e-n,o=0;o1?this.reverse(t.substring(1))+t.substring(0,1):t},t.cutOff=function(t,e,n,i){void 0===i&&(i=!0),e=Math.floor(e),n=Math.floor(n);var r=t.length;e>r&&(e=r);var o,s=e,a=e+n;return i?o=t.substring(0,s)+t.substr(a,r):(a=(s=r-1-e-n)+n,o=t.substring(0,s+1)+t.substr(a+1,r)),o},t.strReplace=function(t,e){for(var n=0,i=e.length;n",">",'"',""","'","'","®","®","©","©","™","™"],t}();!function(t){var e=function(){function t(){}return t.update=function(t){-1==t&&(t=Date.now()),-1==this._lastTime&&(this._lastTime=t);var e=0;(e=-1==t?(t-this._lastTime)/1e3:t)>this.maxDeltaTime&&(e=this.maxDeltaTime),this.totalTime+=e,this.deltaTime=e*this.timeScale,this.unscaledDeltaTime=e,this.timeSinceSceneLoad+=e,this.frameCount++,this._lastTime=t},t.sceneChanged=function(){this.timeSinceSceneLoad=0},t.checkEvery=function(t){return this.timeSinceSceneLoad/t>(this.timeSinceSceneLoad-this.deltaTime)/t},t.totalTime=0,t.unscaledDeltaTime=0,t.deltaTime=0,t.timeScale=1,t.maxDeltaTime=Number.MAX_VALUE,t.frameCount=0,t.timeSinceSceneLoad=0,t._lastTime=-1,t}();t.Time=e}(es||(es={}));var TimeUtils=function(){function t(){}return t.monthId=function(t){void 0===t&&(t=null);var e=(t=t||new Date).getFullYear(),n=t.getMonth()+1;return parseInt(e+(n<10?"0":"")+n)},t.dateId=function(t){void 0===t&&(t=null);var e=(t=t||new Date).getMonth()+1,n=e<10?"0":"",i=t.getDate(),r=i<10?"0":"";return parseInt(t.getFullYear()+n+e+r+i)},t.weekId=function(t,e){void 0===t&&(t=null),void 0===e&&(e=!0),t=t||new Date;var n=new Date;n.setTime(t.getTime()),n.setDate(1),n.setMonth(0);var i=n.getFullYear(),r=n.getDay();0==r&&(r=7);var o=!1;r<=4?(o=r>1,n.setDate(n.getDate()-(r-1))):n.setDate(n.getDate()+7-r+1);var s=this.diffDay(t,n,!1);if(s<0)return n.setDate(1),n.setMonth(0),n.setDate(n.getDate()-1),this.weekId(n,!1);var a=s/7,c=Math.floor(a)+1;if(53==c){n.setTime(t.getTime()),n.setDate(n.getDate()-1);var h=n.getDay();if(0==h&&(h=7),e&&(!o||h<4))return n.setFullYear(n.getFullYear()+1),n.setDate(1),n.setMonth(0),this.weekId(n,!1)}return parseInt(i+"00"+(c>9?"":"0")+c)},t.diffDay=function(t,e,n){void 0===n&&(n=!1);var i=(t.getTime()-e.getTime())/864e5;return n?Math.ceil(i):Math.floor(i)},t.getFirstDayOfWeek=function(t){var e=(t=t||new Date).getDay()||7;return new Date(t.getFullYear(),t.getMonth(),t.getDate()+1-e,0,0,0,0)},t.getFirstOfDay=function(t){return(t=t||new Date).setHours(0,0,0,0),t},t.getNextFirstOfDay=function(t){return new Date(this.getFirstOfDay(t).getTime()+864e5)},t.formatDate=function(t){var e=t.getFullYear(),n=t.getMonth()+1;n=n<10?"0"+n:n;var i=t.getDate();return e+"-"+n+"-"+(i=i<10?"0"+i:i)},t.formatDateTime=function(t){var e=t.getFullYear(),n=t.getMonth()+1;n=n<10?"0"+n:n;var i=t.getDate();i=i<10?"0"+i:i;var r=t.getHours(),o=t.getMinutes();o=o<10?"0"+o:o;var s=t.getSeconds();return e+"-"+n+"-"+i+" "+r+":"+o+":"+(s=s<10?"0"+s:s)},t.parseDate=function(t){var e=Date.parse(t);return isNaN(e)?new Date:new Date(Date.parse(t.replace(/-/g,"/")))},t.secondToTime=function(t,e,n){void 0===t&&(t=0),void 0===e&&(e=":"),void 0===n&&(n=!0);var i=Math.floor(t/3600),r=Math.floor(t%3600/60),o=Math.floor(t%3600%60),s=i.toString(),a=r.toString(),c=o.toString();return i<10&&(s="0"+s),r<10&&(a="0"+a),o<10&&(c="0"+c),n?s+e+a+e+c:a+e+c},t.timeToMillisecond=function(t,e){void 0===e&&(e=":");for(var n=t.split(e),i=0,r=n.length,o=0;o=1?(e.value=1,n=this._points.length-4):(e.value=t.MathHelper.clamp01(e.value)*this._curveCount,n=~~e,e.value-=n,n*=3),n},e.prototype.setControlPoint=function(e,n){if(e%3==0){var i=t.Vector2.subtract(n,this._points[e]);e>0&&this._points[e-1].add(i),e+1-Math.PI&&t<=Math.PI?t:(t%=2*Math.PI)<=-Math.PI?t+2*Math.PI:t>Math.PI?t-2*Math.PI:t},e.isPowerOfTwo=function(t){return t>0&&t%(t-1)==0},e.lerp=function(t,e,n){return t+(e-t)*this.clamp01(n)},e.lerpAngle=function(t,e,n){var i=this.repeat(e-t,360);return i>180&&(i-=360),t+i*this.clamp01(n)},e.lerpAngleRadians=function(t,e,n){var i=this.repeat(e-t,2*Math.PI);return i>Math.PI&&(i-=2*Math.PI),t+i*this.clamp01(n)},e.pingPong=function(t,e){return t=this.repeat(t,2*e),e-Math.abs(t-e)},e.signThreshold=function(t,e){return Math.abs(t)>=e?Math.sign(t):0},e.inverseLerp=function(t,e,n){if(te)return 1}else{if(nt)return 0}return(n-t)/(e-t)},e.lerpPrecise=function(t,e,n){return(1-n)*t+e*n},e.clamp=function(t,e,n){return tn?n:t},e.snap=function(t,e){return Math.round(t/e)*e},e.pointOnCirlce=function(n,i,r){var o=e.toRadians(r);return new t.Vector2(Math.cos(o)*o+n.x,Math.sin(o)*o+n.y)},e.isEven=function(t){return t%2==0},e.isOdd=function(t){return t%2!=0},e.roundWithRoundedAmount=function(t,e){var n=Math.round(t);return e.value=t-n*Math.round(t/n),n},e.clamp01=function(t){return t<0?0:t>1?1:t},e.angleBetweenVectors=function(t,e){return Math.atan2(e.y-t.y,e.x-t.x)},e.angleToVector=function(e,n){return new t.Vector2(Math.cos(e)*n,Math.sin(e)*n)},e.incrementWithWrap=function(t,e){return++t==e?0:t},e.decrementWithWrap=function(t,e){return--t<0?e-1:t},e.hypotenuse=function(t,e){return Math.sqrt(t*t+e*e)},e.closestPowerOfTwoGreaterThan=function(t){return t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,(t|=t>>16)+1},e.roundToNearest=function(t,e){return Math.round(t/e)*e},e.withinEpsilon=function(t,e){return void 0===e&&(e=this.Epsilon),Math.abs(t)180&&(n-=360),n},e.between=function(t,e,n){return t>=e&&t<=n},e.deltaAngleRadians=function(t,e){var n=this.repeat(e-t,2*Math.PI);return n>Math.PI&&(n-=2*Math.PI),n},e.repeat=function(t,e){return t-Math.floor(t/e)*e},e.rotateAround=function(e,n){var i=t.Time.totalTime*n,r=Math.cos(i),o=Math.sin(i);return new t.Vector2(e.x+r,e.y+o)},e.rotateAround2=function(e,n,i){i=this.toRadians(i);var r=Math.cos(i),o=Math.sin(i),s=r*(e.x-n.x)-o*(e.y-n.y)+n.x,a=o*(e.x-n.x)+r*(e.y-n.y)+n.y;return new t.Vector2(s,a)},e.pointOnCircle=function(e,n,i){var r=this.toRadians(i);return new t.Vector2(Math.cos(r)*n+e.x,Math.sin(r)*n+e.y)},e.pointOnCircleRadians=function(e,n,i){return new t.Vector2(Math.cos(i)*n+e.x,Math.sin(i)*n+e.y)},e.lissajou=function(e,n,i,r,o){void 0===e&&(e=2),void 0===n&&(n=3),void 0===i&&(i=1),void 0===r&&(r=1),void 0===o&&(o=0);var s=Math.sin(t.Time.totalTime*e+o)*i,a=Math.cos(t.Time.totalTime*n)*r;return new t.Vector2(s,a)},e.lissajouDamped=function(e,n,i,r,o,s,a){void 0===e&&(e=2),void 0===n&&(n=3),void 0===i&&(i=1),void 0===r&&(r=1),void 0===o&&(o=.5),void 0===s&&(s=0),void 0===a&&(a=5);var c=this.pingPong(t.Time.totalTime,a),h=Math.pow(Math.E,-s*c),u=h*Math.sin(t.Time.totalTime*e+o)*i,l=h*Math.cos(t.Time.totalTime*n)*r;return new t.Vector2(u,l)},e.hermite=function(t,e,n,i,r){return 0==r?t:1==r?n:(2*t-2*n+i+e)*(r*r*r)+(3*n-3*t-2*e-i)*(r*r)+e*r+t},e.isValid=function(t){return!Number.isNaN(t)&&!Number.isFinite(t)},e.Epsilon=1e-5,e.Rad2Deg=57.29578,e.Deg2Rad=.0174532924,e.PiOver2=Math.PI/2,e}();t.MathHelper=e}(es||(es={})),function(t){var e=function(){function t(){}return t.createOrthographicOffCenter=function(e,n,i,r,o,s,a){void 0===a&&(a=new t),a.m11=2/(n-e),a.m12=0,a.m13=0,a.m14=0,a.m21=0,a.m22=2/(r-i),a.m23=0,a.m24=0,a.m31=0,a.m32=0,a.m33=1/(o-s),a.m34=0,a.m41=(e+n)/(e-n),a.m42=(r+i)/(i-r),a.m43=o/(o-s),a.m44=1},t.multiply=function(e,n,i){void 0===i&&(i=new t);var r=e.m11*n.m11+e.m12*n.m21+e.m13*n.m31+e.m14*n.m41,o=e.m11*n.m12+e.m12*n.m22+e.m13*n.m32+e.m14*n.m42,s=e.m11*n.m13+e.m12*n.m23+e.m13*n.m33+e.m14*n.m43,a=e.m11*n.m14+e.m12*n.m24+e.m13*n.m34+e.m14*n.m44,c=e.m21*n.m11+e.m22*n.m21+e.m23*n.m31+e.m24*n.m41,h=e.m21*n.m12+e.m22*n.m22+e.m23*n.m32+e.m24*n.m42,u=e.m21*n.m13+e.m22*n.m23+e.m23*n.m33+e.m24*n.m43,l=e.m21*n.m14+e.m22*n.m24+e.m23*n.m34+e.m24*n.m44,p=e.m31*n.m11+e.m32*n.m21+e.m33*n.m31+e.m34*n.m41,f=e.m31*n.m12+e.m32*n.m22+e.m33*n.m32+e.m34*n.m42,d=e.m31*n.m13+e.m32*n.m23+e.m33*n.m33+e.m34*n.m43,m=e.m31*n.m14+e.m32*n.m24+e.m33*n.m34+e.m34*n.m44,y=e.m41*n.m11+e.m42*n.m21+e.m43*n.m31+e.m44*n.m41,g=e.m41*n.m12+e.m42*n.m22+e.m43*n.m32+e.m44*n.m42,v=e.m41*n.m13+e.m42*n.m23+e.m43*n.m33+e.m44*n.m43,_=e.m41*n.m14+e.m42*n.m24+e.m43*n.m34+e.m44*n.m44;i.m11=r,i.m12=o,i.m13=s,i.m14=a,i.m21=c,i.m22=h,i.m23=u,i.m24=l,i.m31=p,i.m32=f,i.m33=d,i.m34=m,i.m41=y,i.m42=g,i.m43=v,i.m44=_},t}();t.Matrix=e}(es||(es={})),function(t){var e=function(){function e(t,e,n,i,r,o){this.m11=0,this.m12=0,this.m21=0,this.m22=0,this.m31=0,this.m32=0,this.m11=t,this.m12=e,this.m21=n,this.m22=i,this.m31=r,this.m32=o}return Object.defineProperty(e,"identity",{get:function(){return new e(1,0,0,1,0,0)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"translation",{get:function(){return new t.Vector2(this.m31,this.m32)},set:function(t){this.m31=t.x,this.m32=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"rotation",{get:function(){return Math.atan2(this.m21,this.m11)},set:function(t){var e=Math.cos(t),n=Math.sin(t);this.m11=e,this.m12=n,this.m21=-n,this.m22=e},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"rotationDegrees",{get:function(){return t.MathHelper.toDegrees(this.rotation)},set:function(e){this.rotation=t.MathHelper.toRadians(e)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"scale",{get:function(){return new t.Vector2(this.m11,this.m22)},set:function(t){this.m11=t.x,this.m22=t.y},enumerable:!0,configurable:!0}),e.createRotation=function(t){var e=this.identity,n=Math.cos(t),i=Math.sin(t);return e.m11=n,e.m12=i,e.m21=-i,e.m22=n,e},e.createScale=function(t,e){var n=this.identity;return n.m11=t,n.m12=0,n.m21=0,n.m22=e,n.m31=0,n.m32=0,n},e.createTranslation=function(t,e){var n=this.identity;return n.m11=1,n.m12=0,n.m21=0,n.m22=1,n.m31=t,n.m32=e,n},e.invert=function(t){var e=1/t.determinant(),n=this.identity;return n.m11=t.m22*e,n.m12=-t.m12*e,n.m21=-t.m21*e,n.m22=t.m11*e,n.m31=(t.m32*t.m21-t.m31*t.m22)*e,n.m32=-(t.m32*t.m11-t.m31*t.m12)*e,n},e.prototype.add=function(t){return this.m11+=t.m11,this.m12+=t.m12,this.m21+=t.m21,this.m22+=t.m22,this.m31+=t.m31,this.m32+=t.m32,this},e.prototype.substract=function(t){return this.m11-=t.m11,this.m12-=t.m12,this.m21-=t.m21,this.m22-=t.m22,this.m31-=t.m31,this.m32-=t.m32,this},e.prototype.divide=function(t){return this.m11/=t.m11,this.m12/=t.m12,this.m21/=t.m21,this.m22/=t.m22,this.m31/=t.m31,this.m32/=t.m32,this},e.prototype.multiply=function(t){var e=this.m11*t.m11+this.m12*t.m21,n=this.m11*t.m12+this.m12*t.m22,i=this.m21*t.m11+this.m22*t.m21,r=this.m21*t.m12+this.m22*t.m22,o=this.m31*t.m11+this.m32*t.m21+t.m31,s=this.m31*t.m12+this.m32*t.m22+t.m32;return this.m11=e,this.m12=n,this.m21=i,this.m22=r,this.m31=o,this.m32=s,this},e.prototype.determinant=function(){return this.m11*this.m22-this.m12*this.m21},e.lerp=function(t,e,n){return t.m11=t.m11+(e.m11-t.m11)*n,t.m12=t.m12+(e.m12-t.m12)*n,t.m21=t.m21+(e.m21-t.m21)*n,t.m22=t.m22+(e.m22-t.m22)*n,t.m31=t.m31+(e.m31-t.m31)*n,t.m32=t.m32+(e.m32-t.m32)*n,t},e.transpose=function(t){var e=this.identity;return e.m11=t.m11,e.m12=t.m21,e.m21=t.m12,e.m22=t.m22,e.m31=0,e.m32=0,e},e.prototype.mutiplyTranslation=function(n,i){var r=e.createTranslation(n,i);return t.MatrixHelper.mutiply(this,r)},e.prototype.equals=function(t){return this==t},e.toMatrix=function(e){var n=new t.Matrix;return n.m11=e.m11,n.m12=e.m12,n.m13=0,n.m14=0,n.m21=e.m21,n.m22=e.m22,n.m23=0,n.m24=0,n.m31=0,n.m32=0,n.m33=1,n.m34=0,n.m41=e.m31,n.m42=e.m32,n.m43=0,n.m44=1,n},e.prototype.toString=function(){return"{m11:"+this.m11+" m12:"+this.m12+" m21:"+this.m21+" m22:"+this.m22+" m31:"+this.m31+" m32:"+this.m32+"}"},e}();t.Matrix2D=e}(es||(es={})),function(t){var e=function(){function e(){}return e.add=function(e,n){var i=t.Matrix2D.identity;return i.m11=e.m11+n.m11,i.m12=e.m12+n.m12,i.m21=e.m21+n.m21,i.m22=e.m22+n.m22,i.m31=e.m31+n.m31,i.m32=e.m32+n.m32,i},e.divide=function(e,n){var i=t.Matrix2D.identity;return i.m11=e.m11/n.m11,i.m12=e.m12/n.m12,i.m21=e.m21/n.m21,i.m22=e.m22/n.m22,i.m31=e.m31/n.m31,i.m32=e.m32/n.m32,i},e.mutiply=function(e,n){var i=t.Matrix2D.identity;if(n instanceof t.Matrix2D){var r=e.m11*n.m11+e.m12*n.m21,o=n.m11*n.m12+e.m12*n.m22,s=e.m21*n.m11+e.m22*n.m21,a=e.m21*n.m12+e.m22*n.m22,c=e.m31*n.m11+e.m32*n.m21+n.m31,h=e.m31*n.m12+e.m32*n.m22+n.m32;i.m11=r,i.m12=o,i.m21=s,i.m22=a,i.m31=c,i.m32=h}else"number"==typeof n&&(i.m11=e.m11*n,i.m12=e.m12*n,i.m21=e.m21*n,i.m22=e.m22*n,i.m31=e.m31*n,i.m32=e.m32*n);return i},e.subtract=function(e,n){var i=t.Matrix2D.identity;return i.m11=e.m11-n.m11,i.m12=e.m12-n.m12,i.m21=e.m21-n.m21,i.m22=e.m22-n.m22,i.m31=e.m31-n.m31,i.m32=e.m32-n.m32,i},e}();t.MatrixHelper=e}(es||(es={})),function(t){var e=function(){function e(t,e,n,i){void 0===t&&(t=0),void 0===e&&(e=0),void 0===n&&(n=0),void 0===i&&(i=0),this.x=0,this.y=0,this.width=0,this.height=0,this.x=t,this.y=e,this.width=n,this.height=i}return Object.defineProperty(e,"empty",{get:function(){return new e},enumerable:!0,configurable:!0}),Object.defineProperty(e,"maxRect",{get:function(){return new e(Number.MIN_VALUE/2,Number.MIN_VALUE/2,Number.MAX_VALUE,Number.MAX_VALUE)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"left",{get:function(){return this.x},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"right",{get:function(){return this.x+this.width},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"top",{get:function(){return this.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"bottom",{get:function(){return this.y+this.height},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"max",{get:function(){return new t.Vector2(this.right,this.bottom)},enumerable:!0,configurable:!0}),e.prototype.isEmpty=function(){return 0==this.width&&0==this.height&&0==this.x&&0==this.y},Object.defineProperty(e.prototype,"location",{get:function(){return new t.Vector2(this.x,this.y)},set:function(t){this.x=t.x,this.y=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"size",{get:function(){return new t.Vector2(this.width,this.height)},set:function(t){this.width=t.x,this.height=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"center",{get:function(){return new t.Vector2(this.x+this.width/2,this.y+this.height/2)},enumerable:!0,configurable:!0}),e.fromMinMax=function(t,n,i,r){return new e(t,n,i-t,r-n)},e.rectEncompassingPoints=function(t){for(var e=Number.POSITIVE_INFINITY,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY,r=Number.NEGATIVE_INFINITY,o=0;oi&&(i=s.x),s.yr&&(r=s.y)}return this.fromMinMax(e,n,i,r)},e.prototype.getSide=function(e){switch(e){case t.Edge.top:return this.top;case t.Edge.bottom:return this.bottom;case t.Edge.left:return this.left;case t.Edge.right:return this.right;default:throw new Error("Argument Out Of Range")}},e.prototype.contains=function(t,e){return this.x<=t&&tthis.x+this.width)return!1}else{var i=1/t.direction.x,r=(this.x-t.start.x)*i,o=(this.x+this.width-t.start.x)*i;if(r>o){var s=r;r=o,o=s}if(e.value=Math.max(r,e.value),n=Math.min(o,n),e.value>n)return!1}if(Math.abs(t.direction.y)<1e-6){if(t.start.ythis.y+this.height)return!1}else{var a=1/t.direction.y,c=(this.y-t.start.y)*a,h=(this.y+this.height-t.start.y)*a;if(c>h){var u=c;c=h,h=u}if(e.value=Math.max(c,e.value),n=Math.max(h,n),e.value>n)return!1}return!0},e.prototype.containsRect=function(t){return this.x<=t.x&&t.x0?this.x:this.x+t,i.y=n>0?this.y:this.y+n,i.width=t>0?t+this.width:this.width-t,i.height=n>0?n+this.height:this.height-n,i},e.prototype.collisionCheck=function(t,e,n){e.value=n.value=0;var i=t.x-(this.x+this.width),r=t.x+t.width-this.x,o=t.y-(this.y+this.height),s=t.y+t.height-this.y;return!(i>0||r<0||o>0||s<0)&&(e.value=Math.abs(i)=l||Math.abs(u)>=p)return t.Vector2.zero;var f=h>0?l-h:-l-h,d=u>0?p-u:-p-u;return new t.Vector2(f,d)},e.prototype.equals=function(t){return this===t},e.prototype.getHashCode=function(){return this.x^this.y^this.width^this.height},e.prototype.clone=function(){return new e(this.x,this.y,this.width,this.height)},e}();t.Rectangle=e}(es||(es={})),function(t){var e=function(){function t(){this.remainder=0}return t.prototype.update=function(t){this.remainder+=t;var e=Math.floor(Math.trunc(this.remainder));return this.remainder-=e,t=e},t.prototype.reset=function(){this.remainder=0},t}();t.SubpixelFloat=e}(es||(es={})),function(t){var e=function(){function e(){this._x=new t.SubpixelFloat,this._y=new t.SubpixelFloat}return e.prototype.update=function(t){t.x=this._x.update(t.x),t.y=this._y.update(t.y)},e.prototype.reset=function(){this._x.reset(),this._y.reset()},e}();t.SubpixelVector2=e}(es||(es={})),function(t){var e=function(){function e(e){this._activeTriggerIntersections=new t.HashSet,this._previousTriggerIntersections=new t.HashSet,this._tempTriggerList=[],this._entity=e}return e.prototype.update=function(){for(var e=this._entity.getComponents(t.Collider),n=0;n1)return!1;var u=(c.x*o.y-c.y*o.x)/a;return!(u<0||u>1)},n.lineToLineIntersection=function(e,n,i,r,o){void 0===o&&(o=new t.Vector2),o.x=0,o.y=0;var s=t.Vector2.subtract(n,e),a=t.Vector2.subtract(r,i),c=s.x*a.y-s.y*a.x;if(0==c)return!1;var h=t.Vector2.subtract(i,e),u=(h.x*a.y-h.y*a.x)/c;if(u<0||u>1)return!1;var l=(h.x*s.y-h.y*s.x)/c;if(l<0||l>1)return!1;var p=t.Vector2.add(e,new t.Vector2(u*s.x,u*s.y));return o.x=p.x,o.y=p.y,!0},n.closestPointOnLine=function(e,n,i){var r=t.Vector2.subtract(n,e),o=t.Vector2.subtract(i,e),s=t.Vector2.dot(o,r)/t.Vector2.dot(r,r);return s=t.MathHelper.clamp(s,0,1),t.Vector2.add(e,new t.Vector2(r.x*s,r.y*s))},n.circleToCircle=function(e,n,i,r){return t.Vector2.distanceSquared(e,i)<(n+r)*(n+r)},n.circleToLine=function(e,n,i,r){return t.Vector2.distanceSquared(e,this.closestPointOnLine(i,r,e))=t&&r.y>=e&&r.x=t+i&&(s|=e.right),o.y=n+r&&(s|=e.bottom),s},n}();t.Collisions=n}(es||(es={})),function(t){var e=function(){function e(e,n,i,r,o){this.fraction=0,this.distance=0,this.point=t.Vector2.zero,this.normal=t.Vector2.zero,this.collider=e,this.fraction=n,this.distance=i,this.point=r,this.centroid=t.Vector2.zero}return e.prototype.setValues=function(t,e,n,i){this.collider=t,this.fraction=e,this.distance=n,this.point=i},e.prototype.setValuesNonCollider=function(t,e,n,i){this.fraction=t,this.distance=e,this.point=n,this.normal=i},e.prototype.reset=function(){this.collider=null,this.fraction=this.distance=0},e.prototype.toString=function(){return"[RaycastHit] fraction: "+this.fraction+", distance: "+this.distance+", normal: "+this.normal+", centroid: "+this.centroid+", point: "+this.point},e}();t.RaycastHit=e}(es||(es={})),function(t){var e=function(){function e(){}return e.reset=function(){this._spatialHash=new t.SpatialHash(this.spatialHashCellSize),this._hitArray[0].reset(),this._colliderArray[0]=null},e.clear=function(){this._spatialHash.clear()},e.overlapCircle=function(t,n,i){return void 0===i&&(i=e.allLayers),this._colliderArray[0]=null,this._spatialHash.overlapCircle(t,n,this._colliderArray,i),this._colliderArray[0]},e.overlapCircleAll=function(t,e,n,i){if(void 0===i&&(i=-1),0!=n.length)return this._spatialHash.overlapCircle(t,e,n,i);console.warn("传入了一个空的结果数组。不会返回任何结果")},e.boxcastBroadphase=function(t,e){return void 0===e&&(e=this.allLayers),this._spatialHash.aabbBroadphase(t,null,e)},e.boxcastBroadphaseExcludingSelf=function(t,e,n){return void 0===n&&(n=this.allLayers),this._spatialHash.aabbBroadphase(e,t,n)},e.boxcastBroadphaseExcludingSelfNonRect=function(t,e){void 0===e&&(e=this.allLayers);var n=t.bounds.clone();return this._spatialHash.aabbBroadphase(n,t,e)},e.boxcastBroadphaseExcludingSelfDelta=function(t,n,i,r){void 0===r&&(r=e.allLayers);var o=t.bounds.clone().getSweptBroadphaseBounds(n,i);return this._spatialHash.aabbBroadphase(o,t,r)},e.addCollider=function(t){e._spatialHash.register(t)},e.removeCollider=function(t){e._spatialHash.remove(t)},e.updateCollider=function(t){this._spatialHash.remove(t),this._spatialHash.register(t)},e.linecast=function(t,n,i){return void 0===i&&(i=e.allLayers),this._hitArray[0].reset(),this.linecastAll(t,n,this._hitArray,i),this._hitArray[0]},e.linecastAll=function(t,n,i,r){return void 0===r&&(r=e.allLayers),0==i.length?(console.warn("传入了一个空的hits数组。没有点击会被返回"),0):this._spatialHash.linecast(t,n,i,r)},e.overlapRectangle=function(t,n){return void 0===n&&(n=e.allLayers),this._colliderArray[0]=null,this._spatialHash.overlapRectangle(t,this._colliderArray,n),this._colliderArray[0]},e.overlapRectangleAll=function(t,n,i){return void 0===i&&(i=e.allLayers),0==n.length?(console.warn("传入了一个空的结果数组。不会返回任何结果"),0):this._spatialHash.overlapRectangle(t,n,i)},e.gravity=new t.Vector2(0,300),e.spatialHashCellSize=100,e.allLayers=-1,e.raycastsHitTriggers=!1,e.raycastsStartInColliders=!1,e._hitArray=[new t.RaycastHit],e._colliderArray=[null],e}();t.Physics=e}(es||(es={})),function(t){var e=function(){return function(e,n){this.start=e,this.end=n,this.direction=t.Vector2.subtract(this.end,this.start)}}();t.Ray2D=e}(es||(es={})),function(t){var e=function(){function e(e){void 0===e&&(e=100),this.gridBounds=new t.Rectangle,this._overlapTestBox=new t.Box(0,0),this._overlapTestCircle=new t.Circle(0),this._cellDict=new n,this._tempHashSet=new Set,this._cellSize=e,this._inverseCellSize=1/this._cellSize,this._raycastParser=new i}return e.prototype.register=function(e){var n=e.bounds.clone();e.registeredPhysicsBounds=n;var i=this.cellCoords(n.x,n.y),r=this.cellCoords(n.right,n.bottom);this.gridBounds.contains(i.x,i.y)||(this.gridBounds=t.RectangleExt.union(this.gridBounds,i)),this.gridBounds.contains(r.x,r.y)||(this.gridBounds=t.RectangleExt.union(this.gridBounds,r));for(var o=i.x;o<=r.x;o++)for(var s=i.y;s<=r.y;s++){this.cellAtPosition(o,s,!0).push(e)}},e.prototype.remove=function(e){for(var n=e.registeredPhysicsBounds.clone(),i=this.cellCoords(n.x,n.y),r=this.cellCoords(n.right,n.bottom),o=i.x;o<=r.x;o++)for(var s=i.y;s<=r.y;s++){var a=this.cellAtPosition(o,s);t.Insist.isNotNull(a,"从不存在碰撞器的单元格中移除碰撞器: ["+e+"]"),null!=a&&new t.List(a).remove(e)}},e.prototype.removeWithBruteForce=function(t){this._cellDict.remove(t)},e.prototype.clear=function(){this._cellDict.clear()},e.prototype.aabbBroadphase=function(e,n,i){this._tempHashSet.clear();for(var r=this.cellCoords(e.x,e.y),o=this.cellCoords(e.right,e.bottom),s=r.x;s<=o.x;s++)for(var a=r.y;a<=o.y;a++){var c=this.cellAtPosition(s,a);if(null!=c)for(var h=0;h>>0},e.prototype.clear=function(){this._store.clear()},e}();t.NumberDictionary=n;var i=function(){function e(){this._tempHit=new t.RaycastHit,this._checkedColliders=[],this._cellHits=[]}return e.prototype.start=function(t,e,n){this._ray=t,this._hits=e,this._layerMask=n,this.hitCounter=0},e.prototype.checkRayIntersection=function(n,i,r){for(var o=new t.Ref(0),s=0;s=this.points.length?this.points[0]:this.points[i+1];var o=t.Vector2Ext.perpendicular(r,e);t.Vector2Ext.normalize(o),this._edgeNormals[i]=o}},n.buildSymmetricalPolygon=function(e,n){for(var i=new Array(e),r=0;rr&&(r=s,i=o)}return e[i]},n.getClosestPointOnPolygonToPoint=function(e,n,i,r){i.value=Number.MAX_VALUE,r.x=0,r.y=0;for(var o=t.Vector2.zero,s=0,a=0;at.y!=this.points[i].y>t.y&&t.x<(this.points[i].x-this.points[n].x)*(t.y-this.points[n].y)/(this.points[i].y-this.points[n].y)+this.points[n].x&&(e=!e);return e},n.prototype.pointCollidesWithShape=function(e,n){return t.ShapeCollisionsPoint.pointToPoly(e,this,n)},n}(t.Shape);t.Polygon=e}(es||(es={})),function(t){var e=function(e){function n(t,i){var r=e.call(this,n.buildBox(t,i),!0)||this;return r.width=t,r.height=i,r}return __extends(n,e),n.buildBox=function(e,n){var i=e/2,r=n/2,o=new Array(4);return o[0]=new t.Vector2(-i,-r),o[1]=new t.Vector2(i,-r),o[2]=new t.Vector2(i,r),o[3]=new t.Vector2(-i,r),o},n.prototype.updateBox=function(e,n){this.width=e,this.height=n;var i=e/2,r=n/2;this.points[0]=new t.Vector2(-i,-r),this.points[1]=new t.Vector2(i,-r),this.points[2]=new t.Vector2(i,r),this.points[3]=new t.Vector2(-i,r);for(var o=0;o1)return!1;var a,c=t.Vector2.add(s.start,t.Vector2.multiply(s.direction,new t.Vector2(r.value))),h=0;c.xn.bounds.right&&(h|=1),c.yn.bounds.bottom&&(h|=2);var u=a+h;return 3==u&&console.log("m == 3. corner "+t.Time.frameCount),!0},e.corner=function(e,n){var i=new t.Vector2;return i.x=0==(1&n)?e.right:e.left,i.y=0==(1&n)?e.bottom:e.top,i},e.testCircleBox=function(e,n,i){i=n.bounds.getClosestPointOnRectangleToPoint(e.position);var r=t.Vector2.subtract(i,e.position);return t.Vector2.dot(r,r)<=e.radius*e.radius},e}();t.RealtimeCollisions=e}(es||(es={})),function(t){var e=function(){function e(){}return e.boxToBox=function(e,n,i){var r=this.minkowskiDifference(e,n);return!!r.contains(0,0)&&(i.minimumTranslationVector=r.getClosestPointOnBoundsToOrigin(),!i.minimumTranslationVector.equals(t.Vector2.zero)&&(i.normal=new t.Vector2(-i.minimumTranslationVector.x,-i.minimumTranslationVector.y),i.normal.normalize(),!0))},e.boxToBoxCast=function(e,n,i,r){var o=this.minkowskiDifference(e,n);if(o.contains(0,0)){var s=o.getClosestPointOnBoundsToOrigin();return!s.equals(t.Vector2.zero)&&(r.normal=new t.Vector2(-s.x),r.normal.normalize(),r.distance=0,r.fraction=0,!0)}var a=new t.Ray2D(t.Vector2.zero,new t.Vector2(-i.x)),c=new t.Ref(0);return!!(o.rayIntersects(a,c)&&c.value<=1)&&(r.fraction=c.value,r.distance=i.length()*c.value,r.normal=new t.Vector2(-i.x,-i.y),r.normal.normalize(),r.centroid=t.Vector2.add(e.bounds.center,t.Vector2.multiply(i,new t.Vector2(c.value))),!0)},e.minkowskiDifference=function(e,n){var i=t.Vector2.subtract(e.position,t.Vector2.add(e.bounds.location,new t.Vector2(e.bounds.size.x/2,e.bounds.size.y/2))),r=t.Vector2.subtract(t.Vector2.add(e.bounds.location,i),n.bounds.max),o=t.Vector2.add(e.bounds.size,n.bounds.size);return new t.Rectangle(r.x,r.y,o.x,o.y)},e}();t.ShapeCollisionsBox=e}(es||(es={})),function(t){var e=function(){function e(){}return e.circleToCircle=function(e,n,i){void 0===i&&(i=new t.CollisionResult);var r=t.Vector2.distanceSquared(e.position,n.position),o=e.radius+n.radius;if(re.radius*e.radius&&!c)return!1;if(c)r=t.Vector2.multiply(i.normal,new t.Vector2(Math.sqrt(s.value)-e.radius));else if(0==s.value)r=new t.Vector2(i.normal.x*e.radius,i.normal.y*e.radius);else{var h=Math.sqrt(s.value);r=t.Vector2.subtract(new t.Vector2(-1),t.Vector2.subtract(o,a)).multiply(new t.Vector2((e.radius-h)/h))}return i.minimumTranslationVector=r,i.point=t.Vector2.add(a,n.position),!0},e.closestPointOnLine=function(e,n,i){var r=t.Vector2.subtract(n,e),o=t.Vector2.subtract(i,e),s=t.Vector2.dot(o,r)/t.Vector2.dot(r,r);return s=t.MathHelper.clamp(s,0,1),t.Vector2.add(e,t.Vector2.multiply(r,new t.Vector2(s)))},e}();t.ShapeCollisionsCircle=e}(es||(es={})),function(t){var e=function(){function e(){}return e.lineToPoly=function(e,n,i,r){void 0===r&&(r=new t.RaycastHit);for(var o=t.Vector2.zero,s=t.Vector2.zero,a=Number.MAX_VALUE,c=!1,h=i.points.length-1,u=0;u1)return!1;var l=(h.x*s.y-h.y*s.x)/c;return!(l<0||l>1)&&(t.Vector2.add(e,t.Vector2.multiply(new t.Vector2(u),s)),!0)},e.lineToCircle=function(e,n,i,r){var o=t.Vector2.distance(e,n),s=t.Vector2.divide(t.Vector2.subtract(n,e),new t.Vector2(o)),a=t.Vector2.subtract(e,i.position),c=t.Vector2.dot(a,s),h=t.Vector2.dot(a,a)-i.radius*i.radius;if(h>0&&c>0)return!1;var u=c*c-h;return!(u<0)&&(r.fraction=-c-Math.sqrt(u),r.fraction<0&&(r.fraction=0),r.point=t.Vector2.add(e,t.Vector2.multiply(new t.Vector2(r.fraction),s)),r.distance=t.Vector2.distance(e,r.point),r.normal=t.Vector2.normalize(t.Vector2.subtract(r.point,i.position)),r.fraction=r.distance/o,!0)},e}();t.ShapeCollisionsLine=e}(es||(es={})),function(t){var e=function(){function e(){}return e.pointToCircle=function(e,n,i){var r=t.Vector2.distanceSquared(e,n.position),o=1+n.radius;if(r0&&(o=!1),!o)return!1;(y=Math.abs(y))r.value&&(r.value=o)},e.intervalDistance=function(t,e,n,i){return t=0;o--)(e=r[o].func).call.apply(e,__spread([r[o].context],n))},n}();t.Emitter=n}(es||(es={})),function(t){!function(t){t[t.top=0]="top",t[t.bottom=1]="bottom",t[t.left=2]="left",t[t.right=3]="right"}(t.Edge||(t.Edge={}))}(es||(es={})),function(t){var e=function(){function t(){}return t.default=function(){return new t},t.prototype.equals=function(t,e){return"function"==typeof t.equals?t.equals(e):t===e},t.prototype.getHashCode=function(t){var e=this;if("number"==typeof t)return this._getHashCodeForNumber(t);if("string"==typeof t)return this._getHashCodeForString(t);var n=385229220;return this.forOwn(t,function(t){"number"==typeof t?n+=e._getHashCodeForNumber(t):"string"==typeof t?n+=e._getHashCodeForString(t):"object"==typeof t&&e.forOwn(t,function(){n+=e.getHashCode(t)})}),n},t.prototype._getHashCodeForNumber=function(t){return t},t.prototype._getHashCodeForString=function(t){for(var e=385229220,n=0;n>7,n+=n<<3,n^=n>>17,n+=n<<5},t}();t.Hash=e}(es||(es={})),function(t){var e=function(){return function(t){this.value=t}}();t.Ref=e}(es||(es={})),function(t){var e=function(){function e(){}return Object.defineProperty(e,"size",{get:function(){return new t.Vector2(this.width,this.height)},enumerable:!0,configurable:!0}),Object.defineProperty(e,"center",{get:function(){return new t.Vector2(this.width/2,this.height/2)},enumerable:!0,configurable:!0}),e}();t.Screen=e}(es||(es={})),function(t){var e=function(){function t(){}return t.prototype.update=function(t){this.remainder+=t;var e=Math.trunc(this.remainder);return this.remainder-=e,e},t.prototype.reset=function(){this.remainder=0},t}();t.SubpixelNumber=e}(es||(es={})),function(t){var e=function(){function e(){this.triangleIndices=[],this._triPrev=new Array(12),this._triNext=new Array(12)}return e.testPointTriangle=function(e,n,i,r){return!(t.Vector2Ext.cross(t.Vector2.subtract(e,n),t.Vector2.subtract(i,n))<0)&&(!(t.Vector2Ext.cross(t.Vector2.subtract(e,i),t.Vector2.subtract(r,i))<0)&&!(t.Vector2Ext.cross(t.Vector2.subtract(e,r),t.Vector2.subtract(n,r))<0))},e.prototype.triangulate=function(n,i){void 0===i&&(i=!0);var r=n.length;this.initialize(r);for(var o=0,s=0;r>3&&o<500;){o++;var a=!0,c=n[this._triPrev[s]],h=n[s],u=n[this._triNext[s]];if(t.Vector2Ext.isTriangleCCW(c,h,u)){var l=this._triNext[this._triNext[s]];do{if(e.testPointTriangle(n[l],c,h,u)){a=!1;break}l=this._triNext[l]}while(l!=this._triPrev[s])}else a=!1;a?(this.triangleIndices.push(this._triPrev[s]),this.triangleIndices.push(s),this.triangleIndices.push(this._triNext[s]),this._triNext[this._triPrev[s]]=this._triNext[s],this._triPrev[this._triNext[s]]=this._triPrev[s],r--,s=this._triPrev[s]):s=this._triNext[s]}this.triangleIndices.push(this._triPrev[s]),this.triangleIndices.push(s),this.triangleIndices.push(this._triNext[s]),i||this.triangleIndices.reverse()},e.prototype.initialize=function(t){this.triangleIndices.length=0,this._triNext.length>8&255]+e[t>>16&255]+e[t>>24&255]+"-"+e[255&n]+e[n>>8&255]+"-"+e[n>>16&15|64]+e[n>>24&255]+"-"+e[63&i|128]+e[i>>8&255]+"-"+e[i>>16&255]+e[i>>24&255]+e[255&r]+e[r>>8&255]+e[r>>16&255]+e[r>>24&255]},t}();t.UUID=n}(es||(es={})),function(t){t.getClassName=function(t){return t.className||t.name}}(es||(es={})),function(t){var e,n=function(){function t(t){void 0===t&&(t=i),this.getSystemTime=t,this._stopDuration=0,this._completeSlices=[]}return t.prototype.getState=function(){return void 0===this._startSystemTime?e.IDLE:void 0===this._stopSystemTime?e.RUNNING:e.STOPPED},t.prototype.isIdle=function(){return this.getState()===e.IDLE},t.prototype.isRunning=function(){return this.getState()===e.RUNNING},t.prototype.isStopped=function(){return this.getState()===e.STOPPED},t.prototype.slice=function(){return this.recordPendingSlice()},t.prototype.getCompletedSlices=function(){return Array.from(this._completeSlices)},t.prototype.getCompletedAndPendingSlices=function(){return __spread(this._completeSlices,[this.getPendingSlice()])},t.prototype.getPendingSlice=function(){return this.calculatePendingSlice()},t.prototype.getTime=function(){return this.caculateStopwatchTime()},t.prototype.reset=function(){this._startSystemTime=this._pendingSliceStartStopwatchTime=this._stopSystemTime=void 0,this._stopDuration=0,this._completeSlices=[]},t.prototype.start=function(t){if(void 0===t&&(t=!1),t&&this.reset(),void 0!==this._stopSystemTime){var e=(n=this.getSystemTime())-this._stopSystemTime;this._stopDuration+=e,this._stopSystemTime=void 0}else if(void 0===this._startSystemTime){var n=this.getSystemTime();this._startSystemTime=n,this._pendingSliceStartStopwatchTime=0}},t.prototype.stop=function(t){if(void 0===t&&(t=!1),void 0===this._startSystemTime)return 0;var e=this.getSystemTimeOfCurrentStopwatchTime();return t&&this.recordPendingSlice(this.caculateStopwatchTime(e)),this._stopSystemTime=e,this.getTime()},t.prototype.calculatePendingSlice=function(t){return void 0===this._pendingSliceStartStopwatchTime?Object.freeze({startTime:0,endTime:0,duration:0}):(void 0===t&&(t=this.getTime()),Object.freeze({startTime:this._pendingSliceStartStopwatchTime,endTime:t,duration:t-this._pendingSliceStartStopwatchTime}))},t.prototype.caculateStopwatchTime=function(t){return void 0===this._startSystemTime?0:(void 0===t&&(t=this.getSystemTimeOfCurrentStopwatchTime()),t-this._startSystemTime-this._stopDuration)},t.prototype.getSystemTimeOfCurrentStopwatchTime=function(){return void 0===this._stopSystemTime?this.getSystemTime():this._stopSystemTime},t.prototype.recordPendingSlice=function(t){if(void 0!==this._pendingSliceStartStopwatchTime){void 0===t&&(t=this.getTime());var e=this.calculatePendingSlice(t);return this._pendingSliceStartStopwatchTime=e.endTime,this._completeSlices.push(e),e}return this.calculatePendingSlice()},t}();t.Stopwatch=n,function(t){t.IDLE="IDLE",t.RUNNING="RUNNING",t.STOPPED="STOPPED"}(e||(e={})),t.setDefaultSystemTimeGetter=function(t){void 0===t&&(t=Date.now),i=t};var i=Date.now}(es||(es={})),function(t){var e=function(){function t(t){void 0===t&&(t=64),this.size_=0,this.length=0,this.array=[],this.length=t}return t.prototype.removeAt=function(t){var e=this.array[t];return this.array[t]=this.array[--this.size_],this.array[this.size_]=null,e},t.prototype.remove=function(t){var e,n=this.size_;for(e=0;e0){var t=this.array[--this.size_];return this.array[this.size_]=null,t}return null},t.prototype.contains=function(t){var e,n;for(e=0,n=this.size_;n>e;e++)if(t===this.array[e])return!0;return!1},t.prototype.removeAll=function(t){var e,n,i,r,o=!1;for(e=0,i=t.size();e=this.length)throw new Error("ArrayIndexOutOfBoundsException");return this.array[t]},t.prototype.safeGet=function(t){return t>=this.length&&this.grow(7*t/4+1),this.array[t]},t.prototype.size=function(){return this.size_},t.prototype.getCapacity=function(){return this.length},t.prototype.isIndexWithinBounds=function(t){return t=this.length&&this.grow(2*t),this.size_=t+1,this.array[t]=e},t.prototype.grow=function(t){void 0===t&&(t=1+~~(3*this.length/2)),this.length=~~t},t.prototype.ensureCapacity=function(t){t>=this.length&&this.grow(2*t)},t.prototype.clear=function(){var t,e;for(t=0,e=this.size_;te;e++)this.add(t.get(e))},t}();t.Bag=e}(es||(es={})),function(t){var e=function(){function e(e){void 0===e&&(e=1),this._freeValueCellIndex=0,this._collisions=0,this._valuesInfo=new Array(e),this._values=new Array(e),this._buckets=new Array(t.HashHelpers.getPrime(e))}return e.prototype.getValuesArray=function(t){return t.value=this._freeValueCellIndex,this._values},Object.defineProperty(e.prototype,"valuesArray",{get:function(){return this._values},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"count",{get:function(){return this._freeValueCellIndex},enumerable:!0,configurable:!0}),e.prototype.add=function(t,e){if(!this.addValue(t,e,{value:0}))throw new Error("key 已经存在")},e.prototype.addValue=function(i,r,o){var s=t.HashHelpers.getHashCode(i),a=e.reduce(s,this._buckets.length);if(this._freeValueCellIndex==this._values.length){var c=t.HashHelpers.expandPrime(this._freeValueCellIndex);this._values.length=c,this._valuesInfo.length=c}var h=t.NumberExtension.toNumber(this._buckets[a])-1;if(-1==h)this._valuesInfo[this._freeValueCellIndex]=new n(i,s);else{var u=h;do{if(this._valuesInfo[u].hashcode==s&&this._valuesInfo[u].key==i)return this._values[u]=r,o.value=u,!1;u=this._valuesInfo[u].previous}while(-1!=u);this._collisions++,this._valuesInfo[this._freeValueCellIndex]=new n(i,s,h),this._valuesInfo[h].next=this._freeValueCellIndex}if(this._buckets[a]=this._freeValueCellIndex+1,this._values[this._freeValueCellIndex]=r,o.value=this._freeValueCellIndex,this._freeValueCellIndex++,this._collisions>this._buckets.length){this._buckets=new Array(t.HashHelpers.expandPrime(this._collisions)),this._collisions=0;for(var l=0;l=e?t%e:t},e}();t.FasterDictionary=e;var n=function(){return function(t,e,n){void 0===n&&(n=-1),this.key=t,this.hashcode=e,this.previous=n,this.next=-1}}();t.FastNode=n}(es||(es={})),function(t){var e=function(){return function(t,e){this.element=t,this.next=e}}();function n(t,e){return t===e}t.Node=e,t.defaultEquals=n;var i=function(){function t(t){void 0===t&&(t=n),this.count=0,this.next=void 0,this.equalsFn=t,this.head=null}return t.prototype.push=function(t){var n,i=new e(t);if(null==this.head)this.head=i;else{for(n=this.head;null!=n.next;)n=n.next;n.next=i}this.count++},t.prototype.removeAt=function(t){if(t>=0&&t=0&&t<=this.count){for(var e=this.head,n=0;n=0&&n<=this.count){var i=new e(t);if(0===n)i.next=this.head,this.head=i;else{var r=this.getElementAt(n-1);i.next=r.next,r.next=i}return this.count++,!0}return!1},t.prototype.indexOf=function(t){for(var e=this.head,n=0;n0)for(var e=0;ethis._objectQueue.length;)this._objectQueue.shift()},t.clearCache=function(){this._objectQueue.length=0},t.obtain=function(){return this._objectQueue.length>0?this._objectQueue.shift():[]},t.free=function(t){this._objectQueue.unshift(t),t.length=0},t._objectQueue=[],t}();t.ListPool=e}(es||(es={})),function(t){var e=function(){function e(t,e){this.first=t,this.second=e}return e.prototype.clear=function(){this.first=this.second=null},e.prototype.equals=function(t){return this.first==t.first&&this.second==t.second},e.prototype.getHashCode=function(){return 37*t.EqualityComparer.default().getHashCode(this.first)+t.EqualityComparer.default().getHashCode(this.second)},e}();t.Pair=e}(es||(es={})),function(t){var e=function(){function e(){}return e.warmCache=function(t,e){if((e-=this._objectQueue.length)>0)for(var n=0;nthis._objectQueue.length;)this._objectQueue.shift()},e.clearCache=function(){this._objectQueue.length=0},e.obtain=function(t){return this._objectQueue.length>0?this._objectQueue.shift():new t},e.free=function(e){this._objectQueue.unshift(e),t.isIPoolable(e)&&e.reset()},e._objectQueue=[],e}();t.Pool=e,t.isIPoolable=function(t){return void 0!==t.reset}}(es||(es={})),function(t){var e=function(t){function e(e){return t.call(this,e)||this}return __extends(e,t),e.prototype.getHashCode=function(t){return t.getHashCode()},e.prototype.areEqual=function(t,e){return t.equals(e)},e}(function(){function t(t){var e=this;this.clear(),t&&t.forEach(function(t){e.add(t)})}return t.prototype.add=function(t){var e=this,n=this.getHashCode(t),i=this.buckets[n];if(void 0===i){var r=new Array;return r.push(t),this.buckets[n]=r,this.count=this.count+1,!0}return!i.some(function(n){return e.areEqual(n,t)})&&(i.push(t),this.count=this.count+1,!0)},t.prototype.remove=function(t){var e=this,n=this.getHashCode(t),i=this.buckets[n];if(void 0===i)return!1;var r=!1,o=new Array;return i.forEach(function(n){e.areEqual(n,t)?r=!0:o.push(t)}),this.buckets[n]=o,r&&(this.count=this.count-1),r},t.prototype.contains=function(t){return this.bucketsContains(this.buckets,t)},t.prototype.getCount=function(){return this.count},t.prototype.clear=function(){this.buckets=new Array,this.count=0},t.prototype.toArray=function(){var t=new Array;return this.buckets.forEach(function(e){e.forEach(function(e){t.push(e)})}),t},t.prototype.exceptWith=function(t){var e=this;t&&t.forEach(function(t){e.remove(t)})},t.prototype.intersectWith=function(t){var e=this;if(t){var n=this.buildInternalBuckets(t);this.toArray().forEach(function(t){e.bucketsContains(n.Buckets,t)||e.remove(t)})}else this.clear()},t.prototype.unionWith=function(t){var e=this;t.forEach(function(t){e.add(t)})},t.prototype.isSubsetOf=function(t){var e=this,n=this.buildInternalBuckets(t);return this.toArray().every(function(t){return e.bucketsContains(n.Buckets,t)})},t.prototype.isSupersetOf=function(t){var e=this;return t.every(function(t){return e.contains(t)})},t.prototype.overlaps=function(t){var e=this;return t.some(function(t){return e.contains(t)})},t.prototype.setEquals=function(t){var e=this;return this.buildInternalBuckets(t).Count===this.count&&t.every(function(t){return e.contains(t)})},t.prototype.buildInternalBuckets=function(t){var e=this,n=new Array,i=0;return t.forEach(function(t){var r=e.getHashCode(t),o=n[r];if(void 0===o){var s=new Array;s.push(t),n[r]=s,i+=1}else o.some(function(n){return e.areEqual(n,t)})||(o.push(t),i+=1)}),{Buckets:n,Count:i}},t.prototype.bucketsContains=function(t,e){var n=this,i=t[this.getHashCode(e)];return void 0!==i&&i.some(function(t){return n.areEqual(t,e)})},t}());t.HashSet=e}(es||(es={})),function(t){var e=function(){function t(){}return t.waitForSeconds=function(t){return n.waiter.wait(t)},t}();t.Coroutine=e;var n=function(){function t(){this.waitTime=0}return t.prototype.wait=function(e){return t.waiter.waitTime=e,t.waiter},t.waiter=new t,t}();t.WaitForSeconds=n}(es||(es={})),function(t){var e=function(){function t(){this.waitTimer=0,this.useUnscaledDeltaTime=!1}return t.prototype.stop=function(){this.isDone=!0},t.prototype.setUseUnscaledDeltaTime=function(t){return this.useUnscaledDeltaTime=t,this},t.prototype.prepareForUse=function(){this.isDone=!1},t.prototype.reset=function(){this.isDone=!0,this.waitTimer=0,this.waitForCoroutine=null,this.enumerator=null,this.useUnscaledDeltaTime=!1},t}();t.CoroutineImpl=e;var n=function(n){function i(){var t=null!==n&&n.apply(this,arguments)||this;return t._unblockedCoroutines=[],t._shouldRunNextFrame=[],t}return __extends(i,n),i.prototype.startCoroutine=function(n){var i=t.Pool.obtain(e);return i.prepareForUse(),i.enumerator=n,this.tickCoroutine(i)?(this._isInUpdate?this._shouldRunNextFrame.push(i):this._unblockedCoroutines.push(i),i):null},i.prototype.update=function(){this._isInUpdate=!0;for(var e=0;e0?(n.waitTimer-=n.useUnscaledDeltaTime?t.Time.unscaledDeltaTime:t.Time.deltaTime,this._shouldRunNextFrame.push(n)):this.tickCoroutine(n)&&this._shouldRunNextFrame.push(n)}}var i=new t.List(this._unblockedCoroutines);i.clear(),i.addRange(this._shouldRunNextFrame),this._shouldRunNextFrame.length=0,this._isInUpdate=!1},i.prototype.tickCoroutine=function(n){var i=n.enumerator.next();return i.done||n.isDone?(t.Pool.free(n),!1):null==i.value||(i.value instanceof t.WaitForSeconds?(n.waitTimer=i.value.waitTime,!0):"number"==typeof i.value?(n.waitTimer=i.value,!0):"string"==typeof i.value?"break"!=i.value||(t.Pool.free(n),!1):!(i.value instanceof e)||(n.waitForCoroutine=i.value,!0))},i}(t.GlobalManager);t.CoroutineManager=n}(es||(es={})),function(t){var e=function(){function e(t,e,n){void 0===n&&(n=!0),this.binWidth=0,this.binHeight=0,this.usedRectangles=[],this.freeRectangles=[],this.init(t,e,n)}return e.prototype.init=function(e,n,i){void 0===i&&(i=!0),this.binWidth=e,this.binHeight=n,this.allowRotations=i;var r=new t.Rectangle;r.x=0,r.y=0,r.width=e,r.height=n,this.usedRectangles.length=0,this.freeRectangles.length=0,this.freeRectangles.push(r)},e.prototype.insert=function(e,n){var i=new t.Rectangle,r=new t.Ref(0),o=new t.Ref(0);if(0==(i=this.findPositionForNewNodeBestAreaFit(e,n,r,o)).height)return i;for(var s=this.freeRectangles.length,a=0;a=e&&this.freeRectangles[s].height>=n){var c=Math.abs(this.freeRectangles[s].width-e),h=Math.abs(this.freeRectangles[s].height-n),u=Math.min(c,h);(a=n&&this.freeRectangles[s].height>=e){c=Math.abs(this.freeRectangles[s].width-n),h=Math.abs(this.freeRectangles[s].height-e),u=Math.min(c,h);(a=t.x+t.width||e.x+e.width<=t.x||e.y>=t.y+t.height||e.y+e.height<=t.y)return!1;if(e.xt.x){if(e.y>t.y&&e.yt.y){var n;if(e.x>t.x&&e.x=e.x&&t.y>=e.y&&t.x+t.width<=e.x+e.width&&t.y+t.height<=e.y+e.height},e}();t.MaxRectsBinPack=e}(es||(es={})),function(t){var e=function(){function e(){}return e.bubbleSort=function(t){for(var e=!1,n=0;nn;i--)if(t[i]0&&t[r-1]>i;r--)t[r]=t[r-1];t[r]=i}},e.binarySearch=function(t,e){for(var n=0,i=t.length,r=n+i>>1;n=t[r]&&(n=r+1),r=n+i>>1;return t[n]==e?n:-1},e.findElementIndex=function(t,e){for(var n=t.length,i=0;it[e]&&(e=i);return e},e.getMinElementIndex=function(t){for(var e=0,n=t.length,i=1;i=0;--r)n.unshift(e[r]);return n},e.getDifferAry=function(t,e){t=this.getUniqueAry(t),e=this.getUniqueAry(e);for(var n=t.concat(e),i={},r=[],o=n.length,s=0;s=0;e-=1)t.splice(e,1)},e.cloneList=function(t){return t?t.slice(0,t.length):null},e.equals=function(t,e){if(t==e)return!0;var n=t.length;if(n!=e.length)return!1;for(;n--;)if(t[n]!=e[n])return!1;return!0},e.insert=function(t,e,n){if(!t)return null;var i=t.length;if(e>i&&(e=i),e<0&&(e=0),e==i)t.push(n);else if(0==e)t.unshift(n);else{for(var r=i-1;r>=e;r-=1)t[r+1]=t[r];t[e]=n}return n},e.shuffle=function(e){for(var n=e.length;n>1;){n--;var i=t.RandomUtils.randint(0,n+1),r=e[i];e[i]=e[n],e[n]=r}},e.addIfNotPresent=function(e,n){return!new t.List(e).contains(n)&&(e.push(n),!0)},e.lastItem=function(t){return t[t.length-1]},e.randomItem=function(e){return e[t.RandomUtils.randint(0,e.length-1)]},e.randomItems=function(e,n){for(var i=new Set;i.size!=n;){var r=this.randomItem(e);i.has(r)||i.add(r)}var o=t.ListPool.obtain();return i.forEach(function(t){return o.push(t)}),o},e}();t.ArrayUtils=e}(es||(es={})),function(t){var e=function(){function t(){}return Object.defineProperty(t,"nativeBase64",{get:function(){return"function"==typeof window.atob},enumerable:!0,configurable:!0}),t.decode=function(t){if(t=t.replace(/[^A-Za-z0-9\+\/\=]/g,""),this.nativeBase64)return window.atob(t);for(var e,n,i,r,o,s,a=[],c=0;c>4,n=(15&r)<<4|(o=this._keyStr.indexOf(t.charAt(c++)))>>2,i=(3&o)<<6|(s=this._keyStr.indexOf(t.charAt(c++))),a.push(String.fromCharCode(e)),64!==o&&a.push(String.fromCharCode(n)),64!==s&&a.push(String.fromCharCode(i));return a=a.join("")},t.encode=function(t){if(t=t.replace(/\r\n/g,"\n"),!this.nativeBase64){for(var e,n,i,r,o,s,a,c=[],h=0;h>2,o=(3&e)<<4|(n=t.charCodeAt(h++))>>4,s=(15&n)<<2|(i=t.charCodeAt(h++))>>6,a=63&i,isNaN(n)?s=a=64:isNaN(i)&&(a=64),c.push(this._keyStr.charAt(r)),c.push(this._keyStr.charAt(o)),c.push(this._keyStr.charAt(s)),c.push(this._keyStr.charAt(a));return c=c.join("")}window.btoa(t)},t.decodeBase64AsArray=function(e,n){n=n||1;var i,r,o,s=t.decode(e),a=new Uint32Array(s.length/n);for(i=0,o=s.length/n;i=0;--r)a[i]+=s.charCodeAt(i*n+r)<<(r<<3);return a},t.decompress=function(t,e,n){throw new Error("GZIP/ZLIB compressed TMX Tile Map not supported!")},t.decodeCSV=function(t){for(var e=t.replace("\n","").trim().split(","),n=[],i=0;i(e=Math.floor(e))?t++:e++,this.randrange(t,e)},t.randnum=function(t,e){return this.random()*(e-t)+t},t.shuffle=function(t){return t.sort(this._randomCompare),t},t.choice=function(t){if(!t.hasOwnProperty("length"))throw new Error("无法对此对象执行此操作");var e=Math.floor(this.random()*t.length);return t instanceof String?String(t).charAt(e):t[e]},t.sample=function(t,e){var n=t.length;if(e<=0||n=0;)s=Math.floor(this.random()*n);i.push(t[s]),r.push(s)}return i},t.random=function(){return Math.random()},t.boolean=function(t){return void 0===t&&(t=.5),this.random().5?1:-1},t}();t.RandomUtils=e}(es||(es={})),function(t){var e=function(){function e(){}return e.getSide=function(e,n){switch(n){case t.Edge.top:return e.top;case t.Edge.bottom:return e.bottom;case t.Edge.left:return e.left;case t.Edge.right:return e.right}},e.union=function(e,n){var i=new t.Rectangle(n.x,n.y,0,0),r=new t.Rectangle;return r.x=Math.min(e.x,i.x),r.y=Math.min(e.y,i.y),r.width=Math.max(e.right,i.right)-r.x,r.height=Math.max(e.bottom,i.bottom)-r.y,r},e.getHalfRect=function(e,n){switch(n){case t.Edge.top:return new t.Rectangle(e.x,e.y,e.width,e.height/2);case t.Edge.bottom:return new t.Rectangle(e.x,e.y+e.height/2,e.width,e.height/2);case t.Edge.left:return new t.Rectangle(e.x,e.y,e.width/2,e.height);case t.Edge.right:return new t.Rectangle(e.x+e.width/2,e.y,e.width/2,e.height)}},e.getRectEdgePortion=function(e,n,i){switch(void 0===i&&(i=1),n){case t.Edge.top:return new t.Rectangle(e.x,e.y,e.width,i);case t.Edge.bottom:return new t.Rectangle(e.x,e.y+e.height-i,e.width,i);case t.Edge.left:return new t.Rectangle(e.x,e.y,i,e.height);case t.Edge.right:return new t.Rectangle(e.x+e.width-i,e.y,i,e.height)}},e.expandSide=function(e,n,i){switch(i=Math.abs(i),n){case t.Edge.top:e.y-=i,e.height+=i;break;case t.Edge.bottom:e.height+=i;break;case t.Edge.left:e.x-=i,e.width+=i;break;case t.Edge.right:e.width+=i}},e.contract=function(t,e,n){t.x+=e,t.y+=n,t.width-=2*e,t.height-=2*n},e.boundsFromPolygonVector=function(e){for(var n=Number.POSITIVE_INFINITY,i=Number.POSITIVE_INFINITY,r=Number.NEGATIVE_INFINITY,o=Number.NEGATIVE_INFINITY,s=0;sr&&(r=a.x),a.yo&&(o=a.y)}return this.fromMinMaxVector(new t.Vector2(n,i),new t.Vector2(r,o))},e.fromMinMaxVector=function(e,n){return new t.Rectangle(e.x,e.y,n.x-e.x,n.y-e.y)},e.getSweptBroadphaseBounds=function(e,n,i){var r=t.Rectangle.empty;return r.x=n>0?e.x:e.x+n,r.y=i>0?e.y:e.y+i,r.width=n>0?n+e.width:e.width-n,r.height=i>0?i+e.height:e.height-i,r},e.prototype.collisionCheck=function(t,e,n,i){n.value=i.value=0;var r=e.x-(t.x+t.width),o=e.x+e.width-t.x,s=e.y-(t.y+t.height),a=e.y+e.height-t.y;return!(r>0||o<0||s>0||a<0)&&(n.value=Math.abs(r)=l||Math.abs(u)>=p)return t.Vector2.zero;var f=h>0?l-h:-l-h,d=u>0?p-u:-p-u;return new t.Vector2(f,d)},e.getClosestPointOnBoundsToOrigin=function(e){var n=this.getMax(e),i=Math.abs(e.location.x),r=new t.Vector2(e.location.x,0);return Math.abs(n.x)r&&(r=a.x),a.yo&&(o=a.y)}return this.fromMinMaxVector(new t.Vector2(n,i),new t.Vector2(r,o))},e.scale=function(t,e){t.x=t.x*e.x,t.y=t.y*e.y,t.width=t.width*e.x,t.height=t.height*e.y},e.translate=function(t,e){t.location.add(e)},e}();t.RectangleExt=e}(es||(es={})),function(t){var e=function(){function t(){}return t.premultiplyAlpha=function(t){for(var e=t[0],n=0;nt.MathHelper.Epsilon?e.divide(new t.Vector2(n)):e.x=e.y=0},e.transformA=function(t,e,n,i,r,o){for(var s=0;so?e?-1:1:r0},e.prototype.average=function(t){return this.sum(t)/this.count(t)},e.prototype.cast=function(){return new e(this._elements)},e.prototype.clear=function(){this._elements.length=0},e.prototype.concat=function(t){return new e(this._elements.concat(t.toArray()))},e.prototype.contains=function(t){return this.any(function(e){return e===t})},e.prototype.count=function(t){return t?this.where(t).count():this._elements.length},e.prototype.defaultIfEmpty=function(t){return this.count()?this:new e([t])},e.prototype.distinctBy=function(t){var n=this.groupBy(t);return Object.keys(n).reduce(function(t,e){return t.add(n[e][0]),t},new e)},e.prototype.elementAt=function(t){if(t=0)return this._elements[t];throw new Error("ArgumentOutOfRangeException: index is less than 0 or greater than or equal to the number of elements in source.")},e.prototype.elementAtOrDefault=function(t){return t=0?this._elements[t]:void 0},e.prototype.except=function(t){return this.where(function(e){return!t.contains(e)})},e.prototype.first=function(t){if(this.count())return t?this.where(t).first():this._elements[0];throw new Error("InvalidOperationException: The source sequence is empty.")},e.prototype.firstOrDefault=function(t){return this.count(t)?this.first(t):void 0},e.prototype.forEach=function(t){return this._elements.forEach(t)},e.prototype.groupBy=function(t,e){void 0===e&&(e=function(t){return t});return this.aggregate(function(n,i){var r=t(i),o=n[r],s=e(i);return o?o.push(s):n[r]=[s],n},{})},e.prototype.groupJoin=function(t,e,n,i){return this.select(function(r){return i(r,t.where(function(t){return e(r)===n(t)}))})},e.prototype.indexOf=function(t){return this._elements.indexOf(t)},e.prototype.insert=function(t,e){if(t<0||t>this._elements.length)throw new Error("Index is out of range.");this._elements.splice(t,0,e)},e.prototype.intersect=function(t){return this.where(function(e){return t.contains(e)})},e.prototype.join=function(t,e,n,i){return this.selectMany(function(r){return t.where(function(t){return n(t)===e(r)}).select(function(t){return i(r,t)})})},e.prototype.last=function(t){if(this.count())return t?this.where(t).last():this._elements[this.count()-1];throw Error("InvalidOperationException: The source sequence is empty.")},e.prototype.lastOrDefault=function(t){return this.count(t)?this.last(t):void 0},e.prototype.max=function(t){return Math.max.apply(Math,__spread(this._elements.map(t||function(t){return t})))},e.prototype.min=function(t){return Math.min.apply(Math,__spread(this._elements.map(t||function(t){return t})))},e.prototype.ofType=function(t){var e;switch(t){case Number:e="number";break;case String:e="string";break;case Boolean:e=typeof!0;break;case Function:e="function";break;default:e=null}return null===e?this.where(function(e){return e instanceof t}).cast():this.where(function(t){return typeof t===e}).cast()},e.prototype.orderBy=function(e,i){return void 0===i&&(i=t.keyComparer(e,!1)),new n(this._elements,i)},e.prototype.orderByDescending=function(e,i){return void 0===i&&(i=t.keyComparer(e,!0)),new n(this._elements,i)},e.prototype.thenBy=function(t){return this.orderBy(t)},e.prototype.thenByDescending=function(t){return this.orderByDescending(t)},e.prototype.remove=function(t){return-1!==this.indexOf(t)&&(this.removeAt(this.indexOf(t)),!0)},e.prototype.removeAll=function(e){return this.where(t.negate(e))},e.prototype.removeAt=function(t){this._elements.splice(t,1)},e.prototype.reverse=function(){return new e(this._elements.reverse())},e.prototype.select=function(t){return new e(this._elements.map(t))},e.prototype.selectMany=function(t){var n=this;return this.aggregate(function(e,i,r){return e.addRange(n.select(t).elementAt(r).toArray()),e},new e)},e.prototype.sequenceEqual=function(t){return this.all(function(e){return t.contains(e)})},e.prototype.single=function(t){if(1!==this.count(t))throw new Error("The collection does not contain exactly one element.");return this.first(t)},e.prototype.singleOrDefault=function(t){return this.count(t)?this.single(t):void 0},e.prototype.skip=function(t){return new e(this._elements.slice(Math.max(0,t)))},e.prototype.skipLast=function(t){return new e(this._elements.slice(0,-Math.max(0,t)))},e.prototype.skipWhile=function(t){var e=this;return this.skip(this.aggregate(function(n){return t(e.elementAt(n))?++n:n},0))},e.prototype.sum=function(t){return t?this.select(t).sum():this.aggregate(function(t,e){return t+ +e},0)},e.prototype.take=function(t){return new e(this._elements.slice(0,Math.max(0,t)))},e.prototype.takeLast=function(t){return new e(this._elements.slice(-Math.max(0,t)))},e.prototype.takeWhile=function(t){var e=this;return this.take(this.aggregate(function(n){return t(e.elementAt(n))?++n:n},0))},e.prototype.toArray=function(){return this._elements},e.prototype.toDictionary=function(t,n){var i=this;return this.aggregate(function(e,r,o){return e[i.select(t).elementAt(o).toString()]=n?i.select(n).elementAt(o):r,e.add({Key:i.select(t).elementAt(o),Value:n?i.select(n).elementAt(o):r}),e},new e)},e.prototype.toSet=function(){var t,e,n=new Set;try{for(var i=__values(this._elements),r=i.next();!r.done;r=i.next()){var o=r.value;n.add(o)}}catch(e){t={error:e}}finally{try{r&&!r.done&&(e=i.return)&&e.call(i)}finally{if(t)throw t.error}}return n},e.prototype.toList=function(){return this},e.prototype.toLookup=function(t,e){return this.groupBy(t,e)},e.prototype.where=function(t){return new e(this._elements.filter(t))},e.prototype.zip=function(t,e){var n=this;return t.count()e.angle?1:t.angleMath.PI&&(o-=2*Math.PI),r.p1.begin=o>0,r.p2.begin=!r.p1.begin}}catch(e){t={error:e}}finally{try{i&&!i.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}this._isSpotLight&&(this._spotStartAngle=this._segments[0].p2.angle,this._spotEndAngle=this._segments[1].p2.angle)},e._cornerCache=[],e._openSegments=new t.LinkedList,e}();t.VisibilityComputer=e}(es||(es={})),function(t){var e=function(){function e(){this._timeInSeconds=0,this._repeats=!1,this._isDone=!1,this._elapsedTime=0}return e.prototype.getContext=function(){return this.context},e.prototype.reset=function(){this._elapsedTime=0},e.prototype.stop=function(){this._isDone=!0},e.prototype.tick=function(){return!this._isDone&&this._elapsedTime>this._timeInSeconds&&(this._elapsedTime-=this._timeInSeconds,this._onTime(this),this._isDone||this._repeats||(this._isDone=!0)),this._elapsedTime+=t.Time.deltaTime,this._isDone},e.prototype.initialize=function(t,e,n,i){this._timeInSeconds=t,this._repeats=e,this.context=n,this._onTime=i},e.prototype.unload=function(){this.context=null,this._onTime=null},e}();t.Timer=e}(es||(es={})),function(t){var e=function(e){function n(){var t=null!==e&&e.apply(this,arguments)||this;return t._timers=[],t}return __extends(n,e),n.prototype.update=function(){for(var e=this._timers.length-1;e>=0;e--)this._timers[e].tick()&&(this._timers[e].unload(),new t.List(this._timers).removeAt(e))},n.prototype.schedule=function(e,n,i,r){var o=new t.Timer;return o.initialize(e,n,i,r),this._timers.push(o),o},n}(t.GlobalManager);t.TimerManager=e}(es||(es={})); \ No newline at end of file +window.es={};var __awaiter=this&&this.__awaiter||function(t,e,n,i){return new(n||(n=Promise))(function(r,o){function s(t){try{c(i.next(t))}catch(t){o(t)}}function a(t){try{c(i.throw(t))}catch(t){o(t)}}function c(t){t.done?r(t.value):new n(function(e){e(t.value)}).then(s,a)}c((i=i.apply(t,e||[])).next())})},__generator=this&&this.__generator||function(t,e){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]0)&&!(i=o.next()).done;)s.push(i.value)}catch(t){r={error:t}}finally{try{i&&!i.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return s},__spread=this&&this.__spread||function(){for(var t=[],e=0;e=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}}};!function(t){var e=function(){function e(n,i){void 0===n&&(n=!0),void 0===i&&(i=!0),this._globalManagers=[],this._coroutineManager=new t.CoroutineManager,this._timerManager=new t.TimerManager,this._frameCounterElapsedTime=0,this._frameCounter=0,this._totalMemory=0,e._instance=this,e.emitter=new t.Emitter,e.emitter.addObserver(t.CoreEvents.frameUpdated,this.update,this),e.registerGlobalManager(this._coroutineManager),e.registerGlobalManager(new t.TweenManager),e.registerGlobalManager(this._timerManager),e.entitySystemsEnabled=i,this.debug=n,this.initialize()}return Object.defineProperty(e,"Instance",{get:function(){return this._instance},enumerable:!0,configurable:!0}),Object.defineProperty(e,"scene",{get:function(){return this._instance?this._instance._scene:null},set:function(e){t.Insist.isNotNull(e,"场景不能为空"),null==this._instance._scene?(this._instance._scene=e,this._instance.onSceneChanged(),this._instance._scene.begin()):this._instance._nextScene=e},enumerable:!0,configurable:!0}),e.create=function(e){return void 0===e&&(e=!0),null==this._instance&&(this._instance=new t.Core(e)),this._instance},e.registerGlobalManager=function(t){this._instance._globalManagers.push(t),t.enabled=!0},e.unregisterGlobalManager=function(e){new t.List(this._instance._globalManagers).remove(e),e.enabled=!1},e.getGlobalManager=function(t){for(var n=0,i=e._instance._globalManagers.length;n=1)){var e=window.performance.memory;null!=e&&(this._totalMemory=Number((e.totalJSHeapSize/1048576).toFixed(2))),this._titleMemory&&this._titleMemory(this._totalMemory,this._frameCounter),this._frameCounter=0,this._frameCounterElapsedTime-=1}},e.prototype.onSceneChanged=function(){t.Time.sceneChanged()},e.prototype.initialize=function(){},e.prototype.update=function(n){return void 0===n&&(n=-1),__awaiter(this,void 0,void 0,function(){var i;return __generator(this,function(r){if(e.paused)return[2];if(t.Time.update(n),null!=this._scene){for(i=this._globalManagers.length-1;i>=0;i--)this._globalManagers[i].enabled&&this._globalManagers[i].update();this._scene.update(),null!=this._nextScene&&(this._scene.end(),this._scene=this._nextScene,this._nextScene=null,this.onSceneChanged(),this._scene.begin())}return this.startDebugDraw(),[2]})})},e.paused=!1,e.debugRenderEndabled=!1,e}();t.Core=e}(es||(es={})),function(t){var e;!function(t){t[t.error=0]="error",t[t.warn=1]="warn",t[t.log=2]="log",t[t.info=3]="info",t[t.trace=4]="trace"}(e=t.LogType||(t.LogType={}));var n=function(){function t(){}return t.warnIf=function(t,n){for(var i=[],r=2;r=0;t--){this.transform.getChild(t).entity.destroy()}},n.prototype.detachFromScene=function(){this.scene.entities.remove(this),this.components.deregisterAllComponents();for(var t=0;t0?new e(this.x/t,this.y/t):new e(0,1)},e.prototype.normalizeEqual=function(){var t=this.distance();return t>0?(this.setTo(this.x/t,this.y/t),this):(this.setTo(0,1),this)},e.prototype.magnitude=function(){return this.distance()},e.prototype.distance=function(t){return t||(t=e.zero),Math.sqrt(Math.pow(this.x-t.x,2)+Math.pow(this.y-t.y,2))},e.prototype.lengthSquared=function(){return this.x*this.x+this.y*this.y},e.prototype.round=function(){return new e(Math.round(this.x),Math.round(this.y))},e.prototype.angleBetween=function(e,n){var i=e.sub(this),r=n.sub(this);return t.Vector2Ext.angle(i,r)},e.prototype.equals=function(t,e){return void 0===e&&(e=.001),Math.abs(this.x-t.x)<=e&&Math.abs(this.y-t.y)<=e},e.prototype.isValid=function(){return t.MathHelper.isValid(this.x)&&t.MathHelper.isValid(this.y)},e.min=function(t,n){return new e(t.xn.x?t.x:n.x,t.y>n.y?t.y:n.y)},e.hermite=function(n,i,r,o,s){return new e(t.MathHelper.hermite(n.x,i.x,r.x,o.x,s),t.MathHelper.hermite(n.y,i.y,r.y,o.y,s))},e.unsignedAngle=function(e,n,i){void 0===i&&(i=!0),e.normalizeEqual(),n.normalizeEqual();var r=Math.acos(t.MathHelper.clamp(e.dot(n),-1,1))*t.MathHelper.Rad2Deg;return i?Math.round(r):r},e.prototype.clone=function(){return new e(this.x,this.y)},e}();t.Vector2=e}(es||(es={})),function(t){var e=function(){function e(){this._sceneComponents=[],this._renderers=[],this.entities=new t.EntityList(this),this.renderableComponents=new t.RenderableComponentList,this.entityProcessors=new t.EntityProcessorList,this.identifierPool=new t.IdentifierPool,this.initialize()}return e.prototype.initialize=function(){},e.prototype.onStart=function(){},e.prototype.unload=function(){},e.prototype.begin=function(){0==this._renderers.length&&this.addRenderer(new t.DefaultRenderer),t.Physics.reset(),null!=this.entityProcessors&&this.entityProcessors.begin(),this._didSceneBegin=!0,this.onStart()},e.prototype.end=function(){this._didSceneBegin=!1;for(var e=0;e=0;t--)this._sceneComponents[t].enabled&&this._sceneComponents[t].update();null!=this.entityProcessors&&this.entityProcessors.update(),this.entities.update(),null!=this.entityProcessors&&this.entityProcessors.lateUpdate(),this.renderableComponents.updateLists(),this.render()},e.prototype.render=function(){for(var t=0;te.x?-1:1,i=this.position.sub(e).normalize();this.rotation=n*Math.acos(i.dot(t.Vector2.unitY))},i.prototype.setLocalRotation=function(t){return this._localRotation=t,this._localDirty=this._positionDirty=this._localPositionDirty=this._localRotationDirty=this._localScaleDirty=!0,this.setDirty(n.rotationDirty),this},i.prototype.setLocalRotationDegrees=function(e){return this.setLocalRotation(t.MathHelper.toRadians(e))},i.prototype.setScale=function(e){return this._scale=e,null!=this.parent?this.localScale=t.Vector2.divide(e,this.parent._scale):this.localScale=e,this},i.prototype.setLocalScale=function(t){return this._localScale=t,this._localDirty=this._positionDirty=this._localScaleDirty=!0,this.setDirty(n.scaleDirty),this},i.prototype.roundPosition=function(){this.position=t.Vector2Ext.round(this._position)},i.prototype.updateTransform=function(){this.hierarchyDirty!=n.clean&&(null!=this.parent&&this.parent.updateTransform(),this._localDirty&&(this._localPositionDirty&&(t.Matrix2D.createTranslation(this._localPosition.x,this._localPosition.y,this._translationMatrix),this._localPositionDirty=!1),this._localRotationDirty&&(t.Matrix2D.createRotation(this._localRotation,this._rotationMatrix),this._localRotationDirty=!1),this._localScaleDirty&&(t.Matrix2D.createScale(this._localScale.x,this._localScale.y,this._scaleMatrix),this._localScaleDirty=!1),t.Matrix2D.multiply(this._scaleMatrix,this._rotationMatrix,this._localTransform),t.Matrix2D.multiply(this._localTransform,this._translationMatrix,this._localTransform),null==this.parent&&(this._worldTransform=this._localTransform,this._rotation=this._localRotation,this._scale=this._localScale,this._worldInverseDirty=!0),this._localDirty=!1),null!=this.parent&&(t.Matrix2D.multiply(this._localTransform,this.parent._worldTransform,this._worldTransform),this._rotation=this._localRotation+this.parent._rotation,this._scale=this.parent._scale.multiply(this._localScale),this._worldInverseDirty=!0),this._worldToLocalDirty=!0,this._positionDirty=!0,this.hierarchyDirty=n.clean)},i.prototype.setDirty=function(t){if(0==(this.hierarchyDirty&t)){switch(this.hierarchyDirty|=t,t){case n.positionDirty:this.entity.onTransformChanged(e.position);break;case n.rotationDirty:this.entity.onTransformChanged(e.rotation);break;case n.scaleDirty:this.entity.onTransformChanged(e.scale)}for(var i=0;i1e-4?this._inverseMass=1/this._mass:this._inverseMass=0,this},n.prototype.setElasticity=function(e){return this._elasticity=t.MathHelper.clamp01(e),this},n.prototype.setFriction=function(e){return this._friction=t.MathHelper.clamp01(e),this},n.prototype.setGlue=function(e){return this._glue=t.MathHelper.clamp(e,0,10),this},n.prototype.setVelocity=function(t){return this.velocity=t,this},n.prototype.addImpulse=function(e){this.isImmovable||this.velocity.addEqual(e.scale(this._inverseMass*(t.Time.deltaTime*t.Time.deltaTime)*1e5))},n.prototype.onAddedToEntity=function(){this._collider=null;for(var e=0;e0&&(o=t.Vector2.zero);var a=this._friction;return s.lengthSquared()0&&this.collisionState.wasGroundedLastFrame&&(t=this.handleVerticalSlope(t)),0!==t.x&&(t=this.moveHorizontally(t)),0!==t.y&&(t=this.moveVertically(t)),this._player.setPosition(this._player.position.x+t.x,this._player.position.y+t.y),e>0&&(this.velocity.x=t.x/e,this.velocity.y=t.y/e),!this.collisionState.wasGroundedLastFrame&&this.collisionState.below&&(this.collisionState.becameGroundedThisFrame=!0),this._isGoingUpSlope&&(this.velocity.y=0),this._isWarpingToGround||this._triggerHelper.update();for(var n=0;n0&&(this.ignoreOneWayPlatformsTime-=e)},i.prototype.warpToGrounded=function(e){void 0===e&&(e=1e3),this.ignoreOneWayPlatformsTime=0,this._isWarpingToGround=!0;var n=0;do{if(n+=1,this.move(new t.Vector2(0,1),.02),n>e)break}while(!this.isGrounded);this._isWarpingToGround=!1},i.prototype.recalculateDistanceBetweenRays=function(){var t=this._collider.height*Math.abs(this._player.scale.y)-2*this._skinWidth;this._verticalDistanceBetweenRays=t/(this.totalHorizontalRays-1);var e=this._collider.width*Math.abs(this._player.scale.x)-2*this._skinWidth;this._horizontalDistanceBetweenRays=e/(this.totalVerticalRays-1)},i.prototype.primeRaycastOrigins=function(){var e=this._collider.bounds;this._raycastOrigins.topLeft=new t.Vector2(e.x+this._skinWidth,e.y+this._skinWidth),this._raycastOrigins.bottomRight=new t.Vector2(e.right-this._skinWidth,e.bottom-this._skinWidth),this._raycastOrigins.bottomLeft=new t.Vector2(e.x+this._skinWidth,e.bottom-this._skinWidth)},i.prototype.moveHorizontally=function(e){for(var n=e.x>0,i=Math.abs(e.x)+this._skinWidth*this.rayOriginSkinMutiplier,r=n?t.Vector2.right:t.Vector2.left,o=this._raycastOrigins.bottomLeft.y,s=n?this._raycastOrigins.bottomRight.x-this._skinWidth*(this.rayOriginSkinMutiplier-1):this._raycastOrigins.bottomLeft.x+this._skinWidth*(this.rayOriginSkinMutiplier-1),a=0;a0)&&(a&=~this.oneWayPlatformMask);for(var c=0;cthis.jumpingThreshold){var i=this.slopeSpeedMultiplier?this.slopeSpeedMultiplier.lerp(n):1;e.x*=i,e.y=Math.abs(Math.tan(n*t.MathHelper.Deg2Rad)*e.x);var r=e.x>0,o=r?this._raycastOrigins.bottomRight:this._raycastOrigins.bottomLeft,s=null;(s=this.supportSlopedOneWayPlatforms&&this.collisionState.wasGroundedLastFrame?t.Physics.linecast(o,o.add(e),this.platformMask,this.ignoredColliders):t.Physics.linecast(o,o.add(e),this.platformMask&~this.oneWayPlatformMask,this.ignoredColliders)).collider&&(e.x=s.point.x-o.x,e.y=s.point.y-o.y,r?e.x-=this._skinWidth:e.x+=this._skinWidth),this._isGoingUpSlope=!0,this.collisionState.below=!0}}else e.x=0;return!0},i}();t.CharacterController=i}(es||(es={})),function(t){var e=function(){function e(){}return e.getITriggerListener=function(e,n){var i,r;try{for(var o=__values(e.components._components),s=o.next();!s.done;s=o.next()){var a=s.value;t.isITriggerListener(a)&&n.push(a)}}catch(t){i={error:t}}finally{try{s&&!s.done&&(r=o.return)&&r.call(o)}finally{if(i)throw i.error}}for(var c in e.components._componentsToAdd){a=e.components._componentsToAdd[c];t.isITriggerListener(a)&&n.push(a)}return n},e}();t.TriggerListenerHelper=e,t.isITriggerListener=function(t){return void 0!==t.onTriggerEnter}}(es||(es={})),function(t){var e=function(e){function n(){return null!==e&&e.apply(this,arguments)||this}return __extends(n,e),n.prototype.onAddedToEntity=function(){this._triggerHelper=new t.ColliderTriggerHelper(this.entity)},n.prototype.calculateMovement=function(e,n){for(var i,r,o=null,s=0;sn;n++){var i=t[n];this.processDelta(i,this.acc);var r=this.getRemainingDelay(i);r<=0?this.processExpired(i):this.offerDelay(r)}this.acc=0}else this.stop()},n.prototype.checkProcessing=function(){return!!this.running&&(this.acc+=t.Time.deltaTime,this.acc>=this.delay)},n.prototype.offerDelay=function(t){this.running?this.delay=Math.min(this.delay,t):(this.running=!0,this.delay=t)},n.prototype.getInitialTimeDelay=function(){return this.delay},n.prototype.getRemainingTimeUntilProcessing=function(){return this.running?this.delay-this.acc:0},n.prototype.isRunning=function(){return this.running},n.prototype.stop=function(){this.running=!1,this.acc=0},n}(t.EntitySystem);t.DelayedIteratingSystem=e}(es||(es={})),function(t){var e=function(t){function e(e){return t.call(this,e)||this}return __extends(e,t),e.prototype.lateProcessEntity=function(t){},e.prototype.process=function(t){if(0!=t.length)for(var e=0,n=t.length;e=this.interval&&(this.acc-=this.interval,this.intervalDelta=this.acc-this.intervalDelta,!0)},n.prototype.getIntervalDelta=function(){return this.interval+this.intervalDelta},n}(t.EntitySystem);t.IntervalSystem=e}(es||(es={})),function(t){var e=function(t){function e(e,n){return t.call(this,e,n)||this}return __extends(e,t),e.prototype.process=function(t){var e=this;t.forEach(function(t){return e.processEntity(t)})},e}(t.IntervalSystem);t.IntervalIteratingSystem=e}(es||(es={})),function(es){var JobSystem=function(_super){function JobSystem(t,e){var n=_super.call(this,t)||this;n._threads=e,n._jobs=new Array(e);for(var i=0;it.length&&(s=t.length);var a=o._jobs[n];if(a.set(t,r,s,o._executeStr,o),r!=s){var c=es.WorkerUtils.makeWorker(o.queueOnThread);es.WorkerUtils.workerMessage(c)(a).then(function(t){var n=t;e.resetJob(n),c.terminate()}).catch(function(t){a.err=t,c.terminate()})}},o=this,s=0;s-1?eval("(function(){return "+v+" })()"):v}),i=job.from;i0)for(var t=0,e=this._components.length;t0)for(var e=0,n=this._components.length;e0)for(var e=0,n=this._components.length;e0){for(var e=function(t,e){var i=n._componentsToRemoveList[t];n.handleRemove(i);var r=n._components.findIndex(function(t){return t.id==i.id});-1!=r&&n._components.splice(r,1),n.removeComponentsByType(i)},n=this,i=0,r=this._componentsToRemoveList.length;i0){for(i=0,r=this._componentsToAddList.length;i0){for(i=0,r=this._tempBufferList.length;i0){var n=this._updatableComponents.findIndex(function(t){return t.id==e.id});-1!=n&&this._updatableComponents.splice(n,1)}this.decreaseBits(e),this._entity.scene.entityProcessors.onComponentRemoved(this._entity),e.onRemovedFromEntity(),e.entity=null},e.prototype.removeComponentsByType=function(e){var n=this.componentsByType.get(t.TypeUtils.getType(e)),i=n.findIndex(function(t){return t.id==e.id});-1!=i&&n.splice(i,1)},e.prototype.addComponentsByType=function(e){var n=this.componentsByType.get(t.TypeUtils.getType(e));n||(n=[]),n.push(e),this.componentsByType.set(t.TypeUtils.getType(e),n)},e.prototype.removeComponentsToAddByType=function(e){var n=this.componentsToAddByType.get(t.TypeUtils.getType(e)),i=n.findIndex(function(t){return t.id==e.id});-1!=i&&n.splice(i,1)},e.prototype.addComponentsToAddByType=function(e){var n=this.componentsToAddByType.get(t.TypeUtils.getType(e));n||(n=[]),n.push(e),this.componentsToAddByType.set(t.TypeUtils.getType(e),n)},e.prototype.getComponent=function(t,e){var n=this.componentsByType.get(t);if(n&&n.length>0)return n[0];if(!e){var i=this.componentsToAddByType.get(t);if(i&&i.length>0)return i[0]}return null},e.prototype.getComponents=function(t,e){e||(e=[]);var n=this.componentsByType.get(t);n&&(e=e.concat(n));var i=this.componentsToAddByType.get(t);return i&&(e=e.concat(i)),e},e.prototype.update=function(){if(this.updateLists(),this._updatableComponents.length>0)for(var t=0,e=this._updatableComponents.length;t0)for(var e=0,n=this._components.length;e0)for(e=0,n=this._componentsToAddList.length;e0)for(var t=0,e=this._components.length;t0)for(var t=0,e=this._components.length;t0){for(var t=function(t,n){var i=e._entitiesToRemoveList[t];e.removeFromTagList(i);var r=e._entities.findIndex(function(t){return t.id==i.id});-1!=r&&e._entities.splice(r,1),i.onRemovedFromScene(),i.scene=null,e.scene.entityProcessors.onEntityRemoved(i)},e=this,n=0,i=this._entitiesToRemoveList.length;n0){for(n=0,i=this._entitiesToAddedList.length;n0)for(var e=0,n=this._entities.length;e0)for(e=0,n=this._entitiesToAddedList.length;e0)for(var e=0,n=this._entities.length;e0)try{for(var s=__values(r),a=s.next();!a.done;a=s.next()){var c=a.value;o.push(c)}}catch(t){n={error:t}}finally{try{a&&!a.done&&(i=s.return)&&i.call(s)}finally{if(n)throw n.error}}return o},e.prototype.entityWithTag=function(t){var e,n,i=this.getTagList(t);if(i.size>0)try{for(var r=__values(i),o=r.next();!o.done;o=r.next()){return o.value}}catch(t){e={error:t}}finally{try{o&&!o.done&&(n=r.return)&&n.call(r)}finally{if(e)throw e.error}}return null},e.prototype.findComponentOfType=function(t){if(this._entities.length>0)for(var e=0,n=this._entities.length;e0)for(e=0;e0)for(var i=0,r=this._entities.length;i0)for(i=0,r=this._entitiesToAddedList.length;i0)for(var i=0,r=this._entities.length;i0)for(var s=0,a=t.length;s0)for(i=0,r=this._entitiesToAddedList.length;i0)for(s=0,a=t.length;s=t)return n}for(e=1|t;ethis.maxPrimeArrayLength&&this.maxPrimeArrayLength>t?this.maxPrimeArrayLength:this.getPrime(e)},t.getHashCode=function(t){var e,n=0;if(0==(e="object"==typeof t?JSON.stringify(t):t.toString()).length)return n;for(var i=0;i0?this.ids.removeLast():this.nextAvailableId_++},e.prototype.checkIn=function(t){this.ids.add(t)},e}();t.IdentifierPool=e}(es||(es={})),function(t){var e=function(){function e(){this.allSet=[],this.exclusionSet=[],this.oneSet=[]}return e.empty=function(){return new e},e.prototype.getAllSet=function(){return this.allSet},e.prototype.getExclusionSet=function(){return this.exclusionSet},e.prototype.getOneSet=function(){return this.oneSet},e.prototype.isInterestedEntity=function(t){return this.isInterested(t.componentBits)},e.prototype.isInterested=function(e){if(0!=this.allSet.length)for(var n=0,i=this.allSet.length;n0)for(var t=0,e=this._unsortedRenderLayers.length;t=e)return t;var i=!1;"-"==t.substr(0,1)&&(i=!0,t=t.substr(1));for(var r=e-n,o=0;o1?this.reverse(t.substring(1))+t.substring(0,1):t},t.cutOff=function(t,e,n,i){void 0===i&&(i=!0),e=Math.floor(e),n=Math.floor(n);var r=t.length;e>r&&(e=r);var o,s=e,a=e+n;return i?o=t.substring(0,s)+t.substr(a,r):(a=(s=r-1-e-n)+n,o=t.substring(0,s+1)+t.substr(a+1,r)),o},t.strReplace=function(t,e){for(var n=0,i=e.length;n",">",'"',""","'","'","®","®","©","©","™","™"],t}();!function(t){var e=function(){function t(){}return t.update=function(t){-1==t&&(t=Date.now()),-1==this._lastTime&&(this._lastTime=t);var e=0;(e=-1==t?(t-this._lastTime)/1e3:t)>this.maxDeltaTime&&(e=this.maxDeltaTime),this.totalTime+=e,this.deltaTime=e*this.timeScale,this.unscaledDeltaTime=e,this.timeSinceSceneLoad+=e,this.frameCount++,this._lastTime=t},t.sceneChanged=function(){this.timeSinceSceneLoad=0},t.checkEvery=function(t){return this.timeSinceSceneLoad/t>(this.timeSinceSceneLoad-this.deltaTime)/t},t.totalTime=0,t.unscaledDeltaTime=0,t.deltaTime=0,t.timeScale=1,t.maxDeltaTime=Number.MAX_VALUE,t.frameCount=0,t.timeSinceSceneLoad=0,t._lastTime=-1,t}();t.Time=e}(es||(es={}));var TimeUtils=function(){function t(){}return t.monthId=function(t){void 0===t&&(t=null);var e=(t=t||new Date).getFullYear(),n=t.getMonth()+1;return parseInt(e+(n<10?"0":"")+n)},t.dateId=function(t){void 0===t&&(t=null);var e=(t=t||new Date).getMonth()+1,n=e<10?"0":"",i=t.getDate(),r=i<10?"0":"";return parseInt(t.getFullYear()+n+e+r+i)},t.weekId=function(t,e){void 0===t&&(t=null),void 0===e&&(e=!0),t=t||new Date;var n=new Date;n.setTime(t.getTime()),n.setDate(1),n.setMonth(0);var i=n.getFullYear(),r=n.getDay();0==r&&(r=7);var o=!1;r<=4?(o=r>1,n.setDate(n.getDate()-(r-1))):n.setDate(n.getDate()+7-r+1);var s=this.diffDay(t,n,!1);if(s<0)return n.setDate(1),n.setMonth(0),n.setDate(n.getDate()-1),this.weekId(n,!1);var a=s/7,c=Math.floor(a)+1;if(53==c){n.setTime(t.getTime()),n.setDate(n.getDate()-1);var h=n.getDay();if(0==h&&(h=7),e&&(!o||h<4))return n.setFullYear(n.getFullYear()+1),n.setDate(1),n.setMonth(0),this.weekId(n,!1)}return parseInt(i+"00"+(c>9?"":"0")+c)},t.diffDay=function(t,e,n){void 0===n&&(n=!1);var i=(t.getTime()-e.getTime())/864e5;return n?Math.ceil(i):Math.floor(i)},t.getFirstDayOfWeek=function(t){var e=(t=t||new Date).getDay()||7;return new Date(t.getFullYear(),t.getMonth(),t.getDate()+1-e,0,0,0,0)},t.getFirstOfDay=function(t){return(t=t||new Date).setHours(0,0,0,0),t},t.getNextFirstOfDay=function(t){return new Date(this.getFirstOfDay(t).getTime()+864e5)},t.formatDate=function(t){var e=t.getFullYear(),n=t.getMonth()+1;n=n<10?"0"+n:n;var i=t.getDate();return e+"-"+n+"-"+(i=i<10?"0"+i:i)},t.formatDateTime=function(t){var e=t.getFullYear(),n=t.getMonth()+1;n=n<10?"0"+n:n;var i=t.getDate();i=i<10?"0"+i:i;var r=t.getHours(),o=t.getMinutes();o=o<10?"0"+o:o;var s=t.getSeconds();return e+"-"+n+"-"+i+" "+r+":"+o+":"+(s=s<10?"0"+s:s)},t.parseDate=function(t){var e=Date.parse(t);return isNaN(e)?new Date:new Date(Date.parse(t.replace(/-/g,"/")))},t.secondToTime=function(t,e,n){void 0===t&&(t=0),void 0===e&&(e=":"),void 0===n&&(n=!0);var i=Math.floor(t/3600),r=Math.floor(t%3600/60),o=Math.floor(t%3600%60),s=i.toString(),a=r.toString(),c=o.toString();return i<10&&(s="0"+s),r<10&&(a="0"+a),o<10&&(c="0"+c),n?s+e+a+e+c:a+e+c},t.timeToMillisecond=function(t,e){void 0===e&&(e=":");for(var n=t.split(e),i=0,r=n.length,o=0;o1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t},t.fromRGBA=function(e,n,i,r){e/=255,n/=255,i/=255;var o=Math.max(e,n,i),s=Math.min(e,n,i),a=(o+s)/2,c=a,h=a;if(o===s)a=c=0;else{var u=o-s;switch(c=h>.5?u/(2-o-s):u/(o+s),o){case e:a=(n-i)/u+(n=1?(e=1,n.range=this._points.length-4):(e=t.MathHelper.clamp01(e)*this._curveCount,n.range=Math.floor(e),e-=n.range,n.range*=3),n.time=e,n},e.prototype.setControlPoint=function(t,e){if(t%3==0){var n=e.sub(this._points[t]);t>0&&this._points[t-1].addEqual(n),t+1-Math.PI&&t<=Math.PI?t:(t%=2*Math.PI)<=-Math.PI?t+2*Math.PI:t>Math.PI?t-2*Math.PI:t},e.isPowerOfTwo=function(t){return t>0&&t%(t-1)==0},e.lerp=function(t,e,n){return t+(e-t)*this.clamp01(n)},e.betterLerp=function(t,n,i,r){return Math.abs(t-n)180&&(i-=360),t+i*this.clamp01(n)},e.lerpAngleRadians=function(t,e,n){var i=this.repeat(e-t,2*Math.PI);return i>Math.PI&&(i-=2*Math.PI),t+i*this.clamp01(n)},e.pingPong=function(t,e){return t=this.repeat(t,2*e),e-Math.abs(t-e)},e.signThreshold=function(t,e){return Math.abs(t)>=e?Math.sign(t):0},e.inverseLerp=function(t,e,n){if(te)return 1}else{if(nt)return 0}return(n-t)/(e-t)},e.lerpPrecise=function(t,e,n){return(1-n)*t+e*n},e.clamp=function(t,e,n){return tn?n:t},e.snap=function(t,e){return Math.round(t/e)*e},e.pointOnCirlce=function(n,i,r){var o=e.toRadians(r);return new t.Vector2(Math.cos(o)*o+n.x,Math.sin(o)*o+n.y)},e.isEven=function(t){return t%2==0},e.isOdd=function(t){return t%2!=0},e.roundWithRoundedAmount=function(t,e){var n=Math.round(t);return e.value=t-n*Math.round(t/n),n},e.clamp01=function(t){return t<0?0:t>1?1:t},e.angleBetweenVectors=function(t,e){return Math.atan2(e.y-t.y,e.x-t.x)},e.angleToVector=function(e,n){return new t.Vector2(Math.cos(e)*n,Math.sin(e)*n)},e.incrementWithWrap=function(t,e){return++t==e?0:t},e.decrementWithWrap=function(t,e){return--t<0?e-1:t},e.hypotenuse=function(t,e){return Math.sqrt(t*t+e*e)},e.closestPowerOfTwoGreaterThan=function(t){return t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,(t|=t>>16)+1},e.roundToNearest=function(t,e){return Math.round(t/e)*e},e.withinEpsilon=function(t,e){return void 0===e&&(e=this.Epsilon),Math.abs(t)180&&(n-=360),n},e.between=function(t,e,n){return t>=e&&t<=n},e.deltaAngleRadians=function(t,e){var n=this.repeat(e-t,2*Math.PI);return n>Math.PI&&(n-=2*Math.PI),n},e.repeat=function(t,e){return t-Math.floor(t/e)*e},e.floorToInt=function(t){return Math.trunc(Math.floor(t))},e.rotateAround=function(e,n){var i=t.Time.totalTime*n,r=Math.cos(i),o=Math.sin(i);return new t.Vector2(e.x+r,e.y+o)},e.rotateAround2=function(e,n,i){i=this.toRadians(i);var r=Math.cos(i),o=Math.sin(i),s=r*(e.x-n.x)-o*(e.y-n.y)+n.x,a=o*(e.x-n.x)+r*(e.y-n.y)+n.y;return new t.Vector2(s,a)},e.pointOnCircle=function(e,n,i){var r=this.toRadians(i);return new t.Vector2(Math.cos(r)*n+e.x,Math.sin(r)*n+e.y)},e.pointOnCircleRadians=function(e,n,i){return new t.Vector2(Math.cos(i)*n+e.x,Math.sin(i)*n+e.y)},e.lissajou=function(e,n,i,r,o){void 0===e&&(e=2),void 0===n&&(n=3),void 0===i&&(i=1),void 0===r&&(r=1),void 0===o&&(o=0);var s=Math.sin(t.Time.totalTime*e+o)*i,a=Math.cos(t.Time.totalTime*n)*r;return new t.Vector2(s,a)},e.lissajouDamped=function(e,n,i,r,o,s,a){void 0===e&&(e=2),void 0===n&&(n=3),void 0===i&&(i=1),void 0===r&&(r=1),void 0===o&&(o=.5),void 0===s&&(s=0),void 0===a&&(a=5);var c=this.pingPong(t.Time.totalTime,a),h=Math.pow(Math.E,-s*c),u=h*Math.sin(t.Time.totalTime*e+o)*i,l=h*Math.cos(t.Time.totalTime*n)*r;return new t.Vector2(u,l)},e.hermite=function(t,e,n,i,r){return 0==r?t:1==r?n:(2*t-2*n+i+e)*(r*r*r)+(3*n-3*t-2*e-i)*(r*r)+e*r+t},e.isValid=function(t){return!Number.isNaN(t)&&t!==1/0},e.smoothDamp=function(t,e,n,i,r,o){var s=2/(i=Math.max(1e-4,i)),a=s*o,c=1/(1+(a+(a*a*.48+a*(a*a)*.235))),h=t-e,u=e,l=r*i,p=(n+s*(h=this.clamp(h,-1*l,l)))*o;n=(n-s*p)*c;var f=(e=t-h)+(h+p)*c;return u-t>0==f>u&&(n=((f=u)-u)/o),{value:f,currentVelocity:n}},e.smoothDampVector=function(e,n,i,r,o,s){var a=t.Vector2.zero,c=this.smoothDamp(e.x,n.x,i.x,r,o,s);a.x=c.value,i.x=c.currentVelocity;var h=this.smoothDamp(e.y,n.y,i.y,r,o,s);return a.y=h.value,i.y=h.currentVelocity,a},e.mapMinMax=function(t,n,i,r,o){return r+(e.clamp(t,n,i)-n)*(o-r)/(i-n)},e.fromAngle=function(e){return new t.Vector2(Math.cos(e),Math.sin(e)).normalizeEqual()},e.Epsilon=1e-5,e.Rad2Deg=57.29578,e.Deg2Rad=.0174532924,e.PiOver2=Math.PI/2,e}();t.MathHelper=e}(es||(es={})),function(t){var e=function(){function t(t,e,n,i,r,o,s,a,c,h,u,l,p,f,d,m){this.m11=t,this.m12=e,this.m13=n,this.m14=i,this.m21=r,this.m22=o,this.m23=s,this.m24=a,this.m31=c,this.m32=h,this.m33=u,this.m34=l,this.m41=p,this.m42=f,this.m43=d,this.m44=m}return Object.defineProperty(t,"Identity",{get:function(){return this.identity},enumerable:!0,configurable:!0}),t.createOrthographicOffCenter=function(e,n,i,r,o,s,a){void 0===a&&(a=new t),a.m11=2/(n-e),a.m12=0,a.m13=0,a.m14=0,a.m21=0,a.m22=2/(r-i),a.m23=0,a.m24=0,a.m31=0,a.m32=0,a.m33=1/(o-s),a.m34=0,a.m41=(e+n)/(e-n),a.m42=(r+i)/(i-r),a.m43=o/(o-s),a.m44=1},t.createTranslation=function(t,e){e.m11=1,e.m12=0,e.m13=0,e.m14=0,e.m21=0,e.m22=1,e.m23=0,e.m24=0,e.m31=0,e.m32=0,e.m33=1,e.m34=0,e.m41=t.x,e.m42=t.y,e.m43=0,e.m44=1},t.createRotationZ=function(e,n){n=t.Identity;var i=Math.cos(e),r=Math.sin(e);n.m11=i,n.m12=r,n.m21=-r,n.m22=i},t.multiply=function(e,n,i){void 0===i&&(i=new t);var r=e.m11*n.m11+e.m12*n.m21+e.m13*n.m31+e.m14*n.m41,o=e.m11*n.m12+e.m12*n.m22+e.m13*n.m32+e.m14*n.m42,s=e.m11*n.m13+e.m12*n.m23+e.m13*n.m33+e.m14*n.m43,a=e.m11*n.m14+e.m12*n.m24+e.m13*n.m34+e.m14*n.m44,c=e.m21*n.m11+e.m22*n.m21+e.m23*n.m31+e.m24*n.m41,h=e.m21*n.m12+e.m22*n.m22+e.m23*n.m32+e.m24*n.m42,u=e.m21*n.m13+e.m22*n.m23+e.m23*n.m33+e.m24*n.m43,l=e.m21*n.m14+e.m22*n.m24+e.m23*n.m34+e.m24*n.m44,p=e.m31*n.m11+e.m32*n.m21+e.m33*n.m31+e.m34*n.m41,f=e.m31*n.m12+e.m32*n.m22+e.m33*n.m32+e.m34*n.m42,d=e.m31*n.m13+e.m32*n.m23+e.m33*n.m33+e.m34*n.m43,m=e.m31*n.m14+e.m32*n.m24+e.m33*n.m34+e.m34*n.m44,y=e.m41*n.m11+e.m42*n.m21+e.m43*n.m31+e.m44*n.m41,g=e.m41*n.m12+e.m42*n.m22+e.m43*n.m32+e.m44*n.m42,_=e.m41*n.m13+e.m42*n.m23+e.m43*n.m33+e.m44*n.m43,v=e.m41*n.m14+e.m42*n.m24+e.m43*n.m34+e.m44*n.m44;i.m11=r,i.m12=o,i.m13=s,i.m14=a,i.m21=c,i.m22=h,i.m23=u,i.m24=l,i.m31=p,i.m32=f,i.m33=d,i.m34=m,i.m41=y,i.m42=g,i.m43=_,i.m44=v},t.identity=new t(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),t}();t.Matrix=e}(es||(es={})),function(t){var e=function(){function e(){this.m11=0,this.m12=0,this.m21=0,this.m22=0,this.m31=0,this.m32=0}return Object.defineProperty(e,"identity",{get:function(){return(new e).setIdentity()},enumerable:!0,configurable:!0}),e.prototype.setIdentity=function(){return this.setValues(1,0,0,1,0,0)},e.prototype.setValues=function(t,e,n,i,r,o){return this.m11=t,this.m12=e,this.m21=n,this.m22=i,this.m31=r,this.m32=o,this},Object.defineProperty(e.prototype,"translation",{get:function(){return new t.Vector2(this.m31,this.m32)},set:function(t){this.m31=t.x,this.m32=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"rotation",{get:function(){return Math.atan2(this.m21,this.m11)},set:function(t){var e=Math.cos(t),n=Math.sin(t);this.m11=e,this.m12=n,this.m21=-n,this.m22=e},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"rotationDegrees",{get:function(){return t.MathHelper.toDegrees(this.rotation)},set:function(e){this.rotation=t.MathHelper.toRadians(e)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"scale",{get:function(){return new t.Vector2(this.m11,this.m22)},set:function(t){this.m11=t.x,this.m22=t.y},enumerable:!0,configurable:!0}),e.createRotation=function(t,e){e.setIdentity();var n=Math.cos(t),i=Math.sin(t);e.m11=n,e.m12=i,e.m21=-1*i,e.m22=n},e.createRotationOut=function(t,e){var n=Math.cos(t),i=Math.sin(t);e.m11=n,e.m12=i,e.m21=-i,e.m22=n},e.createScale=function(t,e,n){n.m11=t,n.m12=0,n.m21=0,n.m22=e,n.m31=0,n.m32=0},e.createScaleOut=function(t,e,n){n.m11=t,n.m12=0,n.m21=0,n.m22=e,n.m31=0,n.m32=0},e.createTranslation=function(t,e,n){return n.m11=1,n.m12=0,n.m21=0,n.m22=1,n.m31=t,n.m32=e,n},e.createTranslationOut=function(t,e){e.m11=1,e.m12=0,e.m21=0,e.m22=1,e.m31=t.x,e.m32=t.y},e.invert=function(t){var e=1/t.determinant(),n=this.identity;return n.m11=t.m22*e,n.m12=-t.m12*e,n.m21=-t.m21*e,n.m22=t.m11*e,n.m31=(t.m32*t.m21-t.m31*t.m22)*e,n.m32=-(t.m32*t.m11-t.m31*t.m12)*e,n},e.prototype.add=function(t){return this.m11+=t.m11,this.m12+=t.m12,this.m21+=t.m21,this.m22+=t.m22,this.m31+=t.m31,this.m32+=t.m32,this},e.prototype.substract=function(t){return this.m11-=t.m11,this.m12-=t.m12,this.m21-=t.m21,this.m22-=t.m22,this.m31-=t.m31,this.m32-=t.m32,this},e.prototype.divide=function(t){return this.m11/=t.m11,this.m12/=t.m12,this.m21/=t.m21,this.m22/=t.m22,this.m31/=t.m31,this.m32/=t.m32,this},e.prototype.multiply=function(t){var e=this.m11*t.m11+this.m12*t.m21,n=this.m11*t.m12+this.m12*t.m22,i=this.m21*t.m11+this.m22*t.m21,r=this.m21*t.m12+this.m22*t.m22,o=this.m31*t.m11+this.m32*t.m21+t.m31,s=this.m31*t.m12+this.m32*t.m22+t.m32;return this.m11=e,this.m12=n,this.m21=i,this.m22=r,this.m31=o,this.m32=s,this},e.multiply=function(t,e,n){var i=t.m11*e.m11+t.m12*e.m21,r=t.m11*e.m12+t.m12*e.m22,o=t.m21*e.m11+t.m22*e.m21,s=t.m21*e.m12+t.m22*e.m22,a=t.m31*e.m11+t.m32*e.m21+e.m31,c=t.m31*e.m12+t.m32*e.m22+e.m32;n.m11=i,n.m12=r,n.m21=o,n.m22=s,n.m31=a,n.m32=c},e.prototype.determinant=function(){return this.m11*this.m22-this.m12*this.m21},e.lerp=function(t,e,n){return t.m11=t.m11+(e.m11-t.m11)*n,t.m12=t.m12+(e.m12-t.m12)*n,t.m21=t.m21+(e.m21-t.m21)*n,t.m22=t.m22+(e.m22-t.m22)*n,t.m31=t.m31+(e.m31-t.m31)*n,t.m32=t.m32+(e.m32-t.m32)*n,t},e.transpose=function(t){var e=this.identity;return e.m11=t.m11,e.m12=t.m21,e.m21=t.m12,e.m22=t.m22,e.m31=0,e.m32=0,e},e.prototype.mutiplyTranslation=function(n,i){var r=new e;return e.createTranslation(n,i,r),t.MatrixHelper.mutiply(this,r)},e.prototype.equals=function(t){return this==t},e.toMatrix=function(e){var n=new t.Matrix;return n.m11=e.m11,n.m12=e.m12,n.m13=0,n.m14=0,n.m21=e.m21,n.m22=e.m22,n.m23=0,n.m24=0,n.m31=0,n.m32=0,n.m33=1,n.m34=0,n.m41=e.m31,n.m42=e.m32,n.m43=0,n.m44=1,n},e.prototype.toString=function(){return"{m11:"+this.m11+" m12:"+this.m12+" m21:"+this.m21+" m22:"+this.m22+" m31:"+this.m31+" m32:"+this.m32+"}"},e}();t.Matrix2D=e}(es||(es={})),function(t){var e=function(){function e(){}return e.add=function(e,n){var i=t.Matrix2D.identity;return i.m11=e.m11+n.m11,i.m12=e.m12+n.m12,i.m21=e.m21+n.m21,i.m22=e.m22+n.m22,i.m31=e.m31+n.m31,i.m32=e.m32+n.m32,i},e.divide=function(e,n){var i=t.Matrix2D.identity;return i.m11=e.m11/n.m11,i.m12=e.m12/n.m12,i.m21=e.m21/n.m21,i.m22=e.m22/n.m22,i.m31=e.m31/n.m31,i.m32=e.m32/n.m32,i},e.mutiply=function(e,n){var i=t.Matrix2D.identity;if(n instanceof t.Matrix2D){var r=e.m11*n.m11+e.m12*n.m21,o=n.m11*n.m12+e.m12*n.m22,s=e.m21*n.m11+e.m22*n.m21,a=e.m21*n.m12+e.m22*n.m22,c=e.m31*n.m11+e.m32*n.m21+n.m31,h=e.m31*n.m12+e.m32*n.m22+n.m32;i.m11=r,i.m12=o,i.m21=s,i.m22=a,i.m31=c,i.m32=h}else"number"==typeof n&&(i.m11=e.m11*n,i.m12=e.m12*n,i.m21=e.m21*n,i.m22=e.m22*n,i.m31=e.m31*n,i.m32=e.m32*n);return i},e.subtract=function(e,n){var i=t.Matrix2D.identity;return i.m11=e.m11-n.m11,i.m12=e.m12-n.m12,i.m21=e.m21-n.m21,i.m22=e.m22-n.m22,i.m31=e.m31-n.m31,i.m32=e.m32-n.m32,i},e}();t.MatrixHelper=e}(es||(es={})),function(t){var e=function(){function e(e,n,i,r){void 0===e&&(e=0),void 0===n&&(n=0),void 0===i&&(i=0),void 0===r&&(r=0),this.x=0,this.y=0,this.width=0,this.height=0,this._tempMat=new t.Matrix2D,this._transformMat=new t.Matrix2D,this.x=e,this.y=n,this.width=i,this.height=r}return Object.defineProperty(e,"empty",{get:function(){return new e},enumerable:!0,configurable:!0}),Object.defineProperty(e,"maxRect",{get:function(){return new e(Number.MIN_VALUE/2,Number.MIN_VALUE/2,Number.MAX_VALUE,Number.MAX_VALUE)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"left",{get:function(){return this.x},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"right",{get:function(){return this.x+this.width},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"top",{get:function(){return this.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"bottom",{get:function(){return this.y+this.height},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"max",{get:function(){return new t.Vector2(this.right,this.bottom)},enumerable:!0,configurable:!0}),e.prototype.isEmpty=function(){return 0==this.width&&0==this.height&&0==this.x&&0==this.y},Object.defineProperty(e.prototype,"location",{get:function(){return new t.Vector2(this.x,this.y)},set:function(t){this.x=t.x,this.y=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"size",{get:function(){return new t.Vector2(this.width,this.height)},set:function(t){this.width=t.x,this.height=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"center",{get:function(){return new t.Vector2(this.x+this.width/2,this.y+this.height/2)},enumerable:!0,configurable:!0}),e.fromMinMax=function(t,n,i,r){return new e(t,n,i-t,r-n)},e.rectEncompassingPoints=function(t){for(var e=Number.POSITIVE_INFINITY,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY,r=Number.NEGATIVE_INFINITY,o=0;oi&&(i=s.x),s.yr&&(r=s.y)}return this.fromMinMax(e,n,i,r)},e.prototype.getSide=function(e){switch(e){case t.Edge.top:return this.top;case t.Edge.bottom:return this.bottom;case t.Edge.left:return this.left;case t.Edge.right:return this.right;default:throw new Error("Argument Out Of Range")}},e.prototype.contains=function(t,e){return this.x<=t&&tthis.x+this.width)return e}else{var i=1/t.direction.x,r=(this.x-t.start.x)*i,o=(this.x+this.width-t.start.x)*i;if(r>o){var s=r;r=o,o=s}if(e.distance=Math.max(r,e.distance),n=Math.min(o,n),e.distance>n)return e}if(Math.abs(t.direction.y)<1e-6){if(t.start.ythis.y+this.height)return e}else{var a=1/t.direction.y,c=(this.y-t.start.y)*a,h=(this.y+this.height-t.start.y)*a;if(c>h){var u=c;c=h,h=u}if(e.distance=Math.max(c,e.distance),n=Math.max(h,n),e.distance>n)return e}return e.intersected=!0,e},e.prototype.containsRect=function(t){return this.x<=t.x&&t.x0?this.x:this.x+t,i.y=n>0?this.y:this.y+n,i.width=t>0?t+this.width:this.width-t,i.height=n>0?n+this.height:this.height-n,i},e.prototype.collisionCheck=function(t,e,n){e.value=n.value=0;var i=t.x-(this.x+this.width),r=t.x+t.width-this.x,o=t.y-(this.y+this.height),s=t.y+t.height-this.y;return!(i>0||r<0||o>0||s<0)&&(e.value=Math.abs(i)=l||Math.abs(u)>=p)return t.Vector2.zero;var f=h>0?l-h:-l-h,d=u>0?p-u:-p-u;return new t.Vector2(f,d)},e.prototype.equals=function(t){return this===t},e.prototype.getHashCode=function(){return Math.trunc(this.x)^Math.trunc(this.y)^Math.trunc(this.width)^Math.trunc(this.height)},e.prototype.clone=function(){return new e(this.x,this.y,this.width,this.height)},e}();t.Rectangle=e}(es||(es={})),function(t){var e=function(){function t(){this.remainder=0}return t.prototype.update=function(t){this.remainder+=t;var e=Math.trunc(this.remainder);return this.remainder-=e,t=e},t.prototype.reset=function(){this.remainder=0},t}();t.SubpixelFloat=e}(es||(es={})),function(t){var e=function(){function e(){this._x=new t.SubpixelFloat,this._y=new t.SubpixelFloat}return e.prototype.update=function(t){t.x=this._x.update(t.x),t.y=this._y.update(t.y)},e.prototype.reset=function(){this._x.reset(),this._y.reset()},e}();t.SubpixelVector2=e}(es||(es={})),function(t){var e=function(){function e(e){this._activeTriggerIntersections=new t.HashSet,this._previousTriggerIntersections=new t.HashSet,this._tempTriggerList=[],this._entity=e}return e.prototype.update=function(){for(var e,n,i=[],r=this._entity.getComponents(t.Collider),o=0;o=t.Collider.lateSortOrder?i.push(u):this.notifyTriggerListeners(u,!0)),this._activeTriggerIntersections.add(u)}}try{for(var l=__values(i),p=l.next();!p.done;p=l.next()){u=p.value;this.notifyTriggerListeners(u,!0)}}catch(t){e={error:t}}finally{try{p&&!p.done&&(n=l.return)&&n.call(l)}finally{if(e)throw e.error}}this.checkForExitedColliders()},e.prototype.checkForExitedColliders=function(){this._previousTriggerIntersections.exceptWith(this._activeTriggerIntersections.toArray());for(var t=0;t1)return!1;var h=(a.x*r.y-a.y*r.x)/s;return!(h<0||h>1)},n.lineToLineIntersection=function(e,n,i,r,o){void 0===o&&(o=t.Vector2.zero),o.x=0,o.y=0;var s=n.sub(e),a=r.sub(i),c=s.x*a.y-s.y*a.x;if(0==c)return!1;var h=i.sub(e),u=(h.x*a.y-h.y*a.x)/c;if(u<0||u>1)return!1;var l=(h.x*s.y-h.y*s.x)/c;if(l<0||l>1)return!1;var p=e.add(s.scale(u));return o.x=p.x,o.y=p.y,!0},n.closestPointOnLine=function(e,n,i){var r=n.sub(e),o=i.sub(e).dot(r)/r.dot(r);return o=t.MathHelper.clamp(o,0,1),e.add(r.scale(o))},n.circleToCircle=function(e,n,i,r){return t.Vector2.sqrDistance(e,i)<(n+r)*(n+r)},n.circleToLine=function(e,n,i,r){return t.Vector2.sqrDistance(e,this.closestPointOnLine(i,r,e))=t&&r.y>=e&&r.x=t+i&&(s|=e.right),o.y=n+r&&(s|=e.bottom),s},n}();t.Collisions=n}(es||(es={})),function(t){var e=function(){function e(e,n,i,r,o){this.fraction=0,this.distance=0,this.point=t.Vector2.zero,this.normal=t.Vector2.zero,this.collider=e,this.fraction=n,this.distance=i,this.point=r,this.centroid=t.Vector2.zero}return e.prototype.setAllValues=function(t,e,n,i,r){this.collider=t,this.fraction=e,this.distance=n,this.point=i,this.normal=r},e.prototype.setValues=function(t,e,n,i){this.fraction=t,this.distance=e,this.point=n,this.normal=i},e.prototype.reset=function(){this.collider=null,this.fraction=this.distance=0},e.prototype.clone=function(){var t=new e;return t.setAllValues(this.collider,this.fraction,this.distance,this.point,this.normal),t},e.prototype.toString=function(){return"[RaycastHit] fraction: "+this.fraction+", distance: "+this.distance+", normal: "+this.normal+", centroid: "+this.centroid+", point: "+this.point},e}();t.RaycastHit=e}(es||(es={})),function(t){var e=function(){function e(){}return e.reset=function(){this._spatialHash=new t.SpatialHash(this.spatialHashCellSize),this._hitArray[0].reset(),this._colliderArray[0]=null},e.clear=function(){this._spatialHash.clear()},e.debugDraw=function(t){this.debugRender&&this._spatialHash.debugDraw(t)},e.overlapCircle=function(t,n,i){return void 0===i&&(i=e.allLayers),this._colliderArray[0]=null,this._spatialHash.overlapCircle(t,n,this._colliderArray,i),this._colliderArray[0]},e.overlapCircleAll=function(t,e,n,i){return void 0===i&&(i=this.allLayers),this._spatialHash.overlapCircle(t,e,n,i)},e.boxcastBroadphase=function(t,e){return void 0===e&&(e=this.allLayers),this._spatialHash.aabbBroadphase(t,null,e)},e.boxcastBroadphaseExcludingSelf=function(t,e,n){return void 0===n&&(n=this.allLayers),this._spatialHash.aabbBroadphase(e,t,n)},e.boxcastBroadphaseExcludingSelfNonRect=function(t,e){void 0===e&&(e=this.allLayers);var n=t.bounds;return this._spatialHash.aabbBroadphase(n,t,e)},e.boxcastBroadphaseExcludingSelfDelta=function(t,n,i,r){void 0===r&&(r=e.allLayers);var o=t.bounds.getSweptBroadphaseBounds(n,i);return this._spatialHash.aabbBroadphase(o,t,r)},e.addCollider=function(t){e._spatialHash.register(t)},e.removeCollider=function(t){e._spatialHash.remove(t)},e.updateCollider=function(t){this._spatialHash.remove(t),this._spatialHash.register(t)},e.linecast=function(t,n,i,r){return void 0===i&&(i=this.allLayers),void 0===r&&(r=null),this._hitArray[0].reset(),this.linecastAll(t,n,this._hitArray,i),this._hitArray[0].reset(),e.linecastAll(t,n,this._hitArray,i,r),this._hitArray[0].clone()},e.linecastAll=function(t,e,n,i,r){return void 0===i&&(i=this.allLayers),void 0===r&&(r=null),this._spatialHash.linecast(t,e,n,i,r)},e.overlapRectangle=function(t,n){return void 0===n&&(n=e.allLayers),this._colliderArray[0]=null,this._spatialHash.overlapRectangle(t,this._colliderArray,n),this._colliderArray[0]},e.overlapRectangleAll=function(t,n,i){return void 0===i&&(i=e.allLayers),0==n.length?(console.warn("传入了一个空的结果数组。不会返回任何结果"),0):this._spatialHash.overlapRectangle(t,n,i)},e.gravity=new t.Vector2(0,-300),e.spatialHashCellSize=100,e.allLayers=-1,e.raycastsHitTriggers=!1,e.raycastsStartInColliders=!1,e.debugRender=!1,e._hitArray=[new t.RaycastHit],e._colliderArray=[null],e}();t.Physics=e}(es||(es={})),function(t){var e=function(){function t(t,e){this._start=t.clone(),this._end=e.clone(),this._direction=this._end.sub(this._start)}return Object.defineProperty(t.prototype,"start",{get:function(){return this._start},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"direction",{get:function(){return this._direction},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"end",{get:function(){return this._end},enumerable:!0,configurable:!0}),t}();t.Ray2D=e}(es||(es={})),function(t){var e=function(){function e(e){void 0===e&&(e=100),this.gridBounds=new t.Rectangle,this._overlapTestBox=new t.Box(0,0),this._overlapTestCircle=new t.Circle(0),this._cellDict=new n,this._tempHashSet=new Set,this._cellSize=e,this._inverseCellSize=1/this._cellSize,this._raycastParser=new i}return e.prototype.register=function(e){var n=e.bounds.clone();e.registeredPhysicsBounds=n;var i=this.cellCoords(n.x,n.y),r=this.cellCoords(n.right,n.bottom);this.gridBounds.contains(i.x,i.y)||(this.gridBounds=t.RectangleExt.union(this.gridBounds,i)),this.gridBounds.contains(r.x,r.y)||(this.gridBounds=t.RectangleExt.union(this.gridBounds,r));for(var o=i.x;o<=r.x;o++)for(var s=i.y;s<=r.y;s++){this.cellAtPosition(o,s,!0).push(e)}},e.prototype.remove=function(e){for(var n=e.registeredPhysicsBounds.clone(),i=this.cellCoords(n.x,n.y),r=this.cellCoords(n.right,n.bottom),o=i.x;o<=r.x;o++)for(var s=i.y;s<=r.y;s++){var a=this.cellAtPosition(o,s);t.Insist.isNotNull(a,"从不存在碰撞器的单元格中移除碰撞器: ["+e+"]"),null!=a&&new t.List(a).remove(e)}},e.prototype.removeWithBruteForce=function(t){this._cellDict.remove(t)},e.prototype.clear=function(){this._cellDict.clear()},e.prototype.debugDraw=function(t){for(var e=this.gridBounds.x;e<=this.gridBounds.right;e++)for(var n=this.gridBounds.y;n<=this.gridBounds.bottom;n++){var i=this.cellAtPosition(e,n);null!=i&&i.length>0&&this.debugDrawCellDetails(e,n,t)}},e.prototype.debugDrawCellDetails=function(e,n,i){void 0===i&&(i=.5),t.Graphics.instance.batcher.drawHollowRect(e*this._cellSize,n*this._cellSize,this._cellSize,this._cellSize,new t.Color(255,0,0),i),t.Graphics.instance.batcher.end()},e.prototype.aabbBroadphase=function(e,n,i){this._tempHashSet.clear();for(var r=this.cellCoords(e.x,e.y),o=this.cellCoords(e.right,e.bottom),s=r.x;s<=o.x;s++)for(var a=r.y;a<=o.y;a++){var c=this.cellAtPosition(s,a);if(c)for(var h=0;h=this.points.length?this.points[0]:this.points[i+1];var o=t.Vector2Ext.perpendicular(r,e);t.Vector2Ext.normalize(o),this._edgeNormals[i]=o}},n.buildSymmetricalPolygon=function(e,n){for(var i=new Array(e),r=0;ri&&(i=o,n=r)}return t[n]},n.getClosestPointOnPolygonToPoint=function(e,n){for(var i={distanceSquared:Number.MAX_VALUE,edgeNormal:t.Vector2.zero,closestPoint:t.Vector2.zero},r=0,o=0;ot.y!=this.points[i].y>t.y&&t.x<(this.points[i].x-this.points[n].x)*(t.y-this.points[n].y)/(this.points[i].y-this.points[n].y)+this.points[n].x&&(e=!e);return e},n.prototype.pointCollidesWithShape=function(e,n){return t.ShapeCollisionsPoint.pointToPoly(e,this,n)},n}(t.Shape);t.Polygon=e}(es||(es={})),function(t){var e=function(e){function n(t,i){var r=e.call(this,n.buildBox(t,i),!0)||this;return r.width=t,r.height=i,r}return __extends(n,e),n.buildBox=function(e,n){var i=e/2,r=n/2,o=new Array(4);return o[0]=new t.Vector2(-i,-r),o[1]=new t.Vector2(i,-r),o[2]=new t.Vector2(i,r),o[3]=new t.Vector2(-i,r),o},n.prototype.updateBox=function(e,n){this.width=e,this.height=n;var i=e/2,r=n/2;this.points[0]=new t.Vector2(-i,-r),this.points[1]=new t.Vector2(i,-r),this.points[2]=new t.Vector2(i,r),this.points[3]=new t.Vector2(-i,r);for(var o=0;o1)return!1;var c,h=s.start.add(s.direction.scale(r)),u=0;h.xn.bounds.right&&(u|=1),h.yn.bounds.bottom&&(u|=2);var l=c+u;return 3==l&&console.log("m == 3. corner "+t.Time.frameCount),!0},e.corner=function(e,n){var i=t.Vector2.zero;return i.x=0==(1&n)?e.right:e.left,i.y=0==(1&n)?e.bottom:e.top,i},e.testCircleBox=function(t,e,n){var i=e.bounds.getClosestPointOnRectangleToPoint(t.position).sub(t.position);return i.dot(i)<=t.radius*t.radius},e}();t.RealtimeCollisions=e}(es||(es={})),function(t){var e=function(){function e(){}return e.boxToBox=function(e,n,i){var r=this.minkowskiDifference(e,n);return!!r.contains(0,0)&&(i.minimumTranslationVector=r.getClosestPointOnBoundsToOrigin(),!i.minimumTranslationVector.equals(t.Vector2.zero)&&(i.normal=i.minimumTranslationVector.scale(-1),i.normal=i.normal.normalize(),!0))},e.boxToBoxCast=function(e,n,i,r){var o=this.minkowskiDifference(e,n);if(o.contains(0,0)){var s=o.getClosestPointOnBoundsToOrigin();return!s.equals(t.Vector2.zero)&&(r.normal=new t.Vector2(-s.x,-s.y),r.normal=r.normal.normalize(),r.distance=0,r.fraction=0,!0)}var a=new t.Ray2D(t.Vector2.zero,i.scale(-1)),c=o.rayIntersects(a);return!!(c.intersected&&c.distance<=1)&&(r.fraction=c.distance,r.distance=i.magnitude()*c.distance,r.normal=i.scale(-1),r.normal=r.normal.normalize(),r.centroid=e.bounds.center.add(i.scale(c.distance)),!0)},e.minkowskiDifference=function(e,n){var i=e.position.sub(e.bounds.center),r=e.bounds.location.add(i.sub(n.bounds.max)),o=e.bounds.size.add(n.bounds.size);return new t.Rectangle(r.x,r.y,o.x,o.y)},e}();t.ShapeCollisionsBox=e}(es||(es={})),function(t){var e=function(){function e(){}return e.circleToCircleCast=function(e,n,i,r){var o=e.position.add(i),s=this.closestPointOnLine(e.position,o,n.position),a=t.Vector2.sqrDistance(n.position,s),c=(e.radius+n.radius)*(e.radius+n.radius);if(a<=c){var h=i.normalize();s===o&&(o=e.position.add(i.add(h.scale(n.radius))),s=this.closestPointOnLine(e.position,o,n.position),a=t.Vector2.sqrDistance(n.position,s));var u=Math.sqrt(c-a);return r.centroid=s.sub(h.scale(u)),r.normal=r.centroid.sub(n.position).normalize(),r.fraction=(r.centroid.x-e.position.x)/i.x,r.distance=t.Vector2.distance(e.position,r.centroid),r.point=n.position.add(r.normal.scale(n.radius)),!0}return!1},e.circleToCircle=function(e,n,i){void 0===i&&(i=new t.CollisionResult);var r=t.Vector2.sqrDistance(e.position,n.position),o=e.radius+n.radius;if(re.radius*e.radius&&!a)return!1;if(a)s=i.normal.scale(Math.sqrt(o.distanceSquared)-e.radius);else if(0===o.distanceSquared)s=i.normal.scale(e.radius);else{var c=Math.sqrt(o.distanceSquared);s=r.sub(o.closestPoint).scale((e.radius-c)/c*-1)}return i.minimumTranslationVector=s,i.point=o.closestPoint.add(n.position),!0},e.closestPointOnLine=function(e,n,i){var r=n.sub(e),o=i.sub(e).dot(r)/r.dot(r);return o=t.MathHelper.clamp(o,0,1),e.add(r.scaleEqual(o))},e}();t.ShapeCollisionsCircle=e}(es||(es={})),function(t){var e=function(){function e(){}return e.lineToPoly=function(n,i,r,o){void 0===o&&(o=new t.RaycastHit);for(var s=t.Vector2.zero,a=t.Vector2.zero,c=Number.MAX_VALUE,h=!1,u=r.points.length-1,l=0;l1)return!1;var u=(c.x*o.y-c.y*o.x)/a;if(u<0||u>1)return!1;var l=t.add(o.scale(h));return r.x=l.x,r.y=l.y,!0},e.lineToCircle=function(e,n,i,r){var o=t.Vector2.distance(e,n),s=t.Vector2.divideScaler(n.sub(e),o),a=e.sub(i.position),c=a.dot(s),h=a.dot(a)-i.radius*i.radius;if(h>0&&c>0)return!1;var u=c*c-h;return!(u<0)&&(r.fraction=-c-Math.sqrt(u),r.fraction<0&&(r.fraction=0),r.point=e.add(s.scale(r.fraction)),r.distance=t.Vector2.distance(e,r.point),r.normal=r.point.sub(i.position).normalize(),r.fraction=r.distance/o,!0)},e}();t.ShapeCollisionsLine=e}(es||(es={})),function(t){var e=function(){function e(){}return e.pointToCircle=function(e,n,i){var r=t.Vector2.sqrDistance(e,n.position),o=1+n.radius;if(r0&&(o=!1),!o)return!1;(p=Math.abs(p))i.max&&(i.max=n);return i},e.intervalDistance=function(t,e,n,i){return t=0;e--){for(var n=this._composites[e],i=0;in.height?e.y=n.height:e.yn.width&&(e.x=n.width)):(e.yn.height-t.radius&&(e.y=2*(n.height-t.radius)-e.y),e.x>n.width-t.radius&&(e.x=2*(n.width-t.radius)-e.x),e.x=0;t--)this._constraints[t].solve()},e.prototype.updateParticles=function(t,e){for(var n=0;n=0;t--)this._constraints[t].collidesWithColliders&&this._constraints[t].handleCollisions(this.collidesWithLayers)},e.prototype.debugRender=function(e){if(this.drawConstraints)for(var n=0;n0&&h.addConstraint(new t.DistanceConstraint(h.particles[p*o+f],h.particles[p*o+f-1],s)).setTearSensitivity(a).setCollidesWithColliders(!1),p>0&&h.addConstraint(new t.DistanceConstraint(h.particles[p*o+f],h.particles[(p-1)*o+f],s)).setTearSensitivity(a).setCollidesWithColliders(!1),0==p&&y.pin()}return h}return __extends(n,e),n}(t.Composite);t.Cloth=e}(es||(es={})),function(t){var e=function(e){function n(n,i){for(var r=e.call(this)||this,o=0;o0&&r.addConstraint(new t.DistanceConstraint(r.particles[o],r.particles[o-1],i))}return r}return __extends(n,e),n.prototype.pinParticleAtIndex=function(t){return this.particles[t].pin(),this},n}(t.Composite);t.LineSegments=e}(es||(es={})),function(t){var e=function(e){function n(n,i,r){var o=e.call(this)||this,s=r/7.5,a=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)}));a.radius=.75*s,a.mass=4;var c=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)}));c.mass=26,o.addConstraint(new t.DistanceConstraint(a,c,1,5/4*s));var h=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)})),u=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)}));h.mass=2,u.mass=2,o.addConstraint(new t.DistanceConstraint(h,c,1,3*s/2)),o.addConstraint(new t.DistanceConstraint(u,c,1,3*s/2));var l=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)})),p=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)}));l.mass=2,p.mass=2,o.addConstraint(new t.DistanceConstraint(l,h,1,2*s)),o.addConstraint(new t.DistanceConstraint(p,u,1,2*s));var f=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)}));f.mass=15,o.addConstraint(new t.DistanceConstraint(f,c,.8,3.5*s)),o.addConstraint(new t.DistanceConstraint(f,a,.02,2*r)).setCollidesWithColliders(!1);var d=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)})),m=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)}));d.mass=10,m.mass=10,o.addConstraint(new t.DistanceConstraint(d,f,1,2*s)),o.addConstraint(new t.DistanceConstraint(m,f,1,2*s));var y=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)})),g=o.addParticle(new t.Particle({x:n+t.RandomUtils.randint(-5,5),y:i+t.RandomUtils.randint(-5,5)}));return y.mass=5,g.mass=5,o.addConstraint(new t.DistanceConstraint(y,d,1,2*s)),o.addConstraint(new t.DistanceConstraint(g,m,1,2*s)),o.addConstraint(new t.DistanceConstraint(y,c,.001,2*r)).setCollidesWithColliders(!1),o.addConstraint(new t.DistanceConstraint(y,c,.001,2*r)).setCollidesWithColliders(!1),o}return __extends(n,e),n}(t.Composite);t.Ragdoll=e}(es||(es={})),function(t){var e=function(e){function n(n,i,r,o,s){void 0===o&&(o=1),void 0===s&&(s=1);for(var a=e.call(this)||this,c=2*Math.PI/r,h=0;h=Math.PI&&(e-=2*Math.PI),e*=this.stiffness,this._particleA.position=t.MathHelper.rotateAround2(this._particleA.position,this._centerParticle.position,e),this._particleC.position=t.MathHelper.rotateAround2(this._particleC.position,this._centerParticle.position,-e),this._centerParticle.position=t.MathHelper.rotateAround2(this._centerParticle.position,this._particleA.position,e),this._centerParticle.position=t.MathHelper.rotateAround2(this._centerParticle.position,this._particleC.position,-e)},n}(t.Constraint);t.AngleConstraint=e}(es||(es={})),function(t){var e=function(e){function n(t,i,r,o){void 0===o&&(o=-1);var s=e.call(this)||this;return s.stiffness=0,s.restingDistance=0,s.tearSensitivity=Number.POSITIVE_INFINITY,s.shouldApproximateCollisionsWithPoints=!1,s.totalPointsToApproximateCollisionsWith=5,n._polygon.create(2,1),s._particleOne=t,s._particleTwo=i,s.stiffness=r,s.restingDistance=o>-1?o:t.position.distance(i.position),s}return __extends(n,e),n.create=function(e,i,r,o,s){var a=e.position.distance(i.position),c=r.position.distance(i.position);return new n(e,r,o,Math.sqrt(a*a+c*c-2*a*c*Math.cos(s*t.MathHelper.Deg2Rad)))},n.prototype.setTearSensitivity=function(t){return this.tearSensitivity=t,this},n.prototype.setCollidesWithColliders=function(t){return this.collidesWithColliders=t,this},n.prototype.setShouldApproximateCollisionsWithPoints=function(t){return this.shouldApproximateCollisionsWithPoints=t,this},n.prototype.solve=function(){var t=this._particleOne.position.sub(this._particleTwo.position),e=t.distance(),n=(this.restingDistance-e)/e;if(e/this.restingDistance>this.tearSensitivity)this.composite.removeConstraint(this);else{var i=1/this._particleOne.mass,r=i/(i+1/this._particleTwo.mass)*this.stiffness,o=this.stiffness-r;this._particleOne.position=this._particleOne.position.add(t.scale(r*n)),this._particleTwo.position=this._particleTwo.position.sub(t.scale(o*n))}},n.prototype.handleCollisions=function(e){if(this.shouldApproximateCollisionsWithPoints)this.approximateCollisionsWithPoints(e);else{var i=Math.min(this._particleOne.position.x,this._particleTwo.position.x),r=Math.max(this._particleOne.position.x,this._particleTwo.position.x),o=Math.min(this._particleOne.position.y,this._particleTwo.position.y),s=Math.max(this._particleOne.position.y,this._particleTwo.position.y);n._polygon.bounds=t.Rectangle.fromMinMax(i,o,r,s);var a=t.Vector2.zero;this.preparePolygonForCollisionChecks(a);for(var c=t.Physics.boxcastBroadphase(n._polygon.bounds,e),h=0;h=this._duration?(i=this._elapsedTime-this._duration,this._elapsedTime=this._duration,this._tweenState=n.complete):this._isRunningInReverse&&this._elapsedTime<=0&&(i=0-this._elapsedTime,this._elapsedTime=0,this._tweenState=n.complete),this._elapsedTime>=0&&this._elapsedTime<=this._duration&&this.updateValue(),this._loopType!=e.none&&this._tweenState==n.complete&&0!=this._loops&&this.handleLooping(i);var r=this._isTimeScaleIndependent?t.Time.unscaledDeltaTime:t.Time.deltaTime;return r*=this._timeScale,this._isRunningInReverse?this._elapsedTime-=r:this._elapsedTime+=r,this._tweenState==n.complete&&(this._completionHandler&&this._completionHandler(this),null!=this._nextTween&&(this._nextTween.start(),this._nextTween=null),!0)},i.prototype.recycleSelf=function(){this._shouldRecycleTween&&(this._target=null,this._nextTween=null)},i.prototype.isRunning=function(){return this._tweenState==n.running},i.prototype.start=function(){this._isFromValueOverridden||(this._fromValue=this._target.getTargetObject()),this._tweenState==n.complete&&(this._tweenState=n.running,t.TweenManager.addTween(this))},i.prototype.pause=function(){this._tweenState=n.paused},i.prototype.resume=function(){this._tweenState=n.running},i.prototype.stop=function(i){void 0===i&&(i=!1),this._tweenState=n.complete,i?(this._elapsedTime=this._isRunningInReverse?0:this._duration,this._loopType=e.none,this._loops=0):t.TweenManager.removeTween(this)},i.prototype.jumpToElapsedTime=function(e){this._elapsedTime=t.MathHelper.clamp(e,0,this._duration),this.updateValue()},i.prototype.reverseTween=function(){this._isRunningInReverse=!this._isRunningInReverse},i.prototype.waitForCompletion=function(){return __generator(this,function(t){switch(t.label){case 0:return this._tweenState==n.complete?[3,2]:[4,null];case 1:return t.sent(),[3,0];case 2:return[2]}})},i.prototype.getTargetObject=function(){return this._target.getTargetObject()},i.prototype.resetState=function(){this.context=null,this._completionHandler=this._loopCompleteHandler=null,this._isFromValueOverridden=!1,this._isTimeScaleIndependent=!1,this._tweenState=n.complete,this._isRelative=!1,this._easeType=t.TweenManager.defaultEaseType,null!=this._nextTween&&(this._nextTween.recycleSelf(),this._nextTween=null),this._delay=0,this._duration=0,this._timeScale=1,this._elapsedTime=0,this._loopType=e.none,this._delayBetweenLoops=0,this._loops=0,this._isRunningInReverse=!1},i.prototype.initialize=function(t,e,n){this.resetState(),this._target=t,this._toValue=e,this._duration=n},i.prototype.handleLooping=function(t){this._loops--,this._loopType==e.pingpong&&this.reverseTween(),this._loopType!=e.restartFromBeginning&&this._loops%2!=0||this._loopCompleteHandler&&this._completionHandler(this),0!=this._loops&&(this._tweenState=n.running,this._loopType==e.restartFromBeginning?this._elapsedTime=t-this._delayBetweenLoops:this._isRunningInReverse?this._elapsedTime+=this._delayBetweenLoops-t:this._elapsedTime=t-this._delayBetweenLoops,0==this._delayBetweenLoops&&t>0&&this.updateValue())},i}();t.Tween=i}(es||(es={})),function(t){var e=function(e){function n(t,n,i){var r=e.call(this)||this;return r.initialize(t,n,i),r}return __extends(n,e),n.create=function(){return t.TweenManager.cacheNumberTweens?t.Pool.obtain(n):new n},n.prototype.setIsRelative=function(){return this._isRelative=!0,this._toValue+=this._fromValue,this},n.prototype.updateValue=function(){this._target.setTweenedValue(t.Lerps.ease(this._easeType,this._fromValue,this._toValue,this._elapsedTime,this._duration))},n.prototype.recycleSelf=function(){e.prototype.recycleSelf.call(this),this._shouldRecycleTween&&t.TweenManager.cacheNumberTweens&&t.Pool.free(this)},n}(t.Tween);t.NumberTween=e;var n=function(e){function n(t,n,i){var r=e.call(this)||this;return r.initialize(t,n,i),r}return __extends(n,e),n.create=function(){return t.TweenManager.cacheVector2Tweens?t.Pool.obtain(n):new n},n.prototype.setIsRelative=function(){return this._isRelative=!0,this._toValue.add(this._fromValue),this},n.prototype.updateValue=function(){this._target.setTweenedValue(t.Lerps.ease(this._easeType,this._fromValue,this._toValue,this._elapsedTime,this._duration))},n.prototype.recycleSelf=function(){e.prototype.recycleSelf.call(this),this._shouldRecycleTween&&t.TweenManager.cacheVector2Tweens&&t.Pool.free(this)},n}(t.Tween);t.Vector2Tween=n;var i=function(e){function n(t,n,i){var r=e.call(this)||this;return r.initialize(t,n,i),r}return __extends(n,e),n.create=function(){return t.TweenManager.cacheRectTweens?t.Pool.obtain(n):new n},n.prototype.setIsRelative=function(){return this._isRelative=!0,this._toValue=new t.Rectangle(this._toValue.x+this._fromValue.x,this._toValue.y+this._fromValue.y,this._toValue.width+this._fromValue.width,this._toValue.height+this._fromValue.height),this},n.prototype.updateValue=function(){this._target.setTweenedValue(t.Lerps.ease(this._easeType,this._fromValue,this._toValue,this._elapsedTime,this._duration))},n.prototype.recycleSelf=function(){e.prototype.recycleSelf.call(this),this._shouldRecycleTween&&t.TweenManager.cacheRectTweens&&t.Pool.free(this)},n}(t.Tween);t.RectangleTween=i;var r=function(e){function n(t,n,i){var r=e.call(this)||this;return r.initialize(t,n,i),r}return __extends(n,e),n.create=function(){return t.TweenManager.cacheColorTweens?t.Pool.obtain(n):new n},n.prototype.setIsRelative=function(){return this._isRelative=!0,this._toValue.r+=this._fromValue.r,this._toValue.g+=this._fromValue.g,this._toValue.b+=this._fromValue.b,this._toValue.a+=this._fromValue.a,this},n.prototype.updateValue=function(){this._target.setTweenedValue(t.Lerps.ease(this._easeType,this._fromValue,this._toValue,this._elapsedTime,this._duration))},n}(t.Tween);t.ColorTween=r}(es||(es={})),function(t){var e=function(e){function n(){return null!==e&&e.apply(this,arguments)||this}return __extends(n,e),n.prototype.setTweenedValue=function(t){this._renderable.color=t},n.prototype.getTweenedValue=function(){return this._renderable.color},n.prototype.getTargetObject=function(){return this._renderable},n.prototype.updateValue=function(){this.setTweenedValue(t.Lerps.ease(this._easeType,this._fromValue,this._toValue,this._elapsedTime,this._duration))},n.prototype.setTarget=function(t){this._renderable=t},n.prototype.recycleSelf=function(){this._shouldRecycleTween&&(this._renderable=null,this._target=null,this._nextTween=null),this._shouldRecycleTween&&t.TweenManager.cacheColorTweens&&t.Pool.free(this)},n}(t.ColorTween);t.RenderableColorTween=e}(es||(es={})),function(t){var e=function(e){function n(t,n,i){var r=e.call(this)||this;return r.dampingRatio=.23,r.angularFrequency=25,r._transform=t,r._targetType=n,r.setTargetValue(i),r}return __extends(n,e),Object.defineProperty(n.prototype,"targetType",{get:function(){return this._targetType},enumerable:!0,configurable:!0}),n.prototype.setTargetValue=function(e){this._velocity=t.Vector2.zero,this._targetValue=e,this._isCurrentlyManagedByTweenManager||this.start()},n.prototype.updateDampingRatioWithHalfLife=function(t){this.dampingRatio=-t/this.angularFrequency*Math.log(.5)},n.prototype.tick=function(){return this._isPaused||this.setTweenedValue(t.Lerps.fastSpring(this.getCurrentValueOfTweenedTargetType(),this._targetValue,this._velocity,this.dampingRatio,this.angularFrequency)),!1},n.prototype.setTweenedValue=function(e){switch(this._targetType){case t.TransformTargetType.position:this._transform.position=e;break;case t.TransformTargetType.localPosition:this._transform.localPosition=e;break;case t.TransformTargetType.scale:this._transform.scale=e;break;case t.TransformTargetType.localScale:this._transform.localScale=e;break;case t.TransformTargetType.rotationDegrees:this._transform.rotationDegrees=e.x;case t.TransformTargetType.localRotationDegrees:this._transform.localRotationDegrees=e.x}},n.prototype.getCurrentValueOfTweenedTargetType=function(){switch(this._targetType){case t.TransformTargetType.position:return this._transform.position;case t.TransformTargetType.localPosition:return this._transform.localPosition;case t.TransformTargetType.scale:return this._transform.scale;case t.TransformTargetType.localScale:return this._transform.localScale;case t.TransformTargetType.rotationDegrees:return new t.Vector2(this._transform.rotationDegrees);case t.TransformTargetType.localRotationDegrees:return new t.Vector2(this._transform.localRotationDegrees,0);default:return t.Vector2.zero}},n}(t.AbstractTweenable);t.TransformSpringTween=e}(es||(es={})),function(t){var e;!function(t){t[t.position=0]="position",t[t.localPosition=1]="localPosition",t[t.scale=2]="scale",t[t.localScale=3]="localScale",t[t.rotationDegrees=4]="rotationDegrees",t[t.localRotationDegrees=5]="localRotationDegrees"}(e=t.TransformTargetType||(t.TransformTargetType={}));var n=function(n){function i(){return null!==n&&n.apply(this,arguments)||this}return __extends(i,n),i.prototype.setTweenedValue=function(t){switch(this._targetType){case e.position:this._transform.position=t;break;case e.localPosition:this._transform.localPosition=t;break;case e.scale:this._transform.scale=t;break;case e.localScale:this._transform.localScale=t;break;case e.rotationDegrees:this._transform.rotationDegrees=t.x;case e.localRotationDegrees:this._transform.localRotationDegrees=t.x}},i.prototype.getTweenedValue=function(){switch(this._targetType){case e.position:return this._transform.position;case e.localPosition:return this._transform.localPosition;case e.scale:return this._transform.scale;case e.localScale:return this._transform.localScale;case e.rotationDegrees:return new t.Vector2(this._transform.rotationDegrees,this._transform.rotationDegrees);case e.localRotationDegrees:return new t.Vector2(this._transform.localRotationDegrees,0)}},i.prototype.getTargetObject=function(){return this._transform},i.prototype.setTargetAndType=function(t,e){this._transform=t,this._targetType=e},i.prototype.updateValue=function(){this._targetType!=e.rotationDegrees&&this._targetType!=e.localRotationDegrees||this._isRelative?this.setTweenedValue(t.Lerps.ease(this._easeType,this._fromValue,this._toValue,this._elapsedTime,this._duration)):this.setTweenedValue(t.Lerps.easeAngle(this._easeType,this._fromValue,this._toValue,this._elapsedTime,this._duration))},i.prototype.recycleSelf=function(){this._shouldRecycleTween&&(this._target=null,this._nextTween=null,this._transform=null,t.Pool.free(this))},i}(t.Vector2Tween);t.TransformVector2Tween=n}(es||(es={})),function(t){var e;!function(t){t[t.linear=0]="linear",t[t.sineIn=1]="sineIn",t[t.sineOut=2]="sineOut",t[t.sineInOut=3]="sineInOut",t[t.quadIn=4]="quadIn",t[t.quadOut=5]="quadOut",t[t.quadInOut=6]="quadInOut",t[t.quintIn=7]="quintIn",t[t.quintOut=8]="quintOut",t[t.quintInOut=9]="quintInOut",t[t.cubicIn=10]="cubicIn",t[t.cubicOut=11]="cubicOut",t[t.cubicInOut=12]="cubicInOut",t[t.quartIn=13]="quartIn",t[t.quartOut=14]="quartOut",t[t.quartInOut=15]="quartInOut",t[t.expoIn=16]="expoIn",t[t.expoOut=17]="expoOut",t[t.expoInOut=18]="expoInOut",t[t.circleIn=19]="circleIn",t[t.circleOut=20]="circleOut",t[t.circleInOut=21]="circleInOut",t[t.elasticIn=22]="elasticIn",t[t.elasticOut=23]="elasticOut",t[t.elasticInOut=24]="elasticInOut",t[t.punch=25]="punch",t[t.backIn=26]="backIn",t[t.backOut=27]="backOut",t[t.backInOut=28]="backInOut",t[t.bounceIn=29]="bounceIn",t[t.bounceOut=30]="bounceOut",t[t.bounceInOut=31]="bounceInOut"}(e=t.EaseType||(t.EaseType={}));var n=function(){function n(){}return n.oppositeEaseType=function(t){switch(t){case e.linear:return t;case e.backIn:return e.backOut;case e.backOut:return e.backIn;case e.backInOut:return t;case e.bounceIn:return e.bounceOut;case e.bounceOut:return e.bounceIn;case e.bounceInOut:return t;case e.circleIn:return e.circleOut;case e.circleOut:return e.circleIn;case e.circleInOut:return t;case e.cubicIn:return e.cubicOut;case e.cubicOut:return e.cubicIn;case e.circleInOut:case e.punch:return t;case e.expoIn:return e.expoOut;case e.expoOut:return e.expoIn;case e.expoInOut:return t;case e.quadIn:return e.quadOut;case e.quadOut:return e.quadIn;case e.quadInOut:return t;case e.quartIn:return e.quadOut;case e.quartOut:return e.quartIn;case e.quadInOut:return t;case e.sineIn:return e.sineOut;case e.sineOut:return e.sineIn;case e.sineInOut:default:return t}},n.ease=function(n,i,r){switch(n){case e.linear:return t.Easing.Linear.easeNone(i,r);case e.backIn:return t.Easing.Back.easeIn(i,r);case e.backOut:return t.Easing.Back.easeOut(i,r);case e.backInOut:return t.Easing.Back.easeInOut(i,r);case e.bounceIn:return t.Easing.Bounce.easeIn(i,r);case e.bounceOut:return t.Easing.Bounce.easeOut(i,r);case e.bounceInOut:return t.Easing.Bounce.easeInOut(i,r);case e.circleIn:return t.Easing.Circular.easeIn(i,r);case e.circleOut:return t.Easing.Circular.easeOut(i,r);case e.circleInOut:return t.Easing.Circular.easeInOut(i,r);case e.cubicIn:return t.Easing.Cubic.easeIn(i,r);case e.cubicOut:return t.Easing.Cubic.easeOut(i,r);case e.cubicInOut:return t.Easing.Cubic.easeInOut(i,r);case e.elasticIn:return t.Easing.Elastic.easeIn(i,r);case e.elasticOut:return t.Easing.Elastic.easeOut(i,r);case e.elasticInOut:return t.Easing.Elastic.easeInOut(i,r);case e.punch:return t.Easing.Elastic.punch(i,r);case e.expoIn:return t.Easing.Exponential.easeIn(i,r);case e.expoOut:return t.Easing.Exponential.easeOut(i,r);case e.expoInOut:return t.Easing.Exponential.easeInOut(i,r);case e.quadIn:return t.Easing.Quadratic.easeIn(i,r);case e.quadOut:return t.Easing.Quadratic.easeOut(i,r);case e.quadInOut:return t.Easing.Quadratic.easeInOut(i,r);case e.quadIn:return t.Easing.Quadratic.easeIn(i,r);case e.quadOut:return t.Easing.Quadratic.easeOut(i,r);case e.quadInOut:return t.Easing.Quadratic.easeInOut(i,r);case e.quintIn:return t.Easing.Quintic.easeIn(i,r);case e.quintOut:return t.Easing.Quintic.easeOut(i,r);case e.quintInOut:return t.Easing.Quintic.easeInOut(i,r);case e.sineIn:return t.Easing.Sinusoidal.easeIn(i,r);case e.sineOut:return t.Easing.Sinusoidal.easeOut(i,r);case e.sineInOut:return t.Easing.Sinusoidal.easeInOut(i,r);default:return t.Easing.Linear.easeNone(i,r)}},n}();t.EaseHelper=n}(es||(es={})),function(t){var e=function(){function t(){}return Object.defineProperty(t.prototype,"enabled",{get:function(){return this._enabled},set:function(t){this.setEnabled(t)},enumerable:!0,configurable:!0}),t.prototype.setEnabled=function(t){this._enabled!=t&&(this._enabled=t,this._enabled?this.onEnabled():this.onDisabled())},t.prototype.onEnabled=function(){},t.prototype.onDisabled=function(){},t.prototype.update=function(){},t}();t.GlobalManager=e}(es||(es={})),function(t){var e=function(e){function n(){var t=e.call(this)||this;return t._activeTweens=[],t._tempTweens=[],n._instance=t,t}return __extends(n,e),n.prototype.update=function(){this._isUpdating=!0;for(var e=this._activeTweens.length-1;e>=0;--e){var n=this._activeTweens[e];n.tick()&&this._tempTweens.push(n)}this._isUpdating=!1;for(e=0;e=0;--e)n._instance._activeTweens[e].stop(t)},n.allTweensWithContext=function(t){for(var e=[],i=0;i=0;--i)n._instance._activeTweens[i].context==t&&n._instance._activeTweens[i].stop(e)},n.allTweenWithTarget=function(t){for(var e=[],i=0;i=0;--i)if(n._instance._activeTweens[i]){var r=n._instance._activeTweens[i];r.getTargetObject()==t&&r.stop(e)}},n.defaultEaseType=t.EaseType.quartIn,n.removeAllTweensOnLevelLoad=!1,n.cacheNumberTweens=!0,n.cacheVector2Tweens=!0,n.cacheColorTweens=!0,n.cacheRectTweens=!1,n}(t.GlobalManager);t.TweenManager=e}(es||(es={})),function(t){!function(t){var e=function(){function t(){}return t.easeNone=function(t,e){return t/e},t}();t.Linear=e;var n=function(){function t(){}return t.easeIn=function(t,e){return(t/=e)*t},t.easeOut=function(t,e){return-1*(t/=e)*(t-2)},t.easeInOut=function(t,e){return(t/=e/2)<1?.5*t*t:-.5*(--t*(t-2)-1)},t}();t.Quadratic=n;var i=function(){function t(){}return t.easeIn=function(t,e){return(t/=e)*t*(2.70158*t-1.70158)},t.easeOut=function(t,e){return(t=t/e-1)*t*(2.70158*t+1.70158)+1},t.easeInOut=function(t,e){var n=1.70158;return(t/=e/2)<1?t*t*((1+(n*=1.525))*t-n)*.5:.5*((t-=2)*t*((1+(n*=1.525))*t+n)+2)},t}();t.Back=i;var r=function(){function t(){}return t.easeOut=function(t,e){return(t/=e)<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},t.easeIn=function(t,e){return 1-this.easeOut(e-t,e)},t.easeInOut=function(t,e){return t= 2");if(t.sort(function(t,e){return t.t-e.t}),0!==t[0].t)throw new Error("curve must start with 0");if(1!==t[t.length-1].t)throw new Error("curve must end with 1");this._points=t}return Object.defineProperty(e.prototype,"points",{get:function(){return this._points},enumerable:!0,configurable:!0}),e.prototype.lerp=function(e){for(var n=1;n=0;o--)(e=r[o].func).call.apply(e,__spread([r[o].context],n))},n}();t.Emitter=n}(es||(es={})),function(t){!function(t){t[t.top=0]="top",t[t.bottom=1]="bottom",t[t.left=2]="left",t[t.right=3]="right"}(t.Edge||(t.Edge={}))}(es||(es={})),function(t){var e=function(){function t(){}return t.default=function(){return new t},t.prototype.equals=function(t,e){return"function"==typeof t.equals?t.equals(e):t===e},t.prototype.getHashCode=function(t){var e=this;if("number"==typeof t)return this._getHashCodeForNumber(t);if("string"==typeof t)return this._getHashCodeForString(t);var n=385229220;return this.forOwn(t,function(t){"number"==typeof t?n+=e._getHashCodeForNumber(t):"string"==typeof t?n+=e._getHashCodeForString(t):"object"==typeof t&&e.forOwn(t,function(){n+=e.getHashCode(t)})}),n},t.prototype._getHashCodeForNumber=function(t){return t},t.prototype._getHashCodeForString=function(t){for(var e=385229220,n=0;n>7,n+=n<<3,n^=n>>17,n+=n<<5},t}();t.Hash=e}(es||(es={})),function(t){var e=function(){function t(){this._listeners=[]}return t.prototype.addListener=function(t,e){-1===this._listeners.findIndex(function(n){return n.callback===e&&n.caller===t})&&this._listeners.push({caller:t,callback:e})},t.prototype.removeListener=function(t,e){var n=this._listeners.findIndex(function(n){return n.callback===e&&n.caller===t});n>=0&&this._listeners.splice(n,1)},t.prototype.clearListener=function(){this._listeners=[]},t.prototype.clearListenerWithCaller=function(t){for(var e=this._listeners.length-1;e>=0;e--){this._listeners[e].caller===t&&this._listeners.splice(e,1)}},t.prototype.notify=function(){for(var t,e=[],n=0;n=0;i--){var r=this._listeners[i];r.caller?(t=r.callback).call.apply(t,__spread([r.caller],e)):r.callback.apply(r,__spread(e))}},t}();t.Observable=e;var n=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return __extends(e,t),e.prototype.addListener=function(e,n){t.prototype.addListener.call(this,e,n)},e.prototype.removeListener=function(e,n){t.prototype.removeListener.call(this,e,n)},e.prototype.notify=function(e){t.prototype.notify.call(this,e)},e}(e);t.ObservableT=n;var i=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return __extends(e,t),e.prototype.addListener=function(e,n){t.prototype.addListener.call(this,e,n)},e.prototype.removeListener=function(e,n){t.prototype.removeListener.call(this,e,n)},e.prototype.notify=function(e,n){t.prototype.notify.call(this,e,n)},e}(e);t.ObservableTT=i;var r=function(){function t(t,n){this.bindAction(t,n),this._onExec=new e}return t.prototype.bindAction=function(t,e){this._caller=t,this._action=e},t.prototype.dispatch=function(){for(var t,e=[],n=0;n3&&o<500;){o++;var a=!0,c=n[this._triPrev[s]],h=n[s],u=n[this._triNext[s]];if(t.Vector2Ext.isTriangleCCW(c,h,u)){var l=this._triNext[this._triNext[s]];do{if(e.testPointTriangle(n[l],c,h,u)){a=!1;break}l=this._triNext[l]}while(l!=this._triPrev[s])}else a=!1;a?(this.triangleIndices.push(this._triPrev[s]),this.triangleIndices.push(s),this.triangleIndices.push(this._triNext[s]),this._triNext[this._triPrev[s]]=this._triNext[s],this._triPrev[this._triNext[s]]=this._triPrev[s],r--,s=this._triPrev[s]):s=this._triNext[s]}this.triangleIndices.push(this._triPrev[s]),this.triangleIndices.push(s),this.triangleIndices.push(this._triNext[s]),i||this.triangleIndices.reverse()},e.prototype.initialize=function(t){this.triangleIndices.length=0,this._triNext.length>8&255]+e[t>>16&255]+e[t>>24&255]+"-"+e[255&n]+e[n>>8&255]+"-"+e[n>>16&15|64]+e[n>>24&255]+"-"+e[63&i|128]+e[i>>8&255]+"-"+e[i>>16&255]+e[i>>24&255]+e[255&r]+e[r>>8&255]+e[r>>16&255]+e[r>>24&255]},t}();t.UUID=n}(es||(es={})),function(t){t.getClassName=function(t){return t.className||t.name}}(es||(es={})),function(t){var e,n=function(){function t(t){void 0===t&&(t=i),this.getSystemTime=t,this._stopDuration=0,this._completeSlices=[]}return t.prototype.getState=function(){return void 0===this._startSystemTime?e.IDLE:void 0===this._stopSystemTime?e.RUNNING:e.STOPPED},t.prototype.isIdle=function(){return this.getState()===e.IDLE},t.prototype.isRunning=function(){return this.getState()===e.RUNNING},t.prototype.isStopped=function(){return this.getState()===e.STOPPED},t.prototype.slice=function(){return this.recordPendingSlice()},t.prototype.getCompletedSlices=function(){return Array.from(this._completeSlices)},t.prototype.getCompletedAndPendingSlices=function(){return __spread(this._completeSlices,[this.getPendingSlice()])},t.prototype.getPendingSlice=function(){return this.calculatePendingSlice()},t.prototype.getTime=function(){return this.caculateStopwatchTime()},t.prototype.reset=function(){this._startSystemTime=this._pendingSliceStartStopwatchTime=this._stopSystemTime=void 0,this._stopDuration=0,this._completeSlices=[]},t.prototype.start=function(t){if(void 0===t&&(t=!1),t&&this.reset(),void 0!==this._stopSystemTime){var e=(n=this.getSystemTime())-this._stopSystemTime;this._stopDuration+=e,this._stopSystemTime=void 0}else if(void 0===this._startSystemTime){var n=this.getSystemTime();this._startSystemTime=n,this._pendingSliceStartStopwatchTime=0}},t.prototype.stop=function(t){if(void 0===t&&(t=!1),void 0===this._startSystemTime)return 0;var e=this.getSystemTimeOfCurrentStopwatchTime();return t&&this.recordPendingSlice(this.caculateStopwatchTime(e)),this._stopSystemTime=e,this.getTime()},t.prototype.calculatePendingSlice=function(t){return void 0===this._pendingSliceStartStopwatchTime?Object.freeze({startTime:0,endTime:0,duration:0}):(void 0===t&&(t=this.getTime()),Object.freeze({startTime:this._pendingSliceStartStopwatchTime,endTime:t,duration:t-this._pendingSliceStartStopwatchTime}))},t.prototype.caculateStopwatchTime=function(t){return void 0===this._startSystemTime?0:(void 0===t&&(t=this.getSystemTimeOfCurrentStopwatchTime()),t-this._startSystemTime-this._stopDuration)},t.prototype.getSystemTimeOfCurrentStopwatchTime=function(){return void 0===this._stopSystemTime?this.getSystemTime():this._stopSystemTime},t.prototype.recordPendingSlice=function(t){if(void 0!==this._pendingSliceStartStopwatchTime){void 0===t&&(t=this.getTime());var e=this.calculatePendingSlice(t);return this._pendingSliceStartStopwatchTime=e.endTime,this._completeSlices.push(e),e}return this.calculatePendingSlice()},t}();t.Stopwatch=n,function(t){t.IDLE="IDLE",t.RUNNING="RUNNING",t.STOPPED="STOPPED"}(e||(e={})),t.setDefaultSystemTimeGetter=function(t){void 0===t&&(t=Date.now),i=t};var i=Date.now}(es||(es={})),function(t){var e=function(){function t(t){void 0===t&&(t=64),this.size_=0,this.length=0,this.array=[],this.length=t}return t.prototype.removeAt=function(t){var e=this.array[t];return this.array[t]=this.array[--this.size_],this.array[this.size_]=null,e},t.prototype.remove=function(t){var e,n=this.size_;for(e=0;e0){var t=this.array[--this.size_];return this.array[this.size_]=null,t}return null},t.prototype.contains=function(t){var e,n;for(e=0,n=this.size_;n>e;e++)if(t===this.array[e])return!0;return!1},t.prototype.removeAll=function(t){var e,n,i,r,o=!1;for(e=0,i=t.size();e=this.length)throw new Error("ArrayIndexOutOfBoundsException");return this.array[t]},t.prototype.safeGet=function(t){return t>=this.length&&this.grow(7*t/4+1),this.array[t]},t.prototype.size=function(){return this.size_},t.prototype.getCapacity=function(){return this.length},t.prototype.isIndexWithinBounds=function(t){return t=this.length&&this.grow(2*t),this.size_=t+1,this.array[t]=e},t.prototype.grow=function(t){void 0===t&&(t=1+~~(3*this.length/2)),this.length=~~t},t.prototype.ensureCapacity=function(t){t>=this.length&&this.grow(2*t)},t.prototype.clear=function(){var t,e;for(t=0,e=this.size_;te;e++)this.add(t.get(e))},t}();t.Bag=e}(es||(es={})),function(t){var e=function(){function e(e){void 0===e&&(e=1),this._freeValueCellIndex=0,this._collisions=0,this._valuesInfo=new Array(e),this._values=new Array(e),this._buckets=new Array(t.HashHelpers.getPrime(e))}return e.prototype.getValuesArray=function(t){return t.value=this._freeValueCellIndex,this._values},Object.defineProperty(e.prototype,"valuesArray",{get:function(){return this._values},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"count",{get:function(){return this._freeValueCellIndex},enumerable:!0,configurable:!0}),e.prototype.add=function(t,e){if(!this.addValue(t,e,{value:0}))throw new Error("key 已经存在")},e.prototype.addValue=function(i,r,o){var s=t.HashHelpers.getHashCode(i),a=e.reduce(s,this._buckets.length);if(this._freeValueCellIndex==this._values.length){var c=t.HashHelpers.expandPrime(this._freeValueCellIndex);this._values.length=c,this._valuesInfo.length=c}var h=t.NumberExtension.toNumber(this._buckets[a])-1;if(-1==h)this._valuesInfo[this._freeValueCellIndex]=new n(i,s);else{var u=h;do{if(this._valuesInfo[u].hashcode==s&&this._valuesInfo[u].key==i)return this._values[u]=r,o.value=u,!1;u=this._valuesInfo[u].previous}while(-1!=u);this._collisions++,this._valuesInfo[this._freeValueCellIndex]=new n(i,s,h),this._valuesInfo[h].next=this._freeValueCellIndex}if(this._buckets[a]=this._freeValueCellIndex+1,this._values[this._freeValueCellIndex]=r,o.value=this._freeValueCellIndex,this._freeValueCellIndex++,this._collisions>this._buckets.length){this._buckets=new Array(t.HashHelpers.expandPrime(this._collisions)),this._collisions=0;for(var l=0;l=e?t%e:t},e}();t.FasterDictionary=e;var n=function(){return function(t,e,n){void 0===n&&(n=-1),this.key=t,this.hashcode=e,this.previous=n,this.next=-1}}();t.FastNode=n}(es||(es={})),function(t){var e=function(){return function(t,e){this.element=t,this.next=e}}();function n(t,e){return t===e}t.Node=e,t.defaultEquals=n;var i=function(){function t(t){void 0===t&&(t=n),this.count=0,this.next=void 0,this.equalsFn=t,this.head=null}return t.prototype.push=function(t){var n,i=new e(t);if(null==this.head)this.head=i;else{for(n=this.head;null!=n.next;)n=n.next;n.next=i}this.count++},t.prototype.removeAt=function(t){if(t>=0&&t=0&&t<=this.count){for(var e=this.head,n=0;n=0&&n<=this.count){var i=new e(t);if(0===n)i.next=this.head,this.head=i;else{var r=this.getElementAt(n-1);i.next=r.next,r.next=i}return this.count++,!0}return!1},t.prototype.indexOf=function(t){for(var e=this.head,n=0;n0)for(var e=0;ethis._objectQueue.length;)this._objectQueue.shift()},t.clearCache=function(){this._objectQueue.length=0},t.obtain=function(){return this._objectQueue.length>0?this._objectQueue.shift():[]},t.free=function(t){this._objectQueue.unshift(t),t.length=0},t._objectQueue=[],t}();t.ListPool=e}(es||(es={})),function(t){var e=function(){function e(t,e){this.first=t,this.second=e}return e.prototype.clear=function(){this.first=this.second=null},e.prototype.equals=function(t){return this.first==t.first&&this.second==t.second},e.prototype.getHashCode=function(){return 37*t.EqualityComparer.default().getHashCode(this.first)+t.EqualityComparer.default().getHashCode(this.second)},e}();t.Pair=e}(es||(es={})),function(t){var e=function(){function e(){}return e.warmCache=function(t,e){if((e-=this._objectQueue.length)>0)for(var n=0;nthis._objectQueue.length;)this._objectQueue.shift()},e.clearCache=function(){this._objectQueue.length=0},e.obtain=function(t){return this._objectQueue.length>0?this._objectQueue.shift():new t},e.free=function(e){this._objectQueue.unshift(e),t.isIPoolable(e)&&e.reset()},e._objectQueue=[],e}();t.Pool=e,t.isIPoolable=function(t){return void 0!==t.reset}}(es||(es={})),function(t){var e=function(t){function e(e){return t.call(this,e)||this}return __extends(e,t),e.prototype.getHashCode=function(t){return t.getHashCode()},e.prototype.areEqual=function(t,e){return t.equals(e)},e}(function(){function t(t){var e=this;this.clear(),t&&t.forEach(function(t){e.add(t)})}return t.prototype.add=function(t){var e=this,n=this.getHashCode(t),i=this.buckets[n];if(void 0===i){var r=new Array;return r.push(t),this.buckets[n]=r,this.count=this.count+1,!0}return!i.some(function(n){return e.areEqual(n,t)})&&(i.push(t),this.count=this.count+1,!0)},t.prototype.remove=function(t){var e=this,n=this.getHashCode(t),i=this.buckets[n];if(void 0===i)return!1;var r=!1,o=new Array;return i.forEach(function(n){e.areEqual(n,t)?r=!0:o.push(t)}),this.buckets[n]=o,r&&(this.count=this.count-1),r},t.prototype.contains=function(t){return this.bucketsContains(this.buckets,t)},t.prototype.getCount=function(){return this.count},t.prototype.clear=function(){this.buckets=new Array,this.count=0},t.prototype.toArray=function(){var t=new Array;return this.buckets.forEach(function(e){e.forEach(function(e){t.push(e)})}),t},t.prototype.exceptWith=function(t){var e=this;t&&t.forEach(function(t){e.remove(t)})},t.prototype.intersectWith=function(t){var e=this;if(t){var n=this.buildInternalBuckets(t);this.toArray().forEach(function(t){e.bucketsContains(n.Buckets,t)||e.remove(t)})}else this.clear()},t.prototype.unionWith=function(t){var e=this;t.forEach(function(t){e.add(t)})},t.prototype.isSubsetOf=function(t){var e=this,n=this.buildInternalBuckets(t);return this.toArray().every(function(t){return e.bucketsContains(n.Buckets,t)})},t.prototype.isSupersetOf=function(t){var e=this;return t.every(function(t){return e.contains(t)})},t.prototype.overlaps=function(t){var e=this;return t.some(function(t){return e.contains(t)})},t.prototype.setEquals=function(t){var e=this;return this.buildInternalBuckets(t).Count===this.count&&t.every(function(t){return e.contains(t)})},t.prototype.buildInternalBuckets=function(t){var e=this,n=new Array,i=0;return t.forEach(function(t){var r=e.getHashCode(t),o=n[r];if(void 0===o){var s=new Array;s.push(t),n[r]=s,i+=1}else o.some(function(n){return e.areEqual(n,t)})||(o.push(t),i+=1)}),{Buckets:n,Count:i}},t.prototype.bucketsContains=function(t,e){var n=this,i=t[this.getHashCode(e)];return void 0!==i&&i.some(function(t){return n.areEqual(t,e)})},t}());t.HashSet=e}(es||(es={})),function(t){var e=function(){function t(){}return t.waitForSeconds=function(t){return n.waiter.wait(t)},t}();t.Coroutine=e;var n=function(){function t(){this.waitTime=0}return t.prototype.wait=function(e){return t.waiter.waitTime=e,t.waiter},t.waiter=new t,t}();t.WaitForSeconds=n}(es||(es={})),function(t){var e=function(){function t(){this.waitTimer=0,this.useUnscaledDeltaTime=!1}return t.prototype.stop=function(){this.isDone=!0},t.prototype.setUseUnscaledDeltaTime=function(t){return this.useUnscaledDeltaTime=t,this},t.prototype.prepareForUse=function(){this.isDone=!1},t.prototype.reset=function(){this.isDone=!0,this.waitTimer=0,this.waitForCoroutine=null,this.enumerator=null,this.useUnscaledDeltaTime=!1},t}();t.CoroutineImpl=e;var n=function(n){function i(){var t=null!==n&&n.apply(this,arguments)||this;return t._unblockedCoroutines=[],t._shouldRunNextFrame=[],t}return __extends(i,n),i.prototype.startCoroutine=function(n){var i=t.Pool.obtain(e);return i.prepareForUse(),i.enumerator=n,this.tickCoroutine(i)?(this._isInUpdate?this._shouldRunNextFrame.push(i):this._unblockedCoroutines.push(i),i):null},i.prototype.update=function(){this._isInUpdate=!0;for(var e=0;e0?(n.waitTimer-=n.useUnscaledDeltaTime?t.Time.unscaledDeltaTime:t.Time.deltaTime,this._shouldRunNextFrame.push(n)):this.tickCoroutine(n)&&this._shouldRunNextFrame.push(n)}}var i=new t.List(this._unblockedCoroutines);i.clear(),i.addRange(this._shouldRunNextFrame),this._shouldRunNextFrame.length=0,this._isInUpdate=!1},i.prototype.tickCoroutine=function(n){var i=n.enumerator.next();return i.done||n.isDone?(t.Pool.free(n),!1):null==i.value||(i.value instanceof t.WaitForSeconds?(n.waitTimer=i.value.waitTime,!0):"number"==typeof i.value?(n.waitTimer=i.value,!0):"string"==typeof i.value?"break"!=i.value||(t.Pool.free(n),!1):!(i.value instanceof e)||(n.waitForCoroutine=i.value,!0))},i}(t.GlobalManager);t.CoroutineManager=n}(es||(es={})),function(t){var e=function(){function e(t,e,n){void 0===n&&(n=!0),this.binWidth=0,this.binHeight=0,this.usedRectangles=[],this.freeRectangles=[],this.init(t,e,n)}return e.prototype.init=function(e,n,i){void 0===i&&(i=!0),this.binWidth=e,this.binHeight=n,this.allowRotations=i;var r=new t.Rectangle;r.x=0,r.y=0,r.width=e,r.height=n,this.usedRectangles.length=0,this.freeRectangles.length=0,this.freeRectangles.push(r)},e.prototype.insert=function(e,n){var i=new t.Rectangle,r=new t.Ref(0),o=new t.Ref(0);if(0==(i=this.findPositionForNewNodeBestAreaFit(e,n,r,o)).height)return i;for(var s=this.freeRectangles.length,a=0;a=e&&this.freeRectangles[s].height>=n){var c=Math.abs(this.freeRectangles[s].width-e),h=Math.abs(this.freeRectangles[s].height-n),u=Math.min(c,h);(a=n&&this.freeRectangles[s].height>=e){c=Math.abs(this.freeRectangles[s].width-n),h=Math.abs(this.freeRectangles[s].height-e),u=Math.min(c,h);(a=t.x+t.width||e.x+e.width<=t.x||e.y>=t.y+t.height||e.y+e.height<=t.y)return!1;if(e.xt.x){if(e.y>t.y&&e.yt.y){var n;if(e.x>t.x&&e.x=e.x&&t.y>=e.y&&t.x+t.width<=e.x+e.width&&t.y+t.height<=e.y+e.height},e}();t.MaxRectsBinPack=e}(es||(es={})),function(t){var e=function(){function e(){}return e.bubbleSort=function(t){for(var e=!1,n=0;nn;i--)if(t[i]0&&t[r-1]>i;r--)t[r]=t[r-1];t[r]=i}},e.binarySearch=function(t,e){for(var n=0,i=t.length,r=n+i>>1;n=t[r]&&(n=r+1),r=n+i>>1;return t[n]==e?n:-1},e.findElementIndex=function(t,e){for(var n=t.length,i=0;it[e]&&(e=i);return e},e.getMinElementIndex=function(t){for(var e=0,n=t.length,i=1;i=0;--r)n.unshift(e[r]);return n},e.getDifferAry=function(t,e){t=this.getUniqueAry(t),e=this.getUniqueAry(e);for(var n=t.concat(e),i={},r=[],o=n.length,s=0;s=0;e-=1)t.splice(e,1)},e.cloneList=function(t){return t?t.slice(0,t.length):null},e.equals=function(t,e){if(t==e)return!0;var n=t.length;if(n!=e.length)return!1;for(;n--;)if(t[n]!=e[n])return!1;return!0},e.insert=function(t,e,n){if(!t)return null;var i=t.length;if(e>i&&(e=i),e<0&&(e=0),e==i)t.push(n);else if(0==e)t.unshift(n);else{for(var r=i-1;r>=e;r-=1)t[r+1]=t[r];t[e]=n}return n},e.shuffle=function(e){for(var n=e.length;n>1;){n--;var i=t.RandomUtils.randint(0,n+1),r=e[i];e[i]=e[n],e[n]=r}},e.addIfNotPresent=function(e,n){return!new t.List(e).contains(n)&&(e.push(n),!0)},e.lastItem=function(t){return t[t.length-1]},e.randomItem=function(e){return e[t.RandomUtils.randint(0,e.length-1)]},e.randomItems=function(e,n){for(var i=new Set;i.size!=n;){var r=this.randomItem(e);i.has(r)||i.add(r)}var o=t.ListPool.obtain();return i.forEach(function(t){return o.push(t)}),o},e}();t.ArrayUtils=e}(es||(es={})),function(t){var e=function(){function t(){}return Object.defineProperty(t,"nativeBase64",{get:function(){return"function"==typeof window.atob},enumerable:!0,configurable:!0}),t.decode=function(t){if(t=t.replace(/[^A-Za-z0-9\+\/\=]/g,""),this.nativeBase64)return window.atob(t);for(var e,n,i,r,o,s,a=[],c=0;c>4,n=(15&r)<<4|(o=this._keyStr.indexOf(t.charAt(c++)))>>2,i=(3&o)<<6|(s=this._keyStr.indexOf(t.charAt(c++))),a.push(String.fromCharCode(e)),64!==o&&a.push(String.fromCharCode(n)),64!==s&&a.push(String.fromCharCode(i));return a=a.join("")},t.encode=function(t){if(t=t.replace(/\r\n/g,"\n"),!this.nativeBase64){for(var e,n,i,r,o,s,a,c=[],h=0;h>2,o=(3&e)<<4|(n=t.charCodeAt(h++))>>4,s=(15&n)<<2|(i=t.charCodeAt(h++))>>6,a=63&i,isNaN(n)?s=a=64:isNaN(i)&&(a=64),c.push(this._keyStr.charAt(r)),c.push(this._keyStr.charAt(o)),c.push(this._keyStr.charAt(s)),c.push(this._keyStr.charAt(a));return c=c.join("")}window.btoa(t)},t.decodeBase64AsArray=function(e,n){n=n||1;var i,r,o,s=t.decode(e),a=new Uint32Array(s.length/n);for(i=0,o=s.length/n;i=0;--r)a[i]+=s.charCodeAt(i*n+r)<<(r<<3);return a},t.decompress=function(t,e,n){throw new Error("GZIP/ZLIB compressed TMX Tile Map not supported!")},t.decodeCSV=function(t){for(var e=t.replace("\n","").trim().split(","),n=[],i=0;i(e=Math.floor(e))?t++:e++,this.randrange(t,e)},t.randnum=function(t,e){return this.random()*(e-t)+t},t.shuffle=function(t){return t.sort(this._randomCompare),t},t.choice=function(t){if(!t.hasOwnProperty("length"))throw new Error("无法对此对象执行此操作");var e=Math.floor(this.random()*t.length);return t instanceof String?String(t).charAt(e):t[e]},t.sample=function(t,e){var n=t.length;if(e<=0||n=0;)s=Math.floor(this.random()*n);i.push(t[s]),r.push(s)}return i},t.random=function(){return Math.random()},t.boolean=function(t){return void 0===t&&(t=.5),this.random().5?1:-1},t}();t.RandomUtils=e}(es||(es={})),function(t){var e=function(){function e(){}return e.getSide=function(e,n){switch(n){case t.Edge.top:return e.top;case t.Edge.bottom:return e.bottom;case t.Edge.left:return e.left;case t.Edge.right:return e.right}},e.union=function(e,n){var i=new t.Rectangle(n.x,n.y,0,0),r=new t.Rectangle;return r.x=Math.min(e.x,i.x),r.y=Math.min(e.y,i.y),r.width=Math.max(e.right,i.right)-r.x,r.height=Math.max(e.bottom,i.bottom)-r.y,r},e.getHalfRect=function(e,n){switch(n){case t.Edge.top:return new t.Rectangle(e.x,e.y,e.width,e.height/2);case t.Edge.bottom:return new t.Rectangle(e.x,e.y+e.height/2,e.width,e.height/2);case t.Edge.left:return new t.Rectangle(e.x,e.y,e.width/2,e.height);case t.Edge.right:return new t.Rectangle(e.x+e.width/2,e.y,e.width/2,e.height)}},e.getRectEdgePortion=function(e,n,i){switch(void 0===i&&(i=1),n){case t.Edge.top:return new t.Rectangle(e.x,e.y,e.width,i);case t.Edge.bottom:return new t.Rectangle(e.x,e.y+e.height-i,e.width,i);case t.Edge.left:return new t.Rectangle(e.x,e.y,i,e.height);case t.Edge.right:return new t.Rectangle(e.x+e.width-i,e.y,i,e.height)}},e.expandSide=function(e,n,i){switch(i=Math.abs(i),n){case t.Edge.top:e.y-=i,e.height+=i;break;case t.Edge.bottom:e.height+=i;break;case t.Edge.left:e.x-=i,e.width+=i;break;case t.Edge.right:e.width+=i}},e.contract=function(t,e,n){t.x+=e,t.y+=n,t.width-=2*e,t.height-=2*n},e.boundsFromPolygonVector=function(e){for(var n=Number.POSITIVE_INFINITY,i=Number.POSITIVE_INFINITY,r=Number.NEGATIVE_INFINITY,o=Number.NEGATIVE_INFINITY,s=0;sr&&(r=a.x),a.yo&&(o=a.y)}return this.fromMinMaxVector(new t.Vector2(n,i),new t.Vector2(r,o))},e.fromMinMaxVector=function(e,n){return new t.Rectangle(e.x,e.y,n.x-e.x,n.y-e.y)},e.getSweptBroadphaseBounds=function(e,n,i){var r=t.Rectangle.empty;return r.x=n>0?e.x:e.x+n,r.y=i>0?e.y:e.y+i,r.width=n>0?n+e.width:e.width-n,r.height=i>0?i+e.height:e.height-i,r},e.prototype.collisionCheck=function(t,e,n,i){n.value=i.value=0;var r=e.x-(t.x+t.width),o=e.x+e.width-t.x,s=e.y-(t.y+t.height),a=e.y+e.height-t.y;return!(r>0||o<0||s>0||a<0)&&(n.value=Math.abs(r)=l||Math.abs(u)>=p)return t.Vector2.zero;var f=h>0?l-h:-l-h,d=u>0?p-u:-p-u;return new t.Vector2(f,d)},e.getClosestPointOnBoundsToOrigin=function(e){var n=this.getMax(e),i=Math.abs(e.location.x),r=new t.Vector2(e.location.x,0);return Math.abs(n.x)r&&(r=a.x),a.yo&&(o=a.y)}return this.fromMinMaxVector(new t.Vector2(Math.trunc(n),Math.trunc(i)),new t.Vector2(Math.trunc(r),Math.trunc(o)))},e.calculateBounds=function(e,n,i,r,o,s,a,c){if(0==s)e.x=Math.trunc(n.x+i.x-r.x*o.x),e.y=Math.trunc(n.y+i.y-r.y*o.y),e.width=Math.trunc(a*o.x),e.height=Math.trunc(c*o.y);else{var h=n.x+i.x,u=n.y+i.y,l=new t.Matrix2D;t.Matrix2D.createTranslation(-h-r.x,-u-r.y,l),t.Matrix2D.createScale(o.x,o.y,void 0),l=l.multiply(void 0),t.Matrix2D.createRotation(s,void 0),l=l.multiply(void 0),t.Matrix2D.createTranslation(h,u,void 0),l=l.multiply(void 0);var p=new t.Vector2(h,u),f=new t.Vector2(h+a,u),d=new t.Vector2(h,u+c),m=new t.Vector2(h+a,u+c);t.Vector2Ext.transformR(p,l,p),t.Vector2Ext.transformR(f,l,f),t.Vector2Ext.transformR(d,l,d),t.Vector2Ext.transformR(m,l,m);var y=Math.trunc(Math.min(p.x,m.x,f.x,d.x)),g=Math.trunc(Math.max(p.x,m.x,f.x,d.x)),_=Math.trunc(Math.min(p.y,m.y,f.y,d.y)),v=Math.trunc(Math.max(p.y,m.y,f.y,d.y));e.location=new t.Vector2(y,_),e.width=Math.trunc(g-y),e.height=Math.trunc(v-_)}},e.scale=function(t,e){t.x=Math.trunc(t.x*e.x),t.y=Math.trunc(t.y*e.y),t.width=Math.trunc(t.width*e.x),t.height=Math.trunc(t.height*e.y)},e.translate=function(t,e){t.location.addEqual(e)},e}();t.RectangleExt=e}(es||(es={})),function(t){var e=function(){function t(){}return t.premultiplyAlpha=function(t){for(var e=t[0],n=0;nt.MathHelper.Epsilon?e.divideScaler(n):e.x=e.y=0},e.transformA=function(t,e,n,i,r,o){for(var s=0;so?e?-1:1:r0},e.prototype.average=function(t){return this.sum(t)/this.count(t)},e.prototype.cast=function(){return new e(this._elements)},e.prototype.clear=function(){this._elements.length=0},e.prototype.concat=function(t){return new e(this._elements.concat(t.toArray()))},e.prototype.contains=function(t){return this.any(function(e){return e===t})},e.prototype.count=function(t){return t?this.where(t).count():this._elements.length},e.prototype.defaultIfEmpty=function(t){return this.count()?this:new e([t])},e.prototype.distinctBy=function(t){var n=this.groupBy(t);return Object.keys(n).reduce(function(t,e){return t.add(n[e][0]),t},new e)},e.prototype.elementAt=function(t){if(t=0)return this._elements[t];throw new Error("ArgumentOutOfRangeException: index is less than 0 or greater than or equal to the number of elements in source.")},e.prototype.elementAtOrDefault=function(t){return t=0?this._elements[t]:void 0},e.prototype.except=function(t){return this.where(function(e){return!t.contains(e)})},e.prototype.first=function(t){if(this.count())return t?this.where(t).first():this._elements[0];throw new Error("InvalidOperationException: The source sequence is empty.")},e.prototype.firstOrDefault=function(t){return this.count(t)?this.first(t):void 0},e.prototype.forEach=function(t){return this._elements.forEach(t)},e.prototype.groupBy=function(t,e){void 0===e&&(e=function(t){return t});return this.aggregate(function(n,i){var r=t(i),o=n[r],s=e(i);return o?o.push(s):n[r]=[s],n},{})},e.prototype.groupJoin=function(t,e,n,i){return this.select(function(r){return i(r,t.where(function(t){return e(r)===n(t)}))})},e.prototype.indexOf=function(t){return this._elements.indexOf(t)},e.prototype.insert=function(t,e){if(t<0||t>this._elements.length)throw new Error("Index is out of range.");this._elements.splice(t,0,e)},e.prototype.intersect=function(t){return this.where(function(e){return t.contains(e)})},e.prototype.join=function(t,e,n,i){return this.selectMany(function(r){return t.where(function(t){return n(t)===e(r)}).select(function(t){return i(r,t)})})},e.prototype.last=function(t){if(this.count())return t?this.where(t).last():this._elements[this.count()-1];throw Error("InvalidOperationException: The source sequence is empty.")},e.prototype.lastOrDefault=function(t){return this.count(t)?this.last(t):void 0},e.prototype.max=function(t){return Math.max.apply(Math,__spread(this._elements.map(t||function(t){return t})))},e.prototype.min=function(t){return Math.min.apply(Math,__spread(this._elements.map(t||function(t){return t})))},e.prototype.ofType=function(t){var e;switch(t){case Number:e="number";break;case String:e="string";break;case Boolean:e=typeof!0;break;case Function:e="function";break;default:e=null}return null===e?this.where(function(e){return e instanceof t}).cast():this.where(function(t){return typeof t===e}).cast()},e.prototype.orderBy=function(e,i){return void 0===i&&(i=t.keyComparer(e,!1)),new n(this._elements,i)},e.prototype.orderByDescending=function(e,i){return void 0===i&&(i=t.keyComparer(e,!0)),new n(this._elements,i)},e.prototype.thenBy=function(t){return this.orderBy(t)},e.prototype.thenByDescending=function(t){return this.orderByDescending(t)},e.prototype.remove=function(t){return-1!==this.indexOf(t)&&(this.removeAt(this.indexOf(t)),!0)},e.prototype.removeAll=function(e){return this.where(t.negate(e))},e.prototype.removeAt=function(t){this._elements.splice(t,1)},e.prototype.reverse=function(){return new e(this._elements.reverse())},e.prototype.select=function(t){return new e(this._elements.map(t))},e.prototype.selectMany=function(t){var n=this;return this.aggregate(function(e,i,r){return e.addRange(n.select(t).elementAt(r).toArray()),e},new e)},e.prototype.sequenceEqual=function(t){return this.all(function(e){return t.contains(e)})},e.prototype.single=function(t){if(1!==this.count(t))throw new Error("The collection does not contain exactly one element.");return this.first(t)},e.prototype.singleOrDefault=function(t){return this.count(t)?this.single(t):void 0},e.prototype.skip=function(t){return new e(this._elements.slice(Math.max(0,t)))},e.prototype.skipLast=function(t){return new e(this._elements.slice(0,-Math.max(0,t)))},e.prototype.skipWhile=function(t){var e=this;return this.skip(this.aggregate(function(n){return t(e.elementAt(n))?++n:n},0))},e.prototype.sum=function(t){return t?this.select(t).sum():this.aggregate(function(t,e){return t+ +e},0)},e.prototype.take=function(t){return new e(this._elements.slice(0,Math.max(0,t)))},e.prototype.takeLast=function(t){return new e(this._elements.slice(-Math.max(0,t)))},e.prototype.takeWhile=function(t){var e=this;return this.take(this.aggregate(function(n){return t(e.elementAt(n))?++n:n},0))},e.prototype.toArray=function(){return this._elements},e.prototype.toDictionary=function(t,n){var i=this;return this.aggregate(function(e,r,o){return e[i.select(t).elementAt(o).toString()]=n?i.select(n).elementAt(o):r,e.add({Key:i.select(t).elementAt(o),Value:n?i.select(n).elementAt(o):r}),e},new e)},e.prototype.toSet=function(){var t,e,n=new Set;try{for(var i=__values(this._elements),r=i.next();!r.done;r=i.next()){var o=r.value;n.add(o)}}catch(e){t={error:e}}finally{try{r&&!r.done&&(e=i.return)&&e.call(i)}finally{if(t)throw t.error}}return n},e.prototype.toList=function(){return this},e.prototype.toLookup=function(t,e){return this.groupBy(t,e)},e.prototype.where=function(t){return new e(this._elements.filter(t))},e.prototype.zip=function(t,e){var n=this;return t.count()e.angle?1:t.angleMath.PI&&(o-=2*Math.PI),r.p1.begin=o>0,r.p2.begin=!r.p1.begin}}catch(e){t={error:e}}finally{try{i&&!i.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}this._isSpotLight&&(this._spotStartAngle=this._segments[0].p2.angle,this._spotEndAngle=this._segments[1].p2.angle)},e._cornerCache=[],e._openSegments=new t.LinkedList,e}();t.VisibilityComputer=e}(es||(es={})),function(t){var e=function(){function e(){this._timeInSeconds=0,this._repeats=!1,this._isDone=!1,this._elapsedTime=0}return e.prototype.getContext=function(){return this.context},e.prototype.reset=function(){this._elapsedTime=0},e.prototype.stop=function(){this._isDone=!0},e.prototype.tick=function(){return!this._isDone&&this._elapsedTime>this._timeInSeconds&&(this._elapsedTime-=this._timeInSeconds,this._onTime(this),this._isDone||this._repeats||(this._isDone=!0)),this._elapsedTime+=t.Time.deltaTime,this._isDone},e.prototype.initialize=function(t,e,n,i){this._timeInSeconds=t,this._repeats=e,this.context=n,this._onTime=i},e.prototype.unload=function(){this.context=null,this._onTime=null},e}();t.Timer=e}(es||(es={})),function(t){var e=function(e){function n(){var t=null!==e&&e.apply(this,arguments)||this;return t._timers=[],t}return __extends(n,e),n.prototype.update=function(){for(var e=this._timers.length-1;e>=0;e--)this._timers[e].tick()&&(this._timers[e].unload(),new t.List(this._timers).removeAt(e))},n.prototype.schedule=function(e,n,i,r){var o=new t.Timer;return o.initialize(e,n,i,r),this._timers.push(o),o},n}(t.GlobalManager);t.TimerManager=e}(es||(es={})); \ No newline at end of file diff --git a/source/src/Core.ts b/source/src/Core.ts index cefd556a..175efb3d 100644 --- a/source/src/Core.ts +++ b/source/src/Core.ts @@ -43,6 +43,7 @@ module es { Core.emitter.addObserver(CoreEvents.frameUpdated, this.update, this); Core.registerGlobalManager(this._coroutineManager); + Core.registerGlobalManager(new TweenManager()); Core.registerGlobalManager(this._timerManager); Core.entitySystemsEnabled = enableEntitySystems; @@ -122,9 +123,10 @@ module es { * @param type */ public static getGlobalManager(type: new (...args) => T): T { - for (let i = 0; i < this._instance._globalManagers.length; i++) { - if (this._instance._globalManagers[i] instanceof type) - return this._instance._globalManagers[i] as T; + for (let i = 0, s = Core._instance._globalManagers.length; i < s; ++ i) { + let manager = Core._instance._globalManagers[i]; + if (manager instanceof type) + return manager; } return null; } diff --git a/source/src/ECS/Component.ts b/source/src/ECS/Component.ts index 3cc8c5ba..6b580a75 100644 --- a/source/src/ECS/Component.ts +++ b/source/src/ECS/Component.ts @@ -80,9 +80,11 @@ module es { * 当实体的位置改变时调用。这允许组件知道它们由于父实体的移动而移动了。 * @param comp */ - public onEntityTransformChanged(comp: transform.Component) { + public onEntityTransformChanged(comp: ComponentTransform) { } + public debugRender(batcher: IBatcher) {} + /** *当父实体或此组件启用时调用 */ diff --git a/source/src/ECS/Components/Physics/ArcadeRigidbody.ts b/source/src/ECS/Components/Physics/ArcadeRigidbody.ts index 596329ba..fe107568 100644 --- a/source/src/ECS/Components/Physics/ArcadeRigidbody.ts +++ b/source/src/ECS/Components/Physics/ArcadeRigidbody.ts @@ -54,7 +54,7 @@ module es { /** * 该刚体的速度 */ - public velocity: Vector2 = new Vector2(); + public velocity: Vector2 = Vector2.zero; /** * 质量为0的刚体被认为是不可移动的。改变速度和碰撞对它们没有影响 @@ -116,19 +116,30 @@ module es { return this; } + public setVelocity(velocity: Vector2): ArcadeRigidbody { + this.velocity = velocity; + return this; + } + /** * 用刚体的质量给刚体加上一个瞬间的力脉冲。力是一个加速度,单位是每秒像素每秒。将力乘以100000,使数值使用更合理 * @param force */ public addImpulse(force: Vector2) { if (!this.isImmovable) { - this.velocity = this.velocity.add(Vector2.multiplyScaler(force, 100000) - .multiplyScaler(this._inverseMass * Time.deltaTime * Time.deltaTime)); + this.velocity.addEqual(force.scale(100000 * (this._inverseMass * (Time.deltaTime * Time.deltaTime)))); } } public onAddedToEntity() { - this._collider = this.entity.getComponent(es.Collider); + this._collider = null; + for (let i = 0; i < this.entity.components.buffer.length; i++) { + let component = this.entity.components.buffer[i]; + if (component instanceof Collider) { + this._collider = component; + break; + } + } Debug.warnIf(this._collider == null, "ArcadeRigidbody 没有 Collider。ArcadeRigidbody需要一个Collider!"); } @@ -139,14 +150,17 @@ module es { } if (this.shouldUseGravity) - this.velocity = this.velocity.add(Vector2.multiplyScaler(Physics.gravity, Time.deltaTime)); + this.velocity.addEqual(Physics.gravity.scale(Time.deltaTime)); + this.entity.position = this.entity.position.add(this.velocity.scale(Time.deltaTime)); - this.entity.transform.position = this.entity.transform.position.add(Vector2.multiplyScaler(this.velocity, Time.deltaTime)); let collisionResult = new CollisionResult(); // 捞取我们在新的位置上可能会碰撞到的任何东西 - let neighbors = Physics.boxcastBroadphaseExcludingSelfNonRect(this._collider, this._collider.collidesWithLayers.value); - for (let neighbor of neighbors) { + let neighbors = Physics.boxcastBroadphaseExcludingSelf(this._collider, this._collider.bounds, this._collider.collidesWithLayers.value); + for (const neighbor of neighbors) { + if (!neighbor) + continue; + // 如果邻近的对撞机是同一个实体,则忽略它 if (neighbor.entity.equals(this.entity)) { continue; @@ -160,10 +174,9 @@ module es { this.processCollision(neighborRigidbody, collisionResult.minimumTranslationVector); } else { // 没有ArcadeRigidbody,所以我们假设它是不动的,只移动我们自己的 - this.entity.transform.position = this.entity.transform.position.subtract(collisionResult.minimumTranslationVector); - let relativeVelocity = this.velocity.clone(); - this.calculateResponseVelocity(relativeVelocity, collisionResult.minimumTranslationVector, relativeVelocity); - this.velocity = this.velocity.add(relativeVelocity); + this.entity.position = this.entity.position.sub(collisionResult.minimumTranslationVector); + const relativeVelocity = this.calculateResponseVelocity(this.velocity, collisionResult.minimumTranslationVector); + this.velocity.addEqual(relativeVelocity); } } } @@ -176,12 +189,12 @@ module es { */ public processOverlap(other: ArcadeRigidbody, minimumTranslationVector: Vector2) { if (this.isImmovable) { - other.entity.transform.position = other.entity.transform.position.add(minimumTranslationVector); + other.entity.position = other.entity.position.add(minimumTranslationVector); } else if (other.isImmovable) { - this.entity.transform.position = this.entity.transform.position.subtract(minimumTranslationVector); + this.entity.position = this.entity.position.sub(minimumTranslationVector); } else { - this.entity.transform.position = this.entity.transform.position.subtract(Vector2.multiplyScaler(minimumTranslationVector, 0.5)); - other.entity.transform.position = other.entity.transform.position.add(Vector2.multiplyScaler(minimumTranslationVector, 0.5)); + this.entity.position = this.entity.position.sub(minimumTranslationVector.scale(0.5)); + other.entity.position = other.entity.position.add(minimumTranslationVector.scale(0.5)); } } @@ -194,17 +207,18 @@ module es { // 我们计算两个相撞物体的响应。 // 计算的基础是沿碰撞表面法线反射的物体的相对速度。 // 然后,响应的一部分会根据质量加到每个物体上 - let relativeVelocity = Vector2.subtract(this.velocity, other.velocity); + let relativeVelocity = this.velocity.sub(other.velocity); - this.calculateResponseVelocity(relativeVelocity, minimumTranslationVector, relativeVelocity); + relativeVelocity = this.calculateResponseVelocity(relativeVelocity, minimumTranslationVector); // 现在,我们使用质量来线性缩放两个刚体上的响应 - let totalinverseMass = this._inverseMass + other._inverseMass; - let ourResponseFraction = this._inverseMass / totalinverseMass; - let otherResponseFraction = other._inverseMass / totalinverseMass; + const totalinverseMass = this._inverseMass + other._inverseMass; + const ourResponseFraction = this._inverseMass / totalinverseMass; + const otherResponseFraction = other._inverseMass / totalinverseMass; - this.velocity = this.velocity.add(Vector2.multiplyScaler(relativeVelocity, ourResponseFraction)); - other.velocity = other.velocity.subtract(Vector2.multiplyScaler(relativeVelocity, otherResponseFraction)); + + this.velocity = this.velocity.add(relativeVelocity.scale(ourResponseFraction)); + other.velocity = other.velocity.sub(relativeVelocity.scale(otherResponseFraction)); } /** @@ -213,17 +227,16 @@ module es { * @param minimumTranslationVector * @param responseVelocity */ - public calculateResponseVelocity(relativeVelocity: Vector2, minimumTranslationVector: Vector2, responseVelocity: Vector2 = new Vector2()) { + public calculateResponseVelocity(relativeVelocity: Vector2, minimumTranslationVector: Vector2) { // 首先,我们得到反方向的归一化MTV:表面法线 - let inverseMTV = Vector2.multiplyScaler(minimumTranslationVector, -1); - let normal = Vector2.normalize(inverseMTV); - + let inverseMTV = minimumTranslationVector.scale(-1); + let normal = inverseMTV.normalize(); // 速度是沿碰撞法线和碰撞平面分解的。 // 弹性将影响沿法线的响应(法线速度分量),摩擦力将影响速度的切向分量(切向速度分量) - let n = Vector2.dot(relativeVelocity, normal); + let n = relativeVelocity.dot(normal); - let normalVelocityComponent = Vector2.multiplyScaler(normal, n); - let tangentialVelocityComponent = Vector2.subtract(relativeVelocity, normalVelocityComponent); + let normalVelocityComponent = normal.scale(n); + let tangentialVelocityComponent = relativeVelocity.sub(normalVelocityComponent); if (n > 0) normalVelocityComponent = Vector2.zero; @@ -234,8 +247,10 @@ module es { coefficientOfFriction = 1.01; // 弹性影响速度的法向分量,摩擦力影响速度的切向分量 - responseVelocity = Vector2.multiplyScaler(normalVelocityComponent, -(1 + this._elasticity)) - .subtract(Vector2.multiplyScaler(tangentialVelocityComponent, coefficientOfFriction)); + return normalVelocityComponent + .scale(1 + this._elasticity) + .sub(tangentialVelocityComponent.scale(coefficientOfFriction)) + .scale(-1); } } } \ No newline at end of file diff --git a/source/src/ECS/Components/Physics/CharacterController.ts b/source/src/ECS/Components/Physics/CharacterController.ts new file mode 100644 index 00000000..c59def39 --- /dev/null +++ b/source/src/ECS/Components/Physics/CharacterController.ts @@ -0,0 +1,587 @@ +module es { + class CharacterRaycastOrigins { + public topLeft: Vector2; + public bottomRight: Vector2; + public bottomLeft: Vector2; + + public constructor() { + this.topLeft = Vector2.zero; + this.bottomRight = Vector2.zero; + this.bottomLeft = Vector2.zero; + } + } + + class CharacterCollisionState2D { + public right: boolean = false; + public left: boolean = false; + public above: boolean = false; + public below: boolean = false; + public becameGroundedThisFrame: boolean = false; + public wasGroundedLastFrame: boolean = false; + public movingDownSlope: boolean = false; + public slopeAngle: number = 0; + + public hasCollision(): boolean { + return this.below || this.right || this.left || this.above; + } + + public reset(): void { + this.right = this.left = false; + this.above = this.below = false; + this.becameGroundedThisFrame = this.movingDownSlope = false; + this.slopeAngle = 0; + } + + public toString(): string { + return `[CharacterCollisionState2D] r: ${this.right}, l: ${this.left}, a: ${this.above}, b: ${this.below}, movingDownSlope: ${this.movingDownSlope}, angle: ${this.slopeAngle}, wasGroundedLastFrame: ${this.wasGroundedLastFrame}, becameGroundedThisFrame: ${this.becameGroundedThisFrame}`; + } + } + + export class CharacterController implements ITriggerListener { + public onControllerCollidedEvent: ObservableT; + public onTriggerEnterEvent: ObservableT; + public onTriggerExitEvent: ObservableT; + + + /** + * 如果为 true,则在垂直移动单帧时将忽略平台的一种方式 + */ + public ignoreOneWayPlatformsTime: number; + public supportSlopedOneWayPlatforms: boolean; + + public ignoredColliders: Set = new Set(); + + + /** + * 定义距离碰撞射线的边缘有多远。 + * 如果使用 0 范围进行投射,则通常会导致不需要的光线击中(例如,直接从表面水平投射的足部碰撞器可能会导致击中) + */ + public get skinWidth() { + return this._skinWidth; + } + + public set skinWidth(value: number) { + this._skinWidth = value; + this.recalculateDistanceBetweenRays(); + } + + /** + * CC2D 可以爬升的最大坡度角 + */ + public slopeLimit: number = 30; + + /** + * 构成跳跃的帧之间垂直运动变化的阈值 + */ + public jumpingThreshold: number = -7; + + /** + * 基于斜率乘以速度的曲线(负 = 下坡和正 = 上坡) + */ + public slopeSpeedMultiplier: AnimCurve; + + public totalHorizontalRays: number = 5; + public totalVerticalRays: number = 3; + + public collisionState: CharacterCollisionState2D = new CharacterCollisionState2D(); + public velocity: Vector2 = new Vector2(0, 0); + + public get isGrounded(): boolean { + return this.collisionState.below; + } + + public get raycastHitsThisFrame(): RaycastHit[] { + return this._raycastHitsThisFrame; + } + + public constructor( + player: Entity, + skinWidth?: number, + platformMask: number = -1, + onewayPlatformMask: number = -1, + triggerMask: number = -1 + ) { + this.onTriggerEnterEvent = new ObservableT(); + this.onTriggerExitEvent = new ObservableT(); + this.onControllerCollidedEvent = new ObservableT(); + + this.platformMask = platformMask; + this.oneWayPlatformMask = onewayPlatformMask; + this.triggerMask = triggerMask; + + // 将我们的单向平台添加到我们的普通平台掩码中,以便我们可以从上方降落 + this.platformMask |= this.oneWayPlatformMask; + + this._player = player; + let collider = null; + for (let i = 0; i < this._player.components.buffer.length; i++) { + let component = this._player.components.buffer[i]; + if (component instanceof Collider) { + collider = component; + break; + } + } + collider.isTrigger = false; + if (collider instanceof BoxCollider) { + this._collider = collider as BoxCollider; + } else { + throw new Error('player collider must be box'); + } + + // 在这里,我们触发了具有主体的 setter 的属性 + this.skinWidth = skinWidth || collider.width * 0.05; + + this._slopeLimitTangent = Math.tan(75 * MathHelper.Deg2Rad); + this._triggerHelper = new ColliderTriggerHelper(this._player); + + // 我们想设置我们的 CC2D 忽略所有碰撞层,除了我们的 triggerMask + for (let i = 0; i < 32; i++) { + // 查看我们的 triggerMask 是否包含此层,如果不包含则忽略它 + if ((this.triggerMask & (1 << i)) === 0) { + Flags.unsetFlag(this._collider.collidesWithLayers, i); + } + } + } + + public onTriggerEnter(other: Collider, local: Collider): void { + this.onTriggerEnterEvent.notify(other); + } + + public onTriggerExit(other: Collider, local: Collider): void { + this.onTriggerExitEvent.notify(other); + } + + /** + * 尝试将角色移动到位置 + deltaMovement。 任何挡路的碰撞器都会在遇到时导致运动停止 + * @param deltaMovement + * @param deltaTime + */ + public move(deltaMovement: Vector2, deltaTime: number): void { + this.collisionState.wasGroundedLastFrame = this.collisionState.below; + + this.collisionState.reset(); + this._raycastHitsThisFrame = []; + this._isGoingUpSlope = false; + + this.primeRaycastOrigins(); + + if (deltaMovement.y > 0 && this.collisionState.wasGroundedLastFrame) { + deltaMovement = this.handleVerticalSlope(deltaMovement); + } + + if (deltaMovement.x !== 0) { + deltaMovement = this.moveHorizontally(deltaMovement); + } + + if (deltaMovement.y !== 0) { + deltaMovement = this.moveVertically(deltaMovement); + } + + this._player.setPosition( + this._player.position.x + deltaMovement.x, + this._player.position.y + deltaMovement.y + ); + + if (deltaTime > 0) { + this.velocity.x = deltaMovement.x / deltaTime; + this.velocity.y = deltaMovement.y / deltaTime; + } + + if ( + !this.collisionState.wasGroundedLastFrame && + this.collisionState.below + ) { + this.collisionState.becameGroundedThisFrame = true; + } + + if (this._isGoingUpSlope) { + this.velocity.y = 0; + } + + if (!this._isWarpingToGround) { + this._triggerHelper.update(); + } + for (let i = 0; i < this._raycastHitsThisFrame.length; i++) { + this.onControllerCollidedEvent.notify(this._raycastHitsThisFrame[i]); + } + + if (this.ignoreOneWayPlatformsTime > 0) { + this.ignoreOneWayPlatformsTime -= deltaTime; + } + } + + + /** + * 直接向下移动直到接地 + * @param maxDistance + */ + public warpToGrounded(maxDistance: number = 1000): void { + this.ignoreOneWayPlatformsTime = 0; + this._isWarpingToGround = true; + let delta = 0; + do { + delta += 1; + this.move(new Vector2(0, 1), 0.02); + if (delta > maxDistance) { + break; + } + } while (!this.isGrounded); + this._isWarpingToGround = false; + } + + /** + * 这应该在您必须在运行时修改 BoxCollider2D 的任何时候调用。 + * 它将重新计算用于碰撞检测的光线之间的距离。 + * 它也用于 skinWidth setter,以防在运行时更改。 + */ + public recalculateDistanceBetweenRays(): void { + const colliderUsableHeight = + this._collider.height * Math.abs(this._player.scale.y) - + 2 * this._skinWidth; + this._verticalDistanceBetweenRays = + colliderUsableHeight / (this.totalHorizontalRays - 1); + + const colliderUsableWidth = + this._collider.width * Math.abs(this._player.scale.x) - + 2 * this._skinWidth; + this._horizontalDistanceBetweenRays = + colliderUsableWidth / (this.totalVerticalRays - 1); + } + + /** + * 将 raycastOrigins 重置为由 skinWidth 插入的框碰撞器的当前范围。 + * 插入它是为了避免从直接接触另一个碰撞器的位置投射光线,从而导致不稳定的法线数据。 + */ + private primeRaycastOrigins(): void { + const rect = this._collider.bounds; + this._raycastOrigins.topLeft = new Vector2( + rect.x + this._skinWidth, + rect.y + this._skinWidth + ); + this._raycastOrigins.bottomRight = new Vector2( + rect.right - this._skinWidth, + rect.bottom - this._skinWidth + ); + this._raycastOrigins.bottomLeft = new Vector2( + rect.x + this._skinWidth, + rect.bottom - this._skinWidth + ); + } + + /** + * 我们必须在这方面使用一些技巧。 + * 光线必须从我们的碰撞器(skinWidth)内部的一小段距离投射,以避免零距离光线会得到错误的法线。 + * 由于这个小偏移,我们必须增加光线距离 skinWidth 然后记住在实际移动玩家之前从 deltaMovement 中删除 skinWidth + * @param deltaMovement + * @returns + */ + private moveHorizontally(deltaMovement: Vector2): Vector2 { + const isGoingRight = deltaMovement.x > 0; + let rayDistance = + Math.abs(deltaMovement.x) + + this._skinWidth * this.rayOriginSkinMutiplier; + const rayDirection: Vector2 = isGoingRight ? Vector2.right : Vector2.left; + const initialRayOriginY = this._raycastOrigins.bottomLeft.y; + const initialRayOriginX = isGoingRight + ? this._raycastOrigins.bottomRight.x - + this._skinWidth * (this.rayOriginSkinMutiplier - 1) + : this._raycastOrigins.bottomLeft.x + + this._skinWidth * (this.rayOriginSkinMutiplier - 1); + + for (let i = 0; i < this.totalHorizontalRays; i++) { + const ray = new Vector2( + initialRayOriginX, + initialRayOriginY - i * this._verticalDistanceBetweenRays + ); + + // 如果我们接地,我们将只在第一条射线(底部)上包含 oneWayPlatforms。 + // 允许我们走上倾斜的 oneWayPlatforms + if ( + i === 0 && + this.supportSlopedOneWayPlatforms && + this.collisionState.wasGroundedLastFrame + ) { + this._raycastHit = Physics.linecast( + ray, + ray.add(rayDirection.scaleEqual(rayDistance)), + this.platformMask, + this.ignoredColliders + ); + } else { + this._raycastHit = Physics.linecast( + ray, + ray.add(rayDirection.scaleEqual(rayDistance)), + this.platformMask & ~this.oneWayPlatformMask, + this.ignoredColliders + ); + } + + if (this._raycastHit.collider) { + if ( + i === 0 && + this.handleHorizontalSlope( + deltaMovement, + Vector2.unsignedAngle(this._raycastHit.normal, Vector2.up) + ) + ) { + this._raycastHitsThisFrame.push(this._raycastHit); + break; + } + + deltaMovement.x = this._raycastHit.point.x - ray.x; + rayDistance = Math.abs(deltaMovement.x); + + if (isGoingRight) { + deltaMovement.x -= this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.right = true; + } else { + deltaMovement.x += this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.left = true; + } + + this._raycastHitsThisFrame.push(this._raycastHit); + + if ( + rayDistance < + this._skinWidth * this.rayOriginSkinMutiplier + + this.kSkinWidthFloatFudgeFactor + ) { + break; + } + } + } + + return deltaMovement; + } + + private moveVertically(deltaMovement: Vector2): Vector2 { + const isGoingUp = deltaMovement.y < 0; + let rayDistance = + Math.abs(deltaMovement.y) + + this._skinWidth * this.rayOriginSkinMutiplier; + const rayDirection = isGoingUp ? Vector2.up : Vector2.down; + + let initialRayOriginX = this._raycastOrigins.topLeft.x; + const initialRayOriginY = isGoingUp + ? this._raycastOrigins.topLeft.y + + this._skinWidth * (this.rayOriginSkinMutiplier - 1) + : this._raycastOrigins.bottomLeft.y - + this._skinWidth * (this.rayOriginSkinMutiplier - 1); + + initialRayOriginX += deltaMovement.x; + + let mask = this.platformMask; + if (isGoingUp || this.ignoreOneWayPlatformsTime > 0) { + mask &= ~this.oneWayPlatformMask; + } + + for (let i = 0; i < this.totalVerticalRays; i++) { + const rayStart = new Vector2( + initialRayOriginX + i * this._horizontalDistanceBetweenRays, + initialRayOriginY + ); + this._raycastHit = Physics.linecast( + rayStart, + rayStart.add(rayDirection.scaleEqual(rayDistance)), + mask, + this.ignoredColliders + ); + if (this._raycastHit.collider) { + deltaMovement.y = this._raycastHit.point.y - rayStart.y; + rayDistance = Math.abs(deltaMovement.y); + + if (isGoingUp) { + deltaMovement.y += this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.above = true; + } else { + deltaMovement.y -= this._skinWidth * this.rayOriginSkinMutiplier; + this.collisionState.below = true; + } + + this._raycastHitsThisFrame.push(this._raycastHit); + + if (!isGoingUp && deltaMovement.y < -0.00001) { + this._isGoingUpSlope = true; + } + + if ( + rayDistance < + this._skinWidth * this.rayOriginSkinMutiplier + + this.kSkinWidthFloatFudgeFactor + ) { + break; + } + } + } + + return deltaMovement; + } + + /** + * 检查 BoxCollider2D 下的中心点是否存在坡度。 + * 如果找到一个,则调整 deltaMovement 以便玩家保持接地,并考虑slopeSpeedModifier 以加快移动速度。 + * @param deltaMovement + * @returns + */ + private handleVerticalSlope(deltaMovement: Vector2): Vector2 { + const centerOfCollider = + (this._raycastOrigins.bottomLeft.x + + this._raycastOrigins.bottomRight.x) * + 0.5; + const rayDirection = Vector2.down; + + const slopeCheckRayDistance = + this._slopeLimitTangent * + (this._raycastOrigins.bottomRight.x - centerOfCollider); + + const slopeRay = new Vector2( + centerOfCollider, + this._raycastOrigins.bottomLeft.y + ); + + this._raycastHit = Physics.linecast( + slopeRay, + slopeRay.add(rayDirection.scaleEqual(slopeCheckRayDistance)), + this.platformMask, + this.ignoredColliders + ); + if (this._raycastHit.collider) { + const angle = Vector2.unsignedAngle(this._raycastHit.normal, Vector2.up); + if (angle === 0) { + return deltaMovement; + } + + const isMovingDownSlope = + Math.sign(this._raycastHit.normal.x) === Math.sign(deltaMovement.x); + if (isMovingDownSlope) { + const slopeModifier = this.slopeSpeedMultiplier + ? this.slopeSpeedMultiplier.lerp(-angle) + : 1; + deltaMovement.y += + this._raycastHit.point.y - slopeRay.y - this.skinWidth; + deltaMovement.x *= slopeModifier; + this.collisionState.movingDownSlope = true; + this.collisionState.slopeAngle = angle; + } + } + + return deltaMovement; + } + + /** + * 如果我们要上坡,则处理调整 deltaMovement + * @param deltaMovement + * @param angle + * @returns + */ + private handleHorizontalSlope( + deltaMovement: Vector2, + angle: number + ): boolean { + if (Math.round(angle) === 90) { + return false; + } + + if (angle < this.slopeLimit) { + if (deltaMovement.y > this.jumpingThreshold) { + const slopeModifier = this.slopeSpeedMultiplier + ? this.slopeSpeedMultiplier.lerp(angle) + : 1; + deltaMovement.x *= slopeModifier; + + deltaMovement.y = Math.abs( + Math.tan(angle * MathHelper.Deg2Rad) * deltaMovement.x + ); + const isGoingRight = deltaMovement.x > 0; + + const ray = isGoingRight + ? this._raycastOrigins.bottomRight + : this._raycastOrigins.bottomLeft; + let raycastHit = null; + if ( + this.supportSlopedOneWayPlatforms && + this.collisionState.wasGroundedLastFrame + ) { + raycastHit = Physics.linecast( + ray, + ray.add(deltaMovement), + this.platformMask, + this.ignoredColliders + ); + } else { + raycastHit = Physics.linecast( + ray, + ray.add(deltaMovement), + this.platformMask & ~this.oneWayPlatformMask, + this.ignoredColliders + ); + } + + if (raycastHit.collider) { + deltaMovement.x = raycastHit.point.x - ray.x; + deltaMovement.y = raycastHit.point.y - ray.y; + if (isGoingRight) { + deltaMovement.x -= this._skinWidth; + } else { + deltaMovement.x += this._skinWidth; + } + } + + this._isGoingUpSlope = true; + this.collisionState.below = true; + } + } else { + deltaMovement.x = 0; + } + + return true; + } + + private _player: Entity; + private _collider: BoxCollider; + private _skinWidth: number = 0.02; + private _triggerHelper: ColliderTriggerHelper; + + /** + * 这用于计算为检查坡度而投射的向下光线。 + * 我们使用有点随意的值 75 度来计算检查斜率的射线的长度。 + */ + private _slopeLimitTangent: number; + + private readonly kSkinWidthFloatFudgeFactor: number = 0.001; + + /** + * 我们的光线投射原点角的支架(TR、TL、BR、BL) + */ + private _raycastOrigins: CharacterRaycastOrigins = new CharacterRaycastOrigins(); + + /** + * 存储我们在移动过程中命中的光线投射 + */ + private _raycastHit: RaycastHit = new RaycastHit(); + + /** + * 存储此帧发生的任何光线投射命中。 + * 我们必须存储它们,以防我们遇到水平和垂直移动的碰撞,以便我们可以在设置所有碰撞状态后发送事件 + */ + private _raycastHitsThisFrame: RaycastHit[]; + + // 水平/垂直移动数据 + private _verticalDistanceBetweenRays: number; + private _horizontalDistanceBetweenRays: number; + + /** + * 我们使用这个标志来标记我们正在爬坡的情况,我们修改了 delta.y 以允许爬升。 + * 原因是,如果我们到达斜坡的尽头,我们可以进行调整以保持接地 + */ + private _isGoingUpSlope: boolean = false; + + private _isWarpingToGround: boolean = true; + + private platformMask: number = -1; + private triggerMask: number = -1; + private oneWayPlatformMask: number = -1; + + private readonly rayOriginSkinMutiplier = 4; + } +} \ No newline at end of file diff --git a/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts b/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts index e1738c43..6bfaf632 100644 --- a/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts +++ b/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts @@ -8,10 +8,15 @@ module es { * @param width * @param height */ - constructor(x: number, y: number, width: number, height: number) { + constructor(x: number = 0, y: number = 0, width: number = 1, height: number = 1) { super(); - this._localOffset = new Vector2(x + width / 2, y + height / 2); + if (width == 1 && height == 1) { + this._colliderRequiresAutoSizing = true; + } else { + this._localOffset = new Vector2(x + width / 2, y + height / 2); + } + this.shape = new Box(width, height); } @@ -61,7 +66,7 @@ module es { // 更新框,改变边界,如果我们需要更新物理系统中的边界 box.updateBox(width, box.height); this._isPositionDirty = true; - if (this.entity && this._isParentEntityAddedToScene) + if (this.entity != null && this._isParentEntityAddedToScene) Physics.updateCollider(this); } @@ -84,6 +89,18 @@ module es { } } + public debugRender(batcher: IBatcher) { + let poly = this.shape as Polygon; + batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new Color(76, 76, 76, 76), 2); + batcher.end(); + batcher.drawPolygon(this.shape.position, poly.points, new Color(139, 0, 0, 255), true, 2); + batcher.end(); + batcher.drawPixel(this.entity.position, new Color(255, 255, 0), 4); + batcher.end(); + batcher.drawPixel(es.Vector2.add(this.transform.position, this.shape.center), new Color(255, 0, 0), 2); + batcher.end(); + } + public toString() { return `[BoxCollider: bounds: ${this.bounds}]`; } diff --git a/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts b/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts index b7908563..4169df62 100644 --- a/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts +++ b/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts @@ -7,10 +7,13 @@ module es { * * @param radius */ - constructor(radius: number) { + constructor(radius: number = 1) { super(); this.shape = new Circle(radius); + if (radius == 1) { + this._colliderRequiresAutoSizing = true; + } } public get radius(): number { @@ -40,6 +43,17 @@ module es { return this; } + public debugRender(batcher: IBatcher) { + batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new Color(76, 76, 76, 76), 2); + batcher.end(); + batcher.drawCircle(this.shape.position, this.radius, new Color(139, 0, 0), 2); + batcher.end(); + batcher.drawPixel(this.entity.transform.position, new Color(255, 255, 0), 4); + batcher.end(); + batcher.drawPixel(this.shape.position, new Color(255, 0, 0), 2); + batcher.end(); + } + public toString() { return `[CircleCollider: bounds: ${this.bounds}, radius: ${(this.shape as Circle).radius}]` } diff --git a/source/src/ECS/Components/Physics/Colliders/Collider.ts b/source/src/ECS/Components/Physics/Colliders/Collider.ts index c9447c40..7e1c18ab 100644 --- a/source/src/ECS/Components/Physics/Colliders/Collider.ts +++ b/source/src/ECS/Components/Physics/Colliders/Collider.ts @@ -1,5 +1,7 @@ module es { - export class Collider extends Component { + export abstract class Collider extends Component { + public static readonly lateSortOrder = 999; + public castSortOrder: number = 0; /** * 对撞机的基本形状 */ @@ -94,8 +96,8 @@ module es { public setLocalOffset(offset: Vector2): Collider { if (!this._localOffset.equals(offset)) { this.unregisterColliderWithPhysicsSystem(); - this._localOffset = offset; - this._localOffsetLength = this._localOffset.length(); + this._localOffset.setTo(offset.x, offset.y); + this._localOffsetLength = this._localOffset.magnitude(); this._isPositionDirty = true; this.registerColliderWithPhysicsSystem(); } @@ -114,6 +116,33 @@ module es { } public onAddedToEntity() { + if (this._colliderRequiresAutoSizing) { + let renderable = null; + for (let i = 0; i < this.entity.components.buffer.length; i ++) { + let component = this.entity.components.buffer[i]; + if (component instanceof RenderableComponent){ + renderable = component; + break; + } + } + + if (renderable != null) { + let renderableBounds = renderable.bounds.clone(); + + let width = renderableBounds.width / this.entity.transform.scale.x; + let height = renderableBounds.height / this.entity.transform.scale.y; + + if (this instanceof CircleCollider) { + this.radius = Math.max(width, height) * 0.5; + this.localOffset = renderableBounds.center.sub(this.entity.transform.position); + } else if (this instanceof BoxCollider) { + this.width = width; + this.height = height; + + this.localOffset = renderableBounds.center.sub(this.entity.transform.position); + } + } + } this._isParentEntityAddedToScene = true; this.registerColliderWithPhysicsSystem(); } @@ -123,15 +152,15 @@ module es { this._isParentEntityAddedToScene = false; } - public onEntityTransformChanged(comp: transform.Component) { + public onEntityTransformChanged(comp: ComponentTransform) { switch (comp) { - case transform.Component.position: + case ComponentTransform.position: this._isPositionDirty = true; break; - case transform.Component.scale: + case ComponentTransform.scale: this._isPositionDirty = true; break; - case transform.Component.rotation: + case ComponentTransform.rotation: this._isRotationDirty = true; break; } @@ -186,10 +215,10 @@ module es { */ public collidesWith(collider: Collider, motion: Vector2, result: CollisionResult = new CollisionResult()): boolean { // 改变形状的位置,使它在移动后的位置,这样我们可以检查重叠 - let oldPosition = this.entity.position.clone(); - this.entity.position = Vector2.add(this.entity.position, motion); + const oldPosition = this.entity.position; + this.entity.position = this.entity.position.add(motion); - let didCollide = this.shape.collidesWithShape(collider.shape, result); + const didCollide = this.shape.collidesWithShape(collider.shape, result); if (didCollide) result.collider = collider; @@ -210,6 +239,7 @@ module es { return true; } + result.collider = null; return false; } @@ -236,14 +266,14 @@ module es { continue; if (this.collidesWithNonMotion(neighbor, result)) { - motion = Vector2.subtract(motion, result.minimumTranslationVector); - this.shape.position = Vector2.subtract(this.shape.position, result.minimumTranslationVector); + motion = motion.sub(result.minimumTranslationVector); + this.shape.position = this.shape.position.sub(result.minimumTranslationVector); didCollide = true; } } // 将形状位置返回到检查之前的位置 - this.shape.position = oldPosition; + this.shape.position = oldPosition.clone(); return didCollide; } diff --git a/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts b/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts index f5cc0bf1..dce67199 100644 --- a/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts +++ b/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts @@ -13,10 +13,9 @@ module es { // 第一点和最后一点决不能相同。我们想要一个开放的多边形 let isPolygonClosed = points[0] == points[points.length - 1]; - let linqPoints = new es.List(points); // 最后一个移除 if (isPolygonClosed) - linqPoints.remove(linqPoints.last()); + points = points.slice(0, points.length - 1); let center = Polygon.findPolygonCenter(points); this.setLocalOffset(center); diff --git a/source/src/ECS/Components/Physics/Mover.ts b/source/src/ECS/Components/Physics/Mover.ts index 4c7fa8bc..245aad59 100644 --- a/source/src/ECS/Components/Physics/Mover.ts +++ b/source/src/ECS/Components/Physics/Mover.ts @@ -19,12 +19,26 @@ module es { * @param collisionResult */ public calculateMovement(motion: Vector2, collisionResult: CollisionResult): boolean { - if (this.entity.getComponent(Collider) == null || this._triggerHelper == null) { + let collider = null; + for (let i = 0; i < this.entity.components.buffer.length; i++) { + let component = this.entity.components.buffer[i]; + if (component instanceof Collider) { + collider = component; + break; + } + } + if (collider == null || this._triggerHelper == null) { return false; } // 移动所有的非触发碰撞器并获得最近的碰撞 - let colliders: Collider[] = this.entity.getComponents(Collider); + let colliders: Collider[] = []; + for (let i = 0; i < this.entity.components.buffer.length; i ++) { + let component = this.entity.components.buffer[i]; + if (component instanceof Collider) { + colliders.push(component); + } + } for (let i = 0; i < colliders.length; i++) { let collider = colliders[i]; @@ -38,8 +52,7 @@ module es { bounds.y += motion.y; let neighbors = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers.value); - neighbors.forEach(value => { - let neighbor = value; + for (let neighbor of neighbors) { // 不检测触发器 if (neighbor.isTrigger) return; @@ -47,14 +60,17 @@ module es { let _internalcollisionResult: CollisionResult = new CollisionResult(); if (collider.collidesWith(neighbor, motion, _internalcollisionResult)) { // 如果碰撞 则退回之前的移动量 - motion.subtract(_internalcollisionResult.minimumTranslationVector); + motion.sub(_internalcollisionResult.minimumTranslationVector); // 如果我们碰到多个对象,为了简单起见,只取第一个。 if (_internalcollisionResult.collider != null) { - collisionResult = _internalcollisionResult; + collisionResult.collider = _internalcollisionResult.collider; + collisionResult.minimumTranslationVector = _internalcollisionResult.minimumTranslationVector; + collisionResult.normal = _internalcollisionResult.normal; + collisionResult.point = _internalcollisionResult.point; } } - }); + } } ListPool.free(colliders); diff --git a/source/src/ECS/Components/Physics/ProjectileMover.ts b/source/src/ECS/Components/Physics/ProjectileMover.ts index 3416f836..fb87f21f 100644 --- a/source/src/ECS/Components/Physics/ProjectileMover.ts +++ b/source/src/ECS/Components/Physics/ProjectileMover.ts @@ -8,7 +8,15 @@ module es { private _collider: Collider; public onAddedToEntity() { - this._collider = this.entity.getComponent(Collider); + let collider = null; + for (let i = 0; i < this.entity.components.buffer.length; i++) { + let component = this.entity.components.buffer[i]; + if (component instanceof Collider) { + collider = component; + break; + } + } + this._collider = collider; Debug.warnIf(this._collider == null, "ProjectileMover没有Collider。ProjectilMover需要一个Collider!"); } diff --git a/source/src/ECS/Components/Renderables/IRenderable.ts b/source/src/ECS/Components/Renderables/IRenderable.ts new file mode 100644 index 00000000..956f7aa7 --- /dev/null +++ b/source/src/ECS/Components/Renderables/IRenderable.ts @@ -0,0 +1,9 @@ +module es { + export interface IRenderable { + enabled: boolean; + renderLayer: number; + isVisibleFromCamera(camera: ICamera): boolean; + render(batcher: IBatcher, camera: ICamera): void; + debugRender(batcher: IBatcher): void; + } +} \ No newline at end of file diff --git a/source/src/ECS/Components/Renderables/RenderableComponent.ts b/source/src/ECS/Components/Renderables/RenderableComponent.ts new file mode 100644 index 00000000..56b56ec4 --- /dev/null +++ b/source/src/ECS/Components/Renderables/RenderableComponent.ts @@ -0,0 +1,133 @@ +module es { + export abstract class RenderableComponent extends es.Component implements IRenderable { + public getwidth() { + return this.bounds.width; + } + + public getheight() { + return this.bounds.height; + } + + protected _bounds: es.Rectangle = new es.Rectangle(); + public getbounds(): es.Rectangle { + if (this._areBoundsDirty) { + this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, new es.Vector2(this.getwidth() / 2, this.getheight() / 2), + this.entity.transform.scale, this.entity.transform.rotation, this.getwidth(), this.getheight()); + this._areBoundsDirty = false; + } + return this._bounds; + } + public get bounds() { + return this.getbounds(); + } + protected _areBoundsDirty: boolean = true; + public color: Color = Color.White; + + public get renderLayer() { + return this._renderLayer; + } + public set renderLayer(value: number) { + this.setRenderLayer(value); + } + + protected _renderLayer: number = 0; + + public onEntityTransformChanged(comp: ComponentTransform) { + this._areBoundsDirty = true; + } + + public get localOffset() { + return this._localOffset; + } + public set localOffset(value: es.Vector2) { + this.setLocalOffset(value); + } + + public setLocalOffset(offset: es.Vector2) { + if (!this._localOffset.equals(offset)) { + this._localOffset = offset; + this._areBoundsDirty = true; + } + + return this; + } + + public get isVisible() { + return this._isVisible; + } + + public set isVisible(value: boolean) { + if (this._isVisible != value) { + this._isVisible = value; + + if (this._isVisible) { + this.onBecameVisible(); + } else { + this.onBecameInvisible(); + } + } + } + + public debugRenderEnabled: boolean = true; + + protected _isVisible: boolean = false; + protected _localOffset: es.Vector2 = new es.Vector2(); + + public abstract render(batcher: IBatcher, camera: ICamera): void; + + protected onBecameVisible() { + + } + + protected onBecameInvisible() { + + } + + public setRenderLayer(renderLayer: number): RenderableComponent { + if (renderLayer != this._renderLayer) { + let oldRenderLayer = this._renderLayer; + this._renderLayer = renderLayer; + + if (this.entity != null && this.entity.scene != null) + es.Core.scene.renderableComponents.updateRenderableRenderLayer(this, oldRenderLayer, this._renderLayer); + } + + return this; + } + + public isVisibleFromCamera(cam: ICamera): boolean { + this.isVisible = cam.bounds.intersects(this.bounds); + + return this.isVisible; + } + + public debugRender(batcher: IBatcher) { + if (!this.debugRenderEnabled) + return; + + let collider = null; + for (let i = 0; i < this.entity.components.buffer.length; i++) { + let component = this.entity.components.buffer[i]; + if (component instanceof Collider) { + collider = component; + break; + } + } + + if (collider == null) { + batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new Color(255, 255, 0)); + batcher.end(); + } + + batcher.drawPixel(es.Vector2.add(this.entity.transform.position, this._localOffset), new Color(153, 50, 204), 4); + batcher.end(); + } + + public tweenColorTo(to: Color, duration: number) { + const tween = Pool.obtain(RenderableColorTween); + tween.setTarget(this); + tween.initialize(tween, to, duration); + return tween; + } + } +} \ No newline at end of file diff --git a/source/src/ECS/CoreEvents.ts b/source/src/ECS/CoreEvents.ts index 53b4e93a..4058458e 100644 --- a/source/src/ECS/CoreEvents.ts +++ b/source/src/ECS/CoreEvents.ts @@ -8,5 +8,9 @@ module es { * 每帧更新事件 */ frameUpdated, + /** + * 当渲染发生时触发 + */ + renderChanged, } } diff --git a/source/src/ECS/Entity.ts b/source/src/ECS/Entity.ts index c823ffaf..8f3c9ebf 100644 --- a/source/src/ECS/Entity.ts +++ b/source/src/ECS/Entity.ts @@ -192,7 +192,7 @@ module es { return this.transform.worldToLocalTransform; } - public onTransformChanged(comp: transform.Component) { + public onTransformChanged(comp: ComponentTransform) { // 通知我们的子项改变了位置 this.components.onEntityTransformChanged(comp); } @@ -245,7 +245,7 @@ module es { if (scale instanceof Vector2) { this.transform.setScale(scale); } else { - this.transform.setScale(new Vector2(scale)); + this.transform.setScale(new Vector2(scale, scale)); } return this; @@ -257,7 +257,7 @@ module es { if (scale instanceof Vector2) { this.transform.setLocalScale(scale); } else { - this.transform.setLocalScale(new Vector2(scale)); + this.transform.setLocalScale(new Vector2(scale, scale)); } return this; @@ -376,6 +376,11 @@ module es { this.components.update(); } + public debugRender(batcher: IBatcher) { + if (!batcher) return; + this.components.debugRender(batcher); + } + /** * 创建组件的新实例。返回实例组件 * @param componentType @@ -486,6 +491,66 @@ module es { } } + public tweenPositionTo(to: Vector2, duration: number = 0.3): ITween { + const tween = Pool.obtain(TransformVector2Tween); + tween.setTargetAndType(this.transform, TransformTargetType.position); + tween.initialize(tween, to, duration); + + return tween; + } + + public tweenLocalPositionTo(to: Vector2, duration = 0.3): ITween { + const tween = Pool.obtain(TransformVector2Tween); + tween.setTargetAndType(this.transform, TransformTargetType.localPosition); + tween.initialize(tween, to, duration); + + return tween; + } + + public tweenScaleTo(to: Vector2, duration?: number); + public tweenScaleTo(to: number, duration?: number); + public tweenScaleTo(to: Vector2 | number, duration: number = 0.3) { + if (typeof (to) == 'number') { + return this.tweenScaleTo(new Vector2(to, to), duration); + } + + const tween = Pool.obtain(TransformVector2Tween); + tween.setTargetAndType(this.transform, TransformTargetType.scale); + tween.initialize(tween, to, duration); + + return tween; + } + + public tweenLocalScaleTo(to: Vector2, duration?); + public tweenLocalScaleTo(to: number, duration?); + public tweenLocalScaleTo(to: Vector2 | number, duration = 0.3) { + if (typeof (to) == 'number') { + return this.tweenLocalScaleTo(new Vector2(to, to), duration); + } + + const tween = Pool.obtain(TransformVector2Tween); + tween.setTargetAndType(this.transform, TransformTargetType.localScale); + tween.initialize(tween, to, duration); + + return tween; + } + + public tweenRotationDegreesTo(to: number, duration = 0.3) { + const tween = Pool.obtain(TransformVector2Tween); + tween.setTargetAndType(this.transform, TransformTargetType.rotationDegrees); + tween.initialize(tween, new Vector2(to, to), duration); + + return tween; + } + + public tweenLocalRotationDegreesTo(to: number, duration = 0.3) { + const tween = Pool.obtain(TransformVector2Tween); + tween.setTargetAndType(this.transform, TransformTargetType.localRotationDegrees); + tween.initialize(tween, new Vector2(to, to), duration); + + return tween; + } + public compareTo(other: Entity): number { let compare = this._updateOrder - other._updateOrder; if (compare == 0) diff --git a/source/src/ECS/Scene.ts b/source/src/ECS/Scene.ts index 32669a56..5f7b0b10 100644 --- a/source/src/ECS/Scene.ts +++ b/source/src/ECS/Scene.ts @@ -2,22 +2,22 @@ module es { /** 场景 */ export class Scene { - /** - * 这个场景中的实体列表 - */ + public camera: ICamera; + /** 这个场景中的实体列表 */ public readonly entities: EntityList; - - /** - * 管理所有实体处理器 - */ + public readonly renderableComponents: RenderableComponentList; + /** 管理所有实体处理器 */ public readonly entityProcessors: EntityProcessorList; public readonly _sceneComponents: SceneComponent[] = []; + public _renderers: Renderer[] = []; public readonly identifierPool: IdentifierPool; private _didSceneBegin: boolean; constructor() { this.entities = new EntityList(this); + this.renderableComponents = new RenderableComponentList(); + this.entityProcessors = new EntityProcessorList(); this.identifierPool = new IdentifierPool(); @@ -45,6 +45,10 @@ module es { } public begin() { + if (this._renderers.length == 0) { + this.addRenderer(new DefaultRenderer()); + } + Physics.reset(); if (this.entityProcessors != null) @@ -58,6 +62,9 @@ module es { public end() { this._didSceneBegin = false; + for (let i = 0; i < this._renderers.length; i ++) + this._renderers[i].unload(); + this.entities.removeAllEntities(); for (let i = 0; i < this._sceneComponents.length; i++) { @@ -65,6 +72,7 @@ module es { } this._sceneComponents.length = 0; + this.camera = null; Physics.clear(); if (this.entityProcessors) @@ -91,6 +99,38 @@ module es { if (this.entityProcessors != null) this.entityProcessors.lateUpdate(); + + this.renderableComponents.updateLists(); + this.render(); + } + + public render() { + for (let i = 0; i < this._renderers.length; i ++) { + this._renderers[i].render(this); + } + } + + public addRenderer(renderer: T): T { + this._renderers.push(renderer); + this._renderers.sort((self, other) => self.renderOrder - other.renderOrder); + + renderer.onAddedToScene(this); + + return renderer; + } + + public getRenderer(type: new (...args: any[]) => T): T { + for (let i = 0; i < this._renderers.length; i ++) { + if (this._renderers[i] instanceof type) + return this._renderers[i] as T; + } + + return null; + } + + public removeRenderer(renderer: Renderer) { + new List(this._renderers).remove(renderer); + renderer.unload(); } /** diff --git a/source/src/ECS/Transform.ts b/source/src/ECS/Transform.ts index 1653c161..1d813bc2 100644 --- a/source/src/ECS/Transform.ts +++ b/source/src/ECS/Transform.ts @@ -1,12 +1,10 @@ -module transform { - export enum Component { +module es { + export enum ComponentTransform { position, scale, rotation, } -} -module es { export enum DirtyType { clean = 0, positionDirty = 1, @@ -28,14 +26,14 @@ module es { /** * 值会根据位置、旋转和比例自动重新计算 */ - public _localTransform: Matrix2D; + public _localTransform: Matrix2D = Matrix2D.identity; /** * 值将自动从本地和父矩阵重新计算。 */ public _worldTransform = Matrix2D.identity; public _rotationMatrix: Matrix2D = Matrix2D.identity; public _translationMatrix: Matrix2D = Matrix2D.identity; - public _scaleMatrix: Matrix2D; + public _scaleMatrix: Matrix2D = Matrix2D.identity; public _children: Transform[] = []; constructor(entity: Entity) { @@ -106,7 +104,7 @@ module es { public get worldToLocalTransform(): Matrix2D { if (this._worldToLocalDirty) { - if (!this.parent) { + if (this.parent == null) { this._worldToLocalTransform = Matrix2D.identity; } else { this.parent.updateTransform(); @@ -267,13 +265,13 @@ module es { return this; if (this._parent != null) { - let children = new es.List(this._parent._children); - children.remove(this); + const index = this._parent._children.findIndex(t => t == this); + if (index != -1) + this._parent._children.splice(index, 1); } if (parent != null) { - let children = new es.List(parent._children); - children.add(this); + parent._children.push(this); } this._parent = parent; @@ -294,7 +292,7 @@ module es { this._position = position; if (this.parent != null) { - this.localPosition = Vector2.transform(this._position, this._worldToLocalTransform); + this.localPosition = Vector2.transform(this._position, this.worldToLocalTransform); } else { this.localPosition = position; } @@ -325,7 +323,7 @@ module es { */ public setRotation(radians: number): Transform { this._rotation = radians; - if (this.parent) { + if (this.parent != null) { this.localRotation = this.parent.rotation + radians; } else { this.localRotation = radians; @@ -347,9 +345,9 @@ module es { * @param pos */ public lookAt(pos: Vector2) { - let sign = this.position.x > pos.x ? -1 : 1; - let vectorToAlignTo = Vector2.normalize(Vector2.subtract(this.position, pos)); - this.rotation = sign * Math.acos(Vector2.dot(vectorToAlignTo, Vector2.unitY)); + const sign = this.position.x > pos.x ? -1 : 1; + const vectorToAlignTo = this.position.sub(pos).normalize(); + this.rotation = sign * Math.acos(vectorToAlignTo.dot(Vector2.unitY)); } /** @@ -378,7 +376,7 @@ module es { */ public setScale(scale: Vector2): Transform { this._scale = scale; - if (this.parent) { + if (this.parent != null) { this.localScale = Vector2.divide(scale, this.parent._scale); } else { this.localScale = scale; @@ -412,22 +410,22 @@ module es { if (this._localDirty) { if (this._localPositionDirty) { - this._translationMatrix = Matrix2D.createTranslation(this._localPosition.x, this._localPosition.y); + Matrix2D.createTranslation(this._localPosition.x, this._localPosition.y, this._translationMatrix); this._localPositionDirty = false; } if (this._localRotationDirty) { - this._rotationMatrix = Matrix2D.createRotation(this._localRotation); + Matrix2D.createRotation(this._localRotation, this._rotationMatrix); this._localRotationDirty = false; } if (this._localScaleDirty) { - this._scaleMatrix = Matrix2D.createScale(this._localScale.x, this._localScale.y); + Matrix2D.createScale(this._localScale.x, this._localScale.y, this._scaleMatrix); this._localScaleDirty = false; } - this._localTransform = this._scaleMatrix.multiply(this._rotationMatrix); - this._localTransform = this._localTransform.multiply(this._translationMatrix); + Matrix2D.multiply(this._scaleMatrix, this._rotationMatrix, this._localTransform); + Matrix2D.multiply(this._localTransform, this._translationMatrix, this._localTransform); if (this.parent == null) { this._worldTransform = this._localTransform; @@ -440,10 +438,9 @@ module es { } if (this.parent != null) { - this._worldTransform = this._localTransform.multiply(this.parent._worldTransform); - + Matrix2D.multiply(this._localTransform, this.parent._worldTransform, this._worldTransform); this._rotation = this._localRotation + this.parent._rotation; - this._scale = Vector2.multiply(this.parent._scale, this._localScale); + this._scale = this.parent._scale.multiply(this._localScale);; this._worldInverseDirty = true; } @@ -458,14 +455,14 @@ module es { this.hierarchyDirty |= dirtyFlagType; switch (dirtyFlagType) { - case es.DirtyType.positionDirty: - this.entity.onTransformChanged(transform.Component.position); + case DirtyType.positionDirty: + this.entity.onTransformChanged(ComponentTransform.position); break; - case es.DirtyType.rotationDirty: - this.entity.onTransformChanged(transform.Component.rotation); + case DirtyType.rotationDirty: + this.entity.onTransformChanged(ComponentTransform.rotation); break; - case es.DirtyType.scaleDirty: - this.entity.onTransformChanged(transform.Component.scale); + case DirtyType.scaleDirty: + this.entity.onTransformChanged(ComponentTransform.scale); break; } @@ -480,8 +477,8 @@ module es { * @param transform */ public copyFrom(transform: Transform) { - this._position = transform.position; - this._localPosition = transform._localPosition; + this._position = transform.position.clone(); + this._localPosition = transform._localPosition.clone(); this._rotation = transform._rotation; this._localRotation = transform._localRotation; this._scale = transform._scale; diff --git a/source/src/ECS/Utils/ComponentList.ts b/source/src/ECS/Utils/ComponentList.ts index 15710ce8..b8ef5be5 100644 --- a/source/src/ECS/Utils/ComponentList.ts +++ b/source/src/ECS/Utils/ComponentList.ts @@ -77,16 +77,16 @@ module es { for (let i = 0, s = this._components.length; i < s; ++ i) { this.handleRemove(this._components[i]); } - - this.componentsByType.clear(); - this.componentsToAddByType.clear(); - this._components.length = 0; - this._updatableComponents.length = 0; - this._componentsToAdd = {}; - this._componentsToRemove = {}; - this._componentsToAddList.length = 0; - this._componentsToRemoveList.length = 0; } + + this.componentsByType.clear(); + this.componentsToAddByType.clear(); + this._components.length = 0; + this._updatableComponents.length = 0; + this._componentsToAdd = {}; + this._componentsToRemove = {}; + this._componentsToAddList.length = 0; + this._componentsToRemoveList.length = 0; } public deregisterAllComponents() { @@ -95,6 +95,9 @@ module es { let component = this._components[i]; if (!component) continue; + if (component instanceof RenderableComponent) + this._entity.scene.renderableComponents.remove(component); + // 处理IUpdatable if (isIUpdatable(component)) new es.List(this._updatableComponents).remove(component); @@ -109,6 +112,9 @@ module es { if (this._components.length > 0) { for (let i = 0, s = this._components.length; i < s; ++ i) { let component = this._components[i]; + if (component instanceof RenderableComponent) + this._entity.scene.renderableComponents.remove(component); + if (isIUpdatable(component)) this._updatableComponents.push(component); @@ -151,6 +157,9 @@ module es { if (this._componentsToAddList.length > 0) { for (let i = 0, l = this._componentsToAddList.length; i < l; ++ i) { let component = this._componentsToAddList[i]; + if (component instanceof RenderableComponent) + this._entity.scene.renderableComponents.add(component); + if (isIUpdatable(component)) this._updatableComponents.push(component); @@ -186,6 +195,9 @@ module es { } public handleRemove(component: Component) { + if (component instanceof RenderableComponent) + this._entity.scene.renderableComponents.remove(component); + if (isIUpdatable(component) && this._updatableComponents.length > 0) { let index = this._updatableComponents.findIndex((c) => (c as Component).id == component.id); if (index != -1) @@ -282,7 +294,7 @@ module es { } } - public onEntityTransformChanged(comp: transform.Component) { + public onEntityTransformChanged(comp: ComponentTransform) { if (this._components.length > 0 ){ for (let i = 0, s = this._components.length; i < s; ++ i) { let component = this._components[i]; @@ -313,5 +325,14 @@ module es { this._components[i].onDisabled(); } } + + public debugRender(batcher: IBatcher) { + if (!batcher) return; + for (let i = 0; i < this._components.length; i ++) { + if (this._components[i].enabled) { + this._components[i].debugRender(batcher); + } + } + } } } diff --git a/source/src/ECS/Utils/RenderableComponentList.ts b/source/src/ECS/Utils/RenderableComponentList.ts new file mode 100644 index 00000000..6b1e219f --- /dev/null +++ b/source/src/ECS/Utils/RenderableComponentList.ts @@ -0,0 +1,81 @@ +module es { + export class RenderableComponentList { + private _components: IRenderable[] = []; + private _componentsByRenderLayer: Map = new Map(); + private _unsortedRenderLayers: number[] = []; + private _componentsNeedSort = true; + + public get count() { + return this._components.length; + } + + public get(index: number) { + return this._components[index]; + } + + public add(component: IRenderable) { + this._components.push(component); + this.addToRenderLayerList(component, component.renderLayer); + } + + public remove(component: IRenderable) { + new List(this._components).remove(component); + new List(this._componentsByRenderLayer.get(component.renderLayer)).remove(component); + } + + public updateRenderableRenderLayer(component: IRenderable, oldRenderLayer: number, newRenderLayer: number) { + if (this._componentsByRenderLayer.has(oldRenderLayer) && new List(this._componentsByRenderLayer.get(oldRenderLayer)).contains(component)) { + new List(this._componentsByRenderLayer.get(oldRenderLayer)).remove(component); + this.addToRenderLayerList(component, newRenderLayer); + } + } + + public setRenderLayerNeedsComponentSort(renderLayer: number) { + const unsortedRenderLayersList = new List(this._unsortedRenderLayers); + if (!unsortedRenderLayersList.contains(renderLayer)) + unsortedRenderLayersList.add(renderLayer); + this._componentsNeedSort = true; + } + + public setNeedsComponentSort() { + this._componentsNeedSort = true; + } + + private addToRenderLayerList(component: IRenderable, renderLayer: number) { + let list = this.componentsWithRenderLayer(renderLayer); + es.Insist.isFalse(!!list.find(c => c == component), "组件renderLayer列表已包含此组件"); + + list.push(component); + const unsortedRenderLayersList = new List(this._unsortedRenderLayers); + if (!unsortedRenderLayersList.contains(renderLayer)) + unsortedRenderLayersList.add(renderLayer); + this._componentsNeedSort = true; + } + + public componentsWithRenderLayer(renderLayer: number) { + if (!this._componentsByRenderLayer.get(renderLayer)) { + this._componentsByRenderLayer.set(renderLayer, []); + } + + return this._componentsByRenderLayer.get(renderLayer); + } + + public updateLists() { + if (this._componentsNeedSort) { + this._components.sort((self, other) => other.renderLayer - self.renderLayer); + this._componentsNeedSort = false; + } + + if (this._unsortedRenderLayers.length > 0) { + for (let i = 0, count = this._unsortedRenderLayers.length; i < count; i ++) { + const renderLayerComponents = this._componentsByRenderLayer.get(this._unsortedRenderLayers[i]); + if (renderLayerComponents) { + renderLayerComponents.sort((self, other) => other.renderLayer - self.renderLayer); + } + + this._unsortedRenderLayers.length = 0; + } + } + } + } +} \ No newline at end of file diff --git a/source/src/Graphics/Batcher/Color.ts b/source/src/Graphics/Batcher/Color.ts new file mode 100644 index 00000000..ca9d291a --- /dev/null +++ b/source/src/Graphics/Batcher/Color.ts @@ -0,0 +1,475 @@ +module es { + export class Color { + /** + * 红色通道 + */ + public r: number; + /** + * 绿色通道 + */ + public g: number; + /** + * 蓝色通道 + */ + public b: number; + /** + * 透明度通道 (仅0-1之间) + */ + public a: number; + + /** + * 色调 + */ + public h: number; + /** + * 饱和 + */ + public s: number; + /** + * 亮度 + */ + public l: number; + + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + constructor(r: number, g: number, b: number, a?: number) { + this.r = r; + this.g = g; + this.b = b; + this.a = a != null ? a : 1; + } + + /** + * 从 r, g, b, a 创建一个新的 Color 实例 + * + * @param r 颜色的红色分量 (0-255) + * @param g 颜色的绿色成分 (0-255) + * @param b 颜色的蓝色分量 (0-255) + * @param a 颜色的 alpha 分量 (0-1.0) + */ + public static fromRGB(r: number, g: number, b: number, a?: number): Color { + return new Color(r, g, b, a); + } + + /** + * 从十六进制字符串创建一个新的 Color 实例 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + public static createFromHex(hex: string): Color { + const color = new Color(1, 1, 1); + color.fromHex(hex); + return color; + } + + /** + * 从 hsl 值创建一个新的 Color 实例 + * + * @param h 色调表示 [0-1] + * @param s 饱和度表示为 [0-1] + * @param l 亮度表示 [0-1] + * @param a 透明度表示 [0-1] + */ + public static fromHSL( + h: number, + s: number, + l: number, + a: number = 1.0 + ): Color { + const temp = new HSLColor(h, s, l, a); + return temp.toRGBA(); + } + + /** + * 将当前颜色调亮指定的量 + * + * @param factor + */ + public lighten(factor: number = 0.1): Color { + const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.l += temp.l * factor; + return temp.toRGBA(); + } + + /** + * 将当前颜色变暗指定的量 + * + * @param factor + */ + public darken(factor: number = 0.1): Color { + const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.l -= temp.l * factor; + return temp.toRGBA(); + } + + /** + * 使当前颜色饱和指定的量 + * + * @param factor + */ + public saturate(factor: number = 0.1): Color { + const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.s += temp.s * factor; + return temp.toRGBA(); + } + + /** + * 按指定量降低当前颜色的饱和度 + * + * @param factor + */ + public desaturate(factor: number = 0.1): Color { + const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a); + temp.s -= temp.s * factor; + return temp.toRGBA(); + } + + /** + * 将一种颜色乘以另一种颜色,得到更深的颜色 + * + * @param color + */ + public mulitiply(color: Color): Color { + const newR = (((color.r / 255) * this.r) / 255) * 255; + const newG = (((color.g / 255) * this.g) / 255) * 255; + const newB = (((color.b / 255) * this.b) / 255) * 255; + const newA = color.a * this.a; + return new Color(newR, newG, newB, newA); + } + + /** + * 筛选另一种颜色,导致颜色较浅 + * + * @param color + */ + public screen(color: Color): Color { + const color1 = color.invert(); + const color2 = color.invert(); + return color1.mulitiply(color2).invert(); + } + + /** + * 反转当前颜色 + */ + public invert(): Color { + return new Color(255 - this.r, 255 - this.g, 255 - this.b, 1.0 - this.a); + } + + /** + * 将当前颜色与另一个颜色平均 + * + * @param color + */ + public average(color: Color): Color { + const newR = (color.r + this.r) / 2; + const newG = (color.g + this.g) / 2; + const newB = (color.b + this.b) / 2; + const newA = (color.a + this.a) / 2; + return new Color(newR, newG, newB, newA); + } + + /** + * 返回颜色的 CSS 字符串表示形式。 + * + * @param format + */ + public toString(format: 'rgb' | 'hsl' | 'hex' = 'rgb') { + switch (format) { + case 'rgb': + return this.toRGBA(); + case 'hsl': + return this.toHSLA(); + case 'hex': + return this.toHex(); + default: + throw new Error('Invalid Color format'); + } + } + + /** + * 返回颜色分量的十六进制值 + * @param c + * @see https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + */ + private _componentToHex(c: number) { + const hex = c.toString(16); + return hex.length === 1 ? '0' + hex : hex; + } + + /** + *返回颜色的十六进制表示 + */ + public toHex(): string { + return ( + '#' + + this._componentToHex(this.r) + + this._componentToHex(this.g) + + this._componentToHex(this.b) + + this._componentToHex(this.a) + ); + } + + /** + * 从十六进制字符串设置颜色 + * + * @param hex #ffffff 形式的 CSS 颜色字符串,alpha 组件是可选的 + */ + public fromHex(hex: string) { + const hexRegEx: RegExp = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i; + const match = hex.match(hexRegEx); + if (match) { + const r = parseInt(match[1], 16); + const g = parseInt(match[2], 16); + const b = parseInt(match[3], 16); + let a = 1; + if (match[4]) { + a = parseInt(match[4], 16) / 255; + } + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } else { + throw new Error('Invalid hex string: ' + hex); + } + } + + /** + * 返回颜色的 RGBA 表示 + */ + public toRGBA() { + const result = + String(this.r.toFixed(0)) + + ', ' + + String(this.g.toFixed(0)) + + ', ' + + String(this.b.toFixed(0)); + if (this.a !== undefined || this.a != null) { + return 'rgba(' + result + ', ' + String(this.a) + ')'; + } + return 'rgb(' + result + ')'; + } + + /** + * 返回颜色的 HSLA 表示 + */ + public toHSLA() { + return HSLColor.fromRGBA(this.r, this.g, this.b, this.a).toString(); + } + + /** + * 返回颜色的 CSS 字符串表示形式 + */ + public fillStyle() { + return this.toString(); + } + + /** + * 返回当前颜色的克隆 + */ + public clone(): Color { + return new Color(this.r, this.g, this.b, this.a); + } + + /** + * Black (#000000) + */ + public static Black: Color = Color.createFromHex('#000000'); + + /** + * White (#FFFFFF) + */ + public static White: Color = Color.createFromHex('#FFFFFF'); + + /** + * Gray (#808080) + */ + public static Gray: Color = Color.createFromHex('#808080'); + + /** + * Light gray (#D3D3D3) + */ + public static LightGray: Color = Color.createFromHex('#D3D3D3'); + + /** + * Dark gray (#A9A9A9) + */ + public static DarkGray: Color = Color.createFromHex('#A9A9A9'); + + /** + * Yellow (#FFFF00) + */ + public static Yellow: Color = Color.createFromHex('#FFFF00'); + + /** + * Orange (#FFA500) + */ + public static Orange: Color = Color.createFromHex('#FFA500'); + + /** + * Red (#FF0000) + */ + public static Red: Color = Color.createFromHex('#FF0000'); + + /** + * Vermillion (#FF5B31) + */ + public static Vermillion: Color = Color.createFromHex('#FF5B31'); + + /** + * Rose (#FF007F) + */ + public static Rose: Color = Color.createFromHex('#FF007F'); + + /** + * Magenta (#FF00FF) + */ + public static Magenta: Color = Color.createFromHex('#FF00FF'); + + /** + * Violet (#7F00FF) + */ + public static Violet: Color = Color.createFromHex('#7F00FF'); + + /** + * Blue (#0000FF) + */ + public static Blue: Color = Color.createFromHex('#0000FF'); + + /** + * Azure (#007FFF) + */ + public static Azure: Color = Color.createFromHex('#007FFF'); + + /** + * Cyan (#00FFFF) + */ + public static Cyan: Color = Color.createFromHex('#00FFFF'); + + /** + * Viridian (#59978F) + */ + public static Viridian: Color = Color.createFromHex('#59978F'); + + /** + * Green (#00FF00) + */ + public static Green: Color = Color.createFromHex('#00FF00'); + + /** + * Chartreuse (#7FFF00) + */ + public static Chartreuse: Color = Color.createFromHex('#7FFF00'); + + /** + * Transparent (#FFFFFF00) + */ + public static Transparent: Color = Color.createFromHex('#FFFFFF00'); + } + + /** + * 内部 HSL 颜色表示 + * + * http://en.wikipedia.org/wiki/HSL_and_HSV + * http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c + */ + class HSLColor { + constructor( + public h: number, + public s: number, + public l: number, + public a: number + ) { } + + public static hue2rgb(p: number, q: number, t: number): number { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; + } + + public static fromRGBA( + r: number, + g: number, + b: number, + a: number + ): HSLColor { + r /= 255; + g /= 255; + b /= 255; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h = (max + min) / 2; + let s = h; + const l = h; + + if (max === min) { + h = s = 0; // achromatic + } else { + const d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + + return new HSLColor(h, s, l, a); + } + + public toRGBA(): Color { + let r: number; + let g: number; + let b: number; + + if (this.s === 0) { + r = g = b = this.l; // achromatic + } else { + const q = + this.l < 0.5 + ? this.l * (1 + this.s) + : this.l + this.s - this.l * this.s; + const p = 2 * this.l - q; + r = HSLColor.hue2rgb(p, q, this.h + 1 / 3); + g = HSLColor.hue2rgb(p, q, this.h); + b = HSLColor.hue2rgb(p, q, this.h - 1 / 3); + } + + return new Color(r * 255, g * 255, b * 255, this.a); + } + + public toString(): string { + const h = this.h.toFixed(0); + const s = this.s.toFixed(0); + const l = this.l.toFixed(0); + const a = this.a.toFixed(0); + return `hsla(${h}, ${s}, ${l}, ${a})`; + } + } +} \ No newline at end of file diff --git a/source/src/Graphics/Batcher/IBatcher.ts b/source/src/Graphics/Batcher/IBatcher.ts new file mode 100644 index 00000000..06505b8b --- /dev/null +++ b/source/src/Graphics/Batcher/IBatcher.ts @@ -0,0 +1,15 @@ +module es { + export interface IBatcher { + begin(cam: ICamera); + end(); + drawPoints(points: Vector2[], color: Color, thickness?: number); + drawPolygon(poisition: Vector2, points: Vector2[], color: Color, closePoly: boolean, thickness?: number); + drawHollowRect(x: number, y: number, width: number, height: number, color: Color, thickness?: number); + drawCircle(position: Vector2, raidus: number, color: Color, thickness?: number); + drawCircleLow(position: es.Vector2, radius: number, color: Color, thickness?: number, resolution?: number); + drawRect(x: number, y: number, width: number, height: number, color: Color); + drawLine(start: Vector2, end: Vector2, color: Color, thickness: number); + drawPixel(position: Vector2, color: Color, size?: number); + } +} + diff --git a/source/src/Graphics/Batcher/ICamera.ts b/source/src/Graphics/Batcher/ICamera.ts new file mode 100644 index 00000000..bafd9122 --- /dev/null +++ b/source/src/Graphics/Batcher/ICamera.ts @@ -0,0 +1,5 @@ +module es { + export interface ICamera extends Component { + bounds: Rectangle; + } +} diff --git a/source/src/Graphics/Graphics.ts b/source/src/Graphics/Graphics.ts new file mode 100644 index 00000000..229b5dfb --- /dev/null +++ b/source/src/Graphics/Graphics.ts @@ -0,0 +1,10 @@ +module es { + export class Graphics { + public static instance: Graphics; + public batcher: IBatcher; + + constructor(batcher: IBatcher) { + this.batcher = batcher; + } + } +} \ No newline at end of file diff --git a/source/src/Graphics/Renderers/DefaultRenderer.ts b/source/src/Graphics/Renderers/DefaultRenderer.ts new file mode 100644 index 00000000..26cd91a8 --- /dev/null +++ b/source/src/Graphics/Renderers/DefaultRenderer.ts @@ -0,0 +1,29 @@ +/// +module es { + export class DefaultRenderer extends Renderer { + constructor(renderOrder: number = 0, camera: ICamera = null) { + super(renderOrder, camera); + } + + public render(scene: Scene): void { + if (!this.renderDirty) + return; + + this.renderDirty = false; + let cam = this.camera ? this.camera : scene.camera; + this.beginRender(cam); + + for (let i = 0; i < scene.renderableComponents.count; i ++) { + let renderable = scene.renderableComponents.get(i); + if (renderable.enabled && renderable.isVisibleFromCamera(scene.camera)) + this.renderAfterStateCheck(renderable, cam); + } + + if (this.shouldDebugRender && es.Core.debugRenderEndabled) { + this.debugRender(scene); + } + + this.endRender(); + } + } +} \ No newline at end of file diff --git a/source/src/Graphics/Renderers/Renderer.ts b/source/src/Graphics/Renderers/Renderer.ts new file mode 100644 index 00000000..f3799ada --- /dev/null +++ b/source/src/Graphics/Renderers/Renderer.ts @@ -0,0 +1,60 @@ +module es { + export abstract class Renderer { + public camera: ICamera; + public readonly renderOrder: number = 0; + public shouldDebugRender: boolean = true; + protected renderDirty: boolean = true; + + constructor(renderOrder: number, camera: ICamera) { + this.renderOrder = renderOrder; + this.camera = camera; + + Core.emitter.addObserver(CoreEvents.renderChanged, this.onRenderChanged, this); + } + + public onAddedToScene(scene: es.Scene) { } + + public unload() { } + + protected beginRender(cam: ICamera) { + if (!Graphics.instance) + return; + + Graphics.instance.batcher.begin(cam); + } + + protected endRender() { + if (!Graphics.instance) + return; + + Graphics.instance.batcher.end(); + } + + protected onRenderChanged() { + this.renderDirty = true; + } + + public abstract render(scene: Scene): void; + + protected renderAfterStateCheck(renderable: IRenderable, cam: ICamera) { + if (!Graphics.instance) + return; + + renderable.render(Graphics.instance.batcher, cam); + } + + protected debugRender(scene: Scene) { + if (!Graphics.instance) + return; + + es.Physics.debugDraw(2); + + for (let i = 0; i < scene.entities.count; i ++) { + let entity = scene.entities.buffer[i]; + if (entity.enabled) { + entity.debugRender(Graphics.instance.batcher); + } + } + } + } +} \ No newline at end of file diff --git a/source/src/Math/Bezier.ts b/source/src/Math/Bezier.ts index 7ee53c76..2a6c65e6 100644 --- a/source/src/Math/Bezier.ts +++ b/source/src/Math/Bezier.ts @@ -13,9 +13,9 @@ module es { public static getPoint(p0: Vector2, p1: Vector2, p2: Vector2, t: number): Vector2 { t = MathHelper.clamp01(t); let oneMinusT = 1 - t; - return new Vector2(oneMinusT * oneMinusT).multiply(p0) - .add(new Vector2(2 * oneMinusT * t).multiply(p1)) - .add(new Vector2(t * t).multiply(p2)); + return p0.scale(oneMinusT * oneMinusT) + .addEqual(p1.scale(2 * oneMinusT * t)) + .addEqual(p2.scale(t * t)); } /** @@ -29,11 +29,11 @@ module es { public static getPointThree(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2, end: Vector2, t: number): Vector2 { t = MathHelper.clamp01(t); - let oneMinusT = 1 - t; - return new Vector2(oneMinusT * oneMinusT * oneMinusT).multiply(start) - .add(new Vector2(3 * oneMinusT * oneMinusT * t).multiply(firstControlPoint)) - .add(new Vector2(3 * oneMinusT * t * t).multiply(secondControlPoint)) - .add(new Vector2(t * t * t).multiply(end)); + const oneMinusT = 1 - t; + return start.scale(oneMinusT * oneMinusT * oneMinusT) + .addEqual(firstControlPoint.scale(3 * oneMinusT * oneMinusT * t)) + .addEqual(secondControlPoint.scale(3 * oneMinusT * t * t)) + .addEqual(end.scale(t * t * t)); } /** @@ -44,8 +44,8 @@ module es { * @param t */ public static getFirstDerivative(p0: Vector2, p1: Vector2, p2: Vector2, t: number) { - return new Vector2(2 * (1 - t)).multiply(Vector2.subtract(p1, p0)) - .add(new Vector2(2 * t).multiply(Vector2.subtract(p2, p1))); + return p1.sub(p0).scale(2 * (1 - t)) + .addEqual(p2.sub(p1).scale(2 * t)); } /** @@ -60,9 +60,9 @@ module es { end: Vector2, t: number) { t = MathHelper.clamp01(t); let oneMunusT = 1 - t; - return new Vector2(3 * oneMunusT * oneMunusT).multiply(Vector2.subtract(firstControlPoint, start)) - .add(new Vector2(6 * oneMunusT * t).multiply(Vector2.subtract(secondControlPoint, firstControlPoint))) - .add(new Vector2(3 * t * t).multiply(Vector2.subtract(end, secondControlPoint))); + return firstControlPoint.sub(start).scale(3 * oneMunusT * oneMunusT) + .addEqual(secondControlPoint.sub(firstControlPoint).scale(6 * oneMunusT * t)) + .addEqual(end.sub(secondControlPoint).scale(3 * t * t)); } /** @@ -96,19 +96,19 @@ module es { private static recursiveGetOptimizedDrawingPoints(start: Vector2, firstCtrlPoint: Vector2, secondCtrlPoint: Vector2, end: Vector2, points: Vector2[], distanceTolerance: number) { // 计算线段的所有中点 - let pt12 = Vector2.divide(Vector2.add(start, firstCtrlPoint), new Vector2(2)); - let pt23 = Vector2.divide(Vector2.add(firstCtrlPoint, secondCtrlPoint), new Vector2(2)); - let pt34 = Vector2.divide(Vector2.add(secondCtrlPoint, end), new Vector2(2)); + let pt12 = Vector2.divideScaler(start.add(firstCtrlPoint), 2); + let pt23 = Vector2.divideScaler(firstCtrlPoint.add(secondCtrlPoint), 2); + let pt34 = Vector2.divideScaler(secondCtrlPoint.add(end), 2); // 计算新半直线的中点 - let pt123 = Vector2.divide(Vector2.add(pt12, pt23), new Vector2(2)); - let pt234 = Vector2.divide(Vector2.add(pt23, pt34), new Vector2(2)); + let pt123 = Vector2.divideScaler(pt12.add(pt23), 2); + let pt234 = Vector2.divideScaler(pt23.add(pt34), 2); // 最后再细分最后两个中点。如果我们满足我们的距离公差,这将是我们使用的最后一点。 - let pt1234 = Vector2.divide(Vector2.add(pt123, pt234), new Vector2(2)); + let pt1234 = Vector2.divideScaler(pt123.add(pt234), 2); // 试着用一条直线来近似整个三次曲线 - let deltaLine = Vector2.subtract(end, start); + let deltaLine = end.sub(start); let d2 = Math.abs(((firstCtrlPoint.x, end.x) * deltaLine.y - (firstCtrlPoint.y - end.y) * deltaLine.x)); let d3 = Math.abs(((secondCtrlPoint.x - end.x) * deltaLine.y - (secondCtrlPoint.y - end.y) * deltaLine.x)); diff --git a/source/src/Math/BezierSpline.ts b/source/src/Math/BezierSpline.ts index 19871058..c81300be 100644 --- a/source/src/Math/BezierSpline.ts +++ b/source/src/Math/BezierSpline.ts @@ -10,19 +10,20 @@ module es { * 在这个过程中,t被修改为在曲线段的范围内。 * @param t */ - public pointIndexAtTime(t: Ref): number { - let i = 0; - if (t.value >= 1) { - t.value = 1; - i = this._points.length - 4; + public pointIndexAtTime(t: number): {time: number, range: number} { + const res = {time: 0, range: 0}; + if (t >= 1) { + t = 1; + res.range = this._points.length - 4; } else { - t.value = MathHelper.clamp01(t.value) * this._curveCount; - i = ~~t; - t.value -= i; - i *= 3; + t = MathHelper.clamp01(t) * this._curveCount; + res.range = Math.floor(t); + t -= res.range; + res.range *= 3; } - return i; + res.time = t; + return res; } /** @@ -32,12 +33,12 @@ module es { */ public setControlPoint(index: number, point: Vector2) { if (index % 3 == 0) { - let delta = Vector2.subtract(point, this._points[index]); + const delta = point.sub(this._points[index]); if (index > 0) - this._points[index - 1].add(delta); + this._points[index - 1].addEqual(delta); if (index + 1 < this._points.length) - this._points[index + 1].add(delta); + this._points[index + 1].addEqual(delta); } this._points[index] = point; @@ -48,7 +49,8 @@ module es { * @param t */ public getPointAtTime(t: number): Vector2{ - let i = this.pointIndexAtTime(new Ref(t)); + const res = this.pointIndexAtTime(t); + const i = res.range; return Bezier.getPointThree(this._points[i], this._points[i + 1], this._points[i + 2], this._points[i + 3], t); } @@ -58,7 +60,8 @@ module es { * @param t */ public getVelocityAtTime(t: number): Vector2 { - let i = this.pointIndexAtTime(new Ref(t)); + const res = this.pointIndexAtTime(t); + const i = res.range; return Bezier.getFirstDerivativeThree(this._points[i], this._points[i + 1], this._points[i + 2], this._points[i + 3], t); } @@ -68,7 +71,7 @@ module es { * @param t */ public getDirectionAtTime(t: number) { - return Vector2.normalize(this.getVelocityAtTime(t)); + return this.getVelocityAtTime(t).normalize(); } /** diff --git a/source/src/Math/MathHelper.ts b/source/src/Math/MathHelper.ts index 5298b4a8..38034608 100644 --- a/source/src/Math/MathHelper.ts +++ b/source/src/Math/MathHelper.ts @@ -129,6 +129,10 @@ module es { return from + (to - from) * this.clamp01(t); } + public static betterLerp(a: number, b: number, t: number, epsilon: number): number { + return Math.abs(a - b) < epsilon ? b : MathHelper.lerp(a, b, t); + } + /** * 使度数的角度在a和b之间 * 用于处理360度环绕 @@ -381,6 +385,16 @@ module es { return this.repeat(this.approach(start, start + deltaAngle, shift), 360); } + /** + * 将 Vector 投影到另一个 Vector 上 + * @param other + */ + public static project(self: Vector2, other: Vector2) { + let amt = self.dot(other) / other.lengthSquared(); + let vec = other.scale(amt); + return vec; + } + /** * 通过将偏移量(全部以弧度为单位)夹住结果并选择最短路径,起始角度朝向终止角度。 * 起始值可以小于或大于终止值。 @@ -456,6 +470,10 @@ module es { return t - Math.floor(t / length) * length; } + public static floorToInt(f: number) { + return Math.trunc(Math.floor(f)); + } + /** * 将值绕一圈移动的助手 * @param position @@ -588,7 +606,74 @@ module es { return false; } - return !Number.isFinite(x); + return x !== Infinity; + } + + public static smoothDamp(current: number, target: number, currentVelocity: number, smoothTime: number, maxSpeed: number, deltaTime: number): { value: number; currentVelocity: number } { + smoothTime = Math.max(0.0001, smoothTime); + const num: number = 2 / smoothTime; + const num2: number = num * deltaTime; + const num3: number = + 1 / + (1 + (num2 + (0.48 * (num2 * num2) + 0.235 * (num2 * (num2 * num2))))); + let num4: number = current - target; + const num5: number = target; + const num6: number = maxSpeed * smoothTime; + num4 = this.clamp(num4, num6 * -1, num6); + target = current - num4; + const num7: number = (currentVelocity + num * num4) * deltaTime; + currentVelocity = (currentVelocity - num * num7) * num3; + let num8: number = target + (num4 + num7) * num3; + if (num5 - current > 0 === num8 > num5) { + num8 = num5; + currentVelocity = (num8 - num5) / deltaTime; + } + return { value: num8, currentVelocity }; + } + + public static smoothDampVector(current: Vector2, target: Vector2, currentVelocity: Vector2, smoothTime: number, maxSpeed: number, deltaTime: number): Vector2 { + const v = Vector2.zero; + + const resX = this.smoothDamp( + current.x, + target.x, + currentVelocity.x, + smoothTime, + maxSpeed, + deltaTime + ); + v.x = resX.value; + currentVelocity.x = resX.currentVelocity; + + const resY = this.smoothDamp( + current.y, + target.y, + currentVelocity.y, + smoothTime, + maxSpeed, + deltaTime + ); + v.y = resY.value; + currentVelocity.y = resY.currentVelocity; + + return v; + } + + /** + * 将值(在 leftMin - leftMax 范围内)映射到 rightMin - rightMax 范围内的值 + * @param value + * @param leftMin + * @param leftMax + * @param rightMin + * @param rightMax + * @returns + */ + public static mapMinMax(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax): number { + return rightMin + ((MathHelper.clamp(value, leftMin, leftMax) - leftMin) * (rightMax - rightMin)) / (leftMax - leftMin); + } + + public static fromAngle(angle: number) { + return new Vector2(Math.cos(angle), Math.sin(angle)).normalizeEqual(); } } } diff --git a/source/src/Math/Matrix.ts b/source/src/Math/Matrix.ts index 61ed642c..c3cf409e 100644 --- a/source/src/Math/Matrix.ts +++ b/source/src/Math/Matrix.ts @@ -3,6 +3,15 @@ module es { * 代表右手4x4浮点矩阵,可以存储平移、比例和旋转信息 */ export class Matrix { + private static identity = new Matrix(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + + public static get Identity() { + return this.identity; + } + public m11: number; public m12: number; public m13: number; @@ -20,6 +29,26 @@ module es { public m43: number; public m44: number; + constructor(m11?, m12?, m13?, m14?, m21?, m22?, m23?, m24?, m31?, + m32?, m33?, m34?, m41?, m42?, m43?, m44?) { + this.m11 = m11; + this.m12 = m12; + this.m13 = m13; + this.m14 = m14; + this.m21 = m21; + this.m22 = m22; + this.m23 = m23; + this.m24 = m24; + this.m31 = m31; + this.m32 = m32; + this.m33 = m33; + this.m34 = m34; + this.m41 = m41; + this.m42 = m42; + this.m43 = m43; + this.m44 = m44; + } + /** * 为自定义的正交视图创建一个新的投影矩阵 * @param left @@ -47,6 +76,39 @@ module es { result.m44 = 1; } + public static createTranslation(position: Vector2, result: Matrix) + { + result.m11 = 1; + result.m12 = 0; + result.m13 = 0; + result.m14 = 0; + result.m21 = 0; + result.m22 = 1; + result.m23 = 0; + result.m24 = 0; + result.m31 = 0; + result.m32 = 0; + result.m33 = 1; + result.m34 = 0; + result.m41 = position.x; + result.m42 = position.y; + result.m43 = 0; + result.m44 = 1; + } + + public static createRotationZ(radians: number, result: Matrix) + { + result = Matrix.Identity; + + var val1 = Math.cos(radians); + var val2 = Math.sin(radians); + + result.m11 = val1; + result.m12 = val2; + result.m21 = -val2; + result.m22 = val1; + } + /** * 创建一个新的矩阵,其中包含两个矩阵的乘法。 * @param matrix1 @@ -69,23 +131,23 @@ module es { let m41 = (((matrix1.m41 * matrix2.m11) + (matrix1.m42 * matrix2.m21)) + (matrix1.m43 * matrix2.m31)) + (matrix1.m44 * matrix2.m41); let m42 = (((matrix1.m41 * matrix2.m12) + (matrix1.m42 * matrix2.m22)) + (matrix1.m43 * matrix2.m32)) + (matrix1.m44 * matrix2.m42); let m43 = (((matrix1.m41 * matrix2.m13) + (matrix1.m42 * matrix2.m23)) + (matrix1.m43 * matrix2.m33)) + (matrix1.m44 * matrix2.m43); - let m44 = (((matrix1.m41 * matrix2.m14) + (matrix1.m42 * matrix2.m24)) + (matrix1.m43 * matrix2.m34)) + (matrix1.m44 * matrix2.m44); + let m44 = (((matrix1.m41 * matrix2.m14) + (matrix1.m42 * matrix2.m24)) + (matrix1.m43 * matrix2.m34)) + (matrix1.m44 * matrix2.m44); result.m11 = m11; - result.m12 = m12; - result.m13 = m13; - result.m14 = m14; - result.m21 = m21; - result.m22 = m22; - result.m23 = m23; - result.m24 = m24; - result.m31 = m31; - result.m32 = m32; - result.m33 = m33; - result.m34 = m34; - result.m41 = m41; - result.m42 = m42; - result.m43 = m43; - result.m44 = m44; + result.m12 = m12; + result.m13 = m13; + result.m14 = m14; + result.m21 = m21; + result.m22 = m22; + result.m23 = m23; + result.m24 = m24; + result.m31 = m31; + result.m32 = m32; + result.m33 = m33; + result.m34 = m34; + result.m41 = m41; + result.m42 = m42; + result.m43 = m43; + result.m44 = m44; } } } \ No newline at end of file diff --git a/source/src/Math/Matrix2D.ts b/source/src/Math/Matrix2D.ts index a11e4b4f..487b3707 100644 --- a/source/src/Math/Matrix2D.ts +++ b/source/src/Math/Matrix2D.ts @@ -16,7 +16,28 @@ module es { * 返回标识矩阵 */ public static get identity(): Matrix2D { - return new Matrix2D(1, 0, 0, 1, 0, 0); + return new Matrix2D().setIdentity(); + } + + public setIdentity(): Matrix2D { + return this.setValues(1, 0, 0, 1, 0, 0); + } + + public setValues( + m11: number, + m12: number, + m21: number, + m22: number, + m31: number, + m32: number + ): Matrix2D { + this.m11 = m11; + this.m12 = m12; + this.m21 = m21; + this.m22 = m22; + this.m31 = m31; + this.m32 = m32; + return this; } /** @@ -38,7 +59,7 @@ module es { return Math.atan2(this.m21, this.m11); } - public set rotation(value: number){ + public set rotation(value: number) { let val1 = Math.cos(value); let val2 = Math.sin(value); @@ -71,33 +92,21 @@ module es { this.m22 = value.y; } - /** - * 构建一个矩阵 - * @param m11 - * @param m12 - * @param m21 - * @param m22 - * @param m31 - * @param m32 - */ - constructor(m11: number, m12: number, m21: number, m22: number, m31: number, m32: number){ - this.m11 = m11; - this.m12 = m12; - - this.m21 = m21; - this.m22 = m22; - - this.m31 = m31; - this.m32 = m32; - } - /** * 创建一个新的围绕Z轴的旋转矩阵2D * @param radians */ - public static createRotation(radians: number){ - let result: Matrix2D = this.identity; + public static createRotation(radians: number, result: Matrix2D) { + result.setIdentity(); + const val1 = Math.cos(radians); + const val2 = Math.sin(radians); + result.m11 = val1; + result.m12 = val2; + result.m21 = val2 * -1; + result.m22 = val1; + } + public static createRotationOut(radians: number, result: Matrix2D) { let val1 = Math.cos(radians); let val2 = Math.sin(radians); @@ -105,8 +114,6 @@ module es { result.m12 = val2; result.m21 = -val2; result.m22 = val1; - - return result; } /** @@ -114,8 +121,16 @@ module es { * @param xScale * @param yScale */ - public static createScale(xScale: number, yScale: number){ - let result: Matrix2D = this.identity; + public static createScale(xScale: number, yScale: number, result: Matrix2D) { + result.m11 = xScale; + result.m12 = 0; + result.m21 = 0; + result.m22 = yScale; + result.m31 = 0; + result.m32 = 0; + } + + public static createScaleOut(xScale: number, yScale: number, result: Matrix2D) { result.m11 = xScale; result.m12 = 0; @@ -124,8 +139,6 @@ module es { result.m31 = 0; result.m32 = 0; - - return result; } /** @@ -133,18 +146,26 @@ module es { * @param xPosition * @param yPosition */ - public static createTranslation(xPosition: number, yPosition: number) { - let result: Matrix2D = this.identity; + public static createTranslation(xPosition: number, yPosition: number, result: Matrix2D) { + result.m11 = 1; + result.m12 = 0; + result.m21 = 0; + result.m22 = 1; + result.m31 = xPosition; + result.m32 = yPosition; + + return result; + } + + public static createTranslationOut(position: Vector2, result: Matrix2D) { result.m11 = 1; result.m12 = 0; result.m21 = 0; result.m22 = 1; - result.m31 = xPosition; - result.m32 = yPosition; - - return result; + result.m31 = position.x; + result.m32 = position.y; } public static invert(matrix: Matrix2D) { @@ -228,6 +249,26 @@ module es { return this; } + public static multiply(matrix1: Matrix2D, matrix2: Matrix2D, result: Matrix2D) { + const m11 = (matrix1.m11 * matrix2.m11) + (matrix1.m12 * matrix2.m21); + const m12 = (matrix1.m11 * matrix2.m12) + (matrix1.m12 * matrix2.m22); + + const m21 = (matrix1.m21 * matrix2.m11) + (matrix1.m22 * matrix2.m21); + const m22 = (matrix1.m21 * matrix2.m12) + (matrix1.m22 * matrix2.m22); + + const m31 = (matrix1.m31 * matrix2.m11) + (matrix1.m32 * matrix2.m21) + matrix2.m31; + const m32 = (matrix1.m31 * matrix2.m12) + (matrix1.m32 * matrix2.m22) + matrix2.m32; + + result.m11 = m11; + result.m12 = m12; + + result.m21 = m21; + result.m22 = m22; + + result.m31 = m31; + result.m32 = m32; + } + public determinant() { return this.m11 * this.m22 - this.m12 * this.m21; } @@ -238,10 +279,10 @@ module es { * @param matrix2 * @param amount */ - public static lerp(matrix1: Matrix2D, matrix2: Matrix2D, amount: number){ + public static lerp(matrix1: Matrix2D, matrix2: Matrix2D, amount: number) { matrix1.m11 = matrix1.m11 + ((matrix2.m11 - matrix1.m11) * amount); matrix1.m12 = matrix1.m12 + ((matrix2.m12 - matrix1.m12) * amount); - + matrix1.m21 = matrix1.m21 + ((matrix2.m21 - matrix1.m21) * amount); matrix1.m22 = matrix1.m22 + ((matrix2.m22 - matrix1.m22) * amount); @@ -267,8 +308,9 @@ module es { return ret; } - public mutiplyTranslation(x: number, y: number){ - let trans = Matrix2D.createTranslation(x, y); + public mutiplyTranslation(x: number, y: number) { + let trans = new Matrix2D(); + Matrix2D.createTranslation(x, y, trans); return MatrixHelper.mutiply(this, trans); } @@ -276,7 +318,7 @@ module es { * 比较当前实例是否等于指定的Matrix2D * @param other */ - public equals(other: Matrix2D){ + public equals(other: Matrix2D) { return this == other; } diff --git a/source/src/Math/Rectangle.ts b/source/src/Math/Rectangle.ts index 6e207b57..d18f9c02 100644 --- a/source/src/Math/Rectangle.ts +++ b/source/src/Math/Rectangle.ts @@ -107,8 +107,8 @@ module es { } // temp 用于计算边界的矩阵 - public _tempMat: Matrix2D; - public _transformMat: Matrix2D; + public _tempMat: Matrix2D = new Matrix2D(); + public _transformMat: Matrix2D = new Matrix2D(); /** * 创建一个新的Rectanglestruct实例,指定位置、宽度和高度。 @@ -213,49 +213,50 @@ module es { this.top < value.bottom; } - public rayIntersects(ray: Ray2D, distance: Ref): boolean { - distance.value = 0; + public rayIntersects(ray: Ray2D): { intersected: boolean; distance: number } { + const res = {intersected: false, distance: 0}; let maxValue = Number.MAX_VALUE; if (Math.abs(ray.direction.x) < 1E-06) { if ((ray.start.x < this.x) || (ray.start.x > this.x + this.width)) - return false; + return res; } else { - let num11 = 1 / ray.direction.x; + const num11 = 1 / ray.direction.x; let num8 = (this.x - ray.start.x) * num11; let num7 = (this.x + this.width - ray.start.x) * num11; if (num8 > num7) { - let num14 = num8; + const num14 = num8; num8 = num7; num7 = num14; } - distance.value = Math.max(num8, distance.value); + res.distance = Math.max(num8, res.distance); maxValue = Math.min(num7, maxValue); - if (distance.value > maxValue) - return false; + if (res.distance > maxValue) + return res; } - if (Math.abs(ray.direction.y) < 1E-06) { + if (Math.abs(ray.direction.y) < 1e-06) { if ((ray.start.y < this.y) || (ray.start.y > this.y + this.height)) - return false; + return res; } else { - let num10 = 1 / ray.direction.y; + const num10 = 1 / ray.direction.y; let num6 = (this.y - ray.start.y) * num10; let num5 = (this.y + this.height - ray.start.y) * num10; if (num6 > num5) { - let num13 = num6; + const num13 = num6; num6 = num5; num5 = num13; } - distance.value = Math.max(num6, distance.value); + res.distance = Math.max(num6, res.distance); maxValue = Math.max(num5, maxValue); - if (distance.value > maxValue) - return false; + if (res.distance > maxValue) + return res; } - return true; + res.intersected = true; + return res; } /** @@ -304,7 +305,7 @@ module es { */ public getClosestPointOnRectangleToPoint(point: Vector2) { // 对于每条轴,如果点在框外,就把它限制在框内,否则就不要管它 - let res = new Vector2(); + let res = es.Vector2.zero; res.x = MathHelper.clamp(point.x, this.left, this.right); res.y = MathHelper.clamp(point.y, this.top, this.bottom); @@ -319,7 +320,7 @@ module es { */ public getClosestPointOnRectangleBorderToPoint(point: Vector2, edgeNormal: Vector2): Vector2 { // 对于每条轴,如果点在框外,就把它限制在框内,否则就不要管它 - let res = new Vector2(); + let res = es.Vector2.zero; res.x = MathHelper.clamp(point.x, this.left, this.right); res.y = MathHelper.clamp(point.y, this.top, this.bottom); @@ -411,22 +412,22 @@ module es { public calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number) { if (rotation == 0) { - this.x = parentPosition.x + position.x - origin.x * scale.x; - this.y = parentPosition.y + position.y - origin.y * scale.y; - this.width = width * scale.x; - this.height = height * scale.y; + this.x = Math.trunc(parentPosition.x + position.x - origin.x * scale.x); + this.y = Math.trunc(parentPosition.y + position.y - origin.y * scale.y); + this.width = Math.trunc(width * scale.x); + this.height = Math.trunc(height * scale.y); } else { // 我们需要找到我们的绝对最小/最大值,并据此创建边界 let worldPosX = parentPosition.x + position.x; let worldPosY = parentPosition.y + position.y; // 考虑到原点,将参考点设置为世界参考 - this._transformMat = Matrix2D.createTranslation(-worldPosX - origin.x, -worldPosY - origin.y); - this._tempMat = Matrix2D.createScale(scale.x, scale.y); + Matrix2D.createTranslation(-worldPosX - origin.x, -worldPosY - origin.y, this._transformMat); + Matrix2D.createScale(scale.x, scale.y, this._tempMat); this._transformMat = this._transformMat.multiply(this._tempMat); - this._tempMat = Matrix2D.createRotation(rotation); + Matrix2D.createRotation(rotation, this._tempMat); this._transformMat = this._transformMat.multiply(this._tempMat); - this._tempMat = Matrix2D.createTranslation(worldPosX, worldPosY); + Matrix2D.createTranslation(worldPosX, worldPosY, this._tempMat); this._transformMat = this._transformMat.multiply(this._tempMat); // TODO: 我们可以把世界变换留在矩阵中,避免在世界空间中得到所有的四个角 @@ -441,14 +442,14 @@ module es { Vector2Ext.transformR(bottomRight, this._transformMat, bottomRight); // 找出最小值和最大值,这样我们就可以计算出我们的边界框。 - let minX = Math.min(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x); - let maxX = Math.max(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x); - let minY = Math.min(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y); - let maxY = Math.max(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y); + let minX = Math.trunc(Math.min(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + let maxX = Math.trunc(Math.max(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + let minY = Math.trunc(Math.min(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); + let maxY = Math.trunc(Math.max(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); this.location = new Vector2(minX, minY); - this.width = maxX - minX; - this.height = maxY - minY; + this.width = Math.trunc(maxX - minX); + this.height = Math.trunc(maxY - minY); } } @@ -549,7 +550,7 @@ module es { * 获取这个矩形的哈希码 */ public getHashCode(): number{ - return (this.x ^ this.y ^ this.width ^ this.height); + return (Math.trunc(this.x) ^ Math.trunc(this.y) ^ Math.trunc(this.width) ^ Math.trunc(this.height)); } public clone(): Rectangle { diff --git a/source/src/Math/SubpixelFloat.ts b/source/src/Math/SubpixelFloat.ts index 8be15510..fe675739 100644 --- a/source/src/Math/SubpixelFloat.ts +++ b/source/src/Math/SubpixelFloat.ts @@ -16,7 +16,7 @@ module es { */ public update(amount: number){ this.remainder += amount; - let motion = Math.floor(Math.trunc(this.remainder)); + let motion = Math.trunc(this.remainder); this.remainder -= motion; amount = motion; return amount; diff --git a/source/src/Math/Vector2.ts b/source/src/Math/Vector2.ts index fefab83c..1e2cad43 100644 --- a/source/src/Math/Vector2.ts +++ b/source/src/Math/Vector2.ts @@ -9,9 +9,9 @@ module es { * @param x 二维空间中的x坐标 * @param y 二维空间的y坐标 */ - constructor(x?: number, y?: number) { - this.x = x ? x : 0; - this.y = y != undefined ? y : this.x; + constructor(x: number = 0, y: number = 0) { + this.x = x; + this.y = y; } public static get zero() { @@ -30,6 +30,22 @@ module es { return new Vector2(0, 1); } + public static get up() { + return new Vector2(0, -1); + } + + public static get down() { + return new Vector2(0, 1); + } + + public static get left() { + return new Vector2(-1, 0); + } + + public static get right() { + return new Vector2(1, 0); + } + /** * * @param value1 @@ -54,73 +70,20 @@ module es { return result; } - /** - * - * @param value1 - * @param value2 - */ - public static multiply(value1: Vector2, value2: Vector2) { - let result: Vector2 = new Vector2(0, 0); - result.x = value1.x * value2.x; - result.y = value1.y * value2.y; + public static divideScaler(value1: Vector2, value2: number) { + let result: Vector2 = Vector2.zero; + result.x = value1.x / value2; + result.y = value1.y / value2; return result; } - /** - * - * @param value1 - * @param value2 - * @returns - */ - public static multiplyScaler(value1: Vector2, value2: number) { - let result = new Vector2(0, 0); - result.x = value1.x * value2; - result.y = value1.x * value2; - return result; - } - - /** - * - * @param value1 - * @param value2 - */ - public static subtract(value1: Vector2, value2: Vector2) { - let result: Vector2 = new Vector2(0, 0); - result.x = value1.x - value2.x; - result.y = value1.y - value2.y; - return result; - } - - /** - * 创建一个新的Vector2 - * 它包含来自另一个向量的标准化值。 - * @param value - */ - public static normalize(value: Vector2) { - let nValue = new Vector2(value.x, value.y); - let val = 1 / Math.sqrt((nValue.x * nValue.x) + (nValue.y * nValue.y)); - nValue.x *= val; - nValue.y *= val; - return nValue; - } - - /** - * 返回两个向量的点积 - * @param value1 - * @param value2 - */ - public static dot(value1: Vector2, value2: Vector2): number { - return (value1.x * value2.x) + (value1.y * value2.y); - } - /** * 返回两个向量之间距离的平方 * @param value1 * @param value2 */ - public static distanceSquared(value1: Vector2, value2: Vector2) { - let v1 = value1.x - value2.x, v2 = value1.y - value2.y; - return (v1 * v1) + (v2 * v2); + public static sqrDistance(value1: Vector2, value2: Vector2) { + return Math.pow(value1.x - value2.x, 2) + Math.pow(value1.y - value2.y, 2); } /** @@ -183,9 +146,8 @@ module es { * @param value2 * @returns 两个向量之间的距离 */ - public static distance(value1: Vector2, value2: Vector2): number { - let v1 = value1.x - value2.x, v2 = value1.y - value2.y; - return Math.sqrt((v1 * v1) + (v2 * v2)); + public static distance(vec1: Vector2, vec2: Vector2): number { + return Math.sqrt(Math.pow(vec1.x - vec2.x, 2) + Math.pow(vec1.y - vec2.y, 2)); } /** @@ -193,10 +155,10 @@ module es { * @param from * @param to */ - public static angle(from: Vector2, to: Vector2): number{ - from = Vector2.normalize(from); - to = Vector2.normalize(to); - return Math.acos(MathHelper.clamp(Vector2.dot(from, to), -1, 1)) * MathHelper.Rad2Deg; + public static angle(from: Vector2, to: Vector2): number { + from = from.normalize(); + to = to.normalize(); + return Math.acos(MathHelper.clamp(from.dot(to), -1, 1)) * MathHelper.Rad2Deg; } /** @@ -218,7 +180,7 @@ module es { * @returns */ public static reflect(vector: Vector2, normal: Vector2) { - let result: Vector2 = new Vector2(); + let result: Vector2 = es.Vector2.zero; let val = 2 * ((vector.x * normal.x) + (vector.y * normal.y)); result.x = vector.x - (normal.x * val); result.y = vector.y - (normal.y * val); @@ -237,13 +199,26 @@ module es { MathHelper.smoothStep(value1.y, value2.y, amount)); } + public setTo(x: number, y: number) { + this.x = x; + this.y = y; + } + + public negate(): Vector2 { + return this.scale(-1); + } + /** * * @param value */ - public add(value: Vector2): Vector2 { - this.x += value.x; - this.y += value.y; + public add(v: Vector2): Vector2 { + return new Vector2(this.x + v.x, this.y + v.y); + } + + public addEqual(v: Vector2): Vector2 { + this.x += v.x; + this.y += v.y; return this; } @@ -252,9 +227,11 @@ module es { * @param value */ public divide(value: Vector2): Vector2 { - this.x /= value.x; - this.y /= value.y; - return this; + return new Vector2(this.x / value.x, this.y / value.y); + } + + public divideScaler(value: number): Vector2 { + return new Vector2(this.x / value, this.y / value); } /** @@ -262,9 +239,7 @@ module es { * @param value */ public multiply(value: Vector2): Vector2 { - this.x *= value.x; - this.y *= value.y; - return this; + return new Vector2(value.x * this.x, value.y * this.y); } /** @@ -283,24 +258,75 @@ module es { * @param value 要减去的Vector2 * @returns 当前Vector2 */ - public subtract(value: Vector2) { - this.x -= value.x; - this.y -= value.y; + public sub(value: Vector2) { + return new Vector2(this.x - value.x, this.y - value.y); + } + + public subEqual(v: Vector2): Vector2 { + this.x -= v.x; + this.y -= v.y; return this; } + public dot(v: Vector2): number { + return this.x * v.x + this.y * v.y; + } + + /** + * + * @param size + * @returns + */ + public scale(size: number): Vector2 { + return new Vector2(this.x * size, this.y * size); + } + + public scaleEqual(size: number): Vector2 { + this.x *= size; + this.y *= size; + return this; + } + + public transform(matrix: Matrix2D): Vector2 { + return new Vector2( + this.x * matrix.m11 + this.y * matrix.m21 + matrix.m31, + this.x * matrix.m12 + this.y * matrix.m22 + matrix.m32 + ); + } + + public normalize(): Vector2 { + const d = this.distance(); + if (d > 0) { + return new Vector2(this.x / d, this.y / d); + } else { + return new Vector2(0, 1); + } + } + /** * 将这个Vector2变成一个方向相同的单位向量 */ - public normalize() { - let val = 1 / Math.sqrt((this.x * this.x) + (this.y * this.y)); - this.x *= val; - this.y *= val; + public normalizeEqual(): Vector2 { + const d = this.distance(); + if (d > 0) { + this.setTo(this.x / d, this.y / d); + return this; + } else { + this.setTo(0, 1); + return this; + } } - /** 返回它的长度 */ - public length() { - return Math.sqrt((this.x * this.x) + (this.y * this.y)); + public magnitude(): number { + return this.distance(); + } + + public distance(v?: Vector2): number { + if (!v) { + v = Vector2.zero; + } + + return Math.sqrt(Math.pow(this.x - v.x, 2) + Math.pow(this.y - v.y, 2)); } /** @@ -324,8 +350,8 @@ module es { * @param right */ public angleBetween(left: Vector2, right: Vector2) { - let one = Vector2.subtract(left, this); - let two = Vector2.subtract(right, this); + let one = left.sub(this); + let two = right.sub(this); return Vector2Ext.angle(one, two); } @@ -334,12 +360,8 @@ module es { * @param other 要比较的对象 * @returns 如果实例相同true 否则false */ - public equals(other: Vector2 | object): boolean { - if (other instanceof Vector2){ - return other.x == this.x && other.y == this.y; - } - - return false; + public equals(other: Vector2, tolerance: number = 0.001): boolean { + return Math.abs(this.x - other.x) <= tolerance && Math.abs(this.y - other.y) <= tolerance; } public isValid(): boolean { @@ -377,11 +399,20 @@ module es { * @param amount * @returns */ - public static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number){ + public static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number) { return new Vector2(MathHelper.hermite(value1.x, tangent1.x, value2.x, tangent2.x, amount), MathHelper.hermite(value1.y, tangent1.y, value2.y, tangent2.y, amount)); } + public static unsignedAngle(from: Vector2, to: Vector2, round: boolean = true) { + from.normalizeEqual(); + to.normalizeEqual(); + const angle = + Math.acos(MathHelper.clamp(from.dot(to), -1, 1)) * MathHelper.Rad2Deg; + return round ? Math.round(angle) : angle; + } + + public clone(): Vector2 { return new Vector2(this.x, this.y); } diff --git a/source/src/Physics/ColliderTriggerHelper.ts b/source/src/Physics/ColliderTriggerHelper.ts index 28bfdd7f..f095d904 100644 --- a/source/src/Physics/ColliderTriggerHelper.ts +++ b/source/src/Physics/ColliderTriggerHelper.ts @@ -19,35 +19,43 @@ module es { * 它将处理任何与Collider重叠的ITriggerListeners。 */ public update() { + const lateColliders = []; // 对所有实体.colliders进行重叠检查,这些实体.colliders是触发器,与所有宽相碰撞器,无论是否触发器。 // 任何重叠都会导致触发事件 let colliders = this._entity.getComponents(Collider); for (let i = 0; i < colliders.length; i++) { let collider = colliders[i]; - let neighbors = Physics.boxcastBroadphase(collider.bounds, collider.collidesWithLayers); - for (let j = 0; j < neighbors.size; j++) { + let neighbors = Physics.boxcastBroadphaseExcludingSelf(collider.bounds, collider.collidesWithLayers); + for (let j = 0; j < neighbors.length; j++) { let neighbor = neighbors[j]; // 我们至少需要一个碰撞器作为触发器 if (!collider.isTrigger && !neighbor.isTrigger) continue; if (collider.overlaps(neighbor)) { - let pair = new Pair(collider, neighbor); + const pair = new Pair(collider, neighbor); // 如果我们的某一个集合中已经有了这个对子(前一个或当前的触发交叉点),就不要调用输入事件了 - let shouldReportTriggerEvent = !this._activeTriggerIntersections.contains(pair) && + const shouldReportTriggerEvent = !this._activeTriggerIntersections.contains(pair) && !this._previousTriggerIntersections.contains(pair); - if (shouldReportTriggerEvent) - this.notifyTriggerListeners(pair, true); + if (shouldReportTriggerEvent) { + if (neighbor.castSortOrder >= Collider.lateSortOrder) { + lateColliders.push(pair); + } else { + this.notifyTriggerListeners(pair, true); + } + } this._activeTriggerIntersections.add(pair); } } } - ListPool.free(colliders); + for (const pair of lateColliders) { + this.notifyTriggerListeners(pair, true); + } this.checkForExitedColliders(); } diff --git a/source/src/Physics/Collision.ts b/source/src/Physics/Collision.ts index 7733bf4d..405740ff 100644 --- a/source/src/Physics/Collision.ts +++ b/source/src/Physics/Collision.ts @@ -13,48 +13,50 @@ module es { export class Collisions { public static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): boolean { - let b = Vector2.subtract(a2, a1); - let d = Vector2.subtract(b2, b1); - let bDotDPerp = b.x * d.y - b.y * d.x; + const b = a2.sub(a1); + const d = b2.sub(b1); + const bDotDPerp = b.x * d.y - b.y * d.x; // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 if (bDotDPerp == 0) return false; - let c = Vector2.subtract(b1, a1); - let t = (c.x * d.y - c.y * d.x) / bDotDPerp; - if (t < 0 || t > 1) + const c = b1.sub(a1); + const t = (c.x * d.y - c.y * d.x) / bDotDPerp; + if (t < 0 || t > 1) { return false; + } - let u = (c.x * b.y - c.y * b.x) / bDotDPerp; - if (u < 0 || u > 1) + const u = (c.x * b.y - c.y * b.x) / bDotDPerp; + if (u < 0 || u > 1) { return false; + } return true; } - public static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2 = new Vector2()): boolean { + public static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2 = es.Vector2.zero): boolean { intersection.x = 0; intersection.y = 0; - let b = Vector2.subtract(a2, a1); - let d = Vector2.subtract(b2, b1); - let bDotDPerp = b.x * d.y - b.y * d.x; + const b = a2.sub(a1); + const d = b2.sub(b1); + const bDotDPerp = b.x * d.y - b.y * d.x; // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 if (bDotDPerp == 0) return false; - let c = Vector2.subtract(b1, a1); - let t = (c.x * d.y - c.y * d.x) / bDotDPerp; + const c = b1.sub(a1); + const t = (c.x * d.y - c.y * d.x) / bDotDPerp; if (t < 0 || t > 1) return false; - let u = (c.x * b.y - c.y * b.x) / bDotDPerp; + const u = (c.x * b.y - c.y * b.x) / bDotDPerp; if (u < 0 || u > 1) return false; - let temp = Vector2.add(a1, new Vector2(t * b.x, t * b.y)); + const temp = a1.add(b.scale(t)); intersection.x = temp.x; intersection.y = temp.y; @@ -62,24 +64,24 @@ module es { } public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2) { - let v = Vector2.subtract(lineB, lineA); - let w = Vector2.subtract(closestTo, lineA); - let t = Vector2.dot(w, v) / Vector2.dot(v, v); + const v = lineB.sub(lineA); + const w = closestTo.sub(lineA); + let t = w.dot(v) / v.dot(v); t = MathHelper.clamp(t, 0, 1); - return Vector2.add(lineA, new Vector2(v.x * t, v.y * t)); + return lineA.add(v.scale(t)); } public static circleToCircle(circleCenter1: Vector2, circleRadius1: number, circleCenter2: Vector2, circleRadius2: number): boolean { - return Vector2.distanceSquared(circleCenter1, circleCenter2) < (circleRadius1 + circleRadius2) * (circleRadius1 + circleRadius2); + return Vector2.sqrDistance(circleCenter1, circleCenter2) < (circleRadius1 + circleRadius2) * (circleRadius1 + circleRadius2); } public static circleToLine(circleCenter: Vector2, radius: number, lineFrom: Vector2, lineTo: Vector2): boolean { - return Vector2.distanceSquared(circleCenter, this.closestPointOnLine(lineFrom, lineTo, circleCenter)) < radius * radius; + return Vector2.sqrDistance(circleCenter, this.closestPointOnLine(lineFrom, lineTo, circleCenter)) < radius * radius; } public static circleToPoint(circleCenter: Vector2, radius: number, point: Vector2): boolean { - return Vector2.distanceSquared(circleCenter, point) < radius * radius; + return Vector2.sqrDistance(circleCenter, point) < radius * radius; } public static rectToCircle(rect: Rectangle, cPosition: Vector2, cRadius: number): boolean { @@ -90,30 +92,30 @@ module es { // 对照相关边缘检查圆圈 let edgeFrom: Vector2; let edgeTo: Vector2; - let sector = this.getSector(rect.x, rect.y, rect.width, rect.height, cPosition); + const sector = this.getSector(rect.x, rect.y, rect.width, rect.height, cPosition); - if ((sector & PointSectors.top) != 0){ + if ((sector & PointSectors.top) !== 0) { edgeFrom = new Vector2(rect.x, rect.y); edgeTo = new Vector2(rect.x + rect.width, rect.y); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) return true; } - if ((sector & PointSectors.bottom) != 0){ + if ((sector & PointSectors.bottom) !== 0) { edgeFrom = new Vector2(rect.x, rect.y + rect.width); edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) return true; } - if ((sector & PointSectors.left) != 0){ + if ((sector & PointSectors.left) !== 0) { edgeFrom = new Vector2(rect.x, rect.y); edgeTo = new Vector2(rect.x, rect.y + rect.height); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) return true; } - if ((sector & PointSectors.right) != 0) { + if ((sector & PointSectors.right) !== 0) { edgeFrom = new Vector2(rect.x + rect.width, rect.y); edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height); if (this.circleToLine(cPosition, cRadius, edgeFrom, edgeTo)) @@ -124,15 +126,15 @@ module es { } public static rectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2) { - let fromSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineFrom); - let toSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineTo); + const fromSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineFrom); + const toSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineTo); if (fromSector == PointSectors.center || toSector == PointSectors.center) { return true; } else if ((fromSector & toSector) != 0) { return false; } else { - let both = fromSector | toSector; + const both = fromSector | toSector; // 线对边进行检查 let edgeFrom: Vector2; let edgeTo: Vector2; diff --git a/source/src/Physics/Physics.ts b/source/src/Physics/Physics.ts index b56a892f..394e92d6 100644 --- a/source/src/Physics/Physics.ts +++ b/source/src/Physics/Physics.ts @@ -3,7 +3,7 @@ module es { export class Physics { public static _spatialHash: SpatialHash; /** 用于在全局范围内存储重力值的方便字段 */ - public static gravity = new Vector2(0, 300); + public static gravity = new Vector2(0, -300); /** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */ public static spatialHashCellSize = 100; /** 接受layerMask的所有方法的默认值 */ @@ -16,6 +16,7 @@ module es { * 在碰撞器中开始的射线/直线是否强制转换检测到那些碰撞器 */ public static raycastsStartInColliders = false; + public static debugRender: boolean = false; /** * 我们保留它以避免在每次raycast发生时分配它 */ @@ -42,6 +43,11 @@ module es { this._spatialHash.clear(); } + public static debugDraw(secondsToDisplay) { + if (this.debugRender) + this._spatialHash.debugDraw(secondsToDisplay); + } + /** * 检查是否有对撞机落在一个圆形区域内。返回遇到的第一个对撞机 * @param center @@ -61,13 +67,13 @@ module es { * @param results * @param layerMask */ - public static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask = -1) { - if (results.length == 0) { - console.warn("传入了一个空的结果数组。不会返回任何结果"); - return; - } - - return this._spatialHash.overlapCircle(center, randius, results, layerMask); + public static overlapCircleAll(center: Vector2, radius: number, results: Collider[], layerMask: number = this.allLayers) { + return this._spatialHash.overlapCircle( + center, + radius, + results, + layerMask + ); } /** @@ -96,7 +102,7 @@ module es { * @param layerMask */ public static boxcastBroadphaseExcludingSelfNonRect(collider: Collider, layerMask = this.allLayers) { - let bounds = collider.bounds.clone(); + let bounds = collider.bounds; return this._spatialHash.aabbBroadphase(bounds, collider, layerMask); } @@ -108,7 +114,7 @@ module es { * @param layerMask */ public static boxcastBroadphaseExcludingSelfDelta(collider: Collider, deltaX: number, deltaY: number, layerMask: number = Physics.allLayers) { - let colliderBounds = collider.bounds.clone(); + let colliderBounds = collider.bounds; let sweptBounds = colliderBounds.getSweptBroadphaseBounds(deltaX, deltaY); return this._spatialHash.aabbBroadphase(sweptBounds, collider, layerMask); } @@ -144,10 +150,18 @@ module es { * @param end * @param layerMask */ - public static linecast(start: Vector2, end: Vector2, layerMask: number = Physics.allLayers): RaycastHit{ + public static linecast(start: Vector2, end: Vector2, layerMask: number = this.allLayers, ignoredColliders: Set = null): RaycastHit { this._hitArray[0].reset(); this.linecastAll(start, end, this._hitArray, layerMask); - return this._hitArray[0]; + this._hitArray[0].reset(); + Physics.linecastAll( + start, + end, + this._hitArray, + layerMask, + ignoredColliders + ); + return this._hitArray[0].clone(); } /** @@ -157,13 +171,14 @@ module es { * @param hits * @param layerMask */ - public static linecastAll(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number = Physics.allLayers){ - if (hits.length == 0){ - console.warn("传入了一个空的hits数组。没有点击会被返回"); - return 0; - } - - return this._spatialHash.linecast(start, end, hits, layerMask); + public static linecastAll(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number = this.allLayers, ignoredColliders: Set = null) { + return this._spatialHash.linecast( + start, + end, + hits, + layerMask, + ignoredColliders + ); } /** @@ -184,7 +199,7 @@ module es { * @param layerMask */ public static overlapRectangleAll(rect: Rectangle, results: Collider[], layerMask: number = Physics.allLayers) { - if (results.length == 0){ + if (results.length == 0) { console.warn("传入了一个空的结果数组。不会返回任何结果"); return 0; } diff --git a/source/src/Physics/Ray2D.ts b/source/src/Physics/Ray2D.ts index 9a2043ce..bf522a1e 100644 --- a/source/src/Physics/Ray2D.ts +++ b/source/src/Physics/Ray2D.ts @@ -3,14 +3,26 @@ module es { * 不是真正的射线(射线只有开始和方向),作为一条线和射线。 */ export class Ray2D { - public start: Vector2; - public end: Vector2; - public direction: Vector2; - - constructor(position: Vector2, end: Vector2){ - this.start = position; - this.end = end; - this.direction = Vector2.subtract(this.end, this.start); + public get start(): Vector2 { + return this._start; } + + public get direction(): Vector2 { + return this._direction; + } + + public get end(): Vector2 { + return this._end; + } + + constructor(pos: Vector2, end: Vector2) { + this._start = pos.clone(); + this._end = end.clone(); + this._direction = this._end.sub(this._start); + } + + private _start: Vector2; + private _direction: Vector2; + private _end: Vector2; } } \ No newline at end of file diff --git a/source/src/Physics/RaycastHit.ts b/source/src/Physics/RaycastHit.ts index 35047372..95843e3e 100644 --- a/source/src/Physics/RaycastHit.ts +++ b/source/src/Physics/RaycastHit.ts @@ -30,7 +30,7 @@ module es { */ public centroid: Vector2; - constructor(collider?: Collider, fraction?: number, distance?: number, point?: Vector2, normal?: Vector2){ + constructor(collider?: Collider, fraction?: number, distance?: number, point?: Vector2, normal?: Vector2) { this.collider = collider; this.fraction = fraction; this.distance = distance; @@ -38,26 +38,39 @@ module es { this.centroid = Vector2.zero; } - public setValues(collider: Collider, fraction: number, distance: number, point: Vector2){ + public setAllValues(collider: Collider, fraction: number, distance: number, point: Vector2, normal: Vector2) { this.collider = collider; this.fraction = fraction; this.distance = distance; this.point = point; - } - - public setValuesNonCollider(fraction: number, distance: number, point: Vector2, normal: Vector2){ - this.fraction = fraction; - this.distance = distance; - this.point = point; this.normal = normal; } - public reset(){ + public setValues(fraction: number, distance: number, point: Vector2, normal: Vector2) { + this.fraction = fraction; + this.distance = distance; + this.point = point; + this.normal = normal; + } + + public reset() { this.collider = null; this.fraction = this.distance = 0; } - public toString(){ + public clone(): RaycastHit { + const hit = new RaycastHit(); + hit.setAllValues( + this.collider, + this.fraction, + this.distance, + this.point, + this.normal + ); + return hit; + } + + public toString() { return `[RaycastHit] fraction: ${this.fraction}, distance: ${this.distance}, normal: ${this.normal}, centroid: ${this.centroid}, point: ${this.point}`; } } diff --git a/source/src/Physics/Shapes/Circle.ts b/source/src/Physics/Shapes/Circle.ts index 77fe7e24..02c9b541 100644 --- a/source/src/Physics/Shapes/Circle.ts +++ b/source/src/Physics/Shapes/Circle.ts @@ -16,20 +16,20 @@ module es { if (collider.shouldColliderScaleAndRotateWithTransform) { // 我们只将直线缩放为一个圆,所以我们将使用最大值 - let scale = collider.entity.transform.scale; - let hasUnitScale = scale.x == 1 && scale.y == 1; - let maxScale = Math.max(scale.x, scale.y); + const scale = collider.entity.transform.scale; + const hasUnitScale = scale.x === 1 && scale.y === 1; + const maxScale = Math.max(scale.x, scale.y); this.radius = this._originalRadius * maxScale; - if (collider.entity.transform.rotation != 0) { + if (collider.entity.transform.rotation !== 0) { // 为了处理偏移原点的旋转,我们只需要将圆心围绕(0,0)在一个圆上移动,我们的偏移量就是0角 - let offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * MathHelper.Rad2Deg; - let offsetLength = hasUnitScale ? collider._localOffsetLength : Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length(); - this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotationDegrees + offsetAngle); + const offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * MathHelper.Rad2Deg; + const offsetLength = hasUnitScale ? collider._localOffsetLength : collider.localOffset.multiply(collider.entity.transform.scale).magnitude(); + this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotation + offsetAngle); } } - this.position = Vector2.add(collider.entity.transform.position, this.center); + this.position = collider.transform.position.add(this.center); this.bounds = new Rectangle(this.position.x - this.radius, this.position.y - this.radius, this.radius * 2, this.radius * 2); } @@ -67,12 +67,19 @@ module es { return ShapeCollisionsLine.lineToCircle(start, end, this, hit); } + public getPointAlongEdge(angle: number): Vector2 { + return new Vector2( + this.position.x + this.radius * Math.cos(angle), + this.position.y + this.radius * Math.sin(angle) + ); + } + /** * 获取所提供的点是否在此范围内 * @param point */ public containsPoint(point: Vector2) { - return (Vector2.subtract(point, this.position)).lengthSquared() <= this.radius * this.radius; + return (point.sub(this.position)).lengthSquared() <= this.radius * this.radius; } public pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean { diff --git a/source/src/Physics/Shapes/CollisionResult.ts b/source/src/Physics/Shapes/CollisionResult.ts index d4eafa05..5b43fb79 100644 --- a/source/src/Physics/Shapes/CollisionResult.ts +++ b/source/src/Physics/Shapes/CollisionResult.ts @@ -17,15 +17,39 @@ module es { */ public point: Vector2 = Vector2.zero; + public reset() { + this.collider = null; + this.normal.setTo(0, 0); + this.minimumTranslationVector.setTo(0, 0); + if (this.point) { + this.point.setTo(0, 0); + } + } + + public cloneTo(cr: CollisionResult) { + cr.collider = this.collider; + cr.normal.setTo(this.normal.x, this.normal.y); + cr.minimumTranslationVector.setTo( + this.minimumTranslationVector.x, + this.minimumTranslationVector.y + ); + if (this.point) { + if (!cr.point) { + cr.point = new Vector2(0, 0); + } + cr.point.setTo(this.point.x, this.point.y); + } + } + /** * 改变最小平移向量,如果没有相同方向上的运动,它将移除平移的x分量。 * @param deltaMovement */ - public removeHorizontal(deltaMovement: Vector2){ + public removeHorizontalTranslation(deltaMovement: Vector2){ // 检查是否需要横向移动,如果需要,移除并固定响应 - if (Math.sign(this.normal.x) != Math.sign(deltaMovement.x) || (deltaMovement.x == 0 && this.normal.x != 0)){ - let responseDistance = this.minimumTranslationVector.length(); - let fix = responseDistance / this.normal.y; + if (Math.sign(this.normal.x) !== Math.sign(deltaMovement.x) || (deltaMovement.x === 0 && this.normal.x !== 0)){ + const responseDistance = this.minimumTranslationVector.magnitude(); + const fix = responseDistance / this.normal.y; // 检查一些边界情况。因为我们除以法线 使得x == 1和一个非常小的y这将导致一个巨大的固定值 if (Math.abs(this.normal.x) != 1 && Math.abs(fix) < Math.abs(deltaMovement.y * 3)){ @@ -35,9 +59,8 @@ module es { } public invertResult() { - this.minimumTranslationVector = Vector2.negate(this.minimumTranslationVector); - this.normal = Vector2.negate(this.normal); - return this; + this.minimumTranslationVector = this.minimumTranslationVector.negate(); + this.normal = this.normal.negate(); } public toString(){ diff --git a/source/src/Physics/Shapes/Polygon.ts b/source/src/Physics/Shapes/Polygon.ts index b86e0ecc..10c8909c 100644 --- a/source/src/Physics/Shapes/Polygon.ts +++ b/source/src/Physics/Shapes/Polygon.ts @@ -34,6 +34,10 @@ module es { this.isBox = isBox; } + public create(vertCount: number, radius: number) { + Polygon.buildSymmetricalPolygon(vertCount, radius); + } + public _edgeNormals: Vector2[]; /** @@ -54,7 +58,10 @@ module es { this.points = points; this.recalculateCenterAndEdgeNormals(); - this._originalPoints = this.points.slice(); + this._originalPoints = []; + this.points.forEach(p => { + this._originalPoints.push(p.clone()); + }); } /** @@ -96,10 +103,10 @@ module es { * @param radius */ public static buildSymmetricalPolygon(vertCount: number, radius: number) { - let verts = new Array(vertCount); + const verts = new Array(vertCount); for (let i = 0; i < vertCount; i++) { - let a = 2 * Math.PI * (i / vertCount); + const a = 2 * Math.PI * (i / vertCount); verts[i] = new Vector2(Math.cos(a) * radius, Math.sin(a) * radius); } @@ -111,9 +118,9 @@ module es { * @param points */ public static recenterPolygonVerts(points: Vector2[]) { - let center = this.findPolygonCenter(points); + const center = this.findPolygonCenter(points); for (let i = 0; i < points.length; i++) - points[i] = Vector2.subtract(points[i], center); + points[i] = points[i].sub(center); } /** @@ -136,13 +143,13 @@ module es { * @param points * @param direction */ - public static getFarthestPointInDirection(points: Vector2[], direction: Vector2): Vector2{ + public static getFarthestPointInDirection(points: Vector2[], direction: Vector2): Vector2 { let index = 0; - let maxDot = Vector2.dot(points[index], direction); + let maxDot = points[index].dot(direction); - for (let i = 1; i < points.length; i ++){ - let dot = Vector2.dot(points[i], direction); - if (dot > maxDot){ + for (let i = 1; i < points.length; i++) { + let dot = points[i].dot(direction); + if (dot > maxDot) { maxDot = dot; index = i; } @@ -160,35 +167,35 @@ module es { * @param distanceSquared * @param edgeNormal */ - public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2, distanceSquared: Ref, edgeNormal: Vector2): Vector2 { - distanceSquared.value = Number.MAX_VALUE; - edgeNormal.x = 0; - edgeNormal.y = 0; - let closestPoint = Vector2.zero; + public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { distanceSquared: number; edgeNormal: Vector2; closestPoint: Vector2 } { + const res = { + distanceSquared: Number.MAX_VALUE, + edgeNormal: Vector2.zero, + closestPoint: Vector2.zero, + }; let tempDistanceSquared = 0; for (let i = 0; i < points.length; i++) { let j = i + 1; - if (j == points.length) + if (j === points.length) j = 0; - let closest = ShapeCollisionsCircle.closestPointOnLine(points[i], points[j], point); - tempDistanceSquared = Vector2.distanceSquared(point, closest); + const closest = ShapeCollisionsCircle.closestPointOnLine(points[i], points[j], point); + tempDistanceSquared = Vector2.sqrDistance(point, closest); - if (tempDistanceSquared < distanceSquared.value) { - distanceSquared.value = tempDistanceSquared; - closestPoint = closest; + if (tempDistanceSquared < res.distanceSquared) { + res.distanceSquared = tempDistanceSquared; + res.closestPoint = closest; // 求直线的法线 - let line = Vector2.subtract(points[j], points[i]); - edgeNormal.x = -line.y; - edgeNormal.y = line.x; + const line = points[j].sub(points[i]); + res.edgeNormal.x = line.y; + res.edgeNormal.y = -line.x; } } - Vector2Ext.normalize(edgeNormal); - - return closestPoint; + res.edgeNormal = res.edgeNormal.normalize(); + return res; } /** @@ -197,11 +204,11 @@ module es { * @param originalPoints * @param rotatedPoints */ - public static rotatePolygonVerts(radians: number, originalPoints: Vector2[], rotatedPoints: Vector2[]){ + public static rotatePolygonVerts(radians: number, originalPoints: Vector2[], rotatedPoints: Vector2[]) { let cos = Math.cos(radians); let sin = Math.sin(radians); - for (let i = 0; i < originalPoints.length; i ++){ + for (let i = 0; i < originalPoints.length; i++) { let position = originalPoints[i]; rotatedPoints[i] = new Vector2(position.x * cos + position.y * -sin, position.x * sin + position.y * cos); } @@ -209,40 +216,63 @@ module es { public recalculateBounds(collider: Collider) { // 如果我们没有旋转或不关心TRS我们使用localOffset作为中心,我们会从那开始 - this.center = collider.localOffset.clone(); + this.center = collider.localOffset; if (collider.shouldColliderScaleAndRotateWithTransform) { let hasUnitScale = true; - let tempMat: Matrix2D; - let combinedMatrix = Matrix2D.createTranslation(-this._polygonCenter.x, -this._polygonCenter.y); + const tempMat: Matrix2D = new Matrix2D(); + const combinedMatrix: Matrix2D = new Matrix2D(); + Matrix2D.createTranslation( + this._polygonCenter.x * -1, + this._polygonCenter.y * -1, + combinedMatrix + ); if (!collider.entity.transform.scale.equals(Vector2.one)) { - tempMat = Matrix2D.createScale(collider.entity.transform.scale.x, collider.entity.transform.scale.y); - combinedMatrix = combinedMatrix.multiply(tempMat); + Matrix2D.createScale( + collider.entity.scale.x, + collider.entity.scale.y, + tempMat + ); + Matrix2D.multiply(combinedMatrix, tempMat, combinedMatrix); hasUnitScale = false; // 缩放偏移量并将其设置为中心。如果我们有旋转,它会在下面重置 - this.center = Vector2.multiply(collider.localOffset, collider.entity.transform.scale); + const scaledOffset = new Vector2( + collider.localOffset.x * collider.entity.scale.x, + collider.localOffset.y * collider.entity.scale.y + ); + this.center = scaledOffset; } if (collider.entity.transform.rotation != 0) { - tempMat = Matrix2D.createRotation(collider.entity.transform.rotation); - combinedMatrix = combinedMatrix.multiply(tempMat); + Matrix2D.createRotation( + MathHelper.Deg2Rad * collider.entity.rotation, + tempMat + ); + Matrix2D.multiply(combinedMatrix, tempMat, combinedMatrix); // 为了处理偏移原点的旋转我们只需要将圆心在(0,0)附近移动 // 我们的偏移使角度为0我们还需要处理这里的比例所以我们先对偏移进行缩放以得到合适的长度。 - let offsetAngle = Math.atan2(collider.localOffset.y * collider.entity.transform.scale.y, collider.localOffset.x * collider.entity.transform.scale.x) * MathHelper.Rad2Deg; - let offsetLength = hasUnitScale ? collider._localOffsetLength : - Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length(); + const offsetAngle = Math.atan2(collider.localOffset.y * collider.entity.transform.scale.y, collider.localOffset.x * collider.entity.transform.scale.x) * MathHelper.Rad2Deg; + const offsetLength = hasUnitScale ? collider._localOffsetLength : + collider.localOffset.multiply(collider.entity.transform.scale).magnitude(); this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotationDegrees + offsetAngle); } - tempMat = Matrix2D.createTranslation(this._polygonCenter.x, this._polygonCenter.y); - combinedMatrix = combinedMatrix.multiply(tempMat); + Matrix2D.createTranslation( + this._polygonCenter.x, + this._polygonCenter.y, + tempMat + ); + Matrix2D.multiply(combinedMatrix, tempMat, combinedMatrix); // 最后变换原始点 - Vector2Ext.transform(this._originalPoints, combinedMatrix, this.points); + this.points = []; + this._originalPoints.forEach(p => { + this.points.push(p.transform(combinedMatrix)); + }); this.isUnrotated = collider.entity.transform.rotation == 0; @@ -251,9 +281,9 @@ module es { this._areEdgeNormalsDirty = true; } - this.position = Vector2.add(collider.entity.transform.position, this.center); + this.position = collider.transform.position.add(this.center); this.bounds = Rectangle.rectEncompassingPoints(this.points); - this.bounds.location = Vector2.add(this.bounds.location, this.position); + this.bounds.location = this.bounds.location.add(this.position); } public overlaps(other: Shape) { @@ -301,11 +331,11 @@ module es { */ public containsPoint(point: Vector2) { // 将点归一化到多边形坐标空间中 - point.subtract(this.position); + point = point.sub(this.position); let isInside = false; for (let i = 0, j = this.points.length - 1; i < this.points.length; j = i++) { - if (((this.points[i].y > point.y) != (this.points[j].y > point.y)) && + if (((this.points[i].y > point.y) !== (this.points[j].y > point.y)) && (point.x < (this.points[j].x - this.points[i].x) * (point.y - this.points[i].y) / (this.points[j].y - this.points[i].y) + this.points[i].x)) { isInside = !isInside; diff --git a/source/src/Physics/Shapes/RealtimeCollisions.ts b/source/src/Physics/Shapes/RealtimeCollisions.ts index 331221e8..4f89ae3a 100644 --- a/source/src/Physics/Shapes/RealtimeCollisions.ts +++ b/source/src/Physics/Shapes/RealtimeCollisions.ts @@ -1,20 +1,21 @@ module es { export class RealtimeCollisions { - public static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: Ref): boolean { + public static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: number): boolean { // 计算将b按球面半径r扩大后的AABB - let e = b.bounds.clone(); + const e = b.bounds; e.inflate(s.radius, s.radius); // 将射线与展开的矩形e相交,如果射线错过了e,则以无交点退出,否则得到交点p和时间t作为结果。 - let ray = new Ray2D(Vector2.subtract(s.position, movement), s.position); - if (!e.rayIntersects(ray, time) && time.value > 1) + const ray = new Ray2D(s.position.sub(movement), s.position); + const res = e.rayIntersects(ray); + if (!res.intersected && res.distance > 1) return false; // 求交点 - let point = Vector2.add(ray.start, Vector2.multiply(ray.direction, new Vector2(time.value))); + const point = ray.start.add(ray.direction.scale(time)); // 计算交点p位于b的哪个最小面和最大面之外。注意,u和v不能有相同的位集,它们之间必须至少有一个位集。 - let u, v = 0; + let u: number, v: number = 0; if (point.x < b.bounds.left) u |= 1; if (point.x > b.bounds.right) @@ -25,7 +26,7 @@ module es { v |= 2; // 'or'将所有的比特集合在一起,形成一个比特掩码(注意u + v == u | v) - let m = u + v; + const m = u + v; // 如果这3个比特都被设置,那么该点就在顶点区域内。 if (m == 3){ @@ -49,7 +50,7 @@ module es { * @param n */ public static corner(b: Rectangle, n: number){ - let p = new Vector2(); + let p = es.Vector2.zero; p.x = (n & 1) == 0 ? b.right : b.left; p.y = (n & 1) == 0 ? b.bottom : b.top; return p; @@ -66,8 +67,8 @@ module es { point = box.bounds.getClosestPointOnRectangleToPoint(cirlce.position); // 圆和方块相交,如果圆心到点的距离小于圆的半径,则圆和方块相交 - let v = Vector2.subtract(point, cirlce.position); - let dist = Vector2.dot(v, v); + const v = point.sub(cirlce.position); + const dist = v.dot(v); return dist <= cirlce.radius * cirlce.radius; } diff --git a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsBox.ts b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsBox.ts index 09460b49..fdb43604 100644 --- a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsBox.ts +++ b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsBox.ts @@ -1,7 +1,7 @@ module es { export class ShapeCollisionsBox { public static boxToBox(first: Box, second: Box, result: CollisionResult): boolean { - let minkowskiDiff = this.minkowskiDifference(first, second); + const minkowskiDiff = this.minkowskiDifference(first, second); if (minkowskiDiff.contains(0, 0)) { // 计算MTV。如果它是零,我们就可以称它为非碰撞 result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin(); @@ -9,8 +9,8 @@ module es { if (result.minimumTranslationVector.equals(Vector2.zero)) return false; - result.normal = new Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y); - result.normal.normalize(); + result.normal = result.minimumTranslationVector.scale(-1); + result.normal = result.normal.normalize(); return true; } @@ -27,29 +27,29 @@ module es { */ public static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean { // 首先,我们检查是否有重叠。如果有重叠,我们就不做扫描测试 - let minkowskiDiff = this.minkowskiDifference(first, second); + const minkowskiDiff = this.minkowskiDifference(first, second); if (minkowskiDiff.contains(0, 0)) { // 计算MTV。如果它是零,我们就可以称它为非碰撞 - let mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin(); + const mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin(); if (mtv.equals(Vector2.zero)) return false; - hit.normal = new Vector2(-mtv.x); - hit.normal.normalize(); + hit.normal = new Vector2(-mtv.x, -mtv.y); + hit.normal = hit.normal.normalize(); hit.distance = 0; hit.fraction = 0; return true; } else { // 射线投射移动矢量 - let ray = new Ray2D(Vector2.zero, new Vector2(-movement.x)); - let fraction = new Ref(0); - if (minkowskiDiff.rayIntersects(ray, fraction) && fraction.value <= 1) { - hit.fraction = fraction.value; - hit.distance = movement.length() * fraction.value; - hit.normal = new Vector2(-movement.x, -movement.y); - hit.normal.normalize(); - hit.centroid = Vector2.add(first.bounds.center, Vector2.multiply(movement, new Vector2(fraction.value))); + const ray = new Ray2D(Vector2.zero, movement.scale(-1)); + const res = minkowskiDiff.rayIntersects(ray); + if (res.intersected && res.distance <= 1) { + hit.fraction = res.distance; + hit.distance = movement.magnitude() * res.distance; + hit.normal = movement.scale(-1); + hit.normal = hit.normal.normalize(); + hit.centroid = first.bounds.center.add(movement.scale(res.distance)); return true; } @@ -61,9 +61,9 @@ module es { private static minkowskiDifference(first: Box, second: Box): Rectangle { // 我们需要第一个框的左上角 // 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。 - let positionOffset = Vector2.subtract(first.position, Vector2.add(first.bounds.location, new Vector2(first.bounds.size.x / 2, first.bounds.size.y / 2))); - let topLeft = Vector2.subtract(Vector2.add(first.bounds.location, positionOffset), second.bounds.max); - let fullSize = Vector2.add(first.bounds.size, second.bounds.size); + const positionOffset = first.position.sub(first.bounds.center); + const topLeft = first.bounds.location.add(positionOffset.sub(second.bounds.max)); + const fullSize = first.bounds.size.add(second.bounds.size); return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y) } diff --git a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsCircle.ts b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsCircle.ts index 6d87923e..4e85e5e5 100644 --- a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsCircle.ts +++ b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsCircle.ts @@ -1,19 +1,46 @@ module es { export class ShapeCollisionsCircle { - public static circleToCircle(first: Circle, second: Circle, result: CollisionResult = new CollisionResult()): boolean { - let distanceSquared = Vector2.distanceSquared(first.position, second.position); - let sumOfRadii = first.radius + second.radius; - let collided = distanceSquared < sumOfRadii * sumOfRadii; - if (collided) { - result.normal = Vector2.normalize(Vector2.subtract(first.position, second.position)); - let depth = sumOfRadii - Math.sqrt(distanceSquared); - result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth), result.normal); - result.point = Vector2.add(second.position, Vector2.multiply(result.normal, new Vector2(second.radius))); + public static circleToCircleCast(first: Circle,second: Circle,deltaMovement: Vector2,hit: RaycastHit): boolean { + let endPointOfCast = first.position.add(deltaMovement); + let d = this.closestPointOnLine(first.position,endPointOfCast,second.position); - // 这可以得到实际的碰撞点,可能有用也可能没用,所以我们暂时把它留在这里 - // let collisionPointX = ((first.position.x * second.radius) + (second.position.x * first.radius)) / sumOfRadii; - // let collisionPointY = ((first.position.y * second.radius) + (second.position.y * first.radius)) / sumOfRadii; - // result.point = new Vector2(collisionPointX, collisionPointY); + let closestDistanceSquared = Vector2.sqrDistance(second.position, d); + const sumOfRadiiSquared = (first.radius + second.radius) * (first.radius + second.radius); + if (closestDistanceSquared <= sumOfRadiiSquared) { + const normalizedDeltaMovement = deltaMovement.normalize(); + if (d === endPointOfCast) { + endPointOfCast = first.position.add( + deltaMovement.add(normalizedDeltaMovement.scale(second.radius)) + ); + d = this.closestPointOnLine( + first.position, + endPointOfCast, + second.position + ); + closestDistanceSquared = Vector2.sqrDistance(second.position, d); + } + + const backDist = Math.sqrt(sumOfRadiiSquared - closestDistanceSquared); + hit.centroid = d.sub(normalizedDeltaMovement.scale(backDist)); + hit.normal = hit.centroid.sub(second.position).normalize(); + hit.fraction = (hit.centroid.x - first.position.x) / deltaMovement.x; + hit.distance = Vector2.distance(first.position, hit.centroid); + hit.point = second.position.add(hit.normal.scale(second.radius)); + return true; + } + + return false; + } + + public static circleToCircle(first: Circle, second: Circle, result: CollisionResult = new CollisionResult()): boolean { + const distanceSquared = Vector2.sqrDistance(first.position, second.position); + const sumOfRadii = first.radius + second.radius; + const collided = distanceSquared < sumOfRadii * sumOfRadii; + if (collided) { + result.normal = first.position.sub(second.position).normalize(); + const depth = sumOfRadii - Math.sqrt(distanceSquared); + result.minimumTranslationVector = result.normal.scale(-depth); + result.point = second.position.add(result.normal.scale(second.radius)); return true; } @@ -28,31 +55,31 @@ module es { * @param result */ public static circleToBox(circle: Circle, box: Box, result: CollisionResult = new CollisionResult()): boolean { - let closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position, result.normal); + const closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position, result.normal); // 先处理中心在盒子里的圆,如果我们是包含的, 它的成本更低, if (box.containsPoint(circle.position)) { - result.point = closestPointOnBounds.clone(); + result.point = closestPointOnBounds; // 计算MTV。找出安全的、非碰撞的位置,并从中得到MTV - let safePlace = Vector2.add(closestPointOnBounds, Vector2.multiply(result.normal, new Vector2(circle.radius))); - result.minimumTranslationVector = Vector2.subtract(circle.position, safePlace); + const safePlace = closestPointOnBounds.add(result.normal.scale(circle.radius)); + result.minimumTranslationVector = circle.position.sub(safePlace); return true; } - let sqrDistance = Vector2.distanceSquared(closestPointOnBounds, circle.position); + const sqrDistance = Vector2.sqrDistance(closestPointOnBounds, circle.position); // 看框上的点距圆的半径是否小于圆的半径 if (sqrDistance == 0) { - result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(circle.radius)); + result.minimumTranslationVector = result.normal.scale(circle.radius); } else if (sqrDistance <= circle.radius * circle.radius) { - result.normal = Vector2.subtract(circle.position, closestPointOnBounds); - let depth = result.normal.length() - circle.radius; + result.normal = circle.position.sub(closestPointOnBounds); + const depth = result.normal.magnitude() - circle.radius; result.point = closestPointOnBounds; - Vector2Ext.normalize(result.normal); - result.minimumTranslationVector = Vector2.multiply(new Vector2(depth), result.normal); + result.normal = result.normal.normalize(); + result.minimumTranslationVector = result.normal.scale(depth); return true; } @@ -62,47 +89,47 @@ module es { public static circleToPolygon(circle: Circle, polygon: Polygon, result: CollisionResult = new CollisionResult()): boolean { // 圆圈在多边形中的位置坐标 - let poly2Circle = Vector2.subtract(circle.position, polygon.position); + const poly2Circle = circle.position.sub(polygon.position); // 首先,我们需要找到从圆到多边形的最近距离 - let distanceSquared = new Ref(0); - let closestPoint = Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle, distanceSquared, result.normal); + const res = Polygon.getClosestPointOnPolygonToPoint(polygon.points,poly2Circle); + result.normal = res.edgeNormal; // 确保距离的平方小于半径的平方,否则我们不会相撞。 // 请注意,如果圆完全包含在多边形中,距离可能大于半径。 // 正因为如此,我们还要确保圆的位置不在多边形内。 - let circleCenterInsidePoly = polygon.containsPoint(circle.position); - if (distanceSquared.value > circle.radius * circle.radius && !circleCenterInsidePoly) + const circleCenterInsidePoly = polygon.containsPoint(circle.position); + if (res.distanceSquared > circle.radius * circle.radius && !circleCenterInsidePoly) return false; // 算出MTV。我们要注意处理完全包含在多边形中的圆或包含其中心的圆 let mtv: Vector2; if (circleCenterInsidePoly) { - mtv = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared.value) - circle.radius)); + mtv = result.normal.scale(Math.sqrt(res.distanceSquared) - circle.radius); } else { // 如果我们没有距离,这意味着圆心在多边形的边缘上。只需根据它的半径移动它 - if (distanceSquared.value == 0) { - mtv = new Vector2(result.normal.x * circle.radius, result.normal.y * circle.radius); + if (res.distanceSquared === 0) { + mtv = result.normal.scale(circle.radius); } else { - let distance = Math.sqrt(distanceSquared.value); - mtv = Vector2.subtract(new Vector2(-1), Vector2.subtract(poly2Circle, closestPoint)) - .multiply(new Vector2((circle.radius - distance) / distance)); + const distance = Math.sqrt(res.distanceSquared); + mtv = poly2Circle + .sub(res.closestPoint) + .scale(((circle.radius - distance) / distance) * -1); } } result.minimumTranslationVector = mtv; - result.point = Vector2.add(closestPoint, polygon.position); + result.point = res.closestPoint.add(polygon.position); return true; } public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2 { - let v = Vector2.subtract(lineB, lineA); - let w = Vector2.subtract(closestTo, lineA); - let t = Vector2.dot(w, v) / Vector2.dot(v, v); + const v = lineB.sub(lineA); + const w = closestTo.sub(lineA); + let t = w.dot(v) / v.dot(v); t = MathHelper.clamp(t, 0, 1); - - return Vector2.add(lineA, Vector2.multiply(v, new Vector2(t))); + return lineA.add(v.scaleEqual(t)); } } } \ No newline at end of file diff --git a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsLine.ts b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsLine.ts index 6b7f2350..b758f44d 100644 --- a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsLine.ts +++ b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsLine.ts @@ -7,20 +7,20 @@ module es { let hasIntersection = false; for (let j = polygon.points.length - 1, i = 0; i < polygon.points.length; j = i, i ++){ - let edge1 = Vector2.add(polygon.position, polygon.points[j]); - let edge2 = Vector2.add(polygon.position, polygon.points[i]); - let intersection: Vector2 = Vector2.zero; - if (this.lineToLine(edge1, edge2, start, end, intersection)){ + const edge1 = Vector2.add(polygon.position, polygon.points[j]); + const edge2 = Vector2.add(polygon.position, polygon.points[i]); + const intersection: Vector2 = Vector2.zero; + if (ShapeCollisionsLine.lineToLine(edge1, edge2, start, end, intersection)){ hasIntersection = true; // TODO: 这是得到分数的正确和最有效的方法吗? // 先检查x分数。如果是NaN,就用y代替 let distanceFraction = (intersection.x - start.x) / (end.x - start.x); - if (Number.isNaN(distanceFraction) || Number.isFinite(distanceFraction)) + if (Number.isNaN(distanceFraction) || Math.abs(distanceFraction) == Infinity) distanceFraction = (intersection.y - start.y) / (end.y - start.y); if (distanceFraction < fraction){ - let edge = Vector2.subtract(edge2, edge1); + const edge = edge2.sub(edge1); normal = new Vector2(edge.y, -edge.x); fraction = distanceFraction; intersectionPoint = intersection; @@ -29,9 +29,9 @@ module es { } if (hasIntersection){ - normal.normalize(); - let distance = Vector2.distance(start, intersectionPoint); - hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal); + normal = normal.normalize(); + const distance = Vector2.distance(start, intersectionPoint); + hit.setValues(fraction, distance, intersectionPoint, normal); return true; } @@ -39,35 +39,37 @@ module es { } public static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2){ - let b = Vector2.subtract(a2, a1); - let d = Vector2.subtract(b2, b1); - let bDotDPerp = b.x * d.y - b.y * d.x; + const b = a2.sub(a1); + const d = b2.sub(b1); + const bDotDPerp = b.x * d.y - b.y * d.x; // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 if (bDotDPerp == 0) return false; - let c = Vector2.subtract(b1, a1); - let t = (c.x * d.y - c.y * d.x) / bDotDPerp; + const c = b1.sub(a1); + const t = (c.x * d.y - c.y * d.x) / bDotDPerp; if (t < 0 || t > 1) return false; - let u = (c.x * b.y - c.y * b.x) / bDotDPerp; + const u = (c.x * b.y - c.y * b.x) / bDotDPerp; if (u < 0 || u > 1) return false; - intersection = Vector2.add(a1, Vector2.multiply(new Vector2(t), b)); + const r = a1.add(b.scale(t)); + intersection.x = r.x; + intersection.y = r.y; return true; } public static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean{ // 计算这里的长度并分别对d进行标准化,因为如果我们命中了我们需要它来得到分数 - let lineLength = Vector2.distance(start, end); - let d = Vector2.divide(Vector2.subtract(end, start), new Vector2(lineLength)); - let m = Vector2.subtract(start, s.position); - let b = Vector2.dot(m, d); - let c = Vector2.dot(m, m) - s.radius * s.radius; + const lineLength = Vector2.distance(start, end); + const d = Vector2.divideScaler(end.sub(start), lineLength); + const m = start.sub(s.position); + const b = m.dot(d); + const c = m.dot(m) - s.radius * s.radius; // 如果r的原点在s之外,(c>0)和r指向s (b>0) 则返回 if (c > 0 && b > 0) @@ -85,9 +87,9 @@ module es { if (hit.fraction < 0) hit.fraction = 0; - hit.point = Vector2.add(start, Vector2.multiply(new Vector2(hit.fraction), d)); + hit.point = start.add(d.scale(hit.fraction)); hit.distance = Vector2.distance(start, hit.point); - hit.normal = Vector2.normalize(Vector2.subtract(hit.point, s.position)); + hit.normal = hit.point.sub(s.position).normalize(); hit.fraction = hit.distance / lineLength; return true; diff --git a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPoint.ts b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPoint.ts index 4ebc99e0..a2270497 100644 --- a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPoint.ts +++ b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPoint.ts @@ -1,14 +1,14 @@ module es { export class ShapeCollisionsPoint { public static pointToCircle(point: Vector2, circle: Circle, result: CollisionResult): boolean { - let distanceSquared = Vector2.distanceSquared(point, circle.position); + let distanceSquared = Vector2.sqrDistance(point, circle.position); let sumOfRadii = 1 + circle.radius; let collided = distanceSquared < sumOfRadii * sumOfRadii; if (collided) { - result.normal = Vector2.normalize(Vector2.subtract(point, circle.position)); + result.normal = point.sub(circle.position).normalize(); let depth = sumOfRadii - Math.sqrt(distanceSquared); - result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth, -depth), result.normal); - result.point = Vector2.add(circle.position, Vector2.multiply(result.normal, new Vector2(circle.radius, circle.radius))); + result.minimumTranslationVector = result.normal.scale(-depth);; + result.point = circle.position.add(result.normal.scale(circle.radius)); return true; } @@ -16,11 +16,11 @@ module es { return false; } - public static pointToBox(point: Vector2, box: Box, result: CollisionResult = new CollisionResult()){ - if (box.containsPoint(point)){ + public static pointToBox(point: Vector2, box: Box, result: CollisionResult = new CollisionResult()) { + if (box.containsPoint(point)) { // 在方框的空间里找到点 result.point = box.bounds.getClosestPointOnRectangleBorderToPoint(point, result.normal); - result.minimumTranslationVector = Vector2.subtract(point, result.point); + result.minimumTranslationVector = point.sub(result.point); return true; } @@ -30,12 +30,15 @@ module es { public static pointToPoly(point: Vector2, poly: Polygon, result: CollisionResult = new CollisionResult()): boolean { if (poly.containsPoint(point)) { - let distanceSquared = new Ref(0); - let closestPoint = Polygon.getClosestPointOnPolygonToPoint(poly.points, Vector2.subtract(point, poly.position), distanceSquared, result.normal); - - result.minimumTranslationVector = new Vector2(result.normal.x * Math.sqrt(distanceSquared.value), result.normal.y * Math.sqrt(distanceSquared.value)); - result.point = Vector2.add(closestPoint, poly.position); - + const res = Polygon.getClosestPointOnPolygonToPoint( + poly.points, + point.sub(poly.position) + ); + result.normal = res.edgeNormal; + result.minimumTranslationVector = result.normal.scale( + Math.sqrt(res.distanceSquared) + ); + result.point = res.closestPoint.sub(poly.position); return true; } diff --git a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPolygon.ts b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPolygon.ts index b063a2f4..d20c2b4d 100644 --- a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPolygon.ts +++ b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPolygon.ts @@ -9,39 +9,31 @@ module es { public static polygonToPolygon(first: Polygon, second: Polygon, result: CollisionResult): boolean { let isIntersecting = true; - let firstEdges = first.edgeNormals.slice(); - let secondEdges = second.edgeNormals.slice(); + const firstEdges = first.edgeNormals; + const secondEdges = second.edgeNormals; let minIntervalDistance = Number.POSITIVE_INFINITY; - let translationAxis = new Vector2(); - let polygonOffset = Vector2.subtract(first.position, second.position); + let translationAxis = Vector2.zero; + let polygonOffset = first.position.sub(second.position); let axis: Vector2; // 循环穿过两个多边形的所有边 for (let edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) { // 1. 找出当前多边形是否相交 // 多边形的归一化轴垂直于缓存给我们的当前边 - if (edgeIndex < firstEdges.length) { - axis = firstEdges[edgeIndex]; - } else { - axis = secondEdges[edgeIndex - firstEdges.length]; - } + axis = edgeIndex < firstEdges.length ? firstEdges[edgeIndex] : secondEdges[edgeIndex - firstEdges.length]; // 求多边形在当前轴上的投影 - let minA = new Ref(0); - let minB = new Ref(0); - let maxA = new Ref(0); - let maxB = new Ref(0); let intervalDist = 0; - this.getInterval(axis, first, minA, maxA); - this.getInterval(axis, second, minB, maxB); + let {min: minA, max: maxA} = this.getInterval(axis, first); + const {min: minB, max: maxB} = this.getInterval(axis, second); // 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。 - let relativeIntervalOffset = Vector2.dot(polygonOffset, axis); - minA.value += relativeIntervalOffset; - maxA.value += relativeIntervalOffset; + const relativeIntervalOffset = polygonOffset.dot(axis); + minA += relativeIntervalOffset; + maxA += relativeIntervalOffset; // 检查多边形投影是否正在相交 - intervalDist = this.intervalDistance(minA.value, maxA.value, minB.value, maxB.value); + intervalDist = this.intervalDistance(minA, maxA, minB, maxB); if (intervalDist > 0) isIntersecting = false; @@ -56,16 +48,16 @@ module es { intervalDist = Math.abs(intervalDist); if (intervalDist < minIntervalDistance) { minIntervalDistance = intervalDist; - translationAxis = axis; + translationAxis.setTo(axis.x, axis.y); - if (Vector2.dot(translationAxis, polygonOffset) < 0) - translationAxis = new Vector2(-translationAxis.x, -translationAxis.y); + if (translationAxis.dot(polygonOffset) < 0) + translationAxis = translationAxis.scale(-1); } } // 利用最小平移向量对多边形进行推入。 result.normal = translationAxis; - result.minimumTranslationVector = new Vector2(-translationAxis.x * minIntervalDistance, -translationAxis.y * minIntervalDistance); + result.minimumTranslationVector = translationAxis.scale(-minIntervalDistance); return true; } @@ -77,18 +69,21 @@ module es { * @param min * @param max */ - public static getInterval(axis: Vector2, polygon: Polygon, min: Ref, max: Ref) { - let dot = Vector2.dot(polygon.points[0], axis); - min.value = max.value = dot; - + public static getInterval(axis: Vector2, polygon: Polygon): {min: number, max: number} { + const res = {min: 0, max: 0}; + let dot: number; + dot = polygon.points[0].dot(axis); + res.max = dot; + res.min = dot; for (let i = 1; i < polygon.points.length; i++) { - dot = Vector2.dot(polygon.points[i], axis); - if (dot < min.value) { - min.value = dot; - } else if (dot > max.value) { - max.value = dot; + dot = polygon.points[i].dot(axis); + if (dot < res.min) { + res.min = dot; + } else if (dot > res.max) { + res.max = dot; } } + return res; } /** @@ -98,7 +93,7 @@ module es { * @param minB * @param maxB */ - public static intervalDistance(minA: number, maxA: number, minB: number, maxB) { + public static intervalDistance(minA: number, maxA: number, minB: number, maxB: number) { if (minA < minB) return minB - maxA; diff --git a/source/src/Physics/SpatialHash.ts b/source/src/Physics/SpatialHash.ts index 7029ba26..8fa65d31 100644 --- a/source/src/Physics/SpatialHash.ts +++ b/source/src/Physics/SpatialHash.ts @@ -22,7 +22,7 @@ module es { /** * 保存所有数据的字典 */ - public _cellDict: NumberDictionary = new NumberDictionary(); + public _cellDict: NumberDictionary = new NumberDictionary(); /** * 用于返回冲突信息的共享HashSet */ @@ -94,27 +94,42 @@ module es { this._cellDict.clear(); } + public debugDraw(secondsToDisplay: number) { + for (let x = this.gridBounds.x; x <= this.gridBounds.right; x++) { + for (let y = this.gridBounds.y; y <= this.gridBounds.bottom; y++) { + let cell = this.cellAtPosition(x, y); + if (cell != null && cell.length > 0) + this.debugDrawCellDetails(x, y, secondsToDisplay); + } + } + } + + private debugDrawCellDetails(x: number, y: number, secondsToDisplay: number = 0.5) { + Graphics.instance.batcher.drawHollowRect(x * this._cellSize, y * this._cellSize, this._cellSize, this._cellSize, new Color(255, 0, 0), secondsToDisplay); + Graphics.instance.batcher.end(); + } + /** * 返回边框与单元格相交的所有对象 * @param bounds * @param excludeCollider * @param layerMask */ - public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Set { + public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Collider[] { this._tempHashSet.clear(); - let p1 = this.cellCoords(bounds.x, bounds.y); - let p2 = this.cellCoords(bounds.right, bounds.bottom); + const p1 = this.cellCoords(bounds.x, bounds.y); + const p2 = this.cellCoords(bounds.right, bounds.bottom); for (let x = p1.x; x <= p2.x; x++) { for (let y = p1.y; y <= p2.y; y++) { - let cell = this.cellAtPosition(x, y); - if (cell == null) + const cell = this.cellAtPosition(x, y); + if (!cell) continue; // 当cell不为空。循环并取回所有碰撞器 for (let i = 0; i < cell.length; i++) { - let collider = cell[i]; + const collider = cell[i]; // 如果它是自身或者如果它不匹配我们的层掩码 跳过这个碰撞器 if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer.value)) @@ -127,7 +142,7 @@ module es { } } - return this._tempHashSet; + return Array.from(this._tempHashSet); } /** @@ -139,9 +154,9 @@ module es { * @param hits * @param layerMask */ - public linecast(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number){ + public linecast(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number, ignoredColliders: Set) { let ray = new Ray2D(start, end); - this._raycastParser.start(ray, hits, layerMask); + this._raycastParser.start(ray, hits, layerMask, ignoredColliders); // 获取我们的起始/结束位置,与我们的网格在同一空间内 let currentCell = this.cellCoords(start.x, start.y); @@ -174,24 +189,24 @@ module es { // 开始遍历并返回交叉单元格。 let cell = this.cellAtPosition(currentCell.x, currentCell.y); - if (cell && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)){ + if (cell != null && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)) { this._raycastParser.reset(); return this._raycastParser.hitCounter; } - while (currentCell.x != lastCell.x || currentCell.y != lastCell.y){ - if (tMaxX < tMaxY){ - currentCell.x = Math.floor(MathHelper.approach(currentCell.x, lastCell.x, Math.abs(stepX))); + while (currentCell.x != lastCell.x || currentCell.y != lastCell.y) { + if (tMaxX < tMaxY) { + currentCell.x = MathHelper.approach(currentCell.x, lastCell.x, Math.abs(stepX)); tMaxX += tDeltaX; - }else{ - currentCell.y = Math.floor(MathHelper.approach(currentCell.y, lastCell.y, Math.abs(stepY))); + } else { + currentCell.y = MathHelper.approach(currentCell.y, lastCell.y, Math.abs(stepY)); tMaxY += tDeltaY; } cell = this.cellAtPosition(currentCell.x, currentCell.y); - if (cell && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)){ + if (cell && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)) { this._raycastParser.reset(); return this._raycastParser.hitCounter; } @@ -202,6 +217,7 @@ module es { return this._raycastParser.hitCounter; } + /** * 获取所有在指定矩形范围内的碰撞器 * @param rect @@ -210,23 +226,23 @@ module es { */ public overlapRectangle(rect: Rectangle, results: Collider[], layerMask: number) { this._overlapTestBox.updateBox(rect.width, rect.height); - this._overlapTestBox.position = rect.location; + this._overlapTestBox.position = rect.location.clone(); let resultCounter = 0; let potentials = this.aabbBroadphase(rect, null, layerMask); for (let collider of potentials) { if (collider instanceof BoxCollider) { results[resultCounter] = collider; - resultCounter ++; - } else if(collider instanceof CircleCollider) { + resultCounter++; + } else if (collider instanceof CircleCollider) { if (Collisions.rectToCircle(rect, collider.bounds.center, collider.bounds.width * 0.5)) { results[resultCounter] = collider; - resultCounter ++; + resultCounter++; } - } else if(collider instanceof PolygonCollider) { + } else if (collider instanceof PolygonCollider) { if (collider.shape.overlaps(this._overlapTestBox)) { results[resultCounter] = collider; - resultCounter ++; + resultCounter++; } } else { throw new Error("overlapRectangle对这个类型没有实现!"); @@ -247,17 +263,19 @@ module es { * @param layerMask */ public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask): number { - let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2); + const bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2); this._overlapTestCircle.radius = radius; this._overlapTestCircle.position = circleCenter; let resultCounter = 0; - let potentials = this.aabbBroadphase(bounds, null, layerMask); + const potentials = this.aabbBroadphase(bounds, null, layerMask); for (let collider of potentials) { if (collider instanceof BoxCollider) { - results[resultCounter] = collider; - resultCounter++; + if (collider.shape.overlaps(this._overlapTestCircle)) { + results[resultCounter] = collider; + resultCounter++; + } } else if (collider instanceof CircleCollider) { if (collider.shape.overlaps(this._overlapTestCircle)) { results[resultCounter] = collider; @@ -273,7 +291,7 @@ module es { } // 如果我们所有的结果数据有了则返回 - if (resultCounter == results.length) + if (resultCounter === results.length) return resultCounter; } @@ -308,14 +326,10 @@ module es { } } - /** - * 包装一个Unit32,列表碰撞器字典 - * 它的主要目的是将int、int x、y坐标散列到单个Uint32键中,使用O(1)查找。 - */ - export class NumberDictionary { - public _store: Map = new Map(); + export class NumberDictionary { + public _store: Map = new Map(); - public add(x: number, y: number, list: Collider[]) { + public add(x: number, y: number, list: T[]) { this._store.set(this.getKey(x, y), list); } @@ -323,7 +337,7 @@ module es { * 使用蛮力方法从字典存储列表中移除碰撞器 * @param obj */ - public remove(obj: Collider) { + public remove(obj: T) { this._store.forEach(list => { let linqList = new es.List(list); if (linqList.contains(obj)) @@ -331,12 +345,12 @@ module es { }) } - public tryGetValue(x: number, y: number): Collider[] { + public tryGetValue(x: number, y: number): T[] { return this._store.get(this.getKey(x, y)); } - public getKey(x: number, y: number){ - return x << 16 | (y >>> 0); + public getKey(x: number, y: number) { + return `${x}_${y}`; } /** @@ -350,7 +364,11 @@ module es { export class RaycastResultParser { public hitCounter: number; public static compareRaycastHits = (a: RaycastHit, b: RaycastHit) => { - return a.distance - b.distance; + if (a.distance !== b.distance) { + return a.distance - b.distance; + } else { + return a.collider.castSortOrder - b.collider.castSortOrder; + } }; public _hits: RaycastHit[]; @@ -359,11 +377,13 @@ module es { public _cellHits: RaycastHit[] = []; public _ray: Ray2D; public _layerMask: number; + private _ignoredColliders: Set; - public start(ray: Ray2D, hits: RaycastHit[], layerMask: number) { + public start(ray: Ray2D, hits: RaycastHit[], layerMask: number, ignoredColliders: Set) { this._ray = ray; this._hits = hits; this._layerMask = layerMask; + this._ignoredColliders = ignoredColliders; this.hitCounter = 0; } @@ -374,9 +394,8 @@ module es { * @param cell */ public checkRayIntersection(cellX: number, cellY: number, cell: Collider[]): boolean { - let fraction: Ref = new Ref(0); for (let i = 0; i < cell.length; i++) { - let potential = cell[i]; + const potential = cell[i]; // 管理我们已经处理过的碰撞器 if (new es.List(this._checkedColliders).contains(potential)) @@ -391,11 +410,16 @@ module es { if (!Flags.isFlagSet(this._layerMask, potential.physicsLayer.value)) continue; + if (this._ignoredColliders && this._ignoredColliders.has(potential)) { + continue; + } + // TODO: rayIntersects的性能够吗?需要测试它。Collisions.rectToLine可能更快 // TODO: 如果边界检查返回更多数据,我们就不需要为BoxCollider检查做任何事情 // 在做形状测试之前先做一个边界检查 - let colliderBounds = potential.bounds.clone(); - if (colliderBounds.rayIntersects(this._ray, fraction) && fraction.value <= 1){ + const colliderBounds = potential.bounds; + const res = colliderBounds.rayIntersects(this._ray); + if (res.intersected && res.distance <= 1) { if (potential.shape.collidesWithLine(this._ray.start, this._ray.end, this._tempHit)) { // 检查一下,我们应该排除这些射线,射线cast是否在碰撞器中开始 if (!Physics.raycastsStartInColliders && potential.shape.containsPoint(this._ray.start)) @@ -409,27 +433,28 @@ module es { } } - if (this._cellHits.length == 0) + if (this._cellHits.length === 0) return false; // 所有处理单元完成。对结果进行排序并将命中结果打包到结果数组中 this._cellHits.sort(RaycastResultParser.compareRaycastHits); - for (let i = 0; i < this._cellHits.length; i ++){ + for (let i = 0; i < this._cellHits.length; i++) { this._hits[this.hitCounter] = this._cellHits[i]; // 增加命中计数器,如果它已经达到数组大小的限制,我们就完成了 - this.hitCounter ++; - if (this.hitCounter == this._hits.length) + this.hitCounter++; + if (this.hitCounter === this._hits.length) return true; } return false; } - public reset(){ + public reset() { this._hits = null; this._checkedColliders.length = 0; this._cellHits.length = 0; + this._ignoredColliders = null; } } } diff --git a/source/src/Physics/Verlet/Composites/Ball.ts b/source/src/Physics/Verlet/Composites/Ball.ts new file mode 100644 index 00000000..38aad51b --- /dev/null +++ b/source/src/Physics/Verlet/Composites/Ball.ts @@ -0,0 +1,9 @@ +/// +module es { + export class Ball extends Composite { + constructor(position: Vector2, radius: number = 10) { + super(); + this.addParticle(new Particle(position)).radius = radius; + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Composites/Box.ts b/source/src/Physics/Verlet/Composites/Box.ts new file mode 100644 index 00000000..b6fba44f --- /dev/null +++ b/source/src/Physics/Verlet/Composites/Box.ts @@ -0,0 +1,23 @@ +/// +module es { + export class VerletBox extends es.Composite { + constructor(center: es.Vector2, width: number, height: number, borderStiffness: number = 0.2, diagonalStiffness: number = 0.5) { + super(); + + const tl = this.addParticle(new Particle(center.add(new Vector2(-width / 2, -height / 2)))); + const tr = this.addParticle(new Particle(center.add(new Vector2(width / 2, -height / 2)))); + const br = this.addParticle(new Particle(center.add(new Vector2(width / 2, height / 2)))); + const bl = this.addParticle(new Particle(center.add(new Vector2(-width / 2, height / 2)))); + + this.addConstraint(new DistanceConstraint(tl, tr, borderStiffness)); + this.addConstraint(new DistanceConstraint(tr, br, borderStiffness)); + this.addConstraint(new DistanceConstraint(br, bl, borderStiffness)); + this.addConstraint(new DistanceConstraint(bl, tl, borderStiffness)); + + this.addConstraint(new DistanceConstraint(tl, br, diagonalStiffness)) + .setCollidesWithColliders(false); + this.addConstraint(new DistanceConstraint(bl, tr, diagonalStiffness)) + .setCollidesWithColliders(false); + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Composites/Cloth.ts b/source/src/Physics/Verlet/Composites/Cloth.ts new file mode 100644 index 00000000..7622b452 --- /dev/null +++ b/source/src/Physics/Verlet/Composites/Cloth.ts @@ -0,0 +1,33 @@ +module es { + export class Cloth extends Composite { + constructor(topLeftPosition: Vector2, width: number, height: number, segments: number = 20, stiffness: number = 0.25, + tearSensitivity: number = 5, connectHorizontalParticles: boolean = true) { + super(); + const xStride = width / segments; + const yStride = height / segments; + + for (let y = 0; y < segments; y++) { + for (let x = 0; x < segments; x++) { + const px = topLeftPosition.x + x * xStride; + const py = topLeftPosition.y + y + yStride; + const particle = this.addParticle(new Particle(new Vector2(px, py))); + + if (connectHorizontalParticles && x > 0) + this.addConstraint(new DistanceConstraint(this.particles[y * segments + x], + this.particles[y * segments + x - 1], stiffness)) + .setTearSensitivity(tearSensitivity) + .setCollidesWithColliders(false); + + if (y > 0) + this.addConstraint(new DistanceConstraint(this.particles[y * segments + x], + this.particles[(y - 1) * segments + x], stiffness)) + .setTearSensitivity(tearSensitivity) + .setCollidesWithColliders(false); + + if (y == 0) + particle.pin(); + } + } + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Composites/Composite.ts b/source/src/Physics/Verlet/Composites/Composite.ts new file mode 100644 index 00000000..e237956e --- /dev/null +++ b/source/src/Physics/Verlet/Composites/Composite.ts @@ -0,0 +1,88 @@ +module es { + export class Composite { + public friction: Vector2 = new Vector2(0.98, 1); + public drawParticles: boolean = true; + public drawConstraints: boolean = true; + public collidesWithLayers: number = Physics.allLayers; + public particles: Particle[] = []; + _constraints: Constraint[] = []; + + public addParticle(particle: Particle): Particle { + this.particles.push(particle); + return particle; + } + + public removeParticle(particle: Particle) { + const index = this.particles.indexOf(particle); + this.particles.splice(index, 1); + } + + public removeAll() { + this.particles.length = 0; + this._constraints.length = 0; + } + + public addConstraint(constraint: T): T { + this._constraints.push(constraint); + constraint.composite = this; + return constraint; + } + + public removeConstraint(constraint: Constraint) { + const index = this._constraints.indexOf(constraint); + this._constraints.splice(index, 1); + } + + public applyForce(force: Vector2) { + for (let j = 0; j < this.particles.length; j ++) + this.particles[j].applyForce(force); + } + + public solveConstraints() { + for (let i = this._constraints.length - 1; i >= 0; i --) + this._constraints[i].solve(); + } + + public updateParticles(deltaTimeSquared: number, gravity: Vector2) { + for (let j = 0; j < this.particles.length; j ++) { + const p = this.particles[j]; + if (p.isPinned) { + p.position = p.pinnedPosition; + continue; + } + + p.applyForce(gravity.scale(p.mass)); + + const vel = p.position.sub(p.lastPosition).multiply(this.friction); + const nextPos = p.position.add(vel).add(p.acceleration.scale(0.5 * deltaTimeSquared)); + + p.lastPosition = p.position; + p.position = nextPos; + p.acceleration.x = p.acceleration.y = 0; + } + } + + public handleConstraintCollisions() { + for (let i = this._constraints.length - 1; i >= 0; i --) { + if (this._constraints[i].collidesWithColliders) + this._constraints[i].handleCollisions(this.collidesWithLayers); + } + } + + public debugRender(batcher: IBatcher) { + if (this.drawConstraints) { + for (let i = 0; i < this._constraints.length; i ++) + this._constraints[i].debugRender(batcher); + } + + if (this.drawParticles) { + for (let i = 0; i < this.particles.length; i ++) { + if (this.particles[i].radius == 0) + batcher.drawPixel(this.particles[i].position, new Color(220, 52, 94), 4); + else + batcher.drawCircleLow(this.particles[i].position, this.particles[i].radius, new Color(220, 52, 94), 1, 4); + } + } + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Composites/LineSegments.ts b/source/src/Physics/Verlet/Composites/LineSegments.ts new file mode 100644 index 00000000..2e7992f7 --- /dev/null +++ b/source/src/Physics/Verlet/Composites/LineSegments.ts @@ -0,0 +1,20 @@ +module es { + export class LineSegments extends Composite { + constructor(vertices: Vector2[], stiffness: number) { + super(); + + for (let i = 0; i < vertices.length; i ++) { + const p = new Particle(vertices[i]); + this.addParticle(p); + + if (i > 0) + this.addConstraint(new DistanceConstraint(this.particles[i], this.particles[i - 1], stiffness)); + } + } + + public pinParticleAtIndex(index: number): LineSegments { + this.particles[index].pin(); + return this; + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Composites/Ragdoll.ts b/source/src/Physics/Verlet/Composites/Ragdoll.ts new file mode 100644 index 00000000..a1ef573d --- /dev/null +++ b/source/src/Physics/Verlet/Composites/Ragdoll.ts @@ -0,0 +1,55 @@ +module es { + export class Ragdoll extends Composite { + constructor(x: number, y: number, bodyHeight: number) { + super(); + + const headLength = bodyHeight / 7.5; + const head = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + head.radius = headLength * 0.75; + head.mass = 4; + const shoulder = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + shoulder.mass = 26; + this.addConstraint(new DistanceConstraint(head, shoulder, 1, 5 / 4 * headLength)); + + const elbowLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + const elbowRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + elbowLeft.mass = 2; + elbowRight.mass = 2; + this.addConstraint(new DistanceConstraint(elbowLeft, shoulder, 1, headLength * 3 / 2)); + this.addConstraint(new DistanceConstraint(elbowRight, shoulder, 1, headLength * 3 / 2)); + + const handLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + const handRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + handLeft.mass = 2; + handRight.mass = 2; + this.addConstraint(new DistanceConstraint(handLeft, elbowLeft, 1, headLength * 2)); + this.addConstraint(new DistanceConstraint(handRight, elbowRight, 1, headLength * 2)); + + const pelvis = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + pelvis.mass = 15; + this.addConstraint(new DistanceConstraint(pelvis, shoulder, 0.8, headLength * 3.5)); + + this.addConstraint(new DistanceConstraint(pelvis, head, 0.02, bodyHeight * 2)) + .setCollidesWithColliders(false); + + const kneeLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + const kneeRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + kneeLeft.mass = 10; + kneeRight.mass = 10; + this.addConstraint(new DistanceConstraint(kneeLeft, pelvis, 1, headLength * 2)); + this.addConstraint(new DistanceConstraint(kneeRight, pelvis, 1, headLength * 2)); + + const footLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + const footRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) })); + footLeft.mass = 5; + footRight.mass = 5; + this.addConstraint(new DistanceConstraint(footLeft, kneeLeft, 1, headLength * 2)); + this.addConstraint(new DistanceConstraint(footRight, kneeRight, 1, headLength * 2)); + + this.addConstraint(new DistanceConstraint(footLeft, shoulder, 0.001, bodyHeight * 2)) + .setCollidesWithColliders(false); + this.addConstraint(new DistanceConstraint(footLeft, shoulder, 0.001, bodyHeight * 2)) + .setCollidesWithColliders(false); + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Composites/Tire.ts b/source/src/Physics/Verlet/Composites/Tire.ts new file mode 100644 index 00000000..3a472c0d --- /dev/null +++ b/source/src/Physics/Verlet/Composites/Tire.ts @@ -0,0 +1,23 @@ +module es { + export class Tire extends Composite { + constructor(origin: Vector2, radius: number, segments: number, spokeStiffness: number = 1, treadStiffness: number = 1) { + super(); + + const stride = 2 * Math.PI / segments; + for (let i = 0; i < segments; i ++) { + const theta = i * stride; + this.addParticle(new Particle(new Vector2(origin.x + Math.cos(theta) * radius, + origin.y + Math.sin(theta) * radius))); + } + + const centerParticle = this.addParticle(new Particle(origin)); + + for (let i = 0; i < segments; i ++) { + this.addConstraint(new DistanceConstraint(this.particles[i], this.particles[(i + 1) % segments], treadStiffness)); + this.addConstraint(new DistanceConstraint(this.particles[i], centerParticle, spokeStiffness)) + .setCollidesWithColliders(false); + this.addConstraint(new DistanceConstraint(this.particles[i], this.particles[(i + 5) % segments], treadStiffness)); + } + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Constraints/AngleConstraint.ts b/source/src/Physics/Verlet/Constraints/AngleConstraint.ts new file mode 100644 index 00000000..87d8bfd5 --- /dev/null +++ b/source/src/Physics/Verlet/Constraints/AngleConstraint.ts @@ -0,0 +1,47 @@ +/// +module es { + export class AngleConstraint extends Constraint { + public stiffness: number = 0; + public angleInRadius: number = 0; + + _particleA: Particle; + _centerParticle: Particle; + _particleC: Particle; + + constructor(a: Particle, center: Particle, c: Particle, stiffness: number) { + super(); + this._particleA = a; + this._centerParticle = center; + this._particleC = c; + this.stiffness = stiffness; + + this.collidesWithColliders = false; + + this.angleInRadius = this.angleBetweenParticles(); + } + + angleBetweenParticles(): number { + const first = this._particleA.position.sub(this._centerParticle.position); + const second = this._particleC.position.sub(this._centerParticle.position); + + return Math.atan2(first.x * second.y - first.y * second.x, first.x * second.x + first.y * second.y); + } + + public solve() { + const angleBetween = this.angleBetweenParticles(); + let diff = angleBetween - this.angleInRadius; + + if (diff <= -Math.PI) + diff += 2 * Math.PI; + else if(diff >= Math.PI) + diff -= 2 * Math.PI; + + diff *= this.stiffness; + + this._particleA.position = MathHelper.rotateAround2(this._particleA.position, this._centerParticle.position, diff); + this._particleC.position = MathHelper.rotateAround2(this._particleC.position, this._centerParticle.position, -diff); + this._centerParticle.position = MathHelper.rotateAround2(this._centerParticle.position, this._particleA.position, diff); + this._centerParticle.position = MathHelper.rotateAround2(this._centerParticle.position, this._particleC.position, -diff); + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Constraints/Constraint.ts b/source/src/Physics/Verlet/Constraints/Constraint.ts new file mode 100644 index 00000000..dd125e90 --- /dev/null +++ b/source/src/Physics/Verlet/Constraints/Constraint.ts @@ -0,0 +1,16 @@ +module es { + export abstract class Constraint { + public composite: Composite; + public collidesWithColliders: boolean = true; + + public abstract solve(): void; + + public handleCollisions(collidesWithLayers: number) { + + } + + public debugRender(batcher: IBatcher) { + + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Constraints/DistanceConstraint.ts b/source/src/Physics/Verlet/Constraints/DistanceConstraint.ts new file mode 100644 index 00000000..d36a5711 --- /dev/null +++ b/source/src/Physics/Verlet/Constraints/DistanceConstraint.ts @@ -0,0 +1,123 @@ +module es { + export class DistanceConstraint extends Constraint { + public stiffness: number = 0; + public restingDistance: number = 0; + public tearSensitivity = Number.POSITIVE_INFINITY; + public shouldApproximateCollisionsWithPoints: boolean = false; + public totalPointsToApproximateCollisionsWith = 5; + _particleOne: Particle; + _particleTwo: Particle; + static _polygon: Polygon = new Polygon([]); + + constructor(first: Particle, second: Particle, stiffness: number, distance: number = -1) { + super(); + + DistanceConstraint._polygon.create(2, 1); + this._particleOne = first; + this._particleTwo = second; + this.stiffness = stiffness; + + if (distance > -1) + this.restingDistance = distance; + else + this.restingDistance = first.position.distance(second.position); + } + + public static create(a: Particle, center: Particle, c: Particle, stiffness: number, angleInDegrees: number) { + const aToCenter = a.position.distance(center.position); + const cToCenter = c.position.distance(center.position); + const distance = Math.sqrt(aToCenter * aToCenter + cToCenter * cToCenter - (2 * aToCenter * cToCenter * Math.cos(angleInDegrees * MathHelper.Deg2Rad))); + + return new DistanceConstraint(a, c, stiffness, distance); + } + + public setTearSensitivity(tearSensitivity: number) { + this.tearSensitivity = tearSensitivity; + return this; + } + + public setCollidesWithColliders(collidesWithColliders: boolean) { + this.collidesWithColliders = collidesWithColliders; + return this; + } + + public setShouldApproximateCollisionsWithPoints(shouldApproximateCollisionsWithPoints: boolean) { + this.shouldApproximateCollisionsWithPoints = shouldApproximateCollisionsWithPoints; + return this; + } + + public solve(): void { + const diff = this._particleOne.position.sub(this._particleTwo.position); + const d = diff.distance(); + + const difference = (this.restingDistance - d) / d; + if (d / this.restingDistance > this.tearSensitivity) { + this.composite.removeConstraint(this); + return; + } + + const im1 = 1 / this._particleOne.mass; + const im2 = 1 / this._particleTwo.mass; + const scalarP1 = (im1 / (im1 + im2)) * this.stiffness; + const scalarP2 = this.stiffness - scalarP1; + + this._particleOne.position = this._particleOne.position.add(diff.scale(scalarP1 * difference)); + this._particleTwo.position = this._particleTwo.position.sub(diff.scale(scalarP2 * difference)); + } + + public handleCollisions(collidesWithLayers: number) { + if (this.shouldApproximateCollisionsWithPoints) { + this.approximateCollisionsWithPoints(collidesWithLayers); + return; + } + + const minX = Math.min(this._particleOne.position.x, this._particleTwo.position.x); + const maxX = Math.max(this._particleOne.position.x, this._particleTwo.position.x); + const minY = Math.min(this._particleOne.position.y, this._particleTwo.position.y); + const maxY = Math.max(this._particleOne.position.y, this._particleTwo.position.y); + DistanceConstraint._polygon.bounds = Rectangle.fromMinMax(minX, minY, maxX, maxY); + + let midPoint: Vector2 = Vector2.zero; + this.preparePolygonForCollisionChecks(midPoint); + + const colliders = Physics.boxcastBroadphase(DistanceConstraint._polygon.bounds, collidesWithLayers); + for (let i = 0; i < colliders.length; i ++) { + const collider = colliders[i]; + const result = new CollisionResult(); + if (DistanceConstraint._polygon.collidesWithShape(collider.shape, result)) { + this._particleOne.position = this._particleOne.position.sub(result.minimumTranslationVector); + this._particleTwo.position = this._particleTwo.position.sub(result.minimumTranslationVector); + } + } + } + + approximateCollisionsWithPoints(collidesWithLayers: number) { + let pt: Vector2; + for (let j = 0; j < this.totalPointsToApproximateCollisionsWith - 1; j ++) { + pt = Vector2.lerp(this._particleOne.position, this._particleTwo.position, (j + 1) / this.totalPointsToApproximateCollisionsWith); + const collidedCount = Physics.overlapCircleAll(pt, 3, VerletWorld._colliders, collidesWithLayers); + for (let i = 0; i < collidedCount; i ++) { + const collider = VerletWorld._colliders[i]; + const collisionResult = new CollisionResult(); + if (collider.shape.pointCollidesWithShape(pt, collisionResult)) { + this._particleOne.position = this._particleOne.position.sub(collisionResult.minimumTranslationVector); + this._particleTwo.position = this._particleTwo.position.sub(collisionResult.minimumTranslationVector); + } + } + } + } + + preparePolygonForCollisionChecks(midPoint: Vector2) { + const tempMidPoint = Vector2.lerp(this._particleOne.position, this._particleTwo.position, 0.5); + midPoint.setTo(tempMidPoint.x, tempMidPoint.y); + DistanceConstraint._polygon.position = midPoint; + DistanceConstraint._polygon.points[0] = this._particleOne.position.sub(DistanceConstraint._polygon.position); + DistanceConstraint._polygon.points[1] = this._particleTwo.position.sub(DistanceConstraint._polygon.position); + DistanceConstraint._polygon.recalculateCenterAndEdgeNormals(); + } + + public debugRender(batcher: IBatcher) { + batcher.drawLine(this._particleOne.position, this._particleTwo.position, new Color(67, 62, 54), 1); + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/Particle.ts b/source/src/Physics/Verlet/Particle.ts new file mode 100644 index 00000000..b77e1e03 --- /dev/null +++ b/source/src/Physics/Verlet/Particle.ts @@ -0,0 +1,39 @@ +module es { + export class Particle { + public position: Vector2 = Vector2.zero; + public lastPosition: Vector2 = Vector2.zero; + public mass = 1; + public radius: number = 0; + public collidesWithColliders: boolean = true; + public isPinned: boolean = false; + public acceleration: Vector2 = Vector2.zero; + public pinnedPosition: Vector2 = Vector2.zero; + + constructor(position: {x: number, y: number}) { + this.position = new Vector2(position.x, position.y); + this.lastPosition = new Vector2(position.x, position.y); + } + + public applyForce(force: Vector2) { + this.acceleration = this.acceleration.add(force.divideScaler(this.mass)); + } + + public pin(): Particle { + this.isPinned = true; + this.pinnedPosition = this.position; + return this; + } + + public pinTo(position: Vector2): Particle { + this.isPinned = true; + this.pinnedPosition = position; + this.position = this.pinnedPosition; + return this; + } + + public unpin(): Particle { + this.isPinned = false; + return this; + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Verlet/VerletWorld.ts b/source/src/Physics/Verlet/VerletWorld.ts new file mode 100644 index 00000000..6a36c104 --- /dev/null +++ b/source/src/Physics/Verlet/VerletWorld.ts @@ -0,0 +1,163 @@ +module es { + export class VerletWorld { + public gravity: Vector2 = new Vector2(0, -980); + public constraintIterations = 3; + public maximumStepIterations = 5; + public simulationBounds: Rectangle; + public allowDragging: boolean = true; + public selectionRadiusSquared = 20 * 20; + _draggedParticle: Particle; + _composites: Composite[] = []; + public static _colliders: Collider[] = []; + _tempCircle: Circle = new Circle(1); + + _leftOverTime: number = 0; + _fixedDeltaTime: number = 1 / 60; + _iterationSteps: number = 0; + _fixedDeltaTimeSq: number = 0; + + onHandleDrag: Function; + + constructor(simulationBounds: Rectangle = null) { + this.simulationBounds = simulationBounds; + this._fixedDeltaTimeSq = Math.pow(this._fixedDeltaTime, 2); + } + + public update() { + this.updateTiming(); + + if (this.allowDragging) + this.handleDragging(); + + for (let iteration = 1; iteration <= this._iterationSteps; iteration ++) { + for (let i = this._composites.length - 1; i >= 0; i --) { + const composite = this._composites[i]; + for (let s = 0; s < this.constraintIterations; s ++) + composite.solveConstraints(); + + composite.updateParticles(this._fixedDeltaTimeSq, this.gravity); + + composite.handleConstraintCollisions(); + + for (let j = 0; j < composite.particles.length; j ++) { + const p = composite.particles[j]; + + if (this.simulationBounds) { + this.constrainParticleToBounds(p); + } + + if (p.collidesWithColliders) + this.handleCollisions(p, composite.collidesWithLayers); + } + } + } + } + + constrainParticleToBounds(p: Particle) { + const tempPos = p.position; + const bounds = this.simulationBounds; + + if (p.radius == 0) { + if (tempPos.y > bounds.height) + tempPos.y = bounds.height; + else if (tempPos.y < bounds.y) + tempPos.y = bounds.y; + + if (tempPos.x < bounds.x) + tempPos.x = bounds.x; + else if (tempPos.x > bounds.width) + tempPos.x = bounds.width; + } else { + if (tempPos.y < bounds.y + p.radius) + tempPos.y = 2 * (bounds.y + p.radius) - tempPos.y; + if (tempPos.y > bounds.height - p.radius) + tempPos.y = 2 * (bounds.height - p.radius) - tempPos.y; + if (tempPos.x > bounds.width - p.radius) + tempPos.x = 2 * (bounds.width - p.radius) - tempPos.x; + if (tempPos.x < bounds.x + p.radius) + tempPos.x = 2 * (bounds.x + p.radius) - tempPos.x; + } + + p.position = tempPos; + } + + handleCollisions(p: Particle, collidesWithLayers: number) { + const collidedCount = Physics.overlapCircleAll(p.position, p.radius, VerletWorld._colliders, collidesWithLayers); + for (let i = 0; i < collidedCount; i++) { + const collider = VerletWorld._colliders[i]; + if (collider.isTrigger) + continue; + + const collisionResult = new CollisionResult(); + + if (p.radius < 2) { + if (collider.shape.pointCollidesWithShape(p.position, collisionResult)) { + p.position = p.position.sub(collisionResult.minimumTranslationVector); + } + } else { + this._tempCircle.radius = p.radius; + this._tempCircle.position = p.position; + + if (this._tempCircle.collidesWithShape(collider.shape, collisionResult)) { + p.position = p.position.sub(collisionResult.minimumTranslationVector); + } + } + } + } + + updateTiming() { + this._leftOverTime += Time.deltaTime; + this._iterationSteps = Math.trunc(this._leftOverTime / this._fixedDeltaTime); + this._leftOverTime -= this._iterationSteps * this._fixedDeltaTime; + + this._iterationSteps = Math.min(this._iterationSteps, this.maximumStepIterations); + } + + public addComposite(composite: T): T { + this._composites.push(composite); + return composite; + } + + public removeComposite(composite: Composite) { + const index = this._composites.indexOf(composite); + this._composites.splice(index, 1); + } + + handleDragging() { + if (this.onHandleDrag) + this.onHandleDrag(); + } + + public getNearestParticle(position: Vector2) { + let nearestSquaredDistance = this.selectionRadiusSquared; + let particle: Particle = null; + + for (let j = 0; j < this._composites.length; j++) { + const particles = this._composites[j].particles; + for (let i = 0; i < particles.length; i++) { + const p = particles[i]; + const squaredDistanceToParticle = Vector2.sqrDistance(p.position, position); + if (squaredDistanceToParticle <= this.selectionRadiusSquared && + (particle == null || squaredDistanceToParticle < nearestSquaredDistance)) { + particle = p; + nearestSquaredDistance = squaredDistanceToParticle; + } + } + } + + return particle; + } + + public debugRender(batcher: IBatcher) { + for (let i = 0; i < this._composites.length; i ++) { + this._composites[i].debugRender(batcher); + } + + if (this.allowDragging) { + if (this._draggedParticle != null) { + batcher.drawCircle(this._draggedParticle.position, 8, Color.White); + } + } + } + } +} \ No newline at end of file diff --git a/source/src/Tween/AbstractTweenable.ts b/source/src/Tween/AbstractTweenable.ts new file mode 100644 index 00000000..1389dfea --- /dev/null +++ b/source/src/Tween/AbstractTweenable.ts @@ -0,0 +1,51 @@ +module es { + /** + * AbstractTweenable作为你可能想做的任何可以执行的自定义类的基础。 + * 这些类不同于ITweens,因为他们没有实现ITweenT接口。 + * 它只是说一个AbstractTweenable不仅仅是将一个值从开始移动到结束。 + * 它可以做任何需要每帧执行的事情。 + */ + export abstract class AbstractTweenable implements ITweenable { + protected _isPaused: boolean; + + /** + * abstractTweenable在完成后往往会被保留下来。 + * 这个标志可以让它们在内部知道自己当前是否被TweenManager盯上了,以便在必要时可以重新添加自己。 + */ + protected _isCurrentlyManagedByTweenManager: boolean; + + public abstract tick(): boolean; + + public recycleSelf() { + } + + public isRunning(): boolean { + return this._isCurrentlyManagedByTweenManager && !this._isPaused; + } + + public start() { + if (this._isCurrentlyManagedByTweenManager) { + this._isPaused = false; + return; + } + + TweenManager.addTween(this); + this._isCurrentlyManagedByTweenManager = true; + this._isPaused = false; + } + + public pause() { + this._isPaused = true; + } + + public resume() { + this._isPaused = false; + } + + public stop(bringToCompletion: boolean = false) { + TweenManager.removeTween(this); + this._isCurrentlyManagedByTweenManager = false; + this._isPaused = true; + } + } +} \ No newline at end of file diff --git a/source/src/Tween/Easing/EaseType.ts b/source/src/Tween/Easing/EaseType.ts new file mode 100644 index 00000000..f43ee2be --- /dev/null +++ b/source/src/Tween/Easing/EaseType.ts @@ -0,0 +1,207 @@ +module es { + export enum EaseType { + linear, + + sineIn, + sineOut, + sineInOut, + + quadIn, + quadOut, + quadInOut, + + quintIn, + quintOut, + quintInOut, + + cubicIn, + cubicOut, + cubicInOut, + + quartIn, + quartOut, + quartInOut, + + expoIn, + expoOut, + expoInOut, + + circleIn, + circleOut, + circleInOut, + + elasticIn, + elasticOut, + elasticInOut, + punch, + + backIn, + backOut, + backInOut, + + bounceIn, + bounceOut, + bounceInOut + } + + /** + * 助手的一个方法,它接收一个EaseType,并通过给定的持续时间和时间参数来应用该Ease方程。 + * 我们这样做是为了避免传来传去的Funcs为垃圾收集器制造大量垃圾 + */ + export class EaseHelper { + /** + * 返回 easeType 的相反 EaseType + * @param easeType + */ + public static oppositeEaseType(easeType: EaseType) { + switch (easeType) { + case EaseType.linear: + return easeType; + + case EaseType.backIn: + return EaseType.backOut; + case EaseType.backOut: + return EaseType.backIn; + case EaseType.backInOut: + return easeType; + + case EaseType.bounceIn: + return EaseType.bounceOut; + case EaseType.bounceOut: + return EaseType.bounceIn; + case EaseType.bounceInOut: + return easeType; + + case EaseType.circleIn: + return EaseType.circleOut; + case EaseType.circleOut: + return EaseType.circleIn; + case EaseType.circleInOut: + return easeType; + + case EaseType.cubicIn: + return EaseType.cubicOut; + case EaseType.cubicOut: + return EaseType.cubicIn; + case EaseType.circleInOut: + return easeType; + + case EaseType.punch: + return easeType; + + case EaseType.expoIn: + return EaseType.expoOut; + case EaseType.expoOut: + return EaseType.expoIn; + case EaseType.expoInOut: + return easeType; + + case EaseType.quadIn: + return EaseType.quadOut; + case EaseType.quadOut: + return EaseType.quadIn; + case EaseType.quadInOut: + return easeType; + + case EaseType.quartIn: + return EaseType.quadOut; + case EaseType.quartOut: + return EaseType.quartIn; + case EaseType.quadInOut: + return easeType; + + case EaseType.sineIn: + return EaseType.sineOut; + case EaseType.sineOut: + return EaseType.sineIn; + case EaseType.sineInOut: + return easeType; + + default: + return easeType; + } + } + + public static ease(easeType: EaseType, t: number, duration: number) { + switch (easeType) { + case EaseType.linear: + return Easing.Linear.easeNone(t, duration); + + case EaseType.backIn: + return Easing.Back.easeIn(t, duration); + case EaseType.backOut: + return Easing.Back.easeOut(t, duration); + case EaseType.backInOut: + return Easing.Back.easeInOut(t, duration); + + case EaseType.bounceIn: + return Easing.Bounce.easeIn(t, duration); + case EaseType.bounceOut: + return Easing.Bounce.easeOut(t, duration); + case EaseType.bounceInOut: + return Easing.Bounce.easeInOut(t, duration); + + case EaseType.circleIn: + return Easing.Circular.easeIn(t, duration); + case EaseType.circleOut: + return Easing.Circular.easeOut(t, duration); + case EaseType.circleInOut: + return Easing.Circular.easeInOut(t, duration); + + case EaseType.cubicIn: + return Easing.Cubic.easeIn(t, duration); + case EaseType.cubicOut: + return Easing.Cubic.easeOut(t, duration); + case EaseType.cubicInOut: + return Easing.Cubic.easeInOut(t, duration); + + case EaseType.elasticIn: + return Easing.Elastic.easeIn(t, duration); + case EaseType.elasticOut: + return Easing.Elastic.easeOut(t, duration); + case EaseType.elasticInOut: + return Easing.Elastic.easeInOut(t, duration); + case EaseType.punch: + return Easing.Elastic.punch(t, duration); + + case EaseType.expoIn: + return Easing.Exponential.easeIn(t, duration); + case EaseType.expoOut: + return Easing.Exponential.easeOut(t, duration); + case EaseType.expoInOut: + return Easing.Exponential.easeInOut(t, duration); + + case EaseType.quadIn: + return Easing.Quadratic.easeIn(t, duration); + case EaseType.quadOut: + return Easing.Quadratic.easeOut(t, duration); + case EaseType.quadInOut: + return Easing.Quadratic.easeInOut(t, duration); + + case EaseType.quadIn: + return Easing.Quadratic.easeIn(t, duration); + case EaseType.quadOut: + return Easing.Quadratic.easeOut(t, duration); + case EaseType.quadInOut: + return Easing.Quadratic.easeInOut(t, duration); + + case EaseType.quintIn: + return Easing.Quintic.easeIn(t, duration); + case EaseType.quintOut: + return Easing.Quintic.easeOut(t, duration); + case EaseType.quintInOut: + return Easing.Quintic.easeInOut(t, duration); + + case EaseType.sineIn: + return Easing.Sinusoidal.easeIn(t, duration); + case EaseType.sineOut: + return Easing.Sinusoidal.easeOut(t, duration); + case EaseType.sineInOut: + return Easing.Sinusoidal.easeInOut(t, duration); + + default: + return Easing.Linear.easeNone(t, duration); + } + } + } +} \ No newline at end of file diff --git a/source/src/Tween/Easing/Easing.ts b/source/src/Tween/Easing/Easing.ts new file mode 100644 index 00000000..2ebd6f9f --- /dev/null +++ b/source/src/Tween/Easing/Easing.ts @@ -0,0 +1,230 @@ +module es { + /** + * 标准缓和方程通过将b和c参数(起始值和变化值)用0和1替换,然后进行简化。 + * 这样做的目的是为了让我们可以得到一个0 - 1之间的原始值(除了弹性/反弹故意超过界限),然后用这个值来lerp任何东西 + */ + export module Easing { + export class Linear { + public static easeNone(t: number, d: number) { + return t / d; + } + } + + export class Quadratic { + public static easeIn(t: number, d: number) { + return (t /= d) * t; + } + + public static easeOut(t: number, d: number) { + return -1 * (t /= d) * (t - 2); + } + + public static easeInOut(t: number, d: number) { + if ((t /= d / 2) < 1) + return 0.5 * t * t; + return -0.5 * ((--t) * (t - 2) - 1); + } + } + + export class Back { + public static easeIn(t: number, d: number) { + return (t /= d) * t * ((1.70158 + 1) * t - 1.70158); + } + + public static easeOut(t: number, d: number) { + return ((t = t / d - 1) * t * ((1.70158 + 1) * t + 1.70158) + 1); + } + + public static easeInOut(t: number, d: number) { + let s = 1.70158; + if ((t /= d / 2) < 1) { + return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); + } + + return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); + } + } + + export class Bounce { + public static easeOut(t: number, d: number) { + if ((t /= d) < (1 / 2.75)) { + return (7.5625 * t * t); + } else if (t < (2 / 2.75)) { + return (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); + } else if (t < (2.5 / 2.75)) { + return (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); + } else { + return (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); + } + } + + public static easeIn(t: number, d: number) { + return 1 - this.easeOut(d - t, d); + } + + public static easeInOut(t: number, d: number) { + if (t < d / 2) + return this.easeIn(t * 2, d) * 0.5; + else + return this.easeOut(t * 2 - d, d) * 0.5 + 1 * 0.5; + } + } + + export class Circular { + public static easeIn(t: number, d: number) { + return -(Math.sqrt(1 - (t /= d) * t) - 1); + } + + public static easeOut(t: number, d: number) { + return Math.sqrt(1 - (t = t / d - 1) * t); + } + + public static easeInOut(t: number, d: number) { + if ((t /= d / 2) < 1) + return -0.5 * (Math.sqrt(1 - t * t) - 1); + return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); + } + } + + export class Cubic { + public static easeIn(t: number, d: number) { + return (t /= d) * t * t; + } + + public static easeOut(t: number, d: number) { + return ((t = t / d - 1) * t * t + 1); + } + + public static easeInOut(t: number, d: number) { + if ((t /= d / 2) < 1) + return 0.5 * t * t * t; + return 0.5 * ((t -= 2) * t * t + 2); + } + } + + export class Elastic { + public static easeIn(t: number, d: number) { + if (t == 0) + return 0; + + if ((t /= d) == 1) + return 1; + + let p = d * 0.3; + let s = p / 4; + return -(1 * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)); + } + + public static easeOut(t: number, d: number) { + if (t == 0) + return 0; + + if ((t /= d) == 1) + return 1; + + let p = d * 0.3; + let s = p / 4; + return (1 * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + 1); + } + + public static easeInOut(t: number, d: number) { + if (t == 0) + return 0; + + if ((t /= d / 2) == 2) + return 1; + + let p = d * (0.3 * 1.5); + let s = p / 4; + if (t < 1) + return -0.5 * (Math.pow(2, 10 * (t -= 1)) * Math.sin(t * d - s) * (2 * Math.PI) / p); + + return (Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + 1); + } + + public static punch(t: number, d: number) { + if (t == 0) + return 0; + + if ((t /= d) == 1) + return 0; + + const p = 0.3; + return (Math.pow(2, -10 * t) * Math.sin(t * (2 * Math.PI) / p)); + } + } + + export class Exponential { + public static easeIn(t: number, d: number) { + return (t == 0) ? 0 : Math.pow(2, 10 * (t / d - 1)); + } + + public static easeOut(t: number, d: number) { + return t == d ? 1 : (-Math.pow(2, -10 * t / d) + 1); + } + + public static easeInOut(t: number, d: number) { + if (t == 0) + return 0; + + if (t == d) + return 1; + + if ((t /= d / 2) < 1) { + return 0.5 * Math.pow(2, 10 * (t - 1)); + } + return 0.5 * (-Math.pow(2, -10 * --t) + 2); + } + } + + export class Quartic { + public static easeIn(t: number, d: number) { + return (t /= d) * t * t * t; + } + + public static easeOut(t: number, d: number) { + return -1 * ((t = t / d - 1) * t * t * t - 1); + } + + public static easeInOut(t: number, d: number) { + t /= d / 2; + if (t < 1) + return 0.5 * t * t * t * t; + + t -= 2; + return -0.5 * (t * t * t * t - 2); + } + } + + export class Quintic { + public static easeIn(t: number, d: number) { + return (t /= d) * t * t * t * t; + } + + public static easeOut(t: number, d: number) { + return ((t = t / d - 1) * t * t * t * t + 1); + } + + public static easeInOut(t: number, d: number) { + if ((t /= d / 2) < 1) + return 0.5 * t * t * t * t * t; + + return 0.5 * ((t -= 2) * t * t * t * t + 2); + } + } + + export class Sinusoidal { + public static easeIn(t: number, d: number) { + return -1 * Math.cos(t / d * (Math.PI / 2)) + 1; + } + + public static easeOut(t: number, d: number) { + return Math.sin(t / d * (Math.PI / 2)); + } + + public static easeInOut(t: number, d: number) { + return -0.5 * (Math.cos(Math.PI * t / d) - 1); + } + } + } +} \ No newline at end of file diff --git a/source/src/Tween/Easing/Lerps.ts b/source/src/Tween/Easing/Lerps.ts new file mode 100644 index 00000000..53957bda --- /dev/null +++ b/source/src/Tween/Easing/Lerps.ts @@ -0,0 +1,84 @@ +module es { + /** + * 一系列静态方法来处理所有常见的tween类型结构,以及它们的unclamped lerps.unclamped lerps对于超过0-1范围的bounce、elastic或其他tweens是必需的 + */ + export class Lerps { + public static lerp(from: Color, to: Color, t: number); + public static lerp(from: number, to: number, t: number); + public static lerp(from: Rectangle, to: Rectangle, t: number); + public static lerp(from: Vector2, to: Vector2, t: number); + public static lerp(from: any, to: any, t: number) { + if (typeof(from) == "number" && typeof(to) == "number") { + return from + (to - from) * t; + } + + if (from instanceof Color && to instanceof Color) { + const t255 = t * 255; + return new Color(from.r + (to.r - from.r) * t255 / 255, from.g + (to.g - from.g) * t255 / 255, + from.b + (to.b - from.b) * t255 / 255, from.a + (to.a - from.a) * t255 / 255) + } + + if (from instanceof Rectangle && to instanceof Rectangle) { + return new Rectangle( + (from.x + (to.x - from.x) * t), + (from.y + (to.x - from.y) * t), + (from.width + (to.width - from.width) * t), + (from.height + (to.height - from.height) * t) + ); + } + + if (from instanceof Vector2 && to instanceof Vector2) { + return new Vector2(from.x + (to.x - from.x) * t, from.y + (to.y - from.y) * t); + } + } + + public static angleLerp(from: Vector2, to: Vector2, t: number) { + // 我们计算这个lerp的最短角差 + let toMinusFrom = new Vector2(MathHelper.deltaAngle(from.x, to.x), MathHelper.deltaAngle(from.y, to.y)); + return new Vector2(from.x + toMinusFrom.x * t, from.y + toMinusFrom.y * t); + } + + public static ease(easeType: EaseType, from: Rectangle, to: Rectangle, t: number, duration: number); + public static ease(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number); + public static ease(easeType: EaseType, from: number, to: number, t: number, duration: number); + public static ease(easeType: EaseType, from: Color, to: Color, t: number, duration: number); + public static ease(easeType: EaseType, from: any, to: any, t: number, duration: number) { + if (typeof(from) == 'number' && typeof(to) == "number") { + return this.lerp(from, to, EaseHelper.ease(easeType, t, duration)); + } + + if (from instanceof Vector2 && to instanceof Vector2) { + return this.lerp(from, to, EaseHelper.ease(easeType, t, duration)); + } + + if (from instanceof Rectangle && to instanceof Rectangle) { + return this.lerp(from, to, EaseHelper.ease(easeType, t, duration)); + } + + if (from instanceof Color && to instanceof Color) { + return this.lerp(from, to, EaseHelper.ease(easeType, t, duration)); + } + } + + public static easeAngle(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number) { + return this.angleLerp(from, to, EaseHelper.ease(easeType, t, duration)); + } + + /** + * 使用半隐式欧拉方法。速度较慢,但总是很稳定。见 + * http://allenchou.net/2015/04/game-math-more-on-numeric-springing/ + * @param currentValue + * @param targetValue + * @param velocity Velocity的引用。如果在两次调用之间改变targetValue,请务必将其重置为0 + * @param dampingRatio 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + * @param angularFrequency 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定 + */ + public static fastSpring(currentValue: Vector2, targetValue: Vector2, velocity: Vector2, + dampingRatio: number, angularFrequency: number) { + velocity.add(velocity.scale(-2 * Time.deltaTime * dampingRatio * angularFrequency) + .add(targetValue.sub(currentValue).scale(Time.deltaTime * angularFrequency * angularFrequency))); + currentValue.add(velocity.scale(Time.deltaTime)); + return currentValue; + } + } +} \ No newline at end of file diff --git a/source/src/Tween/Interfaces/ITween.ts b/source/src/Tween/Interfaces/ITween.ts new file mode 100644 index 00000000..98fa1c0f --- /dev/null +++ b/source/src/Tween/Interfaces/ITween.ts @@ -0,0 +1,79 @@ +module es { + /** + * 一系列强类型、可链式的方法来设置各种tween属性 + */ + export interface ITween extends ITweenControl { + /** + * 设置该tween的易用性类型 + * @param easeType + */ + setEaseType(easeType: EaseType): ITween; + /** + * 设置启动tween前的延迟 + * @param delay + */ + setDelay(delay: number): ITween; + /** + * 设置tween的持续时间 + * @param duration + */ + setDuration(duration: number): ITween; + /** + * 设置这个tween使用的timeScale。 + * TimeScale将与Time.deltaTime/Time.unscaledDeltaTime相乘,从而得到tween实际使用的delta时间 + * @param timeScale + */ + setTimeScale(timeScale: number): ITween; + /** + * 设置tween使用Time.unscaledDeltaTime代替Time.deltaTime + */ + setIsTimeScaleIndependent(): ITween; + /** + * 设置当tween完成时应该调用的动作 + * @param completionHandler + */ + setCompletionHandler(completionHandler: (tween: ITween) => void): ITween; + /** + * 设置tween的循环类型。一个pingpong循环意味着从开始-结束-开始 + * @param loopType + * @param loops + * @param delayBetweenLoops + */ + setLoops(loopType: LoopType, loops: number, delayBetweenLoops: number): ITween; + /** + * 设置tween的起始位置 + * @param from + */ + setFrom(from: T): ITween; + /** + * 通过重置tween的from/to值和持续时间,为重复使用tween做准备。 + * @param from + * @param to + * @param duration + */ + prepareForReuse(from: T, to: T, duration: number): ITween; + /** + * 如果为true(默认值),tween将在使用后被回收。 + * 如果在TweenManager类中进行了配置,所有的Tween子类都有自己相关的自动缓存 + * @param shouldRecycleTween + */ + setRecycleTween(shouldRecycleTween: boolean): ITween; + /** + * 帮助程序,只是将tween的to值设置为相对于其当前值的+从使tween + */ + setIsRelative(): ITween; + /** + * 允许你通过tween.context.context来设置任何可检索的对象引用。 + * 这对于避免完成处理程序方法的闭包分配是很方便的。 + * 你也可以在TweenManager中搜索具有特定上下文的所有tweens + * @param context + */ + setContext(context): ITween; + /** + * 允许你添加一个tween,这个tween完成后会被运行。 + * 注意 nextTween 必须是一个 ITweenable! 同时注意,所有的ITweenT都是ITweenable + * @param nextTween + */ + setNextTween(nextTween: ITweenable): ITween; + } +} \ No newline at end of file diff --git a/source/src/Tween/Interfaces/ITweenControl.ts b/source/src/Tween/Interfaces/ITweenControl.ts new file mode 100644 index 00000000..8c08870b --- /dev/null +++ b/source/src/Tween/Interfaces/ITweenControl.ts @@ -0,0 +1,24 @@ +module es { + /** + * 更多具体的Tween播放控制在这里 + */ + export interface ITweenControl extends ITweenable { + /** + * 当使用匿名方法时,您可以在任何回调(如完成处理程序)中使用该属性来避免分配 + */ + context; + /** + * 将tween扭曲为elapsedTime,并将其限制在0和duration之间,无论tween对象是暂停、完成还是运行,都会立即更新 + * @param elapsedTime 所用时间 + */ + jumpToElapsedTime(elapsedTime: number); + /** + * 当从StartCoroutine调用时,它将直到tween完成 + */ + waitForCompletion(): any; + /** + * 获取tween的目标,如果TweenTargets不一定都是一个对象,则为null,它的唯一真正用途是让TweenManager按目标查找tweens的列表 + */ + getTargetObject(): any; + } +} \ No newline at end of file diff --git a/source/src/Tween/Interfaces/ITweenTarget.ts b/source/src/Tween/Interfaces/ITweenTarget.ts new file mode 100644 index 00000000..0495efeb --- /dev/null +++ b/source/src/Tween/Interfaces/ITweenTarget.ts @@ -0,0 +1,19 @@ +module es { + /** + * 任何想要被weened的对象都需要实现这个功能。 + * TweenManager内部喜欢做一个简单的对象来实现这个接口,并存储一个对被tweened对象的引用 + */ + export interface ITweenTarget { + /** + * 在你选择的对象上设置最终的tweened值 + * @param value + */ + setTweenedValue(value: T); + + getTweenedValue(): T; + /** + * 获取tween的目标,如果TweenTargets不一定都是一个对象,则为null,它的唯一真正用途是让TweenManager按目标查找tweens的列表 + */ + getTargetObject(): any; + } +} \ No newline at end of file diff --git a/source/src/Tween/Interfaces/ITweenable.ts b/source/src/Tween/Interfaces/ITweenable.ts new file mode 100644 index 00000000..b1f3eae1 --- /dev/null +++ b/source/src/Tween/Interfaces/ITweenable.ts @@ -0,0 +1,34 @@ +module es { + export interface ITweenable { + /** + * 就像内部的Update一样,每一帧都被TweenManager调用 + */ + tick(): boolean; + /** + * 当一个tween被移除时,由TweenManager调用。子 + * 类可以选择自己回收。子类应该首先在其实现中检查_shouldRecycleTween bool! + */ + recycleSelf(); + /** + * 检查是否有tween在运行 + */ + isRunning(): boolean; + /** + * 启动tween + */ + start(); + /** + * 暂停 + */ + pause(); + /** + * 暂停后恢复tween + */ + resume(); + /** + * 停止tween,并可选择将其完成 + * @param bringToCompletion + */ + stop(bringToCompletion: boolean); + } +} \ No newline at end of file diff --git a/source/src/Tween/PropertyTweens.ts b/source/src/Tween/PropertyTweens.ts new file mode 100644 index 00000000..ad27bb89 --- /dev/null +++ b/source/src/Tween/PropertyTweens.ts @@ -0,0 +1,44 @@ +module es { + /** + * 通用ITweenTarget用于所有属性tweens。 + */ + class PropertyTarget implements ITweenTarget { + protected _target; + protected _propertyName; + + constructor(target, propertyName: string) { + this._target = target; + this._propertyName = propertyName; + } + + public getTargetObject() { + return this._target; + } + + public setTweenedValue(value: T) { + this._target[this._propertyName] = value; + } + + public getTweenedValue(): T { + return this._target[this._propertyName]; + } + } + + export class PropertyTweens { + public static NumberPropertyTo(self, memberName: string, to: number, duration: number): ITween { + let tweenTarget = new PropertyTarget(self, memberName); + let tween = TweenManager.cacheNumberTweens ? Pool.obtain(NumberTween) : new NumberTween(); + tween.initialize(tweenTarget, to, duration); + + return tween; + } + + public static Vector2PropertyTo(self, memeberName: string, to: Vector2, duration: number): ITween { + let tweenTarget = new PropertyTarget(self, memeberName); + let tween = TweenManager.cacheVector2Tweens ? Pool.obtain(Vector2Tween) : new Vector2Tween(); + tween.initialize(tweenTarget, to, duration); + + return tween; + } + } +} \ No newline at end of file diff --git a/source/src/Tween/RenderableColorTween.ts b/source/src/Tween/RenderableColorTween.ts new file mode 100644 index 00000000..eea8271f --- /dev/null +++ b/source/src/Tween/RenderableColorTween.ts @@ -0,0 +1,38 @@ +/// +module es { + export class RenderableColorTween extends ColorTween implements ITweenTarget { + _renderable: RenderableComponent; + + setTweenedValue(value: Color) { + this._renderable.color = value; + } + + getTweenedValue(): Color { + return this._renderable.color; + } + + public getTargetObject() { + return this._renderable; + } + + public updateValue() { + this.setTweenedValue(Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } + + public setTarget(renderable: RenderableComponent) { + this._renderable = renderable; + } + + public recycleSelf() { + if (this._shouldRecycleTween) { + this._renderable = null; + this._target = null; + this._nextTween = null; + } + + if (this._shouldRecycleTween && TweenManager.cacheColorTweens) { + Pool.free(this); + } + } + } +} \ No newline at end of file diff --git a/source/src/Tween/TransformSpringTween.ts b/source/src/Tween/TransformSpringTween.ts new file mode 100644 index 00000000..f9d8765f --- /dev/null +++ b/source/src/Tween/TransformSpringTween.ts @@ -0,0 +1,100 @@ +module es { + export class TransformSpringTween extends AbstractTweenable { + public get targetType() { + return this._targetType; + } + + private _transform: Transform; + private _targetType: TransformTargetType; + private _targetValue: Vector2; + private _velocity: Vector2; + + // 阻尼比(dampingRatio)和角频率(angularFrequency)的配置是公开的,以便于在设计时进行调整 + + /** + * 值越低,阻尼越小,值越高,阻尼越大,导致弹簧度越小,应在0.01-1之间,以避免系统不稳定 + */ + public dampingRatio: number = 0.23; + /** + * 角频率为2pi(弧度/秒)意味着振荡在一秒钟内完成一个完整的周期,即1Hz.应小于35左右才能保持稳定角频率 + */ + public angularFrequency: number = 25; + + constructor(transform: Transform, targetType: TransformTargetType, targetValue: Vector2) { + super(); + this._transform = transform; + this._targetType = targetType; + this.setTargetValue(targetValue); + } + + /** + * 你可以在任何时候调用setTargetValue来重置目标值到一个新的Vector2。 + * 如果你没有调用start来添加spring tween,它会为你调用 + * @param targetValue + */ + public setTargetValue(targetValue: Vector2) { + this._velocity = Vector2.zero; + this._targetValue = targetValue; + + if (!this._isCurrentlyManagedByTweenManager) + this.start(); + } + + /** + * lambda应该是振荡幅度减少50%时的理想持续时间 + * @param lambda + */ + public updateDampingRatioWithHalfLife(lambda: number) { + this.dampingRatio = (-lambda / this.angularFrequency) * Math.log(0.5); + } + + public tick() { + if (!this._isPaused) + this.setTweenedValue(Lerps.fastSpring(this.getCurrentValueOfTweenedTargetType(), this._targetValue, this._velocity, + this.dampingRatio, this.angularFrequency)); + + return false; + } + + private setTweenedValue(value: Vector2) { + switch (this._targetType) { + case TransformTargetType.position: + this._transform.position = value; + break; + case TransformTargetType.localPosition: + this._transform.localPosition = value; + break; + case TransformTargetType.scale: + this._transform.scale = value; + break; + case TransformTargetType.localScale: + this._transform.localScale = value; + break; + case TransformTargetType.rotationDegrees: + this._transform.rotationDegrees = value.x; + case TransformTargetType.localRotationDegrees: + this._transform.localRotationDegrees = value.x; + break; + } + } + + private getCurrentValueOfTweenedTargetType() { + switch (this._targetType) { + case TransformTargetType.position: + return this._transform.position; + case TransformTargetType.localPosition: + return this._transform.localPosition; + case TransformTargetType.scale: + return this._transform.scale; + case TransformTargetType.localScale: + return this._transform.localScale; + case TransformTargetType.rotationDegrees: + return new Vector2(this._transform.rotationDegrees); + case TransformTargetType.localRotationDegrees: + return new Vector2(this._transform.localRotationDegrees, 0); + default: + return Vector2.zero; + } + } + } +} \ No newline at end of file diff --git a/source/src/Tween/TransformVector2Tween.ts b/source/src/Tween/TransformVector2Tween.ts new file mode 100644 index 00000000..8f367954 --- /dev/null +++ b/source/src/Tween/TransformVector2Tween.ts @@ -0,0 +1,90 @@ +/// +module es { + /** + * 对任何与Transform相关的属性tweens都是有用的枚举 + */ + export enum TransformTargetType { + position, + localPosition, + scale, + localScale, + rotationDegrees, + localRotationDegrees, + } + + /** + * 这是一个特殊的情况,因为Transform是迄今为止最被ween的对象。 + * 我们将Tween和ITweenTarget封装在一个单一的、可缓存的类中 + */ + export class TransformVector2Tween extends Vector2Tween implements ITweenTarget { + private _transform: Transform; + private _targetType: TransformTargetType; + + public setTweenedValue(value: Vector2) { + switch (this._targetType) { + case TransformTargetType.position: + this._transform.position = value; + break; + case TransformTargetType.localPosition: + this._transform.localPosition = value; + break; + case TransformTargetType.scale: + this._transform.scale = value; + break; + case TransformTargetType.localScale: + this._transform.localScale = value; + break; + case TransformTargetType.rotationDegrees: + this._transform.rotationDegrees = value.x; + case TransformTargetType.localRotationDegrees: + this._transform.localRotationDegrees = value.x; + break; + } + } + + public getTweenedValue(): Vector2 { + switch (this._targetType) { + case TransformTargetType.position: + return this._transform.position; + case TransformTargetType.localPosition: + return this._transform.localPosition; + case TransformTargetType.scale: + return this._transform.scale; + case TransformTargetType.localScale: + return this._transform.localScale; + case TransformTargetType.rotationDegrees: + return new Vector2(this._transform.rotationDegrees, this._transform.rotationDegrees); + case TransformTargetType.localRotationDegrees: + return new Vector2(this._transform.localRotationDegrees, 0); + } + } + + public getTargetObject() { + return this._transform; + } + + public setTargetAndType(transform: Transform, targetType: TransformTargetType) { + this._transform = transform; + this._targetType = targetType; + } + + protected updateValue() { + // 非相对角勒普的特殊情况,使他们采取尽可能短的旋转 + if ((this._targetType == TransformTargetType.rotationDegrees || + this._targetType == TransformTargetType.localRotationDegrees) && !this._isRelative) { + this.setTweenedValue(Lerps.easeAngle(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } else { + this.setTweenedValue(Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } + } + + public recycleSelf() { + if (this._shouldRecycleTween) { + this._target = null; + this._nextTween = null; + this._transform = null; + Pool.free(this); + } + } + } +} \ No newline at end of file diff --git a/source/src/Tween/Tween.ts b/source/src/Tween/Tween.ts new file mode 100644 index 00000000..f594d99c --- /dev/null +++ b/source/src/Tween/Tween.ts @@ -0,0 +1,319 @@ +module es { + export enum LoopType { + none, + restartFromBeginning, + pingpong + } + + export enum TweenState { + running, + paused, + complete + } + + export abstract class Tween implements ITweenable, ITween { + protected _target: ITweenTarget; + protected _isFromValueOverridden: boolean; + protected _fromValue: T; + protected _toValue: T; + protected _easeType: EaseType; + protected _shouldRecycleTween: boolean = true; + protected _isRelative: boolean; + protected _completionHandler: (tween: ITween) => void; + protected _loopCompleteHandler: (tween: ITween) => void; + protected _nextTween: ITweenable; + + protected _tweenState: TweenState = TweenState.complete; + private _isTimeScaleIndependent: boolean; + protected _delay: number; + protected _duration: number; + protected _timeScale: number = 1; + protected _elapsedTime: number; + + protected _loopType: LoopType; + protected _loops: number; + protected _delayBetweenLoops: number; + private _isRunningInReverse: boolean; + + public context: any; + + public setEaseType(easeType: EaseType): ITween { + this._easeType = easeType; + return this; + } + + public setDelay(delay: number): ITween { + this._delay = delay; + this._elapsedTime = -this._delay; + return this; + } + + public setDuration(duration: number): ITween { + this._duration = duration; + return this; + } + + public setTimeScale(timeSclae: number): ITween { + this._timeScale = timeSclae; + return this; + } + + public setIsTimeScaleIndependent(): ITween { + this._isTimeScaleIndependent = true; + return this; + } + + public setCompletionHandler(completeHandler: (tween: ITween) => void): ITween { + this._completionHandler = completeHandler; + return this; + } + + public setLoops(loopType: LoopType, loops: number = 1, delayBetweenLoops: number = 0): ITween { + this._loopType = loopType; + this._delayBetweenLoops = delayBetweenLoops; + + if (loops < 0) + loops = -1; + + if (loopType == LoopType.pingpong) + loops = loops * 2; + + this._loops = loops; + return this; + } + + public setLoopCompletionHanlder(loopCompleteHandler: (tween: ITween) => void): ITween { + this._loopCompleteHandler = loopCompleteHandler; + return this; + } + + public setFrom(from: T): ITween { + this._isFromValueOverridden = true; + this._fromValue = from; + return this; + } + + public prepareForReuse(from: T, to: T, duration: number): ITween { + this.initialize(this._target, to, duration); + return this; + } + + public setRecycleTween(shouldRecycleTween: boolean): ITween { + this._shouldRecycleTween = shouldRecycleTween; + return this; + } + + public abstract setIsRelative(): ITween; + + public setContext(context): ITween { + this.context = context; + return this; + } + + public setNextTween(nextTween: ITweenable): ITween { + this._nextTween = nextTween; + return this; + } + + public tick(): boolean { + if (this._tweenState == TweenState.paused) + return false; + + // 当我们进行循环时,我们会在0和持续时间之间限制数值 + let elapsedTimeExcess = 0; + if (!this._isRunningInReverse && this._elapsedTime >= this._duration) { + elapsedTimeExcess = this._elapsedTime - this._duration; + this._elapsedTime = this._duration; + this._tweenState = TweenState.complete; + } else if (this._isRunningInReverse && this._elapsedTime <= 0) { + elapsedTimeExcess = 0 - this._elapsedTime; + this._elapsedTime = 0; + this._tweenState = TweenState.complete; + } + + // 当我们延迟开始tween的时候,经过的时间会是负数,所以不要更新这个值。 + if (this._elapsedTime >= 0 && this._elapsedTime <= this._duration) { + this.updateValue(); + } + + + // 如果我们有一个loopType,并且我们是Complete(意味着我们达到了0或持续时间)处理循环。 + // handleLooping将采取任何多余的elapsedTime,并将其因子化,并在必要时调用udpateValue来保持tween的完美准确性 + if (this._loopType != LoopType.none && this._tweenState == TweenState.complete && this._loops != 0) { + this.handleLooping(elapsedTimeExcess); + } + + let deltaTime = this._isTimeScaleIndependent ? Time.unscaledDeltaTime : Time.deltaTime; + deltaTime *= this._timeScale; + + // 我们需要减去deltaTime + if (this._isRunningInReverse) + this._elapsedTime -= deltaTime; + else + this._elapsedTime += deltaTime; + + if (this._tweenState == TweenState.complete) { + this._completionHandler && this._completionHandler(this); + + // 如果我们有一个nextTween,把它添加到TweenManager中,这样它就可以开始运行了 + if (this._nextTween != null) { + this._nextTween.start(); + this._nextTween = null; + } + + return true; + } + + return false; + } + + public recycleSelf() { + if (this._shouldRecycleTween) { + this._target = null; + this._nextTween = null; + } + } + + public isRunning(): boolean { + return this._tweenState == TweenState.running; + } + + public start() { + if (!this._isFromValueOverridden) + this._fromValue = this._target.getTargetObject(); + + if (this._tweenState == TweenState.complete) { + this._tweenState = TweenState.running; + TweenManager.addTween(this); + } + } + + public pause() { + this._tweenState = TweenState.paused; + } + + public resume() { + this._tweenState = TweenState.running; + } + + public stop(bringToCompletion: boolean = false) { + this._tweenState = TweenState.complete; + + if (bringToCompletion) { + // 如果我们逆向运行,我们在0处结束,否则我们进入持续时间 + this._elapsedTime = this._isRunningInReverse ? 0 : this._duration; + this._loopType = LoopType.none; + this._loops = 0; + + // TweenManager将在下一个tick上进行删除处理 + } else { + TweenManager.removeTween(this); + } + } + + public jumpToElapsedTime(elapsedTime) { + this._elapsedTime = MathHelper.clamp(elapsedTime, 0, this._duration); + this.updateValue(); + } + + /** + * 反转当前的tween,如果是向前走,就会向后走,反之亦然 + */ + public reverseTween() { + this._isRunningInReverse = !this._isRunningInReverse; + } + + /** + * 当通过StartCoroutine调用时,这将一直持续到tween完成 + */ + public * waitForCompletion() { + while (this._tweenState != TweenState.complete) + yield null; + } + + public getTargetObject() { + return this._target.getTargetObject(); + } + + private resetState() { + this.context = null; + this._completionHandler = this._loopCompleteHandler = null; + this._isFromValueOverridden = false; + this._isTimeScaleIndependent = false; + this._tweenState = TweenState.complete; + + // TODO: 我认为在没有得到用户同意的情况下,我们绝对不应该从_shouldRecycleTween=false。需要研究和思考 + // this._shouldRecycleTween = true; + this._isRelative = false; + this._easeType = TweenManager.defaultEaseType; + + if (this._nextTween != null) { + this._nextTween.recycleSelf(); + this._nextTween = null; + } + + this._delay = 0; + this._duration = 0; + this._timeScale = 1; + this._elapsedTime = 0; + this._loopType = LoopType.none; + this._delayBetweenLoops = 0; + this._loops = 0; + this._isRunningInReverse = false; + } + + /** + * 将所有状态重置为默认值,并根据传入的参数设置初始状态。 + * 这个方法作为一个切入点,这样Tween子类就可以调用它,这样tweens就可以被回收。 + * 当回收时,构造函数不会再被调用,所以这个方法封装了构造函数要做的事情 + * @param target + * @param to + * @param duration + */ + public initialize(target: ITweenTarget, to: T, duration: number) { + // 重置状态,以防我们被回收 + this.resetState(); + + this._target = target; + this._toValue = to; + this._duration = duration; + } + + /** + * 处理循环逻辑 + * @param elapsedTimeExcess + */ + private handleLooping(elapsedTimeExcess: number) { + this._loops--; + if (this._loopType == LoopType.pingpong) { + this.reverseTween(); + } + + if (this._loopType == LoopType.restartFromBeginning || this._loops % 2 == 0) { + this._loopCompleteHandler && this._completionHandler(this); + } + + // 如果我们还有循环要处理,就把我们的状态重置为Running,这样我们就可以继续处理它们了 + if (this._loops != 0) { + this._tweenState = TweenState.running; + + // 现在,我们需要设置我们的经过时间,并考虑到我们的elapsedTimeExcess + if (this._loopType == LoopType.restartFromBeginning) { + this._elapsedTime = elapsedTimeExcess - this._delayBetweenLoops; + } else { + if (this._isRunningInReverse) + this._elapsedTime += this._delayBetweenLoops - elapsedTimeExcess; + else + this._elapsedTime = elapsedTimeExcess - this._delayBetweenLoops; + } + + // 如果我们有一个elapsedTimeExcess,并且没有delayBetweenLoops,则更新该值 + if (this._delayBetweenLoops == 0 && elapsedTimeExcess > 0) { + this.updateValue(); + } + } + } + + protected abstract updateValue(); + } +} \ No newline at end of file diff --git a/source/src/Tween/TweenManager.ts b/source/src/Tween/TweenManager.ts new file mode 100644 index 00000000..653b2d00 --- /dev/null +++ b/source/src/Tween/TweenManager.ts @@ -0,0 +1,152 @@ +/// +/// +module es { + export class TweenManager extends GlobalManager { + public static defaultEaseType: EaseType = EaseType.quartIn; + + /** + * 如果为真,当加载新关卡时,活动的tween列表将被清除 + */ + public static removeAllTweensOnLevelLoad: boolean = false; + + /** + * 这里支持各种类型的自动缓存。请 + * 注意,只有在使用扩展方法启动tweens时,或者在做自定义tweens时从缓存中获取tween时,缓存才会起作用。 + * 关于如何获取缓存的tween,请参见扩展方法的实现 + */ + public static cacheNumberTweens = true; + public static cacheVector2Tweens = true; + public static cacheColorTweens = true; + public static cacheRectTweens = false; + + /** + * 当前所有活跃用户的内部列表 + */ + private _activeTweens: ITweenable[] = []; + private _tempTweens: ITweenable[] = []; + /** + * 标志表示tween更新循环正在运行 + */ + private _isUpdating: boolean; + /** + * 便于暴露一个静态的API以方便访问 + */ + private static _instance: TweenManager; + + constructor() { + super(); + TweenManager._instance = this; + } + + public update() { + this._isUpdating = true; + + // 反向循环,这样我们就可以把完成的weens删除了 + for (let i = this._activeTweens.length - 1; i >= 0; --i) { + let tween = this._activeTweens[i]; + if (tween.tick()) + this._tempTweens.push(tween); + } + + this._isUpdating = false; + + for (let i = 0; i < this._tempTweens.length; i++) { + this._tempTweens[i].recycleSelf(); + new List(this._activeTweens).remove(this._tempTweens[i]); + } + + this._tempTweens.length = 0; + } + + /** + * 将一个tween添加到活动tweens列表中 + * @param tween + */ + public static addTween(tween: ITweenable) { + TweenManager._instance._activeTweens.push(tween); + } + + /** + * 从当前的tweens列表中删除一个tween + * @param tween + */ + public static removeTween(tween: ITweenable) { + if (TweenManager._instance._isUpdating) { + TweenManager._instance._tempTweens.push(tween); + } else { + tween.recycleSelf(); + new List(TweenManager._instance._activeTweens).remove(tween); + } + } + + /** + * 停止所有的tween并选择地把他们全部完成 + * @param bringToCompletion + */ + public static stopAllTweens(bringToCompletion: boolean = false) { + for (let i = TweenManager._instance._activeTweens.length - 1; i >= 0; --i) + TweenManager._instance._activeTweens[i].stop(bringToCompletion); + } + + /** + * 返回具有特定上下文的所有tweens。 + * Tweens以ITweenable的形式返回,因为这就是TweenManager所知道的所有内容 + * @param context + */ + public static allTweensWithContext(context): ITweenable[] { + let foundTweens = []; + for (let i = 0; i < TweenManager._instance._activeTweens.length; i++) { + if ((TweenManager._instance._activeTweens[i] as ITweenControl).context == context) + foundTweens.push(TweenManager._instance._activeTweens[i]); + } + + return foundTweens; + } + + /** + * 停止所有给定上下文的tweens + * @param context + * @param bringToCompletion + */ + public static stopAllTweensWithContext(context, bringToCompletion: boolean = false) { + for (let i = TweenManager._instance._activeTweens.length - 1; i >= 0; --i) { + if ((TweenManager._instance._activeTweens[i] as ITweenControl).context == context) + TweenManager._instance._activeTweens[i].stop(bringToCompletion); + } + } + + /** + * 返回具有特定目标的所有tweens。 + * Tweens以ITweenControl的形式返回,因为TweenManager只知道这些 + * @param target + */ + public static allTweenWithTarget(target): ITweenable[] { + let foundTweens = []; + + for (let i = 0; i < TweenManager._instance._activeTweens.length; i++) { + if (TweenManager._instance._activeTweens[i]) { + let tweenControl = TweenManager._instance._activeTweens[i] as ITweenControl; + if (tweenControl.getTargetObject() == target) + foundTweens.push(TweenManager._instance._activeTweens[i]); + } + } + + return foundTweens; + } + + /** + * 停止所有具有TweenManager知道的特定目标的tweens + * @param target + * @param bringToCompletion + */ + public static stopAllTweensWithTarget(target, bringToCompletion: boolean = false) { + for (let i = TweenManager._instance._activeTweens.length - 1; i >= 0; --i) { + if (TweenManager._instance._activeTweens[i]) { + let tweenControl = TweenManager._instance._activeTweens[i] as ITweenControl; + if (tweenControl.getTargetObject() == target) + tweenControl.stop(bringToCompletion); + } + } + } + } +} \ No newline at end of file diff --git a/source/src/Tween/Tweens.ts b/source/src/Tween/Tweens.ts new file mode 100644 index 00000000..aac1eb75 --- /dev/null +++ b/source/src/Tween/Tweens.ts @@ -0,0 +1,117 @@ +/// +module es { + export class NumberTween extends Tween { + public static create(): NumberTween { + return TweenManager.cacheNumberTweens ? Pool.obtain(NumberTween) : new NumberTween(); + } + + constructor(target?: ITweenTarget, to?: number, duration?: number) { + super(); + this.initialize(target, to, duration); + } + + public setIsRelative(): ITween { + this._isRelative = true; + this._toValue += this._fromValue; + return this; + } + + protected updateValue() { + this._target.setTweenedValue(Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } + + public recycleSelf() { + super.recycleSelf(); + + if (this._shouldRecycleTween && TweenManager.cacheNumberTweens) + Pool.free(this); + } + } + + export class Vector2Tween extends Tween { + public static create(): Vector2Tween { + return TweenManager.cacheVector2Tweens ? Pool.obtain(Vector2Tween) : new Vector2Tween(); + } + + constructor(target?: ITweenTarget, to?: Vector2, duration?: number) { + super(); + this.initialize(target, to, duration); + } + + public setIsRelative(): ITween { + this._isRelative = true; + this._toValue.add(this._fromValue); + return this; + } + + protected updateValue() { + this._target.setTweenedValue(Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } + + public recycleSelf() { + super.recycleSelf(); + + if (this._shouldRecycleTween && TweenManager.cacheVector2Tweens) + Pool.free(this); + } + } + + export class RectangleTween extends Tween { + public static create(): RectangleTween { + return TweenManager.cacheRectTweens ? Pool.obtain(RectangleTween) : new RectangleTween(); + } + + constructor(target?: ITweenTarget, to?: Rectangle, duration?: number) { + super(); + this.initialize(target, to, duration); + } + + public setIsRelative(): ITween { + this._isRelative = true; + this._toValue = new Rectangle( + this._toValue.x + this._fromValue.x, + this._toValue.y + this._fromValue.y, + this._toValue.width + this._fromValue.width, + this._toValue.height + this._fromValue.height); + + return this; + } + + protected updateValue() { + this._target.setTweenedValue(Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration)); + } + + public recycleSelf() { + super.recycleSelf(); + + if (this._shouldRecycleTween && TweenManager.cacheRectTweens) + Pool.free(this); + } + } + + export class ColorTween extends Tween { + public static create() : ColorTween { + return TweenManager.cacheColorTweens ? Pool.obtain(ColorTween) : new ColorTween(); + + } + + constructor(target?: ITweenTarget, to?: Color, duration?: number) { + super(); + + this.initialize(target, to, duration); + } + + public setIsRelative() { + this._isRelative = true; + this._toValue.r += this._fromValue.r; + this._toValue.g += this._fromValue.g; + this._toValue.b += this._fromValue.b; + this._toValue.a += this._fromValue.a; + return this; + } + + protected updateValue() { + this._target.setTweenedValue(Lerps.ease(this._easeType, this._fromValue as any, this._toValue as any, this._elapsedTime, this._duration)); + } + } +} \ No newline at end of file diff --git a/source/src/Utils/AnimCurve.ts b/source/src/Utils/AnimCurve.ts new file mode 100644 index 00000000..47fcf5fc --- /dev/null +++ b/source/src/Utils/AnimCurve.ts @@ -0,0 +1,48 @@ +module es { + export interface IAnimFrame { + t: number; + value: number; + } + + export class AnimCurve { + public get points(): IAnimFrame[] { + return this._points; + } + + public constructor(points: IAnimFrame[]) { + if (points.length < 2) { + throw new Error('curve length must be >= 2'); + } + points.sort((a: IAnimFrame, b: IAnimFrame) => { + return a.t - b.t; + }); + if (points[0].t !== 0) { + throw new Error('curve must start with 0'); + } + if (points[points.length - 1].t !== 1) { + throw new Error('curve must end with 1'); + } + this._points = points; + } + + public lerp(t: number): number { + for (let i = 1; i < this._points.length; i++) { + if (t <= this._points[i].t) { + const m = MathHelper.map01( + t, + this._points[i - 1].t, + this._points[i].t + ); + return MathHelper.lerp( + this._points[i - 1].value, + this._points[i].value, + m + ); + } + } + throw new Error('should never be here'); + } + + public _points: IAnimFrame[]; + } +} \ No newline at end of file diff --git a/source/src/Utils/Extensions/RectangleExt.ts b/source/src/Utils/Extensions/RectangleExt.ts index 64aaf7bc..a7533a2e 100644 --- a/source/src/Utils/Extensions/RectangleExt.ts +++ b/source/src/Utils/Extensions/RectangleExt.ts @@ -250,7 +250,7 @@ module es { */ public static getClosestPointOnRectangleToPoint(rect: Rectangle, point: Vector2) { // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 - let res = new Vector2(); + let res = es.Vector2.zero; res.x = MathHelper.clamp(point.x, rect.left, rect.right) res.y = MathHelper.clamp(point.y, rect.top, rect.bottom); @@ -264,9 +264,9 @@ module es { */ public static getClosestPointOnRectangleBorderToPoint(rect: Rectangle, point: Vector2) { // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 - let res = new Vector2(); - res.x = MathHelper.clamp(point.x, rect.left, rect.right) - res.y = MathHelper.clamp(point.y, rect.top, rect.bottom); + let res = es.Vector2.zero; + res.x = MathHelper.clamp(Math.trunc(point.x), rect.left, rect.right) + res.y = MathHelper.clamp(Math.trunc(point.y), rect.top, rect.bottom); // 如果点在矩形内,我们需要将res推到边框,因为它将在矩形内 if (rect.contains(res.x, res.y)) { @@ -327,7 +327,54 @@ module es { maxY = pt.y; } - return this.fromMinMaxVector(new Vector2(minX, minY), new Vector2(maxX, maxY)); + return this.fromMinMaxVector(new Vector2(Math.trunc(minX), Math.trunc(minY)), new Vector2(Math.trunc(maxX), Math.trunc(maxY))); + } + + public static calculateBounds(rect: Rectangle, parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, + rotation: number, width: number, height: number) { + if (rotation == 0) { + rect.x = Math.trunc(parentPosition.x + position.x - origin.x * scale.x); + rect.y = Math.trunc(parentPosition.y + position.y - origin.y * scale.y); + rect.width = Math.trunc(width * scale.x); + rect.height = Math.trunc(height * scale.y); + } else { + // 我们需要找到我们的绝对最小/最大值,并据此创建边界 + let worldPosX = parentPosition.x + position.x; + let worldPosY = parentPosition.y + position.y; + + let tempMat: Matrix2D; + + // 考虑到原点,将参考点设置为世界参考 + let transformMatrix = new Matrix2D(); + Matrix2D.createTranslation(-worldPosX - origin.x, -worldPosY - origin.y, transformMatrix); + Matrix2D.createScale(scale.x, scale.y, tempMat); + transformMatrix = transformMatrix.multiply(tempMat); + Matrix2D.createRotation(rotation, tempMat); + transformMatrix =transformMatrix.multiply(tempMat); + Matrix2D.createTranslation(worldPosX, worldPosY, tempMat); + transformMatrix = transformMatrix.multiply(tempMat); + + // TODO: 我们可以把世界变换留在矩阵中,避免在世界空间中得到所有的四个角 + let topLeft = new Vector2(worldPosX, worldPosY); + let topRight = new Vector2(worldPosX + width, worldPosY); + let bottomLeft = new Vector2(worldPosX, worldPosY + height); + let bottomRight = new Vector2(worldPosX + width, worldPosY + height); + + Vector2Ext.transformR(topLeft, transformMatrix, topLeft); + Vector2Ext.transformR(topRight, transformMatrix, topRight); + Vector2Ext.transformR(bottomLeft, transformMatrix, bottomLeft); + Vector2Ext.transformR(bottomRight, transformMatrix, bottomRight); + + // 找出最小值和最大值,这样我们就可以计算出我们的边界框。 + let minX = Math.trunc(Math.min(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + let maxX = Math.trunc(Math.max(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x)); + let minY = Math.trunc(Math.min(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); + let maxY = Math.trunc(Math.max(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y)); + + rect.location = new Vector2(minX, minY); + rect.width = Math.trunc(maxX - minX); + rect.height = Math.trunc(maxY - minY); + } } /** @@ -336,14 +383,14 @@ module es { * @param scale */ public static scale(rect: Rectangle, scale: Vector2) { - rect.x = rect.x * scale.x; - rect.y = rect.y * scale.y; - rect.width = rect.width * scale.x; - rect.height = rect.height * scale.y; + rect.x = Math.trunc(rect.x * scale.x); + rect.y = Math.trunc(rect.y * scale.y); + rect.width = Math.trunc(rect.width * scale.x); + rect.height = Math.trunc(rect.height * scale.y); } public static translate(rect: Rectangle, vec: Vector2) { - rect.location.add(vec); + rect.location.addEqual(vec); } } } diff --git a/source/src/Utils/Extensions/Vector2Ext.ts b/source/src/Utils/Extensions/Vector2Ext.ts index 658487a3..7ac94d1a 100644 --- a/source/src/Utils/Extensions/Vector2Ext.ts +++ b/source/src/Utils/Extensions/Vector2Ext.ts @@ -7,7 +7,7 @@ module es { * @param c */ public static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2) { - return this.cross(Vector2.subtract(center, a), Vector2.subtract(c, center)) < 0; + return this.cross(center.sub(a), c.sub(center)) < 0; } public static halfVector(): Vector2 { @@ -48,7 +48,7 @@ module es { public static angle(from: Vector2, to: Vector2) { this.normalize(from); this.normalize(to); - return Math.acos(MathHelper.clamp(Vector2.dot(from, to), -1, 1)) * MathHelper.Rad2Deg; + return Math.acos(MathHelper.clamp(from.dot(to), -1, 1)) * MathHelper.Rad2Deg; } /** @@ -58,8 +58,8 @@ module es { * @param right */ public static angleBetween(self: Vector2, left: Vector2, right: Vector2) { - let one = Vector2.subtract(left, self); - let two = Vector2.subtract(right, self); + const one = left.sub(self); + const two = right.sub(self); return this.angle(one, two); } @@ -71,7 +71,7 @@ module es { * @param d * @param intersection */ - public static getRayIntersection(a: Vector2, b: Vector2, c: Vector2, d: Vector2, intersection: Vector2 = new Vector2()) { + public static getRayIntersection(a: Vector2, b: Vector2, c: Vector2, d: Vector2, intersection: Vector2 = es.Vector2.zero) { let dy1 = b.y - a.y; let dx1 = b.x - a.x; let dy2 = d.y - c.y; @@ -99,7 +99,7 @@ module es { public static normalize(vec: Vector2) { let magnitude = Math.sqrt((vec.x * vec.x) + (vec.y * vec.y)); if (magnitude > MathHelper.Epsilon) { - vec.divide(new Vector2(magnitude)); + vec.divideScaler(magnitude); } else { vec.x = vec.y = 0; } @@ -131,7 +131,7 @@ module es { * @param matrix * @param result */ - public static transformR(position: Vector2, matrix: Matrix2D, result: Vector2 = new Vector2()) { + public static transformR(position: Vector2, matrix: Matrix2D, result: Vector2 = es.Vector2.zero) { let x = (position.x * matrix.m11) + (position.y * matrix.m21) + matrix.m31; let y = (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32; result.x = x; diff --git a/source/src/Utils/Observable.ts b/source/src/Utils/Observable.ts new file mode 100644 index 00000000..47ae8701 --- /dev/null +++ b/source/src/Utils/Observable.ts @@ -0,0 +1,184 @@ +module es { + export interface IListener { + caller: object; + callback: Function; + } + + export interface IObservable { + addListener(caller: object, callback: Function); + removeListener(caller: object, callback: Function); + clearListener(); + clearListenerWithCaller(caller: object); + } + + export class Observable implements IObservable { + public constructor() { + this._listeners = []; + } + + public addListener(caller: object, callback: Function) { + if ( + this._listeners.findIndex( + listener => + listener.callback === callback && listener.caller === caller + ) === -1 + ) { + this._listeners.push({ caller, callback }); + } + } + + public removeListener(caller: object, callback: Function) { + const index = this._listeners.findIndex( + listener => listener.callback === callback && listener.caller === caller + ); + if (index >= 0) { + this._listeners.splice(index, 1); + } + } + + public clearListener() { + this._listeners = []; + } + + public clearListenerWithCaller(caller: object) { + for (let i = this._listeners.length - 1; i >= 0; i--) { + const listener = this._listeners[i]; + if (listener.caller === caller) { + this._listeners.splice(i, 1); + } + } + } + + public notify(...args) { + for (let i = this._listeners.length - 1; i >= 0; i--) { + const listener = this._listeners[i]; + if (listener.caller) { + listener.callback.call(listener.caller, ...args); + } else { + listener.callback(...args); + } + } + } + + private _listeners: IListener[]; + } + + export class ObservableT extends Observable { + public addListener(caller: object, callback: (arg: T) => void) { + super.addListener(caller, callback); + } + + public removeListener(caller: object, callback: (arg: T) => void) { + super.removeListener(caller, callback); + } + + public notify(arg: T) { + super.notify(arg); + } + } + + export class ObservableTT extends Observable { + public addListener(caller: object, callback: (arg1: T, arg2: R) => void) { + super.addListener(caller, callback); + } + + public removeListener(caller: object, callback: (arg: T, arg2: R) => void) { + super.removeListener(caller, callback); + } + + public notify(arg1: T, arg2: R) { + super.notify(arg1, arg2); + } + } + + export class Command implements IObservable { + public constructor(caller: object, action: Function) { + this.bindAction(caller, action); + this._onExec = new Observable(); + } + + public bindAction(caller: object, action: Function) { + this._caller = caller; + this._action = action; + } + + public dispatch(...args: any[]) { + if (this._action) { + if (this._caller) { + this._action.call(this._caller, ...args); + } else { + this._action(...args); + } + this._onExec.notify(); + } else { + console.warn('command not bind with an action'); + } + } + + public addListener(caller: object, callback: Function) { + this._onExec.addListener(caller, callback); + } + + public removeListener(caller: object, callback: Function) { + this._onExec.removeListener(caller, callback); + } + + public clearListener() { + this._onExec.clearListener(); + } + + public clearListenerWithCaller(caller: object) { + this._onExec.clearListenerWithCaller(caller); + } + + private _onExec: Observable; + private _caller: object; + private _action: Function; + } + + export class ValueChangeCommand implements IObservable { + public constructor(value: T) { + this._onValueChange = new Observable(); + this._value = value; + } + + public get onValueChange() { + return this._onValueChange; + } + + public get value() { + return this._value; + } + + public set value(newValue: T) { + this._value = newValue; + } + + public dispatch(value: T) { + if (value !== this._value) { + const oldValue = this._value; + this._value = value; + this._onValueChange.notify(this._value, oldValue); + } + } + + public addListener(caller: object, callback: Function) { + this._onValueChange.addListener(caller, callback); + } + + public removeListener(caller: object, callback: Function) { + this._onValueChange.removeListener(caller, callback); + } + + public clearListener() { + this._onValueChange.clearListener(); + } + + public clearListenerWithCaller(caller: object) { + this._onValueChange.clearListenerWithCaller(caller); + } + + private _onValueChange: Observable; + private _value: T; + } +} \ No newline at end of file diff --git a/source/src/Utils/PolygonLight/VisibilityComputer.ts b/source/src/Utils/PolygonLight/VisibilityComputer.ts index dd00ae62..c174113e 100644 --- a/source/src/Utils/PolygonLight/VisibilityComputer.ts +++ b/source/src/Utils/PolygonLight/VisibilityComputer.ts @@ -63,14 +63,14 @@ module es { * @param radius */ public addCircleOccluder(position: Vector2, radius: number){ - let dirToCircle = Vector2.subtract(position, this._origin); + let dirToCircle = position.sub(this._origin); let angle = Math.atan2(dirToCircle.y, dirToCircle.x); let stepSize = Math.PI / this.lineCountForCircleApproximation; let startAngle = angle + MathHelper.PiOver2; - let lastPt = MathHelper.angleToVector(startAngle, radius).add(position); + let lastPt = MathHelper.angleToVector(startAngle, radius).addEqual(position); for (let i = 1; i < this.lineCountForCircleApproximation; i ++) { - let nextPt = MathHelper.angleToVector(startAngle + i * stepSize, radius).add(position); + let nextPt = MathHelper.angleToVector(startAngle + i * stepSize, radius).addEqual(position); this.addLineOccluder(lastPt, nextPt); lastPt = nextPt; } diff --git a/source/src/Utils/Triangulator.ts b/source/src/Utils/Triangulator.ts index 6994d05d..65e489e0 100644 --- a/source/src/Utils/Triangulator.ts +++ b/source/src/Utils/Triangulator.ts @@ -13,15 +13,15 @@ module es { public static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean { // 如果点在AB的右边,那么外边的三角形是 - if (Vector2Ext.cross(Vector2.subtract(point, a), Vector2.subtract(b, a)) < 0) + if (Vector2Ext.cross(point.sub(a), b.sub(a)) < 0) return false; // 如果点在BC的右边,则在三角形的外侧 - if (Vector2Ext.cross(Vector2.subtract(point, b), Vector2.subtract(c, b)) < 0) + if (Vector2Ext.cross(point.sub(b), c.sub(b)) < 0) return false; // 如果点在ca的右边,则在三角形的外面 - if (Vector2Ext.cross(Vector2.subtract(point, c), Vector2.subtract(a, c)) < 0) + if (Vector2Ext.cross(point.sub(c), a.sub(c)) < 0) return false; // 点在三角形上