Animator not animating a runtime instantiated child (UNITY) - unity3d

I have a GameObject representing a room. As childs of this room I have 3 characters. Room has an animator component that control the synchronized animation of all characters.
The problem is I have another character that is instantiate from a prefab at runtime and this doesn't animate.
I tried several things:
//NOT WORKING
IEnumerator Start()
{
room.SetBool("e8", true);
yield return new WaitForSeconds(6);
...
}
//NOT WORKING
IEnumerator Start()
{
room.Rebind();
room.SetBool("e8", true);
yield return new WaitForSeconds(6);
...
}
//WORKING
IEnumerator Start()
{
yield return new WaitForEndOfFrame();
room.Rebind();
room.SetBool("e8", true);
yield return new WaitForSeconds(6);
...
}
The last try is working but the character looks stopped for an instant and suddenly jumps to the animation. I want the character animating from the beginning like the other characters.
My animator:

Simple solving
With current animator settings, this behavior is normal. You need to change your current animator using Any State section because when you use changing state from other states, Animation needs time to change your states. And for start Animator needs time for initializing, but if you will use Any State this will work.
Try doing something like this:
Entry different states
Either you can use what official documentation offers to you:
You can then add additional transitions from the Entry node to other states, to control whether the state machine should begin in a different state.
https://docs.unity3d.com/2019.4/Documentation/Manual/StateMachineTransitions.html
For example:

Related

How to wait until animation is finished when using Animator?

This is a beginner question that should be available from the first google result but I can't find it.
I have this method that plays an animation using an Animator:
IEnumerator popDownAnimation(string c)
{
animator = GetComponent<Animator>();
animator.Play("Pop Down Animation");
yield return new WaitUntil(() => animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1 && !animator.IsInTransition(0));
animator.Play("New State");
}
Can't remember where I got it from, but yield return new WaitUntil(() => animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1 && !animator.IsInTransition(0)); isn't working. It's instantly jumping from "Pop Down Animation" to "New State", so the "Pop Down Animation" doesn't have a chance to play.
Try this
IEnumerator popDownAnimation(string c)
{
animator = GetComponent<Animator>();
animator.Play("Pop Down Animation");
float animationLength = animator.GetCurrentAnimatorStateInfo(0).length;
yield return new WaitForSecondsRealtime(animationLength);
animator.Play("New State");
}
However, like #NotVeryGoodAtThis mentioned, I wouldn't do this in code unless you have a specific reason why you can't make it work using animation events or using parameters within the animator to transition between animation states. Designing your animation using Unity's Animator window and using states, parameters, and transitions will be easier to manage than doing it through code, especially as your animators become more complicated.
I would suggest using Animation events to do whatever you need. Just remember to make a public method and add the animation event at the end of your animation.

Unity: How to switch scenes and when returning to the original scene, return player to the place they switched their scene

I have two scenes: Main Street & Building Scene
When the player is Main Street, if the player's trigger box touches the building and the player presses "q", the scene would switch to the Building Scene.
I want it so that when the player exits the Building Scene and returns to the Main Street Scene, the player is back to the position they entered the Building Scene that they entered from. Apologies in advance if this doesn't make sense.
sceneSwitchingScript:
public int buildingToLoad;
public Text InputText;
public movement player;
public Vector3 playerPrevPos;
void OnTriggerEnter2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("[Q] to enter");
if(Input.GetKeyDown("q")){
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
//Debug.Log(gameObject.tag);
Application.LoadLevel(buildingToLoad);
}
}
}
void OnTriggerStay2D(Collider2D col){
if(col.CompareTag("Player")){
if(Input.GetKeyDown("q")){
//spawn = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z)
Application.LoadLevel(buildingToLoad);
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
}
}
}
void OnTriggerExit2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("");
}
}
Setting the player's position when they exit the building
public switchScene ss;
void OnTriggerStay2D(Collider2D col){
if(Input.GetKeyDown("q")){
if(col.gameObject.CompareTag("ExitPoint")){
transform.position = ss.playerPrevPos;
}
}
}
However, these two scrips do not work and I'm not sure if this is related but when I make the player do the switch scene thing in-game, this error pops up:
NullReferenceException: Object reference not set to an instance of an object
movement.OnTriggerStay2D (UnityEngine.Collider2D col)
This error message mentions the error on this line:
transform.position = ss.playerPrevPos;
With Unity, the traditional loading of a scene was fairly destructive. There was the concept of setting a GameObject as safe by setting it as DontDestroyOnLoad, which, in a way, removed the object from the scene altogether, which protected it from scene loads.
Unity finally properly implemented multiscene editing and loading. It's essentially DDOL, but done properly.
Now you can actually have multiple scenes loaded at the same time. Now, what that allows you to do is to have a "Manager" scene that handles all of the objects that are common between scenes, and only load (and unload) the specific objects required for that individual scene.
You'd use it like this:
SceneManager.LoadScene("YourScene", LoadSceneMode.Additive);
That would load "YourScene" in ADDITION to the currently loaded scene. Likewise, removing a scene is:
SceneManager.UnloadScene("YourScene");
Now, if you have a Manager scene, you can include in your manager scene a script that holds data for each individual scene. As a hacky example, you might have:
public Vector3 InsideSceneLastPosition { get; set; }
Which you then assign to before loading your outside scene. When you load your inside scene again, you can read InsideSceneLastPosition again to reposition your character.
Here's the link to the LoadSceneAsync page at Unity.
There's more to it than that, for instance, you have to listen for the SceneManager.sceneLoaded event to know when you've actually loaded the next scene, so that you can reposition your GameObejcts. You can find information about that here.
You can see multiscene editing my simply dragging multiple scenes from the Project window into you Hierarchy window. All the scenes listed will additively work together. But you'll have to be careful though, as there's another "gotcha" in that you cant reference objects from one scene to another. You can call scripts cross scene, but you won't be able to drag a game object from one scene, into the object field of a component on a different scene. Don't worry, you'll get the hang on it =)

Unity3D How can I select multiple objects in 3D with a drag and select / lasso select?

I am struggling to find a good tutorial or informations that would allow me to select multiple objects in 3D in a user friendly manner.
So far, the best tutorial I found is this one : https://sharpcoderblog.com/blog/unity-3d-rts-style-unit-selection. The tutorial works by using the transform.position of the selectable objects and checking if it within the user's selection.
What I wish is to have the user be able to select a unit even if it is only partially within the user's selection such as most RTS games do ( both in 2D and 3D ).
One possibility would be to create a temporary mesh using the camera's clipping distances and the user's selection and then check for collisions but I was not able to find any tutorials using this method nor do I know if it is the best approach to the subject.
If I understand correctly you want to
somehow start a selection
collect every object that was "hit" during the collection
somehow end the collection
Couldn't you simply use raycasting? I will assume simple mouse input for now but you could basically port this to whatever input you have.
// Just a little helper class for an event in the Inspector you can add listeners to
[SerializeField]
public class SelectionEvent : UnityEvent<HashSet<GameObject>> { }
public class SelectionController : MonoBehaviour
{
// Adjust via the Inspector and select layers that shall be selectable
[SerializeField] private LayerMask includeLayers;
// Add your custom callbacks here either via code or the Inspector
public SelectionEvent OnSelectionChanged;
// Collects the current selection
private HashSet<GameObject> selection = new HashSet<GameObject>();
// Stores the current Coroutine controlling he selection process
private Coroutine selectionRoutine;
// If possible already reference via the Inspector
[SerializeField] private Camera _mainCamera;
// Otherwise get it once on runtime
private void Awake ()
{
if(!_mainCamera) _mainCamera = Camera.main;
}
// Depending on how exactly you want to start and stop the selection
private void Update()
{
if(Input.GetMouseButtonDown(0))
{
StartSelection();
}
if(Input.GetMouseButtonUp(0))
{
EndSelection();
}
}
public void StartSelection()
{
// if there is already a selection running you don't wanr to start another one
if(selectionRoutine != null) return;
selectionRoutine = StartCoroutine (SelectionRoutine ());
}
public void EndSelection()
{
// If there is no selection running then you can't end one
if(selectionRoutine == null) return;
StopCoroutine (selectionRoutine);
selectionRoutine = null;
// Inform all listeners about the new selection
OnSelectionChanged.Invoke(new HashSet<GameObject>(selection);
}
private IEnumerator SelectionRoutine()
{
// Start with an empty selection
selection.Clear();
// This is ok in a Coroutine as long as you yield somewhere within it
while(true)
{
// Get the ray shooting forward from the camera at the mouse position
// for other inputs simply replace this according to your needs
var ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
// Check if you hit any object
if(Physics.Raycast(ray, out var hit, layerMask = includeLayers ))
{
// If so Add it once to your selection
if(!selection.Contains(hit.gameObject)) selection.Add(hit.gameObject);
}
// IMPORTANT: Tells Unity to "pause" here, render this frame
// and continue from here in the next frame
// (without this your app would freeze in an endless loop!)
yield return null;
}
}
}
Ofcourse you could do it directly in Update in this example but I wanted to provide it in a way where you can easily exchange the input method according to your needs ;)
From UX side you additionally might want to call a second event like OnSelectionPreviewUpdate or something like this every time you add a new object to the selection in order to be able to e.g. visualize the selection outcome.
I might have understood this wrong and it sounds like you rather wanted to get everything inside of a drawn shape.
This is slightly more complex but here would be my idea for that:
Have a dummy selection Rigidbody object that by default is disabled and does nothing
don't even have a renderer on it but a mesh filter and mesh collider
while you "draw" create a mesh based on the input
then use Rigidbody.SweepTestAll in order to check if you hit anything with it
Typed on smartphone but I hope the idea gets clear
I think I would try to create a PolygonCollider2D because it is quite simple comparing to creating a mesh. You can set its path (outline) by giving it 2D points like location of your pointer/mouse. Use the SetPath method for it. You can then use one of its methods to check if another point in space overlaps with that collider shape.
While the PolygonCollider2D interacts with 2D components you can still use its Collider2D.OverlapPoint method to check positions/bounds of your 3D objects after translating it to 2D space.
You can also use its CreateMesh method to create a mesh for drawing your selection area on the screen.
You can read more about the PolygonCollider2D here.
Hope it makes sens and hope it helps.

Unity Mecanim. How to reset animation before disabling a gameobject

Unity 2019.3.7f1
..
The artist I'm working with has made some animations for some UI stuff using the mecanim system Animator component.
I fire the animation with
Animator.SetTrigger("ParamaterName")
The gameobjects with the animator components, which are being animated, can get disabled by some of my scripts.
This can happen while the animation is playing..
So if the animation starts playing, and lets say the animation has made a button get bigger, then the gameobject gets disabled. When the gameobject is re-enabled (the animation is no longer playing) it is still big..
Is there a way to tell the Animator to go back to normal?
I have tried stuff like this in onEnable and OnDisable in a script on the GameObject
Animator.keepAnimatorControllerStateOnDisable = false
Animator.Play("normalState",0,0f);
Animator.playbackTime = 0f;
Animator.Update(0f);
This mecannim thing just seems like a black box as to how it works. I'm not familiar with it as I've always just used my own library to animate stuff that I've been using for donkeys years.
EDIT:
This was the solution...
private void Start()
{
Animator.keepAnimatorControllerStateOnDisable = true;
}
private void OnDisable()
{
Animator.Play("normalState",0,0f);
}
Make sure your keepAnimatorControllerStateOnDisable isn't set to true. Instead, you should set to false to clear the current state of the Animator controller
Animator.keepAnimatorControllerStateOnDisable = false
This way whenever you disable an object with an Animator, the Animator states and all its parameters go back to default.
Update
From your comments was able to understand the default is to have the button bigger. So, it's actually the other way around (set it to true, instead of false).
As you mention, write the following code
private void Start()
{
Animator.keepAnimatorControllerStateOnDisable = true;
}
private void OnDisable()
{
Animator.Play("normalState",0,0f);
}

Randomized Attack Animation RPG

First of all, I'm quite noob with animator and animation systems in unity.
What I'm trying to achieve (and I was trying with Animator component) is a randomized attack only while I keep my mouse button pressed on the enemy and which completes the execution of the attack clip that is playing even if i release the button meanwhile.
I tried adding my 2 attack animations to a list and play it, with something like
anim.Play(Random.Range(0, list.Count))
...but I don'tknow if the problem is that while I keep pressed one animation cancels the other or what.
Therefore I prefer to ask, because I'm probably doing things in the wrong way.
Thank you.
Yes the issue is probably what you said: You have to wait until one Animation finished before starting a new one otherwise you would start a new animation every frame.
You could use a Coroutine (also check the API) to do that.
Ofcourse the same thing could be implemented also only in Update without using a Coroutine but most of the times that becomes really cluttered and sometimes even more complicated to handle. And there is not really any loss or gain (regarding performance) in simply "exporting" it to a Coroutine.
// Reference those in the Inspector or get them elsewhere
public List<AnimationClip> Clips;
public AnimationClip Idle;
private Animator _anim;
// A flag to make sure you can never start the Coroutine multiple times
private bool _isAnimating;
private void Awake()
{
_anim = GetComponent<Animator>();
}
private void Update()
{
if(Input.GetMouseButtonDown(0)
{
// To make sure there is only one routine running
if(!_isAnimating)
{
StartCoroutine(RandomAnimations());
}
}
// This would immediately interrupt the animations when mouse is not pressed anymore
// uncomment if you prefer this otherwise the Coroutine waits for the last animation to finish
// and returns to Idle state afterwards
//else if(Input.GetMouseButtonUp(0))
//{
// // Interrupts the coroutine
// StopCoroutine (RandomAnimations());
//
// // and returns to Idle state
// _anim.Play(Idle.name);
//
// // Reset flag
// _isAnimating = false;
//}
}
private IEnumerator RandomAnimations()
{
// Set flag to prevent another start of this routine
_isAnimating = true;
// Go on picking clips while mouse stays pressed
while(Input.GetMouseButton(0))
{
// Pick random clip from list
var randClip = Clips[Random.Range(0, Clips.Count)];
// Switch to the random clip
_anim.Play(randClip.name);
// Wait until clip finished before picking next one
yield return new WaitForSeconds(randClip.length);
}
// Even if MouseButton not pressed anymore waits until the last animation finished
// then returns to the Idle state
// If you rather want to interrupt immediately if the button is released
// skip this and uncomment the else part in Update
_anim.Play(Idle.name);
// Reset flag
_isAnimating = false;
}
Note that this way of randomizing does not provide things like "Don't play the same animation twice in a row" or "Play all animations before repeating one".
If you want this checkout this answer to a very similar question. There I used a randomized list to run through so there are no doubles