- Home /
Unable to delete object from a list using foreach loop
I have been unable to delete the spike Gameobject. It always throws a null reference exception error. I can't seem to figure out the issue here.
Check KillOldSpikes() the last function in the code.
(Line 225)
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameController : MonoBehaviour
{
public static GameController instance = null;
const float width = 3.7f;
const float height = 7f;
public float snakeSpeed = 1;
public BodyPart bodyPrefab = null;
public GameObject rockPrefab = null;
public GameObject eggPrefab = null;
public GameObject goldEggPrefab = null;
public GameObject spikePrefab = null;
public Sprite tailSprite = null;
public Sprite bodySprite = null;
public SnakeHead snakeHead = null;
public bool alive = true;
public bool waitingToPlay = true;
List<Egg> eggs = new List<Egg>();
List<Egg> spikes = new List<Egg>();
int level = 0;
int noOfEggsForNextLevel = 0;
public int score = 0;
public int hiScore = 0;
public Text scoreText = null;
public Text hiScoreText = null;
public Text tapToPlayText = null;
public Text gameOverText = null;
// Start is called before the first frame update
void Start()
{
instance = this;
Debug.Log("Starting Snake Game");
CreateWalls();
alive = false;
}
// Update is called once per frame
void Update()
{
if (waitingToPlay)
{
foreach (Touch touch in Input.touches)
{
if (touch.phase == TouchPhase.Ended)
{
StartGamePlay();
}
}
if (Input.GetMouseButtonUp(0))
StartGamePlay();
}
}
public void GameOver()
{
alive = false;
waitingToPlay = true;
gameOverText.gameObject.SetActive(true);
tapToPlayText.gameObject.SetActive(true);
}
void StartGamePlay()
{
score = 0;
level = 0;
scoreText.text = "Score = " + score;
hiScoreText.text = "HiScore = " + hiScore;
gameOverText.gameObject.SetActive(false);
tapToPlayText.gameObject.SetActive(false);
waitingToPlay = false;
alive = true;
KillOldEggs();
KillOldSpikes();
LevelUp();
}
void LevelUp()
{
level++;
noOfEggsForNextLevel = 4 + (level * 2);
snakeSpeed = 1f + (level / 4f);
if (snakeSpeed > 6) snakeSpeed = 6;
snakeHead.ResetSnake();
CreateEgg();
//KillOldSpikes();
CreateSpike();
}
public void EggEaten(Egg egg)
{
score++;
noOfEggsForNextLevel--;
if (noOfEggsForNextLevel == 0)
{
score += 10;
LevelUp();
}
else if (noOfEggsForNextLevel == 1) // last egg
CreateEgg(true);
else
CreateEgg(false);
if (score > hiScore)
{
hiScore = score;
hiScoreText.text = "HiScore = " + hiScore;
}
scoreText.text = "Score = " + score;
eggs.Remove(egg);
Destroy(egg.gameObject);
}
void CreateWalls()
{
float z = -1f;
Vector3 start = new Vector3(-width, -height, z);
Vector3 finish = new Vector3(-width, +height, z);
CreateWall(start, finish);
start = new Vector3(+width, -height, z);
finish = new Vector3(+width, +height, z);
CreateWall(start, finish);
start = new Vector3(-width, -height, z);
finish = new Vector3(+width, -height, z);
CreateWall(start, finish);
start = new Vector3(-width, +height, z);
finish = new Vector3(+width, +height, z);
CreateWall(start, finish);
}
void CreateWall(Vector3 start, Vector3 finish)
{
float distance = Vector3.Distance(start, finish);
int noOfRocks = (int)(distance * 3f);
Vector3 delta = (finish - start) / noOfRocks;
Vector3 position = start;
for (int rock = 0; rock <= noOfRocks; rock++)
{
float rotation = Random.Range(0, 360f);
float scale = Random.Range(1.5f, 2f);
CreateRock(position, scale, rotation);
position = position + delta;
}
}
void CreateRock(Vector3 position, float scale, float rotation)
{
GameObject rock = Instantiate(rockPrefab, position, Quaternion.Euler(0, 0, rotation));
rock.transform.localScale = new Vector3(scale, scale, 1);
}
void CreateEgg(bool golden = false)
{
Vector3 position;
position.x = -width + Random.Range(1f, (width * 2) - 2f);
position.y = -height + Random.Range(1f, (height * 2) - 2f);
position.z = -1f;
Egg egg = null;
if (golden)
egg = Instantiate(goldEggPrefab, position, Quaternion.identity).GetComponent<Egg>();
else
egg = Instantiate(eggPrefab, position, Quaternion.identity).GetComponent<Egg>();
eggs.Add(egg);
}
void CreateSpike()
{
Vector3 position;
position.x = -width + Random.Range(1f, (width * 2) - 2f);
position.y = -height + Random.Range(1f, (height * 2) - 2f);
position.z = -1f;
Egg spike = null;
spike = Instantiate(spikePrefab, position, Quaternion.identity).GetComponent<Egg>();
spikes.Add(spike);
}
void KillOldEggs()
{
foreach (Egg egg in eggs)
{
Destroy(egg.gameObject);
}
eggs.Clear();
}
void KillOldSpikes()
{
foreach (Egg spike in spikes)
{
Destroy(spike.gameObject);
}
spikes.Clear();
}
}
Answer by SteenPetersen · May 29, 2020 at 11:27 AM
You cannot delete things using a foreach because when using a foreach in C# you are actually only looking at a copy of the object no the object itself. Your best bet is to use a for loop where you are actually able to delete.
I would follow something like this.
How to remove elements from a generic list while iterating over it?
I checked out the thread. This is what I did. Still not working though :/
void KillOldSpikes()
{
for (int i = 0; i < spikes.Count; i++)
{
spikes.RemoveAt(i);
Destroy(spike.gameObject);
}
spikes.Clear();
}
Any idea where I might be messing up?
Did you use spike.gameObject or was it a typo? You should use spikes[i].gameObject. Also, you don't need RemoveAt, the clearing at the end should be fine.
@KoenigX3 Yeah I forgot to do that. $$anonymous$$y bad. Thanks for that :D
Hi there @DH00
This is what they mean in that thread from going in reverse. This code is tested and working.
[SerializeField] private List<GameObject> spikes = new List<GameObject>();
private void Start()
{
for (int i = spikes.Count - 1; i >= 0; i--)
{
Destroy(spikes[i]); // if you wish for the gameObject to be destroyed
spikes.RemoveAt(i);
}
// no need for the Clear() as the List should be empty
}
However, remember that Destroying a gameobject is somewhat expensive performance-wise so unless it happens very rarely or you are absolutely sure you will never use these types of objects again I would look into object pooling.
Btw if this helped you remember to upvote and mark as solved/correct answer.
But yes as @KoenigX3 accurately points out you could also not removeat and then only clear() at the end.
[SerializeField] private List<GameObject> spikes = new List<GameObject>();
private void Start()
{
for (int i = spikes.Count - 1; i >= 0; i--)
{
Destroy(spikes[i]); // if you wish for the gameObject to be destroyed
}
spikes.Clear();
}
Which is actually more performant.
You're a life saver man! I don't get any of those errors now :D Thanks a lot for helping out
Your answer
Follow this Question
Related Questions
List Elements Double With Replicate Data 0 Answers
null reference exception when I already checked if this was null 1 Answer
Does a new instance of a custom class return null? 2 Answers
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers