移动部分类模块至es
优化框架
This commit is contained in:
@@ -75,7 +75,7 @@ module es {
|
|||||||
* 当实体的位置改变时调用。这允许组件知道它们由于父实体的移动而移动了。
|
* 当实体的位置改变时调用。这允许组件知道它们由于父实体的移动而移动了。
|
||||||
* @param comp
|
* @param comp
|
||||||
*/
|
*/
|
||||||
public onEntityTransformChanged(comp: Transform.Component) {
|
public onEntityTransformChanged(comp: transform.Component) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,33 +1,45 @@
|
|||||||
module es {
|
module es {
|
||||||
|
export enum CameraStyle {
|
||||||
|
lockOn,
|
||||||
|
cameraWindow,
|
||||||
|
}
|
||||||
|
|
||||||
export class Camera extends Component {
|
export class Camera extends Component {
|
||||||
private _zoom;
|
|
||||||
private _origin: Vector2 = Vector2.zero;
|
|
||||||
|
|
||||||
private _minimumZoom = 0.3;
|
|
||||||
private _maximumZoom = 3;
|
|
||||||
|
|
||||||
private _position: Vector2 = Vector2.zero;
|
|
||||||
/**
|
/**
|
||||||
* 如果相机模式为cameraWindow 则会进行缓动移动
|
* 对entity.transform.position的快速访问
|
||||||
* 该值为移动速度
|
|
||||||
*/
|
*/
|
||||||
public followLerp = 0.1;
|
public get position() {
|
||||||
public deadzone: Rectangle = new Rectangle();
|
return this.entity.transform.position;
|
||||||
/** 锁定偏移量 默认中心 */
|
}
|
||||||
public focusOffset: Vector2 = new Vector2();
|
|
||||||
/** 是否地图锁定 如果锁定则需要设置mapSize属性 */
|
|
||||||
public mapLockEnabled: boolean = false;
|
|
||||||
/** 设置地图大小 默认从0 0左上角开始 只需要输入地图宽高 */
|
|
||||||
public mapSize: Vector2 = new Vector2();
|
|
||||||
/** 跟随的实体 设置后镜头将锁定目标为中心 */
|
|
||||||
public targetEntity: Entity;
|
|
||||||
private _worldSpaceDeadZone: Rectangle = new Rectangle();
|
|
||||||
private _desiredPositionDelta: Vector2 = new Vector2();
|
|
||||||
private _targetCollider: Collider;
|
|
||||||
/** 相机模式 */
|
|
||||||
public cameraStyle: CameraStyle = CameraStyle.lockOn;
|
|
||||||
|
|
||||||
public get zoom(){
|
/**
|
||||||
|
* 对entity.transform.position的快速访问
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set position(value: Vector2) {
|
||||||
|
this.entity.transform.position = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对entity.transform.rotation的快速访问
|
||||||
|
*/
|
||||||
|
public get rotation(): number {
|
||||||
|
return this.entity.transform.rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对entity.transform.rotation的快速访问
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set rotation(value: number) {
|
||||||
|
this.entity.transform.rotation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缩放值应该在-1和1之间、然后将该值从minimumZoom转换为maximumZoom。
|
||||||
|
* 允许你设置适当的最小/最大值,然后使用更直观的-1到1的映射来更改缩放
|
||||||
|
*/
|
||||||
|
public get zoom() {
|
||||||
if (this._zoom == 0)
|
if (this._zoom == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -37,93 +49,143 @@ module es {
|
|||||||
return MathHelper.map(this._zoom, 1, this._maximumZoom, 0, 1);
|
return MathHelper.map(this._zoom, 1, this._maximumZoom, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public set zoom(value: number){
|
/**
|
||||||
|
* 缩放值应该在-1和1之间、然后将该值从minimumZoom转换为maximumZoom。
|
||||||
|
* 允许你设置适当的最小/最大值,然后使用更直观的-1到1的映射来更改缩放
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set zoom(value: number) {
|
||||||
this.setZoom(value);
|
this.setZoom(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get minimumZoom(){
|
/**
|
||||||
|
* 相机变焦可以达到的最小非缩放值(0-number.max)。默认为0.3
|
||||||
|
*/
|
||||||
|
public get minimumZoom() {
|
||||||
return this._minimumZoom;
|
return this._minimumZoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set minimumZoom(value: number){
|
/**
|
||||||
|
* 相机变焦可以达到的最小非缩放值(0-number.max)。默认为0.3
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set minimumZoom(value: number) {
|
||||||
this.setMinimumZoom(value);
|
this.setMinimumZoom(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get maximumZoom(){
|
/**
|
||||||
|
* 相机变焦可以达到的最大非缩放值(0-number.max)。默认为3
|
||||||
|
*/
|
||||||
|
public get maximumZoom() {
|
||||||
return this._maximumZoom;
|
return this._maximumZoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set maximumZoom(value: number){
|
/**
|
||||||
|
* 相机变焦可以达到的最大非缩放值(0-number.max)。默认为3
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set maximumZoom(value: number) {
|
||||||
this.setMaximumZoom(value);
|
this.setMaximumZoom(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get origin(){
|
/**
|
||||||
|
* 相机的边框
|
||||||
|
*/
|
||||||
|
public get bounds(){
|
||||||
|
return new Rectangle(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get origin() {
|
||||||
return this._origin;
|
return this._origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set origin(value: Vector2){
|
public set origin(value: Vector2) {
|
||||||
if (this._origin != value){
|
if (this._origin != value) {
|
||||||
this._origin = value;
|
this._origin = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get position(){
|
private _zoom;
|
||||||
return this._position;
|
private _minimumZoom = 0.3;
|
||||||
}
|
private _maximumZoom = 3;
|
||||||
|
private _origin: Vector2 = Vector2.zero;
|
||||||
|
/**
|
||||||
|
* 如果相机模式为cameraWindow 则会进行缓动移动
|
||||||
|
* 该值为移动速度
|
||||||
|
*/
|
||||||
|
public followLerp = 0.1;
|
||||||
|
/**
|
||||||
|
* 在cameraWindow模式下,宽度/高度被用做边界框,允许在不移动相机的情况下移动
|
||||||
|
* 在lockOn模式下,只使用deadZone的x/y值 你可以通过直接setCenteredDeadzone重写它来自定义deadZone
|
||||||
|
*/
|
||||||
|
public deadzone: Rectangle = new Rectangle();
|
||||||
|
/**
|
||||||
|
* 相机聚焦于屏幕中心的偏移
|
||||||
|
*/
|
||||||
|
public focusOffset: Vector2 = new Vector2();
|
||||||
|
/**
|
||||||
|
* 如果为true 相机位置则不会超出地图矩形(0, 0, mapwidth, mapheight)
|
||||||
|
*/
|
||||||
|
public mapLockEnabled: boolean = false;
|
||||||
|
/**
|
||||||
|
* 當前地圖映射的寬度和高度
|
||||||
|
*/
|
||||||
|
public mapSize: Vector2 = new Vector2();
|
||||||
|
|
||||||
public set position(value: Vector2){
|
public _targetEntity: Entity;
|
||||||
this._position = value;
|
public _targetCollider: Collider;
|
||||||
}
|
public _desiredPositionDelta: Vector2 = new Vector2();
|
||||||
|
public _cameraStyle: CameraStyle;
|
||||||
|
public _worldSpaceDeadZone: Rectangle = new Rectangle();
|
||||||
|
|
||||||
public get x(){
|
constructor(targetEntity: Entity = null, cameraStyle: CameraStyle = CameraStyle.lockOn) {
|
||||||
return this._position.x;
|
|
||||||
}
|
|
||||||
public set x(value: number){
|
|
||||||
this._position.x = value;
|
|
||||||
}
|
|
||||||
public get y(){
|
|
||||||
return this._position.y;
|
|
||||||
}
|
|
||||||
public set y(value: number){
|
|
||||||
this._position.y = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.width = SceneManager.stage.stageWidth;
|
this._targetEntity = targetEntity;
|
||||||
this.height = SceneManager.stage.stageHeight;
|
this._cameraStyle = cameraStyle;
|
||||||
this.setZoom(0);
|
this.setZoom(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSceneSizeChanged(newWidth: number, newHeight: number){
|
/**
|
||||||
|
* 当场景渲染目标的大小发生变化时,我们会更新相机的原点并调整它的位置以保持它原来的位置
|
||||||
|
* @param newWidth
|
||||||
|
* @param newHeight
|
||||||
|
*/
|
||||||
|
public onSceneSizeChanged(newWidth: number, newHeight: number) {
|
||||||
let oldOrigin = this._origin;
|
let oldOrigin = this._origin;
|
||||||
this.origin = new Vector2(newWidth / 2, newHeight / 2);
|
this.origin = new Vector2(newWidth / 2, newHeight / 2);
|
||||||
|
|
||||||
this.entity.position = Vector2.add(this.entity.position, Vector2.subtract(this._origin, oldOrigin));
|
this.entity.transform.position = Vector2.add(this.entity.transform.position, Vector2.subtract(this._origin, oldOrigin));
|
||||||
}
|
}
|
||||||
|
|
||||||
public setMinimumZoom(minZoom: number): Camera{
|
/**
|
||||||
if (this._zoom < minZoom)
|
* 对entity.transform.setPosition快速访问
|
||||||
this._zoom = this.minimumZoom;
|
* @param position
|
||||||
|
*/
|
||||||
this._minimumZoom = minZoom;
|
public setPosition(position: Vector2) {
|
||||||
|
this.entity.transform.setPosition(position.x, position.y);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setMaximumZoom(maxZoom: number): Camera {
|
/**
|
||||||
if (this._zoom > maxZoom)
|
* 对entity.transform.setRotation的快速访问
|
||||||
this._zoom = maxZoom;
|
* @param rotation
|
||||||
|
*/
|
||||||
this._maximumZoom = maxZoom;
|
public setRotation(rotation: number): Camera {
|
||||||
|
this.entity.transform.setRotation(rotation);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setZoom(zoom: number): Camera{
|
/**
|
||||||
|
* 设置缩放值,缩放值应该在-1到1之间。然后将该值从minimumZoom转换为maximumZoom
|
||||||
|
* 允许您设置适当的最小/最大值。使用更直观的-1到1的映射来更改缩放
|
||||||
|
* @param zoom
|
||||||
|
*/
|
||||||
|
public setZoom(zoom: number): Camera {
|
||||||
let newZoom = MathHelper.clamp(zoom, -1, 1);
|
let newZoom = MathHelper.clamp(zoom, -1, 1);
|
||||||
if (newZoom == 0){
|
if (newZoom == 0) {
|
||||||
this._zoom = 1;
|
this._zoom = 1;
|
||||||
} else if(newZoom < 0){
|
} else if (newZoom < 0) {
|
||||||
this._zoom = MathHelper.map(newZoom, -1, 0, this._minimumZoom, 1);
|
this._zoom = MathHelper.map(newZoom, -1, 0, this._minimumZoom, 1);
|
||||||
} else {
|
} else {
|
||||||
this._zoom = MathHelper.map(newZoom, 0, 1, 1, this._maximumZoom);
|
this._zoom = MathHelper.map(newZoom, 0, 1, 1, this._maximumZoom);
|
||||||
@@ -134,103 +196,138 @@ module es {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setRotation(rotation: number): Camera {
|
/**
|
||||||
SceneManager.scene.rotation = rotation;
|
* 相机变焦可以达到的最小非缩放值(0-number.max) 默认为0.3
|
||||||
|
* @param minZoom
|
||||||
|
*/
|
||||||
|
public setMinimumZoom(minZoom: number): Camera {
|
||||||
|
if (this._zoom < minZoom)
|
||||||
|
this._zoom = this.minimumZoom;
|
||||||
|
|
||||||
|
this._minimumZoom = minZoom;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPosition(position: Vector2){
|
/**
|
||||||
this.entity.position = position;
|
* 相机变焦可以达到的最大非缩放值(0-number.max) 默认为3
|
||||||
|
* @param maxZoom
|
||||||
return this;
|
*/
|
||||||
}
|
public setMaximumZoom(maxZoom: number): Camera {
|
||||||
|
if (maxZoom <= 0) {
|
||||||
public follow(targetEntity: Entity, cameraStyle: CameraStyle = CameraStyle.cameraWindow){
|
console.error("maximumZoom must be greater than zero");
|
||||||
this.targetEntity = targetEntity;
|
return;
|
||||||
this.cameraStyle = cameraStyle;
|
|
||||||
let cameraBounds = new Rectangle(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
|
||||||
|
|
||||||
switch (this.cameraStyle){
|
|
||||||
case CameraStyle.cameraWindow:
|
|
||||||
let w = cameraBounds.width / 6;
|
|
||||||
let h = cameraBounds.height / 3;
|
|
||||||
this.deadzone = new Rectangle((cameraBounds.width - w) / 2, (cameraBounds.height - h) / 2, w, h);
|
|
||||||
break;
|
|
||||||
case CameraStyle.lockOn:
|
|
||||||
this.deadzone = new Rectangle(cameraBounds.width / 2, cameraBounds.height / 2, 10, 10);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._zoom > maxZoom)
|
||||||
|
this._zoom = maxZoom;
|
||||||
|
|
||||||
|
this._maximumZoom = maxZoom;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(){
|
public zoomIn(deltaZoom: number) {
|
||||||
let cameraBounds = new Rectangle(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
this.zoom += deltaZoom;
|
||||||
let halfScreen = Vector2.multiply(new Vector2(cameraBounds.width, cameraBounds.height), new Vector2(0.5));
|
}
|
||||||
this._worldSpaceDeadZone.x = this.position.x - halfScreen.x * SceneManager.scene.scaleX + this.deadzone.x + this.focusOffset.x;
|
|
||||||
|
public zoomOut(deltaZoom: number) {
|
||||||
|
this.zoom -= deltaZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onAddedToEntity() {
|
||||||
|
this.follow(this._targetEntity, this._cameraStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public update() {
|
||||||
|
let halfScreen = Vector2.multiply(new Vector2(this.bounds.width, this.bounds.height), new Vector2(0.5));
|
||||||
|
this._worldSpaceDeadZone.x = this.position.x - halfScreen.x * SceneManager.scene.scaleX + this.deadzone.x + this.focusOffset.x;
|
||||||
this._worldSpaceDeadZone.y = this.position.y - halfScreen.y * SceneManager.scene.scaleY + this.deadzone.y + this.focusOffset.y;
|
this._worldSpaceDeadZone.y = this.position.y - halfScreen.y * SceneManager.scene.scaleY + this.deadzone.y + this.focusOffset.y;
|
||||||
this._worldSpaceDeadZone.width = this.deadzone.width;
|
this._worldSpaceDeadZone.width = this.deadzone.width;
|
||||||
this._worldSpaceDeadZone.height = this.deadzone.height;
|
this._worldSpaceDeadZone.height = this.deadzone.height;
|
||||||
|
|
||||||
if (this.targetEntity)
|
if (this._targetEntity)
|
||||||
this.updateFollow();
|
this.updateFollow();
|
||||||
|
|
||||||
this.position = Vector2.lerp(this.position, Vector2.add(this.position, this._desiredPositionDelta), this.followLerp);
|
this.position = Vector2.lerp(this.position, Vector2.add(this.position, this._desiredPositionDelta), this.followLerp);
|
||||||
this.entity.roundPosition();
|
this.entity.transform.roundPosition();
|
||||||
|
|
||||||
if (this.mapLockEnabled){
|
if (this.mapLockEnabled) {
|
||||||
this.position = this.clampToMapSize(this.position);
|
this.position = this.clampToMapSize(this.position);
|
||||||
this.entity.roundPosition();
|
this.entity.transform.roundPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private clampToMapSize(position: Vector2){
|
/**
|
||||||
let cameraBounds = new Rectangle(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
* 固定相机 永远不会离开地图的可见区域
|
||||||
let halfScreen = Vector2.multiply(new Vector2(cameraBounds.width, cameraBounds.height), new Vector2(0.5));
|
* @param position
|
||||||
|
*/
|
||||||
|
public clampToMapSize(position: Vector2) {
|
||||||
|
let halfScreen = Vector2.multiply(new Vector2(this.bounds.width, this.bounds.height), new Vector2(0.5));
|
||||||
let cameraMax = new Vector2(this.mapSize.x - halfScreen.x, this.mapSize.y - halfScreen.y);
|
let cameraMax = new Vector2(this.mapSize.x - halfScreen.x, this.mapSize.y - halfScreen.y);
|
||||||
|
|
||||||
return Vector2.clamp(position, halfScreen, cameraMax);
|
return Vector2.clamp(position, halfScreen, cameraMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateFollow(){
|
public updateFollow() {
|
||||||
this._desiredPositionDelta.x = this._desiredPositionDelta.y = 0;
|
this._desiredPositionDelta.x = this._desiredPositionDelta.y = 0;
|
||||||
|
|
||||||
if (this.cameraStyle == CameraStyle.lockOn){
|
if (this._cameraStyle == CameraStyle.lockOn) {
|
||||||
let targetX = this.targetEntity.position.x;
|
let targetX = this._targetEntity.transform.position.x;
|
||||||
let targetY = this.targetEntity.position.y;
|
let targetY = this._targetEntity.transform.position.y;
|
||||||
|
|
||||||
if (this._worldSpaceDeadZone.x > targetX)
|
if (this._worldSpaceDeadZone.x > targetX)
|
||||||
this._desiredPositionDelta.x = targetX - this._worldSpaceDeadZone.x;
|
this._desiredPositionDelta.x = targetX - this._worldSpaceDeadZone.x;
|
||||||
else if(this._worldSpaceDeadZone.x < targetX)
|
else if (this._worldSpaceDeadZone.x < targetX)
|
||||||
this._desiredPositionDelta.x = targetX - this._worldSpaceDeadZone.x;
|
this._desiredPositionDelta.x = targetX - this._worldSpaceDeadZone.x;
|
||||||
|
|
||||||
if (this._worldSpaceDeadZone.y < targetY)
|
if (this._worldSpaceDeadZone.y < targetY)
|
||||||
this._desiredPositionDelta.y = targetY - this._worldSpaceDeadZone.y;
|
this._desiredPositionDelta.y = targetY - this._worldSpaceDeadZone.y;
|
||||||
else if(this._worldSpaceDeadZone.y > targetY)
|
else if (this._worldSpaceDeadZone.y > targetY)
|
||||||
this._desiredPositionDelta.y = targetY - this._worldSpaceDeadZone.y;
|
this._desiredPositionDelta.y = targetY - this._worldSpaceDeadZone.y;
|
||||||
} else {
|
} else {
|
||||||
if (!this._targetCollider){
|
if (!this._targetCollider) {
|
||||||
this._targetCollider = this.targetEntity.getComponent<Collider>(Collider);
|
this._targetCollider = this._targetEntity.getComponent<Collider>(Collider);
|
||||||
if (!this._targetCollider)
|
if (!this._targetCollider)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetBounds = this.targetEntity.getComponent<Collider>(Collider).bounds;
|
let targetBounds = this._targetEntity.getComponent<Collider>(Collider).bounds;
|
||||||
if (!this._worldSpaceDeadZone.containsRect(targetBounds)){
|
if (!this._worldSpaceDeadZone.containsRect(targetBounds)) {
|
||||||
if (this._worldSpaceDeadZone.left > targetBounds.left)
|
if (this._worldSpaceDeadZone.left > targetBounds.left)
|
||||||
this._desiredPositionDelta.x = targetBounds.left - this._worldSpaceDeadZone.left;
|
this._desiredPositionDelta.x = targetBounds.left - this._worldSpaceDeadZone.left;
|
||||||
else if(this._worldSpaceDeadZone.right < targetBounds.right)
|
else if (this._worldSpaceDeadZone.right < targetBounds.right)
|
||||||
this._desiredPositionDelta.x = targetBounds.right - this._worldSpaceDeadZone.right;
|
this._desiredPositionDelta.x = targetBounds.right - this._worldSpaceDeadZone.right;
|
||||||
|
|
||||||
if (this._worldSpaceDeadZone.bottom < targetBounds.bottom)
|
if (this._worldSpaceDeadZone.bottom < targetBounds.bottom)
|
||||||
this._desiredPositionDelta.y = targetBounds.bottom - this._worldSpaceDeadZone.bottom;
|
this._desiredPositionDelta.y = targetBounds.bottom - this._worldSpaceDeadZone.bottom;
|
||||||
else if(this._worldSpaceDeadZone.top > targetBounds.top)
|
else if (this._worldSpaceDeadZone.top > targetBounds.top)
|
||||||
this._desiredPositionDelta.y = targetBounds.top - this._worldSpaceDeadZone.top;
|
this._desiredPositionDelta.y = targetBounds.top - this._worldSpaceDeadZone.top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export enum CameraStyle {
|
public follow(targetEntity: Entity, cameraStyle: CameraStyle = CameraStyle.cameraWindow) {
|
||||||
lockOn,
|
this._targetEntity = targetEntity;
|
||||||
cameraWindow,
|
this._cameraStyle = cameraStyle;
|
||||||
|
|
||||||
|
switch (this._cameraStyle) {
|
||||||
|
case CameraStyle.cameraWindow:
|
||||||
|
let w = this.bounds.width / 6;
|
||||||
|
let h = this.bounds.height / 3;
|
||||||
|
this.deadzone = new Rectangle((this.bounds.width - w) / 2, (this.bounds.height - h) / 2, w, h);
|
||||||
|
break;
|
||||||
|
case CameraStyle.lockOn:
|
||||||
|
this.deadzone = new Rectangle(this.bounds.width / 2, this.bounds.height / 2, 10, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以给定的尺寸设置当前相机边界中心的死区
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
*/
|
||||||
|
public setCenteredDeadzone(width: number, height: number) {
|
||||||
|
this.deadzone = new Rectangle((this.bounds.width - width) / 2, (this.bounds.height - height) / 2, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
source/src/ECS/Components/IUpdatableComparer.ts
Normal file
10
source/src/ECS/Components/IUpdatableComparer.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module es {
|
||||||
|
/**
|
||||||
|
* 用于比较组件更新排序
|
||||||
|
*/
|
||||||
|
export class IUpdatableComparer {
|
||||||
|
public compare(a: Component, b: Component){
|
||||||
|
return a.updateOrder - b.updateOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,161 +1,210 @@
|
|||||||
abstract class Collider extends Component {
|
module es {
|
||||||
/** 对撞机的基本形状 */
|
export abstract class Collider extends Component {
|
||||||
public shape: Shape;
|
/**
|
||||||
protected _localOffset: Vector2 = Vector2.zero;
|
* 对撞机的基本形状
|
||||||
public get localOffset(){
|
*/
|
||||||
return this._localOffset;
|
public shape: Shape;
|
||||||
}
|
/**
|
||||||
public set localOffset(){
|
* 将localOffset添加到实体。获取碰撞器几何图形的最终位置。
|
||||||
|
* 允许向一个实体添加多个碰撞器并分别定位,还允许你设置缩放/旋转
|
||||||
|
*/
|
||||||
|
public get localOffset(): Vector2{
|
||||||
|
return this._localOffset;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
public _localOffsetLength: number;
|
* 将localOffset添加到实体。获取碰撞器几何图形的最终位置。
|
||||||
/** 在处理冲突时,physicsLayer可以用作过滤器。Flags类有帮助位掩码的方法。 */
|
* 允许向一个实体添加多个碰撞器并分别定位,还允许你设置缩放/旋转
|
||||||
public physicsLayer = 1 << 0;
|
* @param value
|
||||||
/** 如果这个碰撞器是一个触发器,它将不会引起碰撞,但它仍然会触发事件 */
|
*/
|
||||||
public isTrigger: boolean;
|
public set localOffset(value: Vector2){
|
||||||
/**
|
this.setLocalOffset(value);
|
||||||
* 这个对撞机在物理系统注册时的边界。
|
}
|
||||||
* 存储这个允许我们始终能够安全地从物理系统中移除对撞机,即使它在试图移除它之前已经被移动了。
|
|
||||||
*/
|
|
||||||
public registeredPhysicsBounds: Rectangle = new Rectangle();
|
|
||||||
/** 如果为true,碰撞器将根据附加的变换缩放和旋转 */
|
|
||||||
public shouldColliderScaleAndRotateWithTransform = true;
|
|
||||||
/** 默认为所有层。 */
|
|
||||||
public collidesWithLayers = Physics.allLayers;
|
|
||||||
|
|
||||||
/** 标记来跟踪我们的实体是否被添加到场景中 */
|
/**
|
||||||
protected _isParentEntityAddedToScene;
|
* 镖师碰撞器的绝对位置
|
||||||
protected _colliderRequiresAutoSizing;
|
*/
|
||||||
/** 标记来记录我们是否注册了物理系统 */
|
public get absolutePosition(): Vector2{
|
||||||
protected _isColliderRegistered;
|
return Vector2.add(this.entity.transform.position, this._localOffset);
|
||||||
|
}
|
||||||
|
|
||||||
public get bounds(): Rectangle {
|
/**
|
||||||
let shapeBounds = this.shape.bounds;
|
* 封装变换。如果碰撞器没和实体一起旋转 则返回transform.rotation
|
||||||
let colliderBuonds = new Rectangle(this.entity.x, this.entity.y, shapeBounds.width, shapeBounds.height);
|
*/
|
||||||
return colliderBuonds;
|
public get rotation(): number{
|
||||||
}
|
if (this.shouldColliderScaleAndRotateWithTransform && this.entity)
|
||||||
|
return this.entity.transform.rotation;
|
||||||
|
|
||||||
/**
|
return 0;
|
||||||
* 将localOffset添加到实体。获取碰撞器的最终位置。
|
}
|
||||||
* 这允许您向一个实体添加多个碰撞器并分别定位它们。
|
|
||||||
* @param offset
|
/**
|
||||||
*/
|
* 如果这个碰撞器是一个触发器,它将不会引起碰撞,但它仍然会触发事件
|
||||||
public setLocalOffset(offset: Vector2): Collider{
|
*/
|
||||||
if (this._localOffset != offset){
|
public isTrigger: boolean;
|
||||||
this.unregisterColliderWithPhysicsSystem();
|
/**
|
||||||
this._localOffset = offset;
|
* 在处理冲突时,physicsLayer可以用作过滤器。Flags类有帮助位掩码的方法
|
||||||
this._localOffsetLength = this._localOffset.length();
|
*/
|
||||||
|
public physicsLayer = 1 << 0;
|
||||||
|
/**
|
||||||
|
* 碰撞器在使用移动器移动时应该碰撞的层
|
||||||
|
* 默认为所有层
|
||||||
|
*/
|
||||||
|
public collidesWithLayers = Physics.allLayers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果为true,碰撞器将根据附加的变换缩放和旋转
|
||||||
|
*/
|
||||||
|
public shouldColliderScaleAndRotateWithTransform = true;
|
||||||
|
|
||||||
|
public get bounds(): Rectangle {
|
||||||
|
this.shape.recalculateBounds(this);
|
||||||
|
|
||||||
|
return this.shape.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个对撞机在物理系统注册时的边界。
|
||||||
|
* 存储这个允许我们始终能够安全地从物理系统中移除对撞机,即使它在试图移除它之前已经被移动了。
|
||||||
|
*/
|
||||||
|
public registeredPhysicsBounds: Rectangle = new Rectangle();
|
||||||
|
protected _colliderRequiresAutoSizing;
|
||||||
|
protected _localOffset: Vector2 = Vector2.zero;
|
||||||
|
public _localOffsetLength: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记来跟踪我们的实体是否被添加到场景中
|
||||||
|
*/
|
||||||
|
protected _isParentEntityAddedToScene;
|
||||||
|
/**
|
||||||
|
* 标记来记录我们是否注册了物理系统
|
||||||
|
*/
|
||||||
|
protected _isColliderRegistered;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将localOffset添加到实体。获取碰撞器的最终位置。
|
||||||
|
* 这允许您向一个实体添加多个碰撞器并分别定位它们。
|
||||||
|
* @param offset
|
||||||
|
*/
|
||||||
|
public setLocalOffset(offset: Vector2): Collider{
|
||||||
|
if (this._localOffset != offset){
|
||||||
|
this.unregisterColliderWithPhysicsSystem();
|
||||||
|
this._localOffset = offset;
|
||||||
|
this._localOffsetLength = this._localOffset.length();
|
||||||
|
this.registerColliderWithPhysicsSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果为true,碰撞器将根据附加的变换缩放和旋转
|
||||||
|
* @param shouldColliderScaleAndRotationWithTransform
|
||||||
|
*/
|
||||||
|
public setShouldColliderScaleAndRotateWithTransform(shouldColliderScaleAndRotationWithTransform: boolean): Collider {
|
||||||
|
this.shouldColliderScaleAndRotateWithTransform = shouldColliderScaleAndRotationWithTransform;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onAddedToEntity() {
|
||||||
|
if (this._colliderRequiresAutoSizing) {
|
||||||
|
if (!(this instanceof BoxCollider || this instanceof CircleCollider)) {
|
||||||
|
console.error("Only box and circle colliders can be created automatically");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let renderable = this.entity.getComponent<RenderableComponent>(RenderableComponent);
|
||||||
|
if (renderable) {
|
||||||
|
let bounds = renderable.bounds;
|
||||||
|
|
||||||
|
// 这里我们需要大小*反尺度,因为当我们自动调整碰撞器的大小时,它需要没有缩放的渲染
|
||||||
|
let width = bounds.width / this.entity.scale.x;
|
||||||
|
let height = bounds.height / this.entity.scale.y;
|
||||||
|
// 圆碰撞器需要特别注意原点
|
||||||
|
if (this instanceof CircleCollider){
|
||||||
|
let circleCollider = this as CircleCollider;
|
||||||
|
circleCollider.radius = Math.max(width, height) * 0.5;
|
||||||
|
} else {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取渲染的中心,将其转移到本地坐标,并使用它作为碰撞器的localOffset
|
||||||
|
this.localOffset = Vector2.subtract(bounds.center, this.entity.transform.position);
|
||||||
|
} else {
|
||||||
|
console.warn("Collider has no shape and no RenderableComponent. Can't figure out how to size it.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isParentEntityAddedToScene = true;
|
||||||
this.registerColliderWithPhysicsSystem();
|
this.registerColliderWithPhysicsSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
public onRemovedFromEntity() {
|
||||||
}
|
this.unregisterColliderWithPhysicsSystem();
|
||||||
|
this._isParentEntityAddedToScene = false;
|
||||||
/**
|
|
||||||
* 父实体会在不同的时间调用它(当添加到场景,启用,等等)
|
|
||||||
*/
|
|
||||||
public registerColliderWithPhysicsSystem() {
|
|
||||||
// 如果在将我们添加到实体之前更改了origin等属性,则实体可以为null
|
|
||||||
if (this._isParentEntityAddedToScene && !this._isColliderRegistered) {
|
|
||||||
Physics.addCollider(this);
|
|
||||||
this._isColliderRegistered = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public onEntityTransformChanged(comp: transform.Component) {
|
||||||
* 父实体会在不同的时候调用它(从场景中移除,禁用,等等)
|
if (this._isColliderRegistered)
|
||||||
*/
|
Physics.updateCollider(this);
|
||||||
public unregisterColliderWithPhysicsSystem() {
|
|
||||||
if (this._isParentEntityAddedToScene && this._isColliderRegistered) {
|
|
||||||
Physics.removeCollider(this);
|
|
||||||
}
|
}
|
||||||
this._isColliderRegistered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public onEnabled() {
|
||||||
* 检查这个形状是否与物理系统中的其他对撞机重叠
|
this.registerColliderWithPhysicsSystem();
|
||||||
* @param other
|
}
|
||||||
*/
|
|
||||||
public overlaps(other: Collider) {
|
|
||||||
return this.shape.overlaps(other.shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public onDisabled() {
|
||||||
* 检查这个与运动应用的碰撞器(移动向量)是否与碰撞器碰撞。如果是这样,将返回true,并且结果将填充碰撞数据。
|
this.unregisterColliderWithPhysicsSystem();
|
||||||
* @param collider
|
}
|
||||||
* @param motion
|
|
||||||
*/
|
|
||||||
public collidesWith(collider: Collider, motion: Vector2) {
|
|
||||||
// 改变形状的位置,使它在移动后的位置,这样我们可以检查重叠
|
|
||||||
let oldPosition = this.entity.position;
|
|
||||||
this.entity.position = Vector2.add(this.entity.position, motion);
|
|
||||||
|
|
||||||
let result = this.shape.collidesWithShape(collider.shape);
|
/**
|
||||||
if (result)
|
* 父实体会在不同的时间调用它(当添加到场景,启用,等等)
|
||||||
result.collider = collider;
|
*/
|
||||||
|
public registerColliderWithPhysicsSystem() {
|
||||||
// 将图形位置返回到检查前的位置
|
// 如果在将我们添加到实体之前更改了origin等属性,则实体可以为null
|
||||||
this.entity.position = oldPosition;
|
if (this._isParentEntityAddedToScene && !this._isColliderRegistered) {
|
||||||
|
Physics.addCollider(this);
|
||||||
return result;
|
this._isColliderRegistered = true;
|
||||||
}
|
|
||||||
|
|
||||||
public onAddedToEntity() {
|
|
||||||
if (this._colliderRequiresAutoSizing) {
|
|
||||||
if (!(this instanceof BoxCollider || this instanceof CircleCollider)) {
|
|
||||||
console.error("Only box and circle colliders can be created automatically");
|
|
||||||
}
|
|
||||||
|
|
||||||
let renderable = this.entity.getComponent<RenderableComponent>(RenderableComponent);
|
|
||||||
if (renderable) {
|
|
||||||
let bounds = renderable.bounds;
|
|
||||||
|
|
||||||
// 这里我们需要大小*反尺度,因为当我们自动调整碰撞器的大小时,它需要没有缩放的渲染
|
|
||||||
let width = bounds.width / this.entity.scale.x;
|
|
||||||
let height = bounds.height / this.entity.scale.y;
|
|
||||||
// 圆碰撞器需要特别注意原点
|
|
||||||
if (this instanceof CircleCollider){
|
|
||||||
let circleCollider = this as CircleCollider;
|
|
||||||
circleCollider.radius = Math.max(width, height) * 0.5;
|
|
||||||
} else {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取渲染的中心,将其转移到本地坐标,并使用它作为碰撞器的localOffset
|
|
||||||
this.localOffset = Vector2.subtract(bounds.center, this.entity.position);
|
|
||||||
} else {
|
|
||||||
console.warn("Collider has no shape and no RenderableComponent. Can't figure out how to size it.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._isParentEntityAddedToScene = true;
|
/**
|
||||||
this.registerColliderWithPhysicsSystem();
|
* 父实体会在不同的时候调用它(从场景中移除,禁用,等等)
|
||||||
}
|
*/
|
||||||
|
public unregisterColliderWithPhysicsSystem() {
|
||||||
|
if (this._isParentEntityAddedToScene && this._isColliderRegistered) {
|
||||||
|
Physics.removeCollider(this);
|
||||||
|
}
|
||||||
|
this._isColliderRegistered = false;
|
||||||
|
}
|
||||||
|
|
||||||
public onRemovedFromEntity() {
|
/**
|
||||||
this.unregisterColliderWithPhysicsSystem();
|
* 检查这个形状是否与物理系统中的其他对撞机重叠
|
||||||
this._isParentEntityAddedToScene = false;
|
* @param other
|
||||||
}
|
*/
|
||||||
|
public overlaps(other: Collider) {
|
||||||
|
return this.shape.overlaps(other.shape);
|
||||||
|
}
|
||||||
|
|
||||||
public onEnabled() {
|
/**
|
||||||
this.registerColliderWithPhysicsSystem();
|
* 检查这个与运动应用的碰撞器(移动向量)是否与碰撞器碰撞。如果是这样,将返回true,并且结果将填充碰撞数据。
|
||||||
}
|
* @param collider
|
||||||
|
* @param motion
|
||||||
|
*/
|
||||||
|
public collidesWith(collider: Collider, motion: Vector2) {
|
||||||
|
// 改变形状的位置,使它在移动后的位置,这样我们可以检查重叠
|
||||||
|
let oldPosition = this.entity.position;
|
||||||
|
this.entity.position = Vector2.add(this.entity.position, motion);
|
||||||
|
|
||||||
public onDisabled() {
|
let result = this.shape.collidesWithShape(collider.shape);
|
||||||
this.unregisterColliderWithPhysicsSystem();
|
if (result)
|
||||||
}
|
result.collider = collider;
|
||||||
|
|
||||||
public onEntityTransformChanged(comp: TransformComponent) {
|
// 将图形位置返回到检查前的位置
|
||||||
if (this._isColliderRegistered)
|
this.entity.position = oldPosition;
|
||||||
Physics.updateCollider(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public update(){
|
return result;
|
||||||
let renderable = this.entity.getComponent<RenderableComponent>(RenderableComponent);
|
|
||||||
if (renderable){
|
|
||||||
this.$setX(renderable.x);
|
|
||||||
this.$setY(renderable.y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
/** 回收实例的组件类型。 */
|
module es {
|
||||||
abstract class PooledComponent extends Component {
|
/** 回收实例的组件类型。 */
|
||||||
public abstract reset();
|
export abstract class PooledComponent extends Component {
|
||||||
}
|
public abstract reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,56 +1,172 @@
|
|||||||
///<reference path="./PooledComponent.ts" />
|
///<reference path="./PooledComponent.ts" />
|
||||||
/**
|
module es {
|
||||||
* 所有可渲染组件的基类
|
/**
|
||||||
*/
|
* 所有可渲染组件的基类
|
||||||
abstract class RenderableComponent extends PooledComponent implements IRenderable {
|
*/
|
||||||
private _isVisible: boolean;
|
export abstract class RenderableComponent extends Component implements IRenderable {
|
||||||
protected _areBoundsDirty = true;
|
/**
|
||||||
protected _bounds: Rectangle = new Rectangle();
|
* renderableComponent的宽度
|
||||||
protected _localOffset: Vector2 = Vector2.zero;
|
* 如果你不重写bounds属性则需要实现这个
|
||||||
|
*/
|
||||||
|
public get width() {
|
||||||
|
return this.bounds.width;
|
||||||
|
}
|
||||||
|
|
||||||
public color: number = 0x000000;
|
/**
|
||||||
|
* renderableComponent的高度
|
||||||
|
* 如果你不重写bounds属性则需要实现这个
|
||||||
|
*/
|
||||||
|
public get height() {
|
||||||
|
return this.bounds.height;
|
||||||
|
}
|
||||||
|
|
||||||
public get width(){
|
/**
|
||||||
return this.getWidth();
|
* 这个物体的AABB, 用于相机剔除
|
||||||
}
|
*/
|
||||||
|
public get bounds(): Rectangle {
|
||||||
|
if (this._areBoundsDirty){
|
||||||
|
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, Vector2.zero,
|
||||||
|
this.entity.transform.scale, this.entity.transform.rotation, this.width, this.height);
|
||||||
|
this._areBoundsDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
public get height(){
|
return this._bounds;
|
||||||
return this.getHeight();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public get isVisible(){
|
/**
|
||||||
return this._isVisible;
|
* 较低的渲染层在前面,较高的在后面
|
||||||
}
|
*/
|
||||||
|
public get renderLayer(): number{
|
||||||
|
return this._renderLayer;
|
||||||
|
}
|
||||||
|
|
||||||
public set isVisible(value: boolean){
|
public set renderLayer(value: number){
|
||||||
this._isVisible = value;
|
|
||||||
|
|
||||||
if (this._isVisible)
|
}
|
||||||
this.onBecameVisible();
|
|
||||||
else
|
|
||||||
this.onBecameInvisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get bounds(): Rectangle{
|
/**
|
||||||
return new Rectangle(this.getBounds().x, this.getBounds().y, this.getBounds().width, this.getBounds().height);
|
* 用于着色器处理精灵
|
||||||
}
|
*/
|
||||||
|
public color: number = 0x000000;
|
||||||
|
|
||||||
protected getWidth(){
|
/**
|
||||||
return this.bounds.width;
|
* 从父实体的偏移量。用于向需要特定定位的实体
|
||||||
}
|
*/
|
||||||
|
public get localOffset(): Vector2{
|
||||||
|
return this._localOffset;
|
||||||
|
}
|
||||||
|
|
||||||
protected getHeight(){
|
/**
|
||||||
return this.bounds.height;
|
* 从父实体的偏移量。用于向需要特定定位的实体
|
||||||
}
|
* @param value
|
||||||
|
*/
|
||||||
|
public set localOffset(value: Vector2){
|
||||||
|
this.setLocalOffset(value);
|
||||||
|
}
|
||||||
|
|
||||||
protected onBecameVisible(){}
|
/**
|
||||||
|
* 可渲染的可见性。状态的改变会调用onBecameVisible/onBecameInvisible方法
|
||||||
|
*/
|
||||||
|
public get isVisible() {
|
||||||
|
return this._isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
protected onBecameInvisible(){}
|
/**
|
||||||
|
* 可渲染的可见性。状态的改变会调用onBecameVisible/onBecameInvisible方法
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set isVisible(value: boolean) {
|
||||||
|
if (this._isVisible != value){
|
||||||
|
this._isVisible = value;
|
||||||
|
|
||||||
public abstract render(camera: Camera);
|
if (this._isVisible)
|
||||||
|
this.onBecameVisible();
|
||||||
|
else
|
||||||
|
this.onBecameInvisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public isVisibleFromCamera(camera: Camera): boolean{
|
protected _localOffset: Vector2 = Vector2.zero;
|
||||||
this.isVisible = camera.getBounds().intersects(this.getBounds());
|
protected _renderLayer: number = 0;
|
||||||
return this.isVisible;
|
protected _bounds: Rectangle = new Rectangle();
|
||||||
|
private _isVisible: boolean;
|
||||||
|
protected _areBoundsDirty = true;
|
||||||
|
|
||||||
|
public onEntityTransformChanged(comp: transform.Component) {
|
||||||
|
this._areBoundsDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由渲染器调用。可以使用摄像机进行剔除
|
||||||
|
* @param camera
|
||||||
|
*/
|
||||||
|
public abstract render(camera: Camera);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当renderableComponent进入相机框架时调用
|
||||||
|
* 如果渲染器不适用isVisibleFromCamera进行剔除检查 这些方法不会被调用
|
||||||
|
*/
|
||||||
|
protected onBecameVisible() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当renderableComponent离开相机框架时调用
|
||||||
|
* 如果渲染器不适用isVisibleFromCamera进行剔除检查 这些方法不会被调用
|
||||||
|
*/
|
||||||
|
protected onBecameInvisible() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果renderableComponent的边界与camera.bounds相交 返回true
|
||||||
|
* 用于处理isVisible标志的状态开关
|
||||||
|
* 在渲染方法中使用这个方法来决定是否渲染
|
||||||
|
* @param camera
|
||||||
|
*/
|
||||||
|
public isVisibleFromCamera(camera: Camera): boolean {
|
||||||
|
this.isVisible = camera.bounds.intersects(this.bounds);
|
||||||
|
return this.isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 较低的渲染层在前面,较高的在后面
|
||||||
|
* @param renderLayer
|
||||||
|
*/
|
||||||
|
public setRenderLayer(renderLayer: number): RenderableComponent{
|
||||||
|
if (renderLayer != this._renderLayer){
|
||||||
|
let oldRenderLayer = this._renderLayer;
|
||||||
|
this._renderLayer = renderLayer;
|
||||||
|
|
||||||
|
// 如果该组件拥有一个实体,那么是由ComponentList管理,需要通知它改变了渲染层
|
||||||
|
if (this.entity && this.entity.scene)
|
||||||
|
this.entity.scene.renderableComponents.updateRenderableRenderLayer(this, oldRenderLayer, this._renderLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于着色器处理精灵
|
||||||
|
* @param color
|
||||||
|
*/
|
||||||
|
public setColor(color: number): RenderableComponent{
|
||||||
|
this.color = color;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从父实体的偏移量。用于向需要特定定位的实体
|
||||||
|
* @param offset
|
||||||
|
*/
|
||||||
|
public setLocalOffset(offset: Vector2): RenderableComponent{
|
||||||
|
if (this._localOffset != offset){
|
||||||
|
this._localOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(){
|
||||||
|
return `[RenderableComponent] ${this}, renderLayer: ${this.renderLayer}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ class Sprite {
|
|||||||
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;
|
||||||
|
|||||||
@@ -1,66 +1,120 @@
|
|||||||
class SpriteRenderer extends RenderableComponent {
|
module es {
|
||||||
private _sprite: Sprite;
|
export class SpriteRenderer extends RenderableComponent {
|
||||||
protected bitmap: egret.Bitmap;
|
public get bounds(){
|
||||||
|
if (this._areBoundsDirty){
|
||||||
|
if (this._sprite){
|
||||||
|
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, this._origin,
|
||||||
|
this.entity.transform.scale, this.entity.transform.rotation, this._sprite.sourceRect.width,
|
||||||
|
this._sprite.sourceRect.height);
|
||||||
|
this._areBoundsDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
/** 应该由这个精灵显示的精灵 */
|
return this._bounds;
|
||||||
public get sprite(): Sprite {
|
}
|
||||||
return this._sprite;
|
|
||||||
}
|
|
||||||
/** 应该由这个精灵显示的精灵 */
|
|
||||||
public set sprite(value: Sprite) {
|
|
||||||
this.setSprite(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setSprite(sprite: Sprite): SpriteRenderer {
|
|
||||||
this.removeChildren();
|
|
||||||
this._sprite = sprite;
|
|
||||||
if (this._sprite) {
|
|
||||||
this.anchorOffsetX = this._sprite.origin.x / this._sprite.sourceRect.width;
|
|
||||||
this.anchorOffsetY = this._sprite.origin.y / this._sprite.sourceRect.height;
|
|
||||||
}
|
}
|
||||||
this.bitmap = new egret.Bitmap(sprite.texture2D);
|
|
||||||
this.addChild(this.bitmap);
|
|
||||||
|
|
||||||
return this;
|
/**
|
||||||
}
|
* 精灵的原点。这是在设置精灵时自动设置的
|
||||||
|
*/
|
||||||
public setColor(color: number): SpriteRenderer {
|
public get origin(): Vector2{
|
||||||
let colorMatrix = [
|
return this._origin;
|
||||||
1, 0, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 0,
|
|
||||||
0, 0, 1, 0, 0,
|
|
||||||
0, 0, 0, 1, 0
|
|
||||||
];
|
|
||||||
colorMatrix[0] = Math.floor(color / 256 / 256) / 255;
|
|
||||||
colorMatrix[6] = Math.floor(color / 256 % 256) / 255;
|
|
||||||
colorMatrix[12] = color % 256 / 255;
|
|
||||||
let colorFilter = new egret.ColorMatrixFilter(colorMatrix);
|
|
||||||
this.filters = [colorFilter];
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isVisibleFromCamera(camera: Camera): boolean {
|
|
||||||
this.isVisible = new Rectangle(0, 0, this.stage.stageWidth, this.stage.stageHeight).intersects(this.bounds);
|
|
||||||
this.visible = this.isVisible;
|
|
||||||
return this.isVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 渲染处理 在每个模块中处理各自的渲染逻辑 */
|
|
||||||
public render(camera: Camera) {
|
|
||||||
if (this.x != -camera.position.x + camera.origin.x ||
|
|
||||||
this.y != -camera.position.y + camera.origin.y) {
|
|
||||||
this.x = -camera.position.x + camera.origin.x;
|
|
||||||
this.y = -camera.position.y + camera.origin.y;
|
|
||||||
this.entity.onEntityTransformChanged(TransformComponent.position);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public onRemovedFromEntity() {
|
/**
|
||||||
if (this.parent)
|
* 精灵的原点。这是在设置精灵时自动设置的
|
||||||
this.parent.removeChild(this);
|
* @param value
|
||||||
}
|
*/
|
||||||
|
public set origin(value: Vector2){
|
||||||
|
this.setOrigin(value);
|
||||||
|
}
|
||||||
|
|
||||||
public reset() {
|
/**
|
||||||
|
* 用归一化方法设置原点
|
||||||
|
* x/y 均为 0-1
|
||||||
|
*/
|
||||||
|
public get originNormalized(): Vector2{
|
||||||
|
return new Vector2(this._origin.x / this.width * this.entity.transform.scale.x,
|
||||||
|
this._origin.y / this.height * this.entity.transform.scale.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用归一化方法设置原点
|
||||||
|
* x/y 均为 0-1
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set originNormalized(value: Vector2){
|
||||||
|
this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x,
|
||||||
|
value.y * this.height / this.entity.transform.scale.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应该由这个精灵显示的精灵
|
||||||
|
* 当设置时,精灵的原点也被设置为精灵的origin
|
||||||
|
*/
|
||||||
|
public get sprite(): Sprite {
|
||||||
|
return this._sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应该由这个精灵显示的精灵
|
||||||
|
* 当设置时,精灵的原点也被设置为精灵的origin
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public set sprite(value: Sprite) {
|
||||||
|
this.setSprite(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _origin: Vector2;
|
||||||
|
protected _sprite: Sprite;
|
||||||
|
|
||||||
|
constructor(sprite: Sprite | egret.Texture) {
|
||||||
|
super();
|
||||||
|
if (sprite instanceof Sprite)
|
||||||
|
this.setSprite(sprite);
|
||||||
|
else if(sprite instanceof egret.Texture)
|
||||||
|
this.setSprite(new Sprite(sprite));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置精灵并更新精灵的原点以匹配sprite.origin
|
||||||
|
* @param sprite
|
||||||
|
*/
|
||||||
|
public setSprite(sprite: Sprite): SpriteRenderer {
|
||||||
|
this._sprite = sprite;
|
||||||
|
if (this._sprite) {
|
||||||
|
this._origin = this._sprite.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置可渲染的原点
|
||||||
|
* @param origin
|
||||||
|
*/
|
||||||
|
public setOrigin(origin: Vector2): SpriteRenderer{
|
||||||
|
if (this._origin != origin){
|
||||||
|
this._origin = origin;
|
||||||
|
this._areBoundsDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用归一化方法设置原点
|
||||||
|
* x/y 均为 0-1
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public setOriginNormalized(value: Vector2): SpriteRenderer{
|
||||||
|
this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x,
|
||||||
|
value.y * this.height / this.entity.transform.scale.y));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(camera: Camera) {
|
||||||
|
// TODO: render
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ module es {
|
|||||||
this.componentBits = new BitSet();
|
this.componentBits = new BitSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onTransformChanged(comp: Transform.Component) {
|
public onTransformChanged(comp: transform.Component) {
|
||||||
// 通知我们的子项改变了位置
|
// 通知我们的子项改变了位置
|
||||||
this.components.onEntityTransformChanged(comp);
|
this.components.onEntityTransformChanged(comp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
|
module transform {
|
||||||
|
export enum Component {
|
||||||
|
position,
|
||||||
|
scale,
|
||||||
|
rotation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module es {
|
module es {
|
||||||
|
export enum DirtyType {
|
||||||
|
clean,
|
||||||
|
positionDirty,
|
||||||
|
scaleDirty,
|
||||||
|
rotationDirty,
|
||||||
|
}
|
||||||
|
|
||||||
export class Transform {
|
export class Transform {
|
||||||
/** 与此转换关联的实体 */
|
/** 与此转换关联的实体 */
|
||||||
public readonly entity: Entity;
|
public readonly entity: Entity;
|
||||||
|
|
||||||
private _parent: Transform;
|
|
||||||
/**
|
/**
|
||||||
* 获取此转换的父转换
|
* 获取此转换的父转换
|
||||||
*/
|
*/
|
||||||
@@ -38,6 +51,9 @@ module es {
|
|||||||
* 变换在世界空间的缩放
|
* 变换在世界空间的缩放
|
||||||
*/
|
*/
|
||||||
public scale: Vector2;
|
public scale: Vector2;
|
||||||
|
|
||||||
|
public _parent: Transform;
|
||||||
|
public hierarchyDirty: DirtyType;
|
||||||
public _children: Transform[];
|
public _children: Transform[];
|
||||||
|
|
||||||
constructor(entity: Entity) {
|
constructor(entity: Entity) {
|
||||||
@@ -68,6 +84,7 @@ module es {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
|
this.setDirty(DirtyType.positionDirty);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -79,6 +96,7 @@ module es {
|
|||||||
*/
|
*/
|
||||||
public setPosition(x: number, y: number): Transform {
|
public setPosition(x: number, y: number): Transform {
|
||||||
this.position = new Vector2(x, y);
|
this.position = new Vector2(x, y);
|
||||||
|
this.setDirty(DirtyType.positionDirty);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +106,7 @@ module es {
|
|||||||
*/
|
*/
|
||||||
public setRotation(degrees: number): Transform {
|
public setRotation(degrees: number): Transform {
|
||||||
this.rotation = degrees;
|
this.rotation = degrees;
|
||||||
|
this.setDirty(DirtyType.rotationDirty);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +116,7 @@ module es {
|
|||||||
*/
|
*/
|
||||||
public setScale(scale: Vector2): Transform {
|
public setScale(scale: Vector2): Transform {
|
||||||
this.scale = scale;
|
this.scale = scale;
|
||||||
|
this.setDirty(DirtyType.scaleDirty);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +137,31 @@ module es {
|
|||||||
this.position = this.position.round();
|
this.position = this.position.round();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setDirty(dirtyFlagType: DirtyType){
|
||||||
|
if ((this.hierarchyDirty & dirtyFlagType) == 0){
|
||||||
|
this.hierarchyDirty |= dirtyFlagType;
|
||||||
|
|
||||||
|
switch (dirtyFlagType) {
|
||||||
|
case es.DirtyType.positionDirty:
|
||||||
|
this.entity.onTransformChanged(transform.Component.position);
|
||||||
|
break;
|
||||||
|
case es.DirtyType.rotationDirty:
|
||||||
|
this.entity.onTransformChanged(transform.Component.rotation);
|
||||||
|
break;
|
||||||
|
case es.DirtyType.scaleDirty:
|
||||||
|
this.entity.onTransformChanged(transform.Component.scale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._children)
|
||||||
|
this._children = [];
|
||||||
|
|
||||||
|
// 告诉子项发生了变换
|
||||||
|
for (let i = 0; i < this._children.length; i ++)
|
||||||
|
this._children[i].setDirty(dirtyFlagType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从另一个transform属性进行拷贝
|
* 从另一个transform属性进行拷贝
|
||||||
* @param transform
|
* @param transform
|
||||||
@@ -125,14 +170,10 @@ module es {
|
|||||||
this.position = transform.position;
|
this.position = transform.position;
|
||||||
this.rotation = transform.rotation;
|
this.rotation = transform.rotation;
|
||||||
this.scale = transform.scale;
|
this.scale = transform.scale;
|
||||||
|
|
||||||
|
this.setDirty(DirtyType.positionDirty);
|
||||||
|
this.setDirty(DirtyType.rotationDirty);
|
||||||
|
this.setDirty(DirtyType.scaleDirty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
module Transform {
|
|
||||||
export enum Component {
|
|
||||||
position,
|
|
||||||
scale,
|
|
||||||
rotation,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
|
///<reference path="../Components/IUpdatableComparer.ts" />
|
||||||
module es {
|
module es {
|
||||||
export class ComponentList {
|
export class ComponentList {
|
||||||
|
/**
|
||||||
|
* 组件列表的全局updateOrder排序
|
||||||
|
*/
|
||||||
|
public static compareUpdatableOrder: IUpdatableComparer = new IUpdatableComparer();
|
||||||
public _entity: Entity;
|
public _entity: Entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,6 +20,10 @@ module es {
|
|||||||
*/
|
*/
|
||||||
public _componentsToRemove: Component[] = [];
|
public _componentsToRemove: Component[] = [];
|
||||||
public _tempBufferList: Component[] = [];
|
public _tempBufferList: Component[] = [];
|
||||||
|
/**
|
||||||
|
* 用于确定是否需要对该框架中的组件进行排序的标志
|
||||||
|
*/
|
||||||
|
public _isComponentListUnsorted: boolean;
|
||||||
|
|
||||||
constructor(entity: Entity) {
|
constructor(entity: Entity) {
|
||||||
this._entity = entity;
|
this._entity = entity;
|
||||||
@@ -28,13 +37,17 @@ module es {
|
|||||||
return this._components;
|
return this._components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public markEntityListUnsorted(){
|
||||||
|
this._isComponentListUnsorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
public add(component: Component) {
|
public add(component: Component) {
|
||||||
this._componentsToAdd.push(component);
|
this._componentsToAdd.push(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove(component: Component) {
|
public remove(component: Component) {
|
||||||
if (this._componentsToRemove.contains(component))
|
if (this._componentsToRemove.contains(component))
|
||||||
console.warn(`You are trying to remove a Component (${component}) that you already removed`)
|
console.warn(`You are trying to remove a Component (${component}) that you already removed`);
|
||||||
|
|
||||||
// 这可能不是一个活动的组件,所以我们必须注意它是否还没有被处理,它可能正在同一帧中被删除
|
// 这可能不是一个活动的组件,所以我们必须注意它是否还没有被处理,它可能正在同一帧中被删除
|
||||||
if (this._componentsToAdd.contains(component)) {
|
if (this._componentsToAdd.contains(component)) {
|
||||||
@@ -111,6 +124,7 @@ module es {
|
|||||||
|
|
||||||
// 在调用onAddedToEntity之前清除,以防添加更多组件
|
// 在调用onAddedToEntity之前清除,以防添加更多组件
|
||||||
this._componentsToAdd.length = 0;
|
this._componentsToAdd.length = 0;
|
||||||
|
this._isComponentListUnsorted = true;
|
||||||
|
|
||||||
// 现在所有的组件都添加到了场景中,我们再次循环并调用onAddedToEntity/onEnabled
|
// 现在所有的组件都添加到了场景中,我们再次循环并调用onAddedToEntity/onEnabled
|
||||||
for (let i = 0; i < this._tempBufferList.length; i++) {
|
for (let i = 0; i < this._tempBufferList.length; i++) {
|
||||||
@@ -125,6 +139,11 @@ module es {
|
|||||||
|
|
||||||
this._tempBufferList.length = 0;
|
this._tempBufferList.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._isComponentListUnsorted){
|
||||||
|
this._components.sort(ComponentList.compareUpdatableOrder.compare);
|
||||||
|
this._isComponentListUnsorted = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleRemove(component: Component) {
|
public handleRemove(component: Component) {
|
||||||
@@ -216,7 +235,7 @@ module es {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onEntityTransformChanged(comp: Transform.Component) {
|
public onEntityTransformChanged(comp: transform.Component) {
|
||||||
for (let i = 0; i < this._components.length; i++) {
|
for (let i = 0; i < this._components.length; i++) {
|
||||||
if (this._components[i].enabled)
|
if (this._components[i].enabled)
|
||||||
this._components[i].onEntityTransformChanged(comp);
|
this._components[i].onEntityTransformChanged(comp);
|
||||||
|
|||||||
@@ -1,22 +1,101 @@
|
|||||||
class RenderableComponentList {
|
///<reference path="../../Graphics/Renderers/IRenderable.ts" />
|
||||||
private _components: IRenderable[] = [];
|
module es {
|
||||||
public get count(){
|
export class RenderableComponentList {
|
||||||
return this._components.length;
|
/**
|
||||||
}
|
* IRenderable列表的全局updatePrder排序
|
||||||
|
*/
|
||||||
|
public static compareUpdatableOrder = new RenderableComparer();
|
||||||
|
/**
|
||||||
|
* 添加到实体的组件列表
|
||||||
|
*/
|
||||||
|
public _components: IRenderable[] = [];
|
||||||
|
/**
|
||||||
|
* 通过渲染层跟踪组件,便于检索
|
||||||
|
*/
|
||||||
|
public _componentsByRenderLayer: Map<number, IRenderable[]> = new Map<number, IRenderable[]>();
|
||||||
|
public _unsortedRenderLayers: number[] = [];
|
||||||
|
public _componentsNeedSort: boolean = true;
|
||||||
|
|
||||||
public get buffer(){
|
public get count() {
|
||||||
return this._components;
|
return this._components.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public add(component: IRenderable){
|
public get buffer() {
|
||||||
this._components.push(component);
|
return this._components;
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove(component: IRenderable){
|
public add(component: IRenderable) {
|
||||||
this._components.remove(component);
|
this._components.push(component);
|
||||||
}
|
this.addToRenderLayerList(component, component.renderLayer);
|
||||||
|
}
|
||||||
|
|
||||||
public updateList(){
|
public remove(component: IRenderable) {
|
||||||
|
this._components.remove(component);
|
||||||
|
this._componentsByRenderLayer.get(component.renderLayer).remove(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateRenderableRenderLayer(component: IRenderable, oldRenderLayer: number, newRenderLayer: number) {
|
||||||
|
// 需要注意的是,如果渲染层在组件update之前发生了改变
|
||||||
|
if (this._componentsByRenderLayer.has(oldRenderLayer) && this._componentsByRenderLayer.get(oldRenderLayer).contains(component)) {
|
||||||
|
this._componentsByRenderLayer.get(oldRenderLayer).remove(component);
|
||||||
|
this.addToRenderLayerList(component, newRenderLayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将渲染层排序标志弄脏,让所有组件重新排序
|
||||||
|
* @param renderLayer
|
||||||
|
*/
|
||||||
|
public setRenderLayerNeedsComponentSort(renderLayer: number) {
|
||||||
|
if (!this._unsortedRenderLayers.contains(renderLayer))
|
||||||
|
this._unsortedRenderLayers.push(renderLayer);
|
||||||
|
this._componentsNeedSort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setNeedsComponentSort() {
|
||||||
|
this._componentsNeedSort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addToRenderLayerList(component: IRenderable, renderLayer: number) {
|
||||||
|
let list = this.componentsWithRenderLayer(renderLayer);
|
||||||
|
if (!list.contains(component)) {
|
||||||
|
console.warn("Component renderLayer list already contains this component");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(component);
|
||||||
|
if (!this._unsortedRenderLayers.contains(renderLayer))
|
||||||
|
this._unsortedRenderLayers.push(renderLayer);
|
||||||
|
this._componentsNeedSort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用给定的渲染层获取所有组件。组件列表是预先排序的
|
||||||
|
* @param renderLayer
|
||||||
|
*/
|
||||||
|
public componentsWithRenderLayer(renderLayer: number): IRenderable[] {
|
||||||
|
if (!this._componentsByRenderLayer.get(renderLayer)) {
|
||||||
|
this._componentsByRenderLayer.set(renderLayer, []);
|
||||||
|
}
|
||||||
|
return this._componentsByRenderLayer.get(renderLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateList() {
|
||||||
|
if (this._componentsNeedSort) {
|
||||||
|
this._components.sort(RenderableComponentList.compareUpdatableOrder.compare);
|
||||||
|
this._componentsNeedSort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._unsortedRenderLayers.length > 0) {
|
||||||
|
for (let i = 0, count = this._unsortedRenderLayers.length; i < count; i++) {
|
||||||
|
let renderLayerComponents = this._componentsByRenderLayer.get(this._unsortedRenderLayers[i]);
|
||||||
|
if (renderLayerComponents) {
|
||||||
|
renderLayerComponents.sort(RenderableComponentList.compareUpdatableOrder.compare);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._unsortedRenderLayers.length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,47 @@
|
|||||||
interface IRenderable {
|
module es {
|
||||||
bounds: Rectangle;
|
/**
|
||||||
enabled: boolean;
|
* 当该接口应用到组件时,它将注册组件以场景渲染器显示
|
||||||
isVisible: boolean;
|
* 该接口请谨慎实现
|
||||||
isVisibleFromCamera(camera: Camera);
|
*/
|
||||||
render(camera: Camera);
|
export interface IRenderable {
|
||||||
}
|
/**
|
||||||
|
* 对象的AABB用于相机剔除
|
||||||
|
*/
|
||||||
|
bounds: Rectangle;
|
||||||
|
/**
|
||||||
|
* 这个组件是否应该被渲染
|
||||||
|
*/
|
||||||
|
enabled: boolean;
|
||||||
|
/**
|
||||||
|
* 较低的渲染层在前面,较高的在后面
|
||||||
|
*/
|
||||||
|
renderLayer: number;
|
||||||
|
/**
|
||||||
|
* 可渲染的可见性。状态的改变会调用onBecameVisible/onBecameInvisible方法
|
||||||
|
*/
|
||||||
|
isVisible: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果renderableComponent的边界与camera.bounds相交 返回true
|
||||||
|
* 用于处理isVisible标志的状态开关
|
||||||
|
* 在渲染方法中使用这个方法来决定是否渲染
|
||||||
|
* @param camera
|
||||||
|
*/
|
||||||
|
isVisibleFromCamera(camera: Camera);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由渲染器调用。可以使用摄像机进行剔除
|
||||||
|
* @param camera
|
||||||
|
*/
|
||||||
|
render(camera: Camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于排序IRenderables的比较器
|
||||||
|
*/
|
||||||
|
export class RenderableComparer {
|
||||||
|
public compare(self: IRenderable, other: IRenderable){
|
||||||
|
return other.renderLayer - self.renderLayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -138,6 +138,13 @@ class Rectangle extends egret.Rectangle {
|
|||||||
return boundsPoint;
|
return boundsPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number){
|
||||||
|
this.x = parentPosition.x + position.x - origin.x * scale.x;
|
||||||
|
this.y = parentPosition.y + position.y - origin.y * scale.y;
|
||||||
|
this.width = width * scale.x;
|
||||||
|
this.height = height * scale.y;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将egret矩形转化为Rectangle
|
* 将egret矩形转化为Rectangle
|
||||||
* @param rect
|
* @param rect
|
||||||
|
|||||||
@@ -1,81 +1,88 @@
|
|||||||
///<reference path="./Polygon.ts" />
|
///<reference path="./Polygon.ts" />
|
||||||
/**
|
module es {
|
||||||
* 多边形的特殊情况。在进行SAT碰撞检查时,我们只需要检查2个轴而不是8个轴
|
|
||||||
*/
|
|
||||||
class Box extends Polygon {
|
|
||||||
constructor(width: number, height: number){
|
|
||||||
super(Box.buildBox(width, height), true);
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在一个盒子的形状中建立多边形需要的点的帮助方法
|
* 多边形的特殊情况。在进行SAT碰撞检查时,我们只需要检查2个轴而不是8个轴
|
||||||
* @param width
|
|
||||||
* @param height
|
|
||||||
*/
|
*/
|
||||||
private static buildBox(width: number, height: number): Vector2[]{
|
export class Box extends Polygon {
|
||||||
// 我们在(0,0)的中心周围创建点
|
public width: number;
|
||||||
let halfWidth = width / 2;
|
public height: number;
|
||||||
let halfHeight = height / 2;
|
|
||||||
let verts = new Array(4);
|
|
||||||
verts[0] = new Vector2(-halfWidth, -halfHeight);
|
|
||||||
verts[1] = new Vector2(halfWidth, -halfHeight);
|
|
||||||
verts[2] = new Vector2(halfWidth, halfHeight);
|
|
||||||
verts[3] = new Vector2(-halfWidth, halfHeight);
|
|
||||||
|
|
||||||
return verts;
|
constructor(width: number, height: number){
|
||||||
}
|
super(Box.buildBox(width, height), true);
|
||||||
|
this.width = width;
|
||||||
public overlaps(other: Shape){
|
this.height = height;
|
||||||
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
|
||||||
if (other instanceof Box)
|
|
||||||
return this.bounds.intersects(other.bounds);
|
|
||||||
|
|
||||||
if (other instanceof Circle)
|
|
||||||
return Collisions.isRectToCircle(this.bounds, other.position, other.radius);
|
|
||||||
|
|
||||||
return super.overlaps(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public collidesWithShape(other: Shape){
|
|
||||||
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
|
||||||
if (other instanceof Box){
|
|
||||||
return ShapeCollisions.boxToBox(this, other);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 让 minkowski 运行于 cricleToBox
|
/**
|
||||||
|
* 在一个盒子的形状中建立多边形需要的点的帮助方法
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
*/
|
||||||
|
private static buildBox(width: number, height: number): Vector2[]{
|
||||||
|
// 我们在(0,0)的中心周围创建点
|
||||||
|
let halfWidth = width / 2;
|
||||||
|
let halfHeight = height / 2;
|
||||||
|
let verts = new Array(4);
|
||||||
|
verts[0] = new Vector2(-halfWidth, -halfHeight);
|
||||||
|
verts[1] = new Vector2(halfWidth, -halfHeight);
|
||||||
|
verts[2] = new Vector2(halfWidth, halfHeight);
|
||||||
|
verts[3] = new Vector2(-halfWidth, halfHeight);
|
||||||
|
|
||||||
return super.collidesWithShape(other);
|
return verts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新框点,重新计算中心,设置宽度/高度
|
* 更新框点,重新计算中心,设置宽度/高度
|
||||||
* @param width
|
* @param width
|
||||||
* @param height
|
* @param height
|
||||||
*/
|
*/
|
||||||
public updateBox(width: number, height: number){
|
public updateBox(width: number, height: number){
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
|
||||||
// 我们在(0,0)的中心周围创建点
|
// 我们在(0,0)的中心周围创建点
|
||||||
let halfWidth = width / 2;
|
let halfWidth = width / 2;
|
||||||
let halfHeight = height / 2;
|
let halfHeight = height / 2;
|
||||||
|
|
||||||
this.points[0] = new Vector2(-halfWidth, -halfHeight);
|
this.points[0] = new Vector2(-halfWidth, -halfHeight);
|
||||||
this.points[1] = new Vector2(halfWidth, -halfHeight);
|
this.points[1] = new Vector2(halfWidth, -halfHeight);
|
||||||
this.points[2] = new Vector2(halfWidth, halfHeight);
|
this.points[2] = new Vector2(halfWidth, halfHeight);
|
||||||
this.points[3] = new Vector2(-halfWidth, halfHeight);
|
this.points[3] = new Vector2(-halfWidth, halfHeight);
|
||||||
|
|
||||||
for (let i = 0; i < this.points.length; i ++)
|
for (let i = 0; i < this.points.length; i ++)
|
||||||
this._originalPoints[i] = this.points[i];
|
this._originalPoints[i] = this.points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public overlaps(other: Shape){
|
||||||
*
|
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
||||||
* @param point
|
if (other instanceof Box)
|
||||||
*/
|
return this.bounds.intersects(other.bounds);
|
||||||
public containsPoint(point: Vector2){
|
|
||||||
return this.bounds.contains(point.x, point.y);
|
if (other instanceof Circle)
|
||||||
|
return Collisions.isRectToCircle(this.bounds, other.position, other.radius);
|
||||||
|
|
||||||
|
return super.overlaps(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collidesWithShape(other: Shape){
|
||||||
|
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
||||||
|
if (other instanceof Box){
|
||||||
|
return ShapeCollisions.boxToBox(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 让 minkowski 运行于 cricleToBox
|
||||||
|
|
||||||
|
return super.collidesWithShape(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param point
|
||||||
|
*/
|
||||||
|
public containsPoint(point: Vector2){
|
||||||
|
return this.bounds.contains(point.x, point.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,215 +1,250 @@
|
|||||||
///<reference path="./Shape.ts" />
|
///<reference path="./Shape.ts" />
|
||||||
/**
|
module es {
|
||||||
* 多边形
|
|
||||||
*/
|
|
||||||
class Polygon extends Shape {
|
|
||||||
/** 组成多边形的点。它们应该是CW和凸的。 */
|
|
||||||
public points: Vector2[];
|
|
||||||
private _polygonCenter: Vector2;
|
|
||||||
private _areEdgeNormalsDirty = true;
|
|
||||||
protected _originalPoints: Vector2[];
|
|
||||||
public center = new Vector2();
|
|
||||||
/**
|
|
||||||
* 多边形坐标
|
|
||||||
* 此为内部字段 可访问
|
|
||||||
*/
|
|
||||||
public position: Vector2 = Vector2.zero;
|
|
||||||
|
|
||||||
public get bounds(){
|
|
||||||
return new Rectangle(this.position.x, this.position.y, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public _edgeNormals: Vector2[];
|
|
||||||
public get edgeNormals(){
|
|
||||||
if (this._areEdgeNormalsDirty)
|
|
||||||
this.buildEdgeNormals();
|
|
||||||
return this._edgeNormals;
|
|
||||||
}
|
|
||||||
public isBox: boolean;
|
|
||||||
|
|
||||||
constructor(points: Vector2[], isBox?: boolean){
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.setPoints(points);
|
|
||||||
this.isBox = isBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildEdgeNormals(){
|
|
||||||
let totalEdges = this.isBox ? 2 : this.points.length;
|
|
||||||
if (this._edgeNormals == null || this._edgeNormals.length != totalEdges)
|
|
||||||
this._edgeNormals = new Array(totalEdges);
|
|
||||||
|
|
||||||
let p2: Vector2;
|
|
||||||
for (let i = 0; i < totalEdges; i ++){
|
|
||||||
let p1 = this.points[i];
|
|
||||||
if (i + 1 >= this.points.length)
|
|
||||||
p2 = this.points[0];
|
|
||||||
else
|
|
||||||
p2 = this.points[i + 1];
|
|
||||||
|
|
||||||
let perp = Vector2Ext.perpendicular(p1, p2);
|
|
||||||
perp = Vector2.normalize(perp);
|
|
||||||
this._edgeNormals[i] = perp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public setPoints(points: Vector2[]) {
|
|
||||||
this.points = points;
|
|
||||||
this.recalculateCenterAndEdgeNormals();
|
|
||||||
|
|
||||||
this._originalPoints = [];
|
|
||||||
for (let i = 0; i < this.points.length; i ++){
|
|
||||||
this._originalPoints.push(this.points[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public collidesWithShape(other: Shape){
|
|
||||||
let result = new CollisionResult();
|
|
||||||
if (other instanceof Polygon){
|
|
||||||
return ShapeCollisions.polygonToPolygon(this, other);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other instanceof Circle){
|
|
||||||
result = ShapeCollisions.circleToPolygon(other, this);
|
|
||||||
if (result){
|
|
||||||
result.invertResult();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`overlaps of Polygon to ${other} are not supported`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public recalculateCenterAndEdgeNormals() {
|
|
||||||
this._polygonCenter = Polygon.findPolygonCenter(this.points);
|
|
||||||
this._areEdgeNormalsDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public overlaps(other: Shape){
|
|
||||||
let result: CollisionResult;
|
|
||||||
if (other instanceof Polygon)
|
|
||||||
return ShapeCollisions.polygonToPolygon(this, other);
|
|
||||||
|
|
||||||
if (other instanceof Circle){
|
|
||||||
result = ShapeCollisions.circleToPolygon(other, this);
|
|
||||||
if (result){
|
|
||||||
result.invertResult();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`overlaps of Pologon to ${other} are not supported`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 找到多边形的中心。注意,这对于正则多边形是准确的。不规则多边形没有中心。
|
* 多边形
|
||||||
* @param points
|
|
||||||
*/
|
*/
|
||||||
public static findPolygonCenter(points: Vector2[]) {
|
export class Polygon extends Shape {
|
||||||
let x = 0, y = 0;
|
/**
|
||||||
|
* 组成多边形的点
|
||||||
|
* 保持顺时针与凸边形
|
||||||
|
*/
|
||||||
|
public points: Vector2[];
|
||||||
|
|
||||||
for (let i = 0; i < points.length; i++) {
|
/**
|
||||||
x += points[i].x;
|
* 边缘法线用于SAT碰撞检测。缓存它们用于避免squareRoots
|
||||||
y += points[i].y;
|
* box只有两个边缘 因为其他两边是平行的
|
||||||
|
*/
|
||||||
|
public get edgeNormals(){
|
||||||
|
if (this._areEdgeNormalsDirty)
|
||||||
|
this.buildEdgeNormals();
|
||||||
|
return this._edgeNormals;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Vector2(x / points.length, y / points.length);
|
public _areEdgeNormalsDirty = true;
|
||||||
}
|
public _edgeNormals: Vector2[];
|
||||||
|
/**
|
||||||
|
* 多边形的原始数据
|
||||||
|
*/
|
||||||
|
public _originalPoints: Vector2[];
|
||||||
|
public _polygonCenter: Vector2;
|
||||||
|
/**
|
||||||
|
* 用于优化未旋转box碰撞
|
||||||
|
*/
|
||||||
|
public isBox: boolean;
|
||||||
|
public isUnrotated: boolean = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重定位多边形的点
|
* 从点构造一个多边形
|
||||||
* @param points
|
* 多边形应该以顺时针方式指定 不能重复第一个/最后一个点,它们以0 0为中心
|
||||||
*/
|
* @param points
|
||||||
public static recenterPolygonVerts(points: Vector2[]){
|
* @param isBox
|
||||||
let center = this.findPolygonCenter(points);
|
*/
|
||||||
for (let i = 0; i < points.length; i ++)
|
constructor(points: Vector2[], isBox?: boolean){
|
||||||
points[i] = Vector2.subtract(points[i], center);
|
super();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
this.setPoints(points);
|
||||||
* 迭代多边形的所有边,并得到任意边上离点最近的点。
|
this.isBox = isBox;
|
||||||
* 通过最近点的平方距离和它所在的边的法线返回。
|
}
|
||||||
* 点应该在多边形的空间中(点-多边形.位置)
|
|
||||||
* @param points
|
|
||||||
* @param point
|
|
||||||
*/
|
|
||||||
public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { closestPoint, distanceSquared, edgeNormal } {
|
|
||||||
let distanceSquared = Number.MAX_VALUE;
|
|
||||||
let edgeNormal = new Vector2(0, 0);
|
|
||||||
let closestPoint = new Vector2(0, 0);
|
|
||||||
|
|
||||||
let tempDistanceSquared;
|
/**
|
||||||
for (let i = 0; i < points.length; i++) {
|
* 重置点并重新计算中心和边缘法线
|
||||||
let j = i + 1;
|
* @param points
|
||||||
if (j == points.length)
|
*/
|
||||||
j = 0;
|
public setPoints(points: Vector2[]) {
|
||||||
|
this.points = points;
|
||||||
|
this.recalculateCenterAndEdgeNormals();
|
||||||
|
|
||||||
let closest = ShapeCollisions.closestPointOnLine(points[i], points[j], point);
|
this._originalPoints = [];
|
||||||
tempDistanceSquared = Vector2.distanceSquared(point, closest);
|
for (let i = 0; i < this.points.length; i ++){
|
||||||
|
this._originalPoints.push(this.points[i]);
|
||||||
if (tempDistanceSquared < distanceSquared) {
|
|
||||||
distanceSquared = tempDistanceSquared;
|
|
||||||
closestPoint = closest;
|
|
||||||
|
|
||||||
// 求直线的法线
|
|
||||||
let line = Vector2.subtract(points[j], points[i]);
|
|
||||||
edgeNormal.x = -line.y;
|
|
||||||
edgeNormal.y = line.x;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeNormal = Vector2.normalize(edgeNormal);
|
/**
|
||||||
|
* 重新计算多边形中心
|
||||||
|
* 如果点数改变必须调用该方法
|
||||||
|
*/
|
||||||
|
public recalculateCenterAndEdgeNormals() {
|
||||||
|
this._polygonCenter = Polygon.findPolygonCenter(this.points);
|
||||||
|
this._areEdgeNormalsDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
return { closestPoint: closestPoint, distanceSquared: distanceSquared, edgeNormal: edgeNormal };
|
/**
|
||||||
}
|
* 建立多边形边缘法线
|
||||||
|
* 它们仅由edgeNormals getter惰性创建和更新
|
||||||
|
*/
|
||||||
|
public buildEdgeNormals(){
|
||||||
|
// 对于box 我们只需要两条边,因为另外两条边是平行的
|
||||||
|
let totalEdges = this.isBox ? 2 : this.points.length;
|
||||||
|
if (this._edgeNormals == null || this._edgeNormals.length != totalEdges)
|
||||||
|
this._edgeNormals = new Array(totalEdges);
|
||||||
|
|
||||||
public recalculateBounds(collider: Collider){
|
let p2: Vector2;
|
||||||
// 如果我们没有旋转或不关心TRS我们使用localOffset作为中心,我们会从那开始
|
for (let i = 0; i < totalEdges; i ++){
|
||||||
|
let p1 = this.points[i];
|
||||||
}
|
if (i + 1 >= this.points.length)
|
||||||
|
p2 = this.points[0];
|
||||||
|
else
|
||||||
|
p2 = this.points[i + 1];
|
||||||
|
|
||||||
public pointCollidesWithShape(point: Vector2): CollisionResult {
|
let perp = Vector2Ext.perpendicular(p1, p2);
|
||||||
return ShapeCollisions.pointToPoly(point, this);
|
perp = Vector2.normalize(perp);
|
||||||
}
|
this._edgeNormals[i] = perp;
|
||||||
|
|
||||||
/**
|
|
||||||
* 本质上,这个算法所做的就是从一个点发射一条射线。
|
|
||||||
* 如果它与奇数条多边形边相交,我们就知道它在多边形内部。
|
|
||||||
* @param point
|
|
||||||
*/
|
|
||||||
public containsPoint(point: Vector2) {
|
|
||||||
// 将点归一化到多边形坐标空间中
|
|
||||||
point = Vector2.subtract(point, this.position);
|
|
||||||
|
|
||||||
let isInside = false;
|
|
||||||
for (let i = 0, j = this.points.length - 1; i < this.points.length; j = i++) {
|
|
||||||
if (((this.points[i].y > point.y) != (this.points[j].y > point.y)) &&
|
|
||||||
(point.x < (this.points[j].x - this.points[i].x) * (point.y - this.points[i].y) / (this.points[j].y - this.points[i].y) +
|
|
||||||
this.points[i].x)) {
|
|
||||||
isInside = !isInside;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isInside;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 建立一个对称的多边形(六边形,八角形,n角形)并返回点
|
* 建立一个对称的多边形(六边形,八角形,n角形)并返回点
|
||||||
* @param vertCount
|
* @param vertCount
|
||||||
* @param radius
|
* @param radius
|
||||||
*/
|
*/
|
||||||
public static buildSymmertricalPolygon(vertCount: number, radius: number) {
|
public static buildSymmetricalPolygon(vertCount: number, radius: number) {
|
||||||
let verts = new Array(vertCount);
|
let verts = new Array(vertCount);
|
||||||
|
|
||||||
for (let i = 0; i < vertCount; i++) {
|
for (let i = 0; i < vertCount; i++) {
|
||||||
let a = 2 * Math.PI * (i / vertCount);
|
let a = 2 * Math.PI * (i / vertCount);
|
||||||
verts[i] = new Vector2(Math.cos(a), Math.sin(a) * radius);
|
verts[i] = new Vector2(Math.cos(a), Math.sin(a) * radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
return verts;
|
||||||
}
|
}
|
||||||
|
|
||||||
return verts;
|
/**
|
||||||
|
* 重定位多边形的点
|
||||||
|
* @param points
|
||||||
|
*/
|
||||||
|
public static recenterPolygonVerts(points: Vector2[]){
|
||||||
|
let center = this.findPolygonCenter(points);
|
||||||
|
for (let i = 0; i < points.length; i ++)
|
||||||
|
points[i] = Vector2.subtract(points[i], center);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到多边形的中心。注意,这对于正则多边形是准确的。不规则多边形没有中心。
|
||||||
|
* @param points
|
||||||
|
*/
|
||||||
|
public static findPolygonCenter(points: Vector2[]) {
|
||||||
|
let x = 0, y = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
x += points[i].x;
|
||||||
|
y += points[i].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Vector2(x / points.length, y / points.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 迭代多边形的所有边,并得到任意边上离点最近的点。
|
||||||
|
* 通过最近点的平方距离和它所在的边的法线返回。
|
||||||
|
* 点应该在多边形的空间中(点-多边形.位置)
|
||||||
|
* @param points
|
||||||
|
* @param point
|
||||||
|
*/
|
||||||
|
public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { closestPoint, distanceSquared, edgeNormal } {
|
||||||
|
let distanceSquared = Number.MAX_VALUE;
|
||||||
|
let edgeNormal = new Vector2(0, 0);
|
||||||
|
let closestPoint = new Vector2(0, 0);
|
||||||
|
|
||||||
|
let tempDistanceSquared;
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
let j = i + 1;
|
||||||
|
if (j == points.length)
|
||||||
|
j = 0;
|
||||||
|
|
||||||
|
let closest = ShapeCollisions.closestPointOnLine(points[i], points[j], point);
|
||||||
|
tempDistanceSquared = Vector2.distanceSquared(point, closest);
|
||||||
|
|
||||||
|
if (tempDistanceSquared < distanceSquared) {
|
||||||
|
distanceSquared = tempDistanceSquared;
|
||||||
|
closestPoint = closest;
|
||||||
|
|
||||||
|
// 求直线的法线
|
||||||
|
let line = Vector2.subtract(points[j], points[i]);
|
||||||
|
edgeNormal.x = -line.y;
|
||||||
|
edgeNormal.y = line.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeNormal = Vector2.normalize(edgeNormal);
|
||||||
|
|
||||||
|
return { closestPoint: closestPoint, distanceSquared: distanceSquared, edgeNormal: edgeNormal };
|
||||||
|
}
|
||||||
|
|
||||||
|
public recalculateBounds(collider: Collider){
|
||||||
|
// 如果我们没有旋转或不关心TRS我们使用localOffset作为中心,我们会从那开始
|
||||||
|
this.center = collider.localOffset;
|
||||||
|
|
||||||
|
if (collider.shouldColliderScaleAndRotateWithTransform){
|
||||||
|
this.isUnrotated = collider.entity.transform.rotation == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.position = Vector2.add(collider.entity.transform.position, this.center);
|
||||||
|
this.bounds = Rectangle.rectEncompassingPoints(this.points);
|
||||||
|
this.bounds.location = Vector2.add(this.bounds.location, this.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public overlaps(other: Shape){
|
||||||
|
let result: CollisionResult;
|
||||||
|
if (other instanceof Polygon)
|
||||||
|
return ShapeCollisions.polygonToPolygon(this, other);
|
||||||
|
|
||||||
|
if (other instanceof Circle){
|
||||||
|
result = ShapeCollisions.circleToPolygon(other, this);
|
||||||
|
if (result){
|
||||||
|
result.invertResult();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`overlaps of Pologon to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collidesWithShape(other: Shape){
|
||||||
|
let result = new CollisionResult();
|
||||||
|
if (other instanceof Polygon){
|
||||||
|
return ShapeCollisions.polygonToPolygon(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other instanceof Circle){
|
||||||
|
result = ShapeCollisions.circleToPolygon(other, this);
|
||||||
|
if (result){
|
||||||
|
result.invertResult();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`overlaps of Polygon to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本质上,这个算法所做的就是从一个点发射一条射线。
|
||||||
|
* 如果它与奇数条多边形边相交,我们就知道它在多边形内部。
|
||||||
|
* @param point
|
||||||
|
*/
|
||||||
|
public containsPoint(point: Vector2) {
|
||||||
|
// 将点归一化到多边形坐标空间中
|
||||||
|
point = Vector2.subtract(point, this.position);
|
||||||
|
|
||||||
|
let isInside = false;
|
||||||
|
for (let i = 0, j = this.points.length - 1; i < this.points.length; j = i++) {
|
||||||
|
if (((this.points[i].y > point.y) != (this.points[j].y > point.y)) &&
|
||||||
|
(point.x < (this.points[j].x - this.points[i].x) * (point.y - this.points[i].y) / (this.points[j].y - this.points[i].y) +
|
||||||
|
this.points[i].x)) {
|
||||||
|
isInside = !isInside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isInside;
|
||||||
|
}
|
||||||
|
|
||||||
|
public pointCollidesWithShape(point: Vector2): CollisionResult {
|
||||||
|
return ShapeCollisions.pointToPoly(point, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
abstract class Shape {
|
module es {
|
||||||
/** 缓存的形状边界 内部字段 */
|
export abstract class Shape {
|
||||||
public bounds: Rectangle;
|
/**
|
||||||
/**
|
* 有一个单独的位置字段可以让我们改变形状的位置来进行碰撞检查,而不是改变entity.position。
|
||||||
* 这不是中心。这个值不一定是物体的中心。对撞机更准确。
|
* 触发碰撞器/边界/散列更新的位置。
|
||||||
* 应用任何转换旋转的localOffset
|
* 内部字段
|
||||||
* 内部字段
|
*/
|
||||||
*/
|
public position: Vector2;
|
||||||
public center: Vector2;
|
/**
|
||||||
/**
|
* 这不是中心。这个值不一定是物体的中心。对撞机更准确。
|
||||||
* 有一个单独的位置字段可以让我们改变形状的位置来进行碰撞检查,而不是改变entity.position。
|
* 应用任何转换旋转的localOffset
|
||||||
* 触发碰撞器/边界/散列更新的位置。
|
* 内部字段
|
||||||
* 内部字段
|
*/
|
||||||
*/
|
public center: Vector2;
|
||||||
public position: Vector2;
|
/** 缓存的形状边界 内部字段 */
|
||||||
|
public bounds: Rectangle;
|
||||||
|
|
||||||
public abstract recalculateBounds(collider: Collider);
|
public abstract recalculateBounds(collider: Collider);
|
||||||
public abstract pointCollidesWithShape(point: Vector2): CollisionResult;
|
public abstract pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||||
public abstract overlaps(other: Shape);
|
public abstract overlaps(other: Shape);
|
||||||
public abstract collidesWithShape(other: Shape): CollisionResult;
|
public abstract collidesWithShape(other: Shape): CollisionResult;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,4 +43,17 @@ class DrawUtils {
|
|||||||
shape.graphics.drawRect(destRect.x, destRect.y, destRect.width, destRect.height);
|
shape.graphics.drawRect(destRect.x, destRect.y, destRect.width, destRect.height);
|
||||||
shape.graphics.endFill();
|
shape.graphics.endFill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getColorMatrix(color: number): egret.ColorMatrixFilter{
|
||||||
|
let colorMatrix = [
|
||||||
|
1, 0, 0, 0, 0,
|
||||||
|
0, 1, 0, 0, 0,
|
||||||
|
0, 0, 1, 0, 0,
|
||||||
|
0, 0, 0, 1, 0
|
||||||
|
];
|
||||||
|
colorMatrix[0] = Math.floor(color / 256 / 256) / 255;
|
||||||
|
colorMatrix[6] = Math.floor(color / 256 % 256) / 255;
|
||||||
|
colorMatrix[12] = color % 256 / 255;
|
||||||
|
return new egret.ColorMatrixFilter(colorMatrix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user