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

Background image layer position is ignored when ratio defined #737

Closed
Giwayume opened this issue Sep 5, 2015 · 26 comments
Closed

Background image layer position is ignored when ratio defined #737

Giwayume opened this issue Sep 5, 2015 · 26 comments
Labels
Milestone

Comments

@Giwayume
Copy link
Contributor

Giwayume commented Sep 5, 2015

Here is an example:
https://www.youtube.com/watch?v=KRjDkCDcC-I

Is this a known issue?

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

I think so. Are you using the 3.0.0 WIP? This was just fixed recently in #725

Is there also a bug with the actual positioning? I mean, does it correct itself to the expected position when the viewport moves, or is the correct position initially set on load? (If the former, then this bug is fixed! If the latter, then we have a different bug!)

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

I've merged my fork with the recent changes. Now it doesn't jump when the camera moves, but the background is always stuck to the top of the screen no matter what the y position is set to in Tiled.

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

https://github.com/melonjs/melonJS/blob/master/src/level/TMXLayer.js#L260

It's trying to position the background in the room using the anchorPoint relative to room boundaries, completely ignoring the Tiled position. However, even going along with this logic it's still broken. If I set any anchorPoint in tiled it fixes the image to the top left of the viewport.

I suggest the following positioning logic for image layers, to keep up with the new positioning for sprites:

var drawX = this.pos.x - (this.imagewidth * this.anchorPoint.x) + (vpos.x * this.ratio.x);
var drawY = this.pos.y - (this.imageheight * this.anchorPoint.y) + (vpos.y * this.ratio.y);

This allows Tiled to set the initial position of the image in the room when the viewport is at position (0,0). Then that image is scrolled according to the ratio of the viewport position offset from the origin of the room.

It's much simpler, and more precise than guessing at an anchorPoint. Also Tiled's position isn't ignored!

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

Use anchorPoint to configure the ImageLayer position when ratio is defined.

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

I'm guessing your browser didn't load in my latest comment when you posted that.

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

I'm probably just confused by how you're using anchorPoint. Our Tiled parser allows JSON values with a special syntax. See documentation for ratio as an example: http://melonjs.github.io/docs/me.ImageLayer.html#ratio

Specifying anchorPoint in the same JSON notation allows positioning the image layer precisely with a unit vector, and allows the layer to scroll appropriately when anchored to the bottom: {"x":0,"y":1}

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

Yep, that's what I'm doing. It's not working.
screenshot from 2015-09-05 16 53 17

Also, that's a hacky solution in my eyes. The anchorPoint doesn't need to be involved at all, and because it is Tiled's position is being ignored.

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

I really think the image should be positioned in the room according to its "position". I mean that makes sense, doesn't it?

The anchor point is fine if you treat it like a Sprite's anchor point, but I really don't like having the image's position ignored just because the ratio isn't 0.

var drawX = this.pos.x - (this.imagewidth * this.anchorPoint.x) + (vpos.x * this.ratio.x);
var drawY = this.pos.y - (this.imageheight * this.anchorPoint.y) + (vpos.y * this.ratio.y);

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

Disadvantages to anchorPoint viewport boundary positioning:

  1. What you see in Tiled isn't what you see in the game.
  2. If you change the size of the room the background's position changes.
  3. You have to "guess and check" to see if the image is placed exactly where you want it when using an anchorPoint.

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

Would you mind sharing an example that displays the issue for debugging?

The image position is purposefully ignored when ratio is defined because adding the position is usually not what the developer actually wants, and it will make the anchorPoint completely inaccurate.

For some history, anchorPoint is used to solve an issue with parallax scrolling layers that expect the viewport to be initialized at the bottom of the map; In this case, you want the bottom of the ImageLayer to align with the bottom of the map (and you can ignore the position defined in Tiled, because ... well, it's scrolling at a ratio of the viewport's position!) Also #632 and #332 (comment)

To answer your comments:

  1. Yes, this is usually not desired. It's going to be a matter of reconciling the initial Tiled state with what's actually supposed to happen at runtime. (See above for anchorPoint context.)
  2. This is an advantage, IMHO; with anchorPoint.y set to 0.5, the image is guaranteed to be anchored to the vertical center (50%) of the map. Or at 0.1 it's always at 10%, etc.
  3. No, you don't have to guess. It's simple math: The appropriate anchorPoint.y to use can be computed by ImageLayer.pos.y / (map.height - ImageLayer.height)
    • If you change the map size and the ImageLayer position to the same ratio, then point 2 above is invalidated.

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

2&3. I'm talking about from an artistic perspective. You don't create visual assets with a command line. When I'm constructing a level, I don't know or care about what percentage or pixel position it ends up as. I want the image to be positioned where I see it positioned in Tiled when I'm creating the level. Only after seeing what the finished product would look like, then I can worry about what pixel/percentage offset it translates to. Having to go back and do extra math to calculate an anchorPoint adds a very large load of extra work when you're planning on constructing 1000 rooms, and many with multiple parallax background layers.

Taking Tiled's position into account + the new way anchorPoints are positioned with sprites, you can achieve the same effect as anchoring to the bottom of the level by setting the y position in tiled to the bottom, then setting the anchorPoint to (0,1). This does not make the anchorPoint inaccurate, it's just a different way of thinking about it.

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

Ok, now take what you see in tiled, and cut out a small rectangle of it to represent the viewport. With this in mind, here are the problems to solve from a logic perspective. (This is the reconciliation I mentioned.)

  1. Where is the viewport rectangle initially positioned within the map?
  2. Given that the answer to 1 is arbitrary (bounded by the difference between map size and viewport size) is the ImageLayer expected to be positioned according to what is displayed in Tiled? (This is the "cut a rectangle from a static image" perspective) Or is it positioned as a ratio of the viewport's position, which is relative to the upper-left corner of the map?
  3. When the viewport position changes, the ImageLayer is positioned at a ratio of the difference from the viewport's last position. How do you compute the ImageLayer's new position as a closed-form expression (i.e. without recording previous position state) when the ImageLayer is positioned in Tiled?

This is not an issue unique to melonJS. For example: http://www.gamasutra.com/blogs/DavidDionPaquet/20140601/218766/Creating_a_parallax_system_in_Unity3D_is_harder_than_it_seems.php?print=1 The videos here demonstrate the viewport rectangle scrolling across a map with parallax layers updating in real-time. If I had my way, Tiled would do this, and we would just replicate it within melonJS. Since that isn't the case (though could one day happen) we need to work within imaginary boundaries and take a "best guess" approach to resolving the conflicts between what Tiled shows, and with what's expected to happen at runtime.

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

Taking Tiled's position into account + the new way anchorPoints are positioned with sprites, you can achieve the same effect as anchoring to the bottom of the level by setting the y position in tiled to the bottom, then setting the anchorPoint to (0,1).

I don't follow; anchorPoint.y === 1 aligns the bottom of the ImageLayer with the bottom of the map when the bottom of the viewport is also aligned to the bottom of the map. But this only works accordingly when ImageLayer.pos.y === 0 If you change the ImageLayer position, then it will be offset by the position.

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

I don't follow; anchorPoint.y === 1 aligns the bottom of the ImageLayer with the bottom of the map when the bottom of the viewport is also aligned to the bottom of the map. But this only works accordingly when ImageLayer.pos.y === 0 If you change the ImageLayer position, then it will be offset by the position.

You just gave me an idea; I think we can solve both of our issues.

  1. Keep anchorPoint the way it works today, aligned to the room bounds.
  2. The position in tiled, instead of being ignored when using the ratio, acts as an offset from the initial position determined by the anchor point.

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

That's kind of what I want to do. But I'm still looking for the closed-form expression to make it happen. (!)

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

Isn't it as simple as adding Tiled's position to the end result? Or am I missing something?

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

Just that adding the position would have the result that I described. That result is unexpected when anchorPoint is anything other than <0,0>

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

The videos here demonstrate the viewport rectangle scrolling across a map with parallax layers updating in real-time. If I had my way, Tiled would do this, and we would just replicate it within melonJS.

mapeditor/tiled#1071

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

Honestly, having an expected result only at anchorPoint <0,0> is better to me than having nothing at all. Though I'm not entirely sure what you mean by the result being unexpected. It's ugly in tiled, for sure, if your anchorPoint is something other than <0,0> and you're using the image position to control a pixel offset in addition to the anchorPoint.

https://github.com/melonjs/melonJS/blob/master/src/level/TMXLayer.js#L314

x = ~~(x + posInTiledX + ax * (bw - width));
y = ~~(y + posInTiledY + ay * (bh - height));

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

Yes, but we're not just trying to solve another use case, here. The goal is to simplify how the inputs behave, so that they are easier for humans to reason about.

Take this idea, for instance: perhaps the position itself describes the anchorPoint... It's the same math, e.g.

anchorPoint.x = imageLayer.pos.x / (map.width - imageLayer.width);
anchorPoint.y = imageLayer.pos.y / (map.height - imageLayer.height);

While this can be done manually and placed into a layer property, it could also be initialized with this code in the default case. If I'm not mistaken, that takes care of the Tiled positioning issue, without changing any of the scrolling behavior. That behavior has gone through three years of evolution, and I'm hesitant to introduce another offsetting mechanism.

In other words, the position which you define in Tiled is the anchorPoint in melonJS. And we already know the anchorPoint math is correct in regard to the viewport extents. And as a bonus, it's already implemented in a closed-form expression.

Do you agree with that?

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

That sounds good. So then manually defining the anchorPoint in Tiled would negate any effect the position had on the image.

I'll try and create a simple example where the anchorPoint isn't working for me. Too bad there's no parallax demo.

@parasyte
Copy link
Collaborator

parasyte commented Sep 5, 2015

Here's a patch to try, when you are ready to test a fix:

diff --git a/src/level/TMXLayer.js b/src/level/TMXLayer.js
index 6e16417..5966290 100644
--- a/src/level/TMXLayer.js
+++ b/src/level/TMXLayer.js
@@ -139,7 +139,10 @@
                  * @default <0.0,0.0>
                  * @name me.ImageLayer#anchorPoint
                  */
-                this.anchorPoint.set(0, 0);
+                this.anchorPoint.set(
+                    x / (viewport.bounds.width - this.imagewidth) || 0,
+                    y / (viewport.bounds.height - this.imageheight) || 0
+                );
             }
             else {
                 if (typeof(settings.anchorPoint) === "number") {

@parasyte parasyte changed the title Background image layer "jumps" to 0,0 when camera moves (and ratio defined) Background image layer position is ignored when ratio defined Sep 5, 2015
@parasyte parasyte added the Bug label Sep 5, 2015
@parasyte parasyte added this to the 3.0.0 milestone Sep 5, 2015
@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

I'm thinking with that implementation if you want the image to be anchored to the bottom of the room the image would be off-screen.

Maybe it makes more sense to pick a point inside the image based of closeness to the room boundaries, instead of always using the top-left corner.

EDIT: Nevermind.

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

#738
I updated the platformer example. I'm seeing the new cloud background I added sticking to the top of the screen (though it has an anchorPoint of 0.5). Also, it's moving as if it has a ratio of 1 (instead of 0.5).

@Giwayume
Copy link
Contributor Author

Giwayume commented Sep 5, 2015

Your patch works great, I just added me.game in front of viewport and removed the anchorPoint definition in Tiled. It must be the way that I'm defining anchorPoint in Tiled that's causing it to not work in the platformer demo provided.

@parasyte
Copy link
Collaborator

parasyte commented Sep 6, 2015

Ok, I'll commit the patch with the syntax error fixed. As mentioned in #738 (comment) You're missing the json: prefix on the property value.

parasyte added a commit that referenced this issue Sep 6, 2015
parasyte added a commit that referenced this issue Sep 6, 2015
- Seems the Wolfram Alpha optimization applied the ratio on the wrong side.
- This fixes the scrolling and positioning, according to further tests.
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

2 participants