- Home /
Projectile destruction with Observer pattern
I've build a little prototype game. Each player can shoot projectile and can't shoot again until it explodes (out of screen bounds, collision, etc). I'm using Observer pattern so player subscribes to projectile instance destruction delegate and changes its state when projectile destroyed. My question is: how do i unsubscribe from projectile that being destroyed?
public class Projectile : MonoBehaviour
{
[SerializeField] private float _speed = 5f;
private Rigidbody2D _rigidbody2D;
public event Action OnDestroy;
private void Awake()
{
_rigidbody2D = GetComponent<Rigidbody2D>();
}
private void FixedUpdate()
{
_rigidbody2D.MovePosition(_rigidbody2D.position + (Vector2)_rigidbody2D.transform.up * _speed * Time.fixedDeltaTime);
}
private void OnBecameInvisible()
{
OnDestroy?.Invoke();
Destroy(gameObject);
}
}
public class Player : MonoBehaviour
{
[SerializeField] private Projectile _projectilePrefab;
private bool _canShoot = true;
private void Update()
{
ShootHandler();
}
private void ShootHandler()
{
if (_canShoot == false)
return;
if (Input.GetButtonDown(KeyCode.Space)) {
_canShoot = false;
Projectile projectile = Instantiate(_projectilePrefab, transform.position, Quaternion.identity);
projectile.OnDestroy += ProjectileDestoyed;
}
}
private void ProjectileDestoyed()
{
_canShoot = true;
}
}
private void ProjectileDestoyed()
{
projectile.OnDestroy -= ProjectileDestoyed;
_canShoot = true;
}
isn't this working?
there is no projectile in ProjectileDestoyed function scope and it seems redundant to store projectile reference in player instance
What about returning the projectile object with event if you don't want to cache it.
something like this :
private void ProjectileDestoyed(projectile myProjectile)
{
myProjectile.OnDestroy -= ProjectileDestoyed;
_canShoot = true;
}
Answer by Hellium · Dec 27, 2019 at 10:52 AM
You don't really need to unsubscribe to the event, because the projectile object has been destroyed. You would need to unsubscribe to the event if the lifetime of the player was shorter than the lifetime of the projectile, but it does not seem to be the case.
Anyway, if you really want to, here is how you could do it:
public class Projectile : MonoBehaviour
{
[SerializeField] private float _speed = 5f;
private Rigidbody2D _rigidbody2D;
// OnDestroy is a function of Unity, you may have problems
// And I personally name my events with a past-tense verb
// meaning something has occured.
public event Action<Projectile> Destroyed;
// ...
private void OnBecameInvisible()
{
Destroyed?.Invoke(this);
Destroy(gameObject);
}
}
public class Player : MonoBehaviour
{
[SerializeField] private Projectile _projectilePrefab;
private bool _canShoot = true;
private void Update()
{
ShootHandler();
}
private void ShootHandler()
{
if (_canShoot == false)
return;
if (Input.GetButtonDown(KeyCode.Space)) {
_canShoot = false;
Projectile projectile = Instantiate(_projectilePrefab, transform.position, Quaternion.identity);
projectile.Destroyed += ProjectileDestoyed;
}
}
private void ProjectileDestoyed(Projectile destroyedProjectile)
{
destroyedProjectile.Destroyed -= ProjectileDestoyed;
_canShoot = true;
}
}
It works, but code seems weird somehow. Unsubscription needed for garbage collection.
Why do you think the code is weird?
When the object is destroyed, the event is destroyed to and the event callbacks are automatically removed and disposed so you don't have to worry.
I don't know. I guess its use of instance inside delegate of the same instance looks strange to me. I'll get used to things like this
Your answer
Follow this Question
Related Questions
How can Unity framework recognize the functions defined in child objects? 1 Answer
Lighting cubic patterns problem 0 Answers
How to spawn a random hexagon pattern? 0 Answers
Render material only when in front of specific objects 0 Answers
How do you make an algebra function for this pattern? 1 Answer