Infinite scroller

When I was just starting to learn Playcanvas and gamedev in general, I didn’t know how to handle infinite scrolling, so I implemented my game in a way that the camera and the floor are actually static, while everything else moves out of view. This works, but makes it hard to add new content, e.g. I cannot place a static decoration, because I would need to move it as well. That closes the door for baked lights and other optimizations for me.

First, I thought, perhaps a stupidly large plane could do the trick. But I immediately rejected that, since I don’t know how to handle it later, when I add textures or shaders to it. Plus, it doesn’t feel elegant or fail proof.

Another way, which I am currently more keen to, is to probably have a few fixed size planes, and then spawn a neighbor plane next to it, as the view gets closer to the edge of one of them. Once the old plane is out of the view, I can return it to the pool.

Elaborating on that method further. My game currently restricts the directions the game can “crawl” to. The isometric view can move either (abs) X or Y (kind of towards top left and right corners of the screen), randomly chosen. To easier visualize it, think of a chess board infront of you, turn it 45 degrees, so its a shape of rhombus. Now place a chess piece at the closest cell to you, at the bottom corner of the rhombus and start progressing the chess piece up the rhombus, one random cell at a time.

This way, I could, for example, spawn a fixed amount of cells (planes) around the player, so no matter which direction it goes, it will be able to move infinitely. However, I don’t know how to handle the view frustum on different devices. How many of cells would I need to spawn to make sure they cover the frustum on all resolutions? Or, I could probably, make them pretty large, so even one would cover the whole view, and I simply spawn another 3 (top-left, top, top-right) or even 5 (extra 2 for left and right) to make sure the player will not see an edge of the cell, if he randomly ended up constantly moving only in 1 direction.

I’m pretty sure this is not a unique problem in game design, but simply my inexperience in it to know the common approach. Please, advice.

Yup, definitely it’s a common problem searching for a solution, infinite scrollers.

You actually listed most of the common solutions:

  1. fixed floor with content coming in/out
  2. stupidly large plane (being there!)
  3. tiles being swapped in place

3rd option is indeed the most elegant and provides lot’s of room for decoration/baked lighting etc. You just need as you said to nail the frustum problem:

  • When is something definitely out of view so it can be swapped/placed back in the queue

I’d say this problem isn’t different than having a level that’s need to be in view no matter the screen resolution. Usually you play with the horizontalFov camera property, so one side of the camera frustum rect is always fixed in size, and the grows when the sizes varies:

That way you can make sure that at least in a direction you have control of the visible payable area.

Other ways I can imagine now is playing with visual effects like fog that show/hide the playable area depending on the size of the disable. You will have to write an algo that relates the two so you can continue having control of the playable area view.

Hope I’ve been of help :slight_smile:

1 Like

Good! I will go ahead and try the tile swapping. Will post an update.

1 Like

You could use fog or something similar to limit the distance need for the tiles.

1 Like

I had some time to give it a try on the ways it could be implemented.

At first, I just spawned a new set of tiles around me. This quickly surfaced an issue that tiles are ignorant of whether there was a tile before or not, it just spawns at the same location regardless, making lots of duplicates. I had to figure out how to make tiles be aware of neighbors and not spawn, if the neighbor position is occupied.

I tried to use the trigger volumes at first, by positioning them at the edges of the tile. Idea was that if a tile spawns on its side, it would collide, and I know there is another tile at the place. But it didn’t go well, since the trigger volume acts on rigid bodies, not statics, if I am correct.

Then I decided to change an approach, and add a position probe to each tile under it. When the tile spawns, its probe tests the directions on each side of the tile, to know if it is available for future spawns. When I step onto that tile, it spawns only at free positions.

Here is a public demo of the current attempt. I put the probes above the ground and with visible lines for easier debugging. The lines represent the probe’s ray and the available direction the next tile can spawn.

I still have some issues with it, that I need to tackle:

  1. If you stand at the intersection of 4 tiles. So you basically trigger all 4 tiles at the same time. I think currently 3 will ignore you, but the last one will require a tile from the pool for a spawn. So that one active tile becomes active at some point, running in circles. The engine will start continuously return the tile to the pool and get another for the neighbor spawn, then proceed with the next one, etc, resulting in flickering.
  2. I don’t know yet which tile to return to the pool. First I simply returned the oldest one that was spawned. But that didn’t go well, when I move forward, then return back to the old tile - it just gets removed as the oldest one. The current implementation is based on the distance from the player. I just pick one tile randomly which is over some distance from player and reclaim it. It works better, but can leave “floating islands”. Suggestions are welcome.

Ahoi @LeXXik!

**Self-aware Tiles:** A script that tracks the distance to the player. Once the distance is above a certain threshold the script disables itself or returns it into the pool. You could attach this script to the tile entity. **Tile Controller:** Or you could create a TileHandler script which periodically checks the distance of all enabled tiles to the player and disables/return to pool accordingly.

A generic central TileHandler would work the best I think. I will try to break this down with words. In case it doesn’t make any sense I can also draw the logic :sunny:

  1. TileHandler knows the position of player.

  2. TileHandler checks periodically which tiles in the player area should be active (i guess in your case 9?). This could be a fixed size so we can preinstance these beforehand. Just check the coordinates and match against a tile/coord array.

  3. If a location does not have a tile then one tile is retrieved from the pool and its position gets set to the missing location (in world space and in the array). You have to keep track of which tile entity is placed where.

  4. Now you have the tiles placed.

Removing them would occur shortly after you calculated which locations have to be populated. Just return all mismatching tiles back to the pool. This would ensure infinity.

Hope this makes sense.

1 Like

Yep, I think that was my initial design, but probably didn’t include in the description. The reason, I didn’t select it, is that I wanted to have a generic system, which doesn’t depend on low amount of tiles (like 9, as you mentioned). Tiles could be pretty small, creating a grid on the screen and beyond. In this case, TileHandler would probably have been expensive to run on every change or frame, as it would have to iterate over all tiles to update the grid. I might be overthinking here, but I simply didn’t want to read tiles that has no need to be read, creating computational overhead, and act only on the ones that require an update.

Look at it this way:

The calculation of all the possible coordinates (9 or 12 or 300) is trivial. Comparing these coordinates against an array of existing tiles (size= 9 or 12 or 300) is at a low CPU cost. Managing the pool is also not really demanding.

You don’t even need to compare them every frame; maybe every second. It depends on how many tiles you initially want to create.

Like I said: break it down:

Calculate possible tile locations
Remove legacy tiles
Populate new tiles

I haven’t quite followed it, but you can still do this after every well executed jump of the player?

Yes. Because everywhere a player could jump to should already have tiles placed. If you want a cool effect you could let those spawning tiles be dropped down from the sky shortly before arriving at the area.

1 Like

I don’t know what is the problem, but I think you need 2x 9 tiles. 9 tiles with the current position and 9 tiles with the new jump object. When the player is jumping or after jumping you move the the 9 tiles in front to the 9 tiles in the back and place 9 new tiles with a new jump object in front. (I think @Sebastian already say something like this).

And of course that is also possible without tiles. You just keep replacing the old with the new (with a piece of world around it that fills the screen).

So you shift the world back and hold the camera in the same place, making it look like you’re going forward.

I try to visualize it in my head. Perhaps I overlook technical problems. Forgive me.

I worked on the TileController (link). The code is a mess but the idea works :crazy_face:

Hey there @Sebastian, so yeah, I’ve incorporated yours and @Albertos suggestions and updated the project. This allowed me to remove the probes alltogether, and just manage the tiles via array. It will suffice for my needs, but feel free to use it, if you need for whatever. Cheers :slight_smile:

1 Like

Cheers mate. That is a nice execution! @LeXXik Way better than my prototyp. Keep up with that great work! :slight_smile: :rocket:

1 Like