Coroutine is not working fine - Unity [C#] - c#

i'm using Unity to track some image target (ARCamera - Vuforia) and everything works fine. I've inserted some 2D sprites on the image target etc... but now i don't want to show all the targets as soon as the image target is found, so i went to DefaultTrackableEventHandler.cs and instantiated my custom class Example like this:
#region PRIVATE_MEMBER_VARIABLES
private TrackableBehaviour mTrackableBehaviour;
#endregion // PRIVATE_MEMBER_VARIABLES
public Example mainClass;
...
private void OnTrackingFound()
{
...
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
mainClass = new Example();
mainClass.OnTrackableFound(); // Public method on 'Example' Class
}
Now on my Example class:
public class Example : MonoBehaviour {
public SpriteRenderer forYou;
public SpriteRenderer map;
public Example () {}
private bool isInTransition = false;
private float transition = 1;
private bool isShowing = false;
private float duration = 0;
void Start()
{
forYou = new SpriteRenderer();
map = new SpriteRenderer();
}
public void OnTrackableFound() {
StartCoroutine(FadeCoroutine());
}
private IEnumerator FadeCoroutine() {
yield return new WaitForSeconds(1);
Fade(true, .1f);
}
public void OnTrackableLost() {}
public void Fade (bool showing, float duration)
{
isShowing = showing;
isInTransition = true;
this.duration = duration;
transition = (isShowing) ? 0 : 1;
}
void Update ()
{
if (Input.GetMouseButtonDown (0)) {
Fade (true, .1f);
}
if (Input.GetMouseButtonUp (0)) {
Fade (false, .1f);
}
if (!isInTransition) {
return;
}
transition += (isShowing) ? Time.deltaTime * (1 / duration) : -Time.deltaTime * (1 / duration);
forYou.color = Color.Lerp (Color.white, new Color (1, 1, 1, 0), transition);
map.color = Color.Lerp (Color.white, new Color (1, 1, 1, 0), transition);
if (transition > 1 || transition < 0) {
isInTransition = false;
}
}
}
Sorry for the amount of code, but basically this is just for fading in/out some sprites. I want to yield as soon as my OnTrackingFound is called at Example class and after idk .5 seconds fade in some of my sprites.
Thank you!
EDIT - SOLVED
So, to attach a script to a game object it must inherit from Monobehaviour but than it cannot be instantiated so what i did was:
I needed the OnTrackingFound() from the DefaultTrackerHandler... so i coded everything on that class [i know it's far from the best solution but...] instead of instantiate another script because as i said it wouldn't be possible to attach it to a game object since it couldn't inherit from Monobehaviour. With public game objects, i attached everything directly from this class that also inherits from Monobehvr. If you instantiate a script that inherits monobehv it will be null. Thanks everyone!

The issue with your code is that you try to instantiate component Example that derives from MonoBehaviour calling constructor with new keyword.
Never do this.
All components in Unity3D Engine needs to be added to existing GameObject with GameObject.AddComponent<T>()
So what you need to do is:
private void OnTrackingFound()
{
...
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
// create new GameObject and add component
mainClass = new GameObject("MyNewGameobject").AddComponent<Example>();
mainClass.OnTrackableFound(); // Public method on 'Example' Class
}

Seems like you are calling mainClass = new Example(); every time a new target is tracked. This will create a new object even-though there might be an existing objects. I don't believe you want this.
Instead try call the GameObject gobj = GameObject.Find() (because the GameObject already exists right?) function to get the existing GameObject that the script is attached to. Then do a gobj.GetComponent<Example>(). OnTrackableFound() to call the event on the GameObject. Hope that helps.

I've answered a question which needs a delay like this in gamedev.
Basically you will increment a float value overtime, and when desired amount of time passed you will fade the sprite.
Pseudo:
float counter;
bool isFadeReady = false;
void Update ()
{
if (OnTrackingFound)
{
isFadeReady = true;
}
if (isFadeReady == true)
{
counter +=0.1f; //change the value for increase speed
}
if(counter>=1.0f) //change the value for desired time
{
//Fade your sprite here
isFadeReady = false;
}
}
Hope this helps! Cheers!

Related

Score counter not increasing after recalling object from a pool with raycast detection hit

Context
Hello, its me again. I have been playing around with "Object Pool" and it is fascinating the amount of CPU performance can save up. However, I have encountered a new "feature" where it scores once for the object being called and going through the pipe.
In my object pool, I have a pipe GameObject where is being Instantiated and set its method Active(false) until it is spawned. Once spawned, my prefabs will be filled accordingly to the Object Pool's size.
Problem
Once it spawns, it does what it should do, both scoring and the same mechanic as "Flappy Bird". However, once the object gets Active again, it doesn't seem to score anymore because it was passed by a player. What I have done is to have a flag that checks if the player (bird) has passed the pipe or not. However, when I pass through it, it will update it as if it was 6 times (one point per frame). You may ask "have you done another flag for the pipe itself?" then the answer is yes. I tried that way also, but it will only keep the score once and not increase further than 5.
I ran out of ideas with the object pool approach. It seems to work fine if it is WITHOUT object pooling, but the flaw here is that it costs me CPU performance.
It either increases it by just 1, or it increases it by 6 times (because for each frame the object is in the ray, it counts another point).
Attempts
I have browsed on the Unity Learning center to find out how to do the object pooling, and it wasn't too bad as I thought. Later, I found this issue and I thought it was a problem with my logic (which it can be). I have spent several hours already (first mistake) to think it is something easy to fix, but apparently it wasn't due the time I have spent to just figure out why it is not working 😅. I have been fiddling around my RayCastDetection, SpawnManager, ObjectPooling, and PlayerControl logic that interacts accordingly to the way I want, but nada.
Code
ObjectPooling.cs
public static ObjectPooling sharedInstance;
public List<GameObject> pooledObjects;
public GameObject objectToPool;
private PlayerControl playerControllerScript;
public int amountToPool;
void Awake()
{
sharedInstance = this;
}
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
pooledObjects = new List<GameObject>();
GameObject tmp;
for (int i = 0; i < amountToPool; i++) //Add objects to the pool and turns them invisible (false)
{
tmp = Instantiate(objectToPool);
tmp.SetActive(false);
playerControllerScript.passedBeam = false;
pooledObjects.Add(tmp);
}
}
public GameObject GetPooledObject()
{
for (int i = 0; i < amountToPool; i++)
{
if (!pooledObjects[i].activeInHierarchy)
return pooledObjects[i];
}
return null;
}
RayCastDetection.cs
public class RayCastDetection : MonoBehaviour
{
public UIManager UIManagerScript;
public PlayerControl playerControlScript;
private RaycastHit hit;
public bool passed;
void Start()
{
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>(); //Used for scoring
playerControlScript = GameObject.Find("Player").GetComponent<PlayerControl>(); //used for player passing through the pipe
passed = false;
playerControlScript.passedBeam = false;
}
void Update()
{
Vector3 beamDown = transform.TransformDirection(Vector3.down);
Ray ray = new Ray(transform.position, beamDown);
if (!passed)
{
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.tag == "Player" && !playerControlScript.passedBeam)
{
playerControlScript.passedBeam = !playerControlScript.passedBeam;
UIManagerScript.score++;
}
Debug.DrawRay(transform.position, hit.point - transform.position);
}
}
else
playerControlScript.passedBeam = false;
}
}
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
public GameObject[] obstaclesPrefab;
private PlayerControl playerControllerScript;
private float startDelay = 1.69f;
private float repeatRate = 1.1f;
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
void Update()
{
}
void SpawnObstacle()
{
// int obstaclesIndex = Random.Range(0, obstaclesPrefab.Length); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
if (playerControllerScript.gameOver == false)
{
float randomY = Random.Range(-2f, 2f);
Vector3 randomHeight = new Vector3(35, randomY, -7);
GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
if (pipe != null)
{
pipe.transform.position = randomHeight;
pipe.SetActive(true);
//My guess is that I want to instantiate the object pipe's beam to false here
}
}
// Instantiate(obstaclesPrefab[obstaclesIndex], randomHeight, obstaclesPrefab[obstaclesIndex].transform.rotation); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
}
}
Feel free to leave some suggestions in what I have missed out or any questions in regards to fill in. Thank you for your time!
I wanted to share some good news. My problems has been fixed!
Before I step into the solution, big thanks to rotgers and sharing me the approach he used. I actually was stubborn and wanted to feel unique in some way when it was coming to a solution. I had to step back a little bit and look at the big picture.
Reflection
If you ever struggle and spent like 8-12hrs like me for some logic nonsense and you feel it in the tip of the tongue, it is time to step a little bit back when you are not going anywhere after 1 hour.
Few tips to do (which I didn't do but I wish I do at the moment) was have drawings in how you are picturing it out mentally. Not drawing for the first time was my first mistake. I felt challenged by my own mind and sabotaging my own work to continuing being stubborn.
Code Solution
Now, the solution is that I added my UIManager script with my SpawnManager and RayCastDetection in order to make it work. Here is how I did it:
UIManager.cs
public class UIManager : MonoBehaviour
{
public Text scoreBoardDisplay;
public Text scoreBoardDisplayShadowed;
public AudioSource sound_score;
public string scoreText;
public int score;
public int oldScore;
public bool success;
public bool incrementScore;
void Start()
{
success = false; //NEW
incrementScore = false; //NEW
}
void Update()
{
if (score != oldScore)
{
sound_score.Play();
scoreText = score.ToString();
scoreBoardDisplay.text = scoreText;
scoreBoardDisplayShadowed.text = scoreText;
oldScore = score;
}
}
}
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
public GameObject[] obstaclesPrefab;
private PlayerControl playerControllerScript;
public UIManager UIManagerScript; //NEW
private float startDelay = 1.69f;
private float repeatRate = 1.1f;
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
void SpawnObstacle()
{
if (playerControllerScript.gameOver == false)
{
float randomY = Random.Range(-2f, 2f);
Vector3 randomHeight = new Vector3(35, randomY, -7);
GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
if (pipe != null)
{
pipe.transform.position = randomHeight;
pipe.SetActive(true);
UIManagerScript.incrementScore = false; //NEW, where my problem happens to be solved
UIManagerScript.success = false; //NEW, where my problems happens to be solved
}
}
}
}
RayCastDetection.cs
public class RayCastDetection : MonoBehaviour
{
public UIManager UIManagerScript;
public PlayerControl playerControlScript;
private RaycastHit hit;
public bool passed;
void Start()
{
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
playerControlScript = GameObject.Find("Tomato").GetComponent<PlayerControl>();
passed = false;
}
void Update()
{
Vector3 beamDown = transform.TransformDirection(Vector3.down);
Ray ray = new Ray(transform.position, beamDown);
if (!passed)
{
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.tag == "Player" && !UIManagerScript.success) //Setting a condition to check if the beam hasn't been touched YET
{
//Once touched, turn this true and increment
UIManagerScript.incrementScore = true;
UIManagerScript.score++;
}
//If I already incremented my score, then there is no need to re-increment it again from the same ray. Thus, a flag for success is handled
if (UIManagerScript.incrementScore)
UIManagerScript.success = true;
Debug.DrawRay(transform.position, hit.point - transform.position);
}
}
}
}
Again, thank you very much for guiding me to the right track :). If I think in something to add, I will update it under my Reflection header.
If you want to play around or use the code, feel free and play with it in my Git

Unity c# mario game

I am following this youtube series that creates a Unity 3D Mario game.
( https://www.youtube.com/watch?v=vjL3S5dKLN4&index=6&list=PLZ1b66Z1KFKgm4QrzZ11jfaeHVaWHSHW7 )
So I am trying to recreate the "going down the pipe" script like on the Mario game. I create an Animator and I am trying to trigger the animation using a C# script on key down press.
1. The problem:
This guys writes his scripts in Javascript and apparently my Unity version does no longer support js so I need to write it in C# ( and I prefer writing it in C#).
Basically I need to convert this js to c#:
var PipeEntry : GameObject;
var StoodOn : int;
function OnTriggerEnter (col : Collider) {
StoodOn = 1;
}
function OnTriggerExit (col : Collider) {
StoodOn = 0;
}
function Update () {
if (Input.GetButtonDown("GoDown")) {
if (StoodOn == 1) {
//GameObject.Find("FPSController").GetComponent("FirstPersonController").enabled=false;
transform.position = Vector3(0, -1000, 0);
WaitingForPipe();
}
}
}
function WaitingForPipe () {
PipeEntry.GetComponent("Animator").enabled=true;
yield WaitForSeconds(2);
PipeEntry.GetComponent("Animator").enabled=false;
//GameObject.Find("FPSController").GetComponent("FirstPersonController").enabled=true;
}
2. MY CODE
My script currently looks like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//
using UnityStandardAssets.Characters.FirstPerson;
public class PipeEntry : MonoBehaviour
{
public GameObject pipe_entry;
public int StoodOn;
public int has_run = 0;
public int waiting_pipe = 0;
public int waiting_pipe_animation = 0;
IEnumerator OnTriggerEnter(Collider col)
{
StoodOn = 1;
yield return StoodOn;
}
IEnumerator OnTriggerExit(Collider col)
{
StoodOn = 0;
yield return StoodOn;
}
IEnumerator WaitPipeAnimation()
{
waiting_pipe_animation = 1;
yield return new WaitForSeconds(2);
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("PipeDown"))
{
if (StoodOn == 1)
{
// Freeze player
//GameObject.Find("Player").GetComponent<FirstPersonController>().enabled = false;
transform.position = new Vector3(0, -1000, 0);
WaitingForPipe();
has_run = 1;
}
}
}
public void WaitingForPipe()
{
pipe_entry.GetComponent<Animator>().enabled = true;
WaitPipeAnimation();
pipe_entry.GetComponent<Animator>().enabled = false;
waiting_pipe = 1;
//GameObject.Find("Player").GetComponent<FirstPersonController>().enabled = true;
}
// Start is called before the first frame update
void Start()
{
}
}
3. Facts
The animation works good if triggered directly from Unity, so the script does not work.
I set some debug variables that are set to value 1 too where script breaks and waiting_pipe_animation is the only one that doesn't reach.
4. UPDATE
- So basically the main issue is with this function:
yield return new WaitForSeconds(2);
If I place it in a void function I get this:
The body cannot be a iterator block because void is not a iterator
interface type.
If I set animation to true and never set it back to false, it works - but not as expected because obviously animation goes forever.
1. The functions OnTriggerEnter and OnTriggerExit don't return IEnumerator and aren't coroutines.
private void OnTriggerEnter(Collider col)
{
StoodOn = 1;
}
private void OnTriggerExit(Collider col)
{
StoodOn = 0;
}
2. If the character is going to have more than one animation, I suggest you to create transitions between the animations and create variables to control it. There are some useful functions, like SetFloat, SetInteger, SetBool, SetTrigger and so on. If not, enabling and disabling the animator like you did will be fine.
3. As stated by Okeme Christian, it's required to use StartCoroutine("WaitPipeAnimation") to call your coroutine.
4. pipe_entry.GetComponent<Animator>().enabled = false; should be inside the WaitPipeAnimation function, because all the code ouside it will still be executed in the same frame. So basically you are activating and deactivating the animator in the same frame and won't be able to see the animation.
IEnumerator WaitPipeAnimation()
{
waiting_pipe_animation = 1;
yield return new WaitForSeconds(2);
pipe_entry.GetComponent<Animator>().enabled = false;
}
If you want to understand better how it works, please test this code below:
void Start()
{
StartCoroutine("CoroutineTest");
Debug.Log("3");
}
IEnumerator CoroutineTest()
{
Debug.Log("1");
yield return new WaitForSeconds(5f);
Debug.Log("2");
}
Note that the print order in the console will be "1", "3", "2".
You cant call a coroutine as a function. Instead you start a coroutine like this
StartCoroutine("Fade");
Where Fade is the coroutine name, so in your case you will start it like this
StartCoroutine("WaitPipeAnimation")

How can I create a boundary using C# code only?

I am trying to create a unity script that detects when the player has left a certain area using C# code only (no boundary boxes). My code should play a sound once the player leaves but the code is not working as expected and I cannot see a fault in the logic.
Expected behaviour
When the player steps outside the boundary, a sound will play once.
Actual Behaviour
The sound plays all the time no matter where the player is.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EX2BoundaryDetect : MonoBehaviour {
// create a GameObject to represent the player
public GameObject playerObject;
public bool leaveArea = false;
// create an Audio-clip object. This assumes we drag a sound file onto the myclip box in the Inspector.
public AudioClip myclip;
// Use this for initialization
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
// get the actual Sound file from the Inspector
GetComponent<AudioSource>().clip = myclip;
}
// Update is called once per frame
void Update () {
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
}
public void PlayAudio1 () {
// play the sound if the player is outside some boundaries
if (transform.position.x > 10) {
leaveArea = true;
}
if (transform.position.x < -29) {
leaveArea = true;
}
if (transform.position.z > 10) {
leaveArea = true;
}
if (transform.position.z < -29) {
leaveArea = true;
}
}
}
leaveArea is only set to false in the class definition. Once set to true, it may never be set to false again, and it may be inadvertently overwritten in the scene definition in the first place.
To fix this issue, set it to false at the beginning of Update:
void Update () {
leaveArea = false;
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
}
Also, GetComponent is an expensive action and it is good to avoid calling it in Update wherever possible. Therefore, you may want to move it into a class property and set it once in Start:
private AudioSource audioSource;
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
audioSource = GetComponent<AudioSource>();
// get the actual Sound file from the Inspector
audioSource.clip = myclip;
}
// Update is called once per frame
void Update () {
leaveArea = false;
PlayAudio1();
if (leaveArea) {
audioSource.Play ();
}
}
Got it working now, by adding
leaveArea = false;
to the Update method.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EX2BoundaryDetect : MonoBehaviour {
// create a GameObject to represent the player
public GameObject playerObject;
public bool leaveArea = false;
// create an Audio-clip object. This assumes we drag a sound file onto the myclip box in the Inspector.
public AudioClip myclip;
// Use this for initialization
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
// get the actual Sound file from the Inspector
GetComponent<AudioSource>().clip = myclip;
}
// Update is called once per frame
void Update () {
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
leaveArea = false;
}
public void PlayAudio1 () {
// play the sound if the player is outside some boundaries
if (transform.position.x > 10) {
leaveArea = true;
}
if (transform.position.x < -29) {
leaveArea = true;
}
if (transform.position.z > 10) {
leaveArea = true;
}
if (transform.position.z < -29) {
leaveArea = true;
}
}
}
I think the above answers still do not solve your problem of "When the player steps outside the boundary, a sound will play once."
I will share a cleaner way of doing it (possibly optimized)
Using Bounds
Create our own bounds by using Bounds https://docs.unity3d.com/ScriptReference/Bounds.html
The constructor of Bounds is Bounds(Vector3 center,vector3 size)
So in your case center will be center = (-9.5f, 0, -9.5f) (midpoint formula) and finally the size of the bounding box will be size = (39.0f, 0f, 39.0f)
Let's move to the Code Part
public class BoundsCheck: MonoBehaviour
{
public bool isInside = false;
public bool isPlayedOnce = false;
private Bounds bounds;
void Start()
{
bounds = new Bounds(new Vector3(-9.5f, 0, -9.5f), new Vector3(39.0f, 0f, 39.0f));
}
void Update()
{
CheckBounds();// play the sound if the player is outside some boundaries
if (!isInside &&!isPlayedOnce)
{
Debug.Log("PLAY");
isPlayedOnce = true;
}
}
private void CheckBounds()
{
bool isInsideBound = bounds.Contains(transform.position);
if (isInsideBound)
{
isInside = true;
if (isPlayedOnce)
{
isPlayedOnce = false;
}
}
else
{
isInside = false;
}
}
}
So instead of multiple ifs, we can just check if the position of the transform is inside the bound by using bool isInsideBound = bounds.Contains(transform.position);
Note:- To play sound only once, I have used one more boolean isPlayedOnce
I am not sure if this the most optimized way of doing it. But surely a cleaner way(via code).

Android Controller for Unity game

I was making game in Unity 3D
and used the one click converter of unity to convert it in Android .apk
The game is opening in Android phone
but the player is not moving
Player controller Script:
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour {
public Vector2 moving = new Vector2();
public int Bulletlimit = 0;
public int MaxBulletlimit = 3;
public bool Gun;
private float lastShotTime ;
public float fireDelay = 0.2f;
public Transform BulletDirection;
public Bullet bullet;
// Use this for initialization
void Start () {
lastShotTime = Time.time;
}
// Update is called once per frame
void Update () {
moving.x = moving.y = 0;
if (Input.GetKey ("right")) {
moving.x = 1;
} else if (Input.GetKey ("left")) {
moving.x = -1;
}
if (Input.GetKey ("up")) {
moving.y = 1;
} else if (Input.GetKey ("down")) {
moving.y = -1;
}
if (Input.GetKey ("s")) {
if(Gun){
if(Bulletlimit < MaxBulletlimit)
{
if(Time.time > lastShotTime + fireDelay)
{
Bullet clone = Instantiate (bullet, BulletDirection.position, Quaternion.identity) as Bullet;
Bulletlimit = Bulletlimit + 1;
lastShotTime = Time.time;
}
}
}
}
}
public void BulletCount()
{
Bulletlimit = Bulletlimit - 1;
}
}
How do I make him move in touch screens?
Your code is based on keystrokes - that (probably) wont apply to your touch screen.
There are a few ways you could do this, for something so simple I would probably try adding a Canvas along with some buttons to act as controls.
From there you can use the OnMouseDown()/OnMouseUp()/OnClick() methods (This also converts to touchscreens) instead of keystrokes.
How you code from there is a choice you have to make but in this case I would likely use the buttons to turn on/off movement bools and check/apply them in the Update() method.
If you're unsure of how to use the new unity UI try this...
https://unity3d.com/learn/tutorials/modules/beginner/ui/ui-canvas
If you're running the new version of unity (currently 5.1 I believe), my editor looks different to the tutorials and won't stay as default for some reason. Simply set the editor/inspector view to default if some of the options appear to be missing.
There are more complex things you can do with the actual touch inputs but I don't think you need to worry about it in this particular case.
Hope this helps :)

Changing an object change all the instantiated objects

I know that the topic of the question has been already asked and some answer have been given to it, but i'm asking it again because i fail misserably when i try to apply the answers from the other topics.
The problem that i have is that, i have a prefab (quad) 'Star' with a material into it and an script that makes it change color after some time:
using UnityEngine;
public class Star: MonoBehaviour {
public enum StarType {
BRIGHT,
DARK,
F,
K,
L,
P
}
private Ship ship;
private StarType starType;
private float timeLimit = .30f, timer;
private System.Random rand;
public void Awake() {
ship = FindObjectOfType<Ship>();
timer = .0f;
rand = new System.Random();
}
public void Start() {
refactor();
}
public void Update() {
timer += Time.deltaTime;
if(timer > timeLimit) {
refactor();
timer = .0f;
}
}
public void LateUpdate() {
transform.eulerAngles = new Vector3(ship.transform.eulerAngles.x,
ship.transform.eulerAngles.y,
ship.transform.eulerAngles.z);
}
public void refactor() {
starType = (StarType) rand.Next(0,
System.Enum.GetValues(typeof(StarType)).Length);
switch(starType ) {
case StarType.BRIGHT:
renderer.material.color = Color.white;
break;
case StarType.DARK:
renderer.material.color = Color.black;
break;
case StarType.F:
renderer.material.color = Color.red;
break;
case StarType.K:
renderer.material.color = Color.cyan;
break;
case StarType.L:
renderer.material.color = Color.green;
break;
case StarType.P:
renderer.material.color = Color.magenta;
break;
}
}
public StarType getStarType() {
return starType;
}
}
And this stars being instantiated in a Chunk class:
public class Chunk : MonoBehaviour {
public Star star;
private IList<Star> stars;
public void Awake() {
stars = new List<Star>();
Star auxStar = (Star) Instantiate(star, transform.position,
Quaternion.identity);
auxStar.transform.SetParent(transform);
stars.Add(auxStar);
}
}
I was assuming that each star would change the color independently, but instead of that they all change to the same color.
I've tried using sharedMaterial instead of material as i read it on some asnwers, though the result and behaviour seems the same?, and i tried giving random init colors to the stars, but the most that i get is having a few of them changing to a color different from the rest, though still the same between them (And i'm trying to change colors all the time, not only upon creation).
I've read too creating a material and asigning to each one at instantiating, but i had no luck:
auxStar.renderer.material = (Material)
Instantiate(star.renderer.material);
Does someone know how can the problem be handled?
According to Unity API reference, you are doing the right thing.
The issue in your code is that you are instantiating a MonoBehaviour
public Star star;
...
Star auxStar = (Star) Instantiate(star, transform.position, Quaternion.identity);
where star is a MonoBehaviour, you are not instantiating the actual GameObject with the Mesh Render and/or Material. Instead make sure you are instantiating the right GameObject Prefab, not just the component, like this:
public GameObject starPrefab;
...
GameObject starClone = (GameObject) Instantiate (starPrefab);
Star auxStar = starClone.GetComponent<Star> ();
In that way you have control of the GameObject, if you need, and every component associated with the Cloned Object, in this case Star. You don't have to instantiate the Prefab Material cause it will be already instantiated.
Here is a simple Example:
using UnityEngine;
using System.Collections;
public class SpawnStar : MonoBehaviour {
public GameObject StarPrefab;
public bool InstantiateStar = false;
void Update () {
if (InstantiateStar) {
GameObject starClone = (GameObject)Instantiate (StarPrefab);
float r = Random.Range (0f, 1f);
float g = Random.Range (0f, 1f);
float b = Random.Range (0f, 1f);
float a = Random.Range (0f, 1f);
starClone.renderer.material.color = new Color (r, g, b, a);
InstantiateStar = false;
}
}
}
Side Note: Make sure to not modify the Prefab during gameplay since is the source of all other cloned objects.
Try using Random.Range() As in System.Random is generated by system time. As we know that Time is not Really Random. It still depends on mil sec times and generate the the logic of Random as we can't really tell which mil sec the random will run.
Since System.Random runs on 1 timeline which your scripts are using. Random.Range() is a bet you can consider since it uses SystemTime and independent time On Run of the platform. Shares with Time.DeltaTime.

Categories