Unity 3d Invokerepeating for blinking Images - c#

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);
}

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
}

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

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!

Unity3d Interactive pause menu that has uses Input.GetAxis to Navigate the new UI

Okay So I'm trying to create an interactive pause menu that has access to Input.GetAxis using Unity 4.6's new UI.
As long as the Navigation relationships are set up for the selectable elements (In my case a series of buttons) and Interactable is set to true then the UI works just fine but, when I set the Time.timeScale = 0.0f; then keyboard input navigation no longer works. However the mouse input clicks and animation still work as intended.
I've tried several different ways to get around the time scale problem and the best I've got so far is checking the value returned from Input.GetAxis() while in the body of the Update message of the MonoBehavor base object. This somewhat works but my results are either the very top or very bottom of the Button selected collection. I'm thinking this is because update gets called a great deal more than FixedUpdate and would make sense if my console printed out more call to the method that moves up and down the selection. So with that I'm thinking its one of those "office space" type errors, off by 1 decimal place or something silly but I just can't seem to see it. Otherwise I think my solution would be a fairly viable work around.
The following is an image of my Unity Setup with mentioned game objects in Unity 4.6 followed by my code.
public class PlayerInGameMenu : MonoBehaviour
{
public EventSystem eventSystem;
Selectable SelectedButton;
public Selectable Status;
public Selectable Settings;
public Selectable Save;
public Selectable Quit;
public bool Paused;
List<Selectable> buttons;
int selecteButtonIndex = 0;
public Canvas Menu;
void Start()
{
Menu.enabled = false;
buttons = new List<Selectable>();
buttons.Add(Status);
buttons.Add(Settings);
buttons.Add(Save);
buttons.Add(Quit);
SelectedButton = buttons[0];
}
void Update()
{
CheckInput();
if (Paused && !Menu.enabled)
{
ShowMenu();
}
else if (!Paused && Menu.enabled)
{
HideMenu();
}
}
void ShowMenu()
{
Paused = true;
Menu.enabled = true;
Time.timeScale = 0.0f;
}
void HideMenu()
{
if (Menu.enabled)
{
Paused = false;
Menu.enabled = false;
Time.timeScale = 1.0f;
}
}
void CheckInput()
{
if (cInput.GetKeyDown("Pause"))
{
Paused = !Paused;
SelectedButton = buttons[selecteButtonIndex];
eventSystem.SetSelectedGameObject(SelectedButton.gameObject, new BaseEventData(eventSystem));
}
if (Paused)
{
float v = cInput.GetAxis("Vertical");
//to attempt to cut down on the input sensitity I am checking 0.5 instead of just 0.0
if (v >= 0.5)
{
GoDown();
}
else if (v <= -0.5)
{
GoUp();
}
}
}
void GoDown()
{
//go to the last button available if we go past the index
if (selecteButtonIndex > buttons.Count - 1)
{
selecteButtonIndex = buttons.Count - 1;
}
else
{
selecteButtonIndex = selecteButtonIndex + 1;
}
}
//go to the first button available if we go past the index
void GoUp()
{
if (selecteButtonIndex < 0)
{
selecteButtonIndex = 0;
}
else
{
selecteButtonIndex = selecteButtonIndex - 1;
}
}
}
I know its in beta but I'm wondering if you are going to implement navigation why would you design it in such a way that Time.timeScale=0.0f; (the easy way to pause a game) does not work with the UI button navigation naturally. Problems for minds greater than I maybe? Or there is a simple way to do it and I just do not know what bit I need to flip.
I've also considered just freezing rigid bodies on pause but that seems like will require a huge time investment in my existing code base and will not be a universal solution across all game objects particularly colliders that do not rely on Rigid bodies and particle systems. I'm pretty open minded about solutions but it seems like there should be a really easy way to do this.
This worked like a charm:
var v = Input.GetAxisRaw("JoyY1"); // Or "Vertical"
if (Math.Abs(v) > ButtonThreashold)
{
var currentlySelected = EventSystem.currentSelectedGameObject
? EventSystem.currentSelectedGameObject.GetComponent<Selectable>()
: FindObjectOfType<Selectable>();
Selectable nextToSelect = null;
if (v > ButtonThreashold)
{
nextToSelect = currentlySelected.FindSelectableOnUp();
}
else if (v < -ButtonThreashold)
{
nextToSelect = currentlySelected.FindSelectableOnDown();
}
if (nextToSelect)
{
EventSystem.SetSelectedGameObject(nextToSelect.gameObject);
}
}
Okay so my solution to this problem was to utilize Time.realtimeSinceStartup to check for input on fixed intervals and develop an abstract class that inherits from MonoBehavior. What that looks like in code:
public abstract class RealtimeMonoBehavior:MonoBehaviour
{
public float updateInterval = 0.5F;
private double lastInterval;
void Start()
{
DefaultIntervalStart();
lastInterval = Time.realtimeSinceStartup;
RealtimeIntervalStart();
}
void Update()
{
DefaultIntervalUpdate();
float timeNow = Time.realtimeSinceStartup;
if (timeNow > lastInterval + updateInterval)
{
lastInterval = timeNow;
RealtimeIntervalUpdate();
}
}
public virtual void DefaultIntervalUpdate(){}
public virtual void DefaultIntervalStart(){}
public virtual void RealtimeIntervalStart(){}
public virtual void RealtimeIntervalUpdate(){}
}
And here is what my code looks like after implementing the change
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using Extensions;
using UnityEngine.EventSystems;
public class PlayerInGameMenu : RealtimeMonoBehavior
{
public EventSystem eventSystem;
Selectable SelectedButton;
public Selectable Status;
public Selectable Settings;
public Selectable Save;
public Selectable Quit;
public float ButtonThreashold;
public bool Paused;
List<Selectable> buttons;
int selectedButtonIndex;
public PlayerMovement PlayerMovement;
public Canvas Menu;
public override void RealtimeIntervalStart()
{
base.RealtimeIntervalStart();
Menu.enabled = false;
buttons = new List<Selectable>();
buttons.Add(Status);
buttons.Add(Settings);
buttons.Add(Save);
buttons.Add(Quit);
selectedButtonIndex = 0;
SelectedButton = buttons[selectedButtonIndex];
eventSystem.SetSelectedGameObject(SelectedButton.gameObject, new BaseEventData(eventSystem));
}
public override void DefaultIntervalUpdate()
{
base.DefaultIntervalUpdate();
if (cInput.GetKeyDown("Pause"))
{
Paused = !Paused;
}
}
public override void RealtimeIntervalUpdate()
{
base.RealtimeIntervalUpdate();
CheckInput();
if (Paused && !Menu.enabled)
{
ShowMenu();
}
else if (!Paused && Menu.enabled)
{
HideMenu();
}
}
void ShowMenu()
{
Paused = true;
Menu.enabled = true;
Time.timeScale = 0.0f;
}
void HideMenu()
{
if (Menu.enabled)
{
Paused = false;
Menu.enabled = false;
Time.timeScale = 1.0f;
}
}
void CheckInput()
{
if (Paused)
{
float v = Input.GetAxisRaw("Vertical");
if (v > ButtonThreashold)
{
GoUp();
}
else if (v < -ButtonThreashold)
{
GoDown();
}
SelectedButton = buttons[selectedButtonIndex];
eventSystem.SetSelectedGameObject(SelectedButton.gameObject, new BaseEventData(eventSystem));
}
}
void GoDown()
{
if (selectedButtonIndex < buttons.Count - 1)
{
selectedButtonIndex = selectedButtonIndex + 1;
}
}
void GoUp()
{
if (selectedButtonIndex > 0)
{
selectedButtonIndex = selectedButtonIndex - 1;
}
}
}
Nod to imapler, Input.GetAxisRaw feels better for checking the input.

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