AnimationPlayer animations do not play in Godot - c#

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.

Related

how to check if a virtual camera has fully transitioned or not in C#?

I'm working on a C#/Unity project, and I want to know when the cinemachine virtual camera has fully transitioned to another cinemachine camera. I've seen people using IsBlending, but I'm still not sure how to implement that code in my project.
My code is like..
private CinemachineBlend activeBlend = null;
public bool IsBlending { get { return activeBlend != null; } }
[SerializeField] private CinemachineVirtualCamera[] virtualCameras;
void TestItIsWorking()
{
virtualCameras[0].Priority = 10;
virtualCameras[1].Priority = 0; // -> this starts the blending the virtual camera
Debug.Log("this function is called"); // -> prints "this function is called"
Debug.Log(IsBlending); // -> prints false
}
So I guess I'm doing something wrong because event the virtual camera has started to change, I get a false value as of IsBlending boolean. I also wrote while loop instead of just Debug logging the IsBlending value, but the result was still the same. I want to run another function right after the blending has finished, so if there is a better solution for this, please also let me know....
Are you sure your activeBlend field getting a reference correctly? Using the IsBlending property of your CinemachineBrain is an option by the way.

Create animations programmatically in Unity?

In my game I have a big catalog of gear: Armors, weapons and shields. The combinations between these can be really immense.
Besides that, the player has the option of switching in-game to a different set of armor-weapon combination. In the end to solve this, I have used the following object structure.
Whenever I switch the weapons, I activate/deactivate the necessary GameObjects. The animations are set in this way:
Now, the problem is creating the animation. I first considered pre-rendering programatically all the combinations, but my catalog is so huge, that it would create 100s, if not 1000s of animations. So I opted for a different solution. Create in playtime the animation, once I knew what gear would the player select. For that, I created a script to take care of that. The problem is that I have been using APIs from UnityEditor, and now I have realized the build will not work. Specifically because of 2 different classes: EditorCurveBinding and ObjectReferenceKeyframe.
This is a couple snippets of how I was using this classes when creating the animations:
static EditorCurveBinding GetEditorCurveBinding(string path = "")
{
EditorCurveBinding spriteBinding = new EditorCurveBinding();
spriteBinding.type = typeof(SpriteRenderer);
spriteBinding.path = path;
spriteBinding.propertyName = "m_Sprite";
return spriteBinding;
}
static ObjectReferenceKeyframe GetKeyframe(float time, Sprite sprite)
{
ObjectReferenceKeyframe keyframe = new ObjectReferenceKeyframe();
keyframe.time = time / FRAMERATE;
keyframe.value = sprite;
return keyframe;
}
Now, the problem with the Curve, I think I managed to solve, replacing it with this code, replacing EditorCurveBinding with AnimationCurve:
AnimationClip clip = ...
AnimationCurve curve = new AnimationCurve();
clip.SetCurve(path, typeof(SpriteRenderer), "m_Sprite", curve);
But I have no idea how to set the sprites for each animation. I thought that using curve.AddKeycould be helpful, but I have seen no way to add a sprite there.
How could I rewrite that code to avoid using UnityEditor?
Full code
Personally, I would completely avoid built-in Animator and Animations, since this tool is for the very narrow purpose of animating a single object in a predefined way (eg: for a cutscene).
Offtopic - performance
Besides that, the player has the option of switching in-game to a different set of armor-weapon combination. In the end to solve this, I have used the following object structure.
As you probably know this is very inefficient memory-wise and will decrease performance (disabled objects still have marginal CPU overhead). And will incread load time and instantiation time of new objects.
Can I use Animator or Animation?
Because Animator has no API to access it's internals and animation type and overall you cannot do pretty mutch nothing with it except from telling it to Play or Stop.
Since I understand that your animation is "Sprite based" and not "Transform based" any sane idea is just inefficient!
Best solution that I vow against is as follows:
Create "trigger points" that you would "animate"
Based on trigger point - change sprite
public class AnimationController : MonoBehaviour
{
public int AnimationIndex;
public Sprite[] AnimationSprites;
public SpriteRenderer SpriteRenderer;
private void Update()
{
SpriteRenderer.sprite = AnimationSprites[AnimationIndex];
}
}
Better solution?
Since we already need to have custom structure that manages our sprites we might want to optimize the whole thing, since we are not using almost any of the features of Animator we could write custom controller that would replace Animator in this case. This should improve performance significantly since Animator is very heavy!
// MonoBehaviour is optional here
public class SpriteRendererAnimationHandler
{
// More fields that would control the animation timing
public Sprite[] AnimationSprites;
public SpriteRenderer SpriteRenderer;
public void OnAnimationUpdate(int index)
{
var resolvedIndex = ResolveIndex(index);
SpriteRenderer.sprite = AnimationSprites[resolvedIndex];
}
private int ResolveIndex(int index)
{
// Resolve animation index to sprite array index
}
}
// One controller per character - or global per game that synchronize all animations to locked FPS 12.
public class AnimationController : MonoBehaviour
{
private List<SpriteRendererAnimationHandler> Handlers = new List<SpriteRendererAnimationHandler>();
public void FixedUpdate()
{
foreach (var handler in Handlers)
{
// Calculate animation index
int calculatedAnimationIndex = ...;
handler.OnAnimationUpdate(calculatedAnimationIndex);
}
}
}
Add a public field named animation index to Tomasz last example, then create the animations in animator as youd normal do for animation pieces, then animate that animation index field. simple if(currentAnimationyion != lastAnimationIndex) to check if need to send to handlers and ya rocking

How can I enable/disable button components via script?

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.

Controlling Light components C#

I have a game mechanic where the player can toggle the car headlights with the L keypress and the backlights witht he S key, which also controls backwards movement.
This is shown in the code below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class headLights : MonoBehaviour {
private Light frontLight;
private Light backLight;
// Use this for initialization
void Start () {
frontLight = GetComponent<Light>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.L))
{
frontLight.enabled = !frontLight.enabled;
}
if (Input.GetKeyDown(KeyCode.S))
{
backLight = GetComponent<Light>();
backLight.enabled = !backLight.enabled;
}
}
}
The problem is when I press L or S, both the front and back lights turn on because which I assume, the GetComponent refers to all extra Light components in the Scene and generalizes them as one.
I want to get the S key to only turn on the "backLights" while it is pressed and the L key to only toggle the "frontLights".
METHODS I HAVE USED TO TRY FIX THE PROBLEM
frontLight = GameObject.Find("Player").GetComponent<Light>();
This code just gives me errors like "the gameobject player does not have any light components attached to it(although it clearly does) blah blah blah.
I have also tried using tags but they confuse me a lot and seem like the easy way out. I know in the future I will have to learn how to do object orientated syntax and coding so I would very much like to learn how to reference it!
Please help me if you can, it would make my day~~ :)
Please note, you do not have to solve the whole problem for me if you are short on time, just giving general syntax that I can just swap out would help me a great deal!
Its really easy to disambiguate the two lights. Just make the variables public and set them in the inspector. Be sure to null check them before you use them to make sure they are set.
I just realized that probably didn't make sense.
Lets say your hierarchy is set up like this with the light objects as children to the car:
car
+-FrontLight
+-RearLight
Instead of putting the custom behavior on the light gameobjects, you should put it on the car.
Then, the behavior would look like this:
public class headLights : MonoBehaviour {
public Light frontLight;
public Light backLight;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.L))
{
if(frontLight != null) {
frontLight.enabled = !frontLight.enabled;
}
}
if (Input.GetKeyDown(KeyCode.S))
{
if(backLight != null) {
backLight.enabled = !backLight.enabled;
}
}
}
This is because the lights are not Components of the car, they are Children of it.

Unity - How to change what GameObjects are visible with button click events?

I am newbie in Unity I am working on Augmented reality project where I am showing 3D Objects based on the marker that is working fine. But what I would like is to show different objects based on click events
Here is the scenario I did so far:
I created 4 different scenes with 3D objects with markers
Made one canvas frame where I put 3 buttons on it
In each button click I am loading next scene, below is the code that I wrote. The problem is it is showing black screen instead of loading new objects. I don't want to load a camera again. Can anyone help me with this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; // <<<<<< ADD THIS.
public class Button_1 : MonoBehaviour
{
public void Button_Click()
{
Debug.Log("Hello, World!");
// Application.LoadLevel("Multi Target");
SceneManager.LoadScene("MultiTarget_1"); // <<<< Then Do this.
}
public void Button_Click2()
{
Debug.Log("Hello, World!");
// Application.LoadScene("Multi Target_1");
SceneManager.LoadScene("NameOfScene"); // <<<< Then Do this.
}
public void Button_Click3()
{
Debug.Log("Hello, World!");
Application.LoadLevel("Multi Target_2");
}
public void Button_String(string msg)
{
Debug.Log("Hello, All!");
Application.LoadLevel("Multi Target_1");
}
}
image
If you have, let's say, two groups of Objects in a Scene, for example:
GoupA = Cars
GroupB = Pedestrians
And you want that by default Cars are visible and Pedestrians are not, so as soon as you click a button Cars become invisible in your scene and Pedestrians become visible, one thing you can do is to assign different layers for each group of objects and then change the culling mask of your camera programatically when one of the buttons is pressed.
Step 1:
You need to create two or more layers in your scene, and assign those layers to your GameObjects in your scene, depending on what objects you want to make visible at the same time after button click. You will need to read about this here:
https://docs.unity3d.com/Manual/Layers.html
Step 2:
Create in your code a reference to the active Camera in your scene:
Camera cam;
Step 3:
Change your logic inside Button_Click methods
public void Button_Click1()
{
// Only render objects in the first layer (Default layer)
cam.cullingMask = 1 << 0;
}
...
These are other things you can do with the culling mask (depending on how you want to display your GameObjects):
// Render everything *except* layer 3
camera.cullingMask = ~(1 << 3);
// Switch off layer 3, leave others as-is
camera.cullingMask = ~(1 << 3);
// Switch on layer 3, leave others as-is
camera.cullingMask |= (1 << 3);
You can read more about it here:
https://docs.unity3d.com/ScriptReference/Camera-cullingMask.html
Hi i think you can fix your project by just grouping and hiding the GameObject. its much more simple that you think... i already write a sample code... and i already add a link to do just that on how to group the object if you need help on that... i am an Augmented Reality Developer as well so i understand the problem that you are having.... hope this will help..
URL: https://www.youtube.com/watch?v=MP62CcK-qTc
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; // <<<<<< ADD THIS.
public class Button_1 : MonoBehaviour
{
public GameObject MultiTarget_1, NameOfScene,Multi_Target_2,Multi_Target_1;
public void Button_Click()
{
Debug.Log("Hello, World!");
HideAll();
MultiTarget_1.SetActive(true);
}
public void Button_Click2()
{
Debug.Log("Hello, World!");
HideAll();
NameOfScene.SetActive(true);
}
public void Button_Click3()
{
Debug.Log("Hello, World!");
HideAll();
Multi_Target_2.SetActive(true);
}
public void Button_String(string msg)
{
Debug.Log("Hello, All!");
HideAll();
Multi_Target_1.SetActive(true);
}
//call this to hide all
public void HideAll(){
MultiTarget_1.SetActive(false);
NameOfScene.SetActive(false);
Multi_Target_2.SetActive(false);
Multi_Target_1.SetActive(false);
}
}
There are two things. First check if the correct scene name is given and that it is loading.
If you want to persist the Camera Object over different scenes, then make the object DontDestroyOnLoad() using a script attached to the Camera.
Read more here:
https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
You dont need different scenes, just 1 scene which has AR sessions and AR session origin.
create a new blank gameobject and reset its transform ( Scale , rotation , position) so that everything is at 0, except "scale" which will be at 1,
select all the gameobject you want during "BUTTON 1" click event , make them the child of the new blank gameobject which you created.
Repeat the same for the all the gameobjects which you want to change during button click.
After you have created each game object for each button, make them PREFABS. ( just drag the game object from The Hierarchy window to project wind where you can see all your files )
Create a script and create public or serialised array of Gameobect variable , and save the script.
public Gameobject obj1[];
If you are not aware of using arrays, you can just create multiple(number of buttons / Parent Gameobjects you have) gameobject variables
Drop the script on a gameobject , on the script you will see that you can drag drop the game objects with the same name which you have given during creation of variables.
In the UI Script ( on button click event) , you just have to enable and disable the prefabs ( VARIABLES WHICH YOU CREATED) accordingly,

Categories