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 包含众多高性能方法以供使用
|
||||
(正在施工中)
|
||||
用于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",
|
||||
"path": "./libs/framework"
|
||||
},
|
||||
{
|
||||
"name": "long",
|
||||
"path": "./libs/long"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+428
-7
@@ -72,33 +72,102 @@ declare class PriorityQueue<T extends PriorityQueueNode> {
|
||||
private swap;
|
||||
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 {
|
||||
entity: Entity;
|
||||
displayRender: egret.DisplayObject;
|
||||
private _enabled;
|
||||
updateInterval: number;
|
||||
readonly transform: Transform;
|
||||
enabled: boolean;
|
||||
setEnabled(isEnabled: boolean): this;
|
||||
abstract initialize(): any;
|
||||
initialize(): void;
|
||||
onAddedToEntity(): void;
|
||||
onRemovedFromEntity(): void;
|
||||
onEnabled(): void;
|
||||
onDisabled(): void;
|
||||
onEntityTransformChanged(comp: ComponentTransform): void;
|
||||
update(): void;
|
||||
bind(displayRender: egret.DisplayObject): this;
|
||||
debugRender(): void;
|
||||
registerComponent(): void;
|
||||
deregisterComponent(): void;
|
||||
}
|
||||
declare class Entity {
|
||||
private static _idGenerator;
|
||||
name: string;
|
||||
readonly id: number;
|
||||
scene: Scene;
|
||||
readonly transform: Transform;
|
||||
readonly components: ComponentList;
|
||||
private _updateOrder;
|
||||
private _enabled;
|
||||
private _isDestoryed;
|
||||
private _tag;
|
||||
componentBits: BitSet;
|
||||
parent: Transform;
|
||||
position: Vector2;
|
||||
@@ -115,15 +184,18 @@ declare class Entity {
|
||||
readonly isDestoryed: boolean;
|
||||
enabled: boolean;
|
||||
setEnabled(isEnabled: boolean): this;
|
||||
tag: number;
|
||||
constructor(name: string);
|
||||
updateOrder: number;
|
||||
setUpdateOrder(updateOrder: number): this;
|
||||
setTag(tag: number): Entity;
|
||||
attachToScene(newScene: Scene): void;
|
||||
detachFromScene(): void;
|
||||
addComponent<T extends Component>(component: T): T;
|
||||
hasComponent<T extends Component>(type: any): boolean;
|
||||
getOrCreateComponent<T extends Component>(type: T): T;
|
||||
getComponent<T extends Component>(type: any): T;
|
||||
getComponents(typeName: string | any, componentList?: any): any;
|
||||
removeComponentForType<T extends Component>(type: any): boolean;
|
||||
removeComponent(component: Component): void;
|
||||
removeAllComponents(): void;
|
||||
@@ -235,11 +307,16 @@ declare class Camera extends Component {
|
||||
private _minimumZoom;
|
||||
private _maximumZoom;
|
||||
private _areMatrixesDirty;
|
||||
private _inset;
|
||||
private _bounds;
|
||||
private _areBoundsDirty;
|
||||
readonly bounds: Rectangle;
|
||||
zoom: number;
|
||||
minimumZoom: number;
|
||||
maximumZoom: number;
|
||||
origin: Vector2;
|
||||
readonly transformMatrix: Matrix2D;
|
||||
readonly inverseTransformMatrix: Matrix2D;
|
||||
constructor();
|
||||
setMinimumZoom(minZoom: number): Camera;
|
||||
setMaximumZoom(maxZoom: number): Camera;
|
||||
@@ -248,8 +325,105 @@ declare class Camera extends Component {
|
||||
update(): void;
|
||||
setPosition(position: Vector2): this;
|
||||
updateMatrixes(): void;
|
||||
screenToWorldPoint(screenPosition: Vector2): Vector2;
|
||||
worldToScreenPoint(worldPosition: Vector2): Vector2;
|
||||
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 {
|
||||
private _scene;
|
||||
private _entities;
|
||||
@@ -309,6 +483,7 @@ declare class ComponentList {
|
||||
updateLists(): void;
|
||||
private handleRemove;
|
||||
getComponent<T extends Component>(type: any, onlyReturnInitializedComponents: boolean): T;
|
||||
getComponents(typeName: string | any, components?: any): any;
|
||||
update(): void;
|
||||
onEntityTransformChanged(comp: any): void;
|
||||
}
|
||||
@@ -323,12 +498,17 @@ declare class EntityList {
|
||||
private _entitiesToAdded;
|
||||
private _tempEntityList;
|
||||
private _entities;
|
||||
private _entityDict;
|
||||
private _unsortedTags;
|
||||
constructor(scene: Scene);
|
||||
readonly count: number;
|
||||
readonly buffer: Entity[];
|
||||
add(entity: Entity): void;
|
||||
remove(entity: Entity): void;
|
||||
findEntity(name: string): Entity;
|
||||
getTagList(tag: number): Entity[];
|
||||
addToTagList(entity: Entity): void;
|
||||
removeFromTagList(entity: Entity): void;
|
||||
update(): void;
|
||||
removeAllEntities(): void;
|
||||
updateLists(): void;
|
||||
@@ -360,14 +540,30 @@ declare class Time {
|
||||
static unscaledDeltaTime: any;
|
||||
static deltaTime: number;
|
||||
static timeScale: number;
|
||||
static frameCount: number;
|
||||
private static _lastTime;
|
||||
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 {
|
||||
static readonly Epsilon: number;
|
||||
static readonly Rad2Deg: number;
|
||||
static readonly Deg2Rad: number;
|
||||
static toDegrees(radians: number): number;
|
||||
static toRadians(degrees: 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 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 {
|
||||
m11: number;
|
||||
@@ -393,21 +589,246 @@ declare class Matrix2D {
|
||||
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
||||
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
||||
}
|
||||
declare class Point {
|
||||
declare class Rectangle {
|
||||
x: 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 {
|
||||
x: number;
|
||||
y: number;
|
||||
private static readonly unitYVector;
|
||||
private static readonly unitXVector;
|
||||
private static readonly unitVector2;
|
||||
static readonly One: Vector2;
|
||||
constructor(x: number, y: number);
|
||||
private static readonly zeroVector2;
|
||||
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 divide(value1: Vector2, value2: Vector2): Vector2;
|
||||
static multiply(value1: Vector2, value2: Vector2): Vector2;
|
||||
static subtract(value1: Vector2, value2: Vector2): Vector2;
|
||||
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 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;
|
||||
}
|
||||
|
||||
+2119
-59
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/tween/tween.js",
|
||||
"libs/modules/promise/promise.js",
|
||||
"libs/framework/framework.js"
|
||||
"libs/framework/framework.js",
|
||||
"libs/long/long.js"
|
||||
],
|
||||
"game": [
|
||||
"bin-debug/AssetAdapter.js",
|
||||
@@ -15,6 +16,7 @@
|
||||
"bin-debug/Main.js",
|
||||
"bin-debug/Platform.js",
|
||||
"bin-debug/ThemeAdapter.js",
|
||||
"bin-debug/game/CoreEmitterType.js",
|
||||
"bin-debug/game/MainScene.js",
|
||||
"bin-debug/game/SpawnerComponent.js",
|
||||
"bin-debug/game/SpawnerSystem.js"
|
||||
|
||||
+29
-2
@@ -29,6 +29,7 @@
|
||||
|
||||
|
||||
class Main extends eui.UILayer {
|
||||
public static emitter: Emitter<CoreEmitterType>;
|
||||
|
||||
protected createChildren(): void {
|
||||
super.createChildren();
|
||||
@@ -51,10 +52,23 @@ class Main extends eui.UILayer {
|
||||
egret.registerImplementation("eui.IAssetAdapter", assetAdapter);
|
||||
egret.registerImplementation("eui.IThemeAdapter", new ThemeAdapter());
|
||||
|
||||
|
||||
Main.emitter = new Emitter<CoreEmitterType>();
|
||||
this.addEventListener(egret.Event.ENTER_FRAME, this.updateFrame, this);
|
||||
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() {
|
||||
await this.loadResource();
|
||||
this.createGameScene();
|
||||
@@ -93,7 +107,20 @@ class Main extends eui.UILayer {
|
||||
protected createGameScene(): void {
|
||||
let scene = SceneManager.createScene("main", new MainScene(this)).setActive();
|
||||
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));
|
||||
// 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){
|
||||
super(displayContent);
|
||||
|
||||
this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
||||
// this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
||||
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(){
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class SpawnComponent extends Component {
|
||||
class SpawnComponent extends Component implements ITriggerListener {
|
||||
public cooldown = -1;
|
||||
public minInterval = 2;
|
||||
public maxInterval = 60;
|
||||
@@ -18,6 +18,16 @@ class SpawnComponent extends Component {
|
||||
public 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 {
|
||||
|
||||
Vendored
+391
-14
@@ -72,22 +72,88 @@ declare class PriorityQueue<T extends PriorityQueueNode> {
|
||||
private swap;
|
||||
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 {
|
||||
entity: Entity;
|
||||
displayRender: egret.DisplayObject;
|
||||
private _enabled;
|
||||
updateInterval: number;
|
||||
readonly transform: Transform;
|
||||
enabled: boolean;
|
||||
setEnabled(isEnabled: boolean): this;
|
||||
abstract initialize(): any;
|
||||
initialize(): void;
|
||||
onAddedToEntity(): void;
|
||||
onRemovedFromEntity(): void;
|
||||
onEnabled(): void;
|
||||
onDisabled(): void;
|
||||
onEntityTransformChanged(comp: ComponentTransform): void;
|
||||
update(): void;
|
||||
bind(displayRender: egret.DisplayObject): this;
|
||||
debugRender(): void;
|
||||
registerComponent(): void;
|
||||
deregisterComponent(): void;
|
||||
}
|
||||
@@ -129,6 +195,7 @@ declare class Entity {
|
||||
hasComponent<T extends Component>(type: any): boolean;
|
||||
getOrCreateComponent<T extends Component>(type: T): T;
|
||||
getComponent<T extends Component>(type: any): T;
|
||||
getComponents(typeName: string | any, componentList?: any): any;
|
||||
removeComponentForType<T extends Component>(type: any): boolean;
|
||||
removeComponent(component: Component): void;
|
||||
removeAllComponents(): void;
|
||||
@@ -240,11 +307,16 @@ declare class Camera extends Component {
|
||||
private _minimumZoom;
|
||||
private _maximumZoom;
|
||||
private _areMatrixesDirty;
|
||||
private _inset;
|
||||
private _bounds;
|
||||
private _areBoundsDirty;
|
||||
readonly bounds: Rectangle;
|
||||
zoom: number;
|
||||
minimumZoom: number;
|
||||
maximumZoom: number;
|
||||
origin: Vector2;
|
||||
readonly transformMatrix: Matrix2D;
|
||||
readonly inverseTransformMatrix: Matrix2D;
|
||||
constructor();
|
||||
setMinimumZoom(minZoom: number): Camera;
|
||||
setMaximumZoom(maxZoom: number): Camera;
|
||||
@@ -253,8 +325,105 @@ declare class Camera extends Component {
|
||||
update(): void;
|
||||
setPosition(position: Vector2): this;
|
||||
updateMatrixes(): void;
|
||||
screenToWorldPoint(screenPosition: Vector2): Vector2;
|
||||
worldToScreenPoint(worldPosition: Vector2): Vector2;
|
||||
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 {
|
||||
private _scene;
|
||||
private _entities;
|
||||
@@ -314,6 +483,7 @@ declare class ComponentList {
|
||||
updateLists(): void;
|
||||
private handleRemove;
|
||||
getComponent<T extends Component>(type: any, onlyReturnInitializedComponents: boolean): T;
|
||||
getComponents(typeName: string | any, components?: any): any;
|
||||
update(): void;
|
||||
onEntityTransformChanged(comp: any): void;
|
||||
}
|
||||
@@ -370,14 +540,30 @@ declare class Time {
|
||||
static unscaledDeltaTime: any;
|
||||
static deltaTime: number;
|
||||
static timeScale: number;
|
||||
static frameCount: number;
|
||||
private static _lastTime;
|
||||
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 {
|
||||
static readonly Epsilon: number;
|
||||
static readonly Rad2Deg: number;
|
||||
static readonly Deg2Rad: number;
|
||||
static toDegrees(radians: number): number;
|
||||
static toRadians(degrees: 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 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 {
|
||||
m11: number;
|
||||
@@ -403,34 +589,65 @@ declare class Matrix2D {
|
||||
static createRotation(radians: 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 {
|
||||
x: number;
|
||||
y: number;
|
||||
width: 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 {
|
||||
x: number;
|
||||
y: number;
|
||||
private static readonly zeroVector;
|
||||
private static readonly unitVector;
|
||||
static readonly One: Vector2;
|
||||
static readonly Zero: Vector2;
|
||||
constructor(x: number, y: number);
|
||||
private static readonly unitYVector;
|
||||
private static readonly unitXVector;
|
||||
private static readonly unitVector2;
|
||||
private static readonly zeroVector2;
|
||||
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 divide(value1: Vector2, value2: Vector2): Vector2;
|
||||
static multiply(value1: Vector2, value2: Vector2): Vector2;
|
||||
static subtract(value1: Vector2, value2: Vector2): Vector2;
|
||||
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 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,
|
||||
@@ -455,3 +672,163 @@ declare class Collisions {
|
||||
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;
|
||||
}
|
||||
|
||||
+1930
-100
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",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"description": "用于egret 包含众多高性能方法以供使用",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "mocha --recursive --reporter tap --growl",
|
||||
"eslint": "eslint src --ext .ts"
|
||||
},
|
||||
"author": "yhh",
|
||||
"license": "MIT",
|
||||
@@ -18,6 +19,7 @@
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-inject-string": "^1.1.2",
|
||||
"gulp-minify": "^3.1.0",
|
||||
"gulp-string-replace": "^1.1.2",
|
||||
"gulp-typescript": "^3.1.6",
|
||||
@@ -25,10 +27,10 @@
|
||||
"tsify": "^3.0.1",
|
||||
"typescript": "^2.2.2",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"watchify": "^3.9.0",
|
||||
"gulp-inject-string": "^1.1.2"
|
||||
"watchify": "^3.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://npm.pkg.github.com/359807859@qq.com"
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class AstarGridGraph implements IAstarGraph<Point> {
|
||||
* @param node
|
||||
*/
|
||||
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){
|
||||
|
||||
@@ -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 {
|
||||
public entity: Entity;
|
||||
public displayRender: egret.DisplayObject;
|
||||
private _enabled: boolean = true;
|
||||
public updateInterval: number = 1;
|
||||
|
||||
@@ -30,7 +29,9 @@ abstract class Component {
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract initialize();
|
||||
public initialize(){
|
||||
|
||||
}
|
||||
|
||||
public onAddedToEntity(){
|
||||
|
||||
@@ -56,11 +57,8 @@ abstract class Component {
|
||||
|
||||
}
|
||||
|
||||
/** 绑定显示对象 */
|
||||
public bind(displayRender: egret.DisplayObject){
|
||||
this.displayRender = displayRender;
|
||||
|
||||
return this;
|
||||
public debugRender(){
|
||||
|
||||
}
|
||||
|
||||
/** 内部使用 运行时不应该调用 */
|
||||
|
||||
@@ -8,6 +8,42 @@ class Camera extends Component {
|
||||
private _minimumZoom = 0.3;
|
||||
private _maximumZoom = 3;
|
||||
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(){
|
||||
if (this._zoom == 0)
|
||||
@@ -51,11 +87,17 @@ class Camera extends Component {
|
||||
}
|
||||
|
||||
public get transformMatrix(){
|
||||
this.updateMatrixes();
|
||||
|
||||
if (this._areBoundsDirty)
|
||||
this.updateMatrixes();
|
||||
return this._transformMatrix;
|
||||
}
|
||||
|
||||
public get inverseTransformMatrix(){
|
||||
if (this._areBoundsDirty)
|
||||
this.updateMatrixes();
|
||||
return this._inverseTransformMatrix;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -98,14 +140,7 @@ class Camera extends Component {
|
||||
}
|
||||
|
||||
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){
|
||||
@@ -130,10 +165,28 @@ class Camera extends Component {
|
||||
|
||||
this._inverseTransformMatrix = Matrix2D.invert(this._transformMatrix);
|
||||
|
||||
this._areBoundsDirty = true;
|
||||
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() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public getComponents(typeName: string | any, componentList?){
|
||||
return this.components.getComponents(typeName, componentList);
|
||||
}
|
||||
|
||||
public removeComponentForType<T extends Component>(type){
|
||||
let comp = this.getComponent<T>(type);
|
||||
if (comp){
|
||||
|
||||
@@ -74,7 +74,9 @@ class Scene extends egret.DisplayObjectContainer {
|
||||
/** 初始化场景 */
|
||||
public initialize(){
|
||||
/** 初始化默认相机 */
|
||||
this.camera = this.createEntity("camera").addComponent(new Camera());
|
||||
this.camera = this.createEntity("camera").getOrCreateComponent(new Camera());
|
||||
|
||||
Physics.reset();
|
||||
|
||||
if (this.entityProcessors)
|
||||
this.entityProcessors.begin();
|
||||
|
||||
@@ -49,7 +49,7 @@ class Transform {
|
||||
|
||||
constructor(entity: Entity){
|
||||
this.entity = entity;
|
||||
this._scale = this._localScale = Vector2.One;
|
||||
this._scale = this._localScale = Vector2.one;
|
||||
this._children = [];
|
||||
}
|
||||
|
||||
@@ -203,14 +203,6 @@ class Transform {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -234,13 +226,6 @@ class Transform {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -270,14 +255,6 @@ class Transform {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,8 +120,46 @@ class ComponentList {
|
||||
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(){
|
||||
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){
|
||||
|
||||
@@ -20,7 +20,8 @@ class EntityList{
|
||||
}
|
||||
|
||||
public add(entity: Entity){
|
||||
this._entitiesToAdded.push(entity);
|
||||
if (this._entitiesToAdded.indexOf(entity) == -1)
|
||||
this._entitiesToAdded.push(entity);
|
||||
}
|
||||
|
||||
public remove(entity: Entity){
|
||||
@@ -108,10 +109,12 @@ class EntityList{
|
||||
this._entitiesToAdded = this._tempEntityList;
|
||||
this._tempEntityList = temp;
|
||||
this._tempEntityList.forEach(entity => {
|
||||
this._entities.push(entity);
|
||||
entity.scene = this.scene;
|
||||
if (!this._entities.contains(entity)){
|
||||
this._entities.push(entity);
|
||||
entity.scene = this.scene;
|
||||
|
||||
this.scene.entityProcessors.onEntityAdded(entity)
|
||||
this.scene.entityProcessors.onEntityAdded(entity)
|
||||
}
|
||||
});
|
||||
|
||||
this._tempEntityList.forEach(entity => entity.onAddedToScene());
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
class Time {
|
||||
public static unscaledDeltaTime;
|
||||
public static deltaTime: number;
|
||||
public static deltaTime: number = 0;
|
||||
public static timeScale = 1;
|
||||
public static frameCount = 0;;
|
||||
|
||||
private static _lastTime = 0;
|
||||
|
||||
@@ -9,6 +10,7 @@ class Time {
|
||||
let dt = (currentTime - this._lastTime) / 1000;
|
||||
this.deltaTime = dt * this.timeScale;
|
||||
this.unscaledDeltaTime = dt;
|
||||
this.frameCount ++;
|
||||
|
||||
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 {
|
||||
public static readonly Epsilon: number = 0.00001;
|
||||
public static readonly Rad2Deg = 57.29578;
|
||||
public static readonly Deg2Rad = 0.0174532924;
|
||||
|
||||
/**
|
||||
* 将弧度转换成角度。
|
||||
* @param radians 用弧度表示的角
|
||||
@@ -27,6 +31,10 @@ class MathHelper {
|
||||
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){
|
||||
if (value < min)
|
||||
return min;
|
||||
@@ -36,4 +44,17 @@ class MathHelper {
|
||||
|
||||
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 y: number;
|
||||
|
||||
constructor(x: number, y: number){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
constructor(x?: number, y?: number){
|
||||
this.x = x ? x : 0;
|
||||
this.y = y ? y : this.x;
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,175 @@ class Rectangle {
|
||||
public width: number;
|
||||
public height: number;
|
||||
|
||||
constructor(x: number, y: number, width: number, height: number){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
private _tempMat: Matrix2D;
|
||||
private _transformMat: Matrix2D;
|
||||
|
||||
public get left() {
|
||||
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 y: number = 0;
|
||||
|
||||
private static readonly zeroVector = new Vector2(0, 0);
|
||||
private static readonly unitVector = new Vector2(1, 1);
|
||||
|
||||
public static get One(){
|
||||
return this.unitVector;
|
||||
private static readonly unitYVector = new Vector2(0, 1);
|
||||
private static readonly unitXVector = new Vector2(1, 0);
|
||||
private static readonly unitVector2 = new Vector2(1, 1);
|
||||
private static readonly zeroVector2 = new Vector2(0, 0);
|
||||
public static get zero(){
|
||||
return Vector2.zeroVector2;
|
||||
}
|
||||
|
||||
public static get Zero(){
|
||||
return this.zeroVector;
|
||||
public static get one(){
|
||||
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 y 二维空间的y坐标
|
||||
*/
|
||||
constructor(x: number, y: number){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
constructor(x? : number, y?: number){
|
||||
this.x = x ? x : 0;
|
||||
this.y = y ? y : this.x;
|
||||
}
|
||||
|
||||
public static add(value1: Vector2, value2: Vector2){
|
||||
value1.x += value2.x;
|
||||
value1.y += value2.y;
|
||||
return value1;
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x + value2.x;
|
||||
result.y = value1.y + value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static divide(value1: Vector2, value2: Vector2){
|
||||
value1.x /= value2.x;
|
||||
value1.y /= value2.y;
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x / value2.x;
|
||||
result.y = value1.y / value2.y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static multiply(value1: Vector2, value2: Vector2){
|
||||
value1.x *= value2.x;
|
||||
value1.y *= value2.y;
|
||||
return value1;
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x * value2.x;
|
||||
result.y = value1.y * value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static subtract(value1: Vector2, value2: Vector2){
|
||||
value1.x -= value2.x;
|
||||
value1.y -= value2.y;
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x - value2.x;
|
||||
result.y = value1.y - value2.y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
@@ -55,6 +68,17 @@ class Vector2 {
|
||||
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
|
||||
@@ -74,7 +98,24 @@ class Vector2 {
|
||||
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){
|
||||
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 {
|
||||
let intersection = Vector2.Zero;
|
||||
let intersection = new Vector2(0, 0);
|
||||
|
||||
let b = Vector2.subtract(a2, a1);
|
||||
let d = Vector2.subtract(b2, b1);
|
||||
@@ -79,42 +79,12 @@ class Collisions {
|
||||
}
|
||||
|
||||
public static isRectToCircle(rect: Rectangle, cPosition: Vector2, cRadius: number): boolean {
|
||||
if (this.isRectToPoint(rect.x, rect.y, rect.width, rect.height, cPosition))
|
||||
return true;
|
||||
let ew = rect.width * 0.5;
|
||||
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;
|
||||
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;
|
||||
return vx * vx + vy * vy < cRadius * cRadius;
|
||||
}
|
||||
|
||||
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