I'm working with a 3D avatar and it's my first time working with animations. I have already done one which goes from the idle animation to another animation. It does the transition when the value of a slidebar is below 40, here is the code:
public Slider barraAlimento;
void Start()
{
anim = GetComponent<Animator>();
}
void Update()
{
if (barraAlimento.value <= 40)
{
anim.SetBool("IrHambriento", true);
}
else
{
anim.SetBool("IrHambriento", false);
}
}
This works correctly, but my problem is that now I want to do another transition. When I click a button, the value of the slidebar increases (that's how is triggered the previous animation), so I want a transition between the previous animation and the new one when the button is pressed. I have tried this, but it doesn't work:
public Button botonManzana;
void Start()
{
anim = GetComponent<Animator>();
botonManzana.onClick.AddListener(ButtonManzanaClicked);
}
void ButtonManzanaClicked()
{
anim.SetBool("IrComiendo", true);
anim.SetBool("IrComiendo", false);
}
The boxes and arrows are like this, if that's useful information.
Animation transition
I believe you're looking for a trigger rather than a bool.
Change the bool value in your animator from Boolean to Trigger and change your code to...
void ButtonManzanaClicked()
{
anim.SetTrigger("IrComiendo");
}
This will make a transition happen every time you "Trigger" the trigger, like a one time thing, that doesn't require any boolean true -> false for resetting like you're attempting in this code.
The reason your code isn't working, I believe, is because you're re-setting it back to false in the same frame, and I think the animation system will look at the values at the end of each code frame, so to speak.
this is probably happening because in:
void ButtonManzanaClicked()
{
anim.SetBool("IrComiendo", true);
anim.SetBool("IrComiendo", false);
}
you are setting it to false right after you set it to true.
Try putting only anim.SetBool("IrComiendo", true); in the button click event, and anim.SetBool("IrComiendo", false); only when you want it to go back to the "Hambriento" animation
EDIT:
try doing this:
change "IrComiendo" to trigger (just to not need to change its value to false)
OBS: use anim.SetTrigger("IrComiendo"); to call it
set the connections as in the image:
* connection not necessary if its impossible that the value of the slider still smaller than 40 after the button is clicked
** put this connection if you want it to go to Comiendo even if it isnt hambriento
OBS: in the button method you just need change the value of the slider and call "IrComiendo"
hope it helped
Related
I'm making an upgrade button for a game I'm creating, obviously I want the upgrade to only be available once, how do I code that?
I looked around the internet and each person suggested trying the following:
Bouton.SetActive (false);
or something like that. However, this makes the button inactive during startup. I want it to be inactive or destroyed after just one click. the button in question must be a GameObject
Anyone know how to fix this?
Thank you in advance!
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class One_One_Click_to_Save_the_GameObject_Deactivate_If_scene_replay : MonoBehaviour
{
public GameObject Bouton;
void Start()
{
if (PlayerPrefs.HasKey("buttonState")) // if the button was saved in memory
{
if (PlayerPrefs.GetInt("buttonState") == 0) //if the value is 0
Bouton.SetActive (false); // make button disabled
}
else
{
PlayerPrefs.SetInt("buttonState", 1); // saving in memory that button is on
}
}
public void buttonCliked()
{
Bouton.SetActive(false); // making the button disabled
PlayerPrefs.SetInt("buttonState", 0); //saving in memory that button is off
}
}
First of all you do not have any code piece to reset the state of your button on PlayerPrefs.
You should go to Edit -> Clear All PlayerPrefs on Unity Editor to get rid of the previous saved states.
Then you will check if the player is already used the upgrade button which you did on start but you dont need the else statement because in default it will be already active on the scene so this should be enough
void Start()
{
if (PlayerPrefs.HasKey("buttonState")) // if the button was saved in memory
{
if (PlayerPrefs.GetInt("buttonState") == 0) //if the value is 0
Bouton.SetActive (false); // make button disabled
}
}
Alternatively you can use the PlayerPrefs.GetInt() method with a default parameter so you don't need to check if it has a key, like this
void Start()
{
if (PlayerPrefs.GetInt("buttonState",1) == 0) // This will return 1 if there is no key with the name "buttonState"
Bouton.SetActive (false);
}
And after you clicked the upgrade which you should be doing on your buttonCliked() method. You can set the buttonstate to zero and it will always disable it on start. If you want to reset the state you need to clear the player prefs again like we did it on first step
public void buttonCliked()
{
Bouton.SetActive(false); // making the button disabled
PlayerPrefs.SetInt("buttonState", 0); //saving in memory that button is off
}
But again alternatively you can disable the clicking function on the button rather than just disabling it on the menu. What you should do is create a Button reference rather than a gameobject (or get the button component on the gameobject) and set it's interactable field to false. Which makes the button not clickable but still be seen on the screen.
You can use public Button Bouton; rather than public GameObject Bouton;and instead of using Bouton.SetActive (false); you can now call Bouton.interactable = false;
Forewords
Firstly, I know posting graphical resources for codes is not encouraged in this platform. I will also post the code but, in this particular case, I think posting a video about it is much more helpful than just posting some arbitrary code because the structuring of game projects really vary depending on their requirements. However, I still respect the platform's rules so if a mod asks me to format my question according to the community rules, I can do that or they also can simply delete my question. I respect that.
The Issue
It's actually a simple issue but it's driving me crazy because of its simplicity. I just want to fade in when I load a scene and then fade out whenever I click a button. As to how I do that, this is the video about it.
To sum up, I load another scene called "Fader" which contains a ColorRect with a black color and AnimationPlayer to change ColorRect's alpha value.
The code is below with extra comments on relevant parts:
using Godot;
using System;
public class TitleScreen : Control
{
private Button[] buttons;
private Control fader; // the scene that I inject
public override void _Ready() // when title screen gets ready
{
GD.Print("Preparing TitleScreen...");
InitButtons();
InitFader(); // initialize fader
FadeIn(); // do fade in animation
}
private void InitFader() // initializing fader
{
GD.Print("Initializing fader...");
var faderScene = (PackedScene)ResourceLoader.Load("res://components/Fader.tscn"); // load external fader scene
fader = (Control)faderScene.Instance(); // instantiate the scene
fader.SetSize(OS.WindowSize); // set the size of fader scene to the game window, just in case
var rect = (ColorRect)fader.GetNode("rect"); // get "rect" child from fader scene
rect.SetSize(OS.WindowSize); // set "rect" size to the game window as well, just in case
fader.Visible = false; // set the visibility to false
AddChild(fader); // add initialized fader scene as a child of title screen
}
private void InitButtons()
{
GD.Print("Initializing buttons...");
buttons = new Button[3]{
(Button)GetNode("menu_container/leftmenu_container/menu/start_button"),
(Button)GetNode("menu_container/leftmenu_container/menu/continue_button"),
(Button)GetNode("menu_container/leftmenu_container/menu/exit_button"),
};
GD.Print("Adding events to buttons...");
buttons[0].Connect("pressed", this, "_StartGame");
buttons[2].Connect("pressed", this, "_QuitGame");
}
private void FadeIn()
{
GD.Print("Fading in...");
fader.Visible = true; // set visibility of fader to true
var player = (AnimationPlayer)fader.GetNode("player"); // get animation player
player.Play("FadeIn"); // play FadeIn animation
fader.Visible = false; // set visibility of fader to false
}
private void FadeOut()
{
// similar to FadeIn
GD.Print("Fading out...");
fader.Visible = true;
var player = (AnimationPlayer)fader.GetNode("player");
player.Play("FadeOut");
fader.Visible = false;
}
public void _StartGame() // whenever I click start game button
{
FadeOut(); // fade out
GetTree().ChangeScene("res://stages/Demo01.tscn");
}
public void _QuitGame() // whenever I click quit game button
{
FadeOut(); // fade out
GetTree().Quit();
}
}
Seems like I can't see something. Why does it not fade in and out?
Environment
Manjaro 19.0.2
Mono JIT Compiler 6.4.0 (if it is relevant)
Godot 3.2
So, the issue was Play method on AnimationPlayer object kinda runs like async (dunno if this is the correct term for it).
Luckily, there is a feature called signals in Godot. There are animation_started and animation_finished signals on AnimationPlayer objects. Basically, I created a C# script for Fader scene, hooked the signals from player to fader as in:
animation_started to _FaderAnimationStart
animation_finished to _FaderAnimationEnd
At the end, my script looks like below:
using Godot;
using System;
public class Fader : Control
{
private ColorRect rect;
private AnimationPlayer player;
public override void _Ready()
{
GD.Print("Initializing Fader...");
rect = (ColorRect)GetNode("rect");
player = (AnimationPlayer)GetNode("player");
SetSize(OS.WindowSize);
rect.SetSize(OS.WindowSize);
Visible = false;
}
private void _FaderAnimationStart(String anim_name)
{
Visible = true;
}
private void _FaderAnimationEnd(String anim_name)
{
Visible = false;
}
}
I solved it thanks to njamster's answer and Hans Passant's comment.
However, this only solves half of the problem. Yes, the scene now fades in when it loads but it does not fade out. Given that it executes kinda-async (again, I'm not sure if this is the correct term), changing scene interrupts while running the animation. I will update the answer when I solve that problem as well.
Update
Well, I cannot seem to solve the fade out part because it requires to access parent node from initialized child scene. There are some methods I can think of.
First one is to somehow parameterize "Fader" scene. This can be done in many ways but at the end, when you initialize it from another scene, you need to cast it to Fader and I don't know if this is a valid way to do it. Another concern is standardizing this in the codebase. A similar method is discussed in here.
Second one is to write it as a plugin which has it benefits and drawbacks. C# is not really battle-tested in this particular area.
Third one is to use a state management system. Here is a redux implementation for Godot. And you need to somehow integrate it for signals, which seems like a hassle.
So, overall, I still do not know how to fade out.
I want to play 3 animations on a cube, when a condition is checked the first animation should be played, then when the second condition is checked, the second animation, and the same thing for the last one.
So I've created 3 animations and make them as legacy, then I attached animation component to the cube, and add the animations to it. The problem I have with the script below is that the first animation works fine, but the 2 rest didn't. What should I do to fix that?
Animation CubeRot;
bool Rot = false;
// Use this for initialization
void Start () {
CubeRot = gameObject.GetComponent<Animation>();
}
// Update is called once per frame
void Update () {
if (FindObjectOfType<HoseController>().Rotated1 == true)
{
if (!Rot)
{
CubeRot.Play("Rot1");
Rot = true;
}
}
if (FindObjectOfType<HoseController>().Rotated2 == true)
{
if (!Rot)
{
CubeRot.Play("Rot2");
Rot = true;
}
}
}
Is it because you are setting Rot = true; in the first update. Then the second update requires the Rot variable to be false but its still true from the 1st update?
First:
You require Rot to be false in both if.
Second:
If Rotated1 is true the first part will loop (since you are in the Update) and since you do not set it to false in your if it will continue to loop.
You can even avoid to use both Rotated1 and Rot
Also you should try to avoid the legacy animation component. It should be easy to do the same behaviour in the new Animator component
Finally avoid to use FindObjectOfType each time
I'm trying to divide the screen to 2 and use them as 2 different buttons (L,R) in Unity. But for the ones who are playing first time, I want to show the buttons' text and image to teach them what they do, and after that I want to disable buttons' image and text, they will be still interactable but invisible.
First time screen
How can I ?
Way easier and more reliable than what you did would simply be do add something like this to your Button:
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Button))]
[DisallowMultipleComponent]
public class HintsController : MonoBehaviour
{
private Image _image;
private Text _text;
private float _originalAlpha;
// optional to control if Hints should be enabled on starting the App
public bool enableAtStart;
private void Awake()
{
// GetComponentInChildren finds the component on this object or any
// child of this object recursively
// (true) : make sure you also find the components if this or the child objects
// are currently disabled
// On this way you don't even have to rely on that the Image is on the
// Button GameObject or below in the hierachy
_image = GetComponentInChildren<Image>(true);
_text = GetComponentInChildren<Text>(true);
if(_image)
{
_originalAlpha = _image.color.a;
}
// optional to controll if Hints should be enabled on starting the App
ShowHints(enableAtStart);
}
public void ShowHints(bool isVisible)
{
// Skip if no Image found
if (_image)
{
var color = _image.color;
color.a = isVisible ? _originalAlpha : 0;
_image.color = color;
}
// Skip if no Text found
if (_text)
{
_text.enabled = isVisible;
}
}
}
Then from any other script, you simply can do e.g.
// TODO somehow get the reference either to the button GameObject or the Button component
var button;
button.GetComponent<HintsController>().ShowHints(false);
To disable hints.
I used button.image.enabled = bool valuefor image component and button.transform.GetChild(0).gameObject.SetActive(bool value) for the text.
i would suggest just making an empty for each button in the scene view, name them left and right. drag you button images(sprites) onte the scene and size them to fill the camera with proper positioning. apply boxCollider2D to both of the parent game objects(the empties you created and named left and right.) then you will need some light scripting.
pseudo-wise i would say:
you will use:
void OnMouseDown(){
//button touched
}
to see if the button is pressed.
when your tutorial is over you will set the image and text invisible with the code you mentioned above or something similar. this script goes on each button.
I am creating a 2D platform game with Unity and C#. I searching for how I can do the following.
I have 5 sets of animations and one gameObject(later in game there should be more than one object that can be changed, so maybe we can keep that in mind) with the tag "Campfire0" stored in anim0 inside the start function. What I want is when the user pressed E on his keyboard it will check which is the current animation of the current gameObject and change that animation to "Camfire25" or another number like 50, 75 or 100 and update the gameObject tag. After that is done, it should again check all if statements and the second time you pressed E it should look at the if statement with if(anim25) and so on.
Problem
Below code I use currently. But the problem is the following. I declare inside the start function the animator of each game object tag, but this will be fired one time if the game is started. After pressing the second time on E, the error "NULL" NullReferenceException will occur. So I think I should store the values inside an array? And how should I do that.
For checking which animation is the current one, I use if statements, but can it also be done with a foreach loop or is there a shorter way to do this?
Current Code:
public Animator anim0;
public Animator anim25;
public Animator anim50;
public Animator anim75;
public Animator anim100;
// Use this for initialization
void Start () {
count = 0;
setCountText ();
currentValue = startingValue;
//anim = GameObject.Find("Campfires").GetComponent<Animator>(); // Parent of all Campfires
anim0 = GameObject.FindGameObjectWithTag("Campfire0").GetComponent<Animator>();
anim25 = GameObject.FindGameObjectWithTag("Campfire25").GetComponent<Animator>();
anim50 = GameObject.FindGameObjectWithTag("Campfire50").GetComponent<Animator>();
anim75 = GameObject.FindGameObjectWithTag("Campfire75").GetComponent<Animator>();
anim100 = GameObject.FindGameObjectWithTag("Campfire100").GetComponent<Animator>();
}
void Update () {
if (inTrigger){
if (Input.GetKeyDown(KeyCode.E) && count > 0){
// Update counter on press
updateCount();
if (anim0){
anim0.SetTrigger ("Campfire25");
GameObject.FindGameObjectWithTag("Campfire0").transform.tag = "Campfire25";
}
if (anim25){
Debug.Log ("hello");
anim25.SetTrigger ("Campfire50");
GameObject.FindGameObjectWithTag("Campfire25").transform.tag = "Campfire50";
}
if (anim50){
anim50.SetTrigger ("Campfire75");
transform.tag = "Campfire75";
}
if (anim75){
anim75.SetTrigger ("Campfire100");
transform.tag = "Campfire100";
}
}
}
}
Above script works for the first time, it will change the campfire tag to "Campfire25" and show another animation clip, after pressing E for the second time it will give me an error of NullReferenceException. This is because the Start Function will be fired ones.