Making GameObject appear and disappear for a finite amount of time - c#

I'm trying to make a GameObject appear and disappear for a finite amount of time (Lets put the time function aside for now).
Here's what I came out with:
using UnityEngine;
using System.Collections;
public class Enemy1Behavior : MonoBehaviour
{
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
this.gameObject.SetActive(false); // Making enemy 1 invisible
Debug.Log("Update called");
DisappearanceLogic(gameObject);
}
private static void DisappearanceLogic(GameObject gameObject)
{
int num = 0;
while (num >= 0)
{
if (num % 2 == 0)
{
gameObject.SetActive(false);
}
else
{
gameObject.SetActive(true);
}
num++;
}
}
}
Now when I click the play button in Unity the program just don't respond, and I can only quit it from the task manager using End Task.
(And yes I know there a infinite loop in the method).
So I guess Im doing something wrong. What is the best way for making a Gameobject Blink/Flash/appear-disappear in Unity?
Thanks guys.

You are using an infinite loop which locks your Update() completely, because num will always be greater then 0.
So you could use InvokeRepeating (http://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html)
public GameObject gameobj;
void Start()
{
InvokeRepeating("DisappearanceLogic", 0, interval);
}
void DisappearanceLogic()
{
if(gameobj.activeSelf)
{
gameobj.SetActive(false);
}
else
{
gameobj.SetActive(true);
}
}
interval is a float - something like 1f 0.5f etc.

You can make animation for blinking etc - Animations in Mecanim. Appearing and disappearing you can achieve using gameObject.SetActive(true/false);. If you want to make something with time its better to use Coroutines or just Invoke with delay parameter - Invoke Unity Docs.

Related

How to do if something happened make number - 1

I am trying to do when i destroy all boxes something happen.
My code is;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class destroy : MonoBehaviour
{
private string BALL_TAG = "ball";
public AudioClip coin;
public AudioSource src;
public float numBox = 120f;
public bool isDestroyed;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag(BALL_TAG))
{
src.clip = coin;
src.Play();
Destroy(gameObject);
isDestroyed = true;
}
}
private void Update()
{
boxes();
}
public void boxes()
{
if(isDestroyed == true)
numBox -= 1f;
if(numBox == 119)
SceneManager.LoadScene("mainManu");
}
private IEnumerator Two()
{
yield return new WaitForSeconds(1f);
Destroy(gameObject);
}
}
But it doesn't work.
It is suppose to do when I broke 1 box it sends me to menu.
I think its problem in "numBox -= 1f;" because I don't know hot to make this.
I don't understand your code completely. So, I need to make some assumptions.
I think the Script is attached to the box and every box has this Script. I also think, that your player Shoots Ball. Those Balls have a collider with an ball tag.
There are multiple problems with your code.
The first one is, that your count variable, numBox, is saved in your destroy Script, which is placed on each box.
this means, that every Box is counting for itself.
You have to centralize this. There are multiple ways for doing this.
One way is to declare this variable as static(https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/static)
This is not best practice, but works.
A Better way is to have a Script on Your Player, which holds this number and every Box searches for this Script and change this number if it is destroyed.
The second big Problem is, that your are doing some really weird thing in your Update and the collision handling
First of all, you are setting isDestroyed to true. Then in your boxes method, which is called in every Frame, you are decrementing your numBox variable by one, if this is Destroyed is true.
So if your Box gets hit, you are decrementing every frame.
After that you are checking every frame if your numBox is 119
If so, you change the Scene.
This is the reason, why you are getting to your MainMenu after only one boy
This behaviour is very weird, because it is totally unnecessary. You can reduce your variable directly in in your OnCollisionEnter2D Method.
There are some little things, which can be improved.
When you are trying to play a Sound, you don't have to specify the AudioClip in code. You can assign this directly in Unity on the AudioSource Component via drag and drop. This makes your code simpler.
You are not calling the Two Coroutine. You've specified this Coroutine but don't call it.
//Script on Player
public class PlayerBoxDestroyManager:MonoBehaviour
{
public int StartBoxes = 120;
private int Boxes;
private void Start()
{
Boxes = StartBoxes;
}
public void DestroyBox()
{
//Reduce our Boxes count
//This is equal to Boxes -= 1
// Boxes = Boxes -1
Boxes--;
// If we have less or zero Boxes left, we End call our EndGame methode
if(Boxes <= 0)
{
EndGame();
}
}
private void EndGame()
{
// We change the Scene to the mainMenu
SceneManager.LoadScene("mainManu");
}
}
```
//Script on all Boxes
public class Box : MonoBehaviour
{
public string Balltag = "ball";
//Audio Source the Audio Clip has to be assigned in the Unity editor
public AudioSource Coin;
private void OnCollisionEnter2D(Collision2D collision)
{
//Check it colliding Object has the right Tag
if(collision.transform.tag == Balltag)
{
//Get the reference to the Player Script
PlayerBoxDestroyManager PBDM = FindObjectOfType<PlayerBoxDestroyManager>();
//We can now access the Destroy Box Methode
PBDM.DestroyBox();
//Play the sound
Coin.Play();
//If we destroy our Object now, the Sound would also be deletet.
//We want to hear the sound, so we have to wait, till the sound is finished.
StartCoroutine(WaitTillAudioIsFinished());
}
}
IEnumerator WaitTillAudioIsFinished()
{
//we wait till the sound is finished
while (Coin.isPlaying)
{
yield return null;
}
//if finished, we destroy the Gameobject
Destroy(gameObject);
}
}
I hope I helped you. If you have questions, feel free to ask.
And sorry for my English:)

c#/Unity: Infinite Loop Issue... Am I going crazy?

So, c# noob here, with a bit of an issue:
I'm trying to script a boss battle for my Unity game, and I'm making it's A.I.
Every 10 seconds, I want the boss to check a random number. If it makes it, it will perform a teleport animation, and teleport. I haven't coded the teleportation itself, just trying to get the animation to trigger. I want this to be keep going throughout the boss fight, until the boss is defeated.
Unfortunately, it's an infinite loop that crashes my Unity every time I run it. I know having it in Update() is a dumb idea, but I've tried a lot of stuff and got nothing. I'm losing my mind here! Am I missing something obvious?!
Anyway, here's the code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SmellseerAI : MonoBehaviour
{
public Animator animator;
public SmellseerHealth smellseerhealth;
public GameObject tele1;
public GameObject tele2;
public GameObject tele3;
public DateTime TimeOfLastTeleport;
private bool teleporting = false;
void Start()
{
TimeOfLastTeleport = System.DateTime.Now;
}
void Update()
{
Debug.Log("Starting teleportcheck!");
int TeleportMin = 1;
int TeleportMax = 10;
int RandomTeleport = UnityEngine.Random.Range(TeleportMin, TeleportMax);
var diffInSeconds = (System.DateTime.Now - TimeOfLastTeleport).TotalSeconds;
Debug.Log("diffInSeconds is " + diffInSeconds);
if ((RandomTeleport > 5) && (diffInSeconds > 3))
{
Debug.Log("Teleporting!");
teleporting = true;
animator.SetBool("teleporting", true);
while (animator.GetCurrentAnimatorStateInfo(0).normalizedTime <= 1)
{ //If normalizedTime is 0 to 1 means animation is playing, if greater than 1 means finished
Debug.Log("anim playing");
}
animator.SetBool("teleporting", false);
TimeOfLastTeleport = System.DateTime.Now;
}
else
{
Debug.Log("Failed to teleport");
}
Debug.Log("Gaming!");
}
}
You're running a while loop inside Update():
while (animator.GetCurrentAnimatorStateInfo(0).normalizedTime <= 1)
but the animator's time isn't changing because it updates each frame, and you're causing the main thread to hang with your while loop. You won't release Update() (and thus won't allow the next frame to be drawn) until your while loop breaks, but your while loop's condition requires more frames. So you just wait forever.
Try moving your teleport values to the class, initialize them in Start(), then re-set them again after you've done all your checks. Then you can return each frame your condition isn't met. Consider the following:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SmellseerAI : MonoBehaviour
{
public Animator animator;
public SmellseerHealth smellseerhealth;
public GameObject tele1;
public GameObject tele2;
public GameObject tele3;
public DateTime TimeOfLastTeleport;
private bool teleporting = false;
// NEW
int TeleportMin = 1;
int TeleportMax = 10;
int RandomTeleport;
void Start()
{
TimeOfLastTeleport = System.DateTime.Now;
//NEW
RandomTeleport = UnityEngine.Random.Range(TeleportMin, TeleportMax);
}
void Update()
{
var diffInSeconds = (System.DateTime.Now - TimeOfLastTeleport).TotalSeconds;
Debug.Log("diffInSeconds is " + diffInSeconds);
if ((RandomTeleport > 5) && (diffInSeconds > 3))
{
Debug.Log("Teleporting!");
teleporting = true;
animator.SetBool("teleporting", true);
// NEW
if (animator.GetCurrentAnimatorStateInfo(0).normalizedTime <= 1)
{ //If normalizedTime is 0 to 1 means animation is playing, if greater than 1 means finished
Debug.Log("anim playing");
// NEW
return;
}
animator.SetBool("teleporting", false);
TimeOfLastTeleport = System.DateTime.Now;
}
else
{
Debug.Log("Failed to teleport");
}
Debug.Log("Gaming!");
// NEW
RandomTeleport = UnityEngine.Random.Range(TeleportMin, TeleportMax);
}
}
When you start the animation your code enters the while loop and never finishes, unity waits until all scripts are done before rendering the next frame, meaning the animation never finishes
Removing the while loop and just making it an if statement to check the animation state should work.

Why does why C# code keep crashing Unity? (I'm a beginner to Unity and C#)

Whenever I run my game it freezes, but it doesn't without this C# script.
I've tried changing around my code, and it works outside of Unity, in .NET (with some tweaks to certain functions) but when it's in Unity it crashes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Throw : MonoBehaviour
{
public Rigidbody rb;
string final = "final:";
public float force = 1;
public float accuracy = 0;
void incto(float amount)
{
while (force < amount)
{
Debug.Log(force);
force++;
}
}
void decto(float amount)
{
while (force > amount)
{
Debug.Log(force);
force--;
}
}
void fstart()
{
while (true)
{
force = 1;
incto(200);
decto(1);
if(Input.GetKey(KeyCode.E))
{
Debug.Log(final + force);
break;
}
}
}
// Start is called before the first frame update
void Start()
{
fstart();
}
// Update is called once per frame
void FixedUpdate()
{
Debug.Log(force);
}
}
It should decrease and increase the force value, then stop when you press E, but Unity just crashes.
Unity takes care of the while(true) for you. Unity's while(true) calls your FixedUpdate, you just need to fill it in.
Unity only captures keystrokes once per frame, so Input.GetKey(KeyCode.E) will always return the same value. Unity crashes because of your while(true) is an infinite loop.
More info: https://docs.unity3d.com/Manual/ExecutionOrder.html
i belive that unity starts catching key strokes after the first frame, not before, try moving fstart() behind a first run bool in the FixedUpdate function
oh and this will hang the entire program every time it executes a frame.....
The code crashes because you have an infinite loop here:
while (true)
{
}
It will never exit the loop so nothing more happens. Just put that code into Update() method, which gets called by the engine on every frame, it will do the trick

Cannot use InvokeRepeating with method parameters. How do I work around this?

I'm trying to implement a damage over time system, but Unity keeps saying "Trying to Invoke method...Couldn't be Called." The method I want to call uses the parameters "Collider coll", but from my research you can't invoke if the method has said paremters.
Here is my code:
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class DamageOverTime : MonoBehaviour
{
public int PHP; //PHP = Player Health from PlayerHealth.cs script.
public int Damage; //Amount of damage.
public int DamageOverTime; //Damage over time.
public float DamageInterval_DOT = .25f; //Damage interval for damage over time.
public string Level;
PlayerHealth player;
void Start()
{
player = GameObject.Find("Player").GetComponent<PlayerHealth>();
InvokeRepeating("OnTriggerEnter", DamageInterval_DOT, DamageInterval_DOT);
}
void Update()
{
PHP = GameObject.Find("Player").GetComponent<PlayerHealth>().PlayerHP;
if (PHP <= 0)
{
SceneManager.LoadScene(Level);
}
}
void OnTriggerEnter(Collider coll)
{
if (coll.gameObject.tag == "Player")
{
GameObject.Find("Player").GetComponent<PlayerHealth>().PlayerHP = PHP - Damage;
}
if (coll.gameObject.tag == "Ball")
{
gameObject.SetActive(false);
SceneManager.LoadScene(Level);
}
}
}
My goal is to get the OnTriggerEnter function to loop ever 1/4 of a second (or lower possibly). Current upon entering a collider my health is drained by 60% in about a second which is far too fast. How should I work around this?
You can't use InvokeRepeating with OnTriggerEnter, because it's a trigger, which means it will trigger once when entrance of its holder occured.
Also InvokeRepeating means that you want to keep repeating an action continously which is not the case here. You want your trigger to occur once and then remove health points over time.
Solution - Coroutine
Unity3D makes custom usage of IEnumerable and yield keyword called Coroutine that always returns an IEnumerator. How it works? It will return control on every yield there is in our Coroutine and then will go back to exact point where it gave back control instead of starting function execution from scratch.
Code:
void OnTriggerEnter(Collider coll)
{
if (coll.gameObject.tag == "Player")
{
StartCoroutine("DamageOverTimeCoroutine");
}
if (coll.gameObject.tag == "Ball")
{
gameObject.SetActive(false);
SceneManager.LoadScene(Level);
}
}
public IEnumerator DamageOverTimeCoroutine()
{
var dotHits = 0;
while (dotHits < 4)
{
//Will remove 1/4 of Damage per tick
GameObject.Find("Player").GetComponent<PlayerHealth>().PlayerHP -= Damage / 4;
dotHits++;
//Will return control over here
yield return new WaitForSeconds(DamageInterval_DOT);
//And then control is returned back here once 0.25s passes
}
}
There's of course room for improvement in this Coroutine. You can pass parameters to it, same as you can to any other method in C#. Also you can implement other invervals that are not hardcoded. Code above is just a simple example on how to deal with such scenarios.
For continous damage over time
public IEnumerator DamageOverTimeCoroutine()
{
var dotHits = 0;
var player = GameObject.Find("Player").GetComponent<PlayerHealth>();
while (true)
{
//Stop removing damage, player is dead already
if (player.PlayerHP <= 0)
yield break;
//Will remove 5 Damage per tick
player.PlayerHP -= 5;
dotHits++;
//Will return control over here
yield return new WaitForSeconds(DamageInterval_DOT);
//And then control is returned back here once 0.25s passes
}
}
To stop Coroutine somewhere else from code use StopCoroutine("DamageOverTimeCoroutine") to stop certain coroutine type or StopAllCoroutines() to stop all coroutines that are active now.

Flashing GameObject in Unity

How can I create a flashing object in Unity using SetActiveRecursively (Moment = 1 second).
My example (for changes):
public GameObject flashing_Label;
private float timer;
void Update()
{
while(true)
{
flashing_Label.SetActiveRecursively(true);
timer = Time.deltaTime;
if(timer > 1)
{
flashing_Label.SetActiveRecursively(false);
timer = 0;
}
}
}
Use InvokeRepeating:
public GameObject flashing_Label;
public float interval;
void Start()
{
InvokeRepeating("FlashLabel", 0, interval);
}
void FlashLabel()
{
if(flashing_Label.activeSelf)
flashing_Label.SetActive(false);
else
flashing_Label.SetActive(true);
}
Take a look on unity WaitForSeconds function.
By passing int param. (seconds), you can toggle your gameObject.
bool fadeIn = true;
IEnumerator Toggler()
{
yield return new WaitForSeconds(1);
fadeIn = !fadeIn;
}
then call this function by StartCoroutine(Toggler()).
You can use the Coroutines and new Unity 4.6 GUI to achieve this very easily. Check this article here which falsges a Text. YOu can modify it easily for gameobject easily
Blinking Text - TGC
If you just need the code, here you go
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class FlashingTextScript : MonoBehaviour {
Text flashingText;
void Start(){
//get the Text component
flashingText = GetComponent<Text>();
//Call coroutine BlinkText on Start
StartCoroutine(BlinkText());
}
//function to blink the text
public IEnumerator BlinkText(){
//blink it forever. You can set a terminating condition depending upon your requirement
while(true){
//set the Text's text to blank
flashingText.text= "";
//display blank text for 0.5 seconds
yield return new WaitForSeconds(.5f);
//display “I AM FLASHING TEXT” for the next 0.5 seconds
flashingText.text= "I AM FLASHING TEXT!";
yield return new WaitForSeconds(.5f);
}
}
}
P.S: Even though it seems to be an infinite loop which is generally considered as a bad programming practice, in this case it works quite well as the MonoBehaviour will be destroyed once the object is destroyed. Also, if you dont need it to flash forever, you can add a terminating condition based on your requirements.
Simple way is to use InvokeRepeating() and CancelInvoke() method.
InvokeRepeating("BlinkText",0,0.3) will repeatedly call BlinkText() for every 0.03 time Interval.
CancelInvoke("BlinkText") will stop the repeating invoke.
Here's the example :
//Call this when you want to start blinking
InvokeRepeating("BlinkText", 0 , 0.03f);
void BlinkText() {
if(Title.gameObject.activeSelf)
Title.gameObject.SetActive(false);
else
Title.gameObject.SetActive(true);
}
//Call this when you want to stop blinking
CancelInvoke("BlinkText");
Unity Documentation

Categories

Resources