2020-06-08 16:23:48 +08:00
|
|
|
///<reference path="../Component.ts"/>
|
2020-07-10 11:24:42 +08:00
|
|
|
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
|
|
|
|
2020-06-09 11:09:26 +08:00
|
|
|
private _minimumZoom = 0.3;
|
|
|
|
|
private _maximumZoom = 3;
|
2020-06-10 17:41:53 +08:00
|
|
|
|
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;
|
2020-06-09 11:09:26 +08:00
|
|
|
|
|
|
|
|
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-06-09 11:09:26 +08:00
|
|
|
|
2020-07-01 14:19:40 +08:00
|
|
|
this.width = SceneManager.stage.stageWidth;
|
|
|
|
|
this.height = SceneManager.stage.stageHeight;
|
2020-06-09 11:09:26 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2020-06-09 11:09:26 +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{
|
2020-06-09 11:09:26 +08:00
|
|
|
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-06-09 11:09:26 +08:00
|
|
|
|
2020-07-01 14:19:40 +08:00
|
|
|
public setRotation(rotation: number): Camera {
|
|
|
|
|
SceneManager.scene.rotation = rotation;
|
2020-06-09 11:09:26 +08:00
|
|
|
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-06-09 11:09:26 +08:00
|
|
|
|
2020-07-01 14:19:40 +08:00
|
|
|
if (this.targetEntity)
|
|
|
|
|
this.updateFollow();
|
2020-06-09 11:09:26 +08:00
|
|
|
|
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-06-09 11:09:26 +08:00
|
|
|
|
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-06-09 11:09:26 +08:00
|
|
|
|
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-06-10 17:41:53 +08:00
|
|
|
|
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-06-10 17:41:53 +08:00
|
|
|
|
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-06-10 17:41:53 +08:00
|
|
|
}
|
|
|
|
|
|
2020-07-01 14:19:40 +08:00
|
|
|
enum CameraStyle {
|
|
|
|
|
lockOn,
|
|
|
|
|
cameraWindow,
|
2020-06-08 11:49:45 +08:00
|
|
|
}
|