Files
esengine/source/src/ECS/Components/Camera.ts

235 lines
8.2 KiB
TypeScript
Raw Normal View History

2020-06-08 16:23:48 +08:00
///<reference path="../Component.ts"/>
class Camera extends Component implements IUpdatable {
2020-06-08 16:23:48 +08:00
private _zoom;
2020-06-18 23:22:54 +08:00
private _origin: Vector2 = Vector2.zero;
2020-06-08 11:49:45 +08:00
private _minimumZoom = 0.3;
private _maximumZoom = 3;
2020-07-08 18:12:17 +08:00
private _position: Vector2 = Vector2.zero;
2020-07-01 14:19:40 +08:00
/**
* cameraWindow
*
*/
public followLerp = 0.1;
public deadzone: Rectangle = new Rectangle();
/** 锁定偏移量 默认中心 */
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(){
if (this._zoom == 0)
return 1;
if (this._zoom < 1)
return MathHelper.map(this._zoom, this._minimumZoom, 1, -1, 0);
return MathHelper.map(this._zoom, 1, this._maximumZoom, 0, 1);
}
public set zoom(value: number){
this.setZoom(value);
}
public get minimumZoom(){
return this._minimumZoom;
}
public set minimumZoom(value: number){
this.setMinimumZoom(value);
}
public get maximumZoom(){
return this._maximumZoom;
}
public set maximumZoom(value: number){
this.setMaximumZoom(value);
}
public get origin(){
return this._origin;
}
public set origin(value: Vector2){
if (this._origin != value){
this._origin = value;
}
}
2020-06-19 09:16:49 +08:00
public get position(){
2020-07-08 18:12:17 +08:00
return this._position;
2020-06-19 09:16:49 +08:00
}
public set position(value: Vector2){
2020-07-08 18:12:17 +08:00
this._position = value;
}
public get x(){
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;
2020-06-19 09:16:49 +08:00
}
2020-06-08 16:23:48 +08:00
constructor() {
super();
2020-07-01 14:19:40 +08:00
this.width = SceneManager.stage.stageWidth;
this.height = SceneManager.stage.stageHeight;
this.setZoom(0);
}
2020-06-18 23:22:54 +08:00
public onSceneSizeChanged(newWidth: number, newHeight: number){
let oldOrigin = this._origin;
this.origin = new Vector2(newWidth / 2, newHeight / 2);
2020-06-29 15:41:02 +08:00
this.entity.position = Vector2.add(this.entity.position, Vector2.subtract(this._origin, oldOrigin));
2020-06-18 23:22:54 +08:00
}
public setMinimumZoom(minZoom: number): Camera{
if (this._zoom < minZoom)
this._zoom = this.minimumZoom;
this._minimumZoom = minZoom;
return this;
}
public setMaximumZoom(maxZoom: number): Camera {
if (this._zoom > maxZoom)
this._zoom = maxZoom;
this._maximumZoom = maxZoom;
return this;
}
2020-07-01 14:19:40 +08:00
public setZoom(zoom: number): Camera{
let newZoom = MathHelper.clamp(zoom, -1, 1);
if (newZoom == 0){
this._zoom = 1;
} else if(newZoom < 0){
this._zoom = MathHelper.map(newZoom, -1, 0, this._minimumZoom, 1);
} else {
this._zoom = MathHelper.map(newZoom, 0, 1, 1, this._maximumZoom);
}
2020-07-01 14:19:40 +08:00
SceneManager.scene.scaleX = this._zoom;
SceneManager.scene.scaleY = this._zoom;
return this;
}
2020-07-01 14:19:40 +08:00
public setRotation(rotation: number): Camera {
SceneManager.scene.rotation = rotation;
return this;
2020-06-08 16:23:48 +08:00
}
public setPosition(position: Vector2){
2020-06-29 15:41:02 +08:00
this.entity.position = position;
2020-06-08 16:23:48 +08:00
return this;
2020-06-08 11:49:45 +08:00
}
2020-07-01 14:19:40 +08:00
public follow(targetEntity: Entity, cameraStyle: CameraStyle = CameraStyle.cameraWindow){
this.targetEntity = targetEntity;
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;
}
2020-06-19 00:38:37 +08:00
}
2020-07-01 14:19:40 +08:00
public update(){
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));
this._worldSpaceDeadZone.x = this.position.x - halfScreen.x + this.deadzone.x + this.focusOffset.x;
this._worldSpaceDeadZone.y = this.position.y - halfScreen.y + this.deadzone.y + this.focusOffset.y;
this._worldSpaceDeadZone.width = this.deadzone.width;
this._worldSpaceDeadZone.height = this.deadzone.height;
2020-07-01 14:19:40 +08:00
if (this.targetEntity)
this.updateFollow();
2020-07-01 14:19:40 +08:00
this.position = Vector2.lerp(this.position, Vector2.add(this.position, this._desiredPositionDelta), this.followLerp);
this.entity.roundPosition();
2020-06-19 00:38:37 +08:00
2020-07-01 14:19:40 +08:00
if (this.mapLockEnabled){
this.position = this.clampToMapSize(this.position);
this.entity.roundPosition();
}
}
2020-07-01 14:19:40 +08:00
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));
let cameraMax = new Vector2(this.mapSize.x - halfScreen.x, this.mapSize.y - halfScreen.y);
2020-07-01 14:19:40 +08:00
return Vector2.clamp(position, halfScreen, cameraMax);
2020-06-08 16:23:48 +08:00
}
2020-07-01 14:19:40 +08:00
private updateFollow(){
this._desiredPositionDelta.x = this._desiredPositionDelta.y = 0;
2020-07-01 14:19:40 +08:00
if (this.cameraStyle == CameraStyle.lockOn){
let targetX = this.targetEntity.position.x;
let targetY = this.targetEntity.position.y;
2020-06-19 00:38:37 +08:00
2020-07-01 14:19:40 +08:00
if (this._worldSpaceDeadZone.x > targetX)
this._desiredPositionDelta.x = targetX - this._worldSpaceDeadZone.x;
else if(this._worldSpaceDeadZone.x < targetX)
this._desiredPositionDelta.x = targetX - this._worldSpaceDeadZone.x;
2020-07-01 14:19:40 +08:00
if (this._worldSpaceDeadZone.y < targetY)
this._desiredPositionDelta.y = targetY - this._worldSpaceDeadZone.y;
else if(this._worldSpaceDeadZone.y > targetY)
this._desiredPositionDelta.y = targetY - this._worldSpaceDeadZone.y;
} else {
if (!this._targetCollider){
this._targetCollider = this.targetEntity.getComponent<Collider>(Collider);
if (!this._targetCollider)
return;
}
2020-06-08 16:23:48 +08:00
2020-07-01 14:19:40 +08:00
let targetBounds = this.targetEntity.getComponent<Collider>(Collider).bounds;
if (!this._worldSpaceDeadZone.containsRect(targetBounds)){
if (this._worldSpaceDeadZone.left > targetBounds.left)
this._desiredPositionDelta.x = targetBounds.left - this._worldSpaceDeadZone.left;
else if(this._worldSpaceDeadZone.right < targetBounds.right)
this._desiredPositionDelta.x = targetBounds.right - this._worldSpaceDeadZone.right;
if (this._worldSpaceDeadZone.bottom < targetBounds.bottom)
this._desiredPositionDelta.y = targetBounds.bottom - this._worldSpaceDeadZone.bottom;
else if(this._worldSpaceDeadZone.top > targetBounds.top)
this._desiredPositionDelta.y = targetBounds.top - this._worldSpaceDeadZone.top;
}
}
2020-06-08 11:49:45 +08:00
}
}
2020-07-01 14:19:40 +08:00
enum CameraStyle {
lockOn,
cameraWindow,
2020-06-08 11:49:45 +08:00
}