Hi everyone,
I still have a problem with how to make a certain app design fit nicely on all screen size.
I read a post long ago that the pixel density was a issue as it differs from mobile to mobile.
But now what is the trick around it?
My guess would be listen to the resize even, but the way I do it takes too much time to the app to make the design(about 4min) which means I don’t know how to use it yet.
Sorry I do not have an answer for you, rather I am very much interested in this as well. I am wondering if there is a buffer scaling option like just resizing the whole content of the window instead of me writing a loop to go through to all object instances and rescale them manually which can be a bit tricky on different devices.
Could you elaborate? Or provide a screenshot?
In my own game, I do it the slow way (recreate everything from scratch), and it’s still almost instant.
Actually, is it possible that you’re getting multiple RESIZE
events when the stage isn’t actually changing size? For instance, rotating your phone can trigger a resize event (I think), but the stage size won’t actually change.
Try setting it up like this:
private var oldStageWidth:Int = 0;
private var oldStageHeight:Int = 0;
public function new() {
//...
stage.addEventListener(Event.RESIZE, onResize);
}
private function onResize(e:Event):Void {
if(stage.stageWidth == oldStageWidth && stage.stageHeight == oldStageHeight) {
return;
}
oldStageWidth = stage.stageWidth;
oldStageHeight = stage.stageHeight;
//Resize...
}
You may be able to get away with using stage.scaleMode
, but it offers less control, and if you use it, you can’t use stage.stageWidth
or stage.stageHeight
.
I am sorry what I have said may have sounded confusing.
it is not the way I use the resize event that takes 4 min but the way I design my app.
I simply store the stage.stageWidth and stage.stageHeight and get its 1%, 5% and 10% value then store them inside some variables, and use them for setting x,y width and height value of anything on the stage.
yeah I know… beginners’ way…
but my thought at that time was "the app will look exactly the same no matter where it is run and no matter what pixels density."
But it takes so much time that it is not practicable(I almost fell asleep while waiting for my program to start).
That still sounds like what I do. Nothing you described should take 4 minutes, unless you’re displaying hundreds of thousands of sprites.
Have you tested the app with the width/height code (and only the width/height code) commented out? If that’s really the cause, commenting it out will fix the issue. (Edit: by “fix the issue” all I mean is “make it not lag.” I’m not saying that it’s an actual solution.)
Yes I had tried that at the very beginning(somewhere around last October), I used pixel size for width/height and it took a less time to run the app… Like it is obvious - no calculation for getting a size before setting it.
But the problem with pixels size is: it won’t be the same because of pixels density.
I totally had made myself believe:
- “it was too much calculation for my PC” because on a i7 it takes half of a second.
- "I don’t understand OpenFL at all"
Now that I am working on the fake 3D racing game, where I have hundreds of calculation made each seconds and it renders properly, I do not understand.
Ok, could you post the code that’s causing all the lag?
Do you work with width/height or scaleX/Y when resizing it in the event callback? The reason I am asking is to figure out the best way. Do I have to worrry about other transformations when scaling for the new stae size?
Btw stage.scaleMode produces emtpy screen on Android, but works on Linux.
Assuming your app has a hard-coded native resolution:
Listen for the screen resize event
Measure the aspect ratio of the current device window (stageWidth/stageHeight).
create a new container that has the dimensions of your native resolution, but stretch the height or width to match the same aspect ratio as the device window. Note that we are just matching the aspect ratio for now, not blowing up to the full screen size.
fill the container with an empty black bitmap
center your entire game inside of this container (this also assumes that your game is contained within its own sub-container).
THEN, just scale that container upward/downward to match exact width and height of the device’s window. because your game is nested inside of the container, it will maintain your native aspect ratio while expanding to fit the device window.
I am targeting Android and at this point I feel like probably the best way is to create the sprites and images with the proper aspect ratio and the placements from the get go. This resizing afterwards sounds more like a desktop way. Am I wrong on this when it comes to mobile? Get the aspect ratio and use it as a leverage to place things properly in the beginning (working with percentages rather than absolute coords) unless one does a bitmap scale as @afroninjadotcom described, at least that is what I understood from his post.
@afroninjadotcom
Please correct me if I misunderstood your points.
Create a new bitmap in the main sprite, copy the scaled current bitmap rendered in the current sprite to the new empty bitmap. And remove the old bitmap.
I implement the above logic on all major targets- flash, desktop, android, and ios.
Don’t get me wrong there are certainly advantages to targeting different resolutions in a dynamic fashion but it’s always been a complete headache for me. I think it applies to functional apps, but for games it’s usually easier to just scale your entire output accordingly (just my opinion though)
I don’t copy any bitmaps- I place my entire game inside of a sprite, then place that sprite into an adjustment container. I then scale the adjustment container to fit the desired resolution.
AfroNinja: what’s the difference between your implementation and StageScaleMode.SHOW_ALL
? The only difference I can find is that your implementation would let you center it. And even then it would be easier to make a workaround to center it:
//Insert your values here:
private static inline var NOMINAL_WIDTH:Int = 800;
private static inline var NOMINAL_HEIGHT:Int = 600;
public function new() {
super();
//...
stage.scaleMode = StageScaleMode.SHOW_ALL;
#if flash
stage.align = untyped "";
#else
stage.addEventListener(Event.RESIZE, onResize);
#end
}
private function onResize(e:Event):Void {
var stageScaleX:Float = stage.stageWidth / NOMINAL_WIDTH;
var stageScaleY:Float = stage.stageHeight / NOMINAL_HEIGHT;
var stageScale:Float = Math.min(stageScaleX, stageScaleY);
Lib.current.x = 0;
Lib.current.y = 0;
if(stageScaleX > stageScaleY) {
Lib.current.x = (stage.stageWidth - NOMINAL_WIDTH * stageScale) / 2;
} else {
Lib.current.y = (stage.stageHeight - NOMINAL_HEIGHT * stageScale) / 2;
}
}
I’ve confirmed that this behaves as expected on Flash, Neko, and Windows.
It’s possible that the OpenFL “next” will provide a StageAlign.CENTER
property, making this even easier.
I guess nothing, I didn’t realize it would be that easy to center with the SHOW_ALL value
also didn’t realize you could move the stage itself with Lib.current.x/y.
It’s not the actual stage. Lib.current
is a movie clip created and managed by OpenFL, and your Main
class is automatically added as a child of it (except in Flash).
So unless you add children directly to the stage, Lib.current
can serve as a parent of everything in the display list.
@player_03
I tried using StageScaleMode for Android but it gives me a blank screen, but it works on Linux Desktop. Maybe not supported on Android?
@afroninjadotcom
Do you have a basic code to show how you do it? I have just started using OpenFL (and no prior as experience), so I do not know all the nitty gritty details of OFL at the moment.
thanks
Ok, then I guess you can try it AfroNinja’s way:
//Insert your values here:
private static inline var NOMINAL_WIDTH:Int = 800;
private static inline var NOMINAL_HEIGHT:Int = 600;
public function new() {
super();
//...
#if flash
stage.scaleMode = StageScaleMode.SHOW_ALL;
stage.align = untyped "";
#else
stage.addEventListener(Event.RESIZE, onResize);
onResize(null);
#end
}
private function onResize(e:Event):Void {
var stageScaleX:Float = stage.stageWidth / NOMINAL_WIDTH;
var stageScaleY:Float = stage.stageHeight / NOMINAL_HEIGHT;
var stageScale:Float = Math.min(stageScaleX, stageScaleY);
Lib.current.x = 0;
Lib.current.y = 0;
Lib.current.scaleX = stageScale;
Lib.current.scaleY = stageScale;
if(stageScaleX > stageScaleY) {
Lib.current.x = (stage.stageWidth - NOMINAL_WIDTH * stageScale) / 2;
} else {
Lib.current.y = (stage.stageHeight - NOMINAL_HEIGHT * stageScale) / 2;
}
}
I should note that I’m testing in OpenFL 2.2.8, not OpenFL 3.
I have no idea but this is segfaulting on Linux ;(
I think it is something about this portion
if(stageScaleX > stageScaleY) {
Lib.current.x = (stage.stageWidth - FRAME_NOMINAL_WIDTH*stageScale)/2;
} else {
Lib.current.y = (stage.stageHeight - FRAME_NOMINAL_HEIGHT*stageScale)/2;
}
progress: I have some actual text on the images, so removing them stopped the segfault which is weird. But now changing the size turns the window into an empty screen.
progress_2:
ok now it seems like all the values are returning inf
Main.hx:115: 1,1
Main.hx:157: resizing,inf,inf,inf (stageScale, stageScaleX,stageScaleY)
bizarre I will infestigate more
I doubt it’s that part, because that was there before and you said it worked on Linux.
Try commenting this part instead:
Lib.current.scaleX = stageScale;
Lib.current.scaleY = stageScale;
If that doesn’t change anything, try adding these two haxedefs to help debug the segfault:
<haxedef name="HXCPP_STACK_LINE" />
<haxedef name="HXCPP_CHECK_POINTER" />
If the scale values are infinity, that suggests that NOMINAL_WIDTH
and NOMINAL_HEIGHT
are 0.
Sorry about the fuss, I had 2 issues. I had to initiate the nominals and then cleaning and making a fresh build of linux target solved the segfault. No idea I seem to get segs here here there and clean build solves that issue.
Your solution is perfect for me for now, Thanks again for your dedicated solution.