I checked all over SO but couldn't find an answer that worked (or at least that I understood how to implement).
I just started Unity not too long ago and I'm trying to make a Pong clone. The game work great on the first run, but when a player wins and it switches to scene 2, then you press the Play Again button and go back to scene 1 (gameplay scene) I get this error while trying to play:
heres my Start method for the problematic class.
public class BallMovement : MonoBehaviour
{
Rigidbody2D ball;
Vector2 vel;
// Start is called before the first frame update
void Start()
{
float posY = Random.Range(-150, 150);
ball = GameObject.FindGameObjectWithTag("Ball").GetComponent<Rigidbody2D>();
transform.localPosition = new Vector2(-19, posY);
vel.x = 250;
vel.y = -150;
ball.velocity = vel;
GameStateManager.PlayerScored = Start;
}
...
sorry for asking such a dumb/ vague question but I'm seriously stuck, I've been working on this pong clone for 8 hours and I managed to cross every hurdle on my own besides this
CodePudding user response:
Mmm I think your bug probably lies on this line:
GameStateManager.PlayerScored = Start;
I'm going to guess that GameStateManager
is somehow surviving across scene loads, whether that's a GameObject that doesn't destroy or a ScriptableObject or something.
What I think is happening is that you're adding a delegate to your PlayerScored
event and then that event isn't getting reset when you're changing scenes. When you come back to the scene the second time, that instance of the BallMovement
class has been deleted, and when that event fires you get the error The object of type 'BallMovement' has been destroyed but you are still trying to access it
.
Another kind of concerning thing I see with this is that you're having a method add itself to an event - when you run Start
the first time, on the Unity Start
callback, you add yourself to the PlayerScored
event.
Then, when PlayerScored
fires, you add Start
to the PlayerScored
event a second time. Then the second time PlayerScored
triggers it fires Start
twice, so you add it twice, and the third trigger now has four events to call. Duplicates are allowed in event invocation lists so you'll trigger Start
for every time it's in the PlayerScored
list, and every time it's called it adds itself again.
The way around this would be to move that functionality out of start, and to remove the delegate from the event when the GameObject is destroyed. Consider the following instead:
public class BallMovement : MonoBehaviour
{
Rigidbody2D ball;
Vector2 vel;
// Start is called before the first frame update
void Start()
{
GameStateManager.PlayerScored = OnPlayerScored;
OnPlayerScored(); // Fire this once on Start to get everything setup!
}
void OnPlayerScored()
{
float posY = Random.Range(-150, 150);
ball = GameObject.FindGameObjectWithTag("Ball").GetComponent<Rigidbody2D>();
transform.localPosition = new Vector2(-19, posY);
vel.x = 250;
vel.y = -150;
ball.velocity = vel;
}
void OnDestroy()
{
GameStateManager.PlayerScored -= OnPlayerScored;
}
// ...
CodePudding user response:
It seems you Delete/ Dispose the BallMovement
class. I assume this is because you Delete/ Dispose the ball, and don't create the new ball with the BallMovement
class/ script.