Unity3D C# not going to the else statement (Invoke Repeating) - c#

I'm currently making my first game using Unity3D written in C#. I've faced some game bug just now, and it's been hours that I've been thinking of what might have been wrong with my code, but I can't see anything wrong.
I have a stat called healthRegen which add HP to the character every second. What I have noticed is even when my player HP drops to zero, it just keep adding HP to my player, thus making it alive again. I have a method that checks if my character HP drops to zero but it didn't call it and I don't know why.
public bool fullHealth() {
if(currentHealth >= maxHealth) {
return true;
} else {
return false;
}
}
public void adjustHealth() {
if(currentHealth > maxHealth) {
currentHealth = maxHealth;
}
if(currentHealth < 0) {
currentHealth = 0;
}
}
That is my method and this is my player script
void Start() {
InvokeRepeating("regenerate", 0f, 1f);
}
// Check if the player is still alive
public bool isDead() {
if (attribute.currentHealth == 0) {
return true;
} else {
return false;
}
}
// Die method
public void dead() {
animation.Play(dieClip.name);
}
private void regenerate() {
if (!attribute.fullHealth()) {
attribute.currentHealth += attribute.healthRegen;
} else {
attribute.adjustHealth();
}
}
This maybe a dumb question for others but I'm sorry, I don't know what to do anymore.

Looks like your problem is that regenerate() is adding health to your player if his health is at 0.
You just need to add a call to isDead() to prevent this.
private void regenerate() {
if (!attribute.fullHealth() && !attribute.isDead()) {
attribute.currentHealth += attribute.healthRegen;
} else {
attribute.adjustHealth();
}
}

I just found out a solution right now, I forgot the CancelInvoke() method
private void regenerate() {
if (!attribute.fullHealth()) {
attribute.currentHealth += attribute.healthRegen;
} else {
CancelInvoke("regenerate");
attribute.adjustHealth();
}
}
Now it's working perfectly!

Related

In Unity how do I stop perpetual math statements?

The question is simple but I can't for the life of me, figure it out.
My logic goes like this
// Static floats are StatBase.maxHealth = 0 and rStat.maxHealth = 70
class rStat : Monobehaviour
{
public bool nomatter = false;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
nomatter = true;
}
if (nomatter == true)
{
healthcalc();
}
void healthcalc()
{
StatBase.maxHealth += rstat.maxHealth; // StatBase.maxHealth should = 70 but the
// number never stops adding
nomatter = false;
}
}
}
To be honest that logic is quite strange.
Why have this bool flag if you already have one you want to act on? You can simply do
void Update()
{
if (Input.GetMouseButtonDown(0))
{
healthcalc();
}
}
// in general rather put this on class level and don't nest it under Update
void healthcalc()
{
StatBase.maxHealth += rstat.maxHealth;
}
or if there is only one line anyway even
void Update()
{
if (Input.GetMouseButtonDown(0))
{
StatBase.maxHealth += rstat.maxHealth;
}
}

Delay before gameobject set active couritine

I try to have a delay for 3 seconds before the game object(two panels) is activated when a button is pressed. I assigned the button on click the panelview() but I could not find how to write the couritine inside as the IEnumerator.
Can somebody help me? Thanks
Here is the part of the code I want to combine the couritine into:
public void panelview()
{
if (totalenergy == 0)
{
panel2.gameObject.SetActive(true);
}
if (totalenergy >= 1)
{
panel.gameObject.SetActive(true);
}
}
In this case you could also use Invoke without any Coroutine:
public void PanelView()
{
Invoke(nameof(ShowPanel), DELAY);
}
private void ShowPanel()
{
if (totalenergy == 0)
{
panel2.gameObject.SetActive(true);
}
else if (totalenergy >= 1)
{
panel.gameObject.SetActive(true);
}
}
Or as a Coroutine
public void PanelView()
{
StartCoroutine (ShowPanelAfterDelay(DELAY));
}
private IEnumerator ShowPanelAfterDelay(float delay)
{
yield return new WaitForSeconds (delay);
if (totalenergy == 0)
{
panel2.gameObject.SetActive(true);
}
else if (totalenergy >= 1)
{
panel.gameObject.SetActive(true);
}
}
where DELAY is the desired delay in seconds.
From a UX point of view though I'd say three seconds is pretty long for giving the user feedback after a button click ;)
This should do the trick (hopefully it compiles - I didn't check it out).
public void OnButtonClick() {
StartCoroutine(Delay());
}
public IEnumerator Delay()
{
yield return new WaitForSeconds(3);
// PanelView();
// or
// code that you want to execute it after 3 seconds
}

Unity 3d Invokerepeating for blinking Images

I am working on a car game project. I have made a UI Panel in which there are two UI Images (Left_Image and Right_Image). When I press "left" button then Left_Image starts blinking and when i press "right" button the Right_Image starts blinking. but what i want is that if Right_Image is already blinking and I press "left" button then Left_Image starts blink but Right_Images should be stopped. I have tried every trick but no luck. Please help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Indicators_UI : MonoBehaviour {
public GameObject flash_right;
public GameObject flash_left;
public float interval;
void Start()
{
InvokeRepeating("RightFlash", 0, interval);
InvokeRepeating("LeftFlash", 0, interval);
}
void Update ()
{
if (ControlFreak2.CF2Input.GetAxis ("right") != 0) {
if (!IsInvoking("RightFlash"))
InvokeRepeating("RightFlash", 0.35f, 0.35f);
}
else
{
CancelInvoke("RightFlash");
}
if (ControlFreak2.CF2Input.GetAxis ("left") != 0) {
if (!IsInvoking("LeftFlash"))
InvokeRepeating("LeftFlash", 0.35f, 0.35f);
}
else
{
CancelInvoke("LeftFlash");
}
}
void RightFlash()
{
if(flash_right.activeSelf)
flash_right.SetActive(false);
else
flash_right.SetActive(true);
}
void LeftFlash()
{
if(flash_left.activeSelf)
flash_left.SetActive(false);
else
flash_left.SetActive(true);
}
}
In general don't use InvokeRepeating etc with string! It is not very maintainable to call methods via their names. If you want to use them at least rather use nameof to be sure the name is not misspelled.
Then can't you just do
void Update ()
{
if (ControlFreak2.CF2Input.GetAxis ("right") != 0)
{
if (!IsInvoking(nameof(RightFlash)))
{
if(IsInvoking(nameof(LeftFlash)) CancelInvoke(nameof(LeftFlash));
InvokeRepeating(nameof(RightFlash), 0.35f, 0.35f);
}
}
else
{
CancelInvoke(nameof(RightFlash));
}
if (ControlFreak2.CF2Input.GetAxis ("left") != 0)
{
if (!IsInvoking(nameof(LeftFlash)))
{
if(IsInvoking(nameof(RightFlash)) CancelInvoke(nameof(RightFlash));
InvokeRepeating(nameof(LeftFlash), 0.35f, 0.35f);
}
}
else
{
CancelInvoke(nameof(LeftFlash));
}
}
// Btw: These you can implement way shorter this way ;)
void RightFlash()
{
flash_right.SetActive(!flash_right.activeSelf);
}
void LeftFlash()
{
flash_left.SetActive(!flash_left.activeSelf);
}
In your case I would actually rather use simple timers since you need the Update method anyway for getting the user input so why overcomplicating:
private float timerLeft;
private float timerRight;
void Update ()
{
if (ControlFreak2.CF2Input.GetAxis ("right") != 0)
{
timerRight -= Time.deltaTime;
if (timerRight <= 0)
{
timerRight = interval;
RightFlash();
}
}
// simply make them exclusive so only one button is handled at a time
// if you press both at the same time right overrules
else if (ControlFreak2.CF2Input.GetAxis ("left") != 0)
{
timerLeft -= Time.deltaTime;
if (timerLeft <= 0)
{
timerLeft = interval;
LeftFlash();
}
}
}
void RightFlash()
{
flash_right.SetActive(!flash_right.activeSelf);
}
void LeftFlash()
{
flash_left.SetActive(!flash_left.activeSelf);
}

How to generate random data without repeating in unity C#

Currently i'm developing a quiz kind of game for my gamification project. where i freeze the actual game when the player reaches some score and make question appear. If the answer is correct it will increase the speed of the player.And it will happen in a loop.
Here i'm using loadscene to load the index scene to avoid repeating of questions. The problem here is when the scene is loads it reloads the whole game instead of reloading the quiz part. Is there any way to do it ?
public class GameManager : MonoBehaviour
{
public Question[] facts;
private static List<Question> unansweredfacts;
private Question currentfacts;
[SerializeField]
private Text FactText;
[SerializeField]
private float TimeBetweenFacts = 3f;
[SerializeField]
private Text TrueAnswerText;
[SerializeField]
private Text FalseAnswerText;
[SerializeField]
private Animator animator;
[SerializeField]
public GameObject canvasquiz;
void Start()
{
if (unansweredfacts == null || unansweredfacts.Count == 0)
{
unansweredfacts = facts.ToList<Question>();
}
SetCurrentfact();
Debug.Log(currentfacts.Fact + "is" + currentfacts.IsTrue);
}
void SetCurrentfact()
{
int RandomFactIndex = Random.Range(0, unansweredfacts.Count);
currentfacts = unansweredfacts[RandomFactIndex];
FactText.text = currentfacts.Fact;
if (currentfacts.IsTrue)
{
TrueAnswerText.text = "CORRECT !";
FalseAnswerText.text = "WRONG !";
}
else
{
TrueAnswerText.text = "WRONG !";
FalseAnswerText.text = "CORRECT !";
}
}
IEnumerator TransitionToNextFact()
{
unansweredfacts.Remove(currentfacts);
yield return new WaitForSeconds(TimeBetweenFacts);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
public void UserSelectTrue()
{
animator.SetTrigger("True");
if (currentfacts.IsTrue)
{
Debug.Log("CORRECT !");
}
else
{
Debug.Log("WRONG !");
}
StartCoroutine(TransitionToNextFact());
}
public void UserSelectFalse()
{
animator.SetTrigger("False");
if (!currentfacts.IsTrue)
{
Debug.Log("CORRECT !");
}
else
{
Debug.Log("WRONG !");
}
StartCoroutine(TransitionToNextFact());
}
Loading the scene again basically restarts it, what you want to do is change the TransitionToNextFact method in the following manner
IEnumerator TransitionToNextFact()
{
unansweredfacts.Remove(currentfacts); // remove the last shown question for the list
canvasquiz.SetActive(false); // disables the quiz canvas until the next question
yield return new WaitForSeconds(TimeBetweenFacts);
SetCurrentfact(); // sets the next random question from the list
canvasquiz.SetActive(true); // show the quiz canvas along with the new question
}
i would also combine the two methods UserSelectTrue and UserSelectFalse into one
public void UserSelected(bool isTrue)
{
animator.SetTrigger(isTrue ? "True" : "False");
if (currentfacts.IsTrue == isTrue)
{
Debug.Log("CORRECT !");
}
else
{
Debug.Log("WRONG !");
}
StartCoroutine(TransitionToNextFact());
}

C# GUI Texture Button Script

I've finally found a script that can be used with a GUI Button in an IOS project. I am using Unity3d game engine. I'm a little familiar with JavaScript buttons and animation but am not familiar at all with C#. My problem is not knowing were to write the function that will play a queued animation in the C# button script when the button is touched. Below is copy of the IOS Button Script and then the code I have to play the queued animation.
using UnityEngine;
using System.Collections;
public enum Btn
{
normal,
hover,
armed
}
[System.Serializable] // Required so it shows up in the inspector
public class ButtonTextures
{
public Texture normal=null;
public Texture hover=null;
public Texture armed=null;
public ButtonTextures() {}
public Texture this [ButtonState state]
{
get
{
switch(state)
{
case ButtonState.normal:
return normal;
case ButtonState.hover:
return hover;
case ButtonState.armed:
return armed;
default:
return null;
}
}
}
}
[RequireComponent(typeof(GUITexture))]
[AddComponentMenu ("GUI/Button")]
public class GuiButton : MonoBehaviour
{
public GameObject messagee;
public string message = "";
public string messageDoubleClick = "";
public ButtonTextures textures;
protected int state = 0;
protected GUITexture myGUITexture;
private int clickCount = 1;
private float lastClickTime = 0.0f;
static private float doubleClickSensitivity = 0.5f;
protected virtual void SetButtonTexture(ButtonState state)
{
if (textures[state] != null)
{
myGUITexture.texture = textures[state];
}
}
public virtual void Reset()
{
messagee = gameObject;
message = "";
messageDoubleClick = "";
}
public bool HitTest(Vector2 pos)
{
return myGUITexture.HitTest(new Vector3(pos.x, pos.y, 0));
}
public virtual void Start()
{
myGUITexture = GetComponent(typeof(GUITexture)) as GUITexture;
SetButtonTexture(ButtonState.normal);
}
public virtual void OnMouseEnter()
{
state++;
if (state == 1)
SetButtonTexture(ButtonState.hover);
}
public virtual void OnMouseDown()
{
state++;
if (state == 2)
SetButtonTexture(ButtonState.armed);
}
public virtual void OnMouseUp()
{
if (Time.time - lastClickTime <= doubleClickSensitivity)
{
++clickCount;
}
else
{
clickCount = 1;
}
if (state == 2)
{
state--;
if (clickCount == 1)
{
if (messagee != null && message != "")
{
messagee.SendMessage(message, this);
}
}
else
{
if (messagee != null && messageDoubleClick != "")
{
messagee.SendMessage(messageDoubleClick, this);
}
}
}
else
{
state --;
if (state < 0)
state = 0;
}
SetButtonTexture(ButtonState.normal);
lastClickTime = Time.time;
}
public virtual void OnMouseExit()
{
if (state > 0)
state--;
if (state == 0)
SetButtonTexture(ButtonState.normal);
}
#if (UNITY_IPHONE || UNITY_ANDROID)
void Update()
{
int count = Input.touchCount;
for (int i = 0; i < count; i++)
{
Touch touch = Input.GetTouch(i);
if (HitTest(touch.position))
{
if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
{
SetButtonTexture(ButtonState.normal);
}
else
{
SetButtonTexture(ButtonState.armed);
}
if (touch.phase == TouchPhase.Began)
{
if (touch.tapCount == 1)
{
if (messagee != null && message != "")
{
messagee.SendMessage(message, this);
}
}
else if (touch.tapCount == 2)
{
if (messagee != null && messageDoubleClick != "")
{
messagee.SendMessage(messageDoubleClick, this);
}
}
}
break;
}
}
}
#endif
}
Most of this seems to deal with button states, where my touch button only has one state, which is 'normal'. Should references to 'hover' and armed just be deleted? I also get an error in the console saying; "the type or namespace "Button State" could not be found. Are you missing a using directive or an assembly reference?"
The code for C# GUI Button play queued animation I want to insert reads like this:
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void Update() {
if (Input.GetButtonDown("Btn"))
animation.PlayQueued("shoot", QueueMode.PlayNow);
}
}
I suppose of queued animation script snippet; Input.GetButtondown....would change to
void Update() {
if(Input.GetTouch("Btn"))
animation.PlayQueued("shoot", QueueMode.PlayNow);
}
and be inserted at about line 148 of the GUI Button script. Please help me if you can, I'm
feeling down. Seriously! Any help in reformatting this script would be greatly appreciated
and used as a template as I have two other GUI buttons to setup. It maybe asking a lot or what's a heaven for?
Respectfully,
Digital D
an analog man,
in a digital world
Okay, there's a lot going on in the script, but it appears to be designed so you don't have to modify it.
What you need to do is go to the object that will be playing the animation, and create a function on it that you wish to be called when the button is clicked.
So something like (you can do this in JS if you like):
public class ShootyMan : MonoBehaviour {
public void Shoot() {
animation.PlayQueued("shoot", QueueMode.PlayNow);
}
}
Now, look at the button in the inspector. I'm not sure if you're familiar with how messaging works in Unity, but basically the button will send a "message" to a "messagee". (ie. it will call a function of name message on any script attached to the messagee).
Set the "messagee" to the GameObject with the "Shoot" function. And set the message to the string "Shoot".

Categories