mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-22 10:05:37 -04:00
392 lines
13 KiB
TypeScript
392 lines
13 KiB
TypeScript
/*
|
|
* GDevelop JS Platform
|
|
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
|
* This project is released under the MIT License.
|
|
*/
|
|
namespace gdjs {
|
|
/**
|
|
* Represents a layer of a scene, used to display objects.
|
|
*/
|
|
export class Layer extends gdjs.RuntimeLayer {
|
|
_cameraRotation: float = 0;
|
|
_zoomFactor: float = 1;
|
|
_cameraX: float;
|
|
_cameraY: float;
|
|
_cameraZ: float = 0;
|
|
/**
|
|
* `_cameraZ` is dirty when the zoom factor is set last.
|
|
*/
|
|
_isCameraZDirty: boolean = true;
|
|
|
|
/**
|
|
* @param layerData The data used to initialize the layer
|
|
* @param instanceContainer The container in which the layer is used
|
|
*/
|
|
constructor(
|
|
layerData: LayerData,
|
|
instanceContainer: gdjs.RuntimeInstanceContainer
|
|
) {
|
|
super(layerData, instanceContainer);
|
|
|
|
this._cameraX = this.getWidth() / 2;
|
|
this._cameraY = this.getHeight() / 2;
|
|
|
|
// Let the renderer do its final set up:
|
|
this._renderer.onCreated();
|
|
}
|
|
|
|
/**
|
|
* Called by the RuntimeScene whenever the game resolution size is changed.
|
|
* Updates the layer width/height and position.
|
|
*/
|
|
onGameResolutionResized(
|
|
oldGameResolutionOriginX: float,
|
|
oldGameResolutionOriginY: float
|
|
): void {
|
|
// Adapt position of the camera center as:
|
|
// * Most cameras following a player/object on the scene will be updating this
|
|
// in events anyway.
|
|
// * Cameras not following a player/object are usually UIs which are intuitively
|
|
// expected not to "move". Not adapting the center position would make the camera
|
|
// move from its initial position (which is centered in the screen) - and anchor
|
|
// behavior would behave counterintuitively.
|
|
this._cameraX +=
|
|
this._runtimeScene.getViewportOriginX() - oldGameResolutionOriginX;
|
|
this._cameraY +=
|
|
this._runtimeScene.getViewportOriginY() - oldGameResolutionOriginY;
|
|
this._renderer.updatePosition();
|
|
}
|
|
|
|
/**
|
|
* Change the camera center X position.
|
|
*
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @return The x position of the camera
|
|
*/
|
|
getCameraX(cameraId?: integer): float {
|
|
this._forceDimensionUpdate();
|
|
return this._cameraX;
|
|
}
|
|
|
|
/**
|
|
* Change the camera center Y position.
|
|
*
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @return The y position of the camera
|
|
*/
|
|
getCameraY(cameraId?: integer): float {
|
|
this._forceDimensionUpdate();
|
|
return this._cameraY;
|
|
}
|
|
|
|
/**
|
|
* Set the camera center X position.
|
|
*
|
|
* @param x The new x position
|
|
* @param cameraId The camera number. Currently ignored.
|
|
*/
|
|
setCameraX(x: float, cameraId?: integer): void {
|
|
this._forceDimensionUpdate();
|
|
this._cameraX = x;
|
|
this._renderer.updatePosition();
|
|
}
|
|
|
|
/**
|
|
* Set the camera center Y position.
|
|
*
|
|
* @param y The new y position
|
|
* @param cameraId The camera number. Currently ignored.
|
|
*/
|
|
setCameraY(y: float, cameraId?: integer): void {
|
|
this._forceDimensionUpdate();
|
|
this._cameraY = y;
|
|
this._renderer.updatePosition();
|
|
}
|
|
|
|
/**
|
|
* Get the camera width (which can be different than the game resolution width
|
|
* if the camera is zoomed).
|
|
*
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @return The width of the camera
|
|
*/
|
|
getCameraWidth(cameraId?: integer): float {
|
|
return this.getWidth() / this._zoomFactor;
|
|
}
|
|
|
|
/**
|
|
* Get the camera height (which can be different than the game resolution height
|
|
* if the camera is zoomed).
|
|
*
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @return The height of the camera
|
|
*/
|
|
getCameraHeight(cameraId?: integer): float {
|
|
return this.getHeight() / this._zoomFactor;
|
|
}
|
|
|
|
/**
|
|
* Set the zoom of a camera.
|
|
*
|
|
* @param newZoom The new zoom. Must be superior to 0. 1 is the default zoom.
|
|
* @param cameraId The camera number. Currently ignored.
|
|
*/
|
|
setCameraZoom(newZoom: float, cameraId?: integer): void {
|
|
this._zoomFactor = newZoom;
|
|
this._isCameraZDirty = true;
|
|
this._renderer.updatePosition();
|
|
}
|
|
|
|
/**
|
|
* Get the zoom of a camera.
|
|
*
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @return The zoom.
|
|
*/
|
|
getCameraZoom(cameraId?: integer): float {
|
|
return this._zoomFactor;
|
|
}
|
|
|
|
/**
|
|
* Set the camera center Z position.
|
|
*
|
|
* @param z The new y position.
|
|
* @param fov The field of view.
|
|
* @param cameraId The camera number. Currently ignored.
|
|
*/
|
|
setCameraZ(z: float, fov: float = 45, cameraId?: integer): void {
|
|
const cameraFovInRadians = gdjs.toRad(fov);
|
|
|
|
// The zoom factor is capped to a not too big value to avoid infinity.
|
|
// MAX_SAFE_INTEGER is an arbitrary choice. It's big but not too big.
|
|
const zoomFactor = Math.min(
|
|
Number.MAX_SAFE_INTEGER,
|
|
(0.5 * this.getHeight()) / (z * Math.tan(0.5 * cameraFovInRadians))
|
|
);
|
|
|
|
if (zoomFactor > 0) {
|
|
this._zoomFactor = zoomFactor;
|
|
}
|
|
|
|
this._cameraZ = z;
|
|
this._isCameraZDirty = false;
|
|
this._renderer.updatePosition();
|
|
}
|
|
|
|
/**
|
|
* Get the camera center Z position.
|
|
*
|
|
* @param fov The field of view.
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @return The z position of the camera
|
|
*/
|
|
getCameraZ(fov: float = 45, cameraId?: integer): float {
|
|
if (!this._isCameraZDirty) {
|
|
return this._cameraZ;
|
|
}
|
|
|
|
// Set the camera so that it displays the whole PixiJS plane, as if it was a 2D rendering.
|
|
// The Z position is computed by taking the half height of the displayed rendering,
|
|
// and using the angle of the triangle defined by the field of view to compute the length
|
|
// of the triangle defining the distance between the camera and the rendering plane.
|
|
const cameraZPosition =
|
|
(0.5 * this.getHeight()) /
|
|
this.getCameraZoom() /
|
|
Math.tan(0.5 * gdjs.toRad(fov));
|
|
|
|
return cameraZPosition;
|
|
}
|
|
|
|
/**
|
|
* Get the rotation of the camera, expressed in degrees.
|
|
*
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @return The rotation, in degrees.
|
|
*/
|
|
getCameraRotation(cameraId?: integer): float {
|
|
return this._cameraRotation;
|
|
}
|
|
|
|
/**
|
|
* Set the rotation of the camera, expressed in degrees.
|
|
* The rotation is made around the camera center.
|
|
*
|
|
* @param rotation The new rotation, in degrees.
|
|
* @param cameraId The camera number. Currently ignored.
|
|
*/
|
|
setCameraRotation(rotation: float, cameraId?: integer): void {
|
|
this._cameraRotation = rotation;
|
|
this._renderer.updatePosition();
|
|
}
|
|
|
|
/**
|
|
* Convert a point from the canvas coordinates (for example,
|
|
* the mouse position) to the container coordinates.
|
|
*
|
|
* This method handles 3D rotations.
|
|
*
|
|
* @param x The x position, in canvas coordinates.
|
|
* @param y The y position, in canvas coordinates.
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @param result The point instance that is used to return the result.
|
|
*/
|
|
convertCoords(
|
|
x: float,
|
|
y: float,
|
|
cameraId: integer = 0,
|
|
result: FloatPoint
|
|
): FloatPoint {
|
|
// This code duplicates applyLayerInverseTransformation for performance reasons;
|
|
|
|
// The result parameter used to be optional.
|
|
let position = result || [0, 0];
|
|
|
|
if (this._renderer.isCameraRotatedIn3D()) {
|
|
return this._renderer.transformTo3DWorld(x, y, 0, cameraId, result);
|
|
}
|
|
|
|
x -= this.getRuntimeScene()._cachedGameResolutionWidth / 2;
|
|
y -= this.getRuntimeScene()._cachedGameResolutionHeight / 2;
|
|
x /= Math.abs(this._zoomFactor);
|
|
y /= Math.abs(this._zoomFactor);
|
|
|
|
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
|
|
const angleInRadians = (this._cameraRotation / 180) * Math.PI;
|
|
const tmp = x;
|
|
const cosValue = Math.cos(angleInRadians);
|
|
const sinValue = Math.sin(angleInRadians);
|
|
x = cosValue * x - sinValue * y;
|
|
y = sinValue * tmp + cosValue * y;
|
|
position[0] = x + this.getCameraX(cameraId);
|
|
position[1] = y + this.getCameraY(cameraId);
|
|
return position;
|
|
}
|
|
|
|
/**
|
|
* Return an array containing the coordinates of the point passed as parameter
|
|
* in layer local coordinates (as opposed to the parent coordinates).
|
|
*
|
|
* All transformations (scale, rotation) are supported.
|
|
*
|
|
* This method doesn't handle 3D rotations.
|
|
*
|
|
* @param x The X position of the point, in parent coordinates.
|
|
* @param y The Y position of the point, in parent coordinates.
|
|
* @param result Array that will be updated with the result
|
|
* @param result The point instance that is used to return the result.
|
|
* (x and y position of the point in layer coordinates).
|
|
*/
|
|
applyLayerInverseTransformation(
|
|
x: float,
|
|
y: float,
|
|
cameraId: integer,
|
|
result: FloatPoint
|
|
): FloatPoint {
|
|
x -= this._runtimeScene.getViewportOriginX();
|
|
y -= this._runtimeScene.getViewportOriginY();
|
|
x /= Math.abs(this._zoomFactor);
|
|
y /= Math.abs(this._zoomFactor);
|
|
|
|
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
|
|
const angleInRadians = (this._cameraRotation / 180) * Math.PI;
|
|
const tmp = x;
|
|
const cosValue = Math.cos(angleInRadians);
|
|
const sinValue = Math.sin(angleInRadians);
|
|
x = cosValue * x - sinValue * y;
|
|
y = sinValue * tmp + cosValue * y;
|
|
result[0] = x + this.getCameraX(cameraId);
|
|
result[1] = y + this.getCameraY(cameraId);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Convert a point from the container coordinates (for example,
|
|
* an object position) to the canvas coordinates.
|
|
*
|
|
* This method doesn't handle 3D rotations.
|
|
*
|
|
* @param x The x position, in container coordinates.
|
|
* @param y The y position, in container coordinates.
|
|
* @param cameraId The camera number. Currently ignored.
|
|
* @param result The point instance that is used to return the result.
|
|
*/
|
|
convertInverseCoords(
|
|
x: float,
|
|
y: float,
|
|
cameraId: integer = 0,
|
|
result: FloatPoint
|
|
): FloatPoint {
|
|
// This code duplicates applyLayerTransformation for performance reasons;
|
|
|
|
// The result parameter used to be optional.
|
|
let position = result || [0, 0];
|
|
x -= this.getCameraX(cameraId);
|
|
y -= this.getCameraY(cameraId);
|
|
|
|
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
|
|
const angleInRadians = (this._cameraRotation / 180) * Math.PI;
|
|
const tmp = x;
|
|
const cosValue = Math.cos(-angleInRadians);
|
|
const sinValue = Math.sin(-angleInRadians);
|
|
x = cosValue * x - sinValue * y;
|
|
y = sinValue * tmp + cosValue * y;
|
|
x *= Math.abs(this._zoomFactor);
|
|
y *= Math.abs(this._zoomFactor);
|
|
position[0] = x + this.getRuntimeScene()._cachedGameResolutionWidth / 2;
|
|
position[1] = y + this.getRuntimeScene()._cachedGameResolutionHeight / 2;
|
|
return position;
|
|
}
|
|
|
|
/**
|
|
* Return an array containing the coordinates of the point passed as parameter
|
|
* in parent coordinate coordinates (as opposed to the layer local coordinates).
|
|
*
|
|
* All transformations (scale, rotation) are supported.
|
|
*
|
|
* This method doesn't handle 3D rotations.
|
|
*
|
|
* @param x The X position of the point, in layer coordinates.
|
|
* @param y The Y position of the point, in layer coordinates.
|
|
* @param result Array that will be updated with the result
|
|
* (x and y position of the point in parent coordinates).
|
|
*/
|
|
applyLayerTransformation(
|
|
x: float,
|
|
y: float,
|
|
cameraId: integer,
|
|
result: FloatPoint
|
|
): FloatPoint {
|
|
x -= this.getCameraX(cameraId);
|
|
y -= this.getCameraY(cameraId);
|
|
|
|
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
|
|
const angleInRadians = (this._cameraRotation / 180) * Math.PI;
|
|
const tmp = x;
|
|
const cosValue = Math.cos(-angleInRadians);
|
|
const sinValue = Math.sin(-angleInRadians);
|
|
x = cosValue * x - sinValue * y;
|
|
y = sinValue * tmp + cosValue * y;
|
|
x *= Math.abs(this._zoomFactor);
|
|
y *= Math.abs(this._zoomFactor);
|
|
x += this._runtimeScene.getViewportOriginX();
|
|
y += this._runtimeScene.getViewportOriginY();
|
|
|
|
result[0] = x;
|
|
result[1] = y;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This ensure that the viewport dimensions are up to date.
|
|
*
|
|
* It's needed because custom objects dimensions are only updated on
|
|
* demand for efficiency reasons.
|
|
*/
|
|
private _forceDimensionUpdate(): void {
|
|
// This will update dimensions.
|
|
this._runtimeScene.getViewportWidth();
|
|
}
|
|
}
|
|
}
|