How to maintain Aspect Ratio
by Lenny Urbanowski · in Torque 2D Beginner · 08/27/2013 (4:38 pm) · 12 replies
Is there a way to make the Scene or SceneWindow (or even just the sprites) maintain their aspect ratio?
I'm brand new to T2D and I'm hoping to produce responsive / cross-platform games with minimal per-platform adjustments.
What I want to do is be able to resize the window in windowed mode to a narrow or wide size and have the game respond appropriately. Currently when I resize the window all of the game objects scale with no regard for aspect ratio of the objects or the scene. So for example my scene has a bunch of squares and when I resize I wind up with rectangles.
What I would like is for the scene (background image mostly) to center and scale (proportionately) so that it is larger than the window and cropped off (on the sides on portrait and on top and bottom on landscape). Then I would resize the game play elements (again proportionately) to fit within the window. This would cause the background image to fill the entire screen (no letter box) and not distort (maintain aspect ratio) and the game play elements to fit within the screen (with margin around it that varies depending on screen size).
Most importantly none of the object should scale in a way that distorts their aspect ratio.
So, my specific Questions...
Is there by any weird chance a simple way to do this in the engine? (wishful thinking I assume)
Is it possible to layer 2 scenes? Then I could have the background images in one scene and the game objects in the other and scale the whole screen with some basic math?
How do I tell the Scene (or SceneWindow not sure which) to not scale to fill the whole window automatically on resize?
And where would I put the code to handle the scaling myself? (eg is there a OnResize method or something?)
I searched the forum for a while and found some similar complaints, but not really anyone trying to do exactly what I'm doing.
Thanks in advance for any help.
Lenny
I'm brand new to T2D and I'm hoping to produce responsive / cross-platform games with minimal per-platform adjustments.
What I want to do is be able to resize the window in windowed mode to a narrow or wide size and have the game respond appropriately. Currently when I resize the window all of the game objects scale with no regard for aspect ratio of the objects or the scene. So for example my scene has a bunch of squares and when I resize I wind up with rectangles.
What I would like is for the scene (background image mostly) to center and scale (proportionately) so that it is larger than the window and cropped off (on the sides on portrait and on top and bottom on landscape). Then I would resize the game play elements (again proportionately) to fit within the window. This would cause the background image to fill the entire screen (no letter box) and not distort (maintain aspect ratio) and the game play elements to fit within the screen (with margin around it that varies depending on screen size).
Most importantly none of the object should scale in a way that distorts their aspect ratio.
So, my specific Questions...
Is there by any weird chance a simple way to do this in the engine? (wishful thinking I assume)
Is it possible to layer 2 scenes? Then I could have the background images in one scene and the game objects in the other and scale the whole screen with some basic math?
How do I tell the Scene (or SceneWindow not sure which) to not scale to fill the whole window automatically on resize?
And where would I put the code to handle the scaling myself? (eg is there a OnResize method or something?)
I searched the forum for a while and found some similar complaints, but not really anyone trying to do exactly what I'm doing.
Thanks in advance for any help.
Lenny
#2
There is no "viewport" object or value that I'm seeing so by viewport do you mean SceneWindow or Scene or Camera?
Is there a OnResize trigger / listener somewhere? I could check the size and adjust in a timer but I'd rather not if there is an event for this already.
08/27/2013 (5:54 pm)
How? Where? When?There is no "viewport" object or value that I'm seeing so by viewport do you mean SceneWindow or Scene or Camera?
Is there a OnResize trigger / listener somewhere? I could check the size and adjust in a timer but I'd rather not if there is an event for this already.
#3
I don't think there are event for this already, but i could be wrong, at least i did not see any at a quick glance.
08/27/2013 (6:25 pm)
Good question, on windows this event get sent as a WM_SIZE to the WindProc, by tracing that you can see that the current resolution get written to $pref::Video::windowedResWhich can easily be read from TS, so you can find the current resolution from there or you could hook yourself into
Platform::setWindowSize(Platform specific) to catch a window resizing event and modify your camera scale accordingly. Other function that might interest you to deal with that case are
SceneWindow::calculateCameraViewand
SceneWindow::onRenderwere the normal camera adjustment happen.
I don't think there are event for this already, but i could be wrong, at least i did not see any at a quick glance.
#4
This occurs automatically when the window is resized and also happens once when the game starts.
Why it isn't called onResize (like many other GuiControls) I have no idea.
08/27/2013 (7:12 pm)
@Lenny : There is a script callback for the scenewindow resizingfunction SceneWindow::onExtentChange(%this,%posx, %posy, %extentx, %extenty)
{
...
}This occurs automatically when the window is resized and also happens once when the game starts.
Why it isn't called onResize (like many other GuiControls) I have no idea.
#5
@Simon...
I couldn't find this callback in the documentation and was starting to dig through the C++ source looking for something that looked right half expecting to need to add my own callback.
Is there somewhere these types of callbacks are documented? ... or do you have any advise on any tool or search terms to try to find them in the engine source?
thanks.
08/27/2013 (7:32 pm)
Thanks for the help. Looks like this will work. I'll post the full solution back when it's done incase anyone else is looking to do something similar.@Simon...
I couldn't find this callback in the documentation and was starting to dig through the C++ source looking for something that looked right half expecting to need to add my own callback.
Is there somewhere these types of callbacks are documented? ... or do you have any advise on any tool or search terms to try to find them in the engine source?
thanks.
#6
08/27/2013 (7:45 pm)
Hah! There you go, this happen during the GuiCanvas frame render, so you will always get your event at least a frame after the resize already happened, probably no biggy in most case, but worth noting.
#7
Unfortunately there is no documented callback list. Definitely something for the documentation to-do list. A community member started one but it's very unfinished:
www.garagegames.com/community/forums/viewthread/133908
To find callbacks in the source, search for the term Con::executef
There is also the beginnings of a callback system refactor on Github, but Charlie's been busy with other things and I don't think this made the cut for 3.0.
github.com/GarageGames/Torque2D/tree/callbacks
08/27/2013 (10:25 pm)
@LennyUnfortunately there is no documented callback list. Definitely something for the documentation to-do list. A community member started one but it's very unfinished:
www.garagegames.com/community/forums/viewthread/133908
To find callbacks in the source, search for the term Con::executef
There is also the beginnings of a callback system refactor on Github, but Charlie's been busy with other things and I don't think this made the cut for 3.0.
github.com/GarageGames/Torque2D/tree/callbacks
#8
08/28/2013 (2:26 am)
To answer one of the other questions in the OP:Quote:Is it possible to layer 2 scenes? Then I could have the background images in one scene and the game objects in the other and scale the whole screen with some basic math?It is not possible to have more than 1 scene assigned to a SceneWindow. It is possible to layer multiple SceneWindows on top of each other though. The MultiWindow toy provides an example of how that is done.
#9
What type of object is %this in onExtentChange?
How can I see what SceneWindow triggered the call?
What I have is 2 scene windows
mySceneWindow is the main scene window and it resizes to match the container window automatically.
mySceneWindow2 contains the main game scene and is resized proportionately based on the size of mySceneWindow
This works fine, but results in an infinite loop because onExtentChange is triggered by ANY SceneWindow. I need to compare %this to mySceneWindow and only resize mySceneWindow2 if mySceneWindow has changed sizes. Currently I'm using the extent of the main window to avoid the loop and it's working fine, but is dirty and I want to do it correct.
My code...
Please answer the following and I should be in good shape...
Please answer the first part even if the behavior idea makes more sense. I feel that learning how to trace a callback to the responsible object and comparing it to existing objects seems like an important thing to learn.
thanks,
Lenny
08/28/2013 (3:21 pm)
Thanks for all the help. I'm almost there but hit one more snag...What type of object is %this in onExtentChange?
How can I see what SceneWindow triggered the call?
What I have is 2 scene windows
mySceneWindow is the main scene window and it resizes to match the container window automatically.
mySceneWindow2 contains the main game scene and is resized proportionately based on the size of mySceneWindow
This works fine, but results in an infinite loop because onExtentChange is triggered by ANY SceneWindow. I need to compare %this to mySceneWindow and only resize mySceneWindow2 if mySceneWindow has changed sizes. Currently I'm using the extent of the main window to avoid the loop and it's working fine, but is dirty and I want to do it correct.
My code...
function SceneWindow::onExtentChange(%this,%posx, %posy, %extentx, %extenty)
{
//if the SceneWindow objects aren't established thou shall not pass
if(!isObject(mySceneWindow) || !isObject(mySceneWindow2)) return;
// avoid infinite resize loop. I'd rather compare the objects than the values
if(%this.getExtent() $= mySceneWindow.getExtent())
{
echo("doing resize!!!!!!!");
%extX=getWord(mySceneWindow.getExtent(),0);
%extY=getWord(mySceneWindow.getExtent(),1);
%aspect=4/3;
if(%extX > %extY)
{
%tExtY=%extY;
%tExtX=%tExtY * %aspect;
}else{
%tExtX=%extX;
%tExtY=%extX / %aspect;
}
mySceneWindow2.setExtent(%tExtX, %tExtY);
mySceneWindow2.setCenter(%extX/2,%extY/2);
}
}Please answer the following and I should be in good shape...
- How can I compare %this to mySceneWindow to see if onExtentChange is triggered by mySceneWindow? (I tried %this == mySceneWindow and %this.owner but neither worked)
- Could / should this be accomplished with a behavior attached to mySceneWindow
Please answer the first part even if the behavior idea makes more sense. I feel that learning how to trace a callback to the responsible object and comparing it to existing objects seems like an important thing to learn.
thanks,
Lenny
#10
I can do most of the SceneWindow calls on %this, but if I output it with echo I get a 4 digit number. If I compare (==) it directly to mySceneWindow it is always false.
08/28/2013 (3:27 pm)
Extra note... I can do most of the SceneWindow calls on %this, but if I output it with echo I get a 4 digit number. If I compare (==) it directly to mySceneWindow it is always false.
#11
You could also have two separate callbacks for each SceneWindow.
In regards to behaviors, only the Scene and any classes derived from SceneObject can use them. Since the SceneWindow is a GuiControl, it is not behavior enabled.
08/29/2013 (2:25 am)
There's a couple ways to approach this. You can have a generic SceneWindow callback like the code you posted above, and in the callback figure out which SceneWindow triggered the callback. If you gave your windows a name, which you did, you can sort them by name or by object id, which is the 4 digit number you also saw. function SceneWindow::onAdd(%this)
{
%name = %this.getName();
%id = %this.getId();
if (%name $= "mySceneWindow")
echo("name matches");
if (%id = mySceneWindow.getId())
echo("id matches");
}You could also have two separate callbacks for each SceneWindow.
function mySceneWindow::onExtentChange(%this, .....)
{
}
function mySceneWindow2::onExtentChange(%this, .....)
{
}In regards to behaviors, only the Scene and any classes derived from SceneObject can use them. Since the SceneWindow is a GuiControl, it is not behavior enabled.
#12
I wrote a custom setGameResolution() to keep my background from being stretched out.
I Looked at your code and changed it to be similar to my setGameResolution() function, you may try this:
This code assumes camera size 100 75. (some TOYS asset use 40 30 and the code below may not work for them)
NOTE: if you use more then one SceneWindow, you'll want to check %this.getName()$= "mymainwindow" !
11/07/2014 (1:31 pm)
Hi Lenny, I had the same problem, and I started a new thread. Someone pointed me here.I wrote a custom setGameResolution() to keep my background from being stretched out.
I Looked at your code and changed it to be similar to my setGameResolution() function, you may try this:
This code assumes camera size 100 75. (some TOYS asset use 40 30 and the code below may not work for them)
NOTE: if you use more then one SceneWindow, you'll want to check %this.getName()$= "mymainwindow" !
function SceneWindow::onExtentChange(%this, %posX, %posY, %ExtentX, %ExtentY) {
// %ExtentX, %ExtentY doesn't pass down sometimes, get them manually...
%ExtentX = getWord(%this.getExtent(),0);
%ExtentY = getWord(%this.getExtent(),1);
// Calculate aspect ratio
%camX = 100;
%AspectRatio = %ExtentX / %ExtentY;
// height
%camY = %camX / %AspectRatio;
//echo( %AspectRatio SPC %camX SPC %camY );
// adjust camera size for box or wide screen
%this.setCameraSize( %camX, %camY );
}
Jonathan Arsenault