Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for screen autoresize #4

Closed
melonjs opened this issue Aug 27, 2011 · 54 comments
Closed

support for screen autoresize #4

melonjs opened this issue Aug 27, 2011 · 54 comments
Labels
Milestone

Comments

@melonjs
Copy link
Collaborator

melonjs commented Aug 27, 2011

Implement graphics auto-resize in melonJS, and use the browser resize event, to create (full)screen size independent games.

@matthewrice345
Copy link

This would be a great feature!

@andyveliz
Copy link

I'm working on this! :D

@andyveliz
Copy link

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:

  • AutoResize: false, AutoZoom: false => What happens now in melonJS, you have always the same canvas size, even if you resize the window.
  • AutoResize: true, AutoZoom: false => The size of the canvas always it the size of the window, but you don't do zoom, so you end up increasing or decreasing the amount of map that is shown on the screen (maybe useful in some RPGs?)
  • AutoResize: false, AutoZoom: true => Does Zoom Keeping the Aspect Ratio of the game
  • AutoResize: true, AutoZoom: true => Does Zoom on the game, but it allows some deformities of the aspect ratio. ?? it need more work...

just tell me what you think...

@parasyte
Copy link
Collaborator

parasyte commented Sep 8, 2012

@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:
Normal

AutoResize = true, AutoZoom = false:
AutoResize

AutoResize = false, AutoZoom = true:
AutoZoom

AutoResize = true, AutoZoom = true:
AutoResize and AutoZoom

@andyveliz
Copy link

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..

@neilcauldwell
Copy link

Parasyte's images look good and might negate the following suggestion...

It would be great to be able to specify which dimensions are resized.

    1. Scale to the view port size whilst retaining aspect ratio
    1. Resize height of canvas
    1. Resize width of canvas

Example: http://www.freeimagehosting.net/6a8aj

Does anyone have a fork with resizing implemented?

@greghouston
Copy link

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.

@andyveliz
Copy link

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.

@andyveliz
Copy link

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.

https://github.com/andyveliz/melonJS

@parasyte
Copy link
Collaborator

@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:

  1. If autosize is true: canvas.width = p.width and canvas.height = p.height
  2. Else if autozoom is true AND p.width >= p.height: canvas.height = p.height
  3. Else if autozoom is true AND p.height > p.width: 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.

@andyveliz
Copy link

@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.

@parasyte
Copy link
Collaborator

I think in the case you don't want to show too much of the map, you just want AutoResize = false?

@andyveliz
Copy link

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.

@greghouston
Copy link

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

@parasyte
Copy link
Collaborator

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.

@greghouston
Copy link

Here is a perfect example. Resize the result container.

http://jsfiddle.net/greghouston/zS7Bu/

@parasyte
Copy link
Collaborator

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

@greghouston
Copy link

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.

@greghouston
Copy link

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.

@greghouston
Copy link

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.

@parasyte
Copy link
Collaborator

http://jsfiddle.net/zS7Bu/1/

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.

@melonjs
Copy link
Collaborator Author

melonjs commented Sep 24, 2012

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)

@parasyte
Copy link
Collaborator

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.

@melonjs
Copy link
Collaborator Author

melonjs commented Sep 24, 2012

If I may a couple of other remarks :

  • Instead of adding a 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)
  • I’m personally not a big fan of the two additional parameters added to the init function. What about the possibility to specify “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 :)
  • Why adding a new 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 :)

@andyveliz
Copy link

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

melonjs pushed a commit that referenced this issue Oct 11, 2012
…ch for further integration/implementation

Also added some TODO I had in mind, while copy/pasting everything :)
@melonjs
Copy link
Collaborator Author

melonjs commented Oct 11, 2012

@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 !

melonjs pushed a commit that referenced this issue Oct 12, 2012
* enforce double buffering if scaling is used
* autozoom is now enable through the scale parameter (if equal to
'auto')
* viewport size is unchanged when zooming/resizing
* enabled autozoom/autoresize on the dirtyRect example
Todo :
* more cleaning
* fix mouse/touch input when scaling/resizing
@melonjs
Copy link
Collaborator Author

melonjs commented Oct 17, 2012

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)

melonjs pushed a commit that referenced this issue Oct 17, 2012
…o` for better consistency

also refactored some code, and rename auto_zoom to auto_scale
internally, to avoid any confusion with what thisi is used for
@andyveliz
Copy link

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...

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 17, 2012

nooo way, you got married ? me too this weekend !!!!!!
Congratulations man :):):)

@andyveliz
Copy link

hahaha! congrats to you too!!
what a coincidence!
hahahahah

@parasyte
Copy link
Collaborator

@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!

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 18, 2012

oh so you actually know each other, what a small world !
else for sure please take your time, I was certainly not pushing anything :)

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 24, 2012

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)

@greghouston
Copy link

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.

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 25, 2012

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 :)

@aaschmitz
Copy link
Contributor

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!

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 25, 2012

@ciangames thank you for the feedback !

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 25, 2012

@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' !

melonjs pushed a commit that referenced this issue Oct 25, 2012
…d also register on the `orientationchange` event
@melonjs
Copy link
Collaborator Author

melonjs commented Oct 25, 2012

@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 } });

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 25, 2012

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....

@greghouston
Copy link

@obiot

Regarding orientation, that makes sense. No need for a ticket for that one.

That just leaves zoom.

@parasyte
Copy link
Collaborator

@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);

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 26, 2012

@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)

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 26, 2012

@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)

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 26, 2012

@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.

@parasyte
Copy link
Collaborator

Well, it's not the clearTimeout that will fix the problem with performance, but the actual time passed to setTimeout. I agree it will prevent multiple defers from executing per frame. But for performance you want the delay to be longer than a single frame.

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 26, 2012

I'm ok to spend more time on this, but do you really believe it's that critical ?

@melonjs
Copy link
Collaborator Author

melonjs commented Oct 26, 2012

So I did that for now (easy & cheap solution). I will give it more attention tonight once back at home, and will try this with my old mbp and several browsers (to see If there is a perfromance issue we should worry about)

melonjs pushed a commit that referenced this issue Oct 26, 2012
@parasyte
Copy link
Collaborator

@obiot: Nope, not at all critical! Just an observation. ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants