新增跟随相机
This commit is contained in:
@@ -88,6 +88,14 @@ class Camera extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
public get position(){
|
||||
return this.entity.transform.position;
|
||||
}
|
||||
|
||||
public set position(value: Vector2){
|
||||
this.entity.transform.position = value;
|
||||
}
|
||||
|
||||
public get transformMatrix(){
|
||||
if (this._areBoundsDirty)
|
||||
this.updateMatrixes();
|
||||
|
||||
115
source/src/ECS/Components/FollowCamera.ts
Normal file
115
source/src/ECS/Components/FollowCamera.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
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.transform.roundPosition();
|
||||
|
||||
if (this.mapLockEnabled){
|
||||
this.camera.position = this.clampToMapSize(this.camera.position);
|
||||
this.camera.entity.transform.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.transform.position.x;
|
||||
let targetY = this._targetEntity.transform.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,
|
||||
}
|
||||
@@ -155,8 +155,8 @@ class Scene extends egret.DisplayObjectContainer {
|
||||
}
|
||||
|
||||
public prepRenderState() {
|
||||
this._projectionMatrix.m11 = 2 / this.stage.width;
|
||||
this._projectionMatrix.m22 = -2 / this.stage.height;
|
||||
this._projectionMatrix.m11 = 2 / this.stage.stageWidth;
|
||||
this._projectionMatrix.m22 = -2 / this.stage.stageHeight;
|
||||
|
||||
this._transformMatrix = this.camera.transformMatrix;
|
||||
this._matrixTransformMatrix = Matrix2D.multiply(this._transformMatrix, this._projectionMatrix);
|
||||
|
||||
@@ -283,6 +283,10 @@ class Transform {
|
||||
}
|
||||
}
|
||||
|
||||
public roundPosition(){
|
||||
this.position = this._position.round();
|
||||
}
|
||||
|
||||
public updateTransform(){
|
||||
if (this._hierachyDirty != DirtyType.clean){
|
||||
if (this.parent)
|
||||
|
||||
@@ -23,7 +23,7 @@ class Rectangle {
|
||||
return this.y + this.height;
|
||||
}
|
||||
|
||||
public get center(){
|
||||
public get center() {
|
||||
return new Vector2(this.x + (this.width / 2), this.y + (this.height / 2));
|
||||
}
|
||||
|
||||
@@ -36,6 +36,15 @@ class Rectangle {
|
||||
this.y = value.y;
|
||||
}
|
||||
|
||||
public get size() {
|
||||
return new Vector2(this.width, this.height);
|
||||
}
|
||||
|
||||
public set size(value: Vector2) {
|
||||
this.width = value.x;
|
||||
this.height = value.y;
|
||||
}
|
||||
|
||||
constructor(x?: number, y?: number, width?: number, height?: number) {
|
||||
this.x = x ? x : 0;
|
||||
this.y = y ? y : 0;
|
||||
@@ -56,53 +65,59 @@ class Rectangle {
|
||||
(value.y < (this.y + this.height)));
|
||||
}
|
||||
|
||||
public containsRect(value: Rectangle) {
|
||||
return ((((this.x <= value.x) && (value.x < (this.x + this.width))) &&
|
||||
(this.y <= value.y)) &&
|
||||
(value.y < (this.y + this.height)));
|
||||
}
|
||||
|
||||
public static fromMinMax(minX: number, minY: number, maxX: number, maxY: number) {
|
||||
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
public getClosestPointOnRectangleBorderToPoint(point: Point): {res: Vector2, edgeNormal: Vector2} {
|
||||
public getClosestPointOnRectangleBorderToPoint(point: Point): { res: Vector2, edgeNormal: Vector2 } {
|
||||
let edgeNormal = new Vector2(0, 0);
|
||||
|
||||
let res = new Vector2(0, 0);
|
||||
res.x = MathHelper.clamp(point.x, this.left, this.right);
|
||||
res.y = MathHelper.clamp(point.y, this.top, this.bottom);
|
||||
|
||||
if (this.contains(res)){
|
||||
if (this.contains(res)) {
|
||||
let dl = res.x - this.left;
|
||||
let dr = this.right - res.x;
|
||||
let dt = res.y - this.top;
|
||||
let db = this.bottom - res.y;
|
||||
|
||||
let min = Math.min(dl, dr, dt, db);
|
||||
if (min == dt){
|
||||
if (min == dt) {
|
||||
res.y = this.top;
|
||||
edgeNormal.y = -1;
|
||||
} else if(min == db){
|
||||
} else if (min == db) {
|
||||
res.y = this.bottom;
|
||||
edgeNormal.y = 1;
|
||||
} else if(min == dl){
|
||||
} else if (min == dl) {
|
||||
res.x = this.left;
|
||||
edgeNormal.x = -1;
|
||||
} else{
|
||||
} else {
|
||||
res.x = this.right;
|
||||
edgeNormal.x = 1;
|
||||
}
|
||||
} else {
|
||||
if (res.x == this.left){
|
||||
if (res.x == this.left) {
|
||||
edgeNormal.x = -1;
|
||||
}
|
||||
if (res.x == this.right){
|
||||
if (res.x == this.right) {
|
||||
edgeNormal.x = 1;
|
||||
}
|
||||
if (res.y == this.top){
|
||||
if (res.y == this.top) {
|
||||
edgeNormal.y = -1;
|
||||
}
|
||||
if (res.y == this.bottom){
|
||||
if (res.y == this.bottom) {
|
||||
edgeNormal.y = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return {res: res, edgeNormal: edgeNormal};
|
||||
return { res: res, edgeNormal: edgeNormal };
|
||||
}
|
||||
|
||||
public calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2,
|
||||
@@ -149,26 +164,26 @@ class Rectangle {
|
||||
* 给定多边形的点,计算边界
|
||||
* @param points
|
||||
*/
|
||||
public static rectEncompassingPoints(points: Vector2[]){
|
||||
public static rectEncompassingPoints(points: Vector2[]) {
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let minY = Number.POSITIVE_INFINITY;
|
||||
let maxX = Number.NEGATIVE_INFINITY;
|
||||
let maxY = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (let i = 0; i < points.length; i ++){
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let pt = points[i];
|
||||
|
||||
if (pt.x < minX){
|
||||
if (pt.x < minX) {
|
||||
minX = pt.x;
|
||||
}
|
||||
if (pt.x > maxX){
|
||||
if (pt.x > maxX) {
|
||||
maxX = pt.x;
|
||||
}
|
||||
|
||||
if (pt.y < minY){
|
||||
if (pt.y < minY) {
|
||||
minY = pt.y;
|
||||
}
|
||||
if (pt.y > maxY){
|
||||
if (pt.y > maxY) {
|
||||
maxY = pt.y;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,11 @@ class Vector2 {
|
||||
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||
}
|
||||
|
||||
/** 对x和y值四舍五入 */
|
||||
public round(): Vector2{
|
||||
return new Vector2(Math.round(this.x), Math.round(this.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的Vector2
|
||||
* 它包含来自另一个向量的标准化值。
|
||||
@@ -124,6 +129,17 @@ class Vector2 {
|
||||
return (v1 * v1) + (v2 * v2);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
public static clamp(value1: Vector2, min: Vector2, max: Vector2){
|
||||
return new Vector2(MathHelper.clamp(value1.x, min.x, max.x),
|
||||
MathHelper.clamp(value1.y, min.y, max.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 包含指定向量的线性插值
|
||||
* @param value1 第一个向量
|
||||
|
||||
Reference in New Issue
Block a user