I'm trying to create projection of the ship trajectory, but I think it is too resource expensive to be usable since the editor freezes after a couple of seconds.
The trajectory has to take into account a couple of things:
Player's velocity (and input)
Player being attracted by other planets. (I THINK this is included in the velocity vector).
I've tried moving expensive operations outside the function that is called at FixedUpdate, link GetComponent or move to scene, but it still freezes, a little less but freezes.
My best guess is that the physics simulation is too expensive to run on FixedUpdate, but I don't know if there is a better method to call it on.
Here is my current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
public class Player : MonoBehaviour
{
...
[SerializeField] public Rigidbody playerRigidBody;
// Projection
private Scene _simulationScene;
private PhysicsScene _physicsScene;
[SerializeField]
private int _maxPhysicsFrameIterations = 10;
private GameObject _ghostObj;
[SerializeField]
private GameObject _markerPrefab;
private List<GameObject> _markersPool;
// Input Values
...
private Rigidbody _ghostRigidBody;
...
// Start is called before the first frame update
void Start()
{
playerRigidBody = GetComponent<Rigidbody>();
currentBoostAmount = maxBoostAmount;
// Projection
_markersPool = new List<GameObject>();
for (int i = 0; i < _maxPhysicsFrameIterations; i++)
{
GameObject marker = Instantiate(_markerPrefab);
_markersPool.Add(marker);
}
CreatePhysicsScene();
}
private void OnDisable()
{
Attractor.players.Remove(this);
Destroy(_ghostObj);
}
// Update is called once per frame
void FixedUpdate()
{
HandleBoosting();
HandleMovement();
if (thrust1D != 0 || upDown1D != 0 || strafe1D != 0 || roll1D != 0 || pitchYaw != Vector2.zero)
{
SimulateTrajectory();
}
}
void CreatePhysicsScene()
{
var scene = SceneManager.GetSceneByName("SimulationScene");
var isSimulatioSceneValid = scene.IsValid();
if (isSimulatioSceneValid)
{
_simulationScene = scene;
}
else
{
_simulationScene = SceneManager.CreateScene("SimulationScene", new CreateSceneParameters(LocalPhysicsMode.Physics3D));
}
_physicsScene = _simulationScene.GetPhysicsScene();
_ghostObj = Instantiate(this.gameObject, transform.position, transform.rotation);
_ghostObj.GetComponent<PlayerInput>().enabled = false;
_ghostObj.GetComponent<Renderer>().enabled = false;
_ghostRigidBody = _ghostObj.GetComponent<Rigidbody>();
SceneManager.MoveGameObjectToScene(_ghostObj, _simulationScene);
}
public void SimulateTrajectory()
{
Physics.autoSimulation = false;
_ghostObj.transform.position = transform.position;
_ghostRigidBody.velocity = playerRigidBody.velocity;
for (int i = 0; i < _maxPhysicsFrameIterations; i++)
{
_physicsScene.Simulate(i + 1 / _maxPhysicsFrameIterations);
_markersPool[i].transform.position = _ghostObj.transform.position;
}
Physics.autoSimulation = true;
}
}
Related
I am working on a flocking system in Unity and am new to c#. I am working with 2 scripts - 1 that manages the overall flock (FlockTest) and the other that manages particle behaviour (FlockParticleBehaviour). I have followed a tutorial which has public boolean values that control seeking behaviour in FlockParticleBehaviour through FlockTest. In play mode, I can toggle these booleans to change the goal seeking behaviour. However, I want to automate this toggling based on time (To add it to an AR session). I have added an if statement to void Update() in the FlockTest and when I hit play, the seekGoal and obedient boolean boxes switch on and off but nothing happens to the particles. I have tried using an invoke method which didn't work(no errors but boxes dont switch on and off) and thought about trying a coRoutine but I am not sure this will work since I don't want to stop and start my script. I am at a loss as to how to get the particles obeying the boolean in update. Am I meant to be referencing in my particle behaviour script's flock function? Very new so would love some help if anyone knows a better way forward!
FlockTest script (contains if statement)
using System.Collections.Generic;
using UnityEngine;
public class FlockTest : MonoBehaviour
{
public GameObject[] particles;
public GameObject particlePrefab;
public int particleCount = 10;
public Vector3 range = new Vector3(5,5,5);
public Vector3 innerLimit = new Vector3(1,1,1);
public bool seekGoal = true;
public bool obedient = true;
public bool willful = false;
[Range(0, 200)]
public int neighbourDistance =50;
[Range(0,2)]
public float maxForce = 0.5f;
[Range(0,5)]
public float maxvelocity = 2.0f;
// Start is called before the first frame update
void Start()
{
int time = (int)Time.time;
particles = new GameObject[particleCount];
for(int i = 0; i < particleCount; i++)
{
Vector3 particlePos = new Vector3(Random.Range(-range.x, range.x), Random.Range(-range.y, range.y), Random.Range(-range.z, range.z));
particles[i] = Instantiate(particlePrefab, this.transform.position + particlePos, Quaternion.identity) as GameObject;
particles[i].GetComponent<FlockParticleBehaviour>().manager = this.gameObject;
}
}
void Update()
// the toggles in the inspector are changing but nothing is happening with the particles.
{
int time = (int)Time.time;
if(time == 3f) {
seekGoal = false;
obedient = false;
willful = true;
}
if(time == 6f)
{
seekGoal = true;
obedient = true;
willful = false;
}
}
}
FlockParticleBehaviour script
using System.Collections.Generic;
using UnityEngine;
public class FlockParticleBehaviour : MonoBehaviour
{
public GameObject manager;
public Vector3 location = Vector3.zero;
public Vector3 velocity;
Vector3 goalPos = Vector3.zero;
Vector3 currentForce; //this is a current force position. pushes particle around by adding all the other forces
// Start is called before the first frame update
void Start()
{
velocity = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f));
location = new Vector3(this.gameObject.transform.position.x, this.gameObject.transform.position.y, this.gameObject.transform.position.z);
}
Vector3 seek(Vector3 target)
{
return(target - location);
}
void applyForce(Vector3 f)
{
Vector3 force = new Vector3(f.x, f.y, f.z);
if(force.magnitude > manager.GetComponent<FlockTest>().maxForce)
{
force = force.normalized;
force *= manager.GetComponent<FlockTest>().maxForce;
}
this.GetComponent<Rigidbody>().AddForce(force);
if(this.GetComponent<Rigidbody>().velocity.magnitude > manager.GetComponent<FlockTest>().maxvelocity)
{
this.GetComponent<Rigidbody>().velocity = this.GetComponent<Rigidbody>().velocity.normalized;
this.GetComponent<Rigidbody>().velocity *= manager.GetComponent<FlockTest>().maxvelocity;
}
Debug.DrawRay(this.transform.position, force, Color.white);
}
Vector3 align()
{
float neighbourdist = manager.GetComponent<FlockTest>().neighbourDistance;
Vector3 sum = Vector3.zero;
int count = 0;
foreach (GameObject other in manager.GetComponent<FlockTest>().particles)
{
if(other == this.gameObject) continue;
float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);
if (d < neighbourdist) {
sum += other.GetComponent<FlockParticleBehaviour>().velocity;
count++;
}
}
if (count >0)
{
sum /= count;
Vector3 steer = sum - velocity;
return steer;
}
return Vector3.zero;
}
Vector3 cohesion()
{
float neighbourdist = manager.GetComponent<FlockTest>().neighbourDistance;
Vector3 sum = Vector3.zero;
int count = 0;
foreach (GameObject other in manager.GetComponent<FlockTest>().particles)
{
if(other == this.gameObject) continue;
float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);
if(d < neighbourdist)
{
sum += other.GetComponent<FlockParticleBehaviour>().location;
count++;
}
}
if (count > 0)
{
sum /= count;
return seek(sum);
}
return Vector3.zero;
}
void flock()
{
location = this.transform.position;
velocity = this.GetComponent<Rigidbody>().velocity;
if(manager.GetComponent<FlockTest>().obedient && Random.Range(0,50) <=1)
{
Vector3 ali = align();
Vector3 coh = cohesion();
Vector3 gl;
if(manager.GetComponent<FlockTest>().seekGoal)
{
gl = seek(goalPos);
currentForce = gl + ali +coh;
}
else
currentForce = ali + coh;
currentForce = currentForce.normalized;
}
if(manager.GetComponent<FlockTest>().willful && Random.Range(0,50)<=1)
{
if(Random.Range(0,50)<1) //change direction
currentForce = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f),Random.Range(0.01f, 0.1f));
}
applyForce(currentForce);
}
// Update is called once per frame
void Update()
{
flock();
goalPos = manager.transform.position;
}
}
Several points:
it is much easier and cleaner to set your flock manager directly as FlockTest, not GameObject to avoid GetComponent calls.
I cannot understand what you want to achieve by calling (int)Time.time and comparing it later with 3 and 6. Time.time returns the number of seconds that passed from the start of the application. So your code in Update method of FlockTest script will not have any chance to be called after the seventh second of your game passed. So obedient will always be true and willful will always be false after the seventh second.
Your Random.Range(0, 50) <= 1 is quite a low chance. It will return an int value from 0 to 49, so it is only a 2% chance that your changes in FlockTest will apply to FlockParticleBehaviour instance. Is it what you wanted to get? You can try to remove this random from the if statement to make this chance 100% and check if this is an issue.
Right now it seems like the chance of changing something is too low to see it in several seconds of the game. As I've said above, after the seventh second your bool values will never change.
This question already has answers here:
How to make the script wait/sleep in a simple way in unity
(7 answers)
Closed 1 year ago.
This is the waypoints manager script attached to empty GameObject :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class WaypointsManager : MonoBehaviour
{
public GameObject npcPrefab;
public int numberOfNpcs;
public GameObject waypointsPrefab;
public List<GameObject> waypoints = new List<GameObject>();
public int numberOfWaypoints;
public bool useWaypointsPrefab = false;
private GameObject waypointObject;
// Start is called before the first frame update
void Awake()
{
for (int i = 0; i < numberOfWaypoints; i++)
{
if (useWaypointsPrefab)
{
waypointObject = Instantiate(npcPrefab, Vector3.zero, Quaternion.identity);
}
else
{
waypointObject = new GameObject();
}
waypointObject.tag = "Waypoint";
waypointObject.name = "Waypoint";
waypointObject.transform.position = new Vector3(Random.Range(0, 10), Random.Range(0, 10), Random.Range(0, 10));
waypoints.Add(waypointObject);
}
for (int i = 0; i < numberOfNpcs; i++)
{
if (npcPrefab != null)
{
GameObject npc = Instantiate(npcPrefab, Vector3.zero, Quaternion.identity);
}
}
}
// Update is called once per frame
void Update()
{
}
}
And this script is attached to each npc :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public List<GameObject> waypoints = new List<GameObject>();
public float movementSpeed;
public float rotationSpeed;
public bool reverse = false;
public bool go = false;
public int numberOfWaypoints;
public int nextWaypointNumber;
private int waypointIndex = 0;
private GameObject nextWayPoint;
// Start is called before the first frame update
void Start()
{
waypoints = GameObject.FindGameObjectsWithTag("Waypoint").ToList();
numberOfWaypoints = waypoints.Count;
if (reverse)
{
waypointIndex = waypoints.Count - 1;
}
else
{
waypointIndex = 0;
}
StartCoroutine(MoveNpc());
}
// Update is called once per frame
void Update()
{
if (go)
{
if (reverse && waypointIndex == 0)
{
waypointIndex = waypoints.Count - 1;
}
if (reverse == false && waypointIndex == waypoints.Count)
{
waypointIndex = 0;
}
nextWayPoint = waypoints[waypointIndex];
nextWaypointNumber = waypointIndex;
transform.position = Vector3.MoveTowards(transform.position,
waypoints[waypointIndex].transform.position, Time.deltaTime * movementSpeed);
float distance = Vector3.Distance(transform.position, waypoints[waypointIndex].transform.position);
if (distance > 0f)
{
// Try to rotate to face the waypoint only if we're not on top of it.
var rotation = Quaternion.LookRotation(nextWayPoint.transform.position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotationSpeed);
}
else
{
numberOfWaypoints--;
if (reverse)
{
waypointIndex--;
}
else
{
waypointIndex++;
}
}
}
}
private IEnumerator MoveNpc()
{
yield return new WaitForSeconds(3f);
go = true;
}
private void OnDrawGizmos()
{
if (waypoints != null)
{
for (int i = 0; i < waypoints.Count; i++)
{
Gizmos.color = Color.green;
Gizmos.DrawSphere(waypoints[i].transform.position, 0.1f);
}
}
if (nextWayPoint != null)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, nextWayPoint.transform.position);
}
}
}
In the Waypoints script I'm starting a coroutine with a 3 seconds delay but still all the npcs are moving at the same time like one npc. I want it to wait 3 seconds send npc wait 3 seconds send npc until all npcs are moving along the waypoints.
StartCoroutine(MoveNpc());
ISSUE
All npcs are being spawned at the same time, each waits 3 seconds (all at the same time), and then start moving.
SOLUTION
You could create a function that takes the amount of time they need to wait, and use that delay time in your coroutine.
NPC
public void StartMovingAfterSeconds(float seconds)
{
StartCoroutine(MoveNPC(seconds));
}
private IEnumerator MoveNpc(float delayTimeSeconds)
{
yield return new WaitForSeconds(delayTimeSeconds);
go = true;
}
From your manager you will need to track the delay time as you spawn the npcs.
MANAGER
// Amount to delay movement by (can be exposed in the inspector)
//
var delayTime = 3f;
// Accumulated delay
//
var currentDelay = 0f;
if (npcPrefab != null)
{
for (int i = 0; i < numberOfNpcs; i++)
{
var npc = Instantiate(npcPrefab, Vector3.zero, Quaternion.identity);
npc.StartMovingAfterSeconds(currentDelay);
currentDelay += delayTime;
}
}
We start with a delay of 0 (currentDelay) and add the amount we want to delay by (delayTime) in each iteration. The first delay is 0, next is 3, then 6, etc..
I am currently working on a 2D isometric game where the player will be able to control different units, and interact through them.
I created a Scriptable Object called UnitType. Thanks to this system I can define an action range and a moving range, that is the maximum distance in cells that the player can move to or interact with.
The problem is I don't know how to implement this through code. This is what I want to achieve, using an action range of 2 cells for the demonstration.
This is the goal
With a friend of mine, we thought about calculating the linear equation of those 4 lines to check if the raycast hit was within them, but it's not working right with negative x.
This is the current system
What would be the best practice ?
Thank you very much for your time and attention,
Mousehit is a Unity GameObject that is the child of the character. Its positions is bound to the center of the tilemap cells thanks to mousePosition that is a script attached to the GameManager.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class Interact : MonoBehaviour
{
[SerializeField] private Sprite upperLeft, upperRight, midLeft, midRight, bottomLeft, bottomMid, bottomRight;
[SerializeField] private GameObject mouseHit;
public UnitType unitType;
private GameObject gameManager;
private CheckMousePosition mousePosition;
private bool isInteracting, isAtGoodDistance;
private SpriteRenderer spriteRenderer;
private Tilemap tilemap;
private RaycastHit2D hit;
//private Transform mouseHit;
void Start()
{
tilemap = GameObject.Find("Tilemap").GetComponent<Tilemap>();
gameManager = GameObject.FindGameObjectWithTag("GameManager");
mousePosition = gameManager.GetComponent<CheckMousePosition>();
spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
isInteracting = false;
mouseHit.SetActive(false);
}
// Update is called once per frame
void Update()
{
if (isInteracting)
{
mouseHit.SetActive(true);
mouseHit.transform.position = new Vector3(mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).x, mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).y, 1);
if (Input.GetMouseButtonDown(0))
{
Interaction();
}
}
if (isInteracting == false)
{
spriteRenderer.sprite = null;
mouseHit.SetActive(false);
}
}
public void InitInteraction()
{
isInteracting = true;
transform.root.GetComponent<ContextualMenu>().CloseContextualMenu();
}
private void Interaction()
{
//When the player interacts, we cast a ray that will determine what he is interacting with.
hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hit.collider != null)
{
if (isInteracting)
{
isInteracting = false; //the player is no longer interacting.
//If the player is interacting with a farmable cell using the worker.
if (transform.root.name == "Worker"
&& hit.collider.GetComponent<FarmableCell>() != null)
{
CheckInteractionDistance();
//hit.collider.GetComponent<FarmableCell>().CheckIfFarmed();
}
}
}
}
private void CheckInteractionDistance()
{
if (mouseHit.transform.localPosition.y <= (-0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (-0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x <= (0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2))
{
Debug.Log("ok");
}
else
{
Debug.Log("not ok");
}
}
}
Alright, so here's what's happening: When I hit play and left click to shoot, unity editor freezes and I have to do the old Ctrl + Alt + Del, now, I am almost certain this script is the source of the issue, because when a bullet is shot, this script is immediately added to it, so here's the script(It's called BulletLife.cs, just letting you know)
using System.Timers;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class BulletLife : MonoBehaviour
{
public GameObject bullet;
public double bulletLifeSpan = 3;
bool bulletLifeEnded;
public LayerMask targetMask;
bool hasHitTarget;
// Start is called before the first frame update
void Start()
{
var bulletAge = new System.Timers.Timer(bulletLifeSpan * 1000);
bulletAge.Elapsed += OnTimedEvent;
bulletAge.AutoReset = false;
while(hasHitTarget == false && bulletLifeEnded == false) {
hasHitTarget = Physics.CheckSphere(bullet.transform.position, bullet.transform.localScale.y, targetMask);
}
Destroy(bullet);
Debug.Log("Finish");
}
private void OnTimedEvent(System.Object Source, ElapsedEventArgs e) {
bulletLifeEnded = true;
}
}
Also, here's the Shoot.cs script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shoot : MonoBehaviour
{
public Transform gun;
public GameObject bullet;
public LayerMask targetMask;
public float bulletSpeed = 1000f;
bool hasHitTarget = false;
// Update is called once per frame
void Update()
{
if(Input.GetButtonDown("LeftClick")) {
GameObject bulletInstance;
bulletInstance = Instantiate(bullet, gun.position, new Quaternion(gun.rotation.w, gun.rotation.x, gun.forward.y, gun.rotation.z));
bulletInstance.AddComponent<Rigidbody>();
bulletInstance.GetComponent<Rigidbody>().useGravity = false;
bulletInstance.GetComponent<Rigidbody>().AddForce(gun.up * bulletSpeed);
bulletInstance.AddComponent<BulletLife>();
bulletInstance.GetComponent<BulletLife>().bullet = bulletInstance;
}
}
}
NOTE: I am using Unity 2019.4.15f1
Well everytime you instantiate a bullet in Start you do
while(hasHitTarget == false && bulletLifeEnded == false)
{
hasHitTarget = Physics.CheckSphere(bullet.transform.position, bullet.transform.localScale.y, targetMask);
}
this loop will never finish since none of the conditions is changed inside the loop. There either is a hit or not .. but then the parameters for the raycast are never changed, the position isn't updated since you are still in the same frame => endless loop => freeze the main thread completely.
What you rather wanted to do is move that thing to Update which is called once a frame like e.g.
//public GameObject bullet; // not needed
public double bulletLifeSpan = 3;
//bool bulletLifeEnded; // not needed
public LayerMask targetMask;
//bool hasHitTarget; // not needed
void Start()
{
var bulletAge = new System.Timers.Timer(bulletLifeSpan * 1000);
bulletAge.Elapsed += OnTimedEvent;
bulletAge.AutoReset = false;
}
private void Update()
{
if(Physics.CheckSphere(transform.position, transform.localScale.y, targetMask))
{
Destroy(gameObject);
Debug.Log("Finish");
}
}
private void OnTimedEvent(System.Object Source, ElapsedEventArgs e)
{
Destroy(gameObject);
Debug.Log("Finish");
}
Or make it a single Coroutine
// If Start returns IEnumerator it is automatically started as Coroutine
// So no need to start an extra routine
private IEnumerator Start()
{
// Keeps track of how long your bullet exists already
var bulletAge = 0f;
while(bulletAge < bulletLifeSpan && !Physics.CheckSphere(transform.position, transform.localScale.y, targetMask))
{
// Increase by the time passed since last frame
bulletAge += Time.deltaTime;
// "Pause" this routine, render this frame
// and continue from here in the next frame
yield return null;
}
Destroy(gameObject);
Debug.Log("Finish");
}
Btw note that in Shoot you can shorten this a lot
void Update()
{
if(Input.GetButtonDown("LeftClick"))
{
// Note that your quaternion made no sense -> simply pass in the gun.rotation
var bulletInstance = Instantiate(bullet, gun.position, gun.rotation);
var rb = bulletInstance.AddComponent<Rigidbody>();
rb.useGravity = false;
rb.AddForce(gun.up * bulletSpeed);
var life = bulletInstance.AddComponent<BulletLife>();
// Assigning the gameObject reference is completely unnecessary
// within BulletLife simply use "gameObject" as show before
}
}
You could shorten this even more by making sure these components already exist on your prefab object and are configured correctly. Then you wouldn't need any of these line but just Instantiate it.
And finally you shouldn't use thisCheckSphere at all but rather let Unity handle its Collision detection itself and use OnCollisionEnter and configure your Collision Layers according to your needs!
The issue with your solution is: If your bullet moves fast it might simply pass a target without your CheckSphere noting it namely if its velocity is higher then localScale.y * 2.
Your Start method is blocking, thus freezing your game.
You'll have to use Update or a Coroutine to make your hit tests.
public class BulletLife : MonoBehaviour
{
public GameObject bullet;
public double bulletLifeSpan = 3;
bool bulletLifeEnded;
public LayerMask targetMask;
bool hasHitTarget;
// Start is called before the first frame update
void Start()
{
StartCoroutine(CheckHit(0, bulletLifeSpan));
}
private IEnumerator CheckHit(float interval, float lifetime){
bool checkEveryFrame = interval <= 0;
WaitForSeconds wait = checkEveryFrame ? null : new WaitForSeconds(interval);
while(lifetime > 0){
yield return wait;
lifetime = lifetime - (checkEveryFrame ? Time.deltaTime : interval);
hasHitTarget = Physics.CheckSphere(bullet.transform.position, bullet.transform.localScale.y, targetMask);
}
bulletLifeEnded = true;
Destroy(bullet);
Debug.Log("Finish");
}
}
To give some context:
In unity I have 2 boxes, which are both tagged "box"
One box is on a plane and the other in the air, when the game is played on box falls on the other.
Here is the console for the engine:
Material 1 (UnityEngine.Material)
UnityEngine.Debug:Log(Object)
colourChangeArray:OnCollisionEnter(Collision) (at Assets/colourChangeArray.cs:28)
With material 1 changing to material 2, 3, 4 to 5 and then doing the 1-5 cycle 10-20 times
Below is the code i'm using
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class colourChangeArray : MonoBehaviour
{
public int trigger = 1;
public Material[] material;
Renderer rend;
// Use this for initialization
void Start()
{
rend = GetComponent <Renderer> ();
rend.enabled = true;
rend.sharedMaterial = material[0];
}
// Update is called on collision
void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "box")
{
for(int i = 0; i <material.Length; i++){
rend.sharedMaterial = material[i];
Debug.Log(rend.sharedMaterial);
}
}
else
{
rend.sharedMaterial = material[0];
Debug.Log(rend.sharedMaterial);
}
}
}
The cycle between materials happen a few times because of the internal bounciness of the objects in unity. You can decrease it somehow (which I don't remember) but, I had a similar case once and no matter how much you decrease that it happens. So the best approach would be to store a value indicating it has been done before like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class colourChangeArray : MonoBehaviour
{
public int trigger = 1;
public Material[] material;
Renderer rend;
private bool changeMaterialDone;
// Use this for initialization
void Start()
{
rend = GetComponent <Renderer> ();
rend.enabled = true;
rend.sharedMaterial = material[0];
changeMaterialDone = false;
}
// Update is called on collision
void OnCollisionEnter(Collision col)
{
if (!changeMaterialDone && col.gameObject.tag == "box")
{
changeMaterialDone = true;
for(int i = 0; i <material.Length; i++)
{
rend.sharedMaterial = material[i];
Debug.Log(rend.sharedMaterial);
}
}
else
{
rend.sharedMaterial = material[0];
Debug.Log(rend.sharedMaterial);
}
}
}
I hope it helps you :)