So I am trying to create this script which changes the color of the sprite when I execute the method OnMouseDown().
The color change should execute every 2 seconds. In this code provided below the colors only change once for some reason.
I have already tried coroutines. But they didn't work for some reason.
Please help thanks,
public bool startstop = false;
SpriteRenderer m_SpriteRenderer;
IEnumerator Changecolor() {
yield return new WaitForSeconds(3);
int random = Random.Range(1, 4);
if (random == 1) {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.blue;
} else if (random == 2) {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.red;
} else if (random == 3) {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.green;
} else {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.yellow;
}
this.StartCoroutine("Changecolor", 3f);
}
private void OnMouseDown() {
if (this.startstop) {
this.StartCoroutine("Changecolor", 3f);
this.startstop = !this.startstop;
} else {
this.StopCoroutine("Changecolor");
this.startstop = !this.startstop;
}
}
No errors just doesn't work.
Do you have a Collider on the object? A Collider is needed to have the OnMouseDown event fired.
The code principally works, but is far from quality.
- Only call GetComponent() once, then cache the result. This call is very expensive.
- Initially you have to click two times, as the Coroutine will be stopped at the first click.
Here is the code with some improvements:
public bool m_isRunning = false;
public SpriteRenderer m_spriteRenderer;
private void Start() {
m_spriteRenderer = this.GetComponent<SpriteRenderer>();
}
private IEnumerator Changecolor() {
yield return new WaitForSeconds(3);
int random = Random.Range(1, 4);
if (random == 1) {
m_spriteRenderer.color = Color.blue;
} else if (random == 2) {
m_spriteRenderer.color = Color.red;
} else if (random == 3) {
m_spriteRenderer.color = Color.green;
} else {
m_spriteRenderer.color = Color.yellow;
}
this.StartCoroutine("Changecolor", 3f);
}
private void OnMouseDown() {
m_isRunning = !m_isRunning;
if (m_isRunning) {
StartCoroutine("Changecolor", 3f);
} else {
StopCoroutine("Changecolor");
}
}
Note that the second parameter you pass in (3f)
StartCoroutine ("Changecolor", 3f);
does nothing since your Changecolor does not take any arguments ...
I would actually suggest to not use Coroutine here at all but rather InvokeRepeating and CancelInvoke
void Changecolor()
{
// Either in Awake or as lazy initialization
if(!m_SpriteRenderer) m_SpriteRenderer = GetComponent<SpriteRenderer>();
int random = Random.Range(1, 4);
switch(random)
{
case 1:
m_spriteRenderer.color = Color.blue;
break;
case 2:
m_spriteRenderer.color = Color.red;
break;
case 3:
m_spriteRenderer.color = Color.green;
break;
default:
m_spriteRenderer.color = Color.yellow;
break;
}
}
private void OnMouseDown()
{
startstop = !startstop;
if (this.startstop)
{
InvokeRepeating(nameof(Changecolor), 0f, 2f);
}
else
{
CancelInvoke(nameof(Changecolor));
}
}
The code you provided works just fine, and sins you say the color changes only once, I assume you have a collider on the object that has the script attached, the only thing you should change is the continues call to GetComponent<SpriteRenderer> because it is pretty costly and should only be called in either Start or Awake another thing, which isn't major, and there is nothing wrong with it, but it kind of rubs me the wrong way, is the creation of a new coroutine at the end of the old, why not do something like this:
private Coroutine _colorChanger;
private SpriteRenderer _renderer;
void Start() //Can be Awake, whichever you choose
{
_renderer = GetComponent<SpriteRenderer>();
if (_renderer == null)
{
Debug.Log("No sprite found.");
return;
}
//This is performed if OnMouseDown is implemented, if you implement the Update with Input.GetKeyDown, then this can be removed
var collider = GetComponent<Collider>();
if (collider == null)
{
collider = gameObject.AddComponent<BoxCollider>(); //or BoxCollider2D if you are applying the script to the sprite itself.
}
collider.isTrigger = true;
}
private void OnMouseDown() //this can be swapped out for what Saif wrote, a Update method which checks if the button is down, should be GetKeyDown instead of GetKey, having it that way will eliminate the need for a collider/UI element
{
if (_colorChanger == null)
{
_colorChanger = StartCoroutine(ChangeColor(2f));
}
else
{
StopCoroutine(_colorChanger);
_colorChanger = null;
}
}
IEnumerator ChangeColor(float timeoutSec)
{
while (true)
{
yield return new WaitForSeconds(timeoutSec);
int random = Random.Range(1, 5); //Change max from 4 to 5
if (random == 1)
{
_renderer.color = Color.blue;
}
else if (random == 2)
{
_renderer.color = Color.red;
}
else if (random == 3)
{
_renderer.color = Color.green;
}
else
{
_renderer.color = Color.yellow;
}
}
}
Update: just noticed something that others missed, you should change the Random.Range(1, 4) to Random.Range(1, 5) or else the yellow color will never come into effect.
Your code is right, no issues in it except that the OnMouseDown() function will not be called since you are not clicking on any box triggers or any UI elements. Hence try to use update function as given below:
void Update()
{
if (Input.GetKey(KeyCode.Mouse0))
{
if (startstop == false)
{
StartCoroutine("Changecolor", 3f);
this.startstop = !this.startstop;
}
else
{
StopCoroutine("Changecolor");
this.startstop = !this.startstop;
}
}
}
Related
I am trying to make it so when you get in a certain range of the GUI it activates. It was working perfectly when I was testing with Debug.Log("hit") however I changed it to obj.setActive and it stops working. I don't understand why this is?
here's the code:
private void Update()
{
if (IsInRange())
{
canvas.SetActive(true);
} else
{
canvas.SetActive(false);
}
}
private bool IsInRange()
{
origin = canvas.transform.position;
direction = canvas.transform.forward;
Collider[] hitColliders = Physics.OverlapSphere(origin, range, playerLayer);
foreach (var hitCollider in hitColliders)
{
if (hitCollider.name == player.name)
{
playerInRange = true;
playerInRangeF1 = true;
}
else
{
count += 1;
}
}
if (count != hitColliders.Length)
{
return true;
} else
{
return false;
}
}
I have finally figured out the answer to my problem. The script I was running was a component of the game object I was trying to hide and show. When you hide a game object it obviously stops all of its components from working. All I had to do was place the script on an object that would not be hidden.
I have troubles trying to make two different actions on trigger enter, its same name tag so I have no idea how to do it. For example I have ground button which has function, etc on first trigger enter it is disabling rotation of some other object, on second trigger enter it enables rotation back.Tried with bools but its not working well, Im thinking about int, but Im not sure. Thanks for any help, Im new in coding.
Heres the code;
if (other.gameObject.CompareTag("Player"))
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.red);
Rotationplane.isRotated = false;
}
EDIT:
#derHugoDont know mate, lost my mind...but finally I managed to do it over the if statements, I have no idea does it effect the performance. Heres code if someone has the same problem. Opening the bottle of whiskey...done for today.
private void FixedUpdate()
{
if(firstCollisionDone == true)
{
firstCollisionAavaliable = false;
secondCollisionAvaliable = true;
}
if(secondCollisionDone == true)
{
secondCollisionAvaliable = false;
firstCollisionAavaliable = true;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if(firstCollisionAavaliable == true)
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.red);
Rotationplane.isRotated = false;
firstCollision = true;
}
if(secondCollisionAvaliable == true)
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.white);
Rotationplane.isRotated = true;
secondCollision = true;
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if(firstCollision == true)
{
firstCollisionDone = true;
firstCollision = false;
firstCollisionAavaliable = false;
secondCollisionDone = false;
}
if(secondCollision == true)
{
secondCollisionDone = true;
secondCollision = false;
secondCollisionAvaliable = false;
firstCollisionDone = false;
}
}
}
}```
I assume the flag you want to change is the Rotationplane.isRotated.
You could simply invert it's state like e.g.
if (other.gameObject.CompareTag("Player"))
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.red);
Rotationplane.isRotated = !Rotationplane.isRotated;
}
In C# Unity3D, I'm trying to get my bullets to fire at an interval of bulletTime. Single shots work perfectly fine at the moment, but when I hold down the fire button, just 1 bullet is created and shot and then nothing else happens.
// Left mouse button HELD down.
else if (fireAgain && Input.GetMouseButton(0))
{
StartCoroutine("LoopNShoot");
fireAgain = false;
}
else if (timerH < bulletTime)
{
timerH += Time.deltaTime;
if(timerH >= bulletTime)
{
fireAgain = true;
timerH = 0;
}
}
IEnumerator LoopNShoot()
{
pivot.transform.Rotate(triggerAngle,0,0);
GameObject bullet = ObjectPooler.SharedInstance.GetPooledObject();
if (bullet != null) {
bullet.transform.position = SSpawn.transform.position;
bullet.transform.rotation = SSpawn.transform.rotation;
bullet.SetActive(true);
}
yield return new WaitForSeconds(.1f);
pivot.transform.rotation = Quaternion.Slerp(transform.rotation, originalRotationValue, Time.deltaTime * rotationResetSpeed);
}
Im thinking I need to place all my if statements and timer inside the coroutine? But that doesnt seem to help either...
Please ignore the pivot and rotation, it works fine for some animation, the only thing taht doesnt work is shooting bullets continuosly at a set interval while the fire button is Held down.
You should remove these else and just do.
private void Update()
{
if (fireAgain && Input.GetMouseButton(0))
{
StartCoroutine("LoopNShoot");
fireAgain = false;
timerH = 0;
}
if (timerH < bulletTime)
{
timerH += Time.deltaTime;
if(timerH >= bulletTime)
{
fireAgain = true;
}
}
}
You could also re-arange this to make clearer how it works:
private void Update()
{
if(!fireAgain)
{
timerH += Time.deltaTime;
if (timerH >= bulletTime)
{
fireAgain = true;
}
}
else
{
if(Input.GetMouseButton(0))
{
StartCoroutine("LoopNShoot");
fireAgain = false;
timerH = 0;
}
}
}
I actually thought you anyway already chose the Invoke method I showed you here
I am trying to draw a line from camera to a instantiated object.I am using the scene UnityARHitTest Example.When I touch on a vertical plane the object gets instantiated and i want to draw a line from camera to the object.When I move my device the line should show from the centre of my camera.For some reason line renderer is not showing when I call it in the Late update.
LineRenderer lins;
public GameObject Lineprefab;
bool HitTestWithResultType (ARPoint point, ARHitTestResultType resultTypes)
{
List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, resultTypes);
if (hitResults.Count > 0 && check==true)
{
foreach (var hitResult in hitResults)
{
Debug.Log ("Got hit!");
if (Select == 0)
{
Debug.Log("hit-zero!");
Instantiate(Instaobj[0], ForSelect);
check = false;
}
if (Select == 1)
{
Debug.Log("hit-one!");
Instantiate(Instaobj[1], ForSelect);
check = false;
}
if (Select == 2)
{
Debug.Log("hit-two!");
Instantiate(Instaobj[2], ForSelect);
check = false;
}
if (Select == 3)
{
Debug.Log("hit-three!");
Instantiate(Instaobj[3], ForSelect);
check = false;
}
if (Select == 4)
{
Debug.Log("hit-four!");
Instantiate(Instaobj[4], ForSelect);
check = false;
}
if (Select == 5)
{
Debug.Log("hit-five!");
Instantiate(Instaobj[5], ForSelect);
check = false;
}
m_HitTransform.position = UnityARMatrixOps.GetPosition (hitResult.worldTransform);
m_HitTransform.rotation = UnityARMatrixOps.GetRotation (hitResult.worldTransform);
Debug.Log (string.Format ("x:{0:0.######} y:{1:0.######} z:{2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z));
obj.StopPlaneTracking();
if (GameObject.Find("debugPlanePrefab(Clone)"))
GameObject.Find("debugPlanePrefab(Clone)").SetActive(false);
else
Debug.Log("no prefab");
//lins.SetPosition(0, m_HitTransform.position);
//lins.SetPosition(1, obj.m_camera.transform.position);
return true;
}
}
return false;
}
When I use lins.setposition() in the above method(which is commented) a line is shown in the output.When I use lins.setposition() in the below LateUpdate() the output is not shown nothing comes.
private void Start()
{
spawngenerator();
}
void spawngenerator()
{
GameObject newline = Instantiate(Lineprefab);
lins = newline.GetComponent<LineRenderer>();
//lins.SetPosition(0, m_HitTransform.position);
//lins.SetPosition(1, obj.m_camera.transform.position);
}
private void LateUpdate()
{
lins.SetPosition(0,obj.m_camera.transform.position );
lins.SetPosition(1,m_HitTransform.position );
}
I have 3 checkboxes
public bool stateForward = false, stateReverse = false, stateRandom = false;
I want that i will be able to chose each time only one checkbox. But also in editor mode and also when the game is running. And when the game is running i want to make it effect on the game.
In the top of the script i added:
[ExecuteInEditMode]
I tried to do in the Start function
void Start()
{
while (true)
{
if (stateForward == true)
{
stateRandom = false;
stateReverse = false;
}
else if (stateReverse == true)
{
stateRandom = false;
stateForward = false;
}
else if (stateRandom == true)
{
stateForward = false;
stateReverse = false;
}
}
anims = GetComponent<Animations>();
waypoints = GameObject.FindGameObjectsWithTag("ClonedObject");
objectsToMove = GameObject.FindGameObjectsWithTag("Robots");
originalPosition = objectsToMove[0].transform.position;
}
But i'm getting on anims: Unreachable code detected
And in the Update function:
void Update()
{
if (MyCommands.walkbetweenwaypoints == true)
{
DrawLinesInScene();
anims.PlayState(Animations.AnimatorStates.RUN);
WayPointsAI();
}
}
And in WayPointsAI
private void WayPointsAI()
{
if (stateForward == true)
{
if (targetIndex == waypoints.Length)
targetIndex = 0;
}
if (stateReverse == true)
{
if (targetIndex == 0)
targetIndex = waypoints.Length;
}
waypoint = waypoints[targetIndex].transform;
float distance = Vector3.Distance(objectsToMove[0].transform.position, waypoint.transform.position);
objectsToMove[0].transform.rotation = Quaternion.Slerp(objectsToMove[0].transform.rotation, Quaternion.LookRotation(waypoint.position - objectsToMove[0].transform.position), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < 2)
{
if (stateForward == true)
targetIndex++;
if (stateReverse == true)
targetIndex--;
}
}
First off, you are never going to get out of your Start() method. You have a while(true) loop that you never break out of, so you will be stuck there.
Instead of multiple checkboxes that you can only choose one of, just use an enumeration. Unity will give you a drop down and you can choose one of the three available states.
enum MyStateEnum{ Forward, Reverse, Random }
public MyStateEnum State;
If you want them to be exclusive, you should probably use a single enum field instead of three bool fields. Your enum would have values Forward, Reverse, and Random, while your MonoBehavior would have a single field of your enum type. This ensures that only one will be chosen at any given time, and the editor should show a drop-down for selecting which value rather than a series of checkboxes. This not only fixes the editor UI issue, but will also lead to cleaner code with fewer potential bugs.