Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Wrapping around a 2D scrollable map (Civilization-Like scrolling)

Discussion in 'Scripting' started by Jacho_Mendt, Jan 24, 2017.

  1. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    Hello everyone,

    I apologize if the question has already been asked, but I can't seem to find any answer whatsoever.
    I'm trying to achieve a map that, much like in the Civilization series, wraps around itself horizzontaly, so that when the player scrolls through one side he appears on the other side.
    Now, many 2D tile games implement something like this, but they usually has a fixed screen, while my map is too big to be displayed all at once (around 16km^2).

    How can I achieve this effect? I thought initially to keep the player still and move the map, but there're also plants and animals on it that should move together with the map. What would be the best course of action?

    If need be i could always generate a falloff map and create bounds around the world, so that there's no wrapping, but that is kind of a meh solution.

    Thanks in advance!
     
  2. TSC

    TSC

    Joined:
    Oct 15, 2012
    Posts:
    271
    How about if you set bounds to the map with a buffer and set origins method,

    cache both ends of the map (vector 3) and in update if the user reaches either cached point plus or minus the buffer then call the set origins method, i just wrote something along the lines of this.
     
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,851
    For a map that large, it's pretty important that you keep the player near the origin anyway. So, your world will be broken into tiles, only the tiles on/near the screen are actually loaded or drawn, and as the player moves, you move the map and everything on it (including plants, animals, etc. — let the scene hierarchy make this simple).

    You can either move the whole world on every step of the player, or only move it (in large increments) when the player gets a certain distance from the origin.
     
  4. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    As of right now, the method i use to load the map is exactly that. First, the map loader class checks if the player has moved more than X units and, if so, unloads the chunks out of visible range and loads the new ones. Coordinates are clamped using a simple % operation, so when the player exceeds the boundaries of the map the chunks on the other side are loaded. It works, but if I keep going in one direction, sooner or later i'll get to a point where coordinates get fuzzy and potentially break up the game.

    I thought initially about keeping the player still and move the world, but wouldn't that be a problem with the physics? Wouldn't the animals be affected by the movement in some way?

    I'm not sure I understood your suggestion, so please, correct me if I'm wrong. What you suggest is, basically, to reset the position of the player the moment it crosses the boundaries of the map, correct? That was one of the ideas initially, but what I fear is that the player wouldn't be able to see on the other side of the map and the feeling of a circular world would be broken. Is there any direction in which you could point me to find a solution to this?


    Thanks to both of you for your answers nonetheless.
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,851
    Ugh, you're using physics? I recommend eliminating that if possible; it's nearly always more trouble than it's worth unless you're making the next roll-a-ball or Angry Birds game. For simple things (running/jumping/etc.) it is easier and better to just code it yourself.

    If you really can't eliminate use of Unity's physics, then yeah, you'll need to be more careful. I wouldn't move everything on every frame, but if you reset everything only when the player gets a certain distance from the origin, and move everything at once, I think it will be OK.

    Why would they? Do they include scripts that depend in some way on their position? Those scripts should continue to reference map positions, which are different from actual Unity world coordinates. You will of course need functions to convert back and forth between the two coordinate systems, but I don't see why that should be a big problem.
     
  6. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    I'm actually quite noobish when it comes with colliders and gravity, so the reason I'm using the built-in physics is just for that, to avoid having floating items (or rather, to make the animals and plants correctly interact with the terrain).
    Both plants and animals already reference map positions aniway, it's just the idea behind moving them all around following the map (since they're not anchored to the map but rather laid upon it) that I'm afraid might not work.


    This being said, I kept looking for some ideas and someone suggested using two cameras, one following the player and the other on the opposite side of the map, changing the viewport so that when on the border, part of the scene is rendered from one camera and the other part from the second one. Then it'd be only a matter of teleporting the player (and, of course, every object which has a path that crosses the border) on the other side of the map.
     
  7. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,851
    You're still quite likely to run into floating-point issues when you have wandered kilometers away from the origin. All positions (and speeds etc.) in Unity are represented with floats, which have only 32 bits of precision. That's just not enough to work with meter-sized (or smaller) objects and positions, km away from zero.

    Also, using physics just to keep things on the terrain is silly. Drop a ray down, see where it intersects the terrain, and set your transform.position accordingly.

    I wish you luck with your project, but I see you have (twice now) chosen the Way of Pain. ;)
     
  8. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    If I understand correctly, this means that, say, going 4kms away from the origin will cause problems with floats? I'm not skeptical, I just don't know enough about this subject and am curious.
    My idea is that, when the player reaches the [1999;-20] coordinate and goes east, he will be teleported at the [-2000;20] coordinate. The rendering of the other side of the map could be done with a strategically placed extra camera and a change of the viewport so that both are used in the right percentage. This would work, if there wasn't any problem with floating point vars, but now I'm not sure it'll work anymore.

    I agree with you that it's silly, but again, I'm quite noobish when it comes to keeping things on ground without anchoring it. I'm going to look for guides on how to do this with raycasts asap, if it's faster and frees me from having troubles with the terrain then that's a very good thing to study

    Thanks :D I sure hope I can get out of this mess that I oh so lovingly put myself into!
     
  9. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    sorry to jump on this, what I've never workout, if for example you have a top down hack and clash game with monsters running around, could this system still work I've done it in pure .net winforms as an example no monsters just a circle moving around, but if you unload the tiles effectively how do you track if a monster bumps into a wall etc if it's not there?

    although all my maps are small 33 x 33, it would be nice to know for the future.
     
  10. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    generally you don't. For unloaded tiles if you want to continue to simulate what is going on there it's usually in a more abstract fashion (50% chance of new monster spawning, 10% chance a new loot item appears etc.), but it's done within the game logic/data not within the scene.

    If a tree falls down and no one is around to see it... just spawn a fallen log next time they go by :)
     
    JoeStrout likes this.
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,851
    This page is a pretty good explanation of how floats work, though they fail to go into precision problems. Basically you have 23 bits to represent the number, plus a sign and an exponent. So that means, if your numbers are generally less than 10, you can represent things with a precision of about 10/2^23. That's really good — if our units are meters, then this is a precision of about 0.001 millimeter.

    But if you're, say, 10 km away from the origin, then the best precision you can get is 10000/2^23, which is a little over a millimeter. That means you can't move something less than a millimeter. Worse, it may even mean that vertices of a mesh can't be positioned more precisely than within a millimeter or so of where things are supposed to be. And when you're trying to do physics calculations, and can't do better than a millimeter precision, weird stuff can start happening, where things don't collide that should, or slow-moving objects stop entirely, etc.

    Actually it's not all that bad at this point, but when you start getting hundreds or thousands of km away from the origin, then it really becomes problematic.

    In your case, going no more than 2000 units from the origin, you might be fine. It'd make me nervous, but you might get away with it nonetheless. :)

    It's definitely an important technique to have in your toolkit! The Physics.RayCast reference has some decent sample code. The most relevant one in this case is this one:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class RaycastExample : MonoBehaviour
    4. {
    5.     public Missile missile;
    6.  
    7.     void FixedUpdate()
    8.     {
    9.         RaycastHit hit;
    10.  
    11.         if (Physics.Raycast(transform.position, -Vector3.up, out hit, 100.0f))
    12.             print("Found an object - distance: " + hit.distance);
    13.     }
    14. }
    ...which casts a ray down from the object's position. If you want to be sure to hit the terrain, you could instead start at a high altitude (higher than any point in the terrain), and leave out the maxDistance parameter so the ray keeps going until it hits something.

    There's also a layer mask available that will allow your ray to ignore everything except the terrain. So then you know where the terrain is below the object, and you just set its Y position to that.
     
  12. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    That was a really good explanation, thank you! It's definitively something I have to sink my teeth into so I'll be more aware when using floating point vars from now on.

    Also, thanks for the tip on Raycast, this sparked more curiosity about that part of Unity! I'm going to study the argument asap.
     
    JoeStrout likes this.