Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c293979a4 | |||
| bc995e36c2 | |||
| 18279d16cd | |||
| 59b2f150f3 | |||
| e6d6c4199f | |||
| ccf8c4e107 | |||
| 9e6e5eccc8 | |||
| 447ea4efe4 | |||
| 7f5b78f340 | |||
| 4ac8bafa87 | |||
| 25ce4c9cf9 | |||
| 2b81a0b06b | |||
| e68f6bd1be | |||
| 06c1aeb97a | |||
| ced176706b | |||
| 8b21edc65f | |||
| 75301f7776 | |||
| dba43b9773 | |||
| 5186bc0187 | |||
| c3120d791f | |||
| 16892eb7af | |||
| 7f8f1cf0d0 | |||
| 246e9a9511 | |||
| da5a1a0c79 | |||
| 6fa56dd572 | |||
| ad68f0e1a0 | |||
| 53ded30e0b | |||
| 2eec9a82f9 | |||
| 74cbb4c9fd | |||
| fa4c3c5d0b | |||
| f62f449d99 | |||
| 94818d5784 | |||
| 5f7c13c8cd | |||
| 538677575d | |||
| 1816b16924 |
@@ -0,0 +1,34 @@
|
|||||||
|
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||||
|
|
||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [8.x]
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: cmd
|
||||||
|
working-directory: ./source
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: npm install
|
||||||
|
- run: npm run build --if-present
|
||||||
|
- run: gulp build
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
// 使用 IntelliSense 了解相关属性。
|
||||||
|
// 悬停以查看现有属性的描述。
|
||||||
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "pwa-chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome against localhost",
|
||||||
|
"url": "http://localhost:8080",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,11 +1,43 @@
|
|||||||
# egret-framework
|
# egret-framework
|
||||||
用于egret 包含众多高性能方法以供使用
|
用于egret的一套框架 包含众多游戏中可能用到的系统
|
||||||
(正在施工中)
|
|
||||||
|
## 当前版本功能
|
||||||
|
|
||||||
|
- [x] 简易ECS框架
|
||||||
|
- [x] A*寻路(AStar)
|
||||||
|
- [x] 常用碰撞检测
|
||||||
|
- [x] 数学库
|
||||||
|
- [x] 简易矩阵类
|
||||||
|
- [x] 简易2d 向量类
|
||||||
|
- [x] 掩码实用类
|
||||||
|
- [x] BreadthFirst 寻路算法
|
||||||
|
- [x] Dijkstra 寻路算法
|
||||||
|
- [x] 事件处理器
|
||||||
|
|
||||||
## 计划列表
|
## 计划列表
|
||||||
|
|
||||||
- [ ] 包含一套适配egret的ecs框架
|
- [ ] ECS
|
||||||
|
- [ ] 组件列表
|
||||||
|
- [x] 碰撞组件
|
||||||
|
- [x] 移动组件
|
||||||
|
- [ ] 刚体组件
|
||||||
|
- [ ] 点光源/灯光组件
|
||||||
|
- [ ] 阴影组件
|
||||||
|
- [ ] 轨迹组件
|
||||||
|
- [ ] 滚动组件
|
||||||
|
- [ ] 网格弹簧组件
|
||||||
|
- [ ] 相机震动组件
|
||||||
|
- [ ] 霓虹灯组件
|
||||||
|
- [ ] 跟随相机组件
|
||||||
|
- [ ] 系统列表
|
||||||
|
- [ ] 被动系统
|
||||||
|
- [ ] 协调系统
|
||||||
|
- [ ] 数学库
|
||||||
|
- [ ] 贝塞尔曲线
|
||||||
|
- [ ] 快速随机数类
|
||||||
|
- [ ] 浮点助手类
|
||||||
|
- [ ] 高性能数组
|
||||||
|
|
||||||
- [ ] 高性能物理引擎
|
## 作者其他库(egret)
|
||||||
|
|
||||||
> 尽可能的简单易用
|
- [x] [行为树/实用AI 系统](https://github.com/esengine/egret-BehaviourTree-ai)
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
{
|
{
|
||||||
"name": "framework",
|
"name": "framework",
|
||||||
"path": "./libs/framework"
|
"path": "./libs/framework"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "long",
|
||||||
|
"path": "./libs/long"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Vendored
+428
-7
@@ -72,33 +72,102 @@ declare class PriorityQueue<T extends PriorityQueueNode> {
|
|||||||
private swap;
|
private swap;
|
||||||
private hasHigherPriority;
|
private hasHigherPriority;
|
||||||
}
|
}
|
||||||
|
declare class BreadthFirstPathfinder {
|
||||||
|
static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
}
|
||||||
|
interface IUnweightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||||
|
edges: Map<T, T[]>;
|
||||||
|
addEdgesForNode(node: T, edges: T[]): this;
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
constructor(x?: number, y?: number);
|
||||||
|
}
|
||||||
|
declare class UnweightedGridGraph implements IUnweightedGraph<Point> {
|
||||||
|
private static readonly CARDINAL_DIRS;
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
private _width;
|
||||||
|
private _hegiht;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
}
|
||||||
|
interface IWeightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
}
|
||||||
|
declare class WeightedGridGraph implements IWeightedGraph<Point> {
|
||||||
|
static readonly CARDINAL_DIRS: Point[];
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
weightedNodes: Point[];
|
||||||
|
defaultWeight: number;
|
||||||
|
weightedNodeWeight: number;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
cost(from: Point, to: Point): number;
|
||||||
|
}
|
||||||
|
declare class WeightedNode<T> extends PriorityQueueNode {
|
||||||
|
data: T;
|
||||||
|
constructor(data: T);
|
||||||
|
}
|
||||||
|
declare class WeightedPathfinder {
|
||||||
|
static search<T>(graph: IWeightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
private static getKey;
|
||||||
|
static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[];
|
||||||
|
}
|
||||||
|
declare class DebugDefaults {
|
||||||
|
static verletParticle: number;
|
||||||
|
static verletConstraintEdge: number;
|
||||||
|
}
|
||||||
declare abstract class Component {
|
declare abstract class Component {
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
displayRender: egret.DisplayObject;
|
|
||||||
private _enabled;
|
private _enabled;
|
||||||
updateInterval: number;
|
updateInterval: number;
|
||||||
readonly transform: Transform;
|
readonly transform: Transform;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
setEnabled(isEnabled: boolean): this;
|
setEnabled(isEnabled: boolean): this;
|
||||||
abstract initialize(): any;
|
initialize(): void;
|
||||||
onAddedToEntity(): void;
|
onAddedToEntity(): void;
|
||||||
onRemovedFromEntity(): void;
|
onRemovedFromEntity(): void;
|
||||||
onEnabled(): void;
|
onEnabled(): void;
|
||||||
onDisabled(): void;
|
onDisabled(): void;
|
||||||
onEntityTransformChanged(comp: ComponentTransform): void;
|
onEntityTransformChanged(comp: ComponentTransform): void;
|
||||||
update(): void;
|
update(): void;
|
||||||
bind(displayRender: egret.DisplayObject): this;
|
debugRender(): void;
|
||||||
registerComponent(): void;
|
registerComponent(): void;
|
||||||
deregisterComponent(): void;
|
deregisterComponent(): void;
|
||||||
}
|
}
|
||||||
declare class Entity {
|
declare class Entity {
|
||||||
|
private static _idGenerator;
|
||||||
name: string;
|
name: string;
|
||||||
|
readonly id: number;
|
||||||
scene: Scene;
|
scene: Scene;
|
||||||
readonly transform: Transform;
|
readonly transform: Transform;
|
||||||
readonly components: ComponentList;
|
readonly components: ComponentList;
|
||||||
private _updateOrder;
|
private _updateOrder;
|
||||||
private _enabled;
|
private _enabled;
|
||||||
private _isDestoryed;
|
private _isDestoryed;
|
||||||
|
private _tag;
|
||||||
componentBits: BitSet;
|
componentBits: BitSet;
|
||||||
parent: Transform;
|
parent: Transform;
|
||||||
position: Vector2;
|
position: Vector2;
|
||||||
@@ -115,15 +184,18 @@ declare class Entity {
|
|||||||
readonly isDestoryed: boolean;
|
readonly isDestoryed: boolean;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
setEnabled(isEnabled: boolean): this;
|
setEnabled(isEnabled: boolean): this;
|
||||||
|
tag: number;
|
||||||
constructor(name: string);
|
constructor(name: string);
|
||||||
updateOrder: number;
|
updateOrder: number;
|
||||||
setUpdateOrder(updateOrder: number): this;
|
setUpdateOrder(updateOrder: number): this;
|
||||||
|
setTag(tag: number): Entity;
|
||||||
attachToScene(newScene: Scene): void;
|
attachToScene(newScene: Scene): void;
|
||||||
detachFromScene(): void;
|
detachFromScene(): void;
|
||||||
addComponent<T extends Component>(component: T): T;
|
addComponent<T extends Component>(component: T): T;
|
||||||
hasComponent<T extends Component>(type: any): boolean;
|
hasComponent<T extends Component>(type: any): boolean;
|
||||||
getOrCreateComponent<T extends Component>(type: T): T;
|
getOrCreateComponent<T extends Component>(type: T): T;
|
||||||
getComponent<T extends Component>(type: any): T;
|
getComponent<T extends Component>(type: any): T;
|
||||||
|
getComponents(typeName: string | any, componentList?: any): any;
|
||||||
removeComponentForType<T extends Component>(type: any): boolean;
|
removeComponentForType<T extends Component>(type: any): boolean;
|
||||||
removeComponent(component: Component): void;
|
removeComponent(component: Component): void;
|
||||||
removeAllComponents(): void;
|
removeAllComponents(): void;
|
||||||
@@ -235,11 +307,16 @@ declare class Camera extends Component {
|
|||||||
private _minimumZoom;
|
private _minimumZoom;
|
||||||
private _maximumZoom;
|
private _maximumZoom;
|
||||||
private _areMatrixesDirty;
|
private _areMatrixesDirty;
|
||||||
|
private _inset;
|
||||||
|
private _bounds;
|
||||||
|
private _areBoundsDirty;
|
||||||
|
readonly bounds: Rectangle;
|
||||||
zoom: number;
|
zoom: number;
|
||||||
minimumZoom: number;
|
minimumZoom: number;
|
||||||
maximumZoom: number;
|
maximumZoom: number;
|
||||||
origin: Vector2;
|
origin: Vector2;
|
||||||
readonly transformMatrix: Matrix2D;
|
readonly transformMatrix: Matrix2D;
|
||||||
|
readonly inverseTransformMatrix: Matrix2D;
|
||||||
constructor();
|
constructor();
|
||||||
setMinimumZoom(minZoom: number): Camera;
|
setMinimumZoom(minZoom: number): Camera;
|
||||||
setMaximumZoom(maxZoom: number): Camera;
|
setMaximumZoom(maxZoom: number): Camera;
|
||||||
@@ -248,8 +325,105 @@ declare class Camera extends Component {
|
|||||||
update(): void;
|
update(): void;
|
||||||
setPosition(position: Vector2): this;
|
setPosition(position: Vector2): this;
|
||||||
updateMatrixes(): void;
|
updateMatrixes(): void;
|
||||||
|
screenToWorldPoint(screenPosition: Vector2): Vector2;
|
||||||
|
worldToScreenPoint(worldPosition: Vector2): Vector2;
|
||||||
destory(): void;
|
destory(): void;
|
||||||
}
|
}
|
||||||
|
declare class CameraInset {
|
||||||
|
left: any;
|
||||||
|
right: any;
|
||||||
|
top: any;
|
||||||
|
bottom: any;
|
||||||
|
}
|
||||||
|
declare class Mesh extends Component {
|
||||||
|
private _verts;
|
||||||
|
private _primitiveCount;
|
||||||
|
private _triangles;
|
||||||
|
private _topLeftVertPosition;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
initialize(): void;
|
||||||
|
setVertPosition(positions: Vector2[]): this;
|
||||||
|
setTriangles(triangles: number[]): this;
|
||||||
|
recalculateBounds(): this;
|
||||||
|
render(): void;
|
||||||
|
}
|
||||||
|
declare class VertexPosition {
|
||||||
|
position: Vector2;
|
||||||
|
constructor(position: Vector2);
|
||||||
|
}
|
||||||
|
declare class PolygonMesh extends Mesh {
|
||||||
|
constructor(points: Vector2[], arePointsCCW?: boolean);
|
||||||
|
}
|
||||||
|
declare abstract class RenderableComponent extends Component {
|
||||||
|
private _isVisible;
|
||||||
|
private _areBoundsDirty;
|
||||||
|
private _bounds;
|
||||||
|
private _localOffset;
|
||||||
|
readonly width: number;
|
||||||
|
readonly height: number;
|
||||||
|
isVisible: boolean;
|
||||||
|
readonly bounds: Rectangle;
|
||||||
|
protected getWidth(): number;
|
||||||
|
protected getHeight(): number;
|
||||||
|
protected getBounds(): Rectangle;
|
||||||
|
protected onBecameVisible(): void;
|
||||||
|
protected onBecameInvisible(): void;
|
||||||
|
isVisibleFromCamera(camera: Camera): boolean;
|
||||||
|
}
|
||||||
|
declare class SpriteRenderer extends RenderableComponent {
|
||||||
|
private _sprite;
|
||||||
|
private _origin;
|
||||||
|
sprite: egret.DisplayObject;
|
||||||
|
setSprite(sprite: egret.DisplayObject): SpriteRenderer;
|
||||||
|
initialize(): void;
|
||||||
|
}
|
||||||
|
interface ITriggerListener {
|
||||||
|
onTriggerEnter(other: Collider, local: Collider): any;
|
||||||
|
onTriggerExit(other: Collider, local: Collider): any;
|
||||||
|
}
|
||||||
|
declare class Mover extends Component {
|
||||||
|
private _triggerHelper;
|
||||||
|
onAddedToEntity(): void;
|
||||||
|
calculateMovement(motion: Vector2): CollisionResult;
|
||||||
|
applyMovement(motion: Vector2): void;
|
||||||
|
move(motion: Vector2): CollisionResult;
|
||||||
|
}
|
||||||
|
declare abstract class Collider extends Component {
|
||||||
|
shape: Shape;
|
||||||
|
physicsLayer: number;
|
||||||
|
isTrigger: boolean;
|
||||||
|
registeredPhysicsBounds: Rectangle;
|
||||||
|
shouldColliderScaleAndRotationWithTransform: boolean;
|
||||||
|
collidesWithLayers: number;
|
||||||
|
_localOffsetLength: number;
|
||||||
|
_isPositionDirty: boolean;
|
||||||
|
_isRotationDirty: boolean;
|
||||||
|
protected _isParentEntityAddedToScene: any;
|
||||||
|
protected _colliderRequiresAutoSizing: any;
|
||||||
|
protected _localOffset: Vector2;
|
||||||
|
protected _isColliderRegistered: any;
|
||||||
|
readonly bounds: Rectangle;
|
||||||
|
localOffset: Vector2;
|
||||||
|
setLocalOffset(offset: Vector2): void;
|
||||||
|
registerColliderWithPhysicsSystem(): void;
|
||||||
|
unregisterColliderWithPhysicsSystem(): void;
|
||||||
|
overlaps(other: Collider): any;
|
||||||
|
collidesWith(collider: Collider, motion: Vector2): CollisionResult;
|
||||||
|
onAddedToEntity(): void;
|
||||||
|
onRemovedFromEntity(): void;
|
||||||
|
onEntityTransformChanged(comp: ComponentTransform): void;
|
||||||
|
onEnabled(): void;
|
||||||
|
onDisabled(): void;
|
||||||
|
}
|
||||||
|
declare class BoxCollider extends Collider {
|
||||||
|
width: number;
|
||||||
|
setWidth(width: number): BoxCollider;
|
||||||
|
height: number;
|
||||||
|
setHeight(height: number): void;
|
||||||
|
constructor();
|
||||||
|
setSize(width: number, height: number): this;
|
||||||
|
}
|
||||||
declare class EntitySystem {
|
declare class EntitySystem {
|
||||||
private _scene;
|
private _scene;
|
||||||
private _entities;
|
private _entities;
|
||||||
@@ -309,6 +483,7 @@ declare class ComponentList {
|
|||||||
updateLists(): void;
|
updateLists(): void;
|
||||||
private handleRemove;
|
private handleRemove;
|
||||||
getComponent<T extends Component>(type: any, onlyReturnInitializedComponents: boolean): T;
|
getComponent<T extends Component>(type: any, onlyReturnInitializedComponents: boolean): T;
|
||||||
|
getComponents(typeName: string | any, components?: any): any;
|
||||||
update(): void;
|
update(): void;
|
||||||
onEntityTransformChanged(comp: any): void;
|
onEntityTransformChanged(comp: any): void;
|
||||||
}
|
}
|
||||||
@@ -323,12 +498,17 @@ declare class EntityList {
|
|||||||
private _entitiesToAdded;
|
private _entitiesToAdded;
|
||||||
private _tempEntityList;
|
private _tempEntityList;
|
||||||
private _entities;
|
private _entities;
|
||||||
|
private _entityDict;
|
||||||
|
private _unsortedTags;
|
||||||
constructor(scene: Scene);
|
constructor(scene: Scene);
|
||||||
readonly count: number;
|
readonly count: number;
|
||||||
readonly buffer: Entity[];
|
readonly buffer: Entity[];
|
||||||
add(entity: Entity): void;
|
add(entity: Entity): void;
|
||||||
remove(entity: Entity): void;
|
remove(entity: Entity): void;
|
||||||
findEntity(name: string): Entity;
|
findEntity(name: string): Entity;
|
||||||
|
getTagList(tag: number): Entity[];
|
||||||
|
addToTagList(entity: Entity): void;
|
||||||
|
removeFromTagList(entity: Entity): void;
|
||||||
update(): void;
|
update(): void;
|
||||||
removeAllEntities(): void;
|
removeAllEntities(): void;
|
||||||
updateLists(): void;
|
updateLists(): void;
|
||||||
@@ -360,14 +540,30 @@ declare class Time {
|
|||||||
static unscaledDeltaTime: any;
|
static unscaledDeltaTime: any;
|
||||||
static deltaTime: number;
|
static deltaTime: number;
|
||||||
static timeScale: number;
|
static timeScale: number;
|
||||||
|
static frameCount: number;
|
||||||
private static _lastTime;
|
private static _lastTime;
|
||||||
static update(currentTime: number): void;
|
static update(currentTime: number): void;
|
||||||
}
|
}
|
||||||
|
declare class Flags {
|
||||||
|
static isFlagSet(self: number, flag: number): boolean;
|
||||||
|
static isUnshiftedFlagSet(self: number, flag: number): boolean;
|
||||||
|
static setFlagExclusive(self: number, flag: number): number;
|
||||||
|
static setFlag(self: number, flag: number): number;
|
||||||
|
static unsetFlag(self: number, flag: number): number;
|
||||||
|
static invertFlags(self: number): number;
|
||||||
|
}
|
||||||
declare class MathHelper {
|
declare class MathHelper {
|
||||||
|
static readonly Epsilon: number;
|
||||||
|
static readonly Rad2Deg: number;
|
||||||
|
static readonly Deg2Rad: number;
|
||||||
static toDegrees(radians: number): number;
|
static toDegrees(radians: number): number;
|
||||||
static toRadians(degrees: number): number;
|
static toRadians(degrees: number): number;
|
||||||
static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number): number;
|
static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number): number;
|
||||||
|
static lerp(value1: number, value2: number, amount: number): number;
|
||||||
static clamp(value: number, min: number, max: number): number;
|
static clamp(value: number, min: number, max: number): number;
|
||||||
|
static minOf(a: number, b: number, c: number, d: number): number;
|
||||||
|
static maxOf(a: number, b: number, c: number, d: number): number;
|
||||||
|
static pointOnCirlce(circleCenter: Vector2, radius: number, angleInDegrees: number): Vector2;
|
||||||
}
|
}
|
||||||
declare class Matrix2D {
|
declare class Matrix2D {
|
||||||
m11: number;
|
m11: number;
|
||||||
@@ -393,21 +589,246 @@ declare class Matrix2D {
|
|||||||
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
||||||
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
||||||
}
|
}
|
||||||
declare class Point {
|
declare class Rectangle {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
constructor(x: number, y: number);
|
width: number;
|
||||||
|
height: number;
|
||||||
|
private _tempMat;
|
||||||
|
private _transformMat;
|
||||||
|
readonly left: number;
|
||||||
|
readonly right: number;
|
||||||
|
readonly top: number;
|
||||||
|
readonly bottom: number;
|
||||||
|
readonly center: Vector2;
|
||||||
|
location: Vector2;
|
||||||
|
constructor(x?: number, y?: number, width?: number, height?: number);
|
||||||
|
intersects(value: Rectangle): boolean;
|
||||||
|
contains(value: Vector2): boolean;
|
||||||
|
static fromMinMax(minX: number, minY: number, maxX: number, maxY: number): Rectangle;
|
||||||
|
getClosestPointOnRectangleBorderToPoint(point: Point): {
|
||||||
|
res: Vector2;
|
||||||
|
edgeNormal: Vector2;
|
||||||
|
};
|
||||||
|
calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number): void;
|
||||||
|
static rectEncompassingPoints(points: Vector2[]): Rectangle;
|
||||||
}
|
}
|
||||||
declare class Vector2 {
|
declare class Vector2 {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
private static readonly unitYVector;
|
||||||
|
private static readonly unitXVector;
|
||||||
private static readonly unitVector2;
|
private static readonly unitVector2;
|
||||||
static readonly One: Vector2;
|
private static readonly zeroVector2;
|
||||||
constructor(x: number, y: number);
|
static readonly zero: Vector2;
|
||||||
|
static readonly one: Vector2;
|
||||||
|
static readonly unitX: Vector2;
|
||||||
|
static readonly unitY: Vector2;
|
||||||
|
constructor(x?: number, y?: number);
|
||||||
static add(value1: Vector2, value2: Vector2): Vector2;
|
static add(value1: Vector2, value2: Vector2): Vector2;
|
||||||
static divide(value1: Vector2, value2: Vector2): Vector2;
|
static divide(value1: Vector2, value2: Vector2): Vector2;
|
||||||
static multiply(value1: Vector2, value2: Vector2): Vector2;
|
static multiply(value1: Vector2, value2: Vector2): Vector2;
|
||||||
static subtract(value1: Vector2, value2: Vector2): Vector2;
|
static subtract(value1: Vector2, value2: Vector2): Vector2;
|
||||||
normalize(): void;
|
normalize(): void;
|
||||||
|
length(): number;
|
||||||
|
static normalize(value: Vector2): Vector2;
|
||||||
|
static dot(value1: Vector2, value2: Vector2): number;
|
||||||
|
static distanceSquared(value1: Vector2, value2: Vector2): number;
|
||||||
|
static lerp(value1: Vector2, value2: Vector2, amount: number): Vector2;
|
||||||
static transform(position: Vector2, matrix: Matrix2D): Vector2;
|
static transform(position: Vector2, matrix: Matrix2D): Vector2;
|
||||||
|
static distance(value1: Vector2, value2: Vector2): number;
|
||||||
|
static negate(value: Vector2): Vector2;
|
||||||
|
}
|
||||||
|
declare class ColliderTriggerHelper {
|
||||||
|
private _entity;
|
||||||
|
private _activeTriggerIntersections;
|
||||||
|
private _previousTriggerIntersections;
|
||||||
|
private _tempTriggerList;
|
||||||
|
constructor(entity: Entity);
|
||||||
|
update(): void;
|
||||||
|
private checkForExitedColliders;
|
||||||
|
private notifyTriggerListeners;
|
||||||
|
}
|
||||||
|
declare enum PointSectors {
|
||||||
|
center = 0,
|
||||||
|
top = 1,
|
||||||
|
bottom = 2,
|
||||||
|
topLeft = 9,
|
||||||
|
topRight = 5,
|
||||||
|
left = 8,
|
||||||
|
right = 4,
|
||||||
|
bottomLeft = 10,
|
||||||
|
bottomRight = 6
|
||||||
|
}
|
||||||
|
declare class Collisions {
|
||||||
|
static isLineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): boolean;
|
||||||
|
static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): Vector2;
|
||||||
|
static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2;
|
||||||
|
static isCircleToCircle(circleCenter1: Vector2, circleRadius1: number, circleCenter2: Vector2, circleRadius2: number): boolean;
|
||||||
|
static isCircleToLine(circleCenter: Vector2, radius: number, lineFrom: Vector2, lineTo: Vector2): boolean;
|
||||||
|
static isCircleToPoint(circleCenter: Vector2, radius: number, point: Vector2): boolean;
|
||||||
|
static isRectToCircle(rect: Rectangle, cPosition: Vector2, cRadius: number): boolean;
|
||||||
|
static isRectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2): boolean;
|
||||||
|
static isRectToPoint(rX: number, rY: number, rW: number, rH: number, point: Vector2): boolean;
|
||||||
|
static getSector(rX: number, rY: number, rW: number, rH: number, point: Vector2): PointSectors;
|
||||||
|
}
|
||||||
|
declare class Physics {
|
||||||
|
private static _spatialHash;
|
||||||
|
static spatialHashCellSize: number;
|
||||||
|
static readonly allLayers: number;
|
||||||
|
static reset(): void;
|
||||||
|
static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask?: number): number;
|
||||||
|
static boxcastBroadphase(rect: Rectangle, layerMask?: number): Collider[];
|
||||||
|
static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask?: number): Collider[];
|
||||||
|
static addCollider(collider: Collider): void;
|
||||||
|
static removeCollider(collider: Collider): void;
|
||||||
|
static updateCollider(collider: Collider): void;
|
||||||
|
}
|
||||||
|
declare abstract class Shape {
|
||||||
|
bounds: Rectangle;
|
||||||
|
position: Vector2;
|
||||||
|
center: Vector2;
|
||||||
|
abstract recalculateBounds(collider: Collider): any;
|
||||||
|
abstract pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
|
abstract overlaps(other: Shape): any;
|
||||||
|
abstract collidesWithShape(other: Shape): CollisionResult;
|
||||||
|
}
|
||||||
|
declare class Polygon extends Shape {
|
||||||
|
points: Vector2[];
|
||||||
|
isUnrotated: boolean;
|
||||||
|
private _polygonCenter;
|
||||||
|
private _areEdgeNormalsDirty;
|
||||||
|
protected _originalPoints: Vector2[];
|
||||||
|
_edgeNormals: Vector2[];
|
||||||
|
readonly edgeNormals: Vector2[];
|
||||||
|
isBox: boolean;
|
||||||
|
constructor(points: Vector2[], isBox?: boolean);
|
||||||
|
private buildEdgeNormals;
|
||||||
|
setPoints(points: Vector2[]): void;
|
||||||
|
collidesWithShape(other: Shape): CollisionResult;
|
||||||
|
recalculateCenterAndEdgeNormals(): void;
|
||||||
|
overlaps(other: Shape): any;
|
||||||
|
static findPolygonCenter(points: Vector2[]): Vector2;
|
||||||
|
static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): {
|
||||||
|
closestPoint: any;
|
||||||
|
distanceSquared: any;
|
||||||
|
edgeNormal: any;
|
||||||
|
};
|
||||||
|
pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
|
containsPoint(point: Vector2): boolean;
|
||||||
|
static buildSymmertricalPolygon(vertCount: number, radius: number): any[];
|
||||||
|
recalculateBounds(collider: Collider): void;
|
||||||
|
}
|
||||||
|
declare class Box extends Polygon {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
constructor(width: number, height: number);
|
||||||
|
private static buildBox;
|
||||||
|
updateBox(width: number, height: number): void;
|
||||||
|
containsPoint(point: Vector2): boolean;
|
||||||
|
}
|
||||||
|
declare class Circle extends Shape {
|
||||||
|
radius: number;
|
||||||
|
private _originalRadius;
|
||||||
|
constructor(radius: number);
|
||||||
|
pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
|
collidesWithShape(other: Shape): CollisionResult;
|
||||||
|
recalculateBounds(collider: Collider): void;
|
||||||
|
overlaps(other: Shape): any;
|
||||||
|
}
|
||||||
|
declare class CollisionResult {
|
||||||
|
collider: Collider;
|
||||||
|
minimumTranslationVector: Vector2;
|
||||||
|
normal: Vector2;
|
||||||
|
point: Vector2;
|
||||||
|
invertResult(): void;
|
||||||
|
}
|
||||||
|
declare class ShapeCollisions {
|
||||||
|
static polygonToPolygon(first: Polygon, second: Polygon): CollisionResult;
|
||||||
|
static intervalDistance(minA: number, maxA: number, minB: number, maxB: any): number;
|
||||||
|
static getInterval(axis: Vector2, polygon: Polygon, min: number, max: number): {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
};
|
||||||
|
static circleToPolygon(circle: Circle, polygon: Polygon): CollisionResult;
|
||||||
|
static circleToBox(circle: Circle, box: Box): CollisionResult;
|
||||||
|
static pointToCircle(point: Vector2, circle: Circle): CollisionResult;
|
||||||
|
static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2;
|
||||||
|
static pointToPoly(point: Vector2, poly: Polygon): CollisionResult;
|
||||||
|
static circleToCircle(first: Circle, second: Circle): CollisionResult;
|
||||||
|
}
|
||||||
|
declare class SpatialHash {
|
||||||
|
gridBounds: Rectangle;
|
||||||
|
private _raycastParser;
|
||||||
|
private _cellSize;
|
||||||
|
private _inverseCellSize;
|
||||||
|
private _overlapTestCircle;
|
||||||
|
private _tempHashSet;
|
||||||
|
private _cellDict;
|
||||||
|
constructor(cellSize?: number);
|
||||||
|
remove(collider: Collider): void;
|
||||||
|
register(collider: Collider): void;
|
||||||
|
overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask: any): number;
|
||||||
|
aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Collider[];
|
||||||
|
private cellAtPosition;
|
||||||
|
private cellCoords;
|
||||||
|
}
|
||||||
|
declare class RaycastResultParser {
|
||||||
|
}
|
||||||
|
declare class NumberDictionary {
|
||||||
|
private _store;
|
||||||
|
private getKey;
|
||||||
|
private intToUint;
|
||||||
|
add(x: number, y: number, list: Collider[]): void;
|
||||||
|
remove(obj: Collider): void;
|
||||||
|
tryGetValue(x: number, y: number): Collider[];
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
declare class Emitter<T> {
|
||||||
|
private _messageTable;
|
||||||
|
constructor();
|
||||||
|
addObserver(eventType: T, handler: Function): void;
|
||||||
|
removeObserver(eventType: T, handler: Function): void;
|
||||||
|
emit(eventType: T, data: any): void;
|
||||||
|
}
|
||||||
|
declare class ListPool {
|
||||||
|
private static readonly _objectQueue;
|
||||||
|
static warmCache(cacheCount: number): void;
|
||||||
|
static trimCache(cacheCount: any): void;
|
||||||
|
static clearCache(): void;
|
||||||
|
static obtain<T>(): Array<T>;
|
||||||
|
static free<T>(obj: Array<T>): void;
|
||||||
|
}
|
||||||
|
declare class Pair<T> {
|
||||||
|
first: T;
|
||||||
|
second: T;
|
||||||
|
constructor(first: T, second: T);
|
||||||
|
clear(): void;
|
||||||
|
equals(other: Pair<T>): boolean;
|
||||||
|
}
|
||||||
|
declare class RectangleExt {
|
||||||
|
static union(first: Rectangle, point: Point): Rectangle;
|
||||||
|
static unionR(value1: Rectangle, value2: Rectangle): Rectangle;
|
||||||
|
}
|
||||||
|
declare class Triangulator {
|
||||||
|
triangleIndices: number[];
|
||||||
|
private _triPrev;
|
||||||
|
private _triNext;
|
||||||
|
triangulate(points: Vector2[], arePointsCCW?: boolean): void;
|
||||||
|
private initialize;
|
||||||
|
static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean;
|
||||||
|
}
|
||||||
|
declare class Vector2Ext {
|
||||||
|
static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2): boolean;
|
||||||
|
static cross(u: Vector2, v: Vector2): number;
|
||||||
|
static perpendicular(first: Vector2, second: Vector2): Vector2;
|
||||||
|
static normalize(vec: Vector2): Vector2;
|
||||||
|
static transformA(sourceArray: Vector2[], sourceIndex: number, matrix: Matrix2D, destinationArray: Vector2[], destinationIndex: number, length: number): void;
|
||||||
|
static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]): void;
|
||||||
|
}
|
||||||
|
declare class WebGLUtils {
|
||||||
|
static getWebGL(): WebGLRenderingContext;
|
||||||
|
static drawUserIndexPrimitives<T>(primitiveType: number, vertexData: T[], vertexOffset: number, numVertices: number, indexData: number[], indexOffset: number, primitiveCount: number): void;
|
||||||
|
private static getElementCountArray;
|
||||||
|
static checkGLError(): void;
|
||||||
}
|
}
|
||||||
|
|||||||
+2114
-54
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1092
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
+3
-1
@@ -7,7 +7,8 @@
|
|||||||
"libs/modules/game/game.js",
|
"libs/modules/game/game.js",
|
||||||
"libs/modules/tween/tween.js",
|
"libs/modules/tween/tween.js",
|
||||||
"libs/modules/promise/promise.js",
|
"libs/modules/promise/promise.js",
|
||||||
"libs/framework/framework.js"
|
"libs/framework/framework.js",
|
||||||
|
"libs/long/long.js"
|
||||||
],
|
],
|
||||||
"game": [
|
"game": [
|
||||||
"bin-debug/AssetAdapter.js",
|
"bin-debug/AssetAdapter.js",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"bin-debug/Main.js",
|
"bin-debug/Main.js",
|
||||||
"bin-debug/Platform.js",
|
"bin-debug/Platform.js",
|
||||||
"bin-debug/ThemeAdapter.js",
|
"bin-debug/ThemeAdapter.js",
|
||||||
|
"bin-debug/game/CoreEmitterType.js",
|
||||||
"bin-debug/game/MainScene.js",
|
"bin-debug/game/MainScene.js",
|
||||||
"bin-debug/game/SpawnerComponent.js",
|
"bin-debug/game/SpawnerComponent.js",
|
||||||
"bin-debug/game/SpawnerSystem.js"
|
"bin-debug/game/SpawnerSystem.js"
|
||||||
|
|||||||
+29
-2
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
|
|
||||||
class Main extends eui.UILayer {
|
class Main extends eui.UILayer {
|
||||||
|
public static emitter: Emitter<CoreEmitterType>;
|
||||||
|
|
||||||
protected createChildren(): void {
|
protected createChildren(): void {
|
||||||
super.createChildren();
|
super.createChildren();
|
||||||
@@ -51,10 +52,23 @@ class Main extends eui.UILayer {
|
|||||||
egret.registerImplementation("eui.IAssetAdapter", assetAdapter);
|
egret.registerImplementation("eui.IAssetAdapter", assetAdapter);
|
||||||
egret.registerImplementation("eui.IThemeAdapter", new ThemeAdapter());
|
egret.registerImplementation("eui.IThemeAdapter", new ThemeAdapter());
|
||||||
|
|
||||||
|
Main.emitter = new Emitter<CoreEmitterType>();
|
||||||
|
this.addEventListener(egret.Event.ENTER_FRAME, this.updateFrame, this);
|
||||||
this.runGame();
|
this.runGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateFrame(evt: egret.Event){
|
||||||
|
Main.emitter.emit(CoreEmitterType.Update, evt);
|
||||||
|
let activeScene = SceneManager.getActiveScene();
|
||||||
|
if (activeScene){
|
||||||
|
let player = activeScene.findEntity("player");
|
||||||
|
if (player){
|
||||||
|
let mover = player.getComponent<Mover>(Mover);
|
||||||
|
mover.move(new Vector2(0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async runGame() {
|
private async runGame() {
|
||||||
await this.loadResource();
|
await this.loadResource();
|
||||||
this.createGameScene();
|
this.createGameScene();
|
||||||
@@ -93,7 +107,20 @@ class Main extends eui.UILayer {
|
|||||||
protected createGameScene(): void {
|
protected createGameScene(): void {
|
||||||
let scene = SceneManager.createScene("main", new MainScene(this)).setActive();
|
let scene = SceneManager.createScene("main", new MainScene(this)).setActive();
|
||||||
let player = scene.createEntity("player");
|
let player = scene.createEntity("player");
|
||||||
|
player.addComponent(new PolygonMesh([new Vector2(0, 0),
|
||||||
|
new Vector2(10, 0),
|
||||||
|
new Vector2(10, 10),
|
||||||
|
new Vector2(0, 10),
|
||||||
|
new Vector2(0, 0)]));
|
||||||
player.addComponent(new SpawnComponent(EnemyType.worm));
|
player.addComponent(new SpawnComponent(EnemyType.worm));
|
||||||
// console.log(player.transform.position);
|
player.addComponent(new BoxCollider()).setSize(100, 100).isTrigger = true;
|
||||||
|
player.addComponent(new Mover());
|
||||||
|
|
||||||
|
let player2 = scene.createEntity("player2");
|
||||||
|
player2.addComponent(new BoxCollider()).setSize(99, 99);
|
||||||
|
|
||||||
|
// Main.emitter.addObserver(CoreEmitterType.Update, ()=>{
|
||||||
|
// console.log("update emitter");
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
enum CoreEmitterType {
|
||||||
|
Update,
|
||||||
|
}
|
||||||
@@ -2,8 +2,36 @@ class MainScene extends Scene {
|
|||||||
constructor(displayContent: egret.DisplayObject){
|
constructor(displayContent: egret.DisplayObject){
|
||||||
super(displayContent);
|
super(displayContent);
|
||||||
|
|
||||||
this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
// this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
||||||
this.astarTest();
|
this.astarTest();
|
||||||
|
this.dijkstraTest();
|
||||||
|
this.breadthfirstTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public breadthfirstTest(){
|
||||||
|
let graph = new UnweightedGraph<string>();
|
||||||
|
|
||||||
|
graph.addEdgesForNode("a", ["b"]); // a->b
|
||||||
|
graph.addEdgesForNode("b", ["a", "c", "d"]); // b->a b->c b->d
|
||||||
|
graph.addEdgesForNode("c", ["a"]); // c->a
|
||||||
|
graph.addEdgesForNode("d", ["e", "a"]); // d->e d->a
|
||||||
|
graph.addEdgesForNode("e", ["b"]); // e->b
|
||||||
|
|
||||||
|
// 计算从c到e的路径
|
||||||
|
let path = BreadthFirstPathfinder.search(graph, "c", "e");
|
||||||
|
console.log(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dijkstraTest(){
|
||||||
|
let graph = new WeightedGridGraph(20, 20);
|
||||||
|
|
||||||
|
graph.weightedNodes.push(new Point(3, 3));
|
||||||
|
graph.weightedNodes.push(new Point(3, 4));
|
||||||
|
graph.weightedNodes.push(new Point(4, 3));
|
||||||
|
graph.weightedNodes.push(new Point(4, 4));
|
||||||
|
|
||||||
|
let path = graph.search(new Point(3, 4), new Point(15, 17));
|
||||||
|
console.log(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public astarTest(){
|
public astarTest(){
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
class SpawnComponent extends Component {
|
class SpawnComponent extends Component implements ITriggerListener {
|
||||||
public cooldown = -1;
|
public cooldown = -1;
|
||||||
public minInterval = 2;
|
public minInterval = 2;
|
||||||
public maxInterval = 60;
|
public maxInterval = 60;
|
||||||
@@ -18,6 +18,16 @@ class SpawnComponent extends Component {
|
|||||||
public update() {
|
public update() {
|
||||||
// console.log("update");
|
// console.log("update");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onTriggerEnter(other: Collider, local: Collider){
|
||||||
|
if (other == local)
|
||||||
|
console.log("repeat collider")
|
||||||
|
console.log("enter collider");
|
||||||
|
}
|
||||||
|
|
||||||
|
public onTriggerExit(other: Collider, local: Collider){
|
||||||
|
console.log("exit collider");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EnemyType {
|
enum EnemyType {
|
||||||
|
|||||||
Vendored
+391
-14
@@ -72,22 +72,88 @@ declare class PriorityQueue<T extends PriorityQueueNode> {
|
|||||||
private swap;
|
private swap;
|
||||||
private hasHigherPriority;
|
private hasHigherPriority;
|
||||||
}
|
}
|
||||||
|
declare class BreadthFirstPathfinder {
|
||||||
|
static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
}
|
||||||
|
interface IUnweightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||||
|
edges: Map<T, T[]>;
|
||||||
|
addEdgesForNode(node: T, edges: T[]): this;
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
constructor(x?: number, y?: number);
|
||||||
|
}
|
||||||
|
declare class UnweightedGridGraph implements IUnweightedGraph<Point> {
|
||||||
|
private static readonly CARDINAL_DIRS;
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
private _width;
|
||||||
|
private _hegiht;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
}
|
||||||
|
interface IWeightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
}
|
||||||
|
declare class WeightedGridGraph implements IWeightedGraph<Point> {
|
||||||
|
static readonly CARDINAL_DIRS: Point[];
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
weightedNodes: Point[];
|
||||||
|
defaultWeight: number;
|
||||||
|
weightedNodeWeight: number;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
cost(from: Point, to: Point): number;
|
||||||
|
}
|
||||||
|
declare class WeightedNode<T> extends PriorityQueueNode {
|
||||||
|
data: T;
|
||||||
|
constructor(data: T);
|
||||||
|
}
|
||||||
|
declare class WeightedPathfinder {
|
||||||
|
static search<T>(graph: IWeightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
private static getKey;
|
||||||
|
static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[];
|
||||||
|
}
|
||||||
|
declare class DebugDefaults {
|
||||||
|
static verletParticle: number;
|
||||||
|
static verletConstraintEdge: number;
|
||||||
|
}
|
||||||
declare abstract class Component {
|
declare abstract class Component {
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
displayRender: egret.DisplayObject;
|
|
||||||
private _enabled;
|
private _enabled;
|
||||||
updateInterval: number;
|
updateInterval: number;
|
||||||
readonly transform: Transform;
|
readonly transform: Transform;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
setEnabled(isEnabled: boolean): this;
|
setEnabled(isEnabled: boolean): this;
|
||||||
abstract initialize(): any;
|
initialize(): void;
|
||||||
onAddedToEntity(): void;
|
onAddedToEntity(): void;
|
||||||
onRemovedFromEntity(): void;
|
onRemovedFromEntity(): void;
|
||||||
onEnabled(): void;
|
onEnabled(): void;
|
||||||
onDisabled(): void;
|
onDisabled(): void;
|
||||||
onEntityTransformChanged(comp: ComponentTransform): void;
|
onEntityTransformChanged(comp: ComponentTransform): void;
|
||||||
update(): void;
|
update(): void;
|
||||||
bind(displayRender: egret.DisplayObject): this;
|
debugRender(): void;
|
||||||
registerComponent(): void;
|
registerComponent(): void;
|
||||||
deregisterComponent(): void;
|
deregisterComponent(): void;
|
||||||
}
|
}
|
||||||
@@ -129,6 +195,7 @@ declare class Entity {
|
|||||||
hasComponent<T extends Component>(type: any): boolean;
|
hasComponent<T extends Component>(type: any): boolean;
|
||||||
getOrCreateComponent<T extends Component>(type: T): T;
|
getOrCreateComponent<T extends Component>(type: T): T;
|
||||||
getComponent<T extends Component>(type: any): T;
|
getComponent<T extends Component>(type: any): T;
|
||||||
|
getComponents(typeName: string | any, componentList?: any): any;
|
||||||
removeComponentForType<T extends Component>(type: any): boolean;
|
removeComponentForType<T extends Component>(type: any): boolean;
|
||||||
removeComponent(component: Component): void;
|
removeComponent(component: Component): void;
|
||||||
removeAllComponents(): void;
|
removeAllComponents(): void;
|
||||||
@@ -240,11 +307,16 @@ declare class Camera extends Component {
|
|||||||
private _minimumZoom;
|
private _minimumZoom;
|
||||||
private _maximumZoom;
|
private _maximumZoom;
|
||||||
private _areMatrixesDirty;
|
private _areMatrixesDirty;
|
||||||
|
private _inset;
|
||||||
|
private _bounds;
|
||||||
|
private _areBoundsDirty;
|
||||||
|
readonly bounds: Rectangle;
|
||||||
zoom: number;
|
zoom: number;
|
||||||
minimumZoom: number;
|
minimumZoom: number;
|
||||||
maximumZoom: number;
|
maximumZoom: number;
|
||||||
origin: Vector2;
|
origin: Vector2;
|
||||||
readonly transformMatrix: Matrix2D;
|
readonly transformMatrix: Matrix2D;
|
||||||
|
readonly inverseTransformMatrix: Matrix2D;
|
||||||
constructor();
|
constructor();
|
||||||
setMinimumZoom(minZoom: number): Camera;
|
setMinimumZoom(minZoom: number): Camera;
|
||||||
setMaximumZoom(maxZoom: number): Camera;
|
setMaximumZoom(maxZoom: number): Camera;
|
||||||
@@ -253,8 +325,105 @@ declare class Camera extends Component {
|
|||||||
update(): void;
|
update(): void;
|
||||||
setPosition(position: Vector2): this;
|
setPosition(position: Vector2): this;
|
||||||
updateMatrixes(): void;
|
updateMatrixes(): void;
|
||||||
|
screenToWorldPoint(screenPosition: Vector2): Vector2;
|
||||||
|
worldToScreenPoint(worldPosition: Vector2): Vector2;
|
||||||
destory(): void;
|
destory(): void;
|
||||||
}
|
}
|
||||||
|
declare class CameraInset {
|
||||||
|
left: any;
|
||||||
|
right: any;
|
||||||
|
top: any;
|
||||||
|
bottom: any;
|
||||||
|
}
|
||||||
|
declare class Mesh extends Component {
|
||||||
|
private _verts;
|
||||||
|
private _primitiveCount;
|
||||||
|
private _triangles;
|
||||||
|
private _topLeftVertPosition;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
initialize(): void;
|
||||||
|
setVertPosition(positions: Vector2[]): this;
|
||||||
|
setTriangles(triangles: number[]): this;
|
||||||
|
recalculateBounds(): this;
|
||||||
|
render(): void;
|
||||||
|
}
|
||||||
|
declare class VertexPosition {
|
||||||
|
position: Vector2;
|
||||||
|
constructor(position: Vector2);
|
||||||
|
}
|
||||||
|
declare class PolygonMesh extends Mesh {
|
||||||
|
constructor(points: Vector2[], arePointsCCW?: boolean);
|
||||||
|
}
|
||||||
|
declare abstract class RenderableComponent extends Component {
|
||||||
|
private _isVisible;
|
||||||
|
private _areBoundsDirty;
|
||||||
|
private _bounds;
|
||||||
|
private _localOffset;
|
||||||
|
readonly width: number;
|
||||||
|
readonly height: number;
|
||||||
|
isVisible: boolean;
|
||||||
|
readonly bounds: Rectangle;
|
||||||
|
protected getWidth(): number;
|
||||||
|
protected getHeight(): number;
|
||||||
|
protected getBounds(): Rectangle;
|
||||||
|
protected onBecameVisible(): void;
|
||||||
|
protected onBecameInvisible(): void;
|
||||||
|
isVisibleFromCamera(camera: Camera): boolean;
|
||||||
|
}
|
||||||
|
declare class SpriteRenderer extends RenderableComponent {
|
||||||
|
private _sprite;
|
||||||
|
private _origin;
|
||||||
|
sprite: egret.DisplayObject;
|
||||||
|
setSprite(sprite: egret.DisplayObject): SpriteRenderer;
|
||||||
|
initialize(): void;
|
||||||
|
}
|
||||||
|
interface ITriggerListener {
|
||||||
|
onTriggerEnter(other: Collider, local: Collider): any;
|
||||||
|
onTriggerExit(other: Collider, local: Collider): any;
|
||||||
|
}
|
||||||
|
declare class Mover extends Component {
|
||||||
|
private _triggerHelper;
|
||||||
|
onAddedToEntity(): void;
|
||||||
|
calculateMovement(motion: Vector2): CollisionResult;
|
||||||
|
applyMovement(motion: Vector2): void;
|
||||||
|
move(motion: Vector2): CollisionResult;
|
||||||
|
}
|
||||||
|
declare abstract class Collider extends Component {
|
||||||
|
shape: Shape;
|
||||||
|
physicsLayer: number;
|
||||||
|
isTrigger: boolean;
|
||||||
|
registeredPhysicsBounds: Rectangle;
|
||||||
|
shouldColliderScaleAndRotationWithTransform: boolean;
|
||||||
|
collidesWithLayers: number;
|
||||||
|
_localOffsetLength: number;
|
||||||
|
_isPositionDirty: boolean;
|
||||||
|
_isRotationDirty: boolean;
|
||||||
|
protected _isParentEntityAddedToScene: any;
|
||||||
|
protected _colliderRequiresAutoSizing: any;
|
||||||
|
protected _localOffset: Vector2;
|
||||||
|
protected _isColliderRegistered: any;
|
||||||
|
readonly bounds: Rectangle;
|
||||||
|
localOffset: Vector2;
|
||||||
|
setLocalOffset(offset: Vector2): void;
|
||||||
|
registerColliderWithPhysicsSystem(): void;
|
||||||
|
unregisterColliderWithPhysicsSystem(): void;
|
||||||
|
overlaps(other: Collider): any;
|
||||||
|
collidesWith(collider: Collider, motion: Vector2): CollisionResult;
|
||||||
|
onAddedToEntity(): void;
|
||||||
|
onRemovedFromEntity(): void;
|
||||||
|
onEntityTransformChanged(comp: ComponentTransform): void;
|
||||||
|
onEnabled(): void;
|
||||||
|
onDisabled(): void;
|
||||||
|
}
|
||||||
|
declare class BoxCollider extends Collider {
|
||||||
|
width: number;
|
||||||
|
setWidth(width: number): BoxCollider;
|
||||||
|
height: number;
|
||||||
|
setHeight(height: number): void;
|
||||||
|
constructor();
|
||||||
|
setSize(width: number, height: number): this;
|
||||||
|
}
|
||||||
declare class EntitySystem {
|
declare class EntitySystem {
|
||||||
private _scene;
|
private _scene;
|
||||||
private _entities;
|
private _entities;
|
||||||
@@ -314,6 +483,7 @@ declare class ComponentList {
|
|||||||
updateLists(): void;
|
updateLists(): void;
|
||||||
private handleRemove;
|
private handleRemove;
|
||||||
getComponent<T extends Component>(type: any, onlyReturnInitializedComponents: boolean): T;
|
getComponent<T extends Component>(type: any, onlyReturnInitializedComponents: boolean): T;
|
||||||
|
getComponents(typeName: string | any, components?: any): any;
|
||||||
update(): void;
|
update(): void;
|
||||||
onEntityTransformChanged(comp: any): void;
|
onEntityTransformChanged(comp: any): void;
|
||||||
}
|
}
|
||||||
@@ -370,14 +540,30 @@ declare class Time {
|
|||||||
static unscaledDeltaTime: any;
|
static unscaledDeltaTime: any;
|
||||||
static deltaTime: number;
|
static deltaTime: number;
|
||||||
static timeScale: number;
|
static timeScale: number;
|
||||||
|
static frameCount: number;
|
||||||
private static _lastTime;
|
private static _lastTime;
|
||||||
static update(currentTime: number): void;
|
static update(currentTime: number): void;
|
||||||
}
|
}
|
||||||
|
declare class Flags {
|
||||||
|
static isFlagSet(self: number, flag: number): boolean;
|
||||||
|
static isUnshiftedFlagSet(self: number, flag: number): boolean;
|
||||||
|
static setFlagExclusive(self: number, flag: number): number;
|
||||||
|
static setFlag(self: number, flag: number): number;
|
||||||
|
static unsetFlag(self: number, flag: number): number;
|
||||||
|
static invertFlags(self: number): number;
|
||||||
|
}
|
||||||
declare class MathHelper {
|
declare class MathHelper {
|
||||||
|
static readonly Epsilon: number;
|
||||||
|
static readonly Rad2Deg: number;
|
||||||
|
static readonly Deg2Rad: number;
|
||||||
static toDegrees(radians: number): number;
|
static toDegrees(radians: number): number;
|
||||||
static toRadians(degrees: number): number;
|
static toRadians(degrees: number): number;
|
||||||
static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number): number;
|
static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number): number;
|
||||||
|
static lerp(value1: number, value2: number, amount: number): number;
|
||||||
static clamp(value: number, min: number, max: number): number;
|
static clamp(value: number, min: number, max: number): number;
|
||||||
|
static minOf(a: number, b: number, c: number, d: number): number;
|
||||||
|
static maxOf(a: number, b: number, c: number, d: number): number;
|
||||||
|
static pointOnCirlce(circleCenter: Vector2, radius: number, angleInDegrees: number): Vector2;
|
||||||
}
|
}
|
||||||
declare class Matrix2D {
|
declare class Matrix2D {
|
||||||
m11: number;
|
m11: number;
|
||||||
@@ -403,34 +589,65 @@ declare class Matrix2D {
|
|||||||
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
||||||
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
||||||
}
|
}
|
||||||
declare class Point {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
constructor(x: number, y: number);
|
|
||||||
}
|
|
||||||
declare class Rectangle {
|
declare class Rectangle {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
constructor(x: number, y: number, width: number, height: number);
|
private _tempMat;
|
||||||
|
private _transformMat;
|
||||||
|
readonly left: number;
|
||||||
|
readonly right: number;
|
||||||
|
readonly top: number;
|
||||||
|
readonly bottom: number;
|
||||||
|
readonly center: Vector2;
|
||||||
|
location: Vector2;
|
||||||
|
constructor(x?: number, y?: number, width?: number, height?: number);
|
||||||
|
intersects(value: Rectangle): boolean;
|
||||||
|
contains(value: Vector2): boolean;
|
||||||
|
static fromMinMax(minX: number, minY: number, maxX: number, maxY: number): Rectangle;
|
||||||
|
getClosestPointOnRectangleBorderToPoint(point: Point): {
|
||||||
|
res: Vector2;
|
||||||
|
edgeNormal: Vector2;
|
||||||
|
};
|
||||||
|
calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number): void;
|
||||||
|
static rectEncompassingPoints(points: Vector2[]): Rectangle;
|
||||||
}
|
}
|
||||||
declare class Vector2 {
|
declare class Vector2 {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
private static readonly zeroVector;
|
private static readonly unitYVector;
|
||||||
private static readonly unitVector;
|
private static readonly unitXVector;
|
||||||
static readonly One: Vector2;
|
private static readonly unitVector2;
|
||||||
static readonly Zero: Vector2;
|
private static readonly zeroVector2;
|
||||||
constructor(x: number, y: number);
|
static readonly zero: Vector2;
|
||||||
|
static readonly one: Vector2;
|
||||||
|
static readonly unitX: Vector2;
|
||||||
|
static readonly unitY: Vector2;
|
||||||
|
constructor(x?: number, y?: number);
|
||||||
static add(value1: Vector2, value2: Vector2): Vector2;
|
static add(value1: Vector2, value2: Vector2): Vector2;
|
||||||
static divide(value1: Vector2, value2: Vector2): Vector2;
|
static divide(value1: Vector2, value2: Vector2): Vector2;
|
||||||
static multiply(value1: Vector2, value2: Vector2): Vector2;
|
static multiply(value1: Vector2, value2: Vector2): Vector2;
|
||||||
static subtract(value1: Vector2, value2: Vector2): Vector2;
|
static subtract(value1: Vector2, value2: Vector2): Vector2;
|
||||||
normalize(): void;
|
normalize(): void;
|
||||||
|
length(): number;
|
||||||
|
static normalize(value: Vector2): Vector2;
|
||||||
static dot(value1: Vector2, value2: Vector2): number;
|
static dot(value1: Vector2, value2: Vector2): number;
|
||||||
static distanceSquared(value1: Vector2, value2: Vector2): number;
|
static distanceSquared(value1: Vector2, value2: Vector2): number;
|
||||||
|
static lerp(value1: Vector2, value2: Vector2, amount: number): Vector2;
|
||||||
static transform(position: Vector2, matrix: Matrix2D): Vector2;
|
static transform(position: Vector2, matrix: Matrix2D): Vector2;
|
||||||
|
static distance(value1: Vector2, value2: Vector2): number;
|
||||||
|
static negate(value: Vector2): Vector2;
|
||||||
|
}
|
||||||
|
declare class ColliderTriggerHelper {
|
||||||
|
private _entity;
|
||||||
|
private _activeTriggerIntersections;
|
||||||
|
private _previousTriggerIntersections;
|
||||||
|
private _tempTriggerList;
|
||||||
|
constructor(entity: Entity);
|
||||||
|
update(): void;
|
||||||
|
private checkForExitedColliders;
|
||||||
|
private notifyTriggerListeners;
|
||||||
}
|
}
|
||||||
declare enum PointSectors {
|
declare enum PointSectors {
|
||||||
center = 0,
|
center = 0,
|
||||||
@@ -455,3 +672,163 @@ declare class Collisions {
|
|||||||
static isRectToPoint(rX: number, rY: number, rW: number, rH: number, point: Vector2): boolean;
|
static isRectToPoint(rX: number, rY: number, rW: number, rH: number, point: Vector2): boolean;
|
||||||
static getSector(rX: number, rY: number, rW: number, rH: number, point: Vector2): PointSectors;
|
static getSector(rX: number, rY: number, rW: number, rH: number, point: Vector2): PointSectors;
|
||||||
}
|
}
|
||||||
|
declare class Physics {
|
||||||
|
private static _spatialHash;
|
||||||
|
static spatialHashCellSize: number;
|
||||||
|
static readonly allLayers: number;
|
||||||
|
static reset(): void;
|
||||||
|
static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask?: number): number;
|
||||||
|
static boxcastBroadphase(rect: Rectangle, layerMask?: number): Collider[];
|
||||||
|
static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask?: number): Collider[];
|
||||||
|
static addCollider(collider: Collider): void;
|
||||||
|
static removeCollider(collider: Collider): void;
|
||||||
|
static updateCollider(collider: Collider): void;
|
||||||
|
}
|
||||||
|
declare abstract class Shape {
|
||||||
|
bounds: Rectangle;
|
||||||
|
position: Vector2;
|
||||||
|
center: Vector2;
|
||||||
|
abstract recalculateBounds(collider: Collider): any;
|
||||||
|
abstract pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
|
abstract overlaps(other: Shape): any;
|
||||||
|
abstract collidesWithShape(other: Shape): CollisionResult;
|
||||||
|
}
|
||||||
|
declare class Polygon extends Shape {
|
||||||
|
points: Vector2[];
|
||||||
|
isUnrotated: boolean;
|
||||||
|
private _polygonCenter;
|
||||||
|
private _areEdgeNormalsDirty;
|
||||||
|
protected _originalPoints: Vector2[];
|
||||||
|
_edgeNormals: Vector2[];
|
||||||
|
readonly edgeNormals: Vector2[];
|
||||||
|
isBox: boolean;
|
||||||
|
constructor(points: Vector2[], isBox?: boolean);
|
||||||
|
private buildEdgeNormals;
|
||||||
|
setPoints(points: Vector2[]): void;
|
||||||
|
collidesWithShape(other: Shape): CollisionResult;
|
||||||
|
recalculateCenterAndEdgeNormals(): void;
|
||||||
|
overlaps(other: Shape): any;
|
||||||
|
static findPolygonCenter(points: Vector2[]): Vector2;
|
||||||
|
static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): {
|
||||||
|
closestPoint: any;
|
||||||
|
distanceSquared: any;
|
||||||
|
edgeNormal: any;
|
||||||
|
};
|
||||||
|
pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
|
containsPoint(point: Vector2): boolean;
|
||||||
|
static buildSymmertricalPolygon(vertCount: number, radius: number): any[];
|
||||||
|
recalculateBounds(collider: Collider): void;
|
||||||
|
}
|
||||||
|
declare class Box extends Polygon {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
constructor(width: number, height: number);
|
||||||
|
private static buildBox;
|
||||||
|
updateBox(width: number, height: number): void;
|
||||||
|
containsPoint(point: Vector2): boolean;
|
||||||
|
}
|
||||||
|
declare class Circle extends Shape {
|
||||||
|
radius: number;
|
||||||
|
private _originalRadius;
|
||||||
|
constructor(radius: number);
|
||||||
|
pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
|
collidesWithShape(other: Shape): CollisionResult;
|
||||||
|
recalculateBounds(collider: Collider): void;
|
||||||
|
overlaps(other: Shape): any;
|
||||||
|
}
|
||||||
|
declare class CollisionResult {
|
||||||
|
collider: Collider;
|
||||||
|
minimumTranslationVector: Vector2;
|
||||||
|
normal: Vector2;
|
||||||
|
point: Vector2;
|
||||||
|
invertResult(): void;
|
||||||
|
}
|
||||||
|
declare class ShapeCollisions {
|
||||||
|
static polygonToPolygon(first: Polygon, second: Polygon): CollisionResult;
|
||||||
|
static intervalDistance(minA: number, maxA: number, minB: number, maxB: any): number;
|
||||||
|
static getInterval(axis: Vector2, polygon: Polygon, min: number, max: number): {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
};
|
||||||
|
static circleToPolygon(circle: Circle, polygon: Polygon): CollisionResult;
|
||||||
|
static circleToBox(circle: Circle, box: Box): CollisionResult;
|
||||||
|
static pointToCircle(point: Vector2, circle: Circle): CollisionResult;
|
||||||
|
static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2;
|
||||||
|
static pointToPoly(point: Vector2, poly: Polygon): CollisionResult;
|
||||||
|
static circleToCircle(first: Circle, second: Circle): CollisionResult;
|
||||||
|
}
|
||||||
|
declare class SpatialHash {
|
||||||
|
gridBounds: Rectangle;
|
||||||
|
private _raycastParser;
|
||||||
|
private _cellSize;
|
||||||
|
private _inverseCellSize;
|
||||||
|
private _overlapTestCircle;
|
||||||
|
private _tempHashSet;
|
||||||
|
private _cellDict;
|
||||||
|
constructor(cellSize?: number);
|
||||||
|
remove(collider: Collider): void;
|
||||||
|
register(collider: Collider): void;
|
||||||
|
overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask: any): number;
|
||||||
|
aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Collider[];
|
||||||
|
private cellAtPosition;
|
||||||
|
private cellCoords;
|
||||||
|
}
|
||||||
|
declare class RaycastResultParser {
|
||||||
|
}
|
||||||
|
declare class NumberDictionary {
|
||||||
|
private _store;
|
||||||
|
private getKey;
|
||||||
|
private intToUint;
|
||||||
|
add(x: number, y: number, list: Collider[]): void;
|
||||||
|
remove(obj: Collider): void;
|
||||||
|
tryGetValue(x: number, y: number): Collider[];
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
declare class Emitter<T> {
|
||||||
|
private _messageTable;
|
||||||
|
constructor();
|
||||||
|
addObserver(eventType: T, handler: Function): void;
|
||||||
|
removeObserver(eventType: T, handler: Function): void;
|
||||||
|
emit(eventType: T, data: any): void;
|
||||||
|
}
|
||||||
|
declare class ListPool {
|
||||||
|
private static readonly _objectQueue;
|
||||||
|
static warmCache(cacheCount: number): void;
|
||||||
|
static trimCache(cacheCount: any): void;
|
||||||
|
static clearCache(): void;
|
||||||
|
static obtain<T>(): Array<T>;
|
||||||
|
static free<T>(obj: Array<T>): void;
|
||||||
|
}
|
||||||
|
declare class Pair<T> {
|
||||||
|
first: T;
|
||||||
|
second: T;
|
||||||
|
constructor(first: T, second: T);
|
||||||
|
clear(): void;
|
||||||
|
equals(other: Pair<T>): boolean;
|
||||||
|
}
|
||||||
|
declare class RectangleExt {
|
||||||
|
static union(first: Rectangle, point: Point): Rectangle;
|
||||||
|
static unionR(value1: Rectangle, value2: Rectangle): Rectangle;
|
||||||
|
}
|
||||||
|
declare class Triangulator {
|
||||||
|
triangleIndices: number[];
|
||||||
|
private _triPrev;
|
||||||
|
private _triNext;
|
||||||
|
triangulate(points: Vector2[], arePointsCCW?: boolean): void;
|
||||||
|
private initialize;
|
||||||
|
static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean;
|
||||||
|
}
|
||||||
|
declare class Vector2Ext {
|
||||||
|
static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2): boolean;
|
||||||
|
static cross(u: Vector2, v: Vector2): number;
|
||||||
|
static perpendicular(first: Vector2, second: Vector2): Vector2;
|
||||||
|
static normalize(vec: Vector2): Vector2;
|
||||||
|
static transformA(sourceArray: Vector2[], sourceIndex: number, matrix: Matrix2D, destinationArray: Vector2[], destinationIndex: number, length: number): void;
|
||||||
|
static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]): void;
|
||||||
|
}
|
||||||
|
declare class WebGLUtils {
|
||||||
|
static getWebGL(): WebGLRenderingContext;
|
||||||
|
static drawUserIndexPrimitives<T>(primitiveType: number, vertexData: T[], vertexOffset: number, numVertices: number, indexData: number[], indexOffset: number, primitiveCount: number): void;
|
||||||
|
private static getElementCountArray;
|
||||||
|
static checkGLError(): void;
|
||||||
|
}
|
||||||
|
|||||||
+1925
-95
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1178
-1178
File diff suppressed because it is too large
Load Diff
+7
-5
@@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "@esengine/egret-framework",
|
"name": "@esengine/egret-framework",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "用于egret 包含众多高性能方法以供使用",
|
"description": "用于egret 包含众多高性能方法以供使用",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
"lib": "lib"
|
"lib": "lib"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "mocha --recursive --reporter tap --growl",
|
||||||
|
"eslint": "eslint src --ext .ts"
|
||||||
},
|
},
|
||||||
"author": "yhh",
|
"author": "yhh",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-babel": "^8.0.0",
|
"gulp-babel": "^8.0.0",
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
|
"gulp-inject-string": "^1.1.2",
|
||||||
"gulp-minify": "^3.1.0",
|
"gulp-minify": "^3.1.0",
|
||||||
"gulp-string-replace": "^1.1.2",
|
"gulp-string-replace": "^1.1.2",
|
||||||
"gulp-typescript": "^3.1.6",
|
"gulp-typescript": "^3.1.6",
|
||||||
@@ -25,10 +27,10 @@
|
|||||||
"tsify": "^3.0.1",
|
"tsify": "^3.0.1",
|
||||||
"typescript": "^2.2.2",
|
"typescript": "^2.2.2",
|
||||||
"vinyl-source-stream": "^1.1.0",
|
"vinyl-source-stream": "^1.1.0",
|
||||||
"watchify": "^3.9.0",
|
"watchify": "^3.9.0"
|
||||||
"gulp-inject-string": "^1.1.2"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://npm.pkg.github.com/359807859@qq.com"
|
"registry": "https://npm.pkg.github.com/359807859@qq.com"
|
||||||
}
|
},
|
||||||
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class AstarGridGraph implements IAstarGraph<Point> {
|
|||||||
* @param node
|
* @param node
|
||||||
*/
|
*/
|
||||||
public isNodePassable(node: Point): boolean {
|
public isNodePassable(node: Point): boolean {
|
||||||
return !this.walls.contains(node);
|
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
public search(start: Point, goal: Point){
|
public search(start: Point, goal: Point){
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 计算路径给定的IUnweightedGraph和开始/目标位置
|
||||||
|
*/
|
||||||
|
class BreadthFirstPathfinder {
|
||||||
|
public static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[]{
|
||||||
|
let foundPath = false;
|
||||||
|
let frontier = [];
|
||||||
|
frontier.unshift(start);
|
||||||
|
|
||||||
|
let cameFrom = new Map<T, T>();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
|
||||||
|
while (frontier.length > 0){
|
||||||
|
let current = frontier.shift();
|
||||||
|
if (JSON.stringify(current) == JSON.stringify(goal)){
|
||||||
|
foundPath = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.getNeighbors(current).forEach(next => {
|
||||||
|
if (!this.hasKey(cameFrom, next)){
|
||||||
|
frontier.unshift(next);
|
||||||
|
cameFrom.set(next, current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPath ? AStarPathfinder.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static hasKey<T>(map: Map<T, T>, compareKey: T){
|
||||||
|
let iterator = map.keys();
|
||||||
|
let r: IteratorResult<T>;
|
||||||
|
while (r = iterator.next() , !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface IUnweightedGraph<T>{
|
||||||
|
/**
|
||||||
|
* getNeighbors方法应该返回从传入的节点可以到达的任何相邻节点。
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 一个未加权图的基本实现。所有的边都被缓存。这种类型的图最适合于非基于网格的图。
|
||||||
|
* 作为边添加的任何节点都必须在边字典中有一个条目作为键。
|
||||||
|
*/
|
||||||
|
class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||||
|
public edges: Map<T, T[]> = new Map<T, T[]>();
|
||||||
|
|
||||||
|
public addEdgesForNode(node: T, edges: T[]){
|
||||||
|
this.edges.set(node, edges);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNeighbors(node: T){
|
||||||
|
return this.edges.get(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
///<reference path="../../../Math/Point.ts" />
|
||||||
|
/**
|
||||||
|
* 基本的未加权网格图形用于BreadthFirstPathfinder
|
||||||
|
*/
|
||||||
|
class UnweightedGridGraph implements IUnweightedGraph<Point> {
|
||||||
|
private static readonly CARDINAL_DIRS: Point[] = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, -1)
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
public walls: Point[] = [];
|
||||||
|
|
||||||
|
private _width: number;
|
||||||
|
private _hegiht: number;
|
||||||
|
|
||||||
|
private _dirs: Point[];
|
||||||
|
private _neighbors: Point[] = new Array(4);
|
||||||
|
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch: boolean = false) {
|
||||||
|
this._width = width;
|
||||||
|
this._hegiht = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? UnweightedGridGraph.COMPASS_DIRS : UnweightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodeInBounds(node: Point): boolean {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._hegiht;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodePassable(node: Point): boolean {
|
||||||
|
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNeighbors(node: Point) {
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
|
||||||
|
this._dirs.forEach(dir => {
|
||||||
|
let next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||||
|
this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public search(start: Point, goal: Point): Point[] {
|
||||||
|
return BreadthFirstPathfinder.search(this, start, goal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
interface IWeightedGraph<T>{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param from
|
||||||
|
* @param to
|
||||||
|
*/
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
///<reference path="../../../Math/Point.ts" />
|
||||||
|
/**
|
||||||
|
* 支持一种加权节点的基本网格图
|
||||||
|
*/
|
||||||
|
class WeightedGridGraph implements IWeightedGraph<Point> {
|
||||||
|
public static readonly CARDINAL_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, 1)
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
public walls: Point[] = [];
|
||||||
|
public weightedNodes: Point[] = [];
|
||||||
|
public defaultWeight = 1;
|
||||||
|
public weightedNodeWeight = 5;
|
||||||
|
|
||||||
|
private _width: number;
|
||||||
|
private _height: number;
|
||||||
|
private _dirs: Point[];
|
||||||
|
private _neighbors: Point[] = new Array(4);
|
||||||
|
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch: boolean = false){
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? WeightedGridGraph.COMPASS_DIRS : WeightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodeInBounds(node: Point){
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodePassable(node: Point): boolean {
|
||||||
|
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
public search(start: Point, goal: Point){
|
||||||
|
return WeightedPathfinder.search(this, start, goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNeighbors(node: Point): Point[]{
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
|
||||||
|
this._dirs.forEach(dir => {
|
||||||
|
let next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||||
|
this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public cost(from: Point, to: Point): number{
|
||||||
|
return this.weightedNodes.find(t => JSON.stringify(t) == JSON.stringify(to)) ? this.weightedNodeWeight : this.defaultWeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
class WeightedNode<T> extends PriorityQueueNode {
|
||||||
|
public data: T;
|
||||||
|
|
||||||
|
constructor(data: T){
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WeightedPathfinder {
|
||||||
|
public static search<T>(graph: IWeightedGraph<T>, start: T, goal: T){
|
||||||
|
let foundPath = false;
|
||||||
|
|
||||||
|
let cameFrom = new Map<T, T>();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
|
||||||
|
let costSoFar = new Map<T, number>();
|
||||||
|
let frontier = new PriorityQueue<WeightedNode<T>>(1000);
|
||||||
|
frontier.enqueue(new WeightedNode<T>(start), 0);
|
||||||
|
|
||||||
|
costSoFar.set(start, 0);
|
||||||
|
|
||||||
|
while (frontier.count > 0){
|
||||||
|
let current = frontier.dequeue();
|
||||||
|
|
||||||
|
if (JSON.stringify(current.data) == JSON.stringify(goal)){
|
||||||
|
foundPath = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.getNeighbors(current.data).forEach(next => {
|
||||||
|
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||||
|
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)){
|
||||||
|
costSoFar.set(next, newCost);
|
||||||
|
let priprity = newCost;
|
||||||
|
frontier.enqueue(new WeightedNode<T>(next), priprity);
|
||||||
|
cameFrom.set(next, current.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static hasKey<T>(map: Map<T, number>, compareKey: T){
|
||||||
|
let iterator = map.keys();
|
||||||
|
let r: IteratorResult<T>;
|
||||||
|
while (r = iterator.next() , !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getKey<T>(map: Map<T, T>, compareKey: T){
|
||||||
|
let iterator = map.keys();
|
||||||
|
let valueIterator = map.values();
|
||||||
|
let r: IteratorResult<T>;
|
||||||
|
let v: IteratorResult<T>;
|
||||||
|
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return v.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[]{
|
||||||
|
let path = [];
|
||||||
|
let current = goal;
|
||||||
|
path.push(goal);
|
||||||
|
|
||||||
|
while (current != start){
|
||||||
|
current = this.getKey(cameFrom, current);
|
||||||
|
path.push(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.reverse();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
class DebugDefaults {
|
||||||
|
public static verletParticle = 0xDC345E;
|
||||||
|
public static verletConstraintEdge = 0x433E36;
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
abstract class Component {
|
abstract class Component {
|
||||||
public entity: Entity;
|
public entity: Entity;
|
||||||
public displayRender: egret.DisplayObject;
|
|
||||||
private _enabled: boolean = true;
|
private _enabled: boolean = true;
|
||||||
public updateInterval: number = 1;
|
public updateInterval: number = 1;
|
||||||
|
|
||||||
@@ -30,7 +29,9 @@ abstract class Component {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract initialize();
|
public initialize(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public onAddedToEntity(){
|
public onAddedToEntity(){
|
||||||
|
|
||||||
@@ -56,11 +57,8 @@ abstract class Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 绑定显示对象 */
|
public debugRender(){
|
||||||
public bind(displayRender: egret.DisplayObject){
|
|
||||||
this.displayRender = displayRender;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 内部使用 运行时不应该调用 */
|
/** 内部使用 运行时不应该调用 */
|
||||||
|
|||||||
@@ -8,6 +8,42 @@ class Camera extends Component {
|
|||||||
private _minimumZoom = 0.3;
|
private _minimumZoom = 0.3;
|
||||||
private _maximumZoom = 3;
|
private _maximumZoom = 3;
|
||||||
private _areMatrixesDirty = true;
|
private _areMatrixesDirty = true;
|
||||||
|
private _inset: CameraInset;
|
||||||
|
private _bounds: Rectangle;
|
||||||
|
private _areBoundsDirty = true;
|
||||||
|
|
||||||
|
public get bounds(){
|
||||||
|
if (this._areMatrixesDirty)
|
||||||
|
this.updateMatrixes();
|
||||||
|
|
||||||
|
if (this._areBoundsDirty){
|
||||||
|
let stage = this.entity.scene.stage;
|
||||||
|
let topLeft = this.screenToWorldPoint(new Vector2(this._inset.left, this._inset.top));
|
||||||
|
let bottomRight = this.screenToWorldPoint(new Vector2(stage.stageWidth - this._inset.right, stage.stageHeight - this._inset.bottom));
|
||||||
|
|
||||||
|
if (this.entity.transform.rotation != 0){
|
||||||
|
let topRight = this.screenToWorldPoint(new Vector2(stage.stageWidth - this._inset.right, this._inset.top));
|
||||||
|
let bottomLeft = this.screenToWorldPoint(new Vector2(this._inset.left, stage.stageHeight - this._inset.bottom));
|
||||||
|
|
||||||
|
let minX = MathHelper.minOf(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x);
|
||||||
|
let maxX = MathHelper.maxOf(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x);
|
||||||
|
let minY = MathHelper.minOf(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y);
|
||||||
|
let maxY = MathHelper.maxOf(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y);
|
||||||
|
|
||||||
|
this._bounds.location = new Vector2(minX, minY);
|
||||||
|
this._bounds.width = maxX - minX;
|
||||||
|
this._bounds.height = maxY - minY;
|
||||||
|
}else{
|
||||||
|
this._bounds.location = topLeft;
|
||||||
|
this._bounds.width = bottomRight.x - topLeft.x;
|
||||||
|
this._bounds.height = bottomRight.y - topLeft.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._areBoundsDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._bounds;
|
||||||
|
}
|
||||||
|
|
||||||
public get zoom(){
|
public get zoom(){
|
||||||
if (this._zoom == 0)
|
if (this._zoom == 0)
|
||||||
@@ -51,11 +87,17 @@ class Camera extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get transformMatrix(){
|
public get transformMatrix(){
|
||||||
|
if (this._areBoundsDirty)
|
||||||
this.updateMatrixes();
|
this.updateMatrixes();
|
||||||
|
|
||||||
return this._transformMatrix;
|
return this._transformMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get inverseTransformMatrix(){
|
||||||
|
if (this._areBoundsDirty)
|
||||||
|
this.updateMatrixes();
|
||||||
|
return this._inverseTransformMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -98,14 +140,7 @@ class Camera extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public update(){
|
public update(){
|
||||||
SceneManager.getActiveScene().entities.buffer.forEach(entity => entity.components.buffer.forEach(component => {
|
|
||||||
if (component.displayRender){
|
|
||||||
let has = this.entity.scene.$children.indexOf(component.displayRender)
|
|
||||||
if (has == -1){
|
|
||||||
this.entity.scene.stage.addChild(component.displayRender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPosition(position: Vector2){
|
public setPosition(position: Vector2){
|
||||||
@@ -130,10 +165,28 @@ class Camera extends Component {
|
|||||||
|
|
||||||
this._inverseTransformMatrix = Matrix2D.invert(this._transformMatrix);
|
this._inverseTransformMatrix = Matrix2D.invert(this._transformMatrix);
|
||||||
|
|
||||||
|
this._areBoundsDirty = true;
|
||||||
this._areMatrixesDirty = false;
|
this._areMatrixesDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public screenToWorldPoint(screenPosition: Vector2){
|
||||||
|
this.updateMatrixes();
|
||||||
|
return Vector2.transform(screenPosition, this._inverseTransformMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public worldToScreenPoint(worldPosition: Vector2){
|
||||||
|
this.updateMatrixes();
|
||||||
|
return Vector2.transform(worldPosition, this._transformMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
public destory() {
|
public destory() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CameraInset {
|
||||||
|
public left;
|
||||||
|
public right;
|
||||||
|
public top;
|
||||||
|
public bottom;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
class Mesh extends Component {
|
||||||
|
private _verts: VertexPosition[];
|
||||||
|
private _primitiveCount: number;
|
||||||
|
private _triangles: number[];
|
||||||
|
private _topLeftVertPosition: Vector2;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
|
||||||
|
public initialize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public setVertPosition(positions: Vector2[]){
|
||||||
|
let createVerts = !this._verts || this._verts.length != positions.length;
|
||||||
|
if (createVerts)
|
||||||
|
this._verts = new Array(positions.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < this._verts.length; i ++){
|
||||||
|
this._verts[i] = new VertexPosition(positions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setTriangles(triangles: number[]){
|
||||||
|
this._primitiveCount = triangles.length / 3;
|
||||||
|
this._triangles = triangles;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public recalculateBounds(){
|
||||||
|
this._topLeftVertPosition = new Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
|
||||||
|
let max = new Vector2(Number.MIN_VALUE, Number.MIN_VALUE);
|
||||||
|
|
||||||
|
for (let i = 0; i < this._verts.length; i ++){
|
||||||
|
this._topLeftVertPosition.x = Math.min(this._topLeftVertPosition.x, this._verts[i].position.x);
|
||||||
|
this._topLeftVertPosition.y = Math.min(this._topLeftVertPosition.y, this._verts[i].position.y);
|
||||||
|
max.x = Math.max(max.x, this._verts[i].position.x);
|
||||||
|
max.y = Math.max(max.y, this._verts[i].position.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._width = max.x - this._topLeftVertPosition.x;
|
||||||
|
this._height = max.y - this._topLeftVertPosition.y;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VertexPosition{
|
||||||
|
public position: Vector2;
|
||||||
|
|
||||||
|
constructor(position: Vector2){
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
///<reference path="./Collider.ts" />
|
||||||
|
class BoxCollider extends Collider {
|
||||||
|
public get width(){
|
||||||
|
return (this.shape as Box).width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set width(value: number){
|
||||||
|
this.setWidth(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setWidth(width: number): BoxCollider{
|
||||||
|
this._colliderRequiresAutoSizing = false;
|
||||||
|
let box = this.shape as Box;
|
||||||
|
if (width != box.width){
|
||||||
|
box.updateBox(width, box.height);
|
||||||
|
this._isPositionDirty = true;
|
||||||
|
if (this.entity && this._isParentEntityAddedToScene)
|
||||||
|
Physics.updateCollider(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get height(){
|
||||||
|
return (this.shape as Box).height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set height(value: number){
|
||||||
|
this.setHeight(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setHeight(height: number){
|
||||||
|
this._colliderRequiresAutoSizing = false;
|
||||||
|
let box = this.shape as Box;
|
||||||
|
if (height != box.height){
|
||||||
|
box.updateBox(box.width, height);
|
||||||
|
this._isPositionDirty = true;
|
||||||
|
if (this.entity && this._isParentEntityAddedToScene)
|
||||||
|
Physics.updateCollider(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.shape = new Box(1, 1);
|
||||||
|
this._colliderRequiresAutoSizing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSize(width: number, height: number){
|
||||||
|
this._colliderRequiresAutoSizing = false;
|
||||||
|
let box = this.shape as Box;
|
||||||
|
if (width != box.width || height != box.height){
|
||||||
|
box.updateBox(width, height);
|
||||||
|
this._isPositionDirty = true;
|
||||||
|
if (this.entity && this._isParentEntityAddedToScene)
|
||||||
|
Physics.updateCollider(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
abstract class Collider extends Component{
|
||||||
|
public shape: Shape;
|
||||||
|
public physicsLayer = 1 << 0;
|
||||||
|
public isTrigger: boolean;
|
||||||
|
public registeredPhysicsBounds: Rectangle;
|
||||||
|
public shouldColliderScaleAndRotationWithTransform = true;
|
||||||
|
public collidesWithLayers = Physics.allLayers;
|
||||||
|
|
||||||
|
public _localOffsetLength: number;
|
||||||
|
public _isPositionDirty = true;
|
||||||
|
public _isRotationDirty = true;
|
||||||
|
protected _isParentEntityAddedToScene;
|
||||||
|
protected _colliderRequiresAutoSizing;
|
||||||
|
protected _localOffset: Vector2 = new Vector2(0, 0);
|
||||||
|
protected _isColliderRegistered;
|
||||||
|
|
||||||
|
public get bounds(): Rectangle {
|
||||||
|
if (this._isPositionDirty || this._isRotationDirty){
|
||||||
|
this.shape.recalculateBounds(this);
|
||||||
|
this._isPositionDirty = this._isRotationDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.shape.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get localOffset(){
|
||||||
|
return this._localOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set localOffset(value: Vector2){
|
||||||
|
this.setLocalOffset(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLocalOffset(offset: Vector2){
|
||||||
|
if (this._localOffset != offset){
|
||||||
|
this.unregisterColliderWithPhysicsSystem();
|
||||||
|
this._localOffset = offset;
|
||||||
|
this._localOffsetLength = this._localOffset.length();
|
||||||
|
this._isPositionDirty = true;
|
||||||
|
this.registerColliderWithPhysicsSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerColliderWithPhysicsSystem(){
|
||||||
|
if (this._isParentEntityAddedToScene && !this._isColliderRegistered){
|
||||||
|
Physics.addCollider(this);
|
||||||
|
this._isColliderRegistered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unregisterColliderWithPhysicsSystem(){
|
||||||
|
if (this._isParentEntityAddedToScene && this._isColliderRegistered){
|
||||||
|
Physics.removeCollider(this);
|
||||||
|
}
|
||||||
|
this._isColliderRegistered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public overlaps(other: Collider){
|
||||||
|
return this.shape.overlaps(other.shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collidesWith(collider: Collider, motion: Vector2){
|
||||||
|
let oldPosition = this.shape.position;
|
||||||
|
this.shape.position = Vector2.add(this.shape.position, motion);
|
||||||
|
|
||||||
|
let result = this.shape.collidesWithShape(collider.shape);
|
||||||
|
if (result)
|
||||||
|
result.collider = collider;
|
||||||
|
|
||||||
|
this.shape.position = oldPosition;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onAddedToEntity(){
|
||||||
|
if (this._colliderRequiresAutoSizing){
|
||||||
|
if (!(this instanceof BoxCollider)){
|
||||||
|
console.error("Only box and circle colliders can be created automatically");
|
||||||
|
}
|
||||||
|
|
||||||
|
let renderable = this.entity.getComponent<RenderableComponent>(RenderableComponent);
|
||||||
|
if (renderable){
|
||||||
|
let renderbaleBounds = renderable.bounds;
|
||||||
|
|
||||||
|
let width = renderbaleBounds.width / this.entity.transform.scale.x;
|
||||||
|
let height = renderbaleBounds.height / this.entity.transform.scale.y;
|
||||||
|
|
||||||
|
if (this instanceof BoxCollider){
|
||||||
|
let boxCollider = this as BoxCollider;
|
||||||
|
boxCollider.width = width;
|
||||||
|
boxCollider.height = height;
|
||||||
|
|
||||||
|
this.localOffset = Vector2.subtract(renderbaleBounds.center, this.entity.transform.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isParentEntityAddedToScene = true;
|
||||||
|
this.registerColliderWithPhysicsSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onRemovedFromEntity(){
|
||||||
|
this.unregisterColliderWithPhysicsSystem();
|
||||||
|
this._isParentEntityAddedToScene = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onEntityTransformChanged(comp: ComponentTransform){
|
||||||
|
switch (comp){
|
||||||
|
case ComponentTransform.position:
|
||||||
|
this._isPositionDirty = true;
|
||||||
|
break;
|
||||||
|
case ComponentTransform.scale:
|
||||||
|
this._isPositionDirty = true;
|
||||||
|
break;
|
||||||
|
case ComponentTransform.rotation:
|
||||||
|
this._isRotationDirty = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._isColliderRegistered)
|
||||||
|
Physics.updateCollider(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onEnabled(){
|
||||||
|
this.registerColliderWithPhysicsSystem();
|
||||||
|
this._isPositionDirty = this._isRotationDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDisabled(){
|
||||||
|
this.unregisterColliderWithPhysicsSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
interface ITriggerListener {
|
||||||
|
onTriggerEnter(other: Collider, local: Collider);
|
||||||
|
onTriggerExit(other: Collider, local: Collider);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
class Mover extends Component {
|
||||||
|
private _triggerHelper: ColliderTriggerHelper;
|
||||||
|
|
||||||
|
public onAddedToEntity(){
|
||||||
|
this._triggerHelper = new ColliderTriggerHelper(this.entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public calculateMovement(motion: Vector2){
|
||||||
|
let collisionResult = new CollisionResult();
|
||||||
|
|
||||||
|
if (!this.entity.getComponent(Collider) || !this._triggerHelper){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let colliders: Collider[] = this.entity.getComponents(Collider);
|
||||||
|
for (let i = 0; i < colliders.length; i ++){
|
||||||
|
let collider = colliders[i];
|
||||||
|
|
||||||
|
if (collider.isTrigger)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let bounds = collider.bounds;
|
||||||
|
bounds.x += motion.x;
|
||||||
|
bounds.y += motion.y;
|
||||||
|
let neighbors = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers);
|
||||||
|
|
||||||
|
for (let j = 0; j < neighbors.length; j ++){
|
||||||
|
let neighbor = neighbors[j];
|
||||||
|
if (neighbor.isTrigger)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let _internalcollisionResult = collider.collidesWith(neighbor, motion);
|
||||||
|
if (_internalcollisionResult){
|
||||||
|
motion = Vector2.subtract(motion, _internalcollisionResult.minimumTranslationVector);
|
||||||
|
|
||||||
|
if (_internalcollisionResult.collider){
|
||||||
|
collisionResult = _internalcollisionResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListPool.free(colliders);
|
||||||
|
|
||||||
|
return collisionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public applyMovement(motion: Vector2){
|
||||||
|
this.entity.transform.position = Vector2.add(this.entity.transform.position, motion);
|
||||||
|
|
||||||
|
if (this._triggerHelper)
|
||||||
|
this._triggerHelper.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public move(motion: Vector2){
|
||||||
|
let collisionResult = this.calculateMovement(motion);
|
||||||
|
|
||||||
|
this.applyMovement(motion);
|
||||||
|
|
||||||
|
return collisionResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
class PolygonMesh extends Mesh {
|
||||||
|
constructor(points: Vector2[], arePointsCCW: boolean = true){
|
||||||
|
super();
|
||||||
|
|
||||||
|
let triangulator = new Triangulator();
|
||||||
|
triangulator.triangulate(points, arePointsCCW);
|
||||||
|
|
||||||
|
this.setVertPosition(points);
|
||||||
|
this.setTriangles(triangulator.triangleIndices);
|
||||||
|
this.recalculateBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 所有可渲染组件的基类
|
||||||
|
*/
|
||||||
|
abstract class RenderableComponent extends Component {
|
||||||
|
private _isVisible: boolean;
|
||||||
|
private _areBoundsDirty = true;
|
||||||
|
private _bounds: Rectangle;
|
||||||
|
private _localOffset: Vector2;
|
||||||
|
|
||||||
|
public get width(){
|
||||||
|
return this.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get height(){
|
||||||
|
return this.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isVisible(){
|
||||||
|
return this._isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set isVisible(value: boolean){
|
||||||
|
this._isVisible = value;
|
||||||
|
|
||||||
|
if (this._isVisible)
|
||||||
|
this.onBecameVisible();
|
||||||
|
else
|
||||||
|
this.onBecameInvisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get bounds(): Rectangle{
|
||||||
|
return this.getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getWidth(){
|
||||||
|
return this.bounds.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getHeight(){
|
||||||
|
return this.bounds.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getBounds(){
|
||||||
|
if (this._areBoundsDirty){
|
||||||
|
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, new Vector2(0, 0),
|
||||||
|
this.entity.transform.scale, this.entity.transform.rotation, this.width, this.height);
|
||||||
|
this._areBoundsDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onBecameVisible(){}
|
||||||
|
|
||||||
|
protected onBecameInvisible(){}
|
||||||
|
|
||||||
|
public isVisibleFromCamera(camera: Camera): boolean{
|
||||||
|
this.isVisible = camera.bounds.intersects(this.bounds);
|
||||||
|
return this.isVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
class SpriteRenderer extends RenderableComponent {
|
||||||
|
private _sprite: egret.DisplayObject;
|
||||||
|
private _origin: Vector2;
|
||||||
|
|
||||||
|
public get sprite(){
|
||||||
|
return this._sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set sprite(value: egret.DisplayObject){
|
||||||
|
this.setSprite(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSprite(sprite: egret.DisplayObject): SpriteRenderer{
|
||||||
|
this._sprite = sprite;
|
||||||
|
if (this._sprite)
|
||||||
|
this._origin = new Vector2(this._sprite.anchorOffsetX, this._sprite.anchorOffsetY);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -212,6 +212,10 @@ class Entity {
|
|||||||
return this.components.getComponent(type, false) as T;
|
return this.components.getComponent(type, false) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getComponents(typeName: string | any, componentList?){
|
||||||
|
return this.components.getComponents(typeName, componentList);
|
||||||
|
}
|
||||||
|
|
||||||
public removeComponentForType<T extends Component>(type){
|
public removeComponentForType<T extends Component>(type){
|
||||||
let comp = this.getComponent<T>(type);
|
let comp = this.getComponent<T>(type);
|
||||||
if (comp){
|
if (comp){
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ class Scene extends egret.DisplayObjectContainer {
|
|||||||
/** 初始化场景 */
|
/** 初始化场景 */
|
||||||
public initialize(){
|
public initialize(){
|
||||||
/** 初始化默认相机 */
|
/** 初始化默认相机 */
|
||||||
this.camera = this.createEntity("camera").addComponent(new Camera());
|
this.camera = this.createEntity("camera").getOrCreateComponent(new Camera());
|
||||||
|
|
||||||
|
Physics.reset();
|
||||||
|
|
||||||
if (this.entityProcessors)
|
if (this.entityProcessors)
|
||||||
this.entityProcessors.begin();
|
this.entityProcessors.begin();
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Transform {
|
|||||||
|
|
||||||
constructor(entity: Entity){
|
constructor(entity: Entity){
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this._scale = this._localScale = Vector2.One;
|
this._scale = this._localScale = Vector2.one;
|
||||||
this._children = [];
|
this._children = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,14 +203,6 @@ class Transform {
|
|||||||
this.localScale = scale;
|
this.localScale = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.entity.components.buffer.length; i ++){
|
|
||||||
let component = this.entity.components.buffer[i];
|
|
||||||
if (component.displayRender){
|
|
||||||
component.displayRender.scaleX = this.scale.x;
|
|
||||||
component.displayRender.scaleY = this.scale.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,13 +226,6 @@ class Transform {
|
|||||||
this.localRotation = radians;
|
this.localRotation = radians;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.entity.components.buffer.length; i ++){
|
|
||||||
let component = this.entity.components.buffer[i];
|
|
||||||
if (component.displayRender){
|
|
||||||
component.displayRender.rotation = this.rotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,14 +255,6 @@ class Transform {
|
|||||||
this.localPosition = position;
|
this.localPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.entity.components.buffer.length; i ++){
|
|
||||||
let component = this.entity.components.buffer[i];
|
|
||||||
if (component.displayRender){
|
|
||||||
component.displayRender.x = this.entity.scene.camera.transformMatrix.m31 + this.position.x;
|
|
||||||
component.displayRender.y = this.entity.scene.camera.transformMatrix.m32 + this.position.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,8 +120,46 @@ class ComponentList {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getComponents(typeName: string | any, components?){
|
||||||
|
if (!components)
|
||||||
|
components = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this._components.length; i ++){
|
||||||
|
let component = this._components[i];
|
||||||
|
if (typeof(typeName) == "string"){
|
||||||
|
if (egret.is(component, typeName)){
|
||||||
|
components.push(component);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (component instanceof typeName){
|
||||||
|
components.push(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._componentsToAdd.length; i ++){
|
||||||
|
let component = this._componentsToAdd[i];
|
||||||
|
if (typeof(typeName) == "string"){
|
||||||
|
if (egret.is(component, typeName)){
|
||||||
|
components.push(component);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (component instanceof typeName){
|
||||||
|
components.push(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
public update(){
|
public update(){
|
||||||
this.updateLists();
|
this.updateLists();
|
||||||
|
for (let i = 0; i < this._components.length; i ++){
|
||||||
|
let component = this._components[i];
|
||||||
|
if (component.enabled && (component.updateInterval == 1 || Time.frameCount % component.updateInterval == 0))
|
||||||
|
component.update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onEntityTransformChanged(comp){
|
public onEntityTransformChanged(comp){
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class EntityList{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public add(entity: Entity){
|
public add(entity: Entity){
|
||||||
|
if (this._entitiesToAdded.indexOf(entity) == -1)
|
||||||
this._entitiesToAdded.push(entity);
|
this._entitiesToAdded.push(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,10 +109,12 @@ class EntityList{
|
|||||||
this._entitiesToAdded = this._tempEntityList;
|
this._entitiesToAdded = this._tempEntityList;
|
||||||
this._tempEntityList = temp;
|
this._tempEntityList = temp;
|
||||||
this._tempEntityList.forEach(entity => {
|
this._tempEntityList.forEach(entity => {
|
||||||
|
if (!this._entities.contains(entity)){
|
||||||
this._entities.push(entity);
|
this._entities.push(entity);
|
||||||
entity.scene = this.scene;
|
entity.scene = this.scene;
|
||||||
|
|
||||||
this.scene.entityProcessors.onEntityAdded(entity)
|
this.scene.entityProcessors.onEntityAdded(entity)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._tempEntityList.forEach(entity => entity.onAddedToScene());
|
this._tempEntityList.forEach(entity => entity.onAddedToScene());
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
class Time {
|
class Time {
|
||||||
public static unscaledDeltaTime;
|
public static unscaledDeltaTime;
|
||||||
public static deltaTime: number;
|
public static deltaTime: number = 0;
|
||||||
public static timeScale = 1;
|
public static timeScale = 1;
|
||||||
|
public static frameCount = 0;;
|
||||||
|
|
||||||
private static _lastTime = 0;
|
private static _lastTime = 0;
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ class Time {
|
|||||||
let dt = (currentTime - this._lastTime) / 1000;
|
let dt = (currentTime - this._lastTime) / 1000;
|
||||||
this.deltaTime = dt * this.timeScale;
|
this.deltaTime = dt * this.timeScale;
|
||||||
this.unscaledDeltaTime = dt;
|
this.unscaledDeltaTime = dt;
|
||||||
|
this.frameCount ++;
|
||||||
|
|
||||||
this._lastTime = currentTime;
|
this._lastTime = currentTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* 帮助处理位掩码的实用程序类
|
||||||
|
* 除了isFlagSet之外,所有方法都期望flag参数是一个非移位的标志
|
||||||
|
* 允许您使用普通的(0、1、2、3等)来设置/取消您的标记
|
||||||
|
*/
|
||||||
|
class Flags {
|
||||||
|
/**
|
||||||
|
* 检查位标志是否已在数值中设置
|
||||||
|
* 检查期望标志是否已经移位
|
||||||
|
* @param self
|
||||||
|
* @param flag
|
||||||
|
*/
|
||||||
|
public static isFlagSet(self: number, flag: number){
|
||||||
|
return (self & flag) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查位标志是否在数值中设置
|
||||||
|
* @param self
|
||||||
|
* @param flag
|
||||||
|
*/
|
||||||
|
public static isUnshiftedFlagSet(self: number, flag: number){
|
||||||
|
flag = 1 << flag;
|
||||||
|
return (self & flag) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置数值标志位,移除所有已经设置的标志
|
||||||
|
* @param self
|
||||||
|
* @param flag
|
||||||
|
*/
|
||||||
|
public static setFlagExclusive(self: number, flag: number){
|
||||||
|
return 1 << flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置标志位
|
||||||
|
* @param self
|
||||||
|
* @param flag
|
||||||
|
*/
|
||||||
|
public static setFlag(self: number, flag: number){
|
||||||
|
return (self | 1 << flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消标志位
|
||||||
|
* @param self
|
||||||
|
* @param flag
|
||||||
|
*/
|
||||||
|
public static unsetFlag(self: number, flag: number){
|
||||||
|
flag = 1 << flag;
|
||||||
|
return (self & (~flag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反转数值集合位
|
||||||
|
* @param self
|
||||||
|
*/
|
||||||
|
public static invertFlags(self: number){
|
||||||
|
return ~self;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
class MathHelper {
|
class MathHelper {
|
||||||
|
public static readonly Epsilon: number = 0.00001;
|
||||||
|
public static readonly Rad2Deg = 57.29578;
|
||||||
|
public static readonly Deg2Rad = 0.0174532924;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将弧度转换成角度。
|
* 将弧度转换成角度。
|
||||||
* @param radians 用弧度表示的角
|
* @param radians 用弧度表示的角
|
||||||
@@ -27,6 +31,10 @@ class MathHelper {
|
|||||||
return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin);
|
return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static lerp(value1: number, value2: number, amount: number){
|
||||||
|
return value1 + (value2 - value1) * amount;
|
||||||
|
}
|
||||||
|
|
||||||
public static clamp(value: number, min: number, max: number){
|
public static clamp(value: number, min: number, max: number){
|
||||||
if (value < min)
|
if (value < min)
|
||||||
return min;
|
return min;
|
||||||
@@ -36,4 +44,17 @@ class MathHelper {
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static minOf(a: number, b: number, c: number, d: number){
|
||||||
|
return Math.min(a, Math.min(b, Math.min(c, d)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static maxOf(a: number, b: number, c: number, d: number){
|
||||||
|
return Math.max(a, Math.max(b, Math.max(c, d)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static pointOnCirlce(circleCenter: Vector2, radius: number, angleInDegrees: number){
|
||||||
|
let radians = MathHelper.toRadians(angleInDegrees);
|
||||||
|
return new Vector2(Math.cos(radians) * radians + circleCenter.x, Math.sin(radians) * radians + circleCenter.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@ class Point {
|
|||||||
public x: number;
|
public x: number;
|
||||||
public y: number;
|
public y: number;
|
||||||
|
|
||||||
constructor(x: number, y: number){
|
constructor(x?: number, y?: number){
|
||||||
this.x = x;
|
this.x = x ? x : 0;
|
||||||
this.y = y;
|
this.y = y ? y : this.x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,175 @@ class Rectangle {
|
|||||||
public width: number;
|
public width: number;
|
||||||
public height: number;
|
public height: number;
|
||||||
|
|
||||||
constructor(x: number, y: number, width: number, height: number){
|
private _tempMat: Matrix2D;
|
||||||
this.x = x;
|
private _transformMat: Matrix2D;
|
||||||
this.y = y;
|
|
||||||
this.width = width;
|
public get left() {
|
||||||
this.height = height;
|
return this.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get right() {
|
||||||
|
return this.x + this.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get top() {
|
||||||
|
return this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get bottom() {
|
||||||
|
return this.y + this.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get center(){
|
||||||
|
return new Vector2(this.x + (this.width / 2), this.y + (this.height / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public get location() {
|
||||||
|
return new Vector2(this.x, this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set location(value: Vector2) {
|
||||||
|
this.x = value.x;
|
||||||
|
this.y = value.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(x?: number, y?: number, width?: number, height?: number) {
|
||||||
|
this.x = x ? x : 0;
|
||||||
|
this.y = y ? y : 0;
|
||||||
|
this.width = width ? width : 0;
|
||||||
|
this.height = height ? height : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public intersects(value: Rectangle) {
|
||||||
|
return value.left < this.right &&
|
||||||
|
this.left < value.right &&
|
||||||
|
value.top < this.bottom &&
|
||||||
|
this.top < value.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public contains(value: Vector2) {
|
||||||
|
return ((((this.x <= value.x) && (value.x < (this.x + this.width))) &&
|
||||||
|
(this.y <= value.y)) &&
|
||||||
|
(value.y < (this.y + this.height)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromMinMax(minX: number, minY: number, maxX: number, maxY: number) {
|
||||||
|
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getClosestPointOnRectangleBorderToPoint(point: Point): {res: Vector2, edgeNormal: Vector2} {
|
||||||
|
let edgeNormal = new Vector2(0, 0);
|
||||||
|
|
||||||
|
let res = new Vector2(0, 0);
|
||||||
|
res.x = MathHelper.clamp(point.x, this.left, this.right);
|
||||||
|
res.y = MathHelper.clamp(point.y, this.top, this.bottom);
|
||||||
|
|
||||||
|
if (this.contains(res)){
|
||||||
|
let dl = res.x - this.left;
|
||||||
|
let dr = this.right - res.x;
|
||||||
|
let dt = res.y - this.top;
|
||||||
|
let db = this.bottom - res.y;
|
||||||
|
|
||||||
|
let min = MathHelper.minOf(dl, dr, dt, db);
|
||||||
|
if (min == dt){
|
||||||
|
res.y = this.top;
|
||||||
|
edgeNormal.y = -1;
|
||||||
|
} else if(min == db){
|
||||||
|
res.y = this.bottom;
|
||||||
|
edgeNormal.y = 1;
|
||||||
|
} else if(min == dl){
|
||||||
|
res.x = this.left;
|
||||||
|
edgeNormal.x = -1;
|
||||||
|
} else{
|
||||||
|
res.x = this.right;
|
||||||
|
edgeNormal.x = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (res.x == this.left){
|
||||||
|
edgeNormal.x = -1;
|
||||||
|
}
|
||||||
|
if (res.x == this.right){
|
||||||
|
edgeNormal.x = 1;
|
||||||
|
}
|
||||||
|
if (res.y == this.top){
|
||||||
|
edgeNormal.y = -1;
|
||||||
|
}
|
||||||
|
if (res.y == this.bottom){
|
||||||
|
edgeNormal.y = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {res: res, edgeNormal: edgeNormal};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
} 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);
|
||||||
|
this._transformMat = Matrix2D.multiply(this._transformMat, this._tempMat);
|
||||||
|
this._tempMat = Matrix2D.createRotation(rotation);
|
||||||
|
this._transformMat = Matrix2D.multiply(this._transformMat, this._tempMat);
|
||||||
|
this._tempMat = Matrix2D.createTranslation(worldPosX, worldPosY);
|
||||||
|
this._transformMat = Matrix2D.multiply(this._transformMat, this._tempMat);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
topLeft = Vector2.transform(topLeft, this._transformMat);
|
||||||
|
topRight = Vector2.transform(topRight, this._transformMat);
|
||||||
|
bottomLeft = Vector2.transform(bottomLeft, this._transformMat);
|
||||||
|
bottomRight = Vector2.transform(bottomRight, this._transformMat);
|
||||||
|
|
||||||
|
let minX = MathHelper.minOf(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x);
|
||||||
|
let maxX = MathHelper.maxOf(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x);
|
||||||
|
let minY = MathHelper.minOf(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y);
|
||||||
|
let maxY = MathHelper.maxOf(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y);
|
||||||
|
|
||||||
|
this.location = new Vector2(minX, minY);
|
||||||
|
this.width = maxX - minX;
|
||||||
|
this.height = maxY - minY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给定多边形的点,计算边界
|
||||||
|
* @param points
|
||||||
|
*/
|
||||||
|
public static rectEncompassingPoints(points: Vector2[]){
|
||||||
|
let minX = Number.POSITIVE_INFINITY;
|
||||||
|
let minY = Number.POSITIVE_INFINITY;
|
||||||
|
let maxX = Number.NEGATIVE_INFINITY;
|
||||||
|
let maxY = Number.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i ++){
|
||||||
|
let pt = points[i];
|
||||||
|
|
||||||
|
if (pt.x < minX){
|
||||||
|
minX = pt.x;
|
||||||
|
}
|
||||||
|
if (pt.x > maxX){
|
||||||
|
maxX = pt.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt.y < minY){
|
||||||
|
minY = pt.y;
|
||||||
|
}
|
||||||
|
if (pt.y > maxY){
|
||||||
|
maxY = pt.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.fromMinMax(minX, minY, maxX, maxY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+61
-20
@@ -3,15 +3,24 @@ class Vector2 {
|
|||||||
public x: number = 0;
|
public x: number = 0;
|
||||||
public y: number = 0;
|
public y: number = 0;
|
||||||
|
|
||||||
private static readonly zeroVector = new Vector2(0, 0);
|
private static readonly unitYVector = new Vector2(0, 1);
|
||||||
private static readonly unitVector = new Vector2(1, 1);
|
private static readonly unitXVector = new Vector2(1, 0);
|
||||||
|
private static readonly unitVector2 = new Vector2(1, 1);
|
||||||
public static get One(){
|
private static readonly zeroVector2 = new Vector2(0, 0);
|
||||||
return this.unitVector;
|
public static get zero(){
|
||||||
|
return Vector2.zeroVector2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static get Zero(){
|
public static get one(){
|
||||||
return this.zeroVector;
|
return Vector2.unitVector2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get unitX(){
|
||||||
|
return Vector2.unitXVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get unitY(){
|
||||||
|
return Vector2.unitYVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,32 +28,36 @@ class Vector2 {
|
|||||||
* @param x 二维空间中的x坐标
|
* @param x 二维空间中的x坐标
|
||||||
* @param y 二维空间的y坐标
|
* @param y 二维空间的y坐标
|
||||||
*/
|
*/
|
||||||
constructor(x: number, y: number){
|
constructor(x? : number, y?: number){
|
||||||
this.x = x;
|
this.x = x ? x : 0;
|
||||||
this.y = y;
|
this.y = y ? y : this.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static add(value1: Vector2, value2: Vector2){
|
public static add(value1: Vector2, value2: Vector2){
|
||||||
value1.x += value2.x;
|
let result: Vector2 = new Vector2(0, 0);
|
||||||
value1.y += value2.y;
|
result.x = value1.x + value2.x;
|
||||||
return value1;
|
result.y = value1.y + value2.y;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static divide(value1: Vector2, value2: Vector2){
|
public static divide(value1: Vector2, value2: Vector2){
|
||||||
value1.x /= value2.x;
|
let result: Vector2 = new Vector2(0, 0);
|
||||||
value1.y /= value2.y;
|
result.x = value1.x / value2.x;
|
||||||
|
result.y = value1.y / value2.y;
|
||||||
return value1;
|
return value1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static multiply(value1: Vector2, value2: Vector2){
|
public static multiply(value1: Vector2, value2: Vector2){
|
||||||
value1.x *= value2.x;
|
let result: Vector2 = new Vector2(0, 0);
|
||||||
value1.y *= value2.y;
|
result.x = value1.x * value2.x;
|
||||||
return value1;
|
result.y = value1.y * value2.y;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static subtract(value1: Vector2, value2: Vector2){
|
public static subtract(value1: Vector2, value2: Vector2){
|
||||||
value1.x -= value2.x;
|
let result: Vector2 = new Vector2(0, 0);
|
||||||
value1.y -= value2.y;
|
result.x = value1.x - value2.x;
|
||||||
|
result.y = value1.y - value2.y;
|
||||||
return value1;
|
return value1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +68,17 @@ class Vector2 {
|
|||||||
this.y *= val;
|
this.y *= val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public length(){
|
||||||
|
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static normalize(value: Vector2){
|
||||||
|
let val = 1 / Math.sqrt((value.x * value.x) + (value.y * value.y));
|
||||||
|
value.x *= val;
|
||||||
|
value.y *= val;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回两个向量的点积
|
* 返回两个向量的点积
|
||||||
* @param value1
|
* @param value1
|
||||||
@@ -74,7 +98,24 @@ class Vector2 {
|
|||||||
return (v1 * v1) + (v2 * v2);
|
return (v1 * v1) + (v2 * v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static lerp(value1: Vector2, value2: Vector2, amount: number){
|
||||||
|
return new Vector2(MathHelper.lerp(value1.x, value2.x, amount), MathHelper.lerp(value1.y, value2.y, amount));
|
||||||
|
}
|
||||||
|
|
||||||
public static transform(position: Vector2, matrix: Matrix2D){
|
public static transform(position: Vector2, matrix: Matrix2D){
|
||||||
return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21), (position.x * matrix.m12) + (position.y * matrix.m22));
|
return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21), (position.x * matrix.m12) + (position.y * matrix.m22));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static distance(value1: Vector2, value2: Vector2){
|
||||||
|
let v1 = value1.x - value2.x, v2 = value1.y - value2.y;
|
||||||
|
return Math.sqrt((v1 * v1) + (v2 * v2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static negate(value: Vector2){
|
||||||
|
let result: Vector2 = new Vector2();
|
||||||
|
result.x = -value.x;
|
||||||
|
result.y = -value.y;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Vendored
+1092
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
|||||||
|
class ColliderTriggerHelper {
|
||||||
|
private _entity: Entity;
|
||||||
|
/** 存储当前帧中发生的所有活动交集对 */
|
||||||
|
private _activeTriggerIntersections: Pair<Collider>[] = [];
|
||||||
|
/** 存储前一帧的交叉对,以便我们可以在移动该帧后检测出口 */
|
||||||
|
private _previousTriggerIntersections: Pair<Collider>[] = [];
|
||||||
|
private _tempTriggerList: ITriggerListener[] = [];
|
||||||
|
|
||||||
|
constructor(entity: Entity) {
|
||||||
|
this._entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体被移动后,应该调用更新。它会处理碰撞器重叠的任何itriggerlistener。
|
||||||
|
*/
|
||||||
|
public update() {
|
||||||
|
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.length; j++) {
|
||||||
|
let neighbor = neighbors[j];
|
||||||
|
if (!collider.isTrigger && !neighbor.isTrigger)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (collider.overlaps(neighbor)) {
|
||||||
|
let pair = new Pair<Collider>(collider, neighbor);
|
||||||
|
let shouldReportTriggerEvent = this._activeTriggerIntersections.findIndex(value => {
|
||||||
|
return value.first == pair.first && value.second == pair.second;
|
||||||
|
}) == -1 && this._previousTriggerIntersections.findIndex(value => {
|
||||||
|
return value.first == pair.first && value.second == pair.second;
|
||||||
|
}) == -1;
|
||||||
|
|
||||||
|
if (shouldReportTriggerEvent)
|
||||||
|
this.notifyTriggerListeners(pair, true);
|
||||||
|
|
||||||
|
if (!this._activeTriggerIntersections.contains(pair))
|
||||||
|
this._activeTriggerIntersections.push(pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListPool.free(colliders);
|
||||||
|
|
||||||
|
this.checkForExitedColliders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkForExitedColliders(){
|
||||||
|
for (let i = 0; i < this._activeTriggerIntersections.length; i ++){
|
||||||
|
let index = this._previousTriggerIntersections.findIndex(value => {
|
||||||
|
if (value.first == this._activeTriggerIntersections[i].first && value.second == this._activeTriggerIntersections[i].second)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (index != -1)
|
||||||
|
this._previousTriggerIntersections.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._previousTriggerIntersections.length; i ++){
|
||||||
|
this.notifyTriggerListeners(this._previousTriggerIntersections[i], false)
|
||||||
|
}
|
||||||
|
this._previousTriggerIntersections.length = 0;
|
||||||
|
for (let i = 0; i < this._activeTriggerIntersections.length; i ++){
|
||||||
|
if (!this._previousTriggerIntersections.contains(this._activeTriggerIntersections[i])){
|
||||||
|
this._previousTriggerIntersections.push(this._activeTriggerIntersections[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._activeTriggerIntersections.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private notifyTriggerListeners(collisionPair: Pair<Collider>, isEntering: boolean) {
|
||||||
|
collisionPair.first.entity.getComponents("ITriggerListener", this._tempTriggerList);
|
||||||
|
for (let i = 0; i < this._tempTriggerList.length; i ++){
|
||||||
|
if (isEntering){
|
||||||
|
this._tempTriggerList[i].onTriggerEnter(collisionPair.second, collisionPair.first);
|
||||||
|
} else {
|
||||||
|
this._tempTriggerList[i].onTriggerExit(collisionPair.second, collisionPair.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tempTriggerList.length = 0;
|
||||||
|
|
||||||
|
if (collisionPair.second.entity){
|
||||||
|
collisionPair.second.entity.getComponents("ITriggerListener", this._tempTriggerList);
|
||||||
|
for (let i = 0; i < this._tempTriggerList.length; i ++){
|
||||||
|
if (isEntering){
|
||||||
|
this._tempTriggerList[i].onTriggerEnter(collisionPair.first, collisionPair.second);
|
||||||
|
} else {
|
||||||
|
this._tempTriggerList[i].onTriggerExit(collisionPair.first, collisionPair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tempTriggerList.length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ class Collisions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): Vector2 {
|
public static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): Vector2 {
|
||||||
let intersection = Vector2.Zero;
|
let intersection = new Vector2(0, 0);
|
||||||
|
|
||||||
let b = Vector2.subtract(a2, a1);
|
let b = Vector2.subtract(a2, a1);
|
||||||
let d = Vector2.subtract(b2, b1);
|
let d = Vector2.subtract(b2, b1);
|
||||||
@@ -79,42 +79,12 @@ class Collisions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static isRectToCircle(rect: Rectangle, cPosition: Vector2, cRadius: number): boolean {
|
public static isRectToCircle(rect: Rectangle, cPosition: Vector2, cRadius: number): boolean {
|
||||||
if (this.isRectToPoint(rect.x, rect.y, rect.width, rect.height, cPosition))
|
let ew = rect.width * 0.5;
|
||||||
return true;
|
let eh = rect.height * 0.5;
|
||||||
|
let vx = Math.max(0, Math.max(cPosition.x - rect.x) - ew);
|
||||||
|
let vy = Math.max(0, Math.max(cPosition.y - rect.y) - eh);
|
||||||
|
|
||||||
let edgeFrom: Vector2;
|
return vx * vx + vy * vy < cRadius * cRadius;
|
||||||
let edgeTo: Vector2;
|
|
||||||
let sector = this.getSector(rect.x, rect.y, rect.width, rect.height, cPosition);
|
|
||||||
|
|
||||||
if ((sector & PointSectors.top) != 0) {
|
|
||||||
edgeFrom = new Vector2(rect.x, rect.y);
|
|
||||||
edgeTo = new Vector2(rect.x + rect.width, rect.y);
|
|
||||||
if (this.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((sector & PointSectors.bottom) != 0) {
|
|
||||||
edgeFrom = new Vector2(rect.x, rect.y + rect.height);
|
|
||||||
edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height);
|
|
||||||
if (this.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((sector & PointSectors.left) != 0) {
|
|
||||||
edgeFrom = new Vector2(rect.x, rect.y);
|
|
||||||
edgeTo = new Vector2(rect.x, rect.y + rect.height);
|
|
||||||
if (this.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isRectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2){
|
public static isRectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2){
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
class Physics {
|
||||||
|
private static _spatialHash: SpatialHash;
|
||||||
|
/** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */
|
||||||
|
public static spatialHashCellSize = 100;
|
||||||
|
|
||||||
|
public static readonly allLayers: number = -1;
|
||||||
|
|
||||||
|
public static reset(){
|
||||||
|
this._spatialHash = new SpatialHash(this.spatialHashCellSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask = -1){
|
||||||
|
return this._spatialHash.overlapCircle(center, randius, results, layerMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boxcastBroadphase(rect: Rectangle, layerMask: number = this.allLayers){
|
||||||
|
return this._spatialHash.aabbBroadphase(rect, null, layerMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask = this.allLayers){
|
||||||
|
return this._spatialHash.aabbBroadphase(rect, collider, layerMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addCollider(collider: Collider){
|
||||||
|
Physics._spatialHash.register(collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static removeCollider(collider: Collider){
|
||||||
|
Physics._spatialHash.remove(collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateCollider(collider: Collider){
|
||||||
|
this._spatialHash.remove(collider);
|
||||||
|
this._spatialHash.register(collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
///<reference path="./Polygon.ts" />
|
||||||
|
class Box extends Polygon {
|
||||||
|
public width: number;
|
||||||
|
public height: number;
|
||||||
|
|
||||||
|
constructor(width: number, height: number){
|
||||||
|
super(Box.buildBox(width, height), true);
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildBox(width: number, height: number): Vector2[]{
|
||||||
|
let halfWidth = width / 2;
|
||||||
|
let halfHeight = height / 2;
|
||||||
|
let verts = new Array(4);
|
||||||
|
verts[0] = new Vector2(-halfWidth, -halfHeight);
|
||||||
|
verts[1] = new Vector2(halfWidth, -halfHeight);
|
||||||
|
verts[2] = new Vector2(halfWidth, halfHeight);
|
||||||
|
verts[3] = new Vector2(-halfWidth, halfHeight);
|
||||||
|
|
||||||
|
return verts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateBox(width: number, height: number){
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
|
||||||
|
let halfWidth = width / 2;
|
||||||
|
let halfHeight = height / 2;
|
||||||
|
|
||||||
|
this.points[0] = new Vector2(-halfWidth, -halfHeight);
|
||||||
|
this.points[1] = new Vector2(halfWidth, -halfHeight);
|
||||||
|
this.points[2] = new Vector2(halfWidth, halfHeight);
|
||||||
|
this.points[3] = new Vector2(-halfWidth, halfHeight);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.points.length; i ++)
|
||||||
|
this._originalPoints[i] = this.points[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public containsPoint(point: Vector2){
|
||||||
|
if (this.isUnrotated)
|
||||||
|
return this.bounds.contains(point);
|
||||||
|
|
||||||
|
return super.containsPoint(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
///<reference path="./Shape.ts" />
|
||||||
|
class Circle extends Shape {
|
||||||
|
public radius: number;
|
||||||
|
private _originalRadius: number;
|
||||||
|
|
||||||
|
constructor(radius: number) {
|
||||||
|
super();
|
||||||
|
this.radius = radius;
|
||||||
|
this._originalRadius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public pointCollidesWithShape(point: Vector2): CollisionResult {
|
||||||
|
return ShapeCollisions.pointToCircle(point, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collidesWithShape(other: Shape): CollisionResult {
|
||||||
|
if (other instanceof Box && (other as Box).isUnrotated) {
|
||||||
|
return ShapeCollisions.circleToBox(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other instanceof Circle) {
|
||||||
|
return ShapeCollisions.circleToCircle(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other instanceof Polygon) {
|
||||||
|
return ShapeCollisions.circleToPolygon(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Collisions of Circle to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public recalculateBounds(collider: Collider) {
|
||||||
|
this.center = collider.localOffset;
|
||||||
|
|
||||||
|
if (collider.shouldColliderScaleAndRotationWithTransform) {
|
||||||
|
let scale = collider.entity.transform.scale;
|
||||||
|
let hasUnitScale = scale.x == 1 && scale.y == 1;
|
||||||
|
let maxScale = Math.max(scale.x, scale.y);
|
||||||
|
this.radius = this._originalRadius * maxScale;
|
||||||
|
|
||||||
|
if (collider.entity.transform.rotation != 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.position = Vector2.add(collider.entity.transform.position, this.center);
|
||||||
|
this.bounds = new Rectangle(this.position.x - this.radius, this.position.y - this.radius, this.radius * 2, this.radius * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public overlaps(other: Shape){
|
||||||
|
if (other instanceof Box && (other as Box).isUnrotated)
|
||||||
|
return Collisions.isRectToCircle(other.bounds, this.position, this.radius);
|
||||||
|
|
||||||
|
if (other instanceof Circle)
|
||||||
|
return Collisions.isCircleToCircle(this.position, this.radius, other.position, (other as Circle).radius);
|
||||||
|
|
||||||
|
if (other instanceof Polygon)
|
||||||
|
return ShapeCollisions.circleToPolygon(this, other);
|
||||||
|
|
||||||
|
throw new Error(`overlaps of circle to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class CollisionResult {
|
||||||
|
public collider: Collider;
|
||||||
|
public minimumTranslationVector: Vector2;
|
||||||
|
public normal: Vector2;
|
||||||
|
public point: Vector2;
|
||||||
|
|
||||||
|
public invertResult(){
|
||||||
|
this.minimumTranslationVector = Vector2.negate(this.minimumTranslationVector);
|
||||||
|
this.normal = Vector2.negate(this.normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
///<reference path="./Shape.ts" />
|
||||||
|
class Polygon extends Shape {
|
||||||
|
public points: Vector2[];
|
||||||
|
public isUnrotated: boolean = true;
|
||||||
|
private _polygonCenter: Vector2;
|
||||||
|
private _areEdgeNormalsDirty = true;
|
||||||
|
protected _originalPoints: Vector2[];
|
||||||
|
|
||||||
|
public _edgeNormals: Vector2[];
|
||||||
|
public get edgeNormals(){
|
||||||
|
if (this._areEdgeNormalsDirty)
|
||||||
|
this.buildEdgeNormals();
|
||||||
|
return this._edgeNormals;
|
||||||
|
}
|
||||||
|
public isBox: boolean;
|
||||||
|
|
||||||
|
constructor(points: Vector2[], isBox?: boolean){
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.setPoints(points);
|
||||||
|
this.isBox = isBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildEdgeNormals(){
|
||||||
|
let totalEdges = this.isBox ? 2 : this.points.length;
|
||||||
|
if (this._edgeNormals == null || this._edgeNormals.length != totalEdges)
|
||||||
|
this._edgeNormals = new Array(totalEdges);
|
||||||
|
|
||||||
|
let p2: Vector2;
|
||||||
|
for (let i = 0; i < totalEdges; i ++){
|
||||||
|
let p1 = this.points[i];
|
||||||
|
if (i + 1 >= this.points.length)
|
||||||
|
p2 = this.points[0];
|
||||||
|
else
|
||||||
|
p2 = this.points[i + 1];
|
||||||
|
|
||||||
|
let perp = Vector2Ext.perpendicular(p1, p2);
|
||||||
|
perp = Vector2.normalize(perp);
|
||||||
|
this._edgeNormals[i] = perp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPoints(points: Vector2[]) {
|
||||||
|
this.points = points;
|
||||||
|
this.recalculateCenterAndEdgeNormals();
|
||||||
|
|
||||||
|
this._originalPoints = [];
|
||||||
|
for (let i = 0; i < this.points.length; i ++){
|
||||||
|
this._originalPoints.push(this.points[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public collidesWithShape(other: Shape){
|
||||||
|
let result = new CollisionResult();
|
||||||
|
if (other instanceof Polygon){
|
||||||
|
result = ShapeCollisions.polygonToPolygon(this, other);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (other instanceof Circle){
|
||||||
|
result = ShapeCollisions.circleToPolygon(other, this);
|
||||||
|
if (result){
|
||||||
|
result.invertResult();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`overlaps of Polygon to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public recalculateCenterAndEdgeNormals() {
|
||||||
|
this._polygonCenter = Polygon.findPolygonCenter(this.points);
|
||||||
|
this._areEdgeNormalsDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public overlaps(other: Shape){
|
||||||
|
let result: CollisionResult;
|
||||||
|
if (other instanceof Polygon)
|
||||||
|
return ShapeCollisions.polygonToPolygon(this, other);
|
||||||
|
|
||||||
|
if (other instanceof Circle){
|
||||||
|
result = ShapeCollisions.circleToPolygon(other, this);
|
||||||
|
if (result){
|
||||||
|
result.invertResult();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`overlaps of Pologon to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static findPolygonCenter(points: Vector2[]) {
|
||||||
|
let x = 0, y = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
x += points[i].x;
|
||||||
|
y += points[i].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Vector2(x / points.length, y / points.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { closestPoint, distanceSquared, edgeNormal } {
|
||||||
|
let distanceSquared = Number.MAX_VALUE;
|
||||||
|
let edgeNormal = new Vector2(0, 0);
|
||||||
|
let closestPoint = new Vector2(0, 0);
|
||||||
|
|
||||||
|
let tempDistanceSquared;
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
let j = i + 1;
|
||||||
|
if (j == points.length)
|
||||||
|
j = 0;
|
||||||
|
|
||||||
|
let closest = ShapeCollisions.closestPointOnLine(points[i], points[j], point);
|
||||||
|
tempDistanceSquared = Vector2.distanceSquared(point, closest);
|
||||||
|
|
||||||
|
if (tempDistanceSquared < distanceSquared) {
|
||||||
|
distanceSquared = tempDistanceSquared;
|
||||||
|
closestPoint = closest;
|
||||||
|
|
||||||
|
let line = Vector2.subtract(points[j], points[i]);
|
||||||
|
edgeNormal.x = -line.y;
|
||||||
|
edgeNormal.y = line.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeNormal = Vector2.normalize(edgeNormal);
|
||||||
|
|
||||||
|
return { closestPoint: closestPoint, distanceSquared: distanceSquared, edgeNormal: edgeNormal };
|
||||||
|
}
|
||||||
|
|
||||||
|
public pointCollidesWithShape(point: Vector2): CollisionResult {
|
||||||
|
return ShapeCollisions.pointToPoly(point, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public containsPoint(point: Vector2) {
|
||||||
|
point = Vector2.subtract(point, 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)) &&
|
||||||
|
(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isInside;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建立一个对称的多边形(六边形,八角形,n角形)并返回点
|
||||||
|
* @param vertCount
|
||||||
|
* @param radius
|
||||||
|
*/
|
||||||
|
public static buildSymmertricalPolygon(vertCount: number, radius: number) {
|
||||||
|
let verts = new Array(vertCount);
|
||||||
|
|
||||||
|
for (let i = 0; i < vertCount; i++) {
|
||||||
|
let a = 2 * Math.PI * (i / vertCount);
|
||||||
|
verts[i] = new Vector2(Math.cos(a), Math.sin(a) * radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
return verts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public recalculateBounds(collider: Collider) {
|
||||||
|
this.center = collider.localOffset;
|
||||||
|
|
||||||
|
if (collider.shouldColliderScaleAndRotationWithTransform){
|
||||||
|
let hasUnitScale = true;
|
||||||
|
let tempMat: Matrix2D;
|
||||||
|
let combinedMatrix = Matrix2D.createTranslation(-this._polygonCenter.x, -this._polygonCenter.y);
|
||||||
|
|
||||||
|
if (collider.entity.transform.scale != Vector2.one){
|
||||||
|
tempMat = Matrix2D.createScale(collider.entity.transform.scale.x, collider.entity.transform.scale.y);
|
||||||
|
combinedMatrix = Matrix2D.multiply(combinedMatrix, tempMat);
|
||||||
|
|
||||||
|
hasUnitScale = false;
|
||||||
|
let scaledOffset = Vector2.multiply(collider.localOffset, collider.entity.transform.scale);
|
||||||
|
this.center = scaledOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collider.entity.transform.rotation != 0){
|
||||||
|
tempMat = Matrix2D.createRotation(collider.entity.transform.rotation);
|
||||||
|
combinedMatrix = Matrix2D.multiply(combinedMatrix, tempMat);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
tempMat = Matrix2D.createTranslation(this._polygonCenter.x, this._polygonCenter.y);
|
||||||
|
combinedMatrix = Matrix2D.multiply(combinedMatrix, tempMat);
|
||||||
|
|
||||||
|
Vector2Ext.transform(this._originalPoints, combinedMatrix, this.points);
|
||||||
|
this.isUnrotated = collider.entity.transform.rotation == 0;
|
||||||
|
|
||||||
|
if (collider._isRotationDirty)
|
||||||
|
this._areEdgeNormalsDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.position = Vector2.add(collider.entity.transform.position, this.center);
|
||||||
|
this.bounds = Rectangle.rectEncompassingPoints(this.points);
|
||||||
|
this.bounds.location = Vector2.add(this.bounds.location, this.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
abstract class Shape {
|
||||||
|
public bounds: Rectangle;
|
||||||
|
public position: Vector2;
|
||||||
|
public center: Vector2;
|
||||||
|
|
||||||
|
public abstract recalculateBounds(collider: Collider);
|
||||||
|
public abstract pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
|
public abstract overlaps(other: Shape);
|
||||||
|
public abstract collidesWithShape(other: Shape): CollisionResult;
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
class ShapeCollisions {
|
||||||
|
/**
|
||||||
|
* 检查两个多边形之间的碰撞
|
||||||
|
* @param first
|
||||||
|
* @param second
|
||||||
|
*/
|
||||||
|
public static polygonToPolygon(first: Polygon, second: Polygon) {
|
||||||
|
let result = new CollisionResult();
|
||||||
|
let isIntersecting = true;
|
||||||
|
|
||||||
|
let firstEdges = first.edgeNormals;
|
||||||
|
let secondEdges = second.edgeNormals;
|
||||||
|
let minIntervalDistance = Number.POSITIVE_INFINITY;
|
||||||
|
let translationAxis = new Vector2();
|
||||||
|
let polygonOffset = Vector2.subtract(first.position, second.position);
|
||||||
|
let axis: Vector2;
|
||||||
|
|
||||||
|
for (let edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) {
|
||||||
|
if (edgeIndex < firstEdges.length) {
|
||||||
|
axis = firstEdges[edgeIndex];
|
||||||
|
} else {
|
||||||
|
axis = secondEdges[edgeIndex - firstEdges.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
let minA = 0;
|
||||||
|
let minB = 0;
|
||||||
|
let maxA = 0;
|
||||||
|
let maxB = 0;
|
||||||
|
let intervalDist = 0;
|
||||||
|
let ta = this.getInterval(axis, first, minA, maxA);
|
||||||
|
minA = ta.min;
|
||||||
|
minB = ta.max;
|
||||||
|
let tb = this.getInterval(axis, second, minB, maxB);
|
||||||
|
minB = tb.min;
|
||||||
|
maxB = tb.max;
|
||||||
|
|
||||||
|
let relativeIntervalOffset = Vector2.dot(polygonOffset, axis);
|
||||||
|
minA += relativeIntervalOffset;
|
||||||
|
maxA += relativeIntervalOffset;
|
||||||
|
|
||||||
|
intervalDist = this.intervalDistance(minA, maxA, minB, maxB);
|
||||||
|
if (intervalDist > 0)
|
||||||
|
isIntersecting = false;
|
||||||
|
|
||||||
|
if (!isIntersecting)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
intervalDist = Math.abs(intervalDist);
|
||||||
|
if (intervalDist < minIntervalDistance) {
|
||||||
|
minIntervalDistance = intervalDist;
|
||||||
|
translationAxis = axis;
|
||||||
|
|
||||||
|
if (Vector2.dot(translationAxis, polygonOffset) < 0)
|
||||||
|
translationAxis = new Vector2(-translationAxis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.normal = translationAxis;
|
||||||
|
result.minimumTranslationVector = Vector2.multiply(new Vector2(-translationAxis), new Vector2(minIntervalDistance));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
|
||||||
|
* @param minA
|
||||||
|
* @param maxA
|
||||||
|
* @param minB
|
||||||
|
* @param maxB
|
||||||
|
*/
|
||||||
|
public static intervalDistance(minA: number, maxA: number, minB: number, maxB) {
|
||||||
|
if (minA < minB)
|
||||||
|
return minB - maxA;
|
||||||
|
|
||||||
|
return minA - minB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算一个多边形在一个轴上的投影,并返回一个[min,max]区间
|
||||||
|
* @param axis
|
||||||
|
* @param polygon
|
||||||
|
* @param min
|
||||||
|
* @param max
|
||||||
|
*/
|
||||||
|
public static getInterval(axis: Vector2, polygon: Polygon, min: number, max: number) {
|
||||||
|
let dot = Vector2.dot(polygon.points[0], axis);
|
||||||
|
min = max = dot;
|
||||||
|
|
||||||
|
for (let i = 1; i < polygon.points.length; i++) {
|
||||||
|
dot = Vector2.dot(polygon.points[i], axis);
|
||||||
|
if (dot < min) {
|
||||||
|
min = dot;
|
||||||
|
} else if (dot > max) {
|
||||||
|
max = dot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { min: min, max: max };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param circle
|
||||||
|
* @param polygon
|
||||||
|
*/
|
||||||
|
public static circleToPolygon(circle: Circle, polygon: Polygon) {
|
||||||
|
let result = new CollisionResult();
|
||||||
|
|
||||||
|
let poly2Circle = Vector2.subtract(circle.position, polygon.position);
|
||||||
|
|
||||||
|
let gpp = Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle);
|
||||||
|
let closestPoint: Vector2 = gpp.closestPoint;
|
||||||
|
let distanceSquared: number = gpp.distanceSquared;
|
||||||
|
result.normal = gpp.edgeNormal;
|
||||||
|
|
||||||
|
let circleCenterInsidePoly = polygon.containsPoint(circle.position);
|
||||||
|
if (distanceSquared > circle.radius * circle.radius && !circleCenterInsidePoly)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let mtv: Vector2;
|
||||||
|
if (circleCenterInsidePoly) {
|
||||||
|
mtv = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared) - circle.radius));
|
||||||
|
} else {
|
||||||
|
if (distanceSquared == 0) {
|
||||||
|
mtv = Vector2.multiply(result.normal, new Vector2(circle.radius));
|
||||||
|
} else {
|
||||||
|
let distance = Math.sqrt(distanceSquared);
|
||||||
|
mtv = Vector2.multiply(new Vector2(-Vector2.subtract(poly2Circle, closestPoint)), new Vector2((circle.radius - distanceSquared) / distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.minimumTranslationVector = mtv;
|
||||||
|
result.point = Vector2.add(closestPoint, polygon.position);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 适用于圆心在方框内以及只与方框外圆心重叠的圆。
|
||||||
|
* @param circle
|
||||||
|
* @param box
|
||||||
|
*/
|
||||||
|
public static circleToBox(circle: Circle, box: Box): CollisionResult {
|
||||||
|
let result = new CollisionResult();
|
||||||
|
let closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position).res;
|
||||||
|
|
||||||
|
if (box.containsPoint(circle.position)) {
|
||||||
|
result.point = closestPointOnBounds;
|
||||||
|
|
||||||
|
let safePlace = Vector2.add(closestPointOnBounds, Vector2.subtract(result.normal, new Vector2(circle.radius)));
|
||||||
|
result.minimumTranslationVector = Vector2.subtract(circle.position, safePlace);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sqrDistance = Vector2.distanceSquared(closestPointOnBounds, circle.position);
|
||||||
|
if (sqrDistance == 0) {
|
||||||
|
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(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 = Vector2Ext.normalize(result.normal);
|
||||||
|
result.minimumTranslationVector = Vector2.multiply(new Vector2(depth), result.normal);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param point
|
||||||
|
* @param circle
|
||||||
|
*/
|
||||||
|
public static pointToCircle(point: Vector2, circle: Circle) {
|
||||||
|
let result = new CollisionResult();
|
||||||
|
|
||||||
|
let distanceSquared = Vector2.distanceSquared(point, circle.position);
|
||||||
|
let sumOfRadii = 1 + circle.radius;
|
||||||
|
let collided = distanceSquared < sumOfRadii * sumOfRadii;
|
||||||
|
if (collided) {
|
||||||
|
result.normal = Vector2.normalize(Vector2.subtract(point, circle.position));
|
||||||
|
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)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param lineA
|
||||||
|
* @param lineB
|
||||||
|
* @param closestTo
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
t = MathHelper.clamp(t, 0, 1);
|
||||||
|
|
||||||
|
return Vector2.add(lineA, Vector2.multiply(v, new Vector2(t, t)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param point
|
||||||
|
* @param poly
|
||||||
|
*/
|
||||||
|
public static pointToPoly(point: Vector2, poly: Polygon) {
|
||||||
|
let result = new CollisionResult();
|
||||||
|
|
||||||
|
if (poly.containsPoint(point)) {
|
||||||
|
let distanceSquared: number;
|
||||||
|
let gpp = Polygon.getClosestPointOnPolygonToPoint(poly.points, Vector2.subtract(point, poly.position));
|
||||||
|
let closestPoint = gpp.closestPoint;
|
||||||
|
distanceSquared = gpp.distanceSquared;
|
||||||
|
result.normal = gpp.edgeNormal;
|
||||||
|
|
||||||
|
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared), Math.sqrt(distanceSquared)));
|
||||||
|
result.point = Vector2.add(closestPoint, poly.position);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param first
|
||||||
|
* @param second
|
||||||
|
*/
|
||||||
|
public static circleToCircle(first: Circle, second: Circle){
|
||||||
|
let result = new CollisionResult();
|
||||||
|
|
||||||
|
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)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
class SpatialHash {
|
||||||
|
public gridBounds: Rectangle = new Rectangle();
|
||||||
|
|
||||||
|
private _raycastParser: RaycastResultParser;
|
||||||
|
private _cellSize: number;
|
||||||
|
private _inverseCellSize: number;
|
||||||
|
private _overlapTestCircle: Circle;
|
||||||
|
private _tempHashSet: Collider[] = [];
|
||||||
|
private _cellDict: NumberDictionary = new NumberDictionary();
|
||||||
|
|
||||||
|
constructor(cellSize: number = 100) {
|
||||||
|
this._cellSize = cellSize;
|
||||||
|
this._inverseCellSize = 1 / this._cellSize;
|
||||||
|
this._raycastParser = new RaycastResultParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(collider: Collider) {
|
||||||
|
let bounds = collider.registeredPhysicsBounds;
|
||||||
|
let p1 = this.cellCoords(bounds.x, bounds.y);
|
||||||
|
let 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)
|
||||||
|
console.error(`removing Collider [${collider}] from a cell that it is not present in`);
|
||||||
|
else
|
||||||
|
cell.remove(collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public register(collider: Collider) {
|
||||||
|
let bounds = collider.bounds;
|
||||||
|
collider.registeredPhysicsBounds = bounds;
|
||||||
|
let p1 = this.cellCoords(bounds.x, bounds.y);
|
||||||
|
let p2 = this.cellCoords(bounds.right, bounds.bottom);
|
||||||
|
|
||||||
|
if (!this.gridBounds.contains(new Vector2(p1.x, p1.y))) {
|
||||||
|
this.gridBounds = RectangleExt.union(this.gridBounds, p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.gridBounds.contains(new Vector2(p2.x, p2.y))) {
|
||||||
|
this.gridBounds = RectangleExt.union(this.gridBounds, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let x = p1.x; x <= p2.x; x++) {
|
||||||
|
for (let y = p1.y; y <= p2.y; y++) {
|
||||||
|
let c = this.cellAtPosition(x, y, true);
|
||||||
|
c.push(collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask) {
|
||||||
|
let 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);
|
||||||
|
for (let i = 0; i < potentials.length; i++) {
|
||||||
|
let collider = potentials[i];
|
||||||
|
if (collider instanceof BoxCollider) {
|
||||||
|
results[resultCounter] = collider;
|
||||||
|
resultCounter++;
|
||||||
|
} else {
|
||||||
|
throw new Error("overlapCircle against this collider type is not implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCounter == results.length)
|
||||||
|
return resultCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number) {
|
||||||
|
this._tempHashSet.length = 0;
|
||||||
|
|
||||||
|
let p1 = this.cellCoords(bounds.x, bounds.y);
|
||||||
|
let 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)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (let i = 0; i < cell.length; i++) {
|
||||||
|
let collider = cell[i];
|
||||||
|
|
||||||
|
if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bounds.intersects(collider.bounds)){
|
||||||
|
if (this._tempHashSet.indexOf(collider) == -1)
|
||||||
|
this._tempHashSet.push(collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._tempHashSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) {
|
||||||
|
let cell: Collider[] = this._cellDict.tryGetValue(x, y);
|
||||||
|
if (!cell) {
|
||||||
|
if (createCellIfEmpty) {
|
||||||
|
cell = [];
|
||||||
|
this._cellDict.add(x, y, cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
private cellCoords(x: number, y: number): Point {
|
||||||
|
return new Point(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RaycastResultParser {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class NumberDictionary {
|
||||||
|
private _store: Map<number, Collider[]> = new Map<number, Collider[]>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据x和y值计算并返回散列键
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
*/
|
||||||
|
private getKey(x: number, y: number): number {
|
||||||
|
return Long.fromNumber(x).shiftLeft(32).or(this.intToUint(y)).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private intToUint(i) {
|
||||||
|
if (i >= 0)
|
||||||
|
return i;
|
||||||
|
else
|
||||||
|
return 4294967296 + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(x: number, y: number, list: Collider[]) {
|
||||||
|
this._store.set(this.getKey(x, y), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(obj: Collider) {
|
||||||
|
this._store.forEach(list => {
|
||||||
|
if (list.contains(obj))
|
||||||
|
list.remove(obj);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public tryGetValue(x: number, y: number): Collider[] {
|
||||||
|
return this._store.get(this.getKey(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this._store.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
class Emitter<T> {
|
||||||
|
private _messageTable: Map<T, Function[]>;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
this._messageTable = new Map<T, Function[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public addObserver(eventType: T, handler: Function){
|
||||||
|
let list: Function[] = this._messageTable.get(eventType);
|
||||||
|
if (!list){
|
||||||
|
list = [];
|
||||||
|
this._messageTable.set(eventType, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.contains(handler))
|
||||||
|
console.warn("您试图添加相同的观察者两次");
|
||||||
|
list.push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeObserver(eventType: T, handler: Function){
|
||||||
|
this._messageTable.get(eventType).remove(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public emit(eventType: T, data: any){
|
||||||
|
let list: Function[] = this._messageTable.get(eventType);
|
||||||
|
if (list){
|
||||||
|
for (let i = list.length - 1; i >= 0; i --)
|
||||||
|
list[i](data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 可以用于列表池的简单类
|
||||||
|
*/
|
||||||
|
class ListPool {
|
||||||
|
private static readonly _objectQueue = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预热缓存,使用最大的cacheCount对象填充缓存
|
||||||
|
* @param cacheCount
|
||||||
|
*/
|
||||||
|
public static warmCache(cacheCount: number){
|
||||||
|
cacheCount -= this._objectQueue.length;
|
||||||
|
if (cacheCount > 0){
|
||||||
|
for (let i = 0; i < cacheCount; i ++){
|
||||||
|
this._objectQueue.unshift([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将缓存修剪为cacheCount项目
|
||||||
|
* @param cacheCount
|
||||||
|
*/
|
||||||
|
public static trimCache(cacheCount){
|
||||||
|
while (cacheCount > this._objectQueue.length)
|
||||||
|
this._objectQueue.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除缓存
|
||||||
|
*/
|
||||||
|
public static clearCache(){
|
||||||
|
this._objectQueue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果可以的话,从堆栈中弹出一个项
|
||||||
|
*/
|
||||||
|
public static obtain<T>(): Array<T>{
|
||||||
|
if (this._objectQueue.length > 0)
|
||||||
|
return this._objectQueue.shift();
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将项推回堆栈
|
||||||
|
* @param obj
|
||||||
|
*/
|
||||||
|
public static free<T>(obj: Array<T>){
|
||||||
|
this._objectQueue.unshift(obj);
|
||||||
|
obj.length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 用于管理一对对象的简单DTO
|
||||||
|
*/
|
||||||
|
class Pair<T> {
|
||||||
|
public first: T;
|
||||||
|
public second: T;
|
||||||
|
|
||||||
|
constructor(first: T, second: T){
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(){
|
||||||
|
this.first = this.second = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public equals(other: Pair<T>){
|
||||||
|
return this.first == other.first && this.second == other.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
class RectangleExt {
|
||||||
|
public static union(first: Rectangle, point: Point){
|
||||||
|
let rect = new Rectangle(point.x, point.y, 0, 0);
|
||||||
|
return this.unionR(first, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unionR(value1: Rectangle, value2: Rectangle){
|
||||||
|
let result = new Rectangle();
|
||||||
|
result.x = Math.min(value1.x, value2.x);
|
||||||
|
result.y = Math.min(value1.y, value2.y);
|
||||||
|
result.width = Math.max(value1.right, value2.right) - result.x;
|
||||||
|
result.height = Math.max(value1.bottom, value2.bottom) - result.y;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* 三角剖分
|
||||||
|
*/
|
||||||
|
class Triangulator {
|
||||||
|
/**
|
||||||
|
* 最后一次三角调用中使用的点列表的三角形列表项的索引
|
||||||
|
*/
|
||||||
|
public triangleIndices: number[] = [];
|
||||||
|
|
||||||
|
private _triPrev: number[] = new Array<number>(12);
|
||||||
|
private _triNext: number[] = new Array<number>(12);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算一个三角形列表,该列表完全覆盖给定点集所包含的区域。如果点不是CCW,则将arePointsCCW参数传递为false
|
||||||
|
* @param points 定义封闭路径的点列表
|
||||||
|
* @param arePointsCCW
|
||||||
|
*/
|
||||||
|
public triangulate(points: Vector2[], arePointsCCW: boolean = true){
|
||||||
|
let count = points.length;
|
||||||
|
|
||||||
|
// 设置前一个链接和下一个链接
|
||||||
|
this.initialize(count);
|
||||||
|
|
||||||
|
// 非三角的多边形断路器
|
||||||
|
let iterations = 0;
|
||||||
|
|
||||||
|
// 从0开始
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
// 继续移除所有的三角形,直到只剩下一个三角形
|
||||||
|
while (count > 3 && iterations < 500){
|
||||||
|
iterations ++;
|
||||||
|
|
||||||
|
let isEar = true;
|
||||||
|
let a = points[this._triPrev[index]];
|
||||||
|
let b = points[index];
|
||||||
|
let c = points[this._triNext[index]];
|
||||||
|
|
||||||
|
if (Vector2Ext.isTriangleCCW(a, b, c)){
|
||||||
|
let k = this._triNext[this._triNext[index]];
|
||||||
|
do {
|
||||||
|
if (Triangulator.testPointTriangle(points[k], a, b, c)){
|
||||||
|
isEar = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = this._triNext[k];
|
||||||
|
} while (k != this._triPrev[index]);
|
||||||
|
}else{
|
||||||
|
isEar = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEar){
|
||||||
|
this.triangleIndices.push(this._triPrev[index]);
|
||||||
|
this.triangleIndices.push(index);
|
||||||
|
this.triangleIndices.push(this._triNext[index]);
|
||||||
|
|
||||||
|
// 删除vert通过重定向相邻vert的上一个和下一个链接,从而减少vertext计数
|
||||||
|
this._triNext[this._triPrev[index]] = this._triNext[index];
|
||||||
|
this._triPrev[this._triNext[index]] = this._triPrev[index];
|
||||||
|
count --;
|
||||||
|
|
||||||
|
// 接下来访问前一个vert
|
||||||
|
index = this._triPrev[index];
|
||||||
|
}else{
|
||||||
|
index = this._triNext[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.triangleIndices.push(this._triPrev[index]);
|
||||||
|
this.triangleIndices.push(index);
|
||||||
|
this.triangleIndices.push(this._triNext[index]);
|
||||||
|
|
||||||
|
if (!arePointsCCW)
|
||||||
|
this.triangleIndices.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialize(count: number){
|
||||||
|
this.triangleIndices.length = 0;
|
||||||
|
|
||||||
|
if (this._triNext.length < count){
|
||||||
|
this._triNext.reverse();
|
||||||
|
this._triNext = new Array<number>(Math.max(this._triNext.length * 2, count));
|
||||||
|
}
|
||||||
|
if (this._triPrev.length < count){
|
||||||
|
this._triPrev.reverse();
|
||||||
|
this._triPrev = new Array<number>(Math.max(this._triPrev.length * 2, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < count;i ++){
|
||||||
|
this._triPrev[i] = i - 1;
|
||||||
|
this._triNext[i] = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._triPrev[0] = count - 1;
|
||||||
|
this._triNext[count - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean{
|
||||||
|
if (Vector2Ext.cross(Vector2.subtract(point, a), Vector2.subtract(b, a)) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Vector2Ext.cross(Vector2.subtract(point, b), Vector2.subtract(c, b)) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Vector2Ext.cross(Vector2.subtract(point, c), Vector2.subtract(a, c)) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
class Vector2Ext {
|
||||||
|
/**
|
||||||
|
* 检查三角形是CCW还是CW
|
||||||
|
* @param a
|
||||||
|
* @param center
|
||||||
|
* @param c
|
||||||
|
*/
|
||||||
|
public static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2) {
|
||||||
|
return this.cross(Vector2.subtract(center, a), Vector2.subtract(c, center)) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算二维伪叉乘点(Perp(u), v)
|
||||||
|
* @param u
|
||||||
|
* @param v
|
||||||
|
*/
|
||||||
|
public static cross(u: Vector2, v: Vector2) {
|
||||||
|
return u.y * v.x - u.x * v.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回与传入向量垂直的向量
|
||||||
|
* @param first
|
||||||
|
* @param second
|
||||||
|
*/
|
||||||
|
public static perpendicular(first: Vector2, second: Vector2) {
|
||||||
|
return new Vector2(-1 * (second.y - first.y), second.x - first.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vector2的临时解决方案
|
||||||
|
* 标准化把向量弄乱了
|
||||||
|
* @param vec
|
||||||
|
*/
|
||||||
|
public static normalize(vec: Vector2) {
|
||||||
|
let magnitude = Math.sqrt((vec.x * vec.x) + (vec.y * vec.y));
|
||||||
|
if (magnitude > MathHelper.Epsilon) {
|
||||||
|
vec = Vector2.divide(vec, new Vector2(magnitude));
|
||||||
|
} else {
|
||||||
|
vec.x = vec.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static transformA(sourceArray: Vector2[], sourceIndex: number, matrix: Matrix2D,
|
||||||
|
destinationArray: Vector2[], destinationIndex: number, length: number) {
|
||||||
|
for (let i = 0; i < length; i ++){
|
||||||
|
let position = sourceArray[sourceIndex + i];
|
||||||
|
let destination = destinationArray[destinationIndex + i];
|
||||||
|
destination.x = (position.x * matrix.m11) + (position.y * matrix.m21) + matrix.m31;
|
||||||
|
destination.y = (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32;
|
||||||
|
destinationArray[destinationIndex + i] = destination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]) {
|
||||||
|
this.transformA(sourceArray, 0, matrix, destinationArray, 0, sourceArray.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
class WebGLUtils {
|
||||||
|
public static getWebGL(): WebGLRenderingContext {
|
||||||
|
if (egret.WebGLUtils.checkCanUseWebGL())
|
||||||
|
return document.querySelector("canvas").getContext("webgl");
|
||||||
|
|
||||||
|
throw new Error("cannot get webgl");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static drawUserIndexPrimitives<T>(primitiveType: number, vertexData: T[], vertexOffset: number,
|
||||||
|
numVertices: number, indexData: number[], indexOffset: number, primitiveCount: number) {
|
||||||
|
let GL = this.getWebGL();
|
||||||
|
|
||||||
|
GL.bindBuffer(GL.ARRAY_BUFFER, 0);
|
||||||
|
this.checkGLError();
|
||||||
|
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
this.checkGLError();
|
||||||
|
|
||||||
|
GL.drawElements(primitiveType,
|
||||||
|
this.getElementCountArray(primitiveType, primitiveCount),
|
||||||
|
GL.UNSIGNED_SHORT,
|
||||||
|
indexOffset * 2);
|
||||||
|
this.checkGLError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getElementCountArray(primitiveType: number, primitiveCount: number) {
|
||||||
|
let GL = this.getWebGL();
|
||||||
|
switch (primitiveType) {
|
||||||
|
case GL.LINES:
|
||||||
|
return primitiveCount * 2;
|
||||||
|
case GL.LINE_STRIP:
|
||||||
|
return primitiveCount + 1;
|
||||||
|
case GL.TRIANGLES:
|
||||||
|
return primitiveCount * 3;
|
||||||
|
case GL.TRIANGLE_STRIP:
|
||||||
|
return primitiveCount + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("not support");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static checkGLError() {
|
||||||
|
let GL = this.getWebGL();
|
||||||
|
let error = GL.getError();
|
||||||
|
if (error != GL.NO_ERROR) {
|
||||||
|
throw new Error("GL.GetError() returned" + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user