module es { 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 则会进行缓动移动 * 该值为移动速度 */ 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; } } public get position(){ return this._position; } public set position(value: Vector2){ 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; } constructor() { super(); this.width = SceneManager.stage.stageWidth; this.height = SceneManager.stage.stageHeight; this.setZoom(0); } public onSceneSizeChanged(newWidth: number, newHeight: number){ let oldOrigin = this._origin; this.origin = new Vector2(newWidth / 2, newHeight / 2); this.entity.position = Vector2.add(this.entity.position, Vector2.subtract(this._origin, oldOrigin)); } 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; } 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); } SceneManager.scene.scaleX = this._zoom; SceneManager.scene.scaleY = this._zoom; return this; } public setRotation(rotation: number): Camera { SceneManager.scene.rotation = rotation; return this; } public setPosition(position: Vector2){ this.entity.position = position; return this; } 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; } } 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 * 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.width = this.deadzone.width; this._worldSpaceDeadZone.height = this.deadzone.height; if (this.targetEntity) this.updateFollow(); this.position = Vector2.lerp(this.position, Vector2.add(this.position, this._desiredPositionDelta), this.followLerp); this.entity.roundPosition(); if (this.mapLockEnabled){ this.position = this.clampToMapSize(this.position); this.entity.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)); let cameraMax = new Vector2(this.mapSize.x - halfScreen.x, this.mapSize.y - halfScreen.y); return Vector2.clamp(position, halfScreen, cameraMax); } private updateFollow(){ this._desiredPositionDelta.x = this._desiredPositionDelta.y = 0; if (this.cameraStyle == CameraStyle.lockOn){ let targetX = this.targetEntity.position.x; let targetY = this.targetEntity.position.y; 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; 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); if (!this._targetCollider) return; } let targetBounds = this.targetEntity.getComponent(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; } } } } export enum CameraStyle { lockOn, cameraWindow, } }