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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How would you go about splitting an environment into 'rooms' and displaying on an in-game map?

Discussion in 'Scripting' started by TheADrain, Feb 1, 2017.

  1. TheADrain

    TheADrain

    Joined:
    Nov 16, 2016
    Posts:
    48
    Hi guys,

    I'm working on something that requires splitting up a 3D environment into 'rooms' that serves a couple different purposes.

    Firstly we need to display the players current location, easy enough the name of the current 'room' can be used. Secondly, we need to display a map that has the currently occupied room highlighted, and displays the players position within that room.

    So something like the old Resident Evil maps, or like Yakuza 0's street map (where the street you're on is highlighted in purple)

    But I'm not sure about the best way of actually segmenting the environment into 'rooms' and to build a map capable of being displayed that way.

    My gut feeling would be to place polygon or box colliders over the appropriate sections on a layer that is reserved for positioning/mapping data, and then associate each of those with a piece of 'room' data that holds the room id, name, and other potential attributes (such as maybe, player can save the game when in this 'room') and then to work out the players position based on their position inside of that collider, then relate that to the position of a polygon on the map screen which can be turned on/off to show the highlight.

    I'd assume I'd have to create the map polygons manually once I'd cemented the design for an area. It looks like Yakuza 0 might be creating it's street map by tagging polygons that make the actual road (or at least the collision for it) as there are some odd shapes here and there.

    Has anybody else done something like this before and could offer some advice/suggestions?

    Cheers guys!
     
  2. VengeanceDesign

    VengeanceDesign

    Joined:
    Sep 7, 2014
    Posts:
    84
    You could probably create a minimap by writing a editor tool to simplify the geometry until it's something that can be viewed on a minimap. You'd probably have to create a new mesh by going through your meshes and merge vertices that show detail your minimap doesn't need (these would be closer together than important vertices for the walls and floors). You'll probably want to bias your merging code to avoid merging vertices from faces with very different normals i.e. walls and floors. You could probably render your super simple mesh onto a large image. A custom shader may be best for rendering your simple mesh i.e. a shader that ignores textures and renders edges as thick lines.

    If your rooms are more or less square, you could probably create a system to detect what room the player is in fairly quickly. You would probably want to use a editor tool again. You can have your editor tool iterate through all room meshes and generate a box collider around them. Attach this box collider to a gameobject attached to your room mesh, and attach a script with your room information. Then you will probably have to manually go through your scripts individually and set the information OR you can have one script for each room that just references a file with the room information. I.e. your scripts just has a string such as "Dungeon 1" and at runtime it finds the data for "Dungeon 1" and loads it itself. Your box colliders will have to be put on a layer that all your physics and raycasts will ignore.

    Disclaimer: I've never done this, but I thought I'd suggest it since I've messed with meshes and stuff enough to know it should be possible.
     
  3. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    secondary orthographic camera with an appropriate culling mask and a selection of planes would do the trick... pipe it's output to a rendertexture and you can put that where you like in the UI as a raw image. Triggers in the doorway of the scene with references to the appropriate plane and a little script to add/remove them to the layer to show/hide with respects to the secondary camera.

    Minimaps have loads of tutorials for all kinds of things.
     
  4. TheADrain

    TheADrain

    Joined:
    Nov 16, 2016
    Posts:
    48
    Thanks for the input guys, I appreciate it.

    I suspect I'll have to use a version of VengeanceDesign's suggestion, although simplified as I don't have as much experience with meshes, and possibly reconstruct the full map from a collection of data pulled from different scenes.

    A simple minimap is super easy to do, as LeftyRighty pointed out, but what's confounding me about this one is that the map itself will encompass areas that are outside of the current scene. Each 'scene' is likely to be a small collection of 'rooms' but the map will be a representation of the entire game world (or at least that floor of it, anyway)

    Functionally very similar to Resident Evil, where you have the map of the entire mansion, even though the it only loads a single playable 'area' at a time. In that game it is simple as each scene only represents one room, you'd simply give each scene an 'id' and then assign that to a shape on the map which you turn red when the player is in that room.

    But if I also want to get the players position and orientation on the map I need to extrapolate that based on what area of the map that scene represents.

    I don't want to have to keep a separate scene for each individual room, but equally I don't think it's achievable to build the entire map in one scene and use the orthographic camera to render the map, either.

    I'll have to break the map up into 'sections' (East Wing, West Wing, etc) and then work out how to position them properly.
     
  5. VengeanceDesign

    VengeanceDesign

    Joined:
    Sep 7, 2014
    Posts:
    84
    I think you could use additive scene loading to load your scenes into a empty scene just for the purpose of minimap generation, then unload the scenes. This assumes your PC can handle the memory requirements. And of course scaling can be an issue. I recommend using a Octree to let you clamp down on the number of meshes whose vertices you have to compare, by making letting you only merge meshes that are close to each other. Either that or use a Physics.Spherecast()/Physics.BoxCast() which may or may not be slower, but will be simpler, to only try and merge a mesh with nearby meshes.

    A note about the additive scene loading: it will make sure the latest version of your scene is loaded.

    There is a few things to consider in terms of performance and general implementation:
    - Usually with modifying meshes you create dynamic lists for your vertices, triangles etc. Putting all your mesh data from all your meshes can use up a lot of memory. Consider first taking each mesh on it's own and performing vertex merging just on that mesh. Also note that most of your mesh data, like UVs etc should not be necessary. I think you only need vertex positions, triangles, vertex normals, and vertex colours.
    - Remember that your triangle list indexes relative to that mesh's vertex lists. So if you have n vertices in your vertex list already, the new triangles must have n added to their vertex references.
    - When choosing what kind of dynamic list remember that List<T> gives you instant access to a element, but requires reallocation when you add elements UNLESS you tell the list to allocate extra space in advance (list.Capacity property). As far as I know when deleting elements, every element after the one you delete is shuffled one space to the right.
    - You could use a matrix to track which meshes you've tried to merge already. i.e. if M[a] = true you've tried to merge meshes A and B
    - For distance calculations, use Vector3.SqrDistance() to avoid many costly square root calculations.
    - For comparing normals both getting the angle between the normals and getting the dot product of the normalized vectors would be costly, so do a quick performance measure i.e. 1000 Vector3.Angle() calculations vs 1000 Vector3.Dot(vector1.normalized, vector2.normalized) calculations to check which is quicker. Unfortunately you normally have to use one of the two, but if you just need to know whether the angle between vector1 and vector2 is >180, just take the dot product of the unnormalized vectors and see if it is positive or negative.