合并camera

This commit is contained in:
yhh
2020-07-01 14:19:40 +08:00
parent 124cf3f66c
commit d9840d60ef
17 changed files with 422 additions and 835 deletions

View File

@@ -25,20 +25,6 @@ abstract class Component extends egret.DisplayObjectContainer {
return this;
}
public get stage(){
if (!this.entity)
return null;
return this.entity.stage;
}
public get scene(){
if (!this.entity)
return null;
return this.entity.scene;
}
public initialize(){
}

View File

@@ -2,48 +2,29 @@
class Camera extends Component {
private _zoom;
private _origin: Vector2 = Vector2.zero;
private _transformMatrix: Matrix2D = new Matrix2D();
private _inverseTransformMatrix = new Matrix2D();
private _minimumZoom = 0.3;
private _maximumZoom = 3;
private _areMatrixesDirty = true;
private _inset: CameraInset = new CameraInset();
private _bounds: Rectangle = new Rectangle();
private _areBoundsDirty = true;
public get bounds(){
if (this._areMatrixesDirty)
this.updateMatrixes();
if (this._areBoundsDirty){
let stage = this.stage;
let topLeft = this.screenToWorldPoint(new Vector2(this._inset.left, this._inset.top));
let bottomRight = this.screenToWorldPoint(new Vector2(stage.stageWidth - this._inset.right, stage.stageHeight - this._inset.bottom));
if (this.entity.rotation != 0){
let topRight = this.screenToWorldPoint(new Vector2(stage.stageWidth - this._inset.right, this._inset.top));
let bottomLeft = this.screenToWorldPoint(new Vector2(this._inset.left, stage.stageHeight - this._inset.bottom));
let minX = Math.min(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x);
let maxX = Math.max(topLeft.x, bottomRight.x, topRight.x, bottomLeft.x);
let minY = Math.min(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y);
let maxY = Math.max(topLeft.y, bottomRight.y, topRight.y, bottomLeft.y);
this._bounds.location = new Vector2(minX, minY);
this._bounds.width = maxX - minX;
this._bounds.height = maxY - minY;
}else{
this._bounds.location = topLeft;
this._bounds.width = bottomRight.x - topLeft.x;
this._bounds.height = bottomRight.y - topLeft.y;
}
this._areBoundsDirty = false;
}
return this._bounds;
}
/**
* 如果相机模式为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)
@@ -82,7 +63,6 @@ class Camera extends Component {
public set origin(value: Vector2){
if (this._origin != value){
this._origin = value;
this._areMatrixesDirty = true;
}
}
@@ -94,21 +74,11 @@ class Camera extends Component {
this.entity.position = value;
}
public get transformMatrix(){
if (this._areBoundsDirty)
this.updateMatrixes();
return this._transformMatrix;
}
public get inverseTransformMatrix(){
if (this._areBoundsDirty)
this.updateMatrixes();
return this._inverseTransformMatrix;
}
constructor() {
super();
this.width = SceneManager.stage.stageWidth;
this.height = SceneManager.stage.stageHeight;
this.setZoom(0);
}
@@ -135,7 +105,7 @@ class Camera extends Component {
return this;
}
public setZoom(zoom: number){
public setZoom(zoom: number): Camera{
let newZoom = MathHelper.clamp(zoom, -1, 1);
if (newZoom == 0){
this._zoom = 1;
@@ -145,8 +115,13 @@ class Camera extends Component {
this._zoom = MathHelper.map(newZoom, 0, 1, 1, this._maximumZoom);
}
this._areMatrixesDirty = true;
SceneManager.scene.scaleX = this._zoom;
SceneManager.scene.scaleY = this._zoom;
return this;
}
public setRotation(rotation: number): Camera {
SceneManager.scene.rotation = rotation;
return this;
}
@@ -156,57 +131,91 @@ class Camera extends Component {
return this;
}
public forceMatrixUpdate(){
this._areMatrixesDirty = true;
}
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);
protected updateMatrixes(){
if (!this._areMatrixesDirty)
return;
let tempMat: Matrix2D;
this._transformMatrix = Matrix2D.createTranslation(-this.entity.position.x, -this.entity.position.y);
if (this._zoom != 1){
tempMat = Matrix2D.createScale(this._zoom, this._zoom);
this._transformMatrix = Matrix2D.multiply(this._transformMatrix, tempMat);
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.entity.rotation != 0){
tempMat = Matrix2D.createRotation(this.entity.rotation);
this._transformMatrix = Matrix2D.multiply(this._transformMatrix, tempMat);
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;
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();
}
tempMat = Matrix2D.createTranslation(this._origin.x, this._origin.y, tempMat);
this._transformMatrix = Matrix2D.multiply(this._transformMatrix, tempMat);
this._inverseTransformMatrix = Matrix2D.invert(this._transformMatrix);
this._areBoundsDirty = true;
this._areMatrixesDirty = false;
}
public screenToWorldPoint(screenPosition: Vector2){
this.updateMatrixes();
return Vector2Ext.transformR(screenPosition, this._inverseTransformMatrix);
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);
}
public worldToScreenPoint(worldPosition: Vector2){
this.updateMatrixes();
return Vector2Ext.transformR(worldPosition, this._transformMatrix);
}
private updateFollow(){
this._desiredPositionDelta.x = this._desiredPositionDelta.y = 0;
public onEntityTransformChanged(comp: ComponentTransform){
this._areMatrixesDirty = true;
}
if (this.cameraStyle == CameraStyle.lockOn){
let targetX = this.targetEntity.position.x;
let targetY = this.targetEntity.position.y;
public destory() {
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>(Collider);
if (!this._targetCollider)
return;
}
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;
}
}
}
}
class CameraInset {
public left = 0;
public right = 0;
public top = 0;
public bottom = 0;
enum CameraStyle {
lockOn,
cameraWindow,
}

View File

@@ -1,115 +0,0 @@
class FollowCamera extends Component {
public camera: Camera;
public followLerp = 0.1;
public deadzone: Rectangle = new Rectangle();
public focusOffset: Vector2 = new Vector2();
public mapLockEnabled: boolean;
public mapSize: Vector2 = new Vector2();
private _targetEntity: Entity;
private _cameraStyle: CameraStyle;
private _worldSpaceDeadZone: Rectangle = new Rectangle();
private _desiredPositionDelta: Vector2 = new Vector2();
private _targetCollider: Collider;
constructor(targetEntity: Entity, cameraStyle: CameraStyle = CameraStyle.lockOn){
super();
this._targetEntity = targetEntity;
this._cameraStyle = cameraStyle;
this.camera = null;
}
public onAddedToEntity(){
if (!this.camera)
this.camera = this.entity.scene.camera;
this.follow(this._targetEntity, this._cameraStyle);
}
public follow(targetEntity: Entity, cameraStyle: CameraStyle = CameraStyle.cameraWindow){
this._targetEntity = targetEntity;
this._cameraStyle = cameraStyle;
let cameraBounds = this.camera.bounds;
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 halfScreen = Vector2.multiply(this.camera.bounds.size, new Vector2(0.5));
this._worldSpaceDeadZone.x = this.camera.position.x - halfScreen.x + this.deadzone.x + this.focusOffset.x;
this._worldSpaceDeadZone.y = this.camera.position.y - halfScreen.y + this.deadzone.y + this.focusOffset.y;
this._worldSpaceDeadZone.width = this.deadzone.width;
this._worldSpaceDeadZone.height = this.deadzone.height;
if (this._targetEntity)
this.updateFollow();
this.camera.position = Vector2.lerp(this.camera.position, Vector2.add(this.camera.position, this._desiredPositionDelta), this.followLerp);
this.camera.entity.roundPosition();
if (this.mapLockEnabled){
this.camera.position = this.clampToMapSize(this.camera.position);
this.camera.entity.roundPosition();
}
}
private clampToMapSize(position: Vector2){
let halfScreen = Vector2.multiply(new Vector2(this.camera.bounds.width, this.camera.bounds.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>(Collider);
if (!this._targetCollider)
return;
}
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;
}
}
}
}
enum CameraStyle {
lockOn,
cameraWindow,
}

View File

@@ -1,7 +1,7 @@
/**
* 所有可渲染组件的基类
*/
abstract class RenderableComponent extends Component implements IRenderable {
abstract class RenderableComponent extends PooledComponent implements IRenderable {
private _isVisible: boolean;
protected _areBoundsDirty = true;
protected _bounds: Rectangle = new Rectangle();
@@ -49,7 +49,7 @@ abstract class RenderableComponent extends Component implements IRenderable {
public abstract render(camera: Camera);
public isVisibleFromCamera(camera: Camera): boolean{
this.isVisible = camera.bounds.intersects(this.bounds);
this.isVisible = camera.getBounds().intersects(this.getBounds());
return this.isVisible;
}

View File

@@ -4,10 +4,10 @@ class Sprite {
public readonly center: Vector2;
public origin: Vector2;
public readonly uvs: Rectangle = new Rectangle();
constructor(texture: egret.Texture,
sourceRect: Rectangle = new Rectangle(texture.textureWidth, texture.textureHeight),
origin: Vector2 = sourceRect.getHalfSize()){
sourceRect: Rectangle = new Rectangle(0, 0, texture.textureWidth, texture.textureHeight),
origin: Vector2 = sourceRect.getHalfSize()) {
this.texture2D = texture;
this.sourceRect = sourceRect;
this.center = new Vector2(sourceRect.width * 0.5, sourceRect.height * 0.5);

View File

@@ -1,5 +1,7 @@
class SpriteRenderer extends RenderableComponent {
private _origin: Vector2;
private _bitmap: egret.Bitmap;
private _sprite: Sprite;
public get origin(){
return this._origin;
@@ -10,17 +12,21 @@ class SpriteRenderer extends RenderableComponent {
public setOrigin(origin: Vector2){
if (this._origin != origin){
this._origin = origin;
this._areBoundsDirty = true;
}
return this;
}
public setSprite(sprite: Sprite){
public setSprite(sprite: Sprite): SpriteRenderer{
this.removeChildren();
this.addChild(new egret.Bitmap(sprite.texture2D));
this._sprite = sprite;
if (this._sprite) this._origin = this._sprite.origin;
this._bitmap = new egret.Bitmap(sprite.texture2D);
this.addChild(this._bitmap);
return this;
}
public setColor(color: number){
public setColor(color: number): SpriteRenderer{
let colorMatrix = [
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
@@ -32,21 +38,27 @@ class SpriteRenderer extends RenderableComponent {
colorMatrix[12] = color % 256 / 255;
let colorFilter = new egret.ColorMatrixFilter(colorMatrix);
this.filters = [colorFilter];
return this;
}
public isVisibleFromCamera(camera: Camera): boolean{
let topLeft = camera.screenToWorldPoint(new Vector2(0, 0));
this.isVisible = new Rectangle(topLeft.x, topLeft.y, this.stage.stageWidth, this.stage.stageHeight).intersects(this.bounds);
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){
this.x = this.entity.position.x - this.origin.x - camera.position.x + camera.origin.x;
this.y = this.entity.position.y - this.origin.y - camera.position.y + camera.origin.y;
}
public onRemovedFromEntity(){
if (this.parent)
this.parent.removeChild(this);
}
}
public reset(){
}
}

View File

@@ -1,6 +1,7 @@
class Entity extends egret.DisplayObjectContainer {
private static _idGenerator: number;
private _position: Vector2 = Vector2.zero;
public name: string;
public readonly id: number;
/** 当前实体所属的场景 */
@@ -19,12 +20,11 @@ class Entity extends egret.DisplayObjectContainer {
}
public get position(){
return new Vector2(this.x, this.y);
return this._position;
}
public set position(value: Vector2){
this.x = value.x;
this.y = value.y;
this._position = value;
}
public get scale(){

View File

@@ -18,6 +18,8 @@ class Scene extends egret.DisplayObjectContainer {
this.renderableComponents = new RenderableComponentList();
this.entities = new EntityList(this);
this.content = new ContentManager();
this.width = SceneManager.stage.stageWidth;
this.height = SceneManager.stage.stageHeight;
this.addEventListener(egret.Event.ACTIVATE, this.onActive, this);
this.addEventListener(egret.Event.DEACTIVATE, this.onDeactive, this);
@@ -135,7 +137,6 @@ class Scene extends egret.DisplayObjectContainer {
Physics.clear();
this.camera.destory();
this.camera = null;
this.content.dispose();
@@ -191,9 +192,6 @@ class Scene extends egret.DisplayObjectContainer {
public render() {
for (let i = 0; i < this._renderers.length; i++) {
if (this._renderers[i].camera)
this._renderers[i].camera.forceMatrixUpdate();
this.camera.forceMatrixUpdate();
this._renderers[i].render(this);
}
}

View File

@@ -4,16 +4,6 @@ class PolyLight extends RenderableComponent {
private _lightEffect;
private _indices: number[] = [];
public get bounds(){
if (this._areBoundsDirty){
this._bounds.calculateBounds(this.entity.position, this._localOffset, new Vector2(this._radius),
Vector2.one, 0, this._radius * 2, this._radius * 2);
this._areBoundsDirty = false;
}
return this._bounds;
}
public get radius(){
return this._radius;
}
@@ -49,4 +39,8 @@ class PolyLight extends RenderableComponent {
public render(camera: Camera) {
}
public reset(){
}
}