移动类至es模块

This commit is contained in:
YHH
2020-07-23 09:10:27 +08:00
parent 15c0844e29
commit 814234ca61
20 changed files with 1101 additions and 998 deletions

View File

@@ -1,4 +1,5 @@
class ComponentPool<T extends PooledComponent>{ module es {
export class ComponentPool<T extends PooledComponent>{
private _cache: T[]; private _cache: T[];
private _type: any; private _type: any;
@@ -19,4 +20,5 @@ class ComponentPool<T extends PooledComponent>{
component.reset(); component.reset();
this._cache.push(component); this._cache.push(component);
} }
}
} }

View File

@@ -1,5 +1,6 @@
///<reference path="./RenderableComponent.ts" /> ///<reference path="./RenderableComponent.ts" />
class Mesh extends RenderableComponent { module es {
export class Mesh extends RenderableComponent {
private _mesh: egret.Mesh; private _mesh: egret.Mesh;
constructor(){ constructor(){
@@ -14,19 +15,10 @@ class Mesh extends RenderableComponent {
return this; return this;
} }
public onAddedToEntity(){
this.addChild(this._mesh);
}
public onRemovedFromEntity(){
this.removeChild(this._mesh);
}
public render(camera: Camera){
this.x = this.entity.position.x - camera.position.x + camera.origin.x;
this.y = this.entity.position.y - camera.position.y + camera.origin.y;
}
public reset() { public reset() {
} }
render(camera: es.Camera) {
}
}
} }

View File

@@ -1,5 +1,6 @@
///<reference path="./Collider.ts" /> ///<reference path="./Collider.ts" />
class BoxCollider extends Collider { module es {
export class BoxCollider extends Collider {
public get width(){ public get width(){
return (this.shape as Box).width; return (this.shape as Box).width;
} }
@@ -8,6 +9,43 @@ class BoxCollider extends Collider {
this.setWidth(value); this.setWidth(value);
} }
public get height(){
return (this.shape as Box).height;
}
public set height(value: number){
this.setHeight(value);
}
/**
* 零参数构造函数要求RenderableComponent在实体上这样碰撞器可以在实体被添加到场景时调整自身的大小。
*/
constructor(){
super();
// 我们在这里插入一个1x1框作为占位符直到碰撞器在下一阵被添加到实体并可以获得更精确的自动调整大小数据
this.shape = new Box(1, 1);
this._colliderRequiresAutoSizing = true;
}
/**
* 设置BoxCollider的大小
* @param width
* @param height
*/
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);
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
}
return this;
}
/** /**
* 设置BoxCollider的宽度 * 设置BoxCollider的宽度
* @param width * @param width
@@ -25,14 +63,6 @@ class BoxCollider extends Collider {
return this; return this;
} }
public get height(){
return (this.shape as Box).height;
}
public set height(value: number){
this.setHeight(value);
}
/** /**
* 设置BoxCollider的高度 * 设置BoxCollider的高度
* @param height * @param height
@@ -48,27 +78,8 @@ class BoxCollider extends Collider {
} }
} }
/** public toString(){
* 零参数构造函数要求RenderableComponent在实体上这样碰撞器可以在实体被添加到场景时调整自身的大小。 return `[BoxCollider: bounds: ${this.bounds}]`;
*/
constructor(){
super();
// 我们在这里插入一个1x1框作为占位符直到碰撞器在下一阵被添加到实体并可以获得更精确的自动调整大小数据
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);
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
}
return this;
} }
} }

View File

@@ -1,8 +1,10 @@
class CircleCollider extends Collider { module es {
public get radius(): number{ export class CircleCollider extends Collider {
public get radius(): number {
return (this.shape as Circle).radius; return (this.shape as Circle).radius;
} }
public set radius(value: number){
public set radius(value: number) {
this.setRadius(value); this.setRadius(value);
} }
@@ -11,7 +13,7 @@ class CircleCollider extends Collider {
* *
* @param radius * @param radius
*/ */
constructor(radius?: number){ constructor(radius?: number) {
super(); super();
if (radius) if (radius)
@@ -25,10 +27,10 @@ class CircleCollider extends Collider {
* 设置圆的半径 * 设置圆的半径
* @param radius * @param radius
*/ */
public setRadius(radius: number): CircleCollider{ public setRadius(radius: number): CircleCollider {
this._colliderRequiresAutoSizing = false; this._colliderRequiresAutoSizing = false;
let circle = this.shape as Circle; let circle = this.shape as Circle;
if (radius != circle.radius){ if (radius != circle.radius) {
circle.radius = radius; circle.radius = radius;
circle._originalRadius = radius; circle._originalRadius = radius;
@@ -38,4 +40,9 @@ class CircleCollider extends Collider {
return this; return this;
} }
public toString() {
return `[CircleCollider: bounds: ${this.bounds}, radius: ${(this.shape as Circle).radius}]`
}
}
} }

View File

@@ -1,12 +1,13 @@
/** module es {
/**
* 多边形应该以顺时针方式定义 * 多边形应该以顺时针方式定义
*/ */
class PolygonCollider extends Collider { export class PolygonCollider extends Collider {
/** /**
* 如果这些点没有居中它们将以localOffset的差异为居中。 * 如果这些点没有居中它们将以localOffset的差异为居中。
* @param points * @param points
*/ */
constructor(points: Vector2[]){ constructor(points: Vector2[]) {
super(); super();
// 第一点和最后一点决不能相同。我们想要一个开放的多边形 // 第一点和最后一点决不能相同。我们想要一个开放的多边形
@@ -16,7 +17,10 @@ class PolygonCollider extends Collider {
if (isPolygonClosed) if (isPolygonClosed)
points.splice(points.length - 1, 1); points.splice(points.length - 1, 1);
let center = Polygon.findPolygonCenter(points);
this.setLocalOffset(center);
Polygon.recenterPolygonVerts(points); Polygon.recenterPolygonVerts(points);
this.shape = new Polygon(points); this.shape = new Polygon(points);
} }
}
} }

View File

@@ -1,4 +1,23 @@
interface ITriggerListener { module es{
/**
* 当添加到组件时,每当实体上的冲突器与另一个组件重叠/退出时,将调用这些方法。
* ITriggerListener方法将在实现接口的触发器实体上的任何组件上调用。
* 注意这个接口只与Mover类一起工作
*/
export interface ITriggerListener {
/**
* 当碰撞器与触发碰撞器相交时调用。这是在触发碰撞器和触发碰撞器上调用的。
* 移动必须由Mover/ProjectileMover方法处理以使其自动工作。
* @param other
* @param local
*/
onTriggerEnter(other: Collider, local: Collider); onTriggerEnter(other: Collider, local: Collider);
/**
* 当另一个碰撞器离开触发碰撞器时调用
* @param other
* @param local
*/
onTriggerExit(other: Collider, local: Collider); onTriggerExit(other: Collider, local: Collider);
}
} }

View File

@@ -1,11 +1,12 @@
/** module es {
/**
* 辅助类说明了一种处理移动的方法,它考虑了包括触发器在内的所有冲突。 * 辅助类说明了一种处理移动的方法,它考虑了包括触发器在内的所有冲突。
* ITriggerListener接口用于管理对移动过程中违反的任何触发器的回调。 * ITriggerListener接口用于管理对移动过程中违反的任何触发器的回调。
* 一个物体只能通过移动器移动。要正确报告触发器的move方法。 * 一个物体只能通过移动器移动。要正确报告触发器的move方法。
* *
* 请注意多个移动者相互交互将多次调用ITriggerListener。 * 请注意多个移动者相互交互将多次调用ITriggerListener。
*/ */
class Mover extends Component { export class Mover extends Component {
private _triggerHelper: ColliderTriggerHelper; private _triggerHelper: ColliderTriggerHelper;
public onAddedToEntity(){ public onAddedToEntity(){
@@ -90,4 +91,5 @@ class Mover extends Component {
return collisionResult; return collisionResult;
} }
}
} }

View File

@@ -1,8 +1,9 @@
/** module es {
/**
* 只向itriggerlistener报告冲突的移动器 * 只向itriggerlistener报告冲突的移动器
* 该对象将始终移动完整的距离 * 该对象将始终移动完整的距离
*/ */
class ProjectileMover extends Component { export class ProjectileMover extends Component {
private _tempTriggerList: ITriggerListener[] = []; private _tempTriggerList: ITriggerListener[] = [];
private _collider: Collider; private _collider: Collider;
@@ -53,4 +54,5 @@ class ProjectileMover extends Component {
} }
this._tempTriggerList.length = 0; this._tempTriggerList.length = 0;
} }
}
} }

View File

@@ -1,5 +1,6 @@
///<reference path="./TiledSpriteRenderer.ts"/> ///<reference path="./TiledSpriteRenderer.ts"/>
class ScrollingSpriteRenderer extends TiledSpriteRenderer { module es {
export class ScrollingSpriteRenderer extends TiledSpriteRenderer {
public scrollSpeedX = 15; public scrollSpeedX = 15;
public scroolSpeedY = 0; public scroolSpeedY = 0;
private _scrollX = 0; private _scrollX = 0;
@@ -31,7 +32,6 @@ class ScrollingSpriteRenderer extends TiledSpriteRenderer {
cacheBitmap.cacheAsBitmap = true; cacheBitmap.cacheAsBitmap = true;
renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height)); renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height));
}
this.bitmap.texture = renderTexture;
} }
} }

View File

@@ -1,4 +1,5 @@
class Sprite { module es {
export class Sprite {
public texture2D: egret.Texture; public texture2D: egret.Texture;
public readonly sourceRect: Rectangle; public readonly sourceRect: Rectangle;
public readonly center: Vector2; public readonly center: Vector2;
@@ -21,4 +22,5 @@ class Sprite {
this.uvs.width = sourceRect.width * inverseTexW; this.uvs.width = sourceRect.width * inverseTexW;
this.uvs.height = sourceRect.height * inverseTexH; this.uvs.height = sourceRect.height * inverseTexH;
} }
}
} }

View File

@@ -1,4 +1,5 @@
class SpriteAnimation { module es {
export class SpriteAnimation {
public readonly sprites: Sprite[]; public readonly sprites: Sprite[];
public readonly frameRate: number; public readonly frameRate: number;
@@ -6,4 +7,5 @@ class SpriteAnimation {
this.sprites = sprites; this.sprites = sprites;
this.frameRate = frameRate; this.frameRate = frameRate;
} }
}
} }

View File

@@ -1,34 +1,103 @@
///<reference path="./SpriteRenderer.ts" /> ///<reference path="./SpriteRenderer.ts" />
class SpriteAnimator extends SpriteRenderer { module es {
/** 在动画完成时触发,包括动画名称; */ export enum LoopMode {
public onAnimationCompletedEvent: Function; /** 在一个循环序列[A][B][C][A][B][C][A][B][C]... */
/** 动画播放速度 */ loop,
/** [A][B][C]然后暂停设置时间为0 [A] */
once,
/** [A][B][C]。当它到达终点时,它会继续播放最后一帧,并且不会停止播放 */
clampForever,
/** 以一个乒乓循环的方式永远播放这个序列 [A][B][C][B][A][B][C][B]... */
pingPong,
/** 将顺序向前播放一次,然后返回到开始[A][B][C][B][A]然后暂停并设置时间为0 */
pingPongOnce,
}
export enum State {
none,
running,
paused,
completed,
}
export class SpriteAnimator extends SpriteRenderer {
/**
* 在动画完成时触发,包括动画名称
*/
public onAnimationCompletedEvent: (string) => {};
/**
* 动画播放速度
*/
public speed = 1; public speed = 1;
/** 动画的当前状态 */ /**
* 动画的当前状态
*/
public animationState = State.none; public animationState = State.none;
/** 当前动画 */ /**
* 当前动画
*/
public currentAnimation: SpriteAnimation; public currentAnimation: SpriteAnimation;
/** 当前动画的名称 */ /**
* 当前动画的名称
*/
public currentAnimationName: string; public currentAnimationName: string;
/** 当前动画的精灵数组中当前帧的索引 */ /**
* 当前动画的精灵数组中当前帧的索引
*/
public currentFrame: number; public currentFrame: number;
/** 检查当前动画是否正在运行 */
public get isRunning(): boolean{ /**
* 检查当前动画是否正在运行
*/
public get isRunning(): boolean {
return this.animationState == State.running; return this.animationState == State.running;
} }
/** 提供对可用动画列表的访问 */ /** 提供对可用动画列表的访问 */
public get animations(){ public get animations() {
return this._animations; return this._animations;
} }
private _animations: Map<string, SpriteAnimation> = new Map<string, SpriteAnimation>(); private _animations: Map<string, SpriteAnimation> = new Map<string, SpriteAnimation>();
private _elapsedTime: number = 0; public _elapsedTime: number = 0;
private _loopMode: LoopMode; public _loopMode: LoopMode;
constructor(sprite?: Sprite){ constructor(sprite?: Sprite) {
super(); super(sprite);
}
if (sprite) this.setSprite(sprite); public update() {
if (this.animationState != State.running || !this.currentAnimation) return;
let animation = this.currentAnimation;
let secondsPerFrame = 1 / (animation.frameRate * this.speed);
let iterationDuration = secondsPerFrame * animation.sprites.length;
this._elapsedTime += Time.deltaTime;
let time = Math.abs(this._elapsedTime);
// Once和PingPongOnce完成后重置为Time = 0
if (this._loopMode == LoopMode.once && time > iterationDuration ||
this._loopMode == LoopMode.pingPongOnce && time > iterationDuration * 2) {
this.animationState = State.completed;
this._elapsedTime = 0;
this.currentFrame = 0;
this.sprite = animation.sprites[this.currentFrame];
return;
}
// 弄清楚我们在哪个坐标系上
let i = Math.floor(time / secondsPerFrame);
let n = animation.sprites.length;
if (n > 2 && (this._loopMode == LoopMode.pingPong || this._loopMode == LoopMode.pingPongOnce)) {
// pingpong
let maxIndex = n - 1;
this.currentFrame = maxIndex - Math.abs(maxIndex - i % (maxIndex * 2));
} else {
this.currentFrame = i % n;
}
this.sprite = animation.sprites[this.currentFrame];
} }
/** /**
@@ -36,7 +105,8 @@ class SpriteAnimator extends SpriteRenderer {
* @param name * @param name
* @param animation * @param animation
*/ */
public addAnimation(name: string, animation: SpriteAnimation): SpriteAnimator{ public addAnimation(name: string, animation: SpriteAnimation): SpriteAnimator {
// 如果我们没有精灵,使用我们找到的第一帧
if (!this.sprite && animation.sprites.length > 0) if (!this.sprite && animation.sprites.length > 0)
this.setSprite(animation.sprites[0]); this.setSprite(animation.sprites[0]);
this._animations[name] = animation; this._animations[name] = animation;
@@ -48,7 +118,7 @@ class SpriteAnimator extends SpriteRenderer {
* @param name * @param name
* @param loopMode * @param loopMode
*/ */
public play(name: string, loopMode: LoopMode = null){ public play(name: string, loopMode: LoopMode = null) {
this.currentAnimation = this._animations[name]; this.currentAnimation = this._animations[name];
this.currentAnimationName = name; this.currentAnimationName = name;
this.currentFrame = 0; this.currentFrame = 0;
@@ -63,84 +133,32 @@ class SpriteAnimator extends SpriteRenderer {
* 检查动画是否正在播放(即动画是活动的)。它可能仍然处于暂停状态) * 检查动画是否正在播放(即动画是活动的)。它可能仍然处于暂停状态)
* @param name * @param name
*/ */
public isAnimationActive(name: string): boolean{ public isAnimationActive(name: string): boolean {
return this.currentAnimation && this.currentAnimationName == name; return this.currentAnimation && this.currentAnimationName == name;
} }
/** /**
* 暂停动画 * 暂停动画
*/ */
public pause(){ public pause() {
this.animationState = State.paused; this.animationState = State.paused;
} }
/** /**
* 继续动画 * 继续动画
*/ */
public unPause(){ public unPause() {
this.animationState = State.running; this.animationState = State.running;
} }
/** /**
* 停止当前动画并将其设为null * 停止当前动画并将其设为null
*/ */
public stop(){ public stop() {
this.currentAnimation = null; this.currentAnimation = null;
this.currentAnimationName = null; this.currentAnimationName = null;
this.currentFrame = 0; this.currentFrame = 0;
this.animationState = State.none; this.animationState = State.none;
} }
public update(){
if (this.animationState != State.running || !this.currentAnimation) return;
let animation = this.currentAnimation;
let secondsPerFrame = 1 / (animation.frameRate * this.speed);
let iterationDuration = secondsPerFrame * animation.sprites.length;
this._elapsedTime += Time.deltaTime;
let time = Math.abs(this._elapsedTime);
if (this._loopMode == LoopMode.once && time > iterationDuration ||
this._loopMode == LoopMode.pingPongOnce && time > iterationDuration * 2){
this.animationState = State.completed;
this._elapsedTime = 0;
this.currentFrame = 0;
this.sprite = animation.sprites[this.currentFrame];
return;
}
// 弄清楚我们在哪个坐标系上
let i = Math.floor(time / secondsPerFrame);
let n = animation.sprites.length;
if (n > 2 && (this._loopMode == LoopMode.pingPong || this._loopMode == LoopMode.pingPongOnce)){
// pingpong
let maxIndex = n - 1;
this.currentFrame = maxIndex - Math.abs(maxIndex - i % (maxIndex * 2));
}else{
this.currentFrame = i % n;
}
this.sprite = animation.sprites[this.currentFrame];
} }
} }
enum LoopMode {
/** 在一个循环序列[A][B][C][A][B][C][A][B][C]... */
loop,
/** [A][B][C]然后暂停设置时间为0 [A] */
once,
/** [A][B][C]。当它到达终点时,它会继续播放最后一帧,并且不会停止播放 */
clampForever,
/** 以一个乒乓循环的方式永远播放这个序列 [A][B][C][B][A][B][C][B]... */
pingPong,
/** 将顺序向前播放一次,然后返回到开始[A][B][C][B][A]然后暂停并设置时间为0 */
pingPongOnce,
}
enum State {
none,
running,
paused,
completed,
}

View File

@@ -1,8 +1,8 @@
module es { module es {
export class SpriteRenderer extends RenderableComponent { export class SpriteRenderer extends RenderableComponent {
public get bounds(){ public get bounds() {
if (this._areBoundsDirty){ if (this._areBoundsDirty) {
if (this._sprite){ if (this._sprite) {
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, this._origin, this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, this._origin,
this.entity.transform.scale, this.entity.transform.rotation, this._sprite.sourceRect.width, this.entity.transform.scale, this.entity.transform.rotation, this._sprite.sourceRect.width,
this._sprite.sourceRect.height); this._sprite.sourceRect.height);
@@ -16,7 +16,7 @@ module es {
/** /**
* 精灵的原点。这是在设置精灵时自动设置的 * 精灵的原点。这是在设置精灵时自动设置的
*/ */
public get origin(): Vector2{ public get origin(): Vector2 {
return this._origin; return this._origin;
} }
@@ -24,7 +24,7 @@ module es {
* 精灵的原点。这是在设置精灵时自动设置的 * 精灵的原点。这是在设置精灵时自动设置的
* @param value * @param value
*/ */
public set origin(value: Vector2){ public set origin(value: Vector2) {
this.setOrigin(value); this.setOrigin(value);
} }
@@ -32,7 +32,7 @@ module es {
* 用归一化方法设置原点 * 用归一化方法设置原点
* x/y 均为 0-1 * x/y 均为 0-1
*/ */
public get originNormalized(): Vector2{ public get originNormalized(): Vector2 {
return new Vector2(this._origin.x / this.width * this.entity.transform.scale.x, return new Vector2(this._origin.x / this.width * this.entity.transform.scale.x,
this._origin.y / this.height * this.entity.transform.scale.y); this._origin.y / this.height * this.entity.transform.scale.y);
} }
@@ -42,7 +42,7 @@ module es {
* x/y 均为 0-1 * x/y 均为 0-1
* @param value * @param value
*/ */
public set originNormalized(value: Vector2){ public set originNormalized(value: Vector2) {
this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x, this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x,
value.y * this.height / this.entity.transform.scale.y)); value.y * this.height / this.entity.transform.scale.y));
} }
@@ -67,11 +67,11 @@ module es {
protected _origin: Vector2; protected _origin: Vector2;
protected _sprite: Sprite; protected _sprite: Sprite;
constructor(sprite: Sprite | egret.Texture) { constructor(sprite: Sprite | egret.Texture = null) {
super(); super();
if (sprite instanceof Sprite) if (sprite instanceof Sprite)
this.setSprite(sprite); this.setSprite(sprite);
else if(sprite instanceof egret.Texture) else if (sprite instanceof egret.Texture)
this.setSprite(new Sprite(sprite)); this.setSprite(new Sprite(sprite));
} }
@@ -92,8 +92,8 @@ module es {
* 设置可渲染的原点 * 设置可渲染的原点
* @param origin * @param origin
*/ */
public setOrigin(origin: Vector2): SpriteRenderer{ public setOrigin(origin: Vector2): SpriteRenderer {
if (this._origin != origin){ if (this._origin != origin) {
this._origin = origin; this._origin = origin;
this._areBoundsDirty = true; this._areBoundsDirty = true;
} }
@@ -106,7 +106,7 @@ module es {
* x/y 均为 0-1 * x/y 均为 0-1
* @param value * @param value
*/ */
public setOriginNormalized(value: Vector2): SpriteRenderer{ public setOriginNormalized(value: Vector2): SpriteRenderer {
this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x, this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x,
value.y * this.height / this.entity.transform.scale.y)); value.y * this.height / this.entity.transform.scale.y));
return this; return this;

View File

@@ -1,8 +1,9 @@
///<reference path="./SpriteRenderer.ts" /> ///<reference path="./SpriteRenderer.ts" />
/** module es {
/**
* 滚动由两张图片组合而成 * 滚动由两张图片组合而成
*/ */
class TiledSpriteRenderer extends SpriteRenderer { export class TiledSpriteRenderer extends SpriteRenderer {
protected sourceRect: Rectangle; protected sourceRect: Rectangle;
protected leftTexture: egret.Bitmap; protected leftTexture: egret.Bitmap;
protected rightTexture: egret.Bitmap; protected rightTexture: egret.Bitmap;
@@ -21,7 +22,7 @@ class TiledSpriteRenderer extends SpriteRenderer {
} }
constructor(sprite: Sprite) { constructor(sprite: Sprite) {
super(); super(sprite);
this.leftTexture = new egret.Bitmap(); this.leftTexture = new egret.Bitmap();
this.rightTexture = new egret.Bitmap(); this.rightTexture = new egret.Bitmap();
@@ -32,7 +33,7 @@ class TiledSpriteRenderer extends SpriteRenderer {
this.sourceRect = sprite.sourceRect; this.sourceRect = sprite.sourceRect;
} }
public render(camera: Camera) { public render(camera: es.Camera) {
if (!this.sprite) if (!this.sprite)
return; return;
@@ -51,7 +52,6 @@ class TiledSpriteRenderer extends SpriteRenderer {
cacheBitmap.cacheAsBitmap = true; cacheBitmap.cacheAsBitmap = true;
renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height)); renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height));
}
this.bitmap.texture = renderTexture;
} }
} }

View File

@@ -1,9 +1,10 @@
/** module es {
/**
* 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。 * 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。
* *
* 它是由一个位向量实现的,但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。 * 它是由一个位向量实现的,但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。
*/ */
class BitSet{ export class BitSet{
private static LONG_MASK: number = 0x3f; private static LONG_MASK: number = 0x3f;
private _bits: number[]; private _bits: number[];
@@ -130,4 +131,5 @@ class BitSet{
this.clear(pos); this.clear(pos);
} }
} }
}
} }

View File

@@ -1,4 +1,5 @@
class ComponentTypeManager{ module es {
export class ComponentTypeManager{
private static _componentTypesMask: Map<any, number> = new Map<any, number>(); private static _componentTypesMask: Map<any, number> = new Map<any, number>();
public static add(type){ public static add(type){
@@ -15,4 +16,5 @@ class ComponentTypeManager{
return v; return v;
} }
}
} }

View File

@@ -1,4 +1,5 @@
class EntityProcessorList { module es {
export class EntityProcessorList {
private _processors: EntitySystem[] = []; private _processors: EntitySystem[] = [];
public add(processor: EntitySystem){ public add(processor: EntitySystem){
@@ -66,4 +67,5 @@ class EntityProcessorList {
return null; return null;
} }
}
} }

View File

@@ -55,18 +55,20 @@ module es {
public overlaps(other: Shape){ public overlaps(other: Shape){
// 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测 // 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测
if (this.isUnrotated){
if (other instanceof Box) if (other instanceof Box)
return this.bounds.intersects(other.bounds); return this.bounds.intersects(other.bounds);
if (other instanceof Circle) if (other instanceof Circle)
return Collisions.isRectToCircle(this.bounds, other.position, other.radius); return Collisions.isRectToCircle(this.bounds, other.position, other.radius);
}
return super.overlaps(other); return super.overlaps(other);
} }
public collidesWithShape(other: Shape){ public collidesWithShape(other: Shape){
// 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测 // 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测
if (other instanceof Box){ if (other instanceof Box && (other as Box).isUnrotated){
return ShapeCollisions.boxToBox(this, other); return ShapeCollisions.boxToBox(this, other);
} }
@@ -75,14 +77,11 @@ module es {
return super.collidesWithShape(other); return super.collidesWithShape(other);
} }
/**
*
* @param point
*/
public containsPoint(point: Vector2){ public containsPoint(point: Vector2){
if (this.isUnrotated)
return this.bounds.contains(point.x, point.y); return this.bounds.contains(point.x, point.y);
return super.containsPoint(point);
} }
} }
} }

View File

@@ -1,15 +1,8 @@
///<reference path="./Shape.ts" /> ///<reference path="./Shape.ts" />
class Circle extends Shape { module es {
export class Circle extends Shape {
public radius: number; public radius: number;
public _originalRadius: number; public _originalRadius: number;
public center = new Vector2();
public get position(){
return new Vector2(this.parent.x, this.parent.y);
}
public get bounds(){
return new Rectangle().setEgretRect(this.getBounds());
}
constructor(radius: number) { constructor(radius: number) {
super(); super();
@@ -17,12 +10,44 @@ class Circle extends Shape {
this._originalRadius = radius; this._originalRadius = radius;
} }
public pointCollidesWithShape(point: Vector2): CollisionResult { public recalculateBounds(collider: es.Collider) {
return ShapeCollisions.pointToCircle(point, this); // 如果我们没有旋转或不关心TRS我们使用localOffset作为中心
this.center = collider.localOffset;
if (collider.shouldColliderScaleAndRotateWithTransform) {
// 我们只将直线缩放为一个圆,所以我们将使用最大值
let scale = collider.entity.transform.scale;
let hasUnitScale = scale.x == 1 && scale.y == 1;
let maxScale = Math.max(scale.x, scale.y);
this.radius = this._originalRadius * maxScale;
if (collider.entity.transform.rotation != 0) {
// 为了处理偏移原点的旋转,我们只需要将圆心围绕(0,0)在一个圆上移动我们的偏移量就是0角
let offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * MathHelper.Rad2Deg;
let offsetLength = hasUnitScale ? collider._localOffsetLength : Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length();
this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotation + offsetAngle);
}
}
this.position = Vector2.add(collider.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`);
} }
public collidesWithShape(other: Shape): CollisionResult { public collidesWithShape(other: Shape): CollisionResult {
if (other instanceof Box) { if (other instanceof Box && (other as Box).isUnrotated) {
return ShapeCollisions.circleToBox(this, other); return ShapeCollisions.circleToBox(this, other);
} }
@@ -37,16 +62,8 @@ class Circle extends Shape {
throw new Error(`Collisions of Circle to ${other} are not supported`); throw new Error(`Collisions of Circle to ${other} are not supported`);
} }
public overlaps(other: Shape){ public pointCollidesWithShape(point: Vector2): CollisionResult {
if (other instanceof Box) return ShapeCollisions.pointToCircle(point, this);
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`);
} }
} }

View File

@@ -1,17 +1,28 @@
class SpatialHash { module es {
export class SpatialHash {
public gridBounds: Rectangle = new Rectangle(); public gridBounds: Rectangle = new Rectangle();
private _raycastParser: RaycastResultParser; public _raycastParser: RaycastResultParser;
/** 散列中每个单元格的大小 */ /**
private _cellSize: number; * 散列中每个单元格的大小
/** 1除以单元格大小。缓存结果因为它被大量使用。 */ */
private _inverseCellSize: number; public _cellSize: number;
/** 缓存的循环用于重叠检查 */ /**
private _overlapTestCircle: Circle = new Circle(0); * 1除以单元格大小。缓存结果因为它被大量使用。
/** 用于返回冲突信息的共享HashSet */ */
private _tempHashSet: Collider[] = []; public _inverseCellSize: number;
/** 保存所有数据的字典 */ /**
private _cellDict: NumberDictionary = new NumberDictionary(); * 缓存的循环用于重叠检查
*/
public _overlapTestCircle: Circle = new Circle(0);
/**
* 保存所有数据的字典
*/
public _cellDict: NumberDictionary = new NumberDictionary();
/**
* 用于返回冲突信息的共享HashSet
*/
public _tempHashSet: Collider[] = [];
constructor(cellSize: number = 100) { constructor(cellSize: number = 100) {
this._cellSize = cellSize; this._cellSize = cellSize;
@@ -20,24 +31,30 @@ class SpatialHash {
} }
/** /**
* 从SpatialHash中删除对象 * 获取单元格的x,y值作为世界空间的x,y值
* @param collider * @param x
* @param y
*/ */
public remove(collider: Collider) { private cellCoords(x: number, y: number): Vector2 {
let bounds = collider.registeredPhysicsBounds; return new Vector2(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize));
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++) { * 获取世界空间x,y值的单元格。
// 单元格应该始终存在,因为这个碰撞器应该在所有查询的单元格 * 如果单元格为空且createCellIfEmpty为true则会创建一个新的单元格
let cell = this.cellAtPosition(x, y); * @param x
if (!cell) * @param y
console.error(`removing Collider [${collider}] from a cell that it is not present in`); * @param createCellIfEmpty
else */
cell.remove(collider); 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;
} }
/** /**
@@ -69,52 +86,56 @@ class SpatialHash {
} }
} }
/**
* 从SpatialHash中删除对象
* @param collider
*/
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);
}
}
}
/**
* 使用蛮力方法从SpatialHash中删除对象
* @param obj
*/
public removeWithBruteForce(obj: Collider){
this._cellDict.remove(obj);
}
public clear(){ public clear(){
this._cellDict.clear(); this._cellDict.clear();
} }
/** /**
* 获取位于指定圆内的所有碰撞器 * debug绘制空间散列的内容
* @param circleCenter * @param secondsToDisplay
* @param radius * @param textScale
* @param results
* @param layerMask
*/ */
public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask) { public debugDraw(secondsToDisplay: number, textScale: number = 1){
let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2); for (let x = this.gridBounds.x; x <= this.gridBounds.right; x ++){
for (let y = this.gridBounds.y; y <= this.gridBounds.bottom; y ++){
this._overlapTestCircle.radius = radius; let cell = this.cellAtPosition(x, y);
// this._overlapTestCircle.position = circleCenter; if (cell && cell.length > 0)
this.debugDrawCellDetails(x, y, cell.length, secondsToDisplay, textScale);
let resultCounter = 0;
let aabbBroadphaseResult = this.aabbBroadphase(bounds, null, layerMask);
bounds = aabbBroadphaseResult.bounds;
let potentials = aabbBroadphaseResult.tempHashSet;
for (let i = 0; i < potentials.length; i++) {
let collider = potentials[i];
if (collider instanceof BoxCollider) {
results[resultCounter] = collider;
resultCounter++;
} else if (collider instanceof CircleCollider) {
if (collider.shape.overlaps(this._overlapTestCircle)){
results[resultCounter] = collider;
resultCounter ++;
} }
} else if(collider instanceof PolygonCollider) {
if (collider.shape.overlaps(this._overlapTestCircle)){
results[resultCounter] = collider;
resultCounter ++;
} }
} else {
throw new Error("overlapCircle against this collider type is not implemented!");
} }
// 如果我们所有的结果数据有了则返回 private debugDrawCellDetails(x: number, y: number, cellCount: number, secondsToDisplay = 0.5, textScale = 1){
if (resultCounter == results.length)
return resultCounter;
}
return resultCounter;
} }
/** /**
@@ -155,62 +176,56 @@ class SpatialHash {
} }
/** /**
* 获取世界空间x,y值的单元格。 * 获取位于指定圆内的所有碰撞器
* 如果单元格为空且createCellIfEmpty为true则会创建一个新的单元格 * @param circleCenter
* @param x * @param radius
* @param y * @param results
* @param createCellIfEmpty * @param layerMask
*/ */
private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) { public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask) {
let cell: Collider[] = this._cellDict.tryGetValue(x, y); let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2);
if (!cell) {
if (createCellIfEmpty) { this._overlapTestCircle.radius = radius;
cell = []; this._overlapTestCircle.position = circleCenter;
this._cellDict.add(x, y, cell);
let resultCounter = 0;
let aabbBroadphaseResult = this.aabbBroadphase(bounds, null, layerMask);
bounds = aabbBroadphaseResult.bounds;
let potentials = aabbBroadphaseResult.tempHashSet;
for (let i = 0; i < potentials.length; i++) {
let collider = potentials[i];
if (collider instanceof BoxCollider) {
results[resultCounter] = collider;
resultCounter++;
} else if (collider instanceof CircleCollider) {
if (collider.shape.overlaps(this._overlapTestCircle)){
results[resultCounter] = collider;
resultCounter ++;
} }
} else if(collider instanceof PolygonCollider) {
if (collider.shape.overlaps(this._overlapTestCircle)){
results[resultCounter] = collider;
resultCounter ++;
}
} else {
throw new Error("overlapCircle against this collider type is not implemented!");
}
// 如果我们所有的结果数据有了则返回
if (resultCounter == results.length)
return resultCounter;
}
return resultCounter;
} }
return cell;
} }
/** /**
* 获取单元格的x,y值作为世界空间的x,y值
* @param x
* @param y
*/
private cellCoords(x: number, y: number): Vector2 {
return new Vector2(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize));
}
/**
* debug绘制空间散列的内容
* @param secondsToDisplay
* @param textScale
*/
public debugDraw(secondsToDisplay: number, textScale: number = 1){
for (let x = this.gridBounds.x; x <= this.gridBounds.right; x ++){
for (let y = this.gridBounds.y; y <= this.gridBounds.bottom; y ++){
let cell = this.cellAtPosition(x, y);
if (cell && cell.length > 0)
this.debugDrawCellDetails(x, y, cell.length, secondsToDisplay, textScale);
}
}
}
private debugDrawCellDetails(x: number, y: number, cellCount: number, secondsToDisplay = 0.5, textScale = 1){
}
}
class RaycastResultParser {
}
/**
* 包装一个Unit32列表碰撞器字典 * 包装一个Unit32列表碰撞器字典
* 它的主要目的是将int、int x、y坐标散列到单个Uint32键中使用O(1)查找。 * 它的主要目的是将int、int x、y坐标散列到单个Uint32键中使用O(1)查找。
*/ */
class NumberDictionary { export class NumberDictionary {
private _store: Map<string, Collider[]> = new Map<string, Collider[]>(); public _store: Map<string, Collider[]> = new Map<string, Collider[]>();
/** /**
* 根据x和y值计算并返回散列键 * 根据x和y值计算并返回散列键
@@ -246,4 +261,9 @@ class NumberDictionary {
public clear() { public clear() {
this._store.clear(); this._store.clear();
} }
}
export class RaycastResultParser {
}
} }