Hello,
I’m making a script which is a powerup that, when collected, the player will become invisible and enemies’ bullets will not collide with it during 5 seconds.
As I have multiple powerup that do different things, I found it easier to create a delegate on the player that receives the effects from the powerups which in turn are coroutines. I needed to do it because when the powerup is collected, it is destroyed, therefore I need to run the coroutine in the player script in order to not destroy the coroutine with the powerup. Also every powerup inherits from a Powerup parent class which handles the things every powerup should do.
Every powerup is working well, except for the one I described, the ghost thing, because it works different from the others. The problem happens when the player collects 2 of this powerup, the second one being collected before the effect of the first one ended.
I found that it happens because the first coroutine is still running when the second one starts, and then there is a conflict because while the first coroutine tries to make the player visible again by increasing it color’s alpha overtime, the second one tries to make the player invisible by decreasing said value. This same thing happens with the bullets not colliding thing. After this, the player remains kinda invisible forever, but not the way it would be, and in Layer 0 (which collides with bullets; therefore, the functional effect isn’t active, just the bugged visual effect). When the player collects 1 of this powerup at a time, however, it works just fine.
Here is my code for the powerup:
[SerializeField] float duration = 7f;
[SerializeField] float opacity = 0.25f;
[SerializeField] Material playerGhostMaterial; // Material with Rendering Mode == Transparent.
[SerializeField] Material playerMaterial; // Material with Rendering Mode == Opaque.
protected override void Awake()
{
base.Awake(); // Calling Awake method from Powerup parent class
playerGhostMaterial = new Material(playerGhostMaterial);
playerMaterial = new Material(playerMaterial);
}
protected override IEnumerator Effect(PlayerController player)
{
MeshRenderer[] meshRenderers = player.gameObject.GetComponentsInChildren<MeshRenderer>();
playerGhostMaterial.color = player.gameObject.GetComponent<MeshRenderer>().material.color; // Because each player has different colors from each other.
player.gameObject.GetComponent<MeshRenderer>().material = playerGhostMaterial;
while (System.Math.Round(meshRenderers[0].material.color.a, 2) > opacity) // Fading to invisible.
{
foreach (MeshRenderer meshRenderer in meshRenderers)
meshRenderer.material.color = new Color(meshRenderer.material.color.r, meshRenderer.material.color.g, meshRenderer.material.color.b, Mathf.Lerp(meshRenderer.material.color.a, opacity, 0.1f));
yield return null;
}
player.gameObject.layer = 8; // Layer 8 doesn't collide with bullets.
yield return new WaitForSeconds(duration); // Wait for the duration before the effect ends.
while (System.Math.Round(meshRenderers[0].material.color.a, 2) < 1.0f) // Fading to visible.
{
foreach (MeshRenderer meshRenderer in meshRenderers)
meshRenderer.material.color = new Color(meshRenderer.material.color.r, meshRenderer.material.color.g, meshRenderer.material.color.b, Mathf.Lerp(meshRenderer.material.color.a, 1.0f, 0.1f));
yield return null;
}
player.gameObject.layer = 0; // Layer 0 is default, collides with bullets.
playerMaterial.color = playerGhostMaterial.color;
player.gameObject.GetComponent<MeshRenderer>().material = playerMaterial;
yield break;
}
Can anybody help me?
Also, I’m kinda new to Unity coding, so if there’s a better way to do something here, please let me know! And just to confirm, if I used Update() to check if the effect should end, the performance would be worse, wouldn’t it?
Thank you!