I'm going to enable bloody screen when got attack then disble it for two seconds.
The first way I tried is using SetActive (true/flase) .
It could work once but never worked back anymore.
I found the question by google, it seems that function will disable everything of that component.
So I found the second way which is using Image Class to do this.
but I not sure whether the way I used correctly or not.
The error msg : Object reference not set to an instance of an object
First Way (working once)
public class GameControllerScript : MonoBehaviour {
public GameObject bloodyScreen;
void Start () {
bloodyScreen.gameObject.SetActive (false); // disable at first
}
void Update () {
}
public void zombieAttack(bool zombieIsThere ){
bloodyScreen.gameObject.SetActive (true);
StartCoroutine(WaitTwoSeconds());
}
IEnumerator WaitTwoSeconds(){
yield return new WaitForSeconds (2f);
bloodyScreen.gameObject.SetActive (false);
} }
Second Way (not working)
public class GameControllerScript : MonoBehaviour {
public Image image;
void Start () {
GameObject go = GameObject.Find("Canvas");
if (!go)
return;
image = go.GetComponent<Image>();
image.enable = false;
}
void Update () {
}
public void zombieAttack(bool zombieIsThere ){
image.enabled = true;
StartCoroutine(WaitTwoSeconds());
}
IEnumerator WaitTwoSeconds(){
yield return new WaitForSeconds (2f);
image.enabled = false;
} }
Sorry for my english if soemthing is not clear.
What my object likes https://i.stack.imgur.com/DChuS.png
there are so many ways of doing the BLOODY SCREEN.
1.)
STEP 1
Make an array of texture then put all of your texture there
STEP 2
On your OnTriggerEnter or OnCollisionEnter or what ever function you want and try implementing this code
Texture[] arrayOfTexture = new Texture
foreach(Texture textures in arrayOfTexture){
//set the alpha here from 1-0/0-1
}
2.)
You can do it also in an Update() function and put it on a timer you can do it something like
float timer = 10f;
void Update(){
timer -= time.DeltaTime;
if(timer < 1){
//reset timer here
}else{
//do the alpha thing here
}
}
void ResetTimer(){
timer = 10f;
}
Related
I'm making a 3D roulette game, where when the player presses the 'bet' button the ball will be positioned in a given location and also a force will be added to the ball. I've added 37 individual box colliders to know where the ball lands.
My problem is that from my understanding the bet function gets executed in a single frame. This means that the script checks for the fallen number before the ball has finished moving and landed inside a box collider. So the for the first bet the number fallen will be 0 even if it lands on another number, and then on the 2nd bet it will have the value of the first fallen number, etc...
public void BetSpinWheel()
{
uiController.repeatButton.interactable = true;
Spin();
int earnings = CalculateEarnings(currentBet.ToArray(), lastNumbers);
UpdateBalance(earnings);
lastBet.Clear();
foreach(Bet bet in currentBet)
{
lastBet.Add(bet);
}
currentBet.Clear();
updateUI();
uiController.ChangeLastNumberText();
}
private void Spin()
{
audioSource.Stop();
audioSource.Play();
ballController.SpinBall();
while (ballController.isMoving)
{ random++; }
if (!ballController.isMoving && ballController.hasBallEnteredCollider)
{
lastNumbers = ballController.numberFallen;
print(lastNumbers);
}
}
and here is the ballController.SpinBall() function:
public void SpinBall()
{
rb.constraints = RigidbodyConstraints.None;
transform.position = ballStartPosition;
rb.velocity = Vector3.zero;
transform.rotation = Quaternion.Euler(0f, 0f, 0f);
rb.AddForce(forceToAdd, ForceMode.Impulse);
print(isMoving);
}
If you would like to view the whole project, you can find it here:
https://github.com/hamzaOud/Assignment02
Use a Coroutine:
// The button should call this
public void BetSpinWheel() {
StartCoroutine(BetSpinWhellCoroutine());
}
private IEnumerator BetSpinWheelCoroutine() {
// Your bet stuff here
}
You can also 'store' a coroutine, so that you can stop it if you need to:
private Coroutine betSpinWheelCoroutine
// The button should call this
public void BetSpinWheel() {
// Stop the coroutine first if it was running.
if (betSpinWheelCoroutine != null){
StopCoroutine(betSpinWheelCoroutine);
}
betSpinWheelCoroutine = StartCoroutine(BetSpinWhellCoroutine());
}
private IEnumerator BetSpinWheelCoroutine() {
// Your bet stuff here
}
I'm planning on have several buttons to spawn monsters, all with a different cooldown. Is there a way to match the animation clip to be the same length as the cooldown of the button?
Here is an example: https://gyazo.com/0a2ae868e5458c701e1a258aac6dc59a
The animation is 1 second but the cooldown is 3 seconds.
Here is my code:
private void ButtonCooldown()
{
if (GetComponent<Button>().interactable == false)
{
buttonTimer += Time.deltaTime;
if (buttonTimer >= cooldown)
{
GetComponent<Button>().interactable = true;
buttonTimer = 0;
}
}
}
public void DisableButton()
{
GetComponent<Button>().interactable = false;
myAnimatior.SetTrigger("ButtonCooldownAnimation");
}
You could adjust the according Animator's speed to adjust it's overall playback speed.
E.g. something like
// Adjust in the inspector
[SerializeField] private float cooldownTime = 3;
// Already reference this via the Inspector if possible
[SerializeField] private Button button;
private void Awake ()
{
// Do this only once!
if(!button) button = GetComponemt<Button>();
}
public void DisableButton()
{
button.interactable = false;
// typo in Animator btw ;)
myAnimatior.SetTrigger("ButtonCooldownAnimation");
// Make the Animator play slower so the animation now takes 3 seconds
myAnimatior.speed = 1/cooldownTime;
// Instead of Update simply use Invoke here
// Execute the method called WhenCooldownDone after cooldownTime seconds
Invoke(nameof(WhenCooldownDone), cooldownTime);
}
private void WhenCooldownDone ()
{
button.interactable = true;
myAnimator.speed = 1;
}
As in the comments I would use Invoke instead of constantly checking the states in Update. In particular never use GetComponentrepeatedly in Update. It is very expensive. Always try to rather store the reference and reuse it.
Below is my C# script. I added a button to my project with a On Click event and called the Rotate() method. But for some reason it is not working
using System.Threading;
using UnityEngine;
public class Orbit : MonoBehaviour {
public GameObject sun;
public float speed;
// Use this for initialization
void Start () {
}
public void Update()
{
Rotate();
}
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed *
Time.deltaTime);
}
}
I commented the Update() method when calling the Rotate() method. I also created a game object for the script.
The reason why it only works in Update currently is that
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
needs to be called repeatetly. Otherwise it will only rotate for exactly one frame and cause of Time.deltaTime only a very small amount. But the onClick event of the Button component is fired only once. It is similar to e.g. Input.GetKeyDown which is only called once when the key goes down. There is no implementation in the Button component itslef to handle a continued button press.
What you want instead as far as I understand is rotating the object after the button click
start to rotate for ever
for a certain duration
until you press the button again
until it is released (-> implement a continuesly firing button see below)
The Button component alone can only do the first three:
Rotate for ever
Either using a Coroutine
private bool isRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isRotating) return;
// start the rotation
StartCoroutine(RotateRoutine());
isRotating = true;
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
or still doing it in Update
private bool isRotating = false;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void Rotate()
{
// enable the rotation
isRotating = true;
}
Note that the Update solution is only for your understanding what is happening. It should not be used like that because it is not that efficient since Update is called continously and checks the bool also if not rotating yet. That produces unnecessary overhead. The same applies to all following examples: Prefere to use the Coroutines over Update (In this case! In other cases it is actuall better and more efficient to use one Update method instead of multiple concurrent Coroutines .. but that's another story.)
Rotate for a certain duration
As Coroutine
// adjust in the inspector
// how long should rotation carry on (in seconds)?
public float duration = 1;
private bool isAlreadyRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isAlreadyRotating) return;
// start a rottaion
StartCoroutine(RotateRoutine());
}
private IEnumerator RotateRoutine()
{
// set the flag to prevent multiple callse
isAlreadyRotating = true;
float timePassed = 0.0f;
while(timePassed < duration)
{
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// add the time passed since last frame
timePassed += Time.deltaTime;
// leave here, render the frame and continue in the next frame
yield return null;
}
// reset the flag so another rotation might be started again
isAlreadyRotating = false;
}
or in Update
public float duration;
private bool isRotating;
private float timer;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// reduce the timer by passed time since last frame
timer -= Time.deltaTime;
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// if the timer is not 0 return
if(timer > 0) return;
// stop rottaing
isRotating = false;
}
public void Rotate()
{
// if already rotating do nothing
if(isRotating) return;
// start rotating
isRotating = true;
// enable timer
timer = duration;
}
Toggle rotation
This is very similar to the one before but this time instead of the timer you stop the rotation by clicking again. (You even could combine the two but than be carefull to reset the isRotating flag correctly ;) )
As Coroutine
private bool isRotating;
public void ToggleRotation()
{
// if rotating stop the routine otherwise start one
if(isRotating)
{
StopCoroutine(RotateRoutine());
isRotating = false;
}
else
{
StartCoroutine(RotateRoutine());
isRotating = true;
}
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
or as Update
private bool isRotating;
private void Update()
{
// if not rotating do nothing
if(!isRottaing) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void ToggleRotation()
{
// toggle the flag
isRotating = !isRotating;
}
Rotate until released
This is the most "complicated" part since the Button alone can not accomplish this (there is no "on Release"). But you can implement this using IPointerXHandler interfaces.
The good news: You can keep your original script as you have it currently
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed *
Time.deltaTime);
}
Now you need an extension for the button. It will call the whilePressed event repeatedly every frame like Update so you just have to reference your Rotate method in whilePressed instead of the onClick.
Again there are two options either implementing it as a Coroutine:
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
// reference the same way as in onClick
public UnityEvent whilePressed;
private Button button;
private bool isPressed;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
// Handle pointer down
public void OnPointerDown()
{
// skip if the button is not interactable
if(!button.enabled || !button.interactable) return;
// skip if already rotating
if(isPressed) return;
StartCoroutine(PressedRoutine());
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
isPressed= false;
}
private IEnumerator RotateRoutine()
{
// repeatedly call whilePressed until button isPressed turns false
while(isPressed)
{
// break the routine if button was disabled meanwhile
if(!button.enabled || !button.interactable)
{
isPressed = false;
yield break;
}
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
// leave here, render the frame and continue in the next frame
yield return null;
}
}
}
or you could do the same in Update again as well
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
public UnityEvent whilePressed;
private bool isPressed;
private Button button;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
private void Update()
{
// if button is not interactable do nothing
if(!button.enabled || !button.interactable) return;
// if not rotating do nothing
if(!isPressed) return;
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
}
// Handle pointer down
public void OnPointerDown()
{
// enable pressed
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
// disable pressed
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
// disable pressed
isPressed= false;
}
}
Place this component next to a Button component. You don't have to reference anything in onClick just leave it empty. Instead reference something in onPressed. Keep the Button component though since it handles also the UI style for us (like hover changes the color/sprite etc.)
Again: The Update solutions might look cleaner/simplier for now but are not as efficient (in this usecase) and easy to controll (this might be opinion based) as the Coroutine solutions.
Please search on the article regarding the key press functionalities . It would help you a lot in finding your answer. Update is used if we need to do something continuously in our project where as the key pressed is used when we have do it for once
this example is also being used to resolve your issue and use this Script when a specific button is is being pressed
I need to make a cooldown for an attack. Right now you can spam it and can swing as fast as you can click but I would like to balance the game better and add a small cooldown. I cant seem to find how to do this anywhere though. Anyone have any ideas?
You can use IEnumerator function which contains something like this:
public class YourClass : MonoBehaviour
{
private float cooldownTime;
private bool isCooldown;
// your code
private void Update()
{
if (!isCooldown)
{
// Do stuff
}
}
private IEnumerator Cooldown()
{
// Start cooldown
isCooldown = true;
// Wait for time you want
yield return new WaitForSeconds(cooldownTime);
// Stop cooldown
isCooldown = false;
}
}
Just start coroutine in attack handler
You would want something along the lines of the following. Since I don't have any of your code, I'll assume this is under a player class and will write accordingly.
class PlayerClass : MonoBehaviour
{
float timer = 0.0f;
float cooldownTime = 1.0f;
void Update() {
if(timer > cooldownTime) {
if(Input.GetMouseButtonDown(0)) {
Attack();
timer = 0;
}
}
if(timer < cooldownTime + 1) // Add some leaniency for inaccurate floating points.
timer += time.DeltaTime;
}
}
Obviously, try this code. I'm at work right now and don't have access to my PC so you'll need to deal with untested C#! And of course, do some googling on timers and stuff like that, because that will be where you need to look. Also look into IEnumerable for asynchronous code if you're looking to get fancy.
(>^3^)>
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