Compare commits

...

8 Commits

Author SHA1 Message Date
Davy Hélard
7ceaf07418 Add more comments 2025-03-28 17:54:28 +01:00
Davy Hélard
7eef5fb722 Format 2025-03-28 17:34:40 +01:00
Davy Hélard
30c750d498 Make the graphical debugger more readable. 2025-03-25 18:27:25 +01:00
Davy Hélard
ecbf73823b Avoid bad setup to slow down games. 2025-03-25 17:41:54 +01:00
Davy Hélard
1156a8ad75 Remove useless code. 2025-03-25 17:08:31 +01:00
Davy Hélard
1438b28cab Fix transformation. 2025-03-25 17:05:29 +01:00
Davy Hélard
9e8fbbe287 Disable resource smoothing by default for the new scale mode. 2025-03-23 19:40:31 +01:00
Davy Hélard
0b4dadd3d5 Add a scale mode for modern pixel-art games 2025-03-23 18:53:35 +01:00
18 changed files with 247 additions and 103 deletions

View File

@@ -374,12 +374,14 @@ class GD_CORE_API Project {
void SetVerticalSyncActivatedByDefault(bool enable) { verticalSync = enable; }
/**
* Return the scale mode used by the game (usually "linear" or "nearest").
* Return the scale mode used by the game (usually "linear", "magnified" or
* "nearest").
*/
const gd::String& GetScaleMode() const { return scaleMode; }
/**
* Set the scale mode used by the game (usually "linear" or "nearest").
* Set the scale mode used by the game (usually "linear", "magnified" or
* "nearest").
*/
void SetScaleMode(const gd::String& scaleMode_) { scaleMode = scaleMode_; }

View File

@@ -211,8 +211,8 @@ namespace gdjs {
const isOutsideCanvas =
canvasRight < 0 ||
canvasBottom < 0 ||
canvasLeft > runtimeGame.getGameResolutionWidth() ||
canvasTop > runtimeGame.getGameResolutionHeight();
canvasLeft > runtimeGame.getRenderingResolutionWidth() ||
canvasTop > runtimeGame.getRenderingResolutionHeight();
if (isOutsideCanvas) {
this._form.style.display = 'none';
return;

View File

@@ -4,6 +4,7 @@ namespace gdjs {
_fontManager: any;
_text: PIXI.Text;
_justCreated: boolean = true;
_upscaleRatio: integer = 1;
constructor(
runtimeObject: gdjs.TextRuntimeObject,
@@ -47,7 +48,7 @@ namespace gdjs {
const style = this._text.style;
style.fontStyle = this._object._italic ? 'italic' : 'normal';
style.fontWeight = this._object._bold ? 'bold' : 'normal';
style.fontSize = this._object._characterSize;
style.fontSize = this._object._characterSize * this._upscaleRatio;
style.fontFamily = fontName;
if (this._object._useGradient) {
style.fill = this._getGradientHex();
@@ -62,7 +63,7 @@ namespace gdjs {
// @ts-ignore
style.align = this._object._textAlign;
style.wordWrap = this._object._wrapping;
style.wordWrapWidth = this._object._wrappingWidth;
style.wordWrapWidth = this._object._wrappingWidth * this._upscaleRatio;
style.breakWords = true;
style.stroke = gdjs.rgbToHexNumber(
this._object._outlineColor[0],
@@ -70,7 +71,7 @@ namespace gdjs {
this._object._outlineColor[2]
);
style.strokeThickness = this._object._isOutlineEnabled
? this._object._outlineThickness
? this._object._outlineThickness * this._upscaleRatio
: 0;
style.dropShadow = this._object._shadow;
style.dropShadowColor = gdjs.rgbToHexNumber(
@@ -79,13 +80,16 @@ namespace gdjs {
this._object._shadowColor[2]
);
style.dropShadowAlpha = this._object._shadowOpacity / 255;
style.dropShadowBlur = this._object._shadowBlur;
style.dropShadowBlur = this._object._shadowBlur * this._upscaleRatio;
style.dropShadowAngle = gdjs.toRad(this._object._shadowAngle);
style.dropShadowDistance = this._object._shadowDistance;
style.dropShadowDistance =
this._object._shadowDistance * this._upscaleRatio;
const extraPaddingForShadow = style.dropShadow
? style.dropShadowDistance + style.dropShadowBlur
? this._object._shadowDistance + this._object._shadowBlur
: 0;
style.padding = Math.ceil(this._object._padding + extraPaddingForShadow);
style.padding =
Math.ceil(this._object._padding + extraPaddingForShadow) *
this._upscaleRatio;
// Prevent spikey outlines by adding a miter limit
style.miterLimit = 3;
@@ -117,7 +121,6 @@ namespace gdjs {
this._text.position.x = this._object.x + this._text.width / 2;
this._text.anchor.x = 0.5;
}
this._text.position.y = this._object.y + this._text.height / 2;
const alignmentY =
this._object._verticalTextAlignment === 'bottom'
@@ -181,18 +184,30 @@ namespace gdjs {
return gradient;
}
/**
* Set the text object upscale ratio.
* @param upscaleRatio The new upscale ratio for the text object.
* @see gdjs.RuntimeGame.getZoomFactor
*/
setUpscaleRatio(upscaleRatio: integer): void {
this._upscaleRatio = upscaleRatio;
this._text.scale.x = this._object.getScaleX() / this._upscaleRatio;
this._text.scale.y = this._object.getScaleY() / this._upscaleRatio;
this.updateStyle();
}
/**
* Get x-scale of the text.
*/
getScaleX(): float {
return this._text.scale.x;
return this._object.getScaleX();
}
/**
* Get y-scale of the text.
*/
getScaleY(): float {
return this._text.scale.y;
return this._object.getScaleY();
}
/**
@@ -200,8 +215,8 @@ namespace gdjs {
* @param newScale The new scale for the text object.
*/
setScale(newScale: float): void {
this._text.scale.x = newScale;
this._text.scale.y = newScale;
this._text.scale.x = newScale / this._upscaleRatio;
this._text.scale.y = newScale / this._upscaleRatio;
}
/**
@@ -209,7 +224,7 @@ namespace gdjs {
* @param newScale The new x-scale for the text object.
*/
setScaleX(newScale: float): void {
this._text.scale.x = newScale;
this._text.scale.x = newScale / this._upscaleRatio;
}
/**
@@ -217,7 +232,7 @@ namespace gdjs {
* @param newScale The new y-scale for the text object.
*/
setScaleY(newScale: float): void {
this._text.scale.y = newScale;
this._text.scale.y = newScale / this._upscaleRatio;
}
destroy() {

View File

@@ -149,7 +149,7 @@ namespace gdjs {
this.onCreated();
}
updateFromObjectData(
override updateFromObjectData(
oldObjectData: TextObjectData,
newObjectData: TextObjectData
): boolean {
@@ -214,7 +214,7 @@ namespace gdjs {
return true;
}
getNetworkSyncData(): TextObjectNetworkSyncData {
override getNetworkSyncData(): TextObjectNetworkSyncData {
return {
...super.getNetworkSyncData(),
str: this._str,
@@ -242,7 +242,7 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(
override updateFromNetworkSyncData(
networkSyncData: TextObjectNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData);
@@ -317,15 +317,15 @@ namespace gdjs {
}
}
getRendererObject() {
override getRendererObject() {
return this._renderer.getRendererObject();
}
update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
override update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
this._renderer.ensureUpToDate();
}
onDestroyed(): void {
override onDestroyed(): void {
super.onDestroyed();
this._renderer.destroy();
}
@@ -345,6 +345,12 @@ namespace gdjs {
}
}
override onGameZoomFactorChanged(): void {
this._renderer.setUpscaleRatio(
this.getRuntimeScene().getGame().getZoomFactor()
);
}
/**
* Update the rendered object position.
*/
@@ -353,27 +359,17 @@ namespace gdjs {
this._renderer.updatePosition();
}
/**
* Set object position on X axis.
*/
setX(x: float): void {
override setX(x: float): void {
super.setX(x);
this._updateTextPosition();
}
/**
* Set object position on Y axis.
*/
setY(y: float): void {
override setY(y: float): void {
super.setY(y);
this._updateTextPosition();
}
/**
* Set the angle of the object.
* @param angle The new angle of the object
*/
setAngle(angle: float): void {
override setAngle(angle: float): void {
super.setAngle(angle);
this._renderer.updateAngle();
}
@@ -499,14 +495,14 @@ namespace gdjs {
/**
* Get width of the text.
*/
getWidth(): float {
override getWidth(): float {
return this._wrapping ? this._wrappingWidth : this._renderer.getWidth();
}
/**
* Get height of the text.
*/
getHeight(): float {
override getHeight(): float {
return this._renderer.getHeight();
}
@@ -685,11 +681,11 @@ namespace gdjs {
}
}
setWidth(width: float): void {
override setWidth(width: float): void {
this.setWrappingWidth(width);
}
getDrawableY(): float {
override getDrawableY(): float {
return (
this.getY() -
(this._verticalTextAlignment === 'center'

View File

@@ -229,6 +229,10 @@ namespace gdjs {
*/
onDestroy(parent: gdjs.RuntimeInstanceContainer) {}
override onGameZoomFactorChanged() {
this._instanceContainer.onGameZoomFactorChanged();
}
override updatePreRender(parent: gdjs.RuntimeInstanceContainer): void {
this._instanceContainer._updateObjectsPreRender();
this.getRenderer().ensureUpToDate();

View File

@@ -547,6 +547,15 @@ namespace gdjs {
return this._allInstancesList;
}
/**
* Called when the game zoom factor is changed to adapt to a new resolution.
*/
onGameZoomFactorChanged(): void {
for (const instance of this.getAdhocListOfAllInstances()) {
instance.onGameZoomFactorChanged();
}
}
/**
* Update the objects before launching the events.
*/

View File

@@ -37,18 +37,10 @@ namespace gdjs {
showPointsNames: boolean,
showCustomPoints: boolean
) {
const pixiContainer = this._instanceContainer
.getRenderer()
.getRendererObject();
if (!this._debugDraw || !this._debugDrawContainer) {
this._debugDrawContainer = new PIXI.Container();
this._debugDraw = new PIXI.Graphics();
// Add on top of all layers:
this._debugDrawContainer.addChild(this._debugDraw);
if (pixiContainer) {
pixiContainer.addChild(this._debugDrawContainer);
}
}
const debugDraw = this._debugDraw;
@@ -112,7 +104,8 @@ namespace gdjs {
const polygon: float[] = [];
polygon.push.apply(
polygon,
layer.applyLayerTransformation(
this.applyLayerTransformation(
layer,
aabb.min[0],
aabb.min[1],
0,
@@ -121,7 +114,8 @@ namespace gdjs {
);
polygon.push.apply(
polygon,
layer.applyLayerTransformation(
this.applyLayerTransformation(
layer,
aabb.max[0],
aabb.min[1],
0,
@@ -130,7 +124,8 @@ namespace gdjs {
);
polygon.push.apply(
polygon,
layer.applyLayerTransformation(
this.applyLayerTransformation(
layer,
aabb.max[0],
aabb.max[1],
0,
@@ -139,7 +134,8 @@ namespace gdjs {
);
polygon.push.apply(
polygon,
layer.applyLayerTransformation(
this.applyLayerTransformation(
layer,
aabb.min[0],
aabb.max[1],
0,
@@ -185,7 +181,8 @@ namespace gdjs {
// as this is for debug draw.
const polygon: float[] = [];
hitboxes[j].vertices.forEach((point) => {
point = layer.applyLayerTransformation(
point = this.applyLayerTransformation(
layer,
point[0],
point[1],
0,
@@ -205,7 +202,8 @@ namespace gdjs {
debugDraw.fill.alpha = 0.3;
// Draw Center point
const centerPoint = layer.applyLayerTransformation(
const centerPoint = this.applyLayerTransformation(
layer,
object.getCenterXInScene(),
object.getCenterYInScene(),
0,
@@ -221,7 +219,8 @@ namespace gdjs {
);
// Draw position point
const positionPoint = layer.applyLayerTransformation(
const positionPoint = this.applyLayerTransformation(
layer,
object.getX(),
object.getY(),
0,
@@ -245,7 +244,8 @@ namespace gdjs {
Math.abs(originPoint[0] - positionPoint[0]) >= 1 ||
Math.abs(originPoint[1] - positionPoint[1]) >= 1
) {
originPoint = layer.applyLayerTransformation(
originPoint = this.applyLayerTransformation(
layer,
originPoint[0],
originPoint[1],
0,
@@ -270,7 +270,8 @@ namespace gdjs {
for (const customPointName in animationFrame.points.items) {
let customPoint = object.getPointPosition(customPointName);
customPoint = layer.applyLayerTransformation(
customPoint = this.applyLayerTransformation(
layer,
customPoint[0],
customPoint[1],
0,
@@ -303,6 +304,27 @@ namespace gdjs {
debugDraw.endFill();
}
private applyLayerTransformation(
layer: gdjs.RuntimeLayer,
x: float,
y: float,
cameraId: integer,
result: FloatPoint
): FloatPoint {
layer.applyLayerTransformation(x, y, cameraId, result);
const gamePixiContainer = this._instanceContainer
.getRenderer()
.getRendererObject();
if (!gamePixiContainer) {
return result;
}
// The scale is usually near 1 unless the 'magnified' scale mode is used.
// See gdjs.RuntimeGame.getZoomFactor
result[0] *= gamePixiContainer.scale.x;
result[1] *= gamePixiContainer.scale.y;
return result;
}
clearDebugDraw(): void {
if (this._debugDraw) {
this._debugDraw.clear();

View File

@@ -262,8 +262,8 @@ namespace gdjs {
if (game.getAntialiasingMode() !== 'none') {
this._threeEffectComposer.addPass(
new THREE_ADDONS.SMAAPass(
game.getGameResolutionWidth(),
game.getGameResolutionHeight()
game.getRenderingResolutionWidth(),
game.getRenderingResolutionHeight()
)
);
}
@@ -304,9 +304,12 @@ namespace gdjs {
this._threePlaneTexture = texture;
this._threePlaneTexture.generateMipmaps = false;
const scaleMode = this._layer
.getRuntimeScene()
.getGame()
.getScaleMode();
const filter =
this._layer.getRuntimeScene().getGame().getScaleMode() ===
'nearest'
scaleMode === 'nearest' || scaleMode === 'magnified'
? THREE.NearestFilter
: THREE.LinearFilter;
this._threePlaneTexture.minFilter = filter;
@@ -501,8 +504,8 @@ namespace gdjs {
if (this._threeEffectComposer) {
const game = this._layer.getRuntimeScene().getGame();
this._threeEffectComposer.setSize(
game.getGameResolutionWidth(),
game.getGameResolutionHeight()
game.getRenderingResolutionWidth(),
game.getRenderingResolutionHeight()
);
}
}

View File

@@ -100,16 +100,16 @@ namespace gdjs {
this._threeRenderer.useLegacyLights = true;
this._threeRenderer.autoClear = false;
this._threeRenderer.setSize(
this._game.getGameResolutionWidth(),
this._game.getGameResolutionHeight()
this._game.getRenderingResolutionWidth(),
this._game.getRenderingResolutionHeight()
);
// Create a PixiJS renderer that use the same GL context as Three.js
// so that both can render to the canvas and even have PixiJS rendering
// reused in Three.js (by using a RenderTexture and the same internal WebGL texture).
this._pixiRenderer = new PIXI.Renderer({
width: this._game.getGameResolutionWidth(),
height: this._game.getGameResolutionHeight(),
width: this._game.getRenderingResolutionWidth(),
height: this._game.getRenderingResolutionHeight(),
view: gameCanvas,
// @ts-ignore - reuse the context from Three.js.
context: this._threeRenderer.getContext(),
@@ -124,8 +124,8 @@ namespace gdjs {
// "preserveDrawingBuffer: true" is needed to avoid flickering
// and background issues on some mobile phones (see #585 #572 #566 #463).
this._pixiRenderer = PIXI.autoDetectRenderer({
width: this._game.getGameResolutionWidth(),
height: this._game.getGameResolutionHeight(),
width: this._game.getRenderingResolutionWidth(),
height: this._game.getRenderingResolutionHeight(),
view: gameCanvas,
preserveDrawingBuffer: true,
antialias: false,
@@ -272,20 +272,20 @@ namespace gdjs {
// There is no "smart" resizing to be done here: the rendering of the game
// should be done with the size set on the game.
if (
this._pixiRenderer.width !== this._game.getGameResolutionWidth() ||
this._pixiRenderer.height !== this._game.getGameResolutionHeight()
this._pixiRenderer.width !== this._game.getRenderingResolutionWidth() ||
this._pixiRenderer.height !== this._game.getRenderingResolutionHeight()
) {
// TODO (3D): It might be useful to resize pixi view in 3D depending on FOV value
// to enable a mode where pixi always fills the whole screen.
this._pixiRenderer.resize(
this._game.getGameResolutionWidth(),
this._game.getGameResolutionHeight()
this._game.getRenderingResolutionWidth(),
this._game.getRenderingResolutionHeight()
);
if (this._threeRenderer) {
this._threeRenderer.setSize(
this._game.getGameResolutionWidth(),
this._game.getGameResolutionHeight()
this._game.getRenderingResolutionWidth(),
this._game.getRenderingResolutionHeight()
);
}
}
@@ -295,8 +295,8 @@ namespace gdjs {
// only, so won't create visual artifacts during the rendering.
const isFullPage =
this._forceFullscreen || this._isFullPage || this._isFullscreen;
let canvasWidth = this._game.getGameResolutionWidth();
let canvasHeight = this._game.getGameResolutionHeight();
let canvasWidth = this._game.getRenderingResolutionWidth();
let canvasHeight = this._game.getRenderingResolutionHeight();
let maxWidth = window.innerWidth - this._marginLeft - this._marginRight;
let maxHeight = window.innerHeight - this._marginTop - this._marginBottom;
if (maxWidth < 0) {
@@ -523,10 +523,10 @@ namespace gdjs {
// Handle the fact that the game is stretched to fill the canvas.
pageCoords[0] =
(canvasCoords[0] * this._canvasWidth) /
this._game.getGameResolutionWidth();
this._game.getRenderingResolutionWidth();
pageCoords[1] =
(canvasCoords[1] * this._canvasHeight) /
this._game.getGameResolutionHeight();
this._game.getRenderingResolutionHeight();
return pageCoords;
}

View File

@@ -275,6 +275,12 @@ namespace gdjs {
pixiRenderer.render(this._pixiContainer, {
clear: this._runtimeScene.getClearCanvas(),
});
const debugContainer = this._runtimeScene
.getDebuggerRenderer()
.getRendererObject();
if (debugContainer) {
pixiRenderer.render(debugContainer, { clear: false });
}
this._layerRenderingMetrics.rendered2DLayersCount++;
}

View File

@@ -141,7 +141,7 @@ namespace gdjs {
_originalHeight: float;
_resizeMode: 'adaptWidth' | 'adaptHeight' | string;
_adaptGameResolutionAtRuntime: boolean;
_scaleMode: 'linear' | 'nearest';
_scaleMode: ScaleMode;
_pixelsRounding: boolean;
_antialiasingMode: 'none' | 'MSAA';
_isAntialisingEnabledOnMobile: boolean;
@@ -158,6 +158,11 @@ namespace gdjs {
* When set to true, the scenes are notified that game resolution size changed.
*/
_notifyScenesForGameResolutionResize: boolean = false;
/**
* When set to true, the scenes are notified that game zoom factor changed.
*/
_notifyScenesForGameZoomFactorChange: boolean = false;
_zoomFactor: number = 1;
/**
* When paused, the game won't step and will be freezed. Useful for debugging.
@@ -551,21 +556,45 @@ namespace gdjs {
}
/**
* Get the game resolution (the size at which the game is played and rendered) width.
* Get the game resolution width for events.
* @returns The game resolution width, in pixels.
*/
getGameResolutionWidth(): float {
return this._gameResolutionWidth / this._zoomFactor;
}
/**
* Get the game resolution height for events.
* @returns The game resolution height, in pixels.
*/
getGameResolutionHeight(): float {
return this._gameResolutionHeight / this._zoomFactor;
}
/**
* Get the game resolution width (the size at which the game is rendered).
* @returns The game resolution width, in pixels.
*/
getRenderingResolutionWidth(): float {
return this._gameResolutionWidth;
}
/**
* Get the game resolution (the size at which the game is played and rendered) height.
* Get the game resolution height (the size at which the game is rendered).
* @returns The game resolution height, in pixels.
*/
getGameResolutionHeight(): float {
getRenderingResolutionHeight(): float {
return this._gameResolutionHeight;
}
/**
* The scale is usually near 1 unless the 'magnified' scale mode is used.
* @returns the factor between game resolution size and rendering resolution size.
*/
getZoomFactor() {
return this._zoomFactor;
}
/**
* Change the game resolution.
*
@@ -577,17 +606,16 @@ namespace gdjs {
this._gameResolutionWidth = width;
this._gameResolutionHeight = height;
if (this._adaptGameResolutionAtRuntime) {
if (
gdjs.RuntimeGameRenderer &&
gdjs.RuntimeGameRenderer.getWindowInnerWidth &&
gdjs.RuntimeGameRenderer.getWindowInnerHeight
) {
const windowInnerWidth =
gdjs.RuntimeGameRenderer.getWindowInnerWidth();
const windowInnerHeight =
gdjs.RuntimeGameRenderer.getWindowInnerHeight();
if (
gdjs.RuntimeGameRenderer &&
gdjs.RuntimeGameRenderer.getWindowInnerWidth &&
gdjs.RuntimeGameRenderer.getWindowInnerHeight
) {
const windowInnerWidth = gdjs.RuntimeGameRenderer.getWindowInnerWidth();
const windowInnerHeight =
gdjs.RuntimeGameRenderer.getWindowInnerHeight();
if (this._adaptGameResolutionAtRuntime) {
// Enlarge either the width or the eight to fill the inner window space.
if (this._resizeMode === 'adaptWidth') {
this._gameResolutionWidth =
@@ -614,6 +642,35 @@ namespace gdjs {
}
}
}
if (
this._scaleMode === 'magnified' &&
this._gameResolutionWidth > 0 &&
this._gameResolutionHeight > 0 &&
// Fall back on linear if magnified is used on a high resolution game.
this._originalWidth <= 960 &&
this._originalHeight <= 540
) {
const pixelSize = Math.max(
1,
Math.ceil(
this._zoomFactor *
Math.min(
windowInnerWidth / this._gameResolutionWidth,
windowInnerHeight / this._gameResolutionHeight
)
)
);
this._gameResolutionWidth = Math.round(
(this._gameResolutionWidth * pixelSize) / this._zoomFactor
);
this._gameResolutionHeight = Math.round(
(this._gameResolutionHeight * pixelSize) / this._zoomFactor
);
if (this._zoomFactor !== pixelSize && pixelSize >= 1) {
this._zoomFactor = pixelSize;
this._notifyScenesForGameZoomFactorChange = true;
}
}
}
// Don't alter the game resolution. The renderer
@@ -682,9 +739,9 @@ namespace gdjs {
}
/**
* Return the scale mode of the game ("linear" or "nearest").
* Return the scale mode of the game ("linear", "magnified" or "nearest").
*/
getScaleMode(): 'linear' | 'nearest' {
getScaleMode(): ScaleMode {
return this._scaleMode;
}
@@ -962,6 +1019,10 @@ namespace gdjs {
this._sceneStack.onGameResolutionResized();
this._notifyScenesForGameResolutionResize = false;
}
if (this._notifyScenesForGameZoomFactorChange) {
this._sceneStack.onGameZoomFactorChanged();
this._notifyScenesForGameZoomFactorChange = false;
}
// Render and step the scene.
if (this._sceneStack.step(elapsedTime)) {

View File

@@ -658,6 +658,11 @@ namespace gdjs {
*/
onSceneResumed(runtimeScene: gdjs.RuntimeScene): void {}
/**
* Called when the game zoom factor is changed to adapt to a new resolution.
*/
onGameZoomFactorChanged(): void {}
//Rendering:
/**
* @return The internal object for a 2D rendering (PIXI.DisplayObject...)

View File

@@ -34,6 +34,15 @@ namespace gdjs {
}
}
/**
* Called when the game zoom factor is changed to adapt to a new resolution.
*/
onGameZoomFactorChanged(): void {
for (let i = 0; i < this._stack.length; ++i) {
this._stack[i].onGameZoomFactorChanged();
}
}
step(elapsedTime: float): boolean {
this._throwIfDisposed();
if (this._isNextLayoutLoading || this._stack.length === 0) {

View File

@@ -329,13 +329,15 @@ declare interface EffectNetworkSyncData {
};
}
declare type ScaleMode = 'linear' | 'magnified' | 'nearest';
declare interface ProjectPropertiesData {
adaptGameResolutionAtRuntime: boolean;
folderProject: boolean;
orientation: string;
packageName: string;
projectFile: string;
scaleMode: 'linear' | 'nearest';
scaleMode: ScaleMode;
pixelsRounding: boolean;
antialiasingMode: 'none' | 'MSAA';
antialisingEnabledOnMobile: boolean;

View File

@@ -129,7 +129,9 @@ export const installResource = (
if (newResource.getKind() === 'image') {
// $FlowExpectedError[prop-missing] - We know the resource is an ImageResource and has the setSmooth method.
newResource.setSmooth(
project.getScaleMode() !== 'nearest' && !isPixelArt(asset)
project.getScaleMode() !== 'nearest' &&
project.getScaleMode() !== 'magnified' &&
!isPixelArt(asset)
);
}

View File

@@ -655,7 +655,8 @@ export default class LayerRenderer {
threePlaneTexture.generateMipmaps = false;
const filter =
this.project.getScaleMode() === 'nearest'
this.project.getScaleMode() === 'nearest' ||
this.project.getScaleMode() === 'magnified'
? THREE.NearestFilter
: THREE.LinearFilter;
threePlaneTexture.minFilter = filter;

View File

@@ -729,11 +729,15 @@ const ProjectPropertiesDialog = (props: Props) => {
>
<SelectOption
value="linear"
label={t`Linear (antialiased rendering, good for most games)`}
label={t`Linear (smooth, good for most games)`}
/>
<SelectOption
value="magnified"
label={t`Magnified (sharp, good for modern pixel-art games)`}
/>
<SelectOption
value="nearest"
label={t`Nearest (no antialiasing, good for pixel perfect games)`}
label={t`Nearest (aliased, good for pixel perfect games)`}
/>
</SelectField>
<Checkbox
@@ -749,7 +753,7 @@ const ProjectPropertiesDialog = (props: Props) => {
notifyOfChange();
}}
/>
{scaleMode === 'nearest' && (
{(scaleMode === 'nearest' || scaleMode === 'magnified') && (
<DismissableAlertMessage
identifier="use-non-smoothed-textures"
kind="info"

View File

@@ -154,7 +154,10 @@ export const applyResourceDefaults = (
newResource: gdResource
) => {
if (newResource instanceof gd.ImageResource) {
newResource.setSmooth(project.getScaleMode() !== 'nearest');
newResource.setSmooth(
project.getScaleMode() !== 'nearest' &&
project.getScaleMode() !== 'magnified'
);
}
};