Closed melonjs closed 11 years ago
This would be a great feature!
I'm working on this! :D
i'm thinking i could rewrite the me.video.init
to have 2 more parameters, autoresize
, and autozoom
. So you face with four different scenarios:
just tell me what you think...
@andyveliz I set this ticket to milestone 0.9.5 as part of my wish list for the next version.
A few suggestions:
AutoZoom should also scale the pixel size by the same ratio that it fills the browser, of course, maintaining the same aspect ratio like you said.
AutoResize = true, AutoZoom = true: The canvas should first be AutoZoomed, like above, and then the canvas resized to fill any empty space in the browser.
Here are some images to help visualize. The white space in the browser is NOT part of the canvas. The black space in the browser IS part of the canvas (rendering the melonJS background color, if any).
AutoResize = false, AutoZoom = false:
AutoResize = true, AutoZoom = false:
AutoResize = false, AutoZoom = true:
AutoResize = true, AutoZoom = true:
Great... thanks for the images... that really clarified a lot...
i'll try to submit some code for you to see in the next few weeks... i hope i can help..
Parasyte's images look good and might negate the following suggestion...
It would be great to be able to specify which dimensions are resized.
Example: http://www.freeimagehosting.net/6a8aj
Does anyone have a fork with resizing implemented?
This feature should have a max-width option so that you can be sure the game is never resized larger than it was designed to be. Otherwise the graphics will start to look bad on larger screens when the resolution is greater than the graphics were made at.
For example, for my game, when it is viewed on a screen with a width larger than 1024px the game is going to be centered and have a box-shadow and there will be additional content on the page, e.g., about info, comments, footer, etc. Using media queries I will remove that extra content when the browser width is 1024px or below so that the game can truly expand to fit the screen when viewed on tablets and phones.
It would also be nice to have a feature to force the game to appear in either portrait or landscape mode when viewed on tablets or phones. One tower defense game I play, Tower Madness is always in portrait mode, and another one, Fieldrunners, is always in landscape mode. Designing a game to look right in both modes, including maps and menus, would be very difficult on smaller screens.
There are orientation media queries.
You can rotate content with CSS3.
Hey thanks for all the good ideas!
currently i'm working on this issue, i have just a few non-working test ... :( but i'll commit some code to my fork of melonjs as soon as i can... i'm only working on resizing on the fly the canvas, with the option to maintain the aspect ratio, basically the images that Jason share with us... i think that this could solve the "resizing when changing orientation" issue... i'll be looking to add the other great features you guys wrote about.
Hi guys, i've commited some code to my fork, in the canvas-resize branch, it's a hack making the autoresize and autozoom functions work, check them out... any feedback is appreciated, I hope this can be useful to the main repository.
@greghouston A max[Height|Width] feature is fine. But there are also some other implications to consider when zooming a canvas to larger or smaller than it was designed for.
When zooming the canvas smaller than its native size, you actually _lose quality_ just as much as when the canvas is enlarged. The quality loss is not as obvious when the image is scaled with interpolation, but it comes at a performance cost. In this case, it is more effective to use two (or more) copies of every image in the game, and choose the closest fit when displaying. This is a technique used in 3D rendering for many years, called MipMapping; when a texture is scaled toward 0.5 times its original size, a smaller version of the texture will be used, increasing performance and visual quality.
Likewise on the other end of the spectrum, when enlarging the canvas, you may not want the image scaled with interpolation enabled, to give the game a distinct "pixelated" look. This can be done with CSS using the image-rendering property on the canvas. That affects the entire canvas, but if you also want it to affect sprites that are scaled or rotated within the canvas, you also need to use context.mozImageSmoothingEnabled = false
and context.webkitImageSmoothingEnabled = false
when drawing the sprites.
So maybe that's a third boolean to pass, whether you want image smoothing enabled (default) or disabled?
@andyveliz I looked through the commit in your canvas-resize branch. The most important thing it has to offer is the math, which I haven't studied yet. It looks pretty good at first glance. And some of the new methods are also very helpful. The one piece of criticism I have is how your hack reuses variables, and also uses the me.sys
namespace unnecessarily.
One idea I want to bring up is the possibility of allowing the canvas to properly reflow with other elements in the HTML document, instead of forcing it to the size of the browser viewport. Imagine the body margins have not been set to 0 (the default is always something larger than 0) then you will stretch the browser viewport by making the canvas the same width/height without taking into account the body margin. Imagine if the canvas also had its own margin... Or if the canvas is inside a div element that sits next to a side-navigation menu. Now we hit all kind of complex situations where the canvas cannot be made the same size as the browser viewport.
The ideal solution, I think, is calculating the size of the canvas's parent element content box, and performing one of three operations when autosize OR autozoom are set, where p
is the parent element's content box:
canvas.width = p.width
and canvas.height = p.height
canvas.height = p.height
canvas.width = p.width
Some special care must be taken when p
== document.body, because the body may have a height that is smaller than the browser viewport height. Easy thing to do for solving that may be unconditionally setting document.body.style.minHeight = window.innerHeight
or similar at the top of the window resize event handler.
I know I haven't covered every edge case, but just adding some more details to take into consideration.
@parasyte
You're right!
I haven't tough of that... i'll re write the code to take in cosideration the wrapper
variable. The resize or zoom should be contained within the object you choose in your html, not always using the full screen.
I took the math from CommandoJS, I think it's allright.
I was thinking about the "max-width" option that @greghouston suggested, it could be a problem if you have a big widescreen, in some games you don't want the player to have the advantage of seeing more map. How do you guys feel about a max-aspect-ratio
variable. You can have both, autozoom and autoresize, as long as you don't go too far in your aspect ratio.
I think in the case you don't want to show too much of the map, you just want AutoResize = false?
I think it may be better to consider this as an exception, so normal players can have a better gaming experience in all the different screen sizes, but at the same time being prepared for bigger screens and more clever players.
I'll write some code, and try to keep it simple.
If both AutoResize and AutoZoom are true the game is going to appear very similar from one screen to another varying only as much as the aspect ratio does, e.g., aspect ratio on iPhone is 1.5:1, the iPad is 1.333:1. My monitor is 1.6:1.
What would be nice though is a maintain aspect ratio option so that it acts like embedded flash does. If you set the width of a flash object to 100%, it will scale to fit the width of the parent container and the height of the flash object will have the correct aspect ratio. This is really important in responsive web designs.
To mimic Flash: AutoResize: true, AutoZoom: true, MaintainAspectRatio: true
Please provide some images that show how a game will render with this third boolean. You'll now have 2**3 = 8 different modes of resize, instead of just four.
Keep in mind that AutoResize = true
will always change the aspect ratio.
Here is a perfect example. Resize the result container.
I just see a broken image.
Failed to load resource: the server responded with a status of 403 (Forbidden) http://www.gamesuncovered.com/uploads/2009/01/10/fieldrunners1-122933.png
But regardless, it looks like what you want is AutoResize = false, AutoZoom = true
Find any image on the web and put its URL in there.
MaintainAspectRatio would make it so that AutoResize resized to full width and then the height was generated via the original aspect ratio ... just like when an image in HTML is set to width: 100%, height: auto. In the case of images the aspect ratio is built in. In our case the aspect ratio is defined by the original viewport size.
I guess you could make MaintainAspectRatio always fit in the screen in which case sometimes it would use 100% height and sometimes 100% width or that could be an option.
Quote: // But regardless, it looks like what you want is AutoResize = false, AutoZoom = true
Not exactly. An image always resizes to full width. In the AutoResize = false, AutoZoom = true example above it has resized to full height.
Actually, in the use case I am thinking, AutoResize = false, AutoZoom = true: may actually work so you may be right. I was thinking of how it would display in a column on a webpage. Since those columns don't have heights defined it would have to use the width.
This is how AutoResize = false, AutoZoom = true
is supposed to work. Notice the image is _always_ the correct aspect ratio, especially when the height of the container would cause the bottom of the image to get chopped off. That "chopping off" means the aspect ratio is changing. And that is not what you want.
as I was also highthing it here : https://groups.google.com/forum/?fromgroups=#!topic/melonjs/DNI1BWr0m7M
me.sys.scale
will need to be modified, to “become” a vector containing the x and y scale ratio, that can be used later to correctly compute mouse coordinates. As from my understanding, currently a use case with AutoResize = true, AutoZoom = true;
(does not maintain aspect ratio) won’t work with mouse event (coordinates will be wrong)
I agree 100%. Scaling without maintaining aspect ratio will really do some strange things. The backbuffer (when double buffering is enabled) will also need some special handling for this case.
If I may a couple of other remarks :
resizeViewport
function to me.game
, I would rather suggest to add a resize
function to the viewport object. (note also that if you resize the viewport, you probably also need to update the deadzone values)“auto”
for the existing scale
parameter, to at least get rid of the autoresize
one ? then I have no idea so far concerning the second one :)updateCanvasSize
function instead of updating the existing updateDisplaySize
one (I’m not sure someone even used it) ? these two function are somehow redundant, with the previous probably even broken@andyveliz, by the way, that's a great work you did here, thank you for working on this :)
Great ideas! well... the autoresize was indeed tricky, but the mouse coordinates are working as far as I tested, correctly.
I think the auto
parameter in scale it's a lot easier, I think I use the resizeViewport
function in me.game
, because the drawManager
parameter it's not public, and i needed to update the fullscreen_rect
variable. Any ideas about this?
About the updateCanvasSize
I created it because it was just a hack, I was a little afraid of breaking some other functionality, but if I have your blessing to mess with it, it will be a lot more elegant.
@parasyte The backbuffer was really tricky, but with my hack it works :D
@andyveliz Hi ! So i started integrating your code to progress on this topic, and while starting to make some modification, I had a question.
I can see that you actually resize the viewport, I guess that you made this for a good reason, but I don't understand why ? :)
The feature here being basically to scale (maintaining or nor aspect ratio) automatically the display to fit with the browser window size, why do we need this? In my point of view, the offscreen canvas and the corresponding viewport should never changed, as only the displayed canvas is scaled/resized.
thanks for the feedback !
@parasyte @andyveliz
At this stage (integration + clean-up), I think we need some beta tester here, any volunteer ? :):):)
I was looking at graphs for work and ran into this, another example of zooming with the mousewheel and dragging the viewport with mousedown. http://sigmajs.org/examples/gexf_example.html
Same functionality in an HTML5 game: https://chrome.google.com/webstore/detail/fieldrunners/lkpikhjbfbffdblahfidklcohlaeabak?utm_source=chrome-ntp-icon
I just want to make sure this sort of functionality will be possible after autoZoom and autoResize.
Another feature that I mentioned in #113 that would be greatly appreciated is a method to assign map layers and objects to at least two different buffers, and to be able to zoom them independently before they are outputted on top of each other on the canvas. For example, I would assign the map layers, object entities, sprites, and animation sheets to the back buffer and gui and hud objects to the front buffer. Zooming with the mousewheel would only effect the back buffer.
An example of this sort of functionality can be seen here: https://chrome.google.com/webstore/detail/fieldrunners/lkpikhjbfbffdblahfidklcohlaeabak?utm_source=chrome-ntp-icon
@parasyte @greghouston @andyveliz
Hi Guys, I would need some advice here before going further, as at this point we have basic automatic scaling stuff working .
What we have here so far :
me.video.init
takes the following parameter function(wrapperid, game_width, game_height, doublebuffering, scale, autoresize)
scale
is equal to 'auto'
, the canvas will be scaled based on the canvas parent container width and heightSo a few questions :
autoresize
parameter to maintainAspectRatio
, as it makes more sense this way, what do you think ?me.event.WINDOW_ONRESIZE
channel, maybe there a cleaner way to implement it through this, instead of adding again a new parameter to the me.video.init
function ?Renaming autoresize to maintainAspectRatio makes sense to me.
I wonder if there are enough parameters now that it would make sense to make it something like:
function(wrapperid, game_width, game_height, { doublebuffering: true, scale: true, maintainAspectRatio: true }
Just a thought.
I don't know if it is already built-in or not, but a nice feature would be adding event listeners for window.resize and orientation change so that the game could resize without requiring a refresh of the page. This could even be yet another option.
This game has great incremental resizing: http://browserquest.mozilla.org/
If you resize the browser small enough it will request, "Please rotate your device to landscape mode". Since our resize is based on the wrapper element, this would be easy to implement with media queries if melonjs resized when there was a window.resize or orientation change.
I sort of think it might be helpful to have a second scale function called zoom. Zoom would not change the canvas size, but either the entire canvas scale or the scale of a buffer. This way if the player is zoomed into the game at 1.5 scale, and then changes orientation on their iPad, the zoom is applied after the canvas scale. So the canvas might have a scale of 2 and zoom be at 1.5.
me.sys.scale me.sys.zoom
This way also a zoom of 1.5 would have the same effect at all scales. A zoom of 1 being the default. In my game for instance, the mousewheel will effect zoom with a range clamp of 1 to 1.5. Zooming should not effect the canvas size. It almost worked with the now deprecated me.video.scale, but was rather buggy.
You might also consider adding me.sys.orientation
that would either return a value of portrait or landscape.
Hi @greghouston
thank you for the feedback,
Concerning notification, the engine publish a message (using minPubSub) on a specific channel, see here : https://github.com/obiot/melonJS/blob/ticket-4/src/video/video.js#L225
but yes, another one for orientation change, and even fullscreen, would be good too (I might think about a generic channel), as the event type is then available in the passed event object.
Concerning the parameters, I will then renamed the auto-resize
parameter to maintainAspectRatio
.
Also the scale
can take a float value ( to specify a fix scaling value) or the auto
value for automatic scaling.
Note finally, that for now, the viewport and "backbuffer" canvas size are unchanged, only the "frontbuffer" display canvas is automatically scaled ( i need to add this back, but I wanted first to clean-up the implementation for the basic scaling)
Hey Guys! sorry for not being around for a bit. I was getting married, so i was a little busy... hehehe
@obiot
The reason i have to change the viewport size was to change the fullscreen_rect
var in the drawManager, the problem i had, was that after resizing the canvas the tiles weren't always drawn so i did a little trace, and found out that you were using that variable to see what tiles shoud be drawn, and that value didn't change when the canvas is resized.
I've also tried changing the size of the backbuffer but that only led to zooming of the map...
i guess maybe you found a better way to do the resizing :)
and I think you're right about changing the name of the variable, it's more descriptive.
i'll test out this new fixes...
nooo way, you got married ? me too this weekend !!!!!! Congratulations man :):):)
hahaha! congrats to you too!! what a coincidence! hahahahah
@obiot I haven't had a chance to look at this, sadly. Have been super busy since last week, but things are starting to clear up in my schedule. (Also @andyveliz my best friend's sister got married last Friday, and I was part of their wedding! Congratulations to you!)
I'm ok with renaming the variable to "maintainAspectRatio", since it is a little bit more "obvious". Good to see you also took the time to reverse the logic on it when renamed. ;) I also kind of prefer Greg's suggestion of passing an optional settings object.
I'll get to some code review on this either today or tomorrow sometime. :) I encourage others to help us all with code reviews, too. They are incredibly useful!
oh so you actually know each other, what a small world ! else for sure please take your time, I was certainly not pushing anything :)
Guys,
I really would like to merge this into the main branch, as it's starting to be difficult to manage as a separate branch, as the main one includes some bug fixes that are also required here (like with the floating object stuff). And I believe this could be a nice first milestone.
what do you think ?
we can then let this one open, or close and create a new ticket for the missing stuff (basically it's the viewport resize)
Let me know what you do. If you close this one I'll make another ticket for zoom and another one for resize on orientation change.
I will merge it a bit later in the main branch, will close this one and create 2 new tickets.
Please have a look after, If I can ask, to ensure I did not miss anything :)
Hi Olivier
I performed some tests with the Screen Auto Resize (for auto resize the canvas, with "auto" scale) and with the hints below (for resize the game DIV):
http://www.html5rocks.com/en/tutorials/casestudies/gopherwoord-studios-resizing-html5-games/
And has worked like a charm!
Thanks!
@ciangames thank you for the feedback !
@greghouston
for the orientation change, what I can do is to create a specific channel you can listen to, like how i did for the resize event or even also trigger the resize event on orientation change (so that you just to listen to the resize), but however I don't really see the benefit of the me.sys.orientation
since it's already available then using the window.orientation
property.
something like this should work : if Math.abs(window.orientation) === 90 then 'portrait' !
@greghouston
an even better example using the last commit :
// register to the window resize channel me.event.subscribe(me.event.WINDOW_ONRESIZE, function() { if (Math.abs(window.orientation) === 90) { // portrait : do something } else { // paysage : do something else } });
bonus question to all :
does anyone has an idea on how to remove/fix this "flickering" effect when the window is resizing (as a reminder, when a canvas is resized; it's content is cleared) ? I tried to defer the function call, but it does not change anything....
@obiot
Regarding orientation, that makes sense. No need for a ticket for that one.
That just leaves zoom.
@obiot: Repainting on resize event like that will cause some performance decrease while resizing. (I know, it's not very common, but maybe it's noticeable on older hardware.)
If that's a concern, you could instead do a "smart" defer, that waits some short period (like 35ms -- perfect for 30fps) and resets itself if the "smart defer" is run again before the 35ms lapse. Then the user resizes by quickly dragging the window handle; the canvas resize will take effect only after the resize handle slows down.
Function.prototype.timeout = function (time /*, args */) {
var fn = this, args = Array.prototype.slice.call(arguments, 1);
if (this.delayId) {
window.clearTimeout(this.delayId);
}
this.delayId = window.setTimeout(function() {
return fn.apply(fn, args);
}, time || 0);
return this.delayId;
};
Use it:
me.video.updateDisplaySize.timeout(35, scale, scale);
@parasyte : yes, I thought about it as well, but is it really a concern ? I tried it on all my machines yesterday and did not notice any lag or issue, so..... and after all it's just re-sizing, people should not spend time resizing the window continuously ?
let's keep anyway this piece of code here, in case of major complains on the feature. (what would need to be modified in my code is however to clear the pending deferred function, in case another is deferred meanwhile)
@parasyte : found this blog entry (http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) while googling our suggestion, this could be actually applied to a lots of things in melonJS (mousemove as a start, as in the bottom of the blog entry example)
@parasyte : else i think that if I could just store the timer id returned by the defer fuction, and clear it before deferring again the next resize function call, it should be enough (for a window resizing). Keep in mind that deferred function are only executed when the stack is empty, which is once every frame.
Implement graphics auto-resize in melonJS, and use the browser resize event, to create (full)screen size independent games.