移动类至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,22 +1,24 @@
class ComponentPool<T extends PooledComponent>{ module es {
private _cache: T[]; export class ComponentPool<T extends PooledComponent>{
private _type: any; private _cache: T[];
private _type: any;
constructor(typeClass: any){ constructor(typeClass: any){
this._type = typeClass; this._type = typeClass;
this._cache = []; this._cache = [];
} }
public obtain(): T{ public obtain(): T{
try { try {
return this._cache.length > 0 ? this._cache.shift() : new this._type(); return this._cache.length > 0 ? this._cache.shift() : new this._type();
} catch(err){ } catch(err){
throw new Error(this._type + err); throw new Error(this._type + err);
}
}
public free(component: T){
component.reset();
this._cache.push(component);
} }
} }
public free(component: T){
component.reset();
this._cache.push(component);
}
} }

View File

@@ -1,32 +1,24 @@
///<reference path="./RenderableComponent.ts" /> ///<reference path="./RenderableComponent.ts" />
class Mesh extends RenderableComponent { module es {
private _mesh: egret.Mesh; export class Mesh extends RenderableComponent {
private _mesh: egret.Mesh;
constructor(){ constructor(){
super(); super();
this._mesh = new egret.Mesh(); this._mesh = new egret.Mesh();
} }
public setTexture(texture: egret.Texture): Mesh{ public setTexture(texture: egret.Texture): Mesh{
this._mesh.texture = texture; this._mesh.texture = texture;
return this; return this;
} }
public onAddedToEntity(){ public reset() {
this.addChild(this._mesh); }
}
public onRemovedFromEntity(){ render(camera: es.Camera) {
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() {
} }
} }

View File

@@ -1,74 +1,85 @@
///<reference path="./Collider.ts" /> ///<reference path="./Collider.ts" />
class BoxCollider extends Collider { module es {
public get width(){ export class BoxCollider extends Collider {
return (this.shape as Box).width; public get width(){
} return (this.shape as Box).width;
public set width(value: number){
this.setWidth(value);
}
/**
* 设置BoxCollider的宽度
* @param width
*/
public setWidth(width: number): BoxCollider{
this._colliderRequiresAutoSizing = false;
let box = this.shape as Box;
if (width != box.width){
// 更新框,改变边界,如果我们需要更新物理系统中的边界
box.updateBox(width, box.height);
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
} }
return this; public set width(value: number){
} this.setWidth(value);
public get height(){
return (this.shape as Box).height;
}
public set height(value: number){
this.setHeight(value);
}
/**
* 设置BoxCollider的高度
* @param height
*/
public setHeight(height: number){
this._colliderRequiresAutoSizing = false;
let box = this.shape as Box;
if (height != box.height){
// 更新框,改变边界,如果我们需要更新物理系统中的边界
box.updateBox(box.width, height);
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
}
}
/**
* 零参数构造函数要求RenderableComponent在实体上这样碰撞器可以在实体被添加到场景时调整自身的大小。
*/
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; 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的宽度
* @param width
*/
public setWidth(width: number): BoxCollider{
this._colliderRequiresAutoSizing = false;
let box = this.shape as Box;
if (width != box.width){
// 更新框,改变边界,如果我们需要更新物理系统中的边界
box.updateBox(width, box.height);
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
}
return this;
}
/**
* 设置BoxCollider的高度
* @param height
*/
public setHeight(height: number){
this._colliderRequiresAutoSizing = false;
let box = this.shape as Box;
if (height != box.height){
// 更新框,改变边界,如果我们需要更新物理系统中的边界
box.updateBox(box.width, height);
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
}
}
public toString(){
return `[BoxCollider: bounds: ${this.bounds}]`;
}
} }
} }

View File

@@ -1,41 +1,48 @@
class CircleCollider extends Collider { module es {
public get radius(): number{ export class CircleCollider extends Collider {
return (this.shape as Circle).radius; public get radius(): number {
} return (this.shape as Circle).radius;
public set radius(value: number){
this.setRadius(value);
}
/**
* 创建一个有半径的圆
*
* @param radius
*/
constructor(radius?: number){
super();
if (radius)
this._colliderRequiresAutoSizing = true;
// 我们在这里插入一个1px的圆圈作为占位符
// 直到碰撞器被添加到实体并可以获得更精确的自动调整大小数据的下一帧
this.shape = new Circle(radius ? radius : 1);
}
/**
* 设置圆的半径
* @param radius
*/
public setRadius(radius: number): CircleCollider{
this._colliderRequiresAutoSizing = false;
let circle = this.shape as Circle;
if (radius != circle.radius){
circle.radius = radius;
circle._originalRadius = radius;
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
} }
return this; public set radius(value: number) {
this.setRadius(value);
}
/**
* 创建一个有半径的圆
*
* @param radius
*/
constructor(radius?: number) {
super();
if (radius)
this._colliderRequiresAutoSizing = true;
// 我们在这里插入一个1px的圆圈作为占位符
// 直到碰撞器被添加到实体并可以获得更精确的自动调整大小数据的下一帧
this.shape = new Circle(radius ? radius : 1);
}
/**
* 设置圆的半径
* @param radius
*/
public setRadius(radius: number): CircleCollider {
this._colliderRequiresAutoSizing = false;
let circle = this.shape as Circle;
if (radius != circle.radius) {
circle.radius = radius;
circle._originalRadius = radius;
if (this.entity && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
}
return this;
}
public toString() {
return `[CircleCollider: bounds: ${this.bounds}, radius: ${(this.shape as Circle).radius}]`
}
} }
} }

View File

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

View File

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

View File

@@ -1,93 +1,95 @@
/** module es {
* 辅助类说明了一种处理移动的方法,它考虑了包括触发器在内的所有冲突。
* ITriggerListener接口用于管理对移动过程中违反的任何触发器的回调。
* 一个物体只能通过移动器移动。要正确报告触发器的move方法。
*
* 请注意多个移动者相互交互将多次调用ITriggerListener。
*/
class Mover extends Component {
private _triggerHelper: ColliderTriggerHelper;
public onAddedToEntity(){
this._triggerHelper = new ColliderTriggerHelper(this.entity);
}
/** /**
* 计算修改运动矢量的运动,以考虑移动时可能发生的碰撞 * 辅助类说明了一种处理移动的方法,它考虑了包括触发器在内的所有冲突。
* @param motion * ITriggerListener接口用于管理对移动过程中违反的任何触发器的回调。
* 一个物体只能通过移动器移动。要正确报告触发器的move方法。
*
* 请注意多个移动者相互交互将多次调用ITriggerListener。
*/ */
public calculateMovement(motion: Vector2){ export class Mover extends Component {
let collisionResult = new CollisionResult(); private _triggerHelper: ColliderTriggerHelper;
if (!this.entity.getComponent(Collider) || !this._triggerHelper){ public onAddedToEntity(){
return null; this._triggerHelper = new ColliderTriggerHelper(this.entity);
} }
// 移动所有的非触发碰撞器并获得最近的碰撞 /**
let colliders: Collider[] = this.entity.getComponents(Collider); * 计算修改运动矢量的运动,以考虑移动时可能发生的碰撞
for (let i = 0; i < colliders.length; i ++){ * @param motion
let collider = colliders[i]; */
public calculateMovement(motion: Vector2){
let collisionResult = new CollisionResult();
// 不检测触发器 在我们移动后会重新访问它 if (!this.entity.getComponent(Collider) || !this._triggerHelper){
if (collider.isTrigger) return null;
continue; }
// 获取我们在新位置可能发生碰撞的任何东西 // 移动所有的非触发碰撞器并获得最近的碰撞
let bounds = collider.bounds; let colliders: Collider[] = this.entity.getComponents(Collider);
bounds.x += motion.x; for (let i = 0; i < colliders.length; i ++){
bounds.y += motion.y; let collider = colliders[i];
let boxcastResult = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers);
bounds = boxcastResult.bounds;
let neighbors = boxcastResult.tempHashSet;
for (let j = 0; j < neighbors.length; j ++){ // 不检测触发器 在我们移动后会重新访问它
let neighbor = neighbors[j]; if (collider.isTrigger)
// 不检测触发器
if (neighbor.isTrigger)
continue; continue;
let _internalcollisionResult = collider.collidesWith(neighbor, motion); // 获取我们在新位置可能发生碰撞的任何东西
if (_internalcollisionResult){ let bounds = collider.bounds;
// 如果碰撞 则退回之前的移动量 bounds.x += motion.x;
motion = Vector2.subtract(motion, _internalcollisionResult.minimumTranslationVector); bounds.y += motion.y;
let boxcastResult = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers);
bounds = boxcastResult.bounds;
let neighbors = boxcastResult.tempHashSet;
// 如果我们碰到多个对象,为了简单起见,只取第一个。 for (let j = 0; j < neighbors.length; j ++){
if (_internalcollisionResult.collider){ let neighbor = neighbors[j];
collisionResult = _internalcollisionResult; // 不检测触发器
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: collisionResult, motion: motion};
} }
ListPool.free(colliders); /**
* 将calculatemomovement应用到实体并更新triggerHelper
* @param motion
*/
public applyMovement(motion: Vector2){
// 移动实体到它的新位置,如果我们有一个碰撞,否则移动全部数量。当碰撞发生时,运动被更新
this.entity.position = Vector2.add(this.entity.position, motion);
return {collisionResult: collisionResult, motion: motion}; // 对所有是触发器的碰撞器与所有宽相位碰撞器进行重叠检查。任何重叠都会导致触发事件。
} if (this._triggerHelper)
this._triggerHelper.update();
}
/** /**
* 将calculatemomovement应用到实体并更新triggerHelper * 通过调用calculateMovement和applyMovement来移动考虑碰撞的实体;
* @param motion * @param motion
*/ */
public applyMovement(motion: Vector2){ public move(motion: Vector2){
// 移动实体到它的新位置,如果我们有一个碰撞,否则移动全部数量。当碰撞发生时,运动被更新 let movementResult = this.calculateMovement(motion);
this.entity.position = Vector2.add(this.entity.position, motion); let collisionResult = movementResult.collisionResult;
motion = movementResult.motion;
// 对所有是触发器的碰撞器与所有宽相位碰撞器进行重叠检查。任何重叠都会导致触发事件。 this.applyMovement(motion);
if (this._triggerHelper)
this._triggerHelper.update();
}
/** return collisionResult;
* 通过调用calculateMovement和applyMovement来移动考虑碰撞的实体; }
* @param motion
*/
public move(motion: Vector2){
let movementResult = this.calculateMovement(motion);
let collisionResult = movementResult.collisionResult;
motion = movementResult.motion;
this.applyMovement(motion);
return collisionResult;
} }
} }

View File

@@ -1,56 +1,58 @@
/** module es {
* 只向itriggerlistener报告冲突的移动器
* 该对象将始终移动完整的距离
*/
class ProjectileMover extends Component {
private _tempTriggerList: ITriggerListener[] = [];
private _collider: Collider;
public onAddedToEntity(){
this._collider = this.entity.getComponent<Collider>(Collider);
if (!this._collider)
console.warn("ProjectileMover has no Collider. ProjectilMover requires a Collider!");
}
/** /**
* 移动考虑碰撞的实体 * 只向itriggerlistener报告冲突的移动器
* @param motion * 该对象将始终移动完整的距离
*/ */
public move(motion: Vector2): boolean{ export class ProjectileMover extends Component {
if (!this._collider) private _tempTriggerList: ITriggerListener[] = [];
return false; private _collider: Collider;
let didCollide = false; public onAddedToEntity(){
this._collider = this.entity.getComponent<Collider>(Collider);
if (!this._collider)
console.warn("ProjectileMover has no Collider. ProjectilMover requires a Collider!");
}
// 获取我们在新位置可能发生碰撞的任何东西 /**
this.entity.position = Vector2.add(this.entity.position, motion); * 移动考虑碰撞的实体
* @param motion
*/
public move(motion: Vector2): boolean{
if (!this._collider)
return false;
// 获取任何可能在新位置发生碰撞的东西 let didCollide = false;
let neighbors = Physics.boxcastBroadphase(this._collider.bounds, this._collider.collidesWithLayers);
for (let i = 0; i < neighbors.colliders.length; i ++){ // 获取我们在新位置可能发生碰撞的任何东西
let neighbor = neighbors.colliders[i]; this.entity.position = Vector2.add(this.entity.position, motion);
if (this._collider.overlaps(neighbor) && neighbor.enabled){
didCollide = true; // 获取任何可能在新位置发生碰撞的东西
this.notifyTriggerListeners(this._collider, neighbor); let neighbors = Physics.boxcastBroadphase(this._collider.bounds, this._collider.collidesWithLayers);
for (let i = 0; i < neighbors.colliders.length; i ++){
let neighbor = neighbors.colliders[i];
if (this._collider.overlaps(neighbor) && neighbor.enabled){
didCollide = true;
this.notifyTriggerListeners(this._collider, neighbor);
}
} }
return didCollide;
} }
return didCollide; private notifyTriggerListeners(self: Collider, other: Collider){
} // 通知我们重叠的碰撞器实体上的任何侦听器
other.entity.getComponents("ITriggerListener", this._tempTriggerList);
for (let i = 0; i < this._tempTriggerList.length; i ++){
this._tempTriggerList[i].onTriggerEnter(self, other);
}
this._tempTriggerList.length = 0;
private notifyTriggerListeners(self: Collider, other: Collider){ // 通知此实体上的任何侦听器
// 通知我们重叠的碰撞器实体上的任何侦听器 this.entity.getComponents("ITriggerListener", this._tempTriggerList);
other.entity.getComponents("ITriggerListener", this._tempTriggerList); for (let i = 0; i < this._tempTriggerList.length; i ++){
for (let i = 0; i < this._tempTriggerList.length; i ++){ this._tempTriggerList[i].onTriggerEnter(other, self);
this._tempTriggerList[i].onTriggerEnter(self, other); }
this._tempTriggerList.length = 0;
} }
this._tempTriggerList.length = 0;
// 通知此实体上的任何侦听器
this.entity.getComponents("ITriggerListener", this._tempTriggerList);
for (let i = 0; i < this._tempTriggerList.length; i ++){
this._tempTriggerList[i].onTriggerEnter(other, self);
}
this._tempTriggerList.length = 0;
} }
} }

View File

@@ -1,37 +1,37 @@
///<reference path="./TiledSpriteRenderer.ts"/> ///<reference path="./TiledSpriteRenderer.ts"/>
class ScrollingSpriteRenderer extends TiledSpriteRenderer { module es {
public scrollSpeedX = 15; export class ScrollingSpriteRenderer extends TiledSpriteRenderer {
public scroolSpeedY = 0; public scrollSpeedX = 15;
private _scrollX = 0; public scroolSpeedY = 0;
private _scrollY = 0; private _scrollX = 0;
private _scrollY = 0;
public update(){ public update(){
this._scrollX += this.scrollSpeedX * Time.deltaTime; this._scrollX += this.scrollSpeedX * Time.deltaTime;
this._scrollY += this.scroolSpeedY * Time.deltaTime; this._scrollY += this.scroolSpeedY * Time.deltaTime;
this.sourceRect.x = this._scrollX; this.sourceRect.x = this._scrollX;
this.sourceRect.y = this._scrollY; this.sourceRect.y = this._scrollY;
} }
public render(camera: Camera) { public render(camera: Camera) {
if (!this.sprite) if (!this.sprite)
return; return;
super.render(camera); super.render(camera);
let renderTexture = new egret.RenderTexture(); let renderTexture = new egret.RenderTexture();
let cacheBitmap = new egret.DisplayObjectContainer(); let cacheBitmap = new egret.DisplayObjectContainer();
cacheBitmap.removeChildren(); cacheBitmap.removeChildren();
cacheBitmap.addChild(this.leftTexture); cacheBitmap.addChild(this.leftTexture);
cacheBitmap.addChild(this.rightTexture); cacheBitmap.addChild(this.rightTexture);
this.leftTexture.x = this.sourceRect.x; this.leftTexture.x = this.sourceRect.x;
this.rightTexture.x = this.sourceRect.x - this.sourceRect.width; this.rightTexture.x = this.sourceRect.x - this.sourceRect.width;
this.leftTexture.y = this.sourceRect.y; this.leftTexture.y = this.sourceRect.y;
this.rightTexture.y = this.sourceRect.y; this.rightTexture.y = this.sourceRect.y;
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,24 +1,26 @@
class Sprite { module es {
public texture2D: egret.Texture; export class Sprite {
public readonly sourceRect: Rectangle; public texture2D: egret.Texture;
public readonly center: Vector2; public readonly sourceRect: Rectangle;
public origin: Vector2; public readonly center: Vector2;
public readonly uvs: Rectangle = new Rectangle(); public origin: Vector2;
public readonly uvs: Rectangle = new Rectangle();
constructor(texture: egret.Texture, constructor(texture: egret.Texture,
sourceRect: Rectangle = new Rectangle(0, 0, texture.textureWidth, texture.textureHeight), sourceRect: Rectangle = new Rectangle(0, 0, texture.textureWidth, texture.textureHeight),
origin: Vector2 = sourceRect.getHalfSize()) { origin: Vector2 = sourceRect.getHalfSize()) {
this.texture2D = texture; this.texture2D = texture;
this.sourceRect = sourceRect; this.sourceRect = sourceRect;
this.center = new Vector2(sourceRect.width * 0.5, sourceRect.height * 0.5); this.center = new Vector2(sourceRect.width * 0.5, sourceRect.height * 0.5);
this.origin = origin; this.origin = origin;
let inverseTexW = 1 / texture.textureWidth; let inverseTexW = 1 / texture.textureWidth;
let inverseTexH = 1 / texture.textureHeight; let inverseTexH = 1 / texture.textureHeight;
this.uvs.x = sourceRect.x * inverseTexW; this.uvs.x = sourceRect.x * inverseTexW;
this.uvs.y = sourceRect.y * inverseTexH; this.uvs.y = sourceRect.y * inverseTexH;
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,9 +1,11 @@
class SpriteAnimation { module es {
public readonly sprites: Sprite[]; export class SpriteAnimation {
public readonly frameRate: number; public readonly sprites: Sprite[];
public readonly frameRate: number;
constructor(sprites: Sprite[], frameRate: number){ constructor(sprites: Sprite[], frameRate: number){
this.sprites = sprites; this.sprites = sprites;
this.frameRate = frameRate; this.frameRate = frameRate;
}
} }
} }

View File

@@ -1,108 +1,84 @@
///<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,
public speed = 1; /** [A][B][C]然后暂停设置时间为0 [A] */
/** 动画的当前状态 */ once,
public animationState = State.none; /** [A][B][C]。当它到达终点时,它会继续播放最后一帧,并且不会停止播放 */
/** 当前动画 */ clampForever,
public currentAnimation: SpriteAnimation; /** 以一个乒乓循环的方式永远播放这个序列 [A][B][C][B][A][B][C][B]... */
/** 当前动画的名称 */ pingPong,
public currentAnimationName: string; /** 将顺序向前播放一次,然后返回到开始[A][B][C][B][A]然后暂停并设置时间为0 */
/** 当前动画的精灵数组中当前帧的索引 */ pingPongOnce,
public currentFrame: number;
/** 检查当前动画是否正在运行 */
public get isRunning(): boolean{
return this.animationState == State.running;
} }
/** 提供对可用动画列表的访问 */ export enum State {
public get animations(){ none,
return this._animations; running,
} paused,
private _animations: Map<string, SpriteAnimation> = new Map<string, SpriteAnimation>(); completed,
private _elapsedTime: number = 0;
private _loopMode: LoopMode;
constructor(sprite?: Sprite){
super();
if (sprite) this.setSprite(sprite);
} }
/** export class SpriteAnimator extends SpriteRenderer {
* 添加一个SpriteAnimation /**
* @param name * 在动画完成时触发,包括动画名称
* @param animation */
*/ public onAnimationCompletedEvent: (string) => {};
public addAnimation(name: string, animation: SpriteAnimation): SpriteAnimator{ /**
if (!this.sprite && animation.sprites.length > 0) * 动画播放速度
this.setSprite(animation.sprites[0]); */
this._animations[name] = animation; public speed = 1;
return this; /**
} * 动画的当前状态
*/
public animationState = State.none;
/**
* 当前动画
*/
public currentAnimation: SpriteAnimation;
/**
* 当前动画的名称
*/
public currentAnimationName: string;
/**
* 当前动画的精灵数组中当前帧的索引
*/
public currentFrame: number;
/** /**
* 以给定的名称放置动画。如果没有指定循环模式,则默认为循环 * 检查当前动画是否正在运行
* @param name */
* @param loopMode public get isRunning(): boolean {
*/ return this.animationState == State.running;
public play(name: string, loopMode: LoopMode = null){ }
this.currentAnimation = this._animations[name];
this.currentAnimationName = name;
this.currentFrame = 0;
this.animationState = State.running;
this.sprite = this.currentAnimation.sprites[0]; /** 提供对可用动画列表的访问 */
this._elapsedTime = 0; public get animations() {
this._loopMode = loopMode ? loopMode : LoopMode.loop; return this._animations;
} }
/** private _animations: Map<string, SpriteAnimation> = new Map<string, SpriteAnimation>();
* 检查动画是否正在播放(即动画是活动的)。它可能仍然处于暂停状态) public _elapsedTime: number = 0;
* @param name public _loopMode: LoopMode;
*/
public isAnimationActive(name: string): boolean{
return this.currentAnimation && this.currentAnimationName == name;
}
/** constructor(sprite?: Sprite) {
* 暂停动画 super(sprite);
*/ }
public pause(){
this.animationState = State.paused;
}
/** public update() {
* 继续动画 if (this.animationState != State.running || !this.currentAnimation) return;
*/
public unPause(){
this.animationState = State.running;
}
/** let animation = this.currentAnimation;
* 停止当前动画并将其设为null let secondsPerFrame = 1 / (animation.frameRate * this.speed);
*/ let iterationDuration = secondsPerFrame * animation.sprites.length;
public stop(){
this.currentAnimation = null;
this.currentAnimationName = null;
this.currentFrame = 0;
this.animationState = State.none;
}
public update(){ this._elapsedTime += Time.deltaTime;
if (this.animationState != State.running || !this.currentAnimation) return; let time = Math.abs(this._elapsedTime);
let animation = this.currentAnimation; // Once和PingPongOnce完成后重置为Time = 0
let secondsPerFrame = 1 / (animation.frameRate * this.speed); if (this._loopMode == LoopMode.once && time > iterationDuration ||
let iterationDuration = secondsPerFrame * animation.sprites.length; this._loopMode == LoopMode.pingPongOnce && time > iterationDuration * 2) {
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.animationState = State.completed;
this._elapsedTime = 0; this._elapsedTime = 0;
this.currentFrame = 0; this.currentFrame = 0;
@@ -113,34 +89,76 @@ class SpriteAnimator extends SpriteRenderer {
// 弄清楚我们在哪个坐标系上 // 弄清楚我们在哪个坐标系上
let i = Math.floor(time / secondsPerFrame); let i = Math.floor(time / secondsPerFrame);
let n = animation.sprites.length; let n = animation.sprites.length;
if (n > 2 && (this._loopMode == LoopMode.pingPong || this._loopMode == LoopMode.pingPongOnce)){ if (n > 2 && (this._loopMode == LoopMode.pingPong || this._loopMode == LoopMode.pingPongOnce)) {
// pingpong // pingpong
let maxIndex = n - 1; let maxIndex = n - 1;
this.currentFrame = maxIndex - Math.abs(maxIndex - i % (maxIndex * 2)); this.currentFrame = maxIndex - Math.abs(maxIndex - i % (maxIndex * 2));
}else{ } else {
this.currentFrame = i % n; this.currentFrame = i % n;
} }
this.sprite = animation.sprites[this.currentFrame]; this.sprite = animation.sprites[this.currentFrame];
}
/**
* 添加一个SpriteAnimation
* @param name
* @param animation
*/
public addAnimation(name: string, animation: SpriteAnimation): SpriteAnimator {
// 如果我们没有精灵,使用我们找到的第一帧
if (!this.sprite && animation.sprites.length > 0)
this.setSprite(animation.sprites[0]);
this._animations[name] = animation;
return this;
}
/**
* 以给定的名称放置动画。如果没有指定循环模式,则默认为循环
* @param name
* @param loopMode
*/
public play(name: string, loopMode: LoopMode = null) {
this.currentAnimation = this._animations[name];
this.currentAnimationName = name;
this.currentFrame = 0;
this.animationState = State.running;
this.sprite = this.currentAnimation.sprites[0];
this._elapsedTime = 0;
this._loopMode = loopMode ? loopMode : LoopMode.loop;
}
/**
* 检查动画是否正在播放(即动画是活动的)。它可能仍然处于暂停状态)
* @param name
*/
public isAnimationActive(name: string): boolean {
return this.currentAnimation && this.currentAnimationName == name;
}
/**
* 暂停动画
*/
public pause() {
this.animationState = State.paused;
}
/**
* 继续动画
*/
public unPause() {
this.animationState = State.running;
}
/**
* 停止当前动画并将其设为null
*/
public stop() {
this.currentAnimation = null;
this.currentAnimationName = null;
this.currentFrame = 0;
this.animationState = State.none;
}
} }
} }
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,57 +1,57 @@
///<reference path="./SpriteRenderer.ts" /> ///<reference path="./SpriteRenderer.ts" />
/** module es {
* 滚动由两张图片组合而成 /**
*/ * 滚动由两张图片组合而成
class TiledSpriteRenderer extends SpriteRenderer { */
protected sourceRect: Rectangle; export class TiledSpriteRenderer extends SpriteRenderer {
protected leftTexture: egret.Bitmap; protected sourceRect: Rectangle;
protected rightTexture: egret.Bitmap; protected leftTexture: egret.Bitmap;
protected rightTexture: egret.Bitmap;
public get scrollX() { public get scrollX() {
return this.sourceRect.x; return this.sourceRect.x;
} }
public set scrollX(value: number) { public set scrollX(value: number) {
this.sourceRect.x = value; this.sourceRect.x = value;
} }
public get scrollY() { public get scrollY() {
return this.sourceRect.y; return this.sourceRect.y;
} }
public set scrollY(value: number) { public set scrollY(value: number) {
this.sourceRect.y = value; this.sourceRect.y = value;
} }
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();
this.leftTexture.texture = sprite.texture2D; this.leftTexture.texture = sprite.texture2D;
this.rightTexture.texture = sprite.texture2D; this.rightTexture.texture = sprite.texture2D;
this.setSprite(sprite); this.setSprite(sprite);
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;
super.render(camera); super.render(camera);
let renderTexture = new egret.RenderTexture(); let renderTexture = new egret.RenderTexture();
let cacheBitmap = new egret.DisplayObjectContainer(); let cacheBitmap = new egret.DisplayObjectContainer();
cacheBitmap.removeChildren(); cacheBitmap.removeChildren();
cacheBitmap.addChild(this.leftTexture); cacheBitmap.addChild(this.leftTexture);
cacheBitmap.addChild(this.rightTexture); cacheBitmap.addChild(this.rightTexture);
this.leftTexture.x = this.sourceRect.x; this.leftTexture.x = this.sourceRect.x;
this.rightTexture.x = this.sourceRect.x - this.sourceRect.width; this.rightTexture.x = this.sourceRect.x - this.sourceRect.width;
this.leftTexture.y = this.sourceRect.y; this.leftTexture.y = this.sourceRect.y;
this.rightTexture.y = this.sourceRect.y; this.rightTexture.y = this.sourceRect.y;
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,133 +1,135 @@
/** module es {
* 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。 /**
* * 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。
* 它是由一个位向量实现的,但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。 *
*/ * 它是由一个位向量实现的,但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。
class BitSet{ */
private static LONG_MASK: number = 0x3f; export class BitSet{
private _bits: number[]; private static LONG_MASK: number = 0x3f;
private _bits: number[];
constructor(nbits: number = 64){ constructor(nbits: number = 64){
let length = nbits >> 6; let length = nbits >> 6;
if ((nbits & BitSet.LONG_MASK) != 0) if ((nbits & BitSet.LONG_MASK) != 0)
length ++; length ++;
this._bits = new Array(length); this._bits = new Array(length);
} }
public and(bs: BitSet){ public and(bs: BitSet){
let max = Math.min(this._bits.length, bs._bits.length); let max = Math.min(this._bits.length, bs._bits.length);
let i; let i;
for (let i = 0; i < max; ++i) for (let i = 0; i < max; ++i)
this._bits[i] &= bs._bits[i]; this._bits[i] &= bs._bits[i];
while (i < this._bits.length) while (i < this._bits.length)
this._bits[i ++] = 0; this._bits[i ++] = 0;
} }
public andNot(bs: BitSet){ public andNot(bs: BitSet){
let i = Math.min(this._bits.length, bs._bits.length); let i = Math.min(this._bits.length, bs._bits.length);
while(--i >= 0) while(--i >= 0)
this._bits[i] &= ~bs._bits[i]; this._bits[i] &= ~bs._bits[i];
} }
public cardinality(): number{ public cardinality(): number{
let card = 0; let card = 0;
for (let i = this._bits.length - 1; i >= 0; i --){ for (let i = this._bits.length - 1; i >= 0; i --){
let a = this._bits[i]; let a = this._bits[i];
if (a == 0) if (a == 0)
continue; continue;
if (a == -1){ if (a == -1){
card += 64; card += 64;
continue; continue;
}
a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555);
a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333);
let b = ((a >> 32) + a);
b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f);
b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff);
card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff);
} }
a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555); return card;
a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333);
let b = ((a >> 32) + a);
b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f);
b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff);
card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff);
} }
return card; public clear(pos?: number){
} if (pos != undefined){
let offset = pos >> 6;
this.ensure(offset);
this._bits[offset] &= ~(1 << pos);
}else{
for (let i = 0; i < this._bits.length; i ++)
this._bits[i] = 0;
}
}
public clear(pos?: number){ private ensure(lastElt: number){
if (pos != undefined){ if (lastElt >= this._bits.length){
let nd = new Number[lastElt + 1];
nd = this._bits.copyWithin(0, 0, this._bits.length);
this._bits = nd;
}
}
public get(pos: number): boolean{
let offset = pos >> 6; let offset = pos >> 6;
this.ensure(offset); if (offset >= this._bits.length)
this._bits[offset] &= ~(1 << pos);
}else{
for (let i = 0; i < this._bits.length; i ++)
this._bits[i] = 0;
}
}
private ensure(lastElt: number){
if (lastElt >= this._bits.length){
let nd = new Number[lastElt + 1];
nd = this._bits.copyWithin(0, 0, this._bits.length);
this._bits = nd;
}
}
public get(pos: number): boolean{
let offset = pos >> 6;
if (offset >= this._bits.length)
return false;
return (this._bits[offset] & (1 << pos)) != 0;
}
public intersects(set: BitSet){
let i = Math.min(this._bits.length, set._bits.length);
while (--i >= 0){
if ((this._bits[i] & set._bits[i]) != 0)
return true;
}
return false;
}
public isEmpty(): boolean{
for (let i = this._bits.length - 1; i >= 0; i --){
if (this._bits[i])
return false; return false;
return (this._bits[offset] & (1 << pos)) != 0;
} }
return true; public intersects(set: BitSet){
} let i = Math.min(this._bits.length, set._bits.length);
while (--i >= 0){
if ((this._bits[i] & set._bits[i]) != 0)
return true;
}
public nextSetBit(from: number){ return false;
let offset = from >> 6;
let mask = 1 << from;
while (offset < this._bits.length){
let h = this._bits[offset];
do {
if ((h & mask) != 0)
return from;
mask <<= 1;
from ++;
} while (mask != 0);
mask = 1;
offset ++;
} }
return -1; public isEmpty(): boolean{
} for (let i = this._bits.length - 1; i >= 0; i --){
if (this._bits[i])
return false;
}
public set(pos: number, value: boolean = true){ return true;
if (value){ }
let offset = pos >> 6;
this.ensure(offset); public nextSetBit(from: number){
this._bits[offset] |= 1 << pos; let offset = from >> 6;
}else{ let mask = 1 << from;
this.clear(pos); while (offset < this._bits.length){
let h = this._bits[offset];
do {
if ((h & mask) != 0)
return from;
mask <<= 1;
from ++;
} while (mask != 0);
mask = 1;
offset ++;
}
return -1;
}
public set(pos: number, value: boolean = true){
if (value){
let offset = pos >> 6;
this.ensure(offset);
this._bits[offset] |= 1 << pos;
}else{
this.clear(pos);
}
} }
} }
} }

View File

@@ -1,18 +1,20 @@
class ComponentTypeManager{ module es {
private static _componentTypesMask: Map<any, number> = new Map<any, number>(); export class ComponentTypeManager{
private static _componentTypesMask: Map<any, number> = new Map<any, number>();
public static add(type){ public static add(type){
if (!this._componentTypesMask.has(type)) if (!this._componentTypesMask.has(type))
this._componentTypesMask[type] = this._componentTypesMask.size; this._componentTypesMask[type] = this._componentTypesMask.size;
}
public static getIndexFor(type){
let v = -1;
if (!this._componentTypesMask.has(type)){
this.add(type);
v = this._componentTypesMask.get(type);
} }
return v; public static getIndexFor(type){
let v = -1;
if (!this._componentTypesMask.has(type)){
this.add(type);
v = this._componentTypesMask.get(type);
}
return v;
}
} }
} }

View File

@@ -1,69 +1,71 @@
class EntityProcessorList { module es {
private _processors: EntitySystem[] = []; export class EntityProcessorList {
private _processors: EntitySystem[] = [];
public add(processor: EntitySystem){ public add(processor: EntitySystem){
this._processors.push(processor); this._processors.push(processor);
}
public remove(processor: EntitySystem){
this._processors.remove(processor);
}
public onComponentAdded(entity: Entity){
this.notifyEntityChanged(entity);
}
public onComponentRemoved(entity: Entity){
this.notifyEntityChanged(entity);
}
public onEntityAdded(entity: Entity){
this.notifyEntityChanged(entity);
}
public onEntityRemoved(entity: Entity){
this.removeFromProcessors(entity);
}
protected notifyEntityChanged(entity: Entity){
for (let i = 0; i < this._processors.length; i ++){
this._processors[i].onChanged(entity);
}
}
protected removeFromProcessors(entity: Entity){
for (let i = 0; i < this._processors.length; i ++){
this._processors[i].remove(entity);
}
}
public begin(){
}
public update(){
for (let i = 0; i < this._processors.length; i++){
this._processors[i].update();
}
}
public lateUpdate(){
for (let i = 0; i < this._processors.length; i ++){
this._processors[i].lateUpdate();
}
}
public end(){
}
public getProcessor<T extends EntitySystem>(): T{
for (let i = 0; i < this._processors.length; i ++){
let processor = this._processors[i];
if (processor instanceof EntitySystem)
return processor as T;
} }
return null; public remove(processor: EntitySystem){
this._processors.remove(processor);
}
public onComponentAdded(entity: Entity){
this.notifyEntityChanged(entity);
}
public onComponentRemoved(entity: Entity){
this.notifyEntityChanged(entity);
}
public onEntityAdded(entity: Entity){
this.notifyEntityChanged(entity);
}
public onEntityRemoved(entity: Entity){
this.removeFromProcessors(entity);
}
protected notifyEntityChanged(entity: Entity){
for (let i = 0; i < this._processors.length; i ++){
this._processors[i].onChanged(entity);
}
}
protected removeFromProcessors(entity: Entity){
for (let i = 0; i < this._processors.length; i ++){
this._processors[i].remove(entity);
}
}
public begin(){
}
public update(){
for (let i = 0; i < this._processors.length; i++){
this._processors[i].update();
}
}
public lateUpdate(){
for (let i = 0; i < this._processors.length; i ++){
this._processors[i].lateUpdate();
}
}
public end(){
}
public getProcessor<T extends EntitySystem>(): T{
for (let i = 0; i < this._processors.length; i ++){
let processor = this._processors[i];
if (processor instanceof EntitySystem)
return processor as T;
}
return null;
}
} }
} }

View File

@@ -55,18 +55,20 @@ module es {
public overlaps(other: Shape){ public overlaps(other: Shape){
// 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测 // 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测
if (other instanceof Box) if (this.isUnrotated){
return this.bounds.intersects(other.bounds); if (other instanceof Box)
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){
return this.bounds.contains(point.x, point.y); if (this.isUnrotated)
return this.bounds.contains(point.x, point.y);
return super.containsPoint(point);
} }
} }
} }

View File

@@ -1,52 +1,69 @@
///<reference path="./Shape.ts" /> ///<reference path="./Shape.ts" />
class Circle extends Shape { module es {
public radius: number; export class Circle extends Shape {
public _originalRadius: number; public radius: number;
public center = new Vector2(); public _originalRadius: number;
public get position(){
return new Vector2(this.parent.x, this.parent.y);
}
public get bounds(){ constructor(radius: number) {
return new Rectangle().setEgretRect(this.getBounds()); super();
} this.radius = radius;
this._originalRadius = radius;
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) {
return ShapeCollisions.circleToBox(this, other);
} }
if (other instanceof Circle) { public recalculateBounds(collider: es.Collider) {
return ShapeCollisions.circleToCircle(this, other); // 如果我们没有旋转或不关心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);
} }
if (other instanceof Polygon) { public overlaps(other: Shape) {
return ShapeCollisions.circleToPolygon(this, other); 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`);
} }
throw new Error(`Collisions of Circle to ${other} are not supported`); public collidesWithShape(other: Shape): CollisionResult {
} if (other instanceof Box && (other as Box).isUnrotated) {
return ShapeCollisions.circleToBox(this, other);
}
public overlaps(other: Shape){ if (other instanceof Circle) {
if (other instanceof Box) return ShapeCollisions.circleToCircle(this, other);
return Collisions.isRectToCircle(other.bounds, this.position, this.radius); }
if (other instanceof Circle) if (other instanceof Polygon) {
return Collisions.isCircleToCircle(this.position, this.radius, other.position, (other as Circle).radius); return ShapeCollisions.circleToPolygon(this, other);
}
if (other instanceof Polygon) throw new Error(`Collisions of Circle to ${other} are not supported`);
return ShapeCollisions.circleToPolygon(this, other); }
throw new Error(`overlaps of circle to ${other} are not supported`); public pointCollidesWithShape(point: Vector2): CollisionResult {
return ShapeCollisions.pointToCircle(point, this);
}
} }
} }

View File

@@ -1,249 +1,269 @@
class SpatialHash { module es {
public gridBounds: Rectangle = new Rectangle(); export class SpatialHash {
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;
this._inverseCellSize = 1 / this._cellSize; this._inverseCellSize = 1 / this._cellSize;
this._raycastParser = new RaycastResultParser(); this._raycastParser = new RaycastResultParser();
}
/**
* 从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 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(p1.x, p1.y)) {
this.gridBounds = RectangleExt.union(this.gridBounds, p1);
} }
if (!this.gridBounds.contains(p2.x, p2.y)) { /**
this.gridBounds = RectangleExt.union(this.gridBounds, p2); * 获取单元格的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));
} }
for (let x = p1.x; x <= p2.x; x++) { /**
for (let y = p1.y; y <= p2.y; y++) { * 获取世界空间x,y值的单元格。
// 如果没有单元格,我们需要创建它 * 如果单元格为空且createCellIfEmpty为true则会创建一个新的单元格
let c = this.cellAtPosition(x, y, true); * @param x
if (c.indexOf(collider) == -1) * @param y
c.push(collider); * @param createCellIfEmpty
} */
} private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) {
} let cell: Collider[] = this._cellDict.tryGetValue(x, y);
if (!cell) {
public clear(){ if (createCellIfEmpty) {
this._cellDict.clear(); cell = [];
} this._cellDict.add(x, y, cell);
/**
* 获取位于指定圆内的所有碰撞器
* @param circleCenter
* @param radius
* @param results
* @param layerMask
*/
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 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!");
} }
return cell;
// 如果我们所有的结果数据有了则返回
if (resultCounter == results.length)
return resultCounter;
} }
return resultCounter; /**
} * 将对象添加到SpatialHash
* @param 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(p1.x, p1.y)) {
* @param bounds this.gridBounds = RectangleExt.union(this.gridBounds, p1);
* @param excludeCollider }
* @param layerMask
*/
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number) {
this._tempHashSet.length = 0;
let p1 = this.cellCoords(bounds.x, bounds.y); if (!this.gridBounds.contains(p2.x, p2.y)) {
let p2 = this.cellCoords(bounds.right, bounds.bottom); this.gridBounds = RectangleExt.union(this.gridBounds, p2);
}
for (let x = p1.x; x <= p2.x; x++) { for (let x = p1.x; x <= p2.x; x++) {
for (let y = p1.y; y <= p2.y; y++) { for (let y = p1.y; y <= p2.y; y++) {
let cell = this.cellAtPosition(x, y); // 如果没有单元格,我们需要创建它
if (!cell) let c = this.cellAtPosition(x, y, true);
continue; if (c.indexOf(collider) == -1)
c.push(collider);
}
}
}
// 当cell不为空。循环并取回所有碰撞器 /**
for (let i = 0; i < cell.length; i++) { * 从SpatialHash中删除对象
let collider = cell[i]; * @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++) {
if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer)) 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(){
this._cellDict.clear();
}
/**
* 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){
}
/**
* 返回边框与单元格相交的所有对象
* @param bounds
* @param excludeCollider
* @param layerMask
*/
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; continue;
if (bounds.intersects(collider.bounds)){ // 当cell不为空。循环并取回所有碰撞器
if (this._tempHashSet.indexOf(collider) == -1) for (let i = 0; i < cell.length; i++) {
this._tempHashSet.push(collider); 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 {tempHashSet: this._tempHashSet, bounds: bounds};
} }
return {tempHashSet: this._tempHashSet, bounds: bounds}; /**
} * 获取位于指定圆内的所有碰撞器
* @param circleCenter
* @param radius
* @param results
* @param layerMask
*/
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;
* 获取世界空间x,y值的单元格。 this._overlapTestCircle.position = circleCenter;
* 如果单元格为空且createCellIfEmpty为true则会创建一个新的单元格
* @param x let resultCounter = 0;
* @param y let aabbBroadphaseResult = this.aabbBroadphase(bounds, null, layerMask);
* @param createCellIfEmpty bounds = aabbBroadphaseResult.bounds;
*/ let potentials = aabbBroadphaseResult.tempHashSet;
private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) { for (let i = 0; i < potentials.length; i++) {
let cell: Collider[] = this._cellDict.tryGetValue(x, y); let collider = potentials[i];
if (!cell) { if (collider instanceof BoxCollider) {
if (createCellIfEmpty) { results[resultCounter] = collider;
cell = []; resultCounter++;
this._cellDict.add(x, y, cell); } 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 cell;
}
/** return resultCounter;
* 获取单元格的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){ /**
* 包装一个Unit32列表碰撞器字典
* 它的主要目的是将int、int x、y坐标散列到单个Uint32键中使用O(1)查找。
*/
export class NumberDictionary {
public _store: Map<string, Collider[]> = new Map<string, Collider[]>();
/**
* 根据x和y值计算并返回散列键
* @param x
* @param y
*/
private getKey(x: number, y: number): string {
return Long.fromNumber(x).shiftLeft(32).or(Long.fromNumber(y, false)).toString();
}
public add(x: number, y: number, list: Collider[]) {
this._store.set(this.getKey(x, y), list);
}
/**
* 使用蛮力方法从字典存储列表中移除碰撞器
* @param obj
*/
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();
}
}
export class RaycastResultParser {
} }
} }
class RaycastResultParser {
}
/**
* 包装一个Unit32列表碰撞器字典
* 它的主要目的是将int、int x、y坐标散列到单个Uint32键中使用O(1)查找。
*/
class NumberDictionary {
private _store: Map<string, Collider[]> = new Map<string, Collider[]>();
/**
* 根据x和y值计算并返回散列键
* @param x
* @param y
*/
private getKey(x: number, y: number): string {
return Long.fromNumber(x).shiftLeft(32).or(Long.fromNumber(y, false)).toString();
}
public add(x: number, y: number, list: Collider[]) {
this._store.set(this.getKey(x, y), list);
}
/**
* 使用蛮力方法从字典存储列表中移除碰撞器
* @param obj
*/
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();
}
}