- Home /
Ideas to help fix a bug in my world generation code?
So I've got the basics for 2D infinite persistent world generation. The way I have it set up is this:
Attached to the player is an empty that has a script that handles world generation functionality and the initial spawning of the world, and the information about the current world (world order list and such).
On my world chunk prefabs I have a trigger collider on the front and back. Each has a script that, when the player triggers the collider, determines if it needs to load a previously generated chunk, or create a new one. Then it destroys the chunk on the opposite side of the world, saving it's data to be read if it needs to be regenerated.
The problem is that because I'm using OnTriggerEnter2D, if the player stops inside of a trigger collider, then turns around, the world generation code doesn't initiate because it's not an OnTriggerEnter2D event.
My question is this, y'all got any ideas to fix this?
well, you could use an OnTriggerExit2D event ins$$anonymous$$d maybe, if you move it s.t. they are not located at the very edge, then check which way your player is going
youd get something like
float colliderXpos;
void Start(){
colliderXpos = transform.position.x;
}
void OnTriggerExit2D(collider2d col){
if(col.gameobject.tag == "player"){
if(col.transform.position.x > colliderXpos) SpawnWorldFront();
else SpawnWorldBack();
}
}
just make sure that there is some room on the old world object for your player to be exiting it so that he doesn't fall off while the new world data is generated/old data is loaded (or do some smart things in your code that ensure that doesn't happen anyway, that's possible too).
I did this and it works pretty well. There's just an issue when if I stop the player after he left the front trigger but hasn't touched the back trigger of the next chunk, then turns around, I get ground despawning when I don't want it to. This seems like an easier fix than previously though. Thanks for the push in the right direction.
Okay I lied, I can't figure out this one either. This is what I did. I created a bool called inBetween. Everytime I called OnTriggerExit2D and the situation was valid (exiting a front spawner going forward or exiting a back spawner going backwards), I set inBetween = true. Then I had OnTriggerEnter2D set up so that when the player entered a front spawner going backwards or a back spawner going forwards, it set inBetween = false. Then I check to see what value inBetween is before I spawn world chunks. However, since I do all chunk spawning in OnTriggerExit2D (in which inBetween gets set to true as soon as a valid movement happens) nothing spawns. Im trying to figure out a way to refactor my existing code so that I can have my spawning code outside of my OnTriggerExit2D while still depending on inBetween. Or maybe this is a bad way to do it?
Answer by GregoryNeal · Apr 26, 2015 at 05:27 PM
Here's what I did and fixed the issue (looks a lot nicer too):
First up, I scrapped everything, the code looked like shit and it made me feel bad as a programmer. I also got rid of every single collider that I had attached to my Ground object prefabs, I just left the edge colliders on top so that I could walk on it.
I've added a GroundDetector script whose job is to raycast downward from the player, only looking for the Ground layermask, it reads the instanceID of the ground object, then keeps raycasting at a set interval and comparing the instanceID of the raycast2D gameobject to the previous one. When they are not equal, this is when I know I've left my current ground object.
Now it's pretty easy to get the velocity of my character, with this I know which direction I need to spawn ground. And it calls a script thats a sibling of GroundDetector, called GroundManager. This spawns my ground.
Here is my raycast script if anyone wants to use it:
using UnityEngine;
using System.Collections;
public class GroundDetector : MonoBehaviour {
public LayerMask mask;
int init;
float time = 0;
int currGroundID;
// Use this for initialization
void Start () {
currGroundID = GetGroundID();
StartCoroutine("RayCastGround",currGroundID);
}
IEnumerator RayCastGround(int currID){
while(currGroundID == currID){
yield return new WaitForSeconds(.01f);
currID = GetGroundID();
}
currGroundID = currID;
InitiateSpawn();
}
int GetGroundID(){
Vector2 pos = new Vector2(transform.position.x,transform.position.y);
RaycastHit2D hit = Physics2D.Raycast(pos,-Vector2.up,Mathf.Infinity,mask);
if(hit.collider != null){
return hit.collider.gameObject.GetInstanceID();
}
else{
return -1;
}
}
void InitiateSpawn(){
//just in case??
StopCoroutine("RayCastGround");
//tell GroundManager to spawn in front or behind
float vel = GetComponentInParent<Rigidbody2D>().velocity.x;
if(vel > 0){
this.GetComponent<GroundManager>().SpawnInFront();
}
else if(vel < 0){
this.GetComponent<GroundManager>().SpawnInBack();
}
StartCoroutine("RayCastGround",currGroundID);
}
}
if you're trying to make some kind of 'endless world' game, you should move the world ins$$anonymous$$d of the player, in order to avoid ending up having to deal with floating-point accuracy problems. (or do a floating origin)
Doing this without colliders is better though, that was my first idea but I figured "rewrite all your code to use a completely different method" wasn't the kind of answer you wanted :P
Yea my original code was pretty shit, but a benefit of the new methods I wrote lets me easily pick up everything and move it back to the origin if I get to a certain coordinate.
Your answer
Follow this Question
Related Questions
Can someone tell me how to create a city similar to EGYPT in unity 5.3.2?? 0 Answers
How to use marching cubes to turn cube terrain into voxel terrain 0 Answers
Changing Terrain tint color via script 1 Answer
Infinite Range, Smooth, Day-Night Cycle 3 Answers
Why does my trigger only fire one time? 0 Answers