Forum begins after the advertisement:
Enemies spawning behind camera
Home › Forums › Video Game Tutorial Series › Creating a Rogue-like Shoot-em Up in Unity › Enemies spawning behind camera
- This topic has 12 replies, 3 voices, and was last updated 1 week ago by Terence.
-
AuthorPosts
-
December 7, 2024 at 11:37 pm #16687::
Hello I have been following along with the tutorial nicely. It is an amazing series. I have pretty much everything built out and my enemy spawn system is working just fine, but it seems to spawn the enemies invisible for the first wave and then they seem to be working after that. it seems that the first couple of waves are spawning the enemies behind the camera because if i move the camera back a bit while the game is running then i can see them, but no matter how far back i put the camera it always spawns them behind. I pretty much have all the same code from the turorials and my main camera is set at -10 in the z position.
December 8, 2024 at 1:15 am #16689::Which part of the tutorial are you at currently?
I’ll need you to share your spawner script as well.
December 8, 2024 at 2:18 am #16692::Hello I am at the end of the tutorial, Here is my spawner script
using UnityEngine; using System.Collections.Generic; public class SpawnManager : MonoBehaviour { int currentWaveIndex; //The index of the current wave [Remember, a list starts from 0] int currentWaveSpawnCount = 0; // Tracks how many enemies current wave has spawned. List<GameObject> existingSpawns = new List<GameObject>(); public WaveData[] data; public Camera referenceCamera; [Tooltip("If there are more than this number of enemies, stop spawning any more. For performance.")] public int maximumEnemyCount = 300; float spawnTimer; // Timer used to determine when to spawn the next group of enemy. float currentWaveDuration = 0f; public bool boostedByCurse = true; public static SpawnManager instance; void Start() { if (instance) Debug.LogWarning("There is more than 1 Spawn Manager in the Scene! Please remove the extras."); instance = this; } void Update() { // Updates the spawn timer at every frame. spawnTimer -= Time.deltaTime; currentWaveDuration += Time.deltaTime; if (spawnTimer <= 0) { // Check if we are ready to move on to the new wave. if (HasWaveEnded()) { currentWaveIndex++; currentWaveDuration = currentWaveSpawnCount = 0; // If we have gone through all the waves, disable this component. if (currentWaveIndex >= data.Length) { Debug.Log("All waves have been spawned! Shutting down.", this); enabled = false; } return; } // Do not spawn enemies if we do not meet the conditions to do so. if (!CanSpawn()) { ActivateCooldown(); return; } // Get the array of enemies that we are spawning for this tick. GameObject[] spawns = data[currentWaveIndex].GetSpawns(EnemyStats.count); // Loop through and spawn all the prefabs. foreach (GameObject prefab in spawns) { // Stop spawning enemies if we exceed the limit. if (!CanSpawn()) continue; // Spawn the enemy. existingSpawns.Add( Instantiate(prefab, GeneratePosition(), Quaternion.identity) ); currentWaveSpawnCount++; } ActivateCooldown(); } } // Resets the spawn interval. public void ActivateCooldown() { float curseBoost = boostedByCurse ? GameManager.GetCumulativeCurse() : 1; spawnTimer += data[currentWaveIndex].GetSpawnInterval() / curseBoost; } // Do we meet the conditions to be able to continue spawning? public bool CanSpawn() { // Don't spawn anymore if we exceed the max limit. if (HasExceededMaxEnemies()) return false; // Don't spawn if we exceeded the max spawns for the wave. if (instance.currentWaveSpawnCount > instance.data[instance.currentWaveIndex].totalSpawns) return false; // Don't spawn if we exceeded the wave's duration. if (instance.currentWaveDuration > instance.data[instance.currentWaveIndex].duration) return false; return true; } // Allows other scripts to check if we have exceeded the maximum number of enemies. public static bool HasExceededMaxEnemies() { if (!instance) return false; // If there is no spawn manager, don't limit max enemies. if (EnemyStats.count > instance.maximumEnemyCount) return true; return false; } public bool HasWaveEnded() { WaveData currentWave = data[currentWaveIndex]; // If waveDuration is one of the exit conditions, check how long the wave has been running. // If current wave duration is not greater than wave duration, do not exit yet. if ((currentWave.exitConditions & WaveData.ExitCondition.waveDuration) > 0) if (currentWaveDuration < currentWave.duration) return false; // If reachedTotalSpawns is one of the exit conditions, check if we have spawned enough // enemies. If not, return false. if ((currentWave.exitConditions & WaveData.ExitCondition.reachedTotalSpawns) > 0) if (currentWaveSpawnCount < currentWave.totalSpawns) return false; // Otherwise, if kill all is checked, we have to make sure there are no more enemies first. existingSpawns.RemoveAll(item => item == null); if (currentWave.mustKillAll && existingSpawns.Count > 0) return false; return true; } void Reset() { referenceCamera = Camera.main; } // Creates a new location where we can place the enemy at. public static Vector3 GeneratePosition() { // If there is no reference camera, then get one. if (!instance.referenceCamera) instance.referenceCamera = Camera.main; // Give a warning if the camera is not orthographic. if (!instance.referenceCamera.orthographic) Debug.LogWarning("The reference camera is not orthographic! This will cause enemy spawns to sometimes appear within camera boundaries!"); // Generate a position outside of camera boundaries using 2 random numbers. float x = Random.Range(0f, 1f), y = Random.Range(0f, 1f); // Then, randomly choose whether we want to round the x or the y value. switch (Random.Range(0, 2)) { case 0: default: return instance.referenceCamera.ViewportToWorldPoint(new Vector3(Mathf.Round(x), y)); case 1: return instance.referenceCamera.ViewportToWorldPoint(new Vector3(x, Mathf.Round(y))); } } // Checking if the enemy is within the camera's boundaries. public static bool IsWithinBoundaries(Transform checkedObject) { // Get the camera to check if we are within boundaries. Camera c = instance && instance.referenceCamera ? instance.referenceCamera : Camera.main; Vector2 viewport = c.WorldToViewportPoint(checkedObject.position); if (viewport.x < 0f || viewport.x > 1f) return false; if (viewport.y < 0f || viewport.y > 1f) return false; return true; } }```
December 8, 2024 at 2:32 am #16693::Hmm.. Also my Props seem to have stopped dropping Health Items. My enemies drop the health items when i can get the spawning to work but the Props no longer drop the health items. I am using a Debug.Log to tell me in DroprateManager.OnDestroy Debug.Log(“Should Drop Something”);.. This gets called when killing enemies and destroying props with the drop rate at 100 percent, but again i cannot see the health items, yet the exp items display.
December 8, 2024 at 2:32 am #16694::using System.Collections; using System.Collections.Generic; using UnityEngine; public class DropRateManager : MonoBehaviour { [System.Serializable] //Serialize the class public class Drops { public string name; public GameObject itemPrefab; public float dropRate; } public bool active = false; public List<Drops> drops; void OnDestroy() { Debug.Log("Should Drop Something"); if (!active) return; if (!gameObject.scene.isLoaded) //Stops the spawning error from appearing when stopping play mode { return; } float randomNumber = UnityEngine.Random.Range(0f, 100f); List<Drops> possibleDrops = new List<Drops>(); foreach (Drops rate in drops) { if (randomNumber <= rate.dropRate) { possibleDrops.Add(rate); } } //Check if there are possible drops if (possibleDrops.Count > 0) { Drops drops = possibleDrops[UnityEngine.Random.Range(0, possibleDrops.Count)]; Instantiate(drops.itemPrefab, transform.position, Quaternion.identity); } } }
December 8, 2024 at 2:50 am #16695December 8, 2024 at 6:59 am #16696::Welp the Droprate manager was a goof on my part. i had the code commented out in the breakable props script to activate the droperate manager in Kill(). Still working on fixing my spawner issue though. I might just go back to the old one if i cant get it lol.
December 9, 2024 at 11:44 am #16702::Sorry for the late reply. Try adding the following to your
GeneratePosition()
function in the spawn manager:public static Vector3 GeneratePosition() { // If there is no reference camera, then get one. if (!instance.referenceCamera) instance.referenceCamera = Camera.main; // Give a warning if the camera is not orthographic. if (!instance.referenceCamera.orthographic) Debug.LogWarning("The reference camera is not orthographic! This will cause enemy spawns to sometimes appear within camera boundaries!"); // Generate a position outside of camera boundaries using 2 random numbers. float x = Random.Range(0f, 1f), y = Random.Range(0f, 1f); // Set the appropriate distance for spawning objects. For orthographic cameras, this can be arbitrary. float spawnDepth = 10f; // Adjust this value based on your scene's depth configuration. // Then, randomly choose whether we want to round the x or the y value. switch (Random.Range(0, 2)) { case 0: default: return instance.referenceCamera.ViewportToWorldPoint(new Vector3(Mathf.Round(x), y, spawnDepth)); case 1: return instance.referenceCamera.ViewportToWorldPoint(new Vector3(x, Mathf.Round(y), spawnDepth)); } }
This introduces a z-offset to the enemies’ spawn positions, which should fix your issue.
December 9, 2024 at 1:07 pm #16703December 10, 2024 at 2:19 am #16713::Yay it seems to be working! I have to fix some minor bugs but i will be posting screenshots soon. Thank you all so much! It has been a blast going through the series, and i have learned so much, and grown as a developer. I have been doing Android development for about 10 years now, I was into Unity back in like 2015 but I ended up getting a job in Android before I could pursue it further.
I have gone over the series about 3 times now and tried different ideas, haha I feel like I know the project like the back of my hand now. I have implemented raycast shooting, as well as a parallax infinite space background. Using your chunker system I am generating asteroids and potential planets that spawn randomly at a given percentage. Now that i have the enemy spawner and the weapons and leveling systems working, I would like to implement a system where when you find a planet it potentially notifies the player that a planet has spawned on a chunk, and this triggers, a “BossWave” event and spawns a boss, hopefully only allowing the enemy spawner to continue once the BossEvent ends. If not then it would at least be cool to have the boss show up.
This is my ultimate goal and I have come this far thanks to your help. I will share my progress soon, and let me know if you need anything or if I can be of any help to anyone in the community!
Cheers!
has upvoted this post. December 10, 2024 at 11:41 am #16714::Glad to hear your progress Von. I’ll be launching a coding challenge for this series soon. The winner will stand to win a copy of Black Myth: Wukong.
More details soon!
December 11, 2024 at 4:46 am #16731::Awesome, that sounds amazing! I’ll be checking in to see when it starts.
I have everything running rather nicely! I just spent all day yesterday and most of today setting up the mobile controls, smh it was such a pain in the rear to get all that set up properly, but i got it! It runs very smoothly on mobile, as well as web, I haven’t tested it on other systems yet. The only last issue is with the waves, it seems that with the assets i am using i guess or im not sure what the problem is yet but the mobs seem to bog the system down a bit. If i add like 20 waves and no mobs it runs perfectly, but if i add say one of your bat mobs, with the same settings i believe you are using in the tutorial, the system bogs down and it generates about 30 – 40 bats in the mob. Its only, a split second or so, not like it is freezing but you can tell when the mob is coming because it pauses for a sec. The flower ring mob on the other hand, will crash my game lol, and if it does make it past the first ring they spawn infinitely and i have to shut it down. I know i am using a bit of lighting and some parallax on the background, but like i said the standard waves, and the standard mobs pretty much work just fine. I am not sure if there is something i am missing, as to how i can limit the number of enemies that appear in the mobs?
has upvoted this post. December 11, 2024 at 6:24 pm #16737::The number of enemies should already be limited by the
EventManager
if I’m not wrong. The code checks for the same variables thatSpawnManager
does before spawning stuff, but I may have missed out something. It’s been awhile since I wrote the code.For the flower mob issue, check out this post from Kyle: https://blog.terresquall.com/community/topic/part-21-fixing-infinite-loops-for-mobs-spawned-by-eventmanager/
And here’s the coding challenge. We’re going to be announcing it proper in about 24 hours so you get a headstart! https://blog.terresquall.com/community/topic/coding-challenge-1-recreating-the-vampire-survivors-runetracer/
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: