so I am practicing Unity's scripting and trying to build a prototype of Pachinko game. What I want to achieve is when the ball hit an obstacle, it would change the color (material) - I have achieve that. But I want the material to be changed back after two seconds, this is where I am confused on how to proceed.
The problem lays on when the ball hits another obstacle before the 2 second marks, the other in OnCollisionEnter(Collision other) is assigned to the new obstacle therefore changing the material of the new obstacle back.
Here's my simple code using a function to changeback the material, and Invoke it with 2 seconds.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollisionBehavior : MonoBehaviour
{
[SerializeField] Material mats;
[SerializeField] Material defaultMats;
Renderer meshRenderer;
void OnCollisionEnter(Collision other) {
if (other.gameObject.tag == "Obstacle") {
meshRenderer = other.gameObject.GetComponent<Renderer>();
meshRenderer.material = mats;
Invoke("ChangeMatBack", 2);
}
}
void ChangeMatBack() {
meshRenderer.material = defaultMats;
}
}
I also tried using Coroutine, but the problem is the same.
void OnCollisionEnter(Collision other) {
if (other.gameObject.tag == "Obstacle") {
meshRenderer = other.gameObject.GetComponent<Renderer>();
meshRenderer.material = mats;
// Invoke("ChangeMatBack", 2);
StartCoroutine(waiting());
}
}
// void ChangeMatBack() {
// meshRenderer.material = defaultMats;
// }
IEnumerator waiting() {
yield return new WaitForSeconds(2f);
meshRenderer.material = defaultMats;
}
So TLDR, OnCollisionEnter function should make the material goes like Initial -> Changed, due to hits -> Wait two seconds -> Initial.
If I understand correctly all you would need to do is pass along the reference of the according Renderer
you want to reset instead of overwriting a class field:
[SerializeField] private Material mat;
private void OnCollisionEnter(Collision other)
{
if (!other.gameObject.CompareTag("Obstacle")) return;
var renderer = other.gameObject.GetComponent<Renderer>();
var defaultMaterial = renderer.material;
renderer.material = mat;
StartCoroutine(ResetRoutine(renderer, defaultMaterial));
}
private IEnumerator ResetRoutine(Renderer renderer, Material[] defaultMaterials)
{
yield return new WaitForSeconds(2f);
renderer.material = defaultMaterial;
}
OnCollisionEnter
can even be a Coroutine itself and you could even simply do:
[SerializeField] private Material mat;
private IEnumerator OnCollisionEnter(Collision other)
{
if (!other.gameObject.CompareTag("Obstacle")) yield break;
var renderer = other.gameObject.GetComponent<Renderer>();
var defaultMaterial = renderer.material;
renderer.material = mat;
yield return new WaitForSeconds(2f);
renderer.material = defaultMaterial;
}