新增渲染接口

This commit is contained in:
yhh
2021-05-27 18:32:38 +08:00
parent 6c44d38c10
commit 26068aaf6f
29 changed files with 1301 additions and 96 deletions

View File

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

View File

@@ -116,19 +116,31 @@ module es {
return this;
}
public setVelocity(velocity: Vector2): ArcadeRigidbody {
this.velocity = velocity;
return this;
}
/**
* 用刚体的质量给刚体加上一个瞬间的力脉冲。力是一个加速度单位是每秒像素每秒。将力乘以100000使数值使用更合理
* @param force
*/
public addImpulse(force: Vector2) {
if (!this.isImmovable) {
this.velocity = this.velocity.add(Vector2.multiplyScaler(force, 100000)
this.velocity.add(Vector2.multiplyScaler(force, 100000)
.multiplyScaler(this._inverseMass * Time.deltaTime * Time.deltaTime));
}
}
public onAddedToEntity() {
this._collider = this.entity.getComponent(es.Collider);
this._collider = null;
for (let i = 0; i < this.entity.components.buffer.length; i++) {
let component = this.entity.components.buffer[i];
if (component instanceof Collider) {
this._collider = component;
break;
}
}
Debug.warnIf(this._collider == null, "ArcadeRigidbody 没有 Collider。ArcadeRigidbody需要一个Collider!");
}
@@ -139,9 +151,9 @@ module es {
}
if (this.shouldUseGravity)
this.velocity = this.velocity.add(Vector2.multiplyScaler(Physics.gravity, Time.deltaTime));
this.velocity.add(Vector2.multiplyScaler(Physics.gravity, Time.deltaTime));
this.entity.transform.position = this.entity.transform.position.add(Vector2.multiplyScaler(this.velocity, Time.deltaTime));
this.entity.position = this.entity.position.add(Vector2.multiplyScaler(this.velocity, Time.deltaTime));
let collisionResult = new CollisionResult();
// 捞取我们在新的位置上可能会碰撞到的任何东西
@@ -160,10 +172,10 @@ module es {
this.processCollision(neighborRigidbody, collisionResult.minimumTranslationVector);
} else {
// 没有ArcadeRigidbody所以我们假设它是不动的只移动我们自己的
this.entity.transform.position = this.entity.transform.position.subtract(collisionResult.minimumTranslationVector);
this.entity.position = this.entity.position.subtract(collisionResult.minimumTranslationVector);
let relativeVelocity = this.velocity.clone();
this.calculateResponseVelocity(relativeVelocity, collisionResult.minimumTranslationVector, relativeVelocity);
this.velocity = this.velocity.add(relativeVelocity);
this.velocity.add(relativeVelocity);
}
}
}
@@ -176,12 +188,12 @@ module es {
*/
public processOverlap(other: ArcadeRigidbody, minimumTranslationVector: Vector2) {
if (this.isImmovable) {
other.entity.transform.position = other.entity.transform.position.add(minimumTranslationVector);
other.entity.position = other.entity.position.add(minimumTranslationVector);
} else if (other.isImmovable) {
this.entity.transform.position = this.entity.transform.position.subtract(minimumTranslationVector);
this.entity.position = this.entity.position.subtract(minimumTranslationVector);
} else {
this.entity.transform.position = this.entity.transform.position.subtract(Vector2.multiplyScaler(minimumTranslationVector, 0.5));
other.entity.transform.position = other.entity.transform.position.add(Vector2.multiplyScaler(minimumTranslationVector, 0.5));
this.entity.position = this.entity.position.subtract(Vector2.multiplyScaler(minimumTranslationVector, 0.5));
other.entity.position = other.entity.position.add(Vector2.multiplyScaler(minimumTranslationVector, 0.5));
}
}
@@ -203,8 +215,8 @@ module es {
let ourResponseFraction = this._inverseMass / totalinverseMass;
let otherResponseFraction = other._inverseMass / totalinverseMass;
this.velocity = this.velocity.add(Vector2.multiplyScaler(relativeVelocity, ourResponseFraction));
other.velocity = other.velocity.subtract(Vector2.multiplyScaler(relativeVelocity, otherResponseFraction));
this.velocity.add(Vector2.multiplyScaler(relativeVelocity, ourResponseFraction));
other.velocity.subtract(Vector2.multiplyScaler(relativeVelocity, otherResponseFraction));
}
/**
@@ -234,8 +246,10 @@ module es {
coefficientOfFriction = 1.01;
// 弹性影响速度的法向分量,摩擦力影响速度的切向分量
responseVelocity = Vector2.multiplyScaler(normalVelocityComponent, -(1 + this._elasticity))
let r = Vector2.multiplyScaler(normalVelocityComponent, -(1 + this._elasticity))
.subtract(Vector2.multiplyScaler(tangentialVelocityComponent, coefficientOfFriction));
responseVelocity.x = r.x;
responseVelocity.y = r.y;
}
}
}

View File

@@ -8,10 +8,15 @@ module es {
* @param width
* @param height
*/
constructor(x: number, y: number, width: number, height: number) {
constructor(x: number = 0, y: number = 0, width: number = 1, height: number = 1) {
super();
this._localOffset = new Vector2(x + width / 2, y + height / 2);
if (width == 1 && height == 1) {
this._colliderRequiresAutoSizing = true;
} else {
this._localOffset = new Vector2(x + width / 2, y + height / 2);
}
this.shape = new Box(width, height);
}
@@ -61,7 +66,7 @@ module es {
// 更新框,改变边界,如果我们需要更新物理系统中的边界
box.updateBox(width, box.height);
this._isPositionDirty = true;
if (this.entity && this._isParentEntityAddedToScene)
if (this.entity != null && this._isParentEntityAddedToScene)
Physics.updateCollider(this);
}
@@ -84,6 +89,18 @@ 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

@@ -7,10 +7,13 @@ module es {
*
* @param radius
*/
constructor(radius: number) {
constructor(radius: number = 1) {
super();
this.shape = new Circle(radius);
if (radius == 1) {
this._colliderRequiresAutoSizing = true;
}
}
public get radius(): number {
@@ -40,6 +43,17 @@ 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

@@ -1,5 +1,5 @@
module es {
export class Collider extends Component {
export abstract class Collider extends Component {
/**
* 对撞机的基本形状
*/
@@ -59,10 +59,10 @@ module es {
}
public get bounds(): Rectangle {
if (this._isPositionDirty || this._isRotationDirty) {
// if (this._isPositionDirty || this._isRotationDirty) {
this.shape.recalculateBounds(this);
this._isPositionDirty = this._isRotationDirty = false;
}
// this._isPositionDirty = this._isRotationDirty = false;
// }
return this.shape.bounds;
}
@@ -114,6 +114,33 @@ 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 = Vector2.subtract(renderableBounds.center, this.entity.transform.position);
} else if (this instanceof BoxCollider) {
this.width = width;
this.height = height;
this.localOffset = Vector2.subtract(renderableBounds.center, this.entity.transform.position);
}
}
}
this._isParentEntityAddedToScene = true;
this.registerColliderWithPhysicsSystem();
}

View File

@@ -19,7 +19,15 @@ module es {
* @param collisionResult
*/
public calculateMovement(motion: Vector2, collisionResult: CollisionResult): boolean {
if (this.entity.getComponent(Collider) == null || this._triggerHelper == null) {
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 || this._triggerHelper == null) {
return false;
}

View File

@@ -8,7 +8,15 @@ module es {
private _collider: Collider;
public onAddedToEntity() {
this._collider = this.entity.getComponent(Collider);
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;
}
}
this._collider = collider;
Debug.warnIf(this._collider == null, "ProjectileMover没有Collider。ProjectilMover需要一个Collider!");
}

View File

@@ -0,0 +1,9 @@
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

@@ -0,0 +1,125 @@
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 get renderLayer() {
return this._renderLayer;
}
public set renderLayer(value: number) {
this.setRenderLayer(value);
}
protected _renderLayer: number = 0;
public onEntityTransformChanged(comp: transform.Component) {
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();
}
}
}

View File

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

View File

@@ -2,22 +2,22 @@
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,6 +45,10 @@ module es {
}
public begin() {
if (this._renderers.length == 0) {
this.addRenderer(new DefaultRenderer());
}
Physics.reset();
if (this.entityProcessors != null)
@@ -58,6 +62,9 @@ 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++) {
@@ -65,6 +72,7 @@ module es {
}
this._sceneComponents.length = 0;
this.camera = null;
Physics.clear();
if (this.entityProcessors)
@@ -91,6 +99,38 @@ 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

@@ -106,7 +106,7 @@ module es {
public get worldToLocalTransform(): Matrix2D {
if (this._worldToLocalDirty) {
if (!this.parent) {
if (this.parent == null) {
this._worldToLocalTransform = Matrix2D.identity;
} else {
this.parent.updateTransform();
@@ -267,13 +267,13 @@ module es {
return this;
if (this._parent != null) {
let children = new es.List(this._parent._children);
children.remove(this);
const index = this._parent._children.findIndex(t => t == this);
if (index != -1)
this._parent._children.splice(index, 1);
}
if (parent != null) {
let children = new es.List(parent._children);
children.add(this);
parent._children.push(this);
}
this._parent = parent;
@@ -294,7 +294,7 @@ module es {
this._position = position;
if (this.parent != null) {
this.localPosition = Vector2.transform(this._position, this._worldToLocalTransform);
this.localPosition = Vector2.transform(this._position, this.worldToLocalTransform);
} else {
this.localPosition = position;
}
@@ -325,7 +325,7 @@ module es {
*/
public setRotation(radians: number): Transform {
this._rotation = radians;
if (this.parent) {
if (this.parent != null) {
this.localRotation = this.parent.rotation + radians;
} else {
this.localRotation = radians;
@@ -378,7 +378,7 @@ module es {
*/
public setScale(scale: Vector2): Transform {
this._scale = scale;
if (this.parent) {
if (this.parent != null) {
this.localScale = Vector2.divide(scale, this.parent._scale);
} else {
this.localScale = scale;

View File

@@ -95,6 +95,9 @@ module es {
let component = this._components[i];
if (!component) continue;
if (component instanceof RenderableComponent)
this._entity.scene.renderableComponents.remove(component);
// 处理IUpdatable
if (isIUpdatable(component))
new es.List(this._updatableComponents).remove(component);
@@ -109,6 +112,9 @@ 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.remove(component);
if (isIUpdatable(component))
this._updatableComponents.push(component);
@@ -151,6 +157,9 @@ 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);
@@ -186,6 +195,9 @@ 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)
@@ -313,5 +325,13 @@ module es {
this._components[i].onDisabled();
}
}
public debugRender(batcher: IBatcher) {
for (let i = 0; i < this._components.length; i ++) {
if (this._components[i].enabled) {
this._components[i].debugRender(batcher);
}
}
}
}
}

View File

@@ -0,0 +1,81 @@
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;
}
}
}
}
}