移除渲染模块,提供更纯净的ecs

This commit is contained in:
YHH
2022-03-05 10:23:49 +08:00
parent f3f5d0bbd1
commit ccc603b59f
43 changed files with 131 additions and 3826 deletions

View File

@@ -83,8 +83,6 @@ module es {
public onEntityTransformChanged(comp: ComponentTransform) {
}
public debugRender(batcher: IBatcher) {}
/**
*当父实体或此组件启用时调用
*/

View File

@@ -11,11 +11,7 @@ module es {
constructor(x: number = 0, y: number = 0, width: number = 1, height: number = 1) {
super();
if (width == 1 && height == 1) {
this._colliderRequiresAutoSizing = true;
} else {
this._localOffset = new Vector2(x + width / 2, y + height / 2);
}
this._localOffset = new Vector2(x + width / 2, y + height / 2);
this.shape = new Box(width, height);
}
@@ -42,7 +38,6 @@ module es {
* @param height
*/
public setSize(width: number, height: number) {
this._colliderRequiresAutoSizing = false;
let box = this.shape as Box;
if (width != box.width || height != box.height) {
// 更新框,改变边界,如果我们需要更新物理系统中的边界
@@ -60,7 +55,6 @@ module es {
* @param width
*/
public setWidth(width: number): BoxCollider {
this._colliderRequiresAutoSizing = false;
let box = this.shape as Box;
if (width != box.width) {
// 更新框,改变边界,如果我们需要更新物理系统中的边界
@@ -78,7 +72,6 @@ module es {
* @param height
*/
public setHeight(height: number) {
this._colliderRequiresAutoSizing = false;
let box = this.shape as Box;
if (height != box.height) {
// 更新框,改变边界,如果我们需要更新物理系统中的边界
@@ -89,18 +82,6 @@ module es {
}
}
public debugRender(batcher: IBatcher) {
let poly = this.shape as Polygon;
batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new Color(76, 76, 76, 76), 2);
batcher.end();
batcher.drawPolygon(this.shape.position, poly.points, new Color(139, 0, 0, 255), true, 2);
batcher.end();
batcher.drawPixel(this.entity.position, new Color(255, 255, 0), 4);
batcher.end();
batcher.drawPixel(es.Vector2.add(this.transform.position, this.shape.center), new Color(255, 0, 0), 2);
batcher.end();
}
public toString() {
return `[BoxCollider: bounds: ${this.bounds}]`;
}

View File

@@ -11,9 +11,6 @@ module es {
super();
this.shape = new Circle(radius);
if (radius == 1) {
this._colliderRequiresAutoSizing = true;
}
}
public get radius(): number {
@@ -29,7 +26,6 @@ module es {
* @param radius
*/
public setRadius(radius: number): CircleCollider {
this._colliderRequiresAutoSizing = false;
let circle = this.shape as Circle;
if (radius != circle.radius) {
circle.radius = radius;
@@ -43,17 +39,6 @@ module es {
return this;
}
public debugRender(batcher: IBatcher) {
batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new Color(76, 76, 76, 76), 2);
batcher.end();
batcher.drawCircle(this.shape.position, this.radius, new Color(139, 0, 0), 2);
batcher.end();
batcher.drawPixel(this.entity.transform.position, new Color(255, 255, 0), 4);
batcher.end();
batcher.drawPixel(this.shape.position, new Color(255, 0, 0), 2);
batcher.end();
}
public toString() {
return `[CircleCollider: bounds: ${this.bounds}, radius: ${(this.shape as Circle).radius}]`
}

View File

@@ -29,8 +29,6 @@ module es {
*/
public registeredPhysicsBounds: Rectangle = new Rectangle();
protected _colliderRequiresAutoSizing: boolean;
public _localOffsetLength: number;
public _isPositionDirty: boolean = true;
public _isRotationDirty: boolean = true;
@@ -116,33 +114,6 @@ module es {
}
public onAddedToEntity() {
if (this._colliderRequiresAutoSizing) {
let renderable = null;
for (let i = 0; i < this.entity.components.buffer.length; i ++) {
let component = this.entity.components.buffer[i];
if (component instanceof RenderableComponent){
renderable = component;
break;
}
}
if (renderable != null) {
let renderableBounds = renderable.bounds.clone();
let width = renderableBounds.width / this.entity.transform.scale.x;
let height = renderableBounds.height / this.entity.transform.scale.y;
if (this instanceof CircleCollider) {
this.radius = Math.max(width, height) * 0.5;
this.localOffset = renderableBounds.center.sub(this.entity.transform.position);
} else if (this instanceof BoxCollider) {
this.width = width;
this.height = height;
this.localOffset = renderableBounds.center.sub(this.entity.transform.position);
}
}
}
this._isParentEntityAddedToScene = true;
this.registerColliderWithPhysicsSystem();
}

View File

@@ -1,9 +0,0 @@
module es {
export interface IRenderable {
enabled: boolean;
renderLayer: number;
isVisibleFromCamera(camera: ICamera): boolean;
render(batcher: IBatcher, camera: ICamera): void;
debugRender(batcher: IBatcher): void;
}
}

View File

@@ -1,133 +0,0 @@
module es {
export abstract class RenderableComponent extends es.Component implements IRenderable {
public getwidth() {
return this.bounds.width;
}
public getheight() {
return this.bounds.height;
}
protected _bounds: es.Rectangle = new es.Rectangle();
public getbounds(): es.Rectangle {
if (this._areBoundsDirty) {
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, new es.Vector2(this.getwidth() / 2, this.getheight() / 2),
this.entity.transform.scale, this.entity.transform.rotation, this.getwidth(), this.getheight());
this._areBoundsDirty = false;
}
return this._bounds;
}
public get bounds() {
return this.getbounds();
}
protected _areBoundsDirty: boolean = true;
public color: Color = Color.White;
public get renderLayer() {
return this._renderLayer;
}
public set renderLayer(value: number) {
this.setRenderLayer(value);
}
protected _renderLayer: number = 0;
public onEntityTransformChanged(comp: ComponentTransform) {
this._areBoundsDirty = true;
}
public get localOffset() {
return this._localOffset;
}
public set localOffset(value: es.Vector2) {
this.setLocalOffset(value);
}
public setLocalOffset(offset: es.Vector2) {
if (!this._localOffset.equals(offset)) {
this._localOffset = offset;
this._areBoundsDirty = true;
}
return this;
}
public get isVisible() {
return this._isVisible;
}
public set isVisible(value: boolean) {
if (this._isVisible != value) {
this._isVisible = value;
if (this._isVisible) {
this.onBecameVisible();
} else {
this.onBecameInvisible();
}
}
}
public debugRenderEnabled: boolean = true;
protected _isVisible: boolean = false;
protected _localOffset: es.Vector2 = new es.Vector2();
public abstract render(batcher: IBatcher, camera: ICamera): void;
protected onBecameVisible() {
}
protected onBecameInvisible() {
}
public setRenderLayer(renderLayer: number): RenderableComponent {
if (renderLayer != this._renderLayer) {
let oldRenderLayer = this._renderLayer;
this._renderLayer = renderLayer;
if (this.entity != null && this.entity.scene != null)
es.Core.scene.renderableComponents.updateRenderableRenderLayer(this, oldRenderLayer, this._renderLayer);
}
return this;
}
public isVisibleFromCamera(cam: ICamera): boolean {
this.isVisible = cam.bounds.intersects(this.bounds);
return this.isVisible;
}
public debugRender(batcher: IBatcher) {
if (!this.debugRenderEnabled)
return;
let collider = null;
for (let i = 0; i < this.entity.components.buffer.length; i++) {
let component = this.entity.components.buffer[i];
if (component instanceof Collider) {
collider = component;
break;
}
}
if (collider == null) {
batcher.drawHollowRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height, new Color(255, 255, 0));
batcher.end();
}
batcher.drawPixel(es.Vector2.add(this.entity.transform.position, this._localOffset), new Color(153, 50, 204), 4);
batcher.end();
}
public tweenColorTo(to: Color, duration: number) {
const tween = Pool.obtain(RenderableColorTween);
tween.setTarget(this);
tween.initialize(tween, to, duration);
return tween;
}
}
}

View File

@@ -376,11 +376,6 @@ module es {
this.components.update();
}
public debugRender(batcher: IBatcher) {
if (!batcher) return;
this.components.debugRender(batcher);
}
/**
* 创建组件的新实例。返回实例组件
* @param componentType

View File

@@ -2,21 +2,17 @@
module es {
/** 场景 */
export class Scene {
public camera: ICamera;
/** 这个场景中的实体列表 */
public readonly entities: EntityList;
public readonly renderableComponents: RenderableComponentList;
/** 管理所有实体处理器 */
public readonly entityProcessors: EntityProcessorList;
public readonly _sceneComponents: SceneComponent[] = [];
public _renderers: Renderer[] = [];
public readonly identifierPool: IdentifierPool;
private _didSceneBegin: boolean;
constructor() {
this.entities = new EntityList(this);
this.renderableComponents = new RenderableComponentList();
this.entityProcessors = new EntityProcessorList();
this.identifierPool = new IdentifierPool();
@@ -45,10 +41,6 @@ module es {
}
public begin() {
if (this._renderers.length == 0) {
this.addRenderer(new DefaultRenderer());
}
Physics.reset();
if (this.entityProcessors != null)
@@ -62,9 +54,6 @@ module es {
public end() {
this._didSceneBegin = false;
for (let i = 0; i < this._renderers.length; i ++)
this._renderers[i].unload();
this.entities.removeAllEntities();
for (let i = 0; i < this._sceneComponents.length; i++) {
@@ -72,7 +61,6 @@ module es {
}
this._sceneComponents.length = 0;
this.camera = null;
Physics.clear();
if (this.entityProcessors)
@@ -99,38 +87,6 @@ module es {
if (this.entityProcessors != null)
this.entityProcessors.lateUpdate();
this.renderableComponents.updateLists();
this.render();
}
public render() {
for (let i = 0; i < this._renderers.length; i ++) {
this._renderers[i].render(this);
}
}
public addRenderer<T extends Renderer>(renderer: T): T {
this._renderers.push(renderer);
this._renderers.sort((self, other) => self.renderOrder - other.renderOrder);
renderer.onAddedToScene(this);
return renderer;
}
public getRenderer<T extends Renderer>(type: new (...args: any[]) => T): T {
for (let i = 0; i < this._renderers.length; i ++) {
if (this._renderers[i] instanceof type)
return this._renderers[i] as T;
}
return null;
}
public removeRenderer(renderer: Renderer) {
new List(this._renderers).remove(renderer);
renderer.unload();
}
/**

View File

@@ -94,9 +94,6 @@ module es {
for (let i = 0, s = this._components.length; i < s; ++ i) {
let component = this._components[i];
if (!component) continue;
if (component instanceof RenderableComponent)
this._entity.scene.renderableComponents.remove(component);
// 处理IUpdatable
if (isIUpdatable(component))
@@ -112,9 +109,6 @@ module es {
if (this._components.length > 0) {
for (let i = 0, s = this._components.length; i < s; ++ i) {
let component = this._components[i];
if (component instanceof RenderableComponent)
this._entity.scene.renderableComponents.add(component);
if (isIUpdatable(component))
this._updatableComponents.push(component);
@@ -157,8 +151,6 @@ module es {
if (this._componentsToAddList.length > 0) {
for (let i = 0, l = this._componentsToAddList.length; i < l; ++ i) {
let component = this._componentsToAddList[i];
if (component instanceof RenderableComponent)
this._entity.scene.renderableComponents.add(component);
if (isIUpdatable(component))
this._updatableComponents.push(component);
@@ -195,9 +187,6 @@ module es {
}
public handleRemove(component: Component) {
if (component instanceof RenderableComponent)
this._entity.scene.renderableComponents.remove(component);
if (isIUpdatable(component) && this._updatableComponents.length > 0) {
let index = this._updatableComponents.findIndex((c) => (<any>c as Component).id == component.id);
if (index != -1)
@@ -325,14 +314,5 @@ module es {
this._components[i].onDisabled();
}
}
public debugRender(batcher: IBatcher) {
if (!batcher) return;
for (let i = 0; i < this._components.length; i ++) {
if (this._components[i].enabled) {
this._components[i].debugRender(batcher);
}
}
}
}
}

View File

@@ -1,81 +0,0 @@
module es {
export class RenderableComponentList {
private _components: IRenderable[] = [];
private _componentsByRenderLayer: Map<number, IRenderable[]> = new Map();
private _unsortedRenderLayers: number[] = [];
private _componentsNeedSort = true;
public get count() {
return this._components.length;
}
public get(index: number) {
return this._components[index];
}
public add(component: IRenderable) {
this._components.push(component);
this.addToRenderLayerList(component, component.renderLayer);
}
public remove(component: IRenderable) {
new List(this._components).remove(component);
new List(this._componentsByRenderLayer.get(component.renderLayer)).remove(component);
}
public updateRenderableRenderLayer(component: IRenderable, oldRenderLayer: number, newRenderLayer: number) {
if (this._componentsByRenderLayer.has(oldRenderLayer) && new List(this._componentsByRenderLayer.get(oldRenderLayer)).contains(component)) {
new List(this._componentsByRenderLayer.get(oldRenderLayer)).remove(component);
this.addToRenderLayerList(component, newRenderLayer);
}
}
public setRenderLayerNeedsComponentSort(renderLayer: number) {
const unsortedRenderLayersList = new List(this._unsortedRenderLayers);
if (!unsortedRenderLayersList.contains(renderLayer))
unsortedRenderLayersList.add(renderLayer);
this._componentsNeedSort = true;
}
public setNeedsComponentSort() {
this._componentsNeedSort = true;
}
private addToRenderLayerList(component: IRenderable, renderLayer: number) {
let list = this.componentsWithRenderLayer(renderLayer);
es.Insist.isFalse(!!list.find(c => c == component), "组件renderLayer列表已包含此组件");
list.push(component);
const unsortedRenderLayersList = new List(this._unsortedRenderLayers);
if (!unsortedRenderLayersList.contains(renderLayer))
unsortedRenderLayersList.add(renderLayer);
this._componentsNeedSort = true;
}
public componentsWithRenderLayer(renderLayer: number) {
if (!this._componentsByRenderLayer.get(renderLayer)) {
this._componentsByRenderLayer.set(renderLayer, []);
}
return this._componentsByRenderLayer.get(renderLayer);
}
public updateLists() {
if (this._componentsNeedSort) {
this._components.sort((self, other) => other.renderLayer - self.renderLayer);
this._componentsNeedSort = false;
}
if (this._unsortedRenderLayers.length > 0) {
for (let i = 0, count = this._unsortedRenderLayers.length; i < count; i ++) {
const renderLayerComponents = this._componentsByRenderLayer.get(this._unsortedRenderLayers[i]);
if (renderLayerComponents) {
renderLayerComponents.sort((self, other) => other.renderLayer - self.renderLayer);
}
this._unsortedRenderLayers.length = 0;
}
}
}
}
}

View File

@@ -1,475 +0,0 @@
module es {
export class Color {
/**
* 红色通道
*/
public r: number;
/**
* 绿色通道
*/
public g: number;
/**
* 蓝色通道
*/
public b: number;
/**
* 透明度通道 (仅0-1之间)
*/
public a: number;
/**
* 色调
*/
public h: number;
/**
* 饱和
*/
public s: number;
/**
* 亮度
*/
public l: number;
/**
* 从 r, g, b, a 创建一个新的 Color 实例
*
* @param r 颜色的红色分量 (0-255)
* @param g 颜色的绿色成分 (0-255)
* @param b 颜色的蓝色分量 (0-255)
* @param a 颜色的 alpha 分量 (0-1.0)
*/
constructor(r: number, g: number, b: number, a?: number) {
this.r = r;
this.g = g;
this.b = b;
this.a = a != null ? a : 1;
}
/**
* 从 r, g, b, a 创建一个新的 Color 实例
*
* @param r 颜色的红色分量 (0-255)
* @param g 颜色的绿色成分 (0-255)
* @param b 颜色的蓝色分量 (0-255)
* @param a 颜色的 alpha 分量 (0-1.0)
*/
public static fromRGB(r: number, g: number, b: number, a?: number): Color {
return new Color(r, g, b, a);
}
/**
* 从十六进制字符串创建一个新的 Color 实例
*
* @param hex #ffffff 形式的 CSS 颜色字符串alpha 组件是可选的
*/
public static createFromHex(hex: string): Color {
const color = new Color(1, 1, 1);
color.fromHex(hex);
return color;
}
/**
* 从 hsl 值创建一个新的 Color 实例
*
* @param h 色调表示 [0-1]
* @param s 饱和度表示为 [0-1]
* @param l 亮度表示 [0-1]
* @param a 透明度表示 [0-1]
*/
public static fromHSL(
h: number,
s: number,
l: number,
a: number = 1.0
): Color {
const temp = new HSLColor(h, s, l, a);
return temp.toRGBA();
}
/**
* 将当前颜色调亮指定的量
*
* @param factor
*/
public lighten(factor: number = 0.1): Color {
const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a);
temp.l += temp.l * factor;
return temp.toRGBA();
}
/**
* 将当前颜色变暗指定的量
*
* @param factor
*/
public darken(factor: number = 0.1): Color {
const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a);
temp.l -= temp.l * factor;
return temp.toRGBA();
}
/**
* 使当前颜色饱和指定的量
*
* @param factor
*/
public saturate(factor: number = 0.1): Color {
const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a);
temp.s += temp.s * factor;
return temp.toRGBA();
}
/**
* 按指定量降低当前颜色的饱和度
*
* @param factor
*/
public desaturate(factor: number = 0.1): Color {
const temp = HSLColor.fromRGBA(this.r, this.g, this.b, this.a);
temp.s -= temp.s * factor;
return temp.toRGBA();
}
/**
* 将一种颜色乘以另一种颜色,得到更深的颜色
*
* @param color
*/
public mulitiply(color: Color): Color {
const newR = (((color.r / 255) * this.r) / 255) * 255;
const newG = (((color.g / 255) * this.g) / 255) * 255;
const newB = (((color.b / 255) * this.b) / 255) * 255;
const newA = color.a * this.a;
return new Color(newR, newG, newB, newA);
}
/**
* 筛选另一种颜色,导致颜色较浅
*
* @param color
*/
public screen(color: Color): Color {
const color1 = color.invert();
const color2 = color.invert();
return color1.mulitiply(color2).invert();
}
/**
* 反转当前颜色
*/
public invert(): Color {
return new Color(255 - this.r, 255 - this.g, 255 - this.b, 1.0 - this.a);
}
/**
* 将当前颜色与另一个颜色平均
*
* @param color
*/
public average(color: Color): Color {
const newR = (color.r + this.r) / 2;
const newG = (color.g + this.g) / 2;
const newB = (color.b + this.b) / 2;
const newA = (color.a + this.a) / 2;
return new Color(newR, newG, newB, newA);
}
/**
* 返回颜色的 CSS 字符串表示形式。
*
* @param format
*/
public toString(format: 'rgb' | 'hsl' | 'hex' = 'rgb') {
switch (format) {
case 'rgb':
return this.toRGBA();
case 'hsl':
return this.toHSLA();
case 'hex':
return this.toHex();
default:
throw new Error('Invalid Color format');
}
}
/**
* 返回颜色分量的十六进制值
* @param c
* @see https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
*/
private _componentToHex(c: number) {
const hex = c.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}
/**
*返回颜色的十六进制表示
*/
public toHex(): string {
return (
'#' +
this._componentToHex(this.r) +
this._componentToHex(this.g) +
this._componentToHex(this.b) +
this._componentToHex(this.a)
);
}
/**
* 从十六进制字符串设置颜色
*
* @param hex #ffffff 形式的 CSS 颜色字符串alpha 组件是可选的
*/
public fromHex(hex: string) {
const hexRegEx: RegExp = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i;
const match = hex.match(hexRegEx);
if (match) {
const r = parseInt(match[1], 16);
const g = parseInt(match[2], 16);
const b = parseInt(match[3], 16);
let a = 1;
if (match[4]) {
a = parseInt(match[4], 16) / 255;
}
this.r = r;
this.g = g;
this.b = b;
this.a = a;
} else {
throw new Error('Invalid hex string: ' + hex);
}
}
/**
* 返回颜色的 RGBA 表示
*/
public toRGBA() {
const result =
String(this.r.toFixed(0)) +
', ' +
String(this.g.toFixed(0)) +
', ' +
String(this.b.toFixed(0));
if (this.a !== undefined || this.a != null) {
return 'rgba(' + result + ', ' + String(this.a) + ')';
}
return 'rgb(' + result + ')';
}
/**
* 返回颜色的 HSLA 表示
*/
public toHSLA() {
return HSLColor.fromRGBA(this.r, this.g, this.b, this.a).toString();
}
/**
* 返回颜色的 CSS 字符串表示形式
*/
public fillStyle() {
return this.toString();
}
/**
* 返回当前颜色的克隆
*/
public clone(): Color {
return new Color(this.r, this.g, this.b, this.a);
}
/**
* Black (#000000)
*/
public static Black: Color = Color.createFromHex('#000000');
/**
* White (#FFFFFF)
*/
public static White: Color = Color.createFromHex('#FFFFFF');
/**
* Gray (#808080)
*/
public static Gray: Color = Color.createFromHex('#808080');
/**
* Light gray (#D3D3D3)
*/
public static LightGray: Color = Color.createFromHex('#D3D3D3');
/**
* Dark gray (#A9A9A9)
*/
public static DarkGray: Color = Color.createFromHex('#A9A9A9');
/**
* Yellow (#FFFF00)
*/
public static Yellow: Color = Color.createFromHex('#FFFF00');
/**
* Orange (#FFA500)
*/
public static Orange: Color = Color.createFromHex('#FFA500');
/**
* Red (#FF0000)
*/
public static Red: Color = Color.createFromHex('#FF0000');
/**
* Vermillion (#FF5B31)
*/
public static Vermillion: Color = Color.createFromHex('#FF5B31');
/**
* Rose (#FF007F)
*/
public static Rose: Color = Color.createFromHex('#FF007F');
/**
* Magenta (#FF00FF)
*/
public static Magenta: Color = Color.createFromHex('#FF00FF');
/**
* Violet (#7F00FF)
*/
public static Violet: Color = Color.createFromHex('#7F00FF');
/**
* Blue (#0000FF)
*/
public static Blue: Color = Color.createFromHex('#0000FF');
/**
* Azure (#007FFF)
*/
public static Azure: Color = Color.createFromHex('#007FFF');
/**
* Cyan (#00FFFF)
*/
public static Cyan: Color = Color.createFromHex('#00FFFF');
/**
* Viridian (#59978F)
*/
public static Viridian: Color = Color.createFromHex('#59978F');
/**
* Green (#00FF00)
*/
public static Green: Color = Color.createFromHex('#00FF00');
/**
* Chartreuse (#7FFF00)
*/
public static Chartreuse: Color = Color.createFromHex('#7FFF00');
/**
* Transparent (#FFFFFF00)
*/
public static Transparent: Color = Color.createFromHex('#FFFFFF00');
}
/**
* 内部 HSL 颜色表示
*
* http://en.wikipedia.org/wiki/HSL_and_HSV
* http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
*/
class HSLColor {
constructor(
public h: number,
public s: number,
public l: number,
public a: number
) { }
public static hue2rgb(p: number, q: number, t: number): number {
if (t < 0) {
t += 1;
}
if (t > 1) {
t -= 1;
}
if (t < 1 / 6) {
return p + (q - p) * 6 * t;
}
if (t < 1 / 2) {
return q;
}
if (t < 2 / 3) {
return p + (q - p) * (2 / 3 - t) * 6;
}
return p;
}
public static fromRGBA(
r: number,
g: number,
b: number,
a: number
): HSLColor {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h = (max + min) / 2;
let s = h;
const l = h;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return new HSLColor(h, s, l, a);
}
public toRGBA(): Color {
let r: number;
let g: number;
let b: number;
if (this.s === 0) {
r = g = b = this.l; // achromatic
} else {
const q =
this.l < 0.5
? this.l * (1 + this.s)
: this.l + this.s - this.l * this.s;
const p = 2 * this.l - q;
r = HSLColor.hue2rgb(p, q, this.h + 1 / 3);
g = HSLColor.hue2rgb(p, q, this.h);
b = HSLColor.hue2rgb(p, q, this.h - 1 / 3);
}
return new Color(r * 255, g * 255, b * 255, this.a);
}
public toString(): string {
const h = this.h.toFixed(0);
const s = this.s.toFixed(0);
const l = this.l.toFixed(0);
const a = this.a.toFixed(0);
return `hsla(${h}, ${s}, ${l}, ${a})`;
}
}
}

View File

@@ -1,15 +0,0 @@
module es {
export interface IBatcher {
begin(cam: ICamera);
end();
drawPoints(points: Vector2[], color: Color, thickness?: number);
drawPolygon(poisition: Vector2, points: Vector2[], color: Color, closePoly: boolean, thickness?: number);
drawHollowRect(x: number, y: number, width: number, height: number, color: Color, thickness?: number);
drawCircle(position: Vector2, raidus: number, color: Color, thickness?: number);
drawCircleLow(position: es.Vector2, radius: number, color: Color, thickness?: number, resolution?: number);
drawRect(x: number, y: number, width: number, height: number, color: Color);
drawLine(start: Vector2, end: Vector2, color: Color, thickness: number);
drawPixel(position: Vector2, color: Color, size?: number);
}
}

View File

@@ -1,5 +0,0 @@
module es {
export interface ICamera extends Component {
bounds: Rectangle;
}
}

View File

@@ -1,10 +0,0 @@
module es {
export class Graphics {
public static instance: Graphics;
public batcher: IBatcher;
constructor(batcher: IBatcher) {
this.batcher = batcher;
}
}
}

View File

@@ -1,29 +0,0 @@
///<reference path="Renderer.ts" />
module es {
export class DefaultRenderer extends Renderer {
constructor(renderOrder: number = 0, camera: ICamera = null) {
super(renderOrder, camera);
}
public render(scene: Scene): void {
if (!this.renderDirty)
return;
this.renderDirty = false;
let cam = this.camera ? this.camera : scene.camera;
this.beginRender(cam);
for (let i = 0; i < scene.renderableComponents.count; i ++) {
let renderable = scene.renderableComponents.get(i);
if (renderable.enabled && renderable.isVisibleFromCamera(scene.camera))
this.renderAfterStateCheck(renderable, cam);
}
if (this.shouldDebugRender && es.Core.debugRenderEndabled) {
this.debugRender(scene);
}
this.endRender();
}
}
}

View File

@@ -1,60 +0,0 @@
module es {
export abstract class Renderer {
public camera: ICamera;
public readonly renderOrder: number = 0;
public shouldDebugRender: boolean = true;
protected renderDirty: boolean = true;
constructor(renderOrder: number, camera: ICamera) {
this.renderOrder = renderOrder;
this.camera = camera;
Core.emitter.addObserver(CoreEvents.renderChanged, this.onRenderChanged, this);
}
public onAddedToScene(scene: es.Scene) { }
public unload() { }
protected beginRender(cam: ICamera) {
if (!Graphics.instance)
return;
Graphics.instance.batcher.begin(cam);
}
protected endRender() {
if (!Graphics.instance)
return;
Graphics.instance.batcher.end();
}
protected onRenderChanged() {
this.renderDirty = true;
}
public abstract render(scene: Scene): void;
protected renderAfterStateCheck(renderable: IRenderable, cam: ICamera) {
if (!Graphics.instance)
return;
renderable.render(Graphics.instance.batcher, cam);
}
protected debugRender(scene: Scene) {
if (!Graphics.instance)
return;
es.Physics.debugDraw(2);
for (let i = 0; i < scene.entities.count; i ++) {
let entity = scene.entities.buffer[i];
if (entity.enabled) {
entity.debugRender(Graphics.instance.batcher);
}
}
}
}
}

View File

@@ -43,11 +43,6 @@ module es {
this._spatialHash.clear();
}
public static debugDraw(secondsToDisplay) {
if (this.debugRender)
this._spatialHash.debugDraw(secondsToDisplay);
}
/**
* 检查是否有对撞机落在一个圆形区域内。返回遇到的第一个对撞机
* @param center

View File

@@ -94,21 +94,6 @@ module es {
this._cellDict.clear();
}
public debugDraw(secondsToDisplay: number) {
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 != null && cell.length > 0)
this.debugDrawCellDetails(x, y, secondsToDisplay);
}
}
}
private debugDrawCellDetails(x: number, y: number, secondsToDisplay: number = 0.5) {
Graphics.instance.batcher.drawHollowRect(x * this._cellSize, y * this._cellSize, this._cellSize, this._cellSize, new Color(255, 0, 0), secondsToDisplay);
Graphics.instance.batcher.end();
}
/**
* 返回边框与单元格相交的所有对象
* @param bounds

View File

@@ -1,9 +0,0 @@
///<reference path="./Composite.ts" />
module es {
export class Ball extends Composite {
constructor(position: Vector2, radius: number = 10) {
super();
this.addParticle(new Particle(position)).radius = radius;
}
}
}

View File

@@ -1,23 +0,0 @@
///<reference path="./Composite.ts" />
module es {
export class VerletBox extends es.Composite {
constructor(center: es.Vector2, width: number, height: number, borderStiffness: number = 0.2, diagonalStiffness: number = 0.5) {
super();
const tl = this.addParticle(new Particle(center.add(new Vector2(-width / 2, -height / 2))));
const tr = this.addParticle(new Particle(center.add(new Vector2(width / 2, -height / 2))));
const br = this.addParticle(new Particle(center.add(new Vector2(width / 2, height / 2))));
const bl = this.addParticle(new Particle(center.add(new Vector2(-width / 2, height / 2))));
this.addConstraint(new DistanceConstraint(tl, tr, borderStiffness));
this.addConstraint(new DistanceConstraint(tr, br, borderStiffness));
this.addConstraint(new DistanceConstraint(br, bl, borderStiffness));
this.addConstraint(new DistanceConstraint(bl, tl, borderStiffness));
this.addConstraint(new DistanceConstraint(tl, br, diagonalStiffness))
.setCollidesWithColliders(false);
this.addConstraint(new DistanceConstraint(bl, tr, diagonalStiffness))
.setCollidesWithColliders(false);
}
}
}

View File

@@ -1,33 +0,0 @@
module es {
export class Cloth extends Composite {
constructor(topLeftPosition: Vector2, width: number, height: number, segments: number = 20, stiffness: number = 0.25,
tearSensitivity: number = 5, connectHorizontalParticles: boolean = true) {
super();
const xStride = width / segments;
const yStride = height / segments;
for (let y = 0; y < segments; y++) {
for (let x = 0; x < segments; x++) {
const px = topLeftPosition.x + x * xStride;
const py = topLeftPosition.y + y + yStride;
const particle = this.addParticle(new Particle(new Vector2(px, py)));
if (connectHorizontalParticles && x > 0)
this.addConstraint(new DistanceConstraint(this.particles[y * segments + x],
this.particles[y * segments + x - 1], stiffness))
.setTearSensitivity(tearSensitivity)
.setCollidesWithColliders(false);
if (y > 0)
this.addConstraint(new DistanceConstraint(this.particles[y * segments + x],
this.particles[(y - 1) * segments + x], stiffness))
.setTearSensitivity(tearSensitivity)
.setCollidesWithColliders(false);
if (y == 0)
particle.pin();
}
}
}
}
}

View File

@@ -1,88 +0,0 @@
module es {
export class Composite {
public friction: Vector2 = new Vector2(0.98, 1);
public drawParticles: boolean = true;
public drawConstraints: boolean = true;
public collidesWithLayers: number = Physics.allLayers;
public particles: Particle[] = [];
_constraints: Constraint[] = [];
public addParticle(particle: Particle): Particle {
this.particles.push(particle);
return particle;
}
public removeParticle(particle: Particle) {
const index = this.particles.indexOf(particle);
this.particles.splice(index, 1);
}
public removeAll() {
this.particles.length = 0;
this._constraints.length = 0;
}
public addConstraint<T extends Constraint>(constraint: T): T {
this._constraints.push(constraint);
constraint.composite = this;
return constraint;
}
public removeConstraint(constraint: Constraint) {
const index = this._constraints.indexOf(constraint);
this._constraints.splice(index, 1);
}
public applyForce(force: Vector2) {
for (let j = 0; j < this.particles.length; j ++)
this.particles[j].applyForce(force);
}
public solveConstraints() {
for (let i = this._constraints.length - 1; i >= 0; i --)
this._constraints[i].solve();
}
public updateParticles(deltaTimeSquared: number, gravity: Vector2) {
for (let j = 0; j < this.particles.length; j ++) {
const p = this.particles[j];
if (p.isPinned) {
p.position = p.pinnedPosition;
continue;
}
p.applyForce(gravity.scale(p.mass));
const vel = p.position.sub(p.lastPosition).multiply(this.friction);
const nextPos = p.position.add(vel).add(p.acceleration.scale(0.5 * deltaTimeSquared));
p.lastPosition = p.position;
p.position = nextPos;
p.acceleration.x = p.acceleration.y = 0;
}
}
public handleConstraintCollisions() {
for (let i = this._constraints.length - 1; i >= 0; i --) {
if (this._constraints[i].collidesWithColliders)
this._constraints[i].handleCollisions(this.collidesWithLayers);
}
}
public debugRender(batcher: IBatcher) {
if (this.drawConstraints) {
for (let i = 0; i < this._constraints.length; i ++)
this._constraints[i].debugRender(batcher);
}
if (this.drawParticles) {
for (let i = 0; i < this.particles.length; i ++) {
if (this.particles[i].radius == 0)
batcher.drawPixel(this.particles[i].position, new Color(220, 52, 94), 4);
else
batcher.drawCircleLow(this.particles[i].position, this.particles[i].radius, new Color(220, 52, 94), 1, 4);
}
}
}
}
}

View File

@@ -1,20 +0,0 @@
module es {
export class LineSegments extends Composite {
constructor(vertices: Vector2[], stiffness: number) {
super();
for (let i = 0; i < vertices.length; i ++) {
const p = new Particle(vertices[i]);
this.addParticle(p);
if (i > 0)
this.addConstraint(new DistanceConstraint(this.particles[i], this.particles[i - 1], stiffness));
}
}
public pinParticleAtIndex(index: number): LineSegments {
this.particles[index].pin();
return this;
}
}
}

View File

@@ -1,55 +0,0 @@
module es {
export class Ragdoll extends Composite {
constructor(x: number, y: number, bodyHeight: number) {
super();
const headLength = bodyHeight / 7.5;
const head = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
head.radius = headLength * 0.75;
head.mass = 4;
const shoulder = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
shoulder.mass = 26;
this.addConstraint(new DistanceConstraint(head, shoulder, 1, 5 / 4 * headLength));
const elbowLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
const elbowRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
elbowLeft.mass = 2;
elbowRight.mass = 2;
this.addConstraint(new DistanceConstraint(elbowLeft, shoulder, 1, headLength * 3 / 2));
this.addConstraint(new DistanceConstraint(elbowRight, shoulder, 1, headLength * 3 / 2));
const handLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
const handRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
handLeft.mass = 2;
handRight.mass = 2;
this.addConstraint(new DistanceConstraint(handLeft, elbowLeft, 1, headLength * 2));
this.addConstraint(new DistanceConstraint(handRight, elbowRight, 1, headLength * 2));
const pelvis = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
pelvis.mass = 15;
this.addConstraint(new DistanceConstraint(pelvis, shoulder, 0.8, headLength * 3.5));
this.addConstraint(new DistanceConstraint(pelvis, head, 0.02, bodyHeight * 2))
.setCollidesWithColliders(false);
const kneeLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
const kneeRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
kneeLeft.mass = 10;
kneeRight.mass = 10;
this.addConstraint(new DistanceConstraint(kneeLeft, pelvis, 1, headLength * 2));
this.addConstraint(new DistanceConstraint(kneeRight, pelvis, 1, headLength * 2));
const footLeft = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
const footRight = this.addParticle(new Particle({ x: x + RandomUtils.randint(-5, 5), y: y + RandomUtils.randint(-5, 5) }));
footLeft.mass = 5;
footRight.mass = 5;
this.addConstraint(new DistanceConstraint(footLeft, kneeLeft, 1, headLength * 2));
this.addConstraint(new DistanceConstraint(footRight, kneeRight, 1, headLength * 2));
this.addConstraint(new DistanceConstraint(footLeft, shoulder, 0.001, bodyHeight * 2))
.setCollidesWithColliders(false);
this.addConstraint(new DistanceConstraint(footLeft, shoulder, 0.001, bodyHeight * 2))
.setCollidesWithColliders(false);
}
}
}

View File

@@ -1,23 +0,0 @@
module es {
export class Tire extends Composite {
constructor(origin: Vector2, radius: number, segments: number, spokeStiffness: number = 1, treadStiffness: number = 1) {
super();
const stride = 2 * Math.PI / segments;
for (let i = 0; i < segments; i ++) {
const theta = i * stride;
this.addParticle(new Particle(new Vector2(origin.x + Math.cos(theta) * radius,
origin.y + Math.sin(theta) * radius)));
}
const centerParticle = this.addParticle(new Particle(origin));
for (let i = 0; i < segments; i ++) {
this.addConstraint(new DistanceConstraint(this.particles[i], this.particles[(i + 1) % segments], treadStiffness));
this.addConstraint(new DistanceConstraint(this.particles[i], centerParticle, spokeStiffness))
.setCollidesWithColliders(false);
this.addConstraint(new DistanceConstraint(this.particles[i], this.particles[(i + 5) % segments], treadStiffness));
}
}
}
}

View File

@@ -1,47 +0,0 @@
///<reference path="./Constraint.ts" />
module es {
export class AngleConstraint extends Constraint {
public stiffness: number = 0;
public angleInRadius: number = 0;
_particleA: Particle;
_centerParticle: Particle;
_particleC: Particle;
constructor(a: Particle, center: Particle, c: Particle, stiffness: number) {
super();
this._particleA = a;
this._centerParticle = center;
this._particleC = c;
this.stiffness = stiffness;
this.collidesWithColliders = false;
this.angleInRadius = this.angleBetweenParticles();
}
angleBetweenParticles(): number {
const first = this._particleA.position.sub(this._centerParticle.position);
const second = this._particleC.position.sub(this._centerParticle.position);
return Math.atan2(first.x * second.y - first.y * second.x, first.x * second.x + first.y * second.y);
}
public solve() {
const angleBetween = this.angleBetweenParticles();
let diff = angleBetween - this.angleInRadius;
if (diff <= -Math.PI)
diff += 2 * Math.PI;
else if(diff >= Math.PI)
diff -= 2 * Math.PI;
diff *= this.stiffness;
this._particleA.position = MathHelper.rotateAround2(this._particleA.position, this._centerParticle.position, diff);
this._particleC.position = MathHelper.rotateAround2(this._particleC.position, this._centerParticle.position, -diff);
this._centerParticle.position = MathHelper.rotateAround2(this._centerParticle.position, this._particleA.position, diff);
this._centerParticle.position = MathHelper.rotateAround2(this._centerParticle.position, this._particleC.position, -diff);
}
}
}

View File

@@ -1,16 +0,0 @@
module es {
export abstract class Constraint {
public composite: Composite;
public collidesWithColliders: boolean = true;
public abstract solve(): void;
public handleCollisions(collidesWithLayers: number) {
}
public debugRender(batcher: IBatcher) {
}
}
}

View File

@@ -1,123 +0,0 @@
module es {
export class DistanceConstraint extends Constraint {
public stiffness: number = 0;
public restingDistance: number = 0;
public tearSensitivity = Number.POSITIVE_INFINITY;
public shouldApproximateCollisionsWithPoints: boolean = false;
public totalPointsToApproximateCollisionsWith = 5;
_particleOne: Particle;
_particleTwo: Particle;
static _polygon: Polygon = new Polygon([]);
constructor(first: Particle, second: Particle, stiffness: number, distance: number = -1) {
super();
DistanceConstraint._polygon.create(2, 1);
this._particleOne = first;
this._particleTwo = second;
this.stiffness = stiffness;
if (distance > -1)
this.restingDistance = distance;
else
this.restingDistance = first.position.distance(second.position);
}
public static create(a: Particle, center: Particle, c: Particle, stiffness: number, angleInDegrees: number) {
const aToCenter = a.position.distance(center.position);
const cToCenter = c.position.distance(center.position);
const distance = Math.sqrt(aToCenter * aToCenter + cToCenter * cToCenter - (2 * aToCenter * cToCenter * Math.cos(angleInDegrees * MathHelper.Deg2Rad)));
return new DistanceConstraint(a, c, stiffness, distance);
}
public setTearSensitivity(tearSensitivity: number) {
this.tearSensitivity = tearSensitivity;
return this;
}
public setCollidesWithColliders(collidesWithColliders: boolean) {
this.collidesWithColliders = collidesWithColliders;
return this;
}
public setShouldApproximateCollisionsWithPoints(shouldApproximateCollisionsWithPoints: boolean) {
this.shouldApproximateCollisionsWithPoints = shouldApproximateCollisionsWithPoints;
return this;
}
public solve(): void {
const diff = this._particleOne.position.sub(this._particleTwo.position);
const d = diff.distance();
const difference = (this.restingDistance - d) / d;
if (d / this.restingDistance > this.tearSensitivity) {
this.composite.removeConstraint(this);
return;
}
const im1 = 1 / this._particleOne.mass;
const im2 = 1 / this._particleTwo.mass;
const scalarP1 = (im1 / (im1 + im2)) * this.stiffness;
const scalarP2 = this.stiffness - scalarP1;
this._particleOne.position = this._particleOne.position.add(diff.scale(scalarP1 * difference));
this._particleTwo.position = this._particleTwo.position.sub(diff.scale(scalarP2 * difference));
}
public handleCollisions(collidesWithLayers: number) {
if (this.shouldApproximateCollisionsWithPoints) {
this.approximateCollisionsWithPoints(collidesWithLayers);
return;
}
const minX = Math.min(this._particleOne.position.x, this._particleTwo.position.x);
const maxX = Math.max(this._particleOne.position.x, this._particleTwo.position.x);
const minY = Math.min(this._particleOne.position.y, this._particleTwo.position.y);
const maxY = Math.max(this._particleOne.position.y, this._particleTwo.position.y);
DistanceConstraint._polygon.bounds = Rectangle.fromMinMax(minX, minY, maxX, maxY);
let midPoint: Vector2 = Vector2.zero;
this.preparePolygonForCollisionChecks(midPoint);
const colliders = Physics.boxcastBroadphase(DistanceConstraint._polygon.bounds, collidesWithLayers);
for (let i = 0; i < colliders.length; i ++) {
const collider = colliders[i];
const result = new CollisionResult();
if (DistanceConstraint._polygon.collidesWithShape(collider.shape, result)) {
this._particleOne.position = this._particleOne.position.sub(result.minimumTranslationVector);
this._particleTwo.position = this._particleTwo.position.sub(result.minimumTranslationVector);
}
}
}
approximateCollisionsWithPoints(collidesWithLayers: number) {
let pt: Vector2;
for (let j = 0; j < this.totalPointsToApproximateCollisionsWith - 1; j ++) {
pt = Vector2.lerp(this._particleOne.position, this._particleTwo.position, (j + 1) / this.totalPointsToApproximateCollisionsWith);
const collidedCount = Physics.overlapCircleAll(pt, 3, VerletWorld._colliders, collidesWithLayers);
for (let i = 0; i < collidedCount; i ++) {
const collider = VerletWorld._colliders[i];
const collisionResult = new CollisionResult();
if (collider.shape.pointCollidesWithShape(pt, collisionResult)) {
this._particleOne.position = this._particleOne.position.sub(collisionResult.minimumTranslationVector);
this._particleTwo.position = this._particleTwo.position.sub(collisionResult.minimumTranslationVector);
}
}
}
}
preparePolygonForCollisionChecks(midPoint: Vector2) {
const tempMidPoint = Vector2.lerp(this._particleOne.position, this._particleTwo.position, 0.5);
midPoint.setTo(tempMidPoint.x, tempMidPoint.y);
DistanceConstraint._polygon.position = midPoint;
DistanceConstraint._polygon.points[0] = this._particleOne.position.sub(DistanceConstraint._polygon.position);
DistanceConstraint._polygon.points[1] = this._particleTwo.position.sub(DistanceConstraint._polygon.position);
DistanceConstraint._polygon.recalculateCenterAndEdgeNormals();
}
public debugRender(batcher: IBatcher) {
batcher.drawLine(this._particleOne.position, this._particleTwo.position, new Color(67, 62, 54), 1);
}
}
}

View File

@@ -1,39 +0,0 @@
module es {
export class Particle {
public position: Vector2 = Vector2.zero;
public lastPosition: Vector2 = Vector2.zero;
public mass = 1;
public radius: number = 0;
public collidesWithColliders: boolean = true;
public isPinned: boolean = false;
public acceleration: Vector2 = Vector2.zero;
public pinnedPosition: Vector2 = Vector2.zero;
constructor(position: {x: number, y: number}) {
this.position = new Vector2(position.x, position.y);
this.lastPosition = new Vector2(position.x, position.y);
}
public applyForce(force: Vector2) {
this.acceleration = this.acceleration.add(force.divideScaler(this.mass));
}
public pin(): Particle {
this.isPinned = true;
this.pinnedPosition = this.position;
return this;
}
public pinTo(position: Vector2): Particle {
this.isPinned = true;
this.pinnedPosition = position;
this.position = this.pinnedPosition;
return this;
}
public unpin(): Particle {
this.isPinned = false;
return this;
}
}
}

View File

@@ -1,163 +0,0 @@
module es {
export class VerletWorld {
public gravity: Vector2 = new Vector2(0, -980);
public constraintIterations = 3;
public maximumStepIterations = 5;
public simulationBounds: Rectangle;
public allowDragging: boolean = true;
public selectionRadiusSquared = 20 * 20;
_draggedParticle: Particle;
_composites: Composite[] = [];
public static _colliders: Collider[] = [];
_tempCircle: Circle = new Circle(1);
_leftOverTime: number = 0;
_fixedDeltaTime: number = 1 / 60;
_iterationSteps: number = 0;
_fixedDeltaTimeSq: number = 0;
onHandleDrag: Function;
constructor(simulationBounds: Rectangle = null) {
this.simulationBounds = simulationBounds;
this._fixedDeltaTimeSq = Math.pow(this._fixedDeltaTime, 2);
}
public update() {
this.updateTiming();
if (this.allowDragging)
this.handleDragging();
for (let iteration = 1; iteration <= this._iterationSteps; iteration ++) {
for (let i = this._composites.length - 1; i >= 0; i --) {
const composite = this._composites[i];
for (let s = 0; s < this.constraintIterations; s ++)
composite.solveConstraints();
composite.updateParticles(this._fixedDeltaTimeSq, this.gravity);
composite.handleConstraintCollisions();
for (let j = 0; j < composite.particles.length; j ++) {
const p = composite.particles[j];
if (this.simulationBounds) {
this.constrainParticleToBounds(p);
}
if (p.collidesWithColliders)
this.handleCollisions(p, composite.collidesWithLayers);
}
}
}
}
constrainParticleToBounds(p: Particle) {
const tempPos = p.position;
const bounds = this.simulationBounds;
if (p.radius == 0) {
if (tempPos.y > bounds.height)
tempPos.y = bounds.height;
else if (tempPos.y < bounds.y)
tempPos.y = bounds.y;
if (tempPos.x < bounds.x)
tempPos.x = bounds.x;
else if (tempPos.x > bounds.width)
tempPos.x = bounds.width;
} else {
if (tempPos.y < bounds.y + p.radius)
tempPos.y = 2 * (bounds.y + p.radius) - tempPos.y;
if (tempPos.y > bounds.height - p.radius)
tempPos.y = 2 * (bounds.height - p.radius) - tempPos.y;
if (tempPos.x > bounds.width - p.radius)
tempPos.x = 2 * (bounds.width - p.radius) - tempPos.x;
if (tempPos.x < bounds.x + p.radius)
tempPos.x = 2 * (bounds.x + p.radius) - tempPos.x;
}
p.position = tempPos;
}
handleCollisions(p: Particle, collidesWithLayers: number) {
const collidedCount = Physics.overlapCircleAll(p.position, p.radius, VerletWorld._colliders, collidesWithLayers);
for (let i = 0; i < collidedCount; i++) {
const collider = VerletWorld._colliders[i];
if (collider.isTrigger)
continue;
const collisionResult = new CollisionResult();
if (p.radius < 2) {
if (collider.shape.pointCollidesWithShape(p.position, collisionResult)) {
p.position = p.position.sub(collisionResult.minimumTranslationVector);
}
} else {
this._tempCircle.radius = p.radius;
this._tempCircle.position = p.position;
if (this._tempCircle.collidesWithShape(collider.shape, collisionResult)) {
p.position = p.position.sub(collisionResult.minimumTranslationVector);
}
}
}
}
updateTiming() {
this._leftOverTime += Time.deltaTime;
this._iterationSteps = Math.trunc(this._leftOverTime / this._fixedDeltaTime);
this._leftOverTime -= this._iterationSteps * this._fixedDeltaTime;
this._iterationSteps = Math.min(this._iterationSteps, this.maximumStepIterations);
}
public addComposite<T extends Composite>(composite: T): T {
this._composites.push(composite);
return composite;
}
public removeComposite(composite: Composite) {
const index = this._composites.indexOf(composite);
this._composites.splice(index, 1);
}
handleDragging() {
if (this.onHandleDrag)
this.onHandleDrag();
}
public getNearestParticle(position: Vector2) {
let nearestSquaredDistance = this.selectionRadiusSquared;
let particle: Particle = null;
for (let j = 0; j < this._composites.length; j++) {
const particles = this._composites[j].particles;
for (let i = 0; i < particles.length; i++) {
const p = particles[i];
const squaredDistanceToParticle = Vector2.sqrDistance(p.position, position);
if (squaredDistanceToParticle <= this.selectionRadiusSquared &&
(particle == null || squaredDistanceToParticle < nearestSquaredDistance)) {
particle = p;
nearestSquaredDistance = squaredDistanceToParticle;
}
}
}
return particle;
}
public debugRender(batcher: IBatcher) {
for (let i = 0; i < this._composites.length; i ++) {
this._composites[i].debugRender(batcher);
}
if (this.allowDragging) {
if (this._draggedParticle != null) {
batcher.drawCircle(this._draggedParticle.position, 8, Color.White);
}
}
}
}
}

View File

@@ -3,7 +3,6 @@ module es {
* 一系列静态方法来处理所有常见的tween类型结构以及它们的unclamped lerps.unclamped lerps对于超过0-1范围的bounce、elastic或其他tweens是必需的
*/
export class Lerps {
public static lerp(from: Color, to: Color, t: number);
public static lerp(from: number, to: number, t: number);
public static lerp(from: Rectangle, to: Rectangle, t: number);
public static lerp(from: Vector2, to: Vector2, t: number);
@@ -12,12 +11,6 @@ module es {
return from + (to - from) * t;
}
if (from instanceof Color && to instanceof Color) {
const t255 = t * 255;
return new Color(from.r + (to.r - from.r) * t255 / 255, from.g + (to.g - from.g) * t255 / 255,
from.b + (to.b - from.b) * t255 / 255, from.a + (to.a - from.a) * t255 / 255)
}
if (from instanceof Rectangle && to instanceof Rectangle) {
return new Rectangle(
(from.x + (to.x - from.x) * t),
@@ -41,7 +34,6 @@ module es {
public static ease(easeType: EaseType, from: Rectangle, to: Rectangle, t: number, duration: number);
public static ease(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number);
public static ease(easeType: EaseType, from: number, to: number, t: number, duration: number);
public static ease(easeType: EaseType, from: Color, to: Color, t: number, duration: number);
public static ease(easeType: EaseType, from: any, to: any, t: number, duration: number) {
if (typeof(from) == 'number' && typeof(to) == "number") {
return this.lerp(from, to, EaseHelper.ease(easeType, t, duration));
@@ -54,10 +46,6 @@ module es {
if (from instanceof Rectangle && to instanceof Rectangle) {
return this.lerp(from, to, EaseHelper.ease(easeType, t, duration));
}
if (from instanceof Color && to instanceof Color) {
return this.lerp(from, to, EaseHelper.ease(easeType, t, duration));
}
}
public static easeAngle(easeType: EaseType, from: Vector2, to: Vector2, t: number, duration: number) {

View File

@@ -1,38 +0,0 @@
///<reference path="./Tweens.ts"/>
module es {
export class RenderableColorTween extends ColorTween implements ITweenTarget<Color> {
_renderable: RenderableComponent;
setTweenedValue(value: Color) {
this._renderable.color = value;
}
getTweenedValue(): Color {
return this._renderable.color;
}
public getTargetObject() {
return this._renderable;
}
public updateValue() {
this.setTweenedValue(Lerps.ease(this._easeType, this._fromValue, this._toValue, this._elapsedTime, this._duration));
}
public setTarget(renderable: RenderableComponent) {
this._renderable = renderable;
}
public recycleSelf() {
if (this._shouldRecycleTween) {
this._renderable = null;
this._target = null;
this._nextTween = null;
}
if (this._shouldRecycleTween && TweenManager.cacheColorTweens) {
Pool.free(ColorTween, this);
}
}
}
}

View File

@@ -88,30 +88,4 @@ module es {
Pool.free(RectangleTween, this);
}
}
export class ColorTween extends Tween<Color> {
public static create() : ColorTween {
return TweenManager.cacheColorTweens ? Pool.obtain(ColorTween) : new ColorTween();
}
constructor(target?: ITweenTarget<Color>, to?: Color, duration?: number) {
super();
this.initialize(target, to, duration);
}
public setIsRelative() {
this._isRelative = true;
this._toValue.r += this._fromValue.r;
this._toValue.g += this._fromValue.g;
this._toValue.b += this._fromValue.b;
this._toValue.a += this._fromValue.a;
return this;
}
protected updateValue() {
this._target.setTweenedValue(Lerps.ease(this._easeType, this._fromValue as any, this._toValue as any, this._elapsedTime, this._duration));
}
}
}