HaxeFlixel / flixel

Free, cross-platform 2D game engine powered by Haxe and OpenFL
https://haxeflixel.com/
MIT License
1.96k stars 432 forks source link

HaxeFlixel windowed games doesn't play well with Windows Display Scale #2578

Open NQNStudios opened 2 years ago

NQNStudios commented 2 years ago

I use a laptop with a smallish screen of 1920x1080 resolution. Windows provides a system setting, System -> Display -> Scale, which I set to 150% and this makes most apps and their text bigger so my eyes don't get strained reading tiny text. I don't fully understand everything this setting does, but I know it makes working on this laptop much more comfortable.

But it creates a problem when I launch my HaxeFlixel projects, which request 1280x720 window size in Project.xml. The Windows setting is scaling the window by 150% so it creates a 1920x1080 game window. I think the game graphics are scaling too, so this would be fine, but the 1920x1080 game window takes my whole screen, and the window border with the minimize and close buttons, is pushed off the top of my screen and I actually can't click any buttons or even drag the window to make the buttons accessible.

I don't know what exactly I want HaxeFlixel to do about this. Just ignoring the 150% scale setting doesn't seem quite right, although it would solve the problem as long as I implement my own resolution and scaling choices for players. Maybe at least, bumping the window lower so the border is visible and accessible on my screen, and the bottom of my game window is clipped instead, so I at least am not forced to Alt+F4 or Ctrl+Alt+Del? Or if I just knew a way to work around this problem myself without changing flixel, that would be great too.

Thanks!

EDIT: This behavior is consistent on Neko, and goes away when I change display scale to 100%. I'm currently testing it with the cpp target to see if it's Neko-specific.

NQNStudios commented 2 years ago

I just found the documentation for ScaleModes and I think it might help me figure out what's going on. I printed the type and fields of the default FlxG.scaleMode, finding that it's a RatioScaleMode:

{
    "deviceSize":{"x":1280,"y":701,"_weak":false,"_inPool":false},
    "gameSize":{"x":1280,"y":701,"_weak":false,"_inPool":false},
    "scale":{"x":1,"y":1,"_weak":false,"_inPool":false},
    "offset":{"x":0,"y":0,"_weak":false,"_inPool":false},
    "horizontalAlign":1,"verticalAlign":1,"fillScreen":false
}

I wonder where deviceSize is coming from? Shouldn't it be 1920x1080, or does Windows pass all programs a smaller device size when you have accessibility scaling on? (I can test that.)

EDIT: when I print the ScaleMode with the Windows setting disabled, I get this:

{
    "deviceSize":{"x":1280,"y":720,"_weak":false,"_inPool":false},
    "gameSize":{"x":1280,"y":720,"_weak":false,"_inPool":false},
    "scale":{"x":1,"y":1,"_weak":false,"_inPool":false},
    "offset":{"x":0,"y":0,"_weak":false,"_inPool":false},
    "horizontalAlign":1,"verticalAlign":1,"fillScreen":false}

which is pretty close, but it gets the full 720 for height. I'm guessing because when windows scales up the window, the height gets pinched to fit.

Bloodninj commented 1 year ago

I'm assuming you're using the Windows export target. To allow your game to see the real resolution as opposed to the scaled virtual resolution Windows supplies to programs that don't declare themselves DPI-aware (or supplied by browsers on the HTML5 target), add the allow-high-dpi="true" attribute to the relevant export in Project.xml, like so:

<window if="desktop" width="640" height="480" allow-high-dpi="true" />

Your project should now see the real pixel dimensions.

The scaling is done by Windows to support older applications (usually pre-Windows 8) which don't declare that they can handle DPI settings, to ensure applications remain usable on higher-resolution displays. Browsers also scale base font and pixel sizes to ensure that you can read and interact with web pages on high-res displays, particularly mobile devices.

NQNStudios commented 1 year ago

I just tried this and it didn't change anything. I don't see allow-high-dpi mentioned in the Lime documentation here: https://lime.openfl.org/docs/project-files/xml-format/

Here's all my window settings:

        <!--These window settings apply to all targets-->
    <window width="1280" height="720" fps="60" background="#000000" hardware="true" vsync="false" />

    <!--HTML5-specific-->
    <window if="html5" resizable="false" />

    <!--Desktop-specific-->
    <window if="desktop" orientation="landscape" fullscreen="false" resizable="true" allow-high-dpi="true" />

    <!--Mobile-specific-->
    <window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" />
Bloodninj commented 1 year ago

Does this also happen on HashLink? IIRC, the Windows executable for HL is already DPI-aware, so it should display at a real 1280x720 window regardless of whether or not you include allow-high-dpi="true". To my understanding, Neko has generally been succeeded by HashLink these days. That allow-high-dpi flag should work though, at least for the CPP target.

What versions of lime/openfl/flixel are you using? Have you tried a clean build, either by deleting your export folder or passing -clean as a build argument, like lime test windows -clean?

NQNStudios commented 1 year ago

Still haven't figured this out.

library versions as of today: lime 8.0.1 openfl 9.2.1 flixel 5.3.0

A clean cpp build on Windows 11 with allow-high-dpi doesn't fix this.

Building with HashLink, my game launches and crashes:

Uncaught exception: Access violation
Called from hl.types.ArrayObj.getDyn(C:\Users\natqu\AppData\Roaming/haxe/versions/4.3.1/std/hl/types/ArrayObj.hx:330)
Called from hl.types.ArrayObj.getDyn(C:\Users\natqu\AppData\Roaming/haxe/versions/4.3.1/std/hl/types/ArrayObj.hx:330)
Called from hl.types.ArrayObj.getDyn(C:\Users\natqu\AppData\Roaming/haxe/versions/4.3.1/std/hl/types/ArrayObj.hx:330)
Called from hl.types.ArrayDyn.getDyn(C:\Users\natqu\AppData\Roaming/haxe/versions/4.3.1/std/hl/types/ArrayDyn.hx:62)
Called from kiss.$Prelude._or(kiss/Prelude.hx:78)
Called from $Reflect.~_makeVarArgs.0(C:\Users\natqu\AppData\Roaming/haxe/versions/4.3.1/std/hl/_std/Reflect.hx:139)
Called from $Reflect.callMethod(C:\Users\natqu\AppData\Roaming/haxe/versions/4.3.1/std/hl/_std/Reflect.hx:83)
Called from kiss_flixel.SimpleWindow.update(kiss_flixel/SimpleWindow.kiss:273)
Called from flixel.group.FlxTypedGroup.update(flixel/group/FlxGroup.hx:170)
Called from flixel.FlxState.tryUpdate(flixel/FlxState.hx:220)
Called from flixel.FlxGame.update(flixel/FlxGame.hx:752)
Called from flixel.FlxGame.step(flixel/FlxGame.hx:682)
Called from flixel.FlxGame.onEnterFrame(flixel/FlxGame.hx:550)
Called from openfl.display.DefaultPreloader.~this_onComplete.1(lime/app/Module.hx:0)
Called from openfl.events.EventDispatcher.__dispatchEvent(openfl/events/EventDispatcher.hx:402)
Called from openfl.display.DisplayObject.__dispatch(openfl/display/DisplayObject.hx:1399)
Called from openfl.display.Stage.__broadcastEvent(openfl/display/Stage.hx:1181)
Called from openfl.display.Stage.__onLimeRender(openfl/display/Stage.hx:2046)
Called from lime.app._Event_lime_graphics_RenderContext_Void.dispatch(lime/_internal/macros/EventMacro.hx:91)
Called from lime._internal.backend.native.NativeApplication.handleRenderEvent(lime/_internal/backend/native/NativeApplication.hx:371)
Called from lime._internal.backend.native.NativeApplication.exec(lime/_internal/backend/native/NativeApplication.hx:146)
Called from lime.app.Application.exec(lime/app/Application.hx:150)
Called from $ApplicationMain.create(ApplicationMain.hx:135)
Called from $ApplicationMain.main(ApplicationMain.hx:26)
Called from .init(?:1)

Probably because of something super weird I'm doing. So I might see about fixing the HashLink error, but also this should probably be fixed for c++ targets.