Compare commits

..

35 Commits

Author SHA1 Message Date
yhh 9c293979a4 v1.0.1 2020-06-18 10:07:08 +08:00
YHH bc995e36c2 修复 triggerlistener 发生碰撞后不断触发问题(仅第一次碰撞触发) 2020-06-18 09:06:59 +08:00
YHH 18279d16cd 修复 collsionTriggerHelper 遍历问题 2020-06-18 00:03:01 +08:00
YHH 59b2f150f3 新增Long库 移除原有Long.ts 2020-06-17 23:19:25 +08:00
YHH e6d6c4199f fix long 2020-06-17 22:40:49 +08:00
yhh ccf8c4e107 新增 Long 类型 2020-06-17 20:40:56 +08:00
yhh 9e6e5eccc8 新增numberdictionary的散列键 2020-06-16 20:22:22 +08:00
yhh 447ea4efe4 修复box因缺少初始化报错问题 2020-06-16 16:35:17 +08:00
yhh 7f5b78f340 新增gulp build 2020-06-16 13:27:06 +08:00
YHH 4ac8bafa87 Merge pull request #1 from esengine/develop-test-ci
Create node.js.yml
2020-06-16 13:19:06 +08:00
yhh 25ce4c9cf9 remove test 2020-06-16 13:16:58 +08:00
yhh 2b81a0b06b Merge branch 'master' into develop-test-ci 2020-06-16 13:14:37 +08:00
yhh e68f6bd1be packages 2020-06-16 13:13:55 +08:00
YHH 06c1aeb97a Create node.js.yml 2020-06-16 12:46:40 +08:00
yhh ced176706b 修复physics register失效问题 2020-06-16 11:59:40 +08:00
yhh 8b21edc65f 新增mover移动器组件 用于处理itriggerListener接口碰撞信息 2020-06-16 11:22:37 +08:00
YHH 75301f7776 完善 colliderTriggerHelper 用于更新碰撞信息 2020-06-16 09:10:09 +08:00
YHH dba43b9773 完善shapeCollision 支持多边形 2020-06-16 00:04:28 +08:00
yhh 5186bc0187 修复polygon数组错误 修复emit空注册报错 2020-06-15 20:08:21 +08:00
yhh c3120d791f 新增事件发送接收器 2020-06-15 12:16:23 +08:00
yhh 16892eb7af 移除物理引擎 移动到新库 2020-06-15 10:42:06 +08:00
YHH 7f8f1cf0d0 优化collision 2020-06-15 08:46:38 +08:00
yhh 246e9a9511 新增shapecollision 用于计算多边形碰撞 2020-06-12 20:24:51 +08:00
YHH da5a1a0c79 优化Flags 2020-06-12 08:56:10 +08:00
YHH 6fa56dd572 新增flag 帮助处理位掩码 2020-06-12 08:47:13 +08:00
yhh ad68f0e1a0 新增shape形状 2020-06-11 20:36:36 +08:00
yhh 53ded30e0b 优化圆和矩形碰撞检测 2020-06-11 10:06:29 +08:00
YHH 2eec9a82f9 新增box复合体 修复vector2运算问题 2020-06-11 00:03:26 +08:00
yhh 74cbb4c9fd 新增verletworld 2020-06-10 20:29:16 +08:00
yhh fa4c3c5d0b 新增相机bounds与可渲染组件bounds 并添加可渲染组件的剔除方法 2020-06-10 17:41:53 +08:00
yhh f62f449d99 新增renderableComponent 用于控制可渲染组件的基类 不要在transform当中直接更改目标属性 2020-06-10 16:25:39 +08:00
yhh 94818d5784 新增webgl帮助类 用于自定义绘图 2020-06-10 13:38:04 +08:00
yhh 5f7c13c8cd 新增breadthfirst与dijkstra寻路算法 2020-06-10 12:23:19 +08:00
YHH 538677575d 新增polygonmesh 2020-06-10 08:57:17 +08:00
YHH 1816b16924 readme 更新 2020-06-09 23:14:47 +08:00
68 changed files with 12194 additions and 1487 deletions
+34
View File
@@ -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
+15
View File
@@ -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}"
}
]
}
+37 -5
View File
@@ -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)
+4
View File
@@ -27,6 +27,10 @@
{
"name": "framework",
"path": "./libs/framework"
},
{
"name": "long",
"path": "./libs/long"
}
]
}
+428 -7
View File
@@ -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;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1092
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+3 -1
View File
@@ -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
View File
@@ -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");
// });
}
}
+3
View File
@@ -0,0 +1,3 @@
enum CoreEmitterType {
Update,
}
+29 -1
View File
@@ -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(){
+11 -1
View File
@@ -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 {
+391 -14
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1178 -1178
View File
File diff suppressed because it is too large Load Diff
+7 -5
View File
@@ -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;
}
}
+4
View File
@@ -0,0 +1,4 @@
class DebugDefaults {
public static verletParticle = 0xDC345E;
public static verletConstraintEdge = 0x433E36;
}
+5 -7
View File
@@ -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(){
}
/** 内部使用 运行时不应该调用 */
+63 -10
View File
@@ -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;
}
+57
View File
@@ -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;
}
}
+12
View File
@@ -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() {
}
}
+4
View File
@@ -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){
+3 -1
View File
@@ -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();
+1 -24
View File
@@ -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;
}
+38
View File
@@ -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){
+7 -4
View File
@@ -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());
+3 -1
View File
@@ -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;
}
+62
View File
@@ -0,0 +1,62 @@
/**
*
* isFlagSet之外flag参数是一个非移位的标志
* 使(0123)/
*/
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;
}
}
+21
View File
@@ -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);
}
}
+3 -3
View File
@@ -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;
}
}
+170 -5
View File
@@ -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
View File
@@ -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;
}
}
+1092
View File
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;
}
}
}
}
+6 -36
View File
@@ -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){
+36
View File
@@ -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);
}
}
+46
View File
@@ -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);
}
}
+64
View File
@@ -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);
}
}
+212
View File
@@ -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);
}
}
+10
View File
@@ -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;
}
/**
* [minmax]
* @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;
}
}
+165
View File
@@ -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();
}
}
+31
View File
@@ -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);
}
}
}
+54
View File
@@ -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;
}
}
+20
View File
@@ -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;
}
}
+16
View File
@@ -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;
}
}
+111
View File
@@ -0,0 +1,111 @@
/**
*
*/
class Triangulator {
/**
* 使
*/
public triangleIndices: number[] = [];
private _triPrev: number[] = new Array<number>(12);
private _triNext: number[] = new Array<number>(12);
/**
* CCWarePointsCCW参数传递为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;
}
}
+60
View File
@@ -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);
}
}
+48
View File
@@ -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);
}
}
}