Search Unity

  1. Get all the Unite Berlin 2018 news on the blog.
    Dismiss Notice
  2. Unity 2018.2 has arrived! Read about it here.
    Dismiss Notice
  3. Improve your Unity skills with a certified instructor in a private, interactive classroom. Learn more.
    Dismiss Notice
  4. ARCore is out of developer preview! Read about it here.
    Dismiss Notice
  5. Magic Leap’s Lumin SDK Technical Preview for Unity lets you get started creating content for Magic Leap One™. Find more information on our blog!
    Dismiss Notice
  6. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Need Help With Procedural 2D Platformer

Discussion in '2D' started by Purplepigfarm, Jan 11, 2018.

  1. Purplepigfarm

    Purplepigfarm

    Joined:
    Aug 10, 2016
    Posts:
    25
    Hey there!

    What I've been trying to do the past few days is create a procedural 2d platformer made of pre-built rooms. I tried without rooms before, but that sometimes ended up with gaps between platforms impossible to jump, etc; Without even adding in any items or obstacles. So I then decided on pre-built rooms, and just finished building two different room prefebs to test with.

    The problem I'm running into is spawning the rooms in the correct position. Every part of each room is a child of an empty gameobject (to keep it all together), so whenever you instantiate its prefab to a location, the center of the room will be set to that location. I wasn't planning on making every room exactly the same dimensions, but if I need to then that's okay.

    I would like to have a trigger that detects the player before they make it to the end of the current room, and then chooses and spawns the next room to line up exactly with the current room.


    I'm open to all suggestions and advise. Thanks!
     
  2. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,241
    An easy way to line up rooms is to give the parent object a script with a reference to an empty gameobject as the connection point. One point for connecting to the previous room, and one point where the next room will line itself up.
     
    Purplepigfarm likes this.
  3. Purplepigfarm

    Purplepigfarm

    Joined:
    Aug 10, 2016
    Posts:
    25
    @jeffreyschoch That's brilliant! I'll try it right away. Any ideas on how the code might look?
     
  4. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,241
    Well I assume you have something that spawns rooms, so maybe have that keep track of the last room, or a list of all previous rooms. You could also use that list to deactivate or destroy old rooms.

    Once you spawn a new room, and having a reference to the old room, you could do something like this maybe:
    Code (CSharp):
    1. public class Room : MonoBehaviour
    2. {
    3.     public Transform startPoint;
    4.     public Transform endPoint;
    5. }
    Code (CSharp):
    1. newRoom.transform.position = lastRoom.endPoint.position - newRoom.startPoint.localPosition;
    You could even get really fancy and have your rooms have multiple start/end points, and randomly choose one, and block others, split the path etc. Adding to the randomness.
     
    Purplepigfarm likes this.
  5. Purplepigfarm

    Purplepigfarm

    Joined:
    Aug 10, 2016
    Posts:
    25
    Awesome! Thanks! Now all I need to do is figure out how to track lastRoom and newRoom. I don't think I should need a list of last rooms since I have a empty gameobject that follows the camera and destroys everything. Here's what I have so far:


    Code (CSharp):
    1. public class SpawnNextRoom : MonoBehaviour {
    2.     // Array of rooms to spawn
    3.     public GameObject[] obj;
    4.  
    5.     // 1st and 2nd rooms
    6.     private GameObject newRoom;
    7.     private GameObject lastRoom;
    8.  
    9.  
    10.     public void Spawn()
    11.     {
    12.         newRoom.transform.position = lastRoom.endPoint.position - newRoom.startPoint.localPosition;
    13.  
    14.         Instantiate(obj[Random.Range(0, obj.GetLength(0))], newRoom.transform.position , Quaternion.identity);
    15.     }
    16.  
    17.     private void OnTriggerEnter2D(Collider2D coll)
    18.     {
    19.         if (coll.tag == ("Player"))
    20.         {
    21.             Spawn();
    22.         }
    23.     }
    24. }


    Code (CSharp):
    1. public class Room : MonoBehaviour {
    2.  
    3.     public Vector2 startPoint;
    4.     public Vector2 endPoint;
    5. }
     
  6. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,241
    Don't forget that your "newRoom" and "lastRoom" variables should be of type "Room" to use them in the way you've written. Also your "startPoint" and "endPoint" would also need to be Transforms for the way you've written it.

    ie: "lastRoom.endPoint.position" would only make sense if "endPoint" was a Transform, and "lastRoom" is a "Room" type.

    Vector2's could work, but you'll either have to manually set and tweak those values to be correct, or code a widget function to show where those Vector2's represent in relation to the Room instance in the editor.

    You'll need to set "lastRoom = newRoom" before setting "newRoom" to the new instance. Lastly you'll need to check if "lastRoom == null" before trying to use it, if it's null then it's the first one so you don't have to account for start/end points.
     
    Purplepigfarm likes this.
  7. Purplepigfarm

    Purplepigfarm

    Joined:
    Aug 10, 2016
    Posts:
    25
    Oh wow. I had no idea you could set the type to one of your own classes. That makes things so much less confusing now.

    I made those changes, but I'm still confused about how I would set newRoom to the new instance.

    Sorry for all the questions, and thank you for answering them all!
     
    Last edited: Jan 13, 2018
  8. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,241
    You would create the new room object, get the Room component, and assign it to the variable like so:
    Code (CSharp):
    1. lastRoom = newRoom; // save the previous "newRoom"
    2. newRoom = Instantiate( ... ).GetComponent<Room>(); // create the new one
     
    Purplepigfarm likes this.
  9. Purplepigfarm

    Purplepigfarm

    Joined:
    Aug 10, 2016
    Posts:
    25
    Ah okay. I was missing GetComponent<Room>(). Now getting a NullReferenceException: Object reference not set to an instance of an object

    for line 18:
    Code (CSharp):
    1.  newRoom = Instantiate(obj[Random.Range(0, obj.GetLength(0))], newRoom.transform.position , Quaternion.identity).GetComponent<Room>();

    Code (CSharp):
    1. public class SpawnNextRoom : MonoBehaviour {
    2.     // Array of rooms to spawn
    3.     public GameObject[] obj;
    4.  
    5.     // 1st and 2nd rooms
    6.     private Room newRoom;
    7.     private Room lastRoom;
    8.  
    9.  
    10.     public void Spawn()
    11.     {
    12.         Debug.Log("Spawning");
    13.         lastRoom = newRoom;
    14.         newRoom = Instantiate(obj[Random.Range(0, obj.GetLength(0))], newRoom.transform.position , Quaternion.identity).GetComponent<Room>();
    15.         newRoom.transform.position = lastRoom.endPoint.position - newRoom.startPoint.localPosition;
    16.  
    17.     }
    18.  
    19.  
    20.     private void OnTriggerEnter2D(Collider2D coll)
    21.     {
    22.         if (coll.tag == ("Player"))
    23.         {
    24.             Debug.Log("Player Detected");
    25.             Spawn();
    26.         }
    27.     }
    28. }
     
  10. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,241
    That's happening the first time you run it, because "newRoom" doesn't exist yet, and you're trying to use "newRoom.transform" in the Instantiate line.

    try this instead, i broke apart some of the logic to make it easier to read:
    Code (CSharp):
    1. public void Spawn() {
    2.     Debug.Log("Spawning");
    3.     int randomIndex = Random.Range(0, obj.GetLength(0));
    4.     GameObject spawnPrefab = obj[randomIndex];
    5.     Vector3 spawnPosition = transform.position;
    6.  
    7.     // save the current newroom as lastroom
    8.     lastRoom = newRoom;
    9.  
    10.     // if last room exists, use its endpoint for the starting point
    11.     if (lastRoom != null) {
    12.         spawnPosition = lastRoom.endPoint.position;
    13.     }
    14.  
    15.     // create a new room
    16.     newRoom = Instantiate(spawnPrefab, spawnPosition, Quaternion.identity).GetComponent<Room>();
    17.  
    18.     // if the new room was successfully created (and has a Room component)
    19.     if (newRoom != null) {
    20.         // offset position by newRoom's startpoint
    21.         newRoom.transform.position -= newRoom.startPoint.localPosition;
    22.     }
    23. }
     
    Purplepigfarm likes this.
  11. Purplepigfarm

    Purplepigfarm

    Joined:
    Aug 10, 2016
    Posts:
    25
    I feel like a retarded noob now hahaha. Thank you so much man. All I need to do is tweak the positions and the collider and it'll be seamless and infinite.