110
\$\begingroup\$

For example the famous Flappy Bird game, or anything that sort really, is the player(in this case the bird, or the camera whichever you prefer) moving forward or is the whole world moving backwards(the bird only changes Y position and has a constant X position)?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ We have deleted many off-topic comments from this question and its answers. We've also moved several reasonable discussions to chat, in some cases repeatedly. Despite this, several users have felt the need to ignore the fact that comments aren't for extended discussions and continued on. As such, this question has been temporarily locked. \$\endgroup\$
    – user1430
    Dec 7, 2017 at 16:18

4 Answers 4

146
\$\begingroup\$

I slightly disagree with Philipp's answer; or at least with how he presented it. It gives the impression that moving the world around the player might be a better idea; when it's the exact opposite. So here is my own answer...


Both options can work, but it's generally a bad idea to "invert the physics" by moving the world around the player rather than the player around the world.


Performance loss/waste:

The world will usually have a lot of objects; many if not most, static or sleeping. The player will have one, or relatively few, objects. Moving the whole world around the player, means moving everything in the scene except for the player. Static objects, sleeping dynamic objects, active dynamic objects, light-sources, sound-sources, etc; all have to be moved.

That's (obviously) considerably more expensive than moving only what's actually moving (the player, and maybe a few more stuff).


Maintainability & Extensibility:

Moving the world around the player makes the world (and everything in it) be the point where things are most actively happening. Any bug or change in the system means that, potentially, everything changes. This is not a good way to do things; you want bugs/changes to be as isolated as possible, so that you don't get unexpected behaviors somewhere you haven't made changes to.

There are also many other problems with this approach. For example, it breaks many assumptions of how things are supposed to work in-engine. You'd not be able to use dynamic RigidBody for anything other than the player, for example; as an object with an attached RigidBody not set to kinematic will behave unexpectedly when setting position/rotation/scale (which you'd be doing every frame, for every object in the scene, except the player 😨)


So the answer is to only move the player then!

Well...yes and no. As mentioned in Philipp's answer, in an infinite-runner type of game (or any game with a large seamless explorable area), going too far from origin would eventually introduce noticeable FPPEs (Floating-Point Precision Errors), and further still, eventually, overflow the numeric type, either causing your game to crash, or, basically, make the game-world smoke crack...On steroids! 😵 (because by this point, FPPEs would make the game already be on "normal" crack)


The actual solution:

Do neither and do both! You should keep the world static, and move the player around it. But "re-root" both, the player and the world, when the player starts getting too far from the root (position [0, 0, 0]) of the scene.

If you keep the relative position of things (player to the world around it), and do this process in a single frame-update, the (actual) player won't even notice!

To do it, you have two primary options:

  1. Move the player to the root of the scene, and move the world chunk to it's new position relative to the player.
  2. Think of the world like a grid; move the part of the grid that the player is in to the root, and move the player to it's new position relative to that part of the grid.

Here is an example of this process in action


But, how far is too far?

If you look at Unity's source-code, they use 1e-5 (0.00001) as the basis for considering two floating-point values "equal", inside of Vector2 and Vector3 (the data-types responsible for objects' positions, [euler-]rotations and scales). Since floating-point precision loss happens both ways away from zero, it's safe to assume that anything under 1e+5 (100000) units away from the scene root/origin is safe to work with.

But! Since...

  1. It's more appropriate to make a system to handle these re-rooting processes automatically.
  2. Whatever your game is, there is no need for a contiguous "section" of the world to be 100000 units (usually meters) wide.

... then it's probably a good idea to re-root far sooner/more often than that 100000 units mark. The example video I provided seems to do it every 1000 units or so, for example.

Or, if your project allows it (most do), just have the world be "small enough" and avoid the problem altogether. On this note; congratulations, you've just found out one of the reasons why most games, even "open-world" or "free-roam" type ones like GTA, don't do maps/areas bigger than 10km-by-10km or 100km-by-100km.

\$\endgroup\$
6
  • 2
    \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. Please use that chat instead of posting more comments here. \$\endgroup\$
    – Vaillancourt
    Dec 5, 2017 at 13:25
  • \$\begingroup\$ > That's (obviously) considerably more expensive than moving only what's actually moving < Is it? When rendering, you will anyway have to do a matrices multiplication with the camera matrix on all objects, having the camera fixed at the identity would be less expensive in this manner. \$\endgroup\$
    – Matsemann
    Dec 11, 2017 at 11:08
  • 1
    \$\begingroup\$ That moment when game development becomes unity development... \$\endgroup\$
    – LJᛃ
    Dec 11, 2017 at 13:45
  • \$\begingroup\$ @Matsemann - If you had went to chat, you'd have noticed this is not a new point. As already explained, render NOT-EQUALS scene; they are different and almost completely independent subjects and pipelines. Doing an inversion of frame of reference in the way objects are positioned in the scene will mean you will have a heavier process on both ends, rather than just in render. --- Also, even if performance was equally-traded as you argue, all other problems with the inversion would still remain unsolved, making that approach still worse than the re-rooting one. \$\endgroup\$ Dec 11, 2017 at 20:07
  • \$\begingroup\$ @LJᛃ The question was made about unity specifically (see OP's tags prior to D. Everhard's [IMHO defacing] edits), and so the answer was tailored to reflect that. --- But here's the best part: Be it Unity, Unreal, CryEngine, Source, etc... The best and/or most popular engines work this way (and they do it for a reason), so the answer is not only perfectly valid in general, but the points being made are still at the pinnacle of accuracy. Generally speaking, if you invert the physics' frame of reference, you can expect to run into problems, specially on dynamic objects. \$\endgroup\$ Dec 11, 2017 at 20:22
87
\$\begingroup\$

Both options work.

But if you want the endless runner to be truly endless, you will have to keep the player stationary and move the world. Otherwise you will eventually hit the limits of the variables you use to store the X-position. An integer would eventually overflow and a floating point variable would become increasingly less accurate which would make the gameplay glitchy after a while. But you can avoid this problem by using a large enough type that nobody will encounter these problems within the timespan one could conceivably play in one session (when a player moves 1000 pixels per second, a 32 bit integer will overflow after 49 days).

So do whatever feels conceptually more intuitive to you.

\$\endgroup\$
5
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$
    – user1430
    Dec 6, 2017 at 16:17
  • 1
    \$\begingroup\$ I don't think that is solves anything. Instead of keeping player's (and camera) position, you keep world's scroll position - how is that any different? \$\endgroup\$
    – Agent_L
    Dec 8, 2017 at 13:26
  • 1
    \$\begingroup\$ @Agent_L Generally, worlds are generated piecemeal, and each piece has its X/Y position. When the piece gets behind the player by enough (e.g. offscreen), it's deleted, and they're only generated a certain amount in advance, so they're always within a relatively small range -- one game I made never had a coordinate greater than ~1000 or less than 0, despite being an infinite, randomly generated runner, because of that -- I kept track of the player's position in each chunk, and each chunk's index in the sequence. \$\endgroup\$
    – anon
    Dec 8, 2017 at 20:14
  • \$\begingroup\$ I don't buy the "variable overflow" concept. If the player isn't moving, then the "background" will be negatively offset by the same amount that the player would be offset if it were moving. Both cases would run into exactly the same problem (in opposite directions), and both would require special handling at the limits of overflow. \$\endgroup\$
    – spender
    Dec 10, 2017 at 18:27
  • 2
    \$\begingroup\$ at 1000 pixels per second it would take over 586 million years to go over an 64 bit integer. This is only really a problem if you are using really small types like 16 bit integers. \$\endgroup\$ Dec 11, 2017 at 9:21
37
\$\begingroup\$

Building off of XenoRo's answer, instead of the re-rooting method they describe, one could do the following:

Create a circular buffer of parts of your infinite map generated, which your character moves through with position updated with modulo arithmetic (so you just run around the circular buffer). Start replacing parts of your buffer as soon as your character leaves the chunk. The players update equation would be something like:

player.position = (player.position + player.velocity) % worldBuffer.width;

here is a pictoral example of what I'm talking about:

enter image description here

Here is an example of what happens on wrapping at the end.

enter image description here

With this method you don't run into precision errors ever, but you may need to make a very large buffer if you intend to do this in 3d with very far view distance (as you will still need to be able to see ahead of yourself). If its flappy bird your chunk size will probably only be as large as it needs to be to hold a single scene for a single screen, and your buffer can be very tiny.

Note that you will start to get repeating results with ANY prng, and the max number of non repeating sequences of a PRNG generation is typically less than the length of pow(2,number of bits used internally), with merzenne twister this isn't much of an issue, as it uses 2.5k of internal state, but if you use the tiny variant, you have 2^127-1 max iterations before repeat (or worse), this is still an astronomically large number however. You can fix repeating period problems even if your PRNG has a short period via good avalanche mixing functions for the seed (as you add more state implicitly) repeatedly.

\$\endgroup\$
1
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$
    – user1430
    Dec 5, 2017 at 16:36
14
\$\begingroup\$

As already asked and accepted, it really depends on the scope and style of your game, but since it wasn't mentioned: FlappyBird moves the obstacle across the screen, rather than the player across the world.

A spawner is instantiating objects off screen with a fixed speed in the Vector2.left direction.

\$\endgroup\$
4
  • 3
    \$\begingroup\$ That works for FlappyBird because it's a very simple game. As mentioned in my answer, the same technique, while still possible, would be very problematic on anything more complex (with more world objects / details), like Subway Surfer. \$\endgroup\$ Dec 4, 2017 at 22:57
  • 15
    \$\begingroup\$ I agree, and your answer is very thorough. I just didn't see anyone mention which method flapping bird actually uses, so I figured I'd add it. \$\endgroup\$
    – Stephan
    Dec 4, 2017 at 23:00
  • \$\begingroup\$ @XenoRo Side-scrolling games have been doing it for decades, actually, and 99% of them a LOT more complex than Flappy Bird. Not only that, they ran on machines that had a fraction of the performance of 2017-era systems... \$\endgroup\$
    – ROGRat
    Mar 23, 2022 at 11:17
  • \$\begingroup\$ @ROGRat Did I ever say no games were ever doing it? -- I don't think games that did/do it are mechanically as complex as you think. Namely, they will deal with static objects or fixed animations. Not significant amounts of dynamic physics. -- Which is also why they were not as mechanically complex, and not what many game engines now assume the games are going to be. Games in which preference to this method is justified are now a very niche subset, even in the side scrolling genre. You should not make Terraria, Ori And The Blind Forest, The Pedestrian or Inside with this technique, for example. \$\endgroup\$ Mar 23, 2022 at 21:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .