How to have a button run while it is being pressed - c#

Im trying to have a method continuously run while a button is pressed. At the moment the method only runs once when the UI button is pressed. I've tried implementing a coroutine but I couldn't figure out a way a condition for StopCoroutine() to run. I've also tried using the event triggers PointerUp and PointerDown that set a boolean to false and true that determine if a coroutine runs or not but that seemed to work.
Would anyone know how to implement a continuous on button hold? Any help is appreciated.
The method I want to continuously run while the button is held. This method is called in a PointerDown event trigger in the UI button.
public void spawnLaser()
{
GameObject laser = Instantiate(laserprefab, transform.position, Quaternion.identity) as GameObject;
laser.GetComponent<Rigidbody2D>().velocity = new Vector2(laserSpeed, 0);
}

Input.GetKeyDown returns true during the frame the user starts pressing down the key identified by name, while Input.GetKey returns true while the user holds down the key identified by name.
Seems you need to give Input.GetKey a try.
To keep it calling while pressed, call it from the Update().
public void spawnLaser()
{
GameObject laser = Instantiate(laserprefab, transform.position,
Quaternion.identity) as GameObject;
laser.GetComponent<Rigidbody2D>().velocity = new Vector2(laserSpeed, 0);
}
void Update()
{
if (Input.GetKey("up"))
{
print("up arrow key is held down");
spawnLaser();
}
}

In general you can just implement the IPointerDownHandler, IPointerUpHandler, IPointerExitHandler and IPointerEnterHandler interfaces like e.g.
public class WhilePressedButton : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
{
public UnityEvent whilePressed;
// adjust according to your needs via the Inspector
[Min(0f)] public float delay = 0.3f;
public void OnPointerDown(PointerEventData pointerEventData)
{
StartCoroutine(ContinuousButton());
}
public void OnPointerUp(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
public void OnPointerEnter(PointerEventData pointerEventData)
{
// Actually not really needed
// but not sure if maybe required for having OnPointerExit work
}
public void OnPointerExit(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
private IEnumerator ContinuousButton()
{
// Whut? o.O
// No worries, this is fine in a Coroutine as long as you yield somewhere inside
while (true)
{
whilePressed.Invoke();
// Very important for not freezing the editor completely!
// This tells unity to "pause" the routine here
// render the current frame and continue
// from this point on the next frame
// If you want some delay between calls
if(delay > 0)
{
yield return new WaitForSeconds (delay);
}
else
{
// Otherwise it directly contineus in the next frame
yield return null;
}
}
}
}
And reference your spawnLaser method in whilePressed like usually in a Button onClick via the Inspector or in code.
Note: This is either on an UI element such as a UI.Button or on a 3D object with a collider but then you additionally require a PhysicsRaycaster component on your Camera.
You do NOT need an EventTrigger component for this! The interface methods OnPointerXY will simply be called by the UI itself!
However, are you really going to Instantiate a new object every frame while the button is pressed?
You should probably checkout Object Pooling or in general only activate and deactivate your object instead of Instantiate and delete it over and over again.

According to Unity Documentation:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;// Required when using Event data.
public class ExampleClass : MonoBehaviour, IPointerDownHandler// required interface when using the OnPointerDown method.
{
//Do this when the mouse is clicked over the selectable object this script is attached to.
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log(this.gameObject.name + " Was Clicked.");
}
}
Add the IPointerDownHandler interface to your class and implement the OnPointerDown(PointerEventData) method.
I recommend using a bool to track the state of the button (e.g true if pressed) and then in your Update() method, if the bool is true, make your lazer move.

Related

Unity: Even when the "player" game object gets destroyed, the text doesn't appear

I've been using Unity to create a simple 2D game but the problem is that even when the game object "player" gets destroyed, the gameobject "isDead" (text) doesn't appear.
This is my script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class youDied_Text : MonoBehaviour
{
private Transform player;
private Text isDead;
// public static bool isDead;
// Start is called before the first frame update
private void Start() {
isDead = GetComponent<Text>();
}
void checkForDeath()
{
if (player==false)
{
isDead.gameObject.SetActive(true);
}
else
{
isDead.gameObject.SetActive(false);
}
}
// Update is called once per frame
void Update()
{
player = GameObject.FindWithTag("Player").transform;
checkForDeath();
}
}
This script is attached in the text which I need to display in UI element.
As was noted currently you would get a NullReferenceException which is definitely not what you want.
There is absolutely no need / redundancy going through Transform at all actually. Simply store the GameObject reference instead
You are currently setting the object to inactive which has the Text attached ... which is the same object your component is attached to as well!
=> As soon as you end up in the second case once you set it to inactive => from now on Update is never called anymore!
In general as it sounds like this should only happen once anyway I would use a more event driven approach and have a component on your player like e.g.
public class Player : MonoBehaviour
{
public UnityEvent onDied;
private void OnDestroy ()
{
onDied.Invoke();
}
}
And then simply attach a listener/callback to that event once without poll checking states. You can do this either via the Inspector directly (just like in e.g. Button.onClick) or via code like e.g.
public class youDied_Text : MonoBehaviour
{
// Already reference things via the Inspector if possible!
[SerializeField] private GameObject player;
[SerializeField] private Text isDead;
private void Awake()
{
if(!isDead) isDead = GetComponent<Text>();
isDead.gameObject.SetActive(false);
// If you want to rather set it via the Inspector remove all the rest here
//if(!player) player = GameObject.FindWithTag("Player"). GetComponent<Player>();
// or even simpler
if(!player) player = FindObjectOfType<Player>();
player.onDied.AddListener(OnPlayerDied);
}
// If you want to rather set it via the Inspector make this public
private void OnPlayerDied()
{
isDead.gameObject.SetActive(true);
}
}

Unity changing components of different assets when a button is clicked

I am very new to Unity and c# in general but I currently have a game that has 2 different backgrounds, and I would like to figure out how to swap between the 2 backgrounds - LightBackground and NightBackground - when a button is clicked, with the default background being the LightBackground and when the button is clicked, the NightBackground is used. My idea was to change the order in layer (sortingOrder) of the NightBackground when the button is clicked but was very unsuccessful upon attempting many things.
At the moment I have made a Night/Dark script which I have placed in the button on-click and it changes the sortingOrder of the Nightbackground but how can i make it simultaneously change the sortingOrder of the LightBackground. In addition, currently i have a game object called Game Manager which is linked to the Gameover Canvas which displays the button I want and the replay button; but as soon as the button is clicked, the background does change but as soon as the replay button is clicked, the background goes back to the LightBackground.
NightDark Script:
public class NightDark : MonoBehaviour
{
public GameObject NightBackground;
// Start is called before the first frame update
void Start()
{
NightBackground.GetComponent<SpriteRenderer>().sortingOrder++;
}
// Update is called once per frame
void Update()
{
}
}
Game Manager Script:
public class GameManager : MonoBehaviour
{
public GameObject gameOverCanvas;
private void Start()
{
Time.timeScale = 1;
}
public void GameOver()
{
gameOverCanvas.SetActive(true);
Time.timeScale = 0;
}
public void Replay()
{
SceneManager.LoadScene(0);
}
}
It may be easier to view the Game Manager and NightDark here.
If any of you could help in the slightest, it would be much appreciated since I am really struggling at the moment. Thanks again.
What speaks against having one single background controller and rather only switch out the Sprite you display in the backgrounds SpriteRenderer.sprite property
public class BackgroundController : MonoBehaviour
{
// Drag all these in via the Inspector
[Header("References")]
[SerielizeField] private SpriteRenderer backgroundRenderer;
[Header("Assets")]
[SerielizeField] private Sprite daySprite;
[SerielizeField] private Sprite nightSprite;
// This is static so it keeps its value session wide also after
// reloading the scene
private static bool _isDay;
private void Start()
{
// inverts the _isDay -> starts as day the first time
SwitchBackground();
}
// This you call when the button is clicked
public void SwitchBackground()
{
// invert the flag
_isDay = !_isDay;
// chose the new sprite according to the flag
backgroundRenderer.sprite = _isDay ? daySprite : nightSprite;
}
}

How can I change this animation transition to happen automatically?

Currently, my code allows the player to go to the next scene by clicking. I want to, however, make the fade out into the next scene animation automatic after 4 seconds. How can I do this?
I've tried looking up information, but nothing seems to work.
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class LevelChanger : MonoBehaviour
{
// Start is called before the first frame update
float timer = 4f;
public Animator animator;
private int levelToLoad;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Invoke("FadeToLevel(1)", 2f);
}
}
public void FadeToLevel (int levelIndex)
{
levelToLoad = levelIndex;
animator.SetTrigger("FadeBlack");
}
public void OnF`enter code here`adeComplete()
{
SceneManager.LoadScene(levelToLoad);
}
}
The code works as intended, but I want the animation to happen automatically.
If I understood correctly, what you are aiming is to make an animation to play automatically when the player enters the new scene.
If that's the case, then you are looking for the sceneLoaded() method from SceneManager
Also, this discussion may be useful

How to make an object clickable depending on player distance?

I need to make an item in my scene clickable but only when the player is near the item. In my script I make the item in question go automatically to an empty GameObject that is child of my Player in the hierarchy to define the position but the click is able as soon as the camera have it framed. I'm using the character controller provided in the 2d physics and not a 2drigidbody so I'm even more confused because I can't use a collider.
I'm pretty sure you can have both a character controller and a collider on a gameobject (at least a trigger collider).
Then instead of whatever you're using to detect the click, you should use in an Update loop something like Input.GetKeyDown(KeyCode.Mouse0), and use a raycast where you can specify the length of the ray. https://docs.unity3d.com/ScriptReference/Physics.Raycast.html
In order to make anything clickable I would recommend IPointerXHandler interfaces (replace X with Click, Enter, Exit, Down, Up, etc).
Note:
Ensure an EventSystem exists in the Scene to allow click detection. For click detection on non-UI GameObjects, ensure a PhysicsRaycaster is attached to the Camera.
If you only want to click IPointerClickHandler is enough. If you want some visiual feedback like changing colors etc you'll have to expand it with at least IPointerEnterHandler and IPointerExitHandler.
public class MyClickable : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData pointerEventData)
{
...
}
}
Then in order to get the distance between two obects you can simply use Vector3.Distance e.g. with a ceratin threshold
// configure those e.g. in the Inspector
public float distanceThreshold;
public Transofrm playerTransform;
public Transform itemTransform;
and than use something like
if(Vector3.Distance(playerTransform.position, itemTransform.position) <= distanceThreshold)
{
...
}
so if you directly implement this into the MyClickable you could do visual feedback also in Update something like
public class MyClickable : MonoBehaviour, IPointerClickHandler
{
public float distanceThreshold;
public Transofrm playerTransform;
// this gives you an event you can configure in the Inspector
// exactly like you would with a button
public UnityEvent onClick;
private bool isInRange;
public void OnPointerClick(PointerEventData pointerEventData)
{
// if too far away do nothing
if(Vector3.Distance(playerTransform.position, transform.position) > distanceThreshold) return;
....
onClick.Invoke();
}
private void Update()
{
if(Vector3.Distance(playerTransform.position, transform.position) <= distanceThreshold)
{
// e.g. make object green
}
else
{
// e.g. make object grey
}
}
}
My suggestion is to use the onMouseDown() method. If you have a collider or trigger attached to your gameObject, onMouseDown() will detect mouse clicks on the object. Then in then body of onMouseDown() you can test if it is in range.
This is an example script how it could work:
public class ItemClickable : MonoBehaviour
{
public Transform player; // player-transform reference (depends if you have a singleton or not)
public float range; // radius (maybe you have a range or radius already set in your player instance)
void Start()
{
// Setup for your references
}
private void OnMouseDown()
{
// Checks if the item is in the range of the player
if ((player.position-gameObject.transform.position).magnitude < range) // Vector3.Distance() is also possible
{
Destroy(gameObject); // or do whatever you want in here
}
}
}

Check if animation has finished playing?

How can I check if a specific animation has finished playing in Unity, then execute an action? [C#] I am not using an animator.
From: http://answers.unity3d.com/questions/52005/destroy-game-object-after-animation.html
To execute an action from the animation editor...
-Create a script with a simple public function that will destroy the object. e.g.
public class Destroyable : MonoBehaviour
{
public void DestroyMe()
{
Destroy(gameObject);
}
}
-Add that script to the animated object you want to destroy.
-In the animation editor, move the animation scrubber to the end of the animation.
-Use the 'Add Event' button in the animation toolbar
-Select 'DestroyMe' from the function drop-down in the Edit Animation Event dialog.
-Now your animation should play, run the 'DeleteMe' function, and destroy the object/do your action.
I've used this method a few times, comes in handy for certain things in animations :)
You should check Animation.IsPlaying value.
From the docs:
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
public Animation anim;
void Start() {
anim = GetComponent<Animation>();
}
void OnMouseEnter() {
if (!anim.IsPlaying("mouseOverEffect"))
anim.Play("mouseOverEffect");
}
}
Just as Andrea said in his post : Animation-IsPlaying is pretty much what you need since you don't use Animator. Check Animation to see other sweet stuff you can use.
using UnityEngine;
using UnityEngine.Collections;
public class ExampleClass : MonoBehaviour
{
Animation anim;
void Start()
{
anim = GetComponent<Animation>();
}
//In update or in another method you might want to check
if(!anim.isPlaying("StringWithAnimationClip") //or anim.clip.name
//Do Something
}
You can also force stop the animation with anim.Stop();
Now you commented that you don't want to use isPlaying() so if you can please elaborate I will edit my post.

Categories

Resources