Change motion using script in Unity Editor - unity3d

Hi how do we change motion in an AnimatorController using script in Unity Editor?
The red highlight is the one I'd like to change, but using script
My use case is to copy animations from one object, process the animation e.g. adding offset rotation, then add to another object. Since my object has many child objects, I need to create a script to automate this.

Changing the AnimatorController via editor scripts is always quite tricky!
First of all you need to have your AnimationClip from somewhere
// somewhere get the AnimationClip from
var clip = new AnimationClip();
Then you will have to cast the runtimeAnimatorController to an AnimatorController which is only available in the Editor! (put using UnityEditor; at the top of the script!)
var controller = (AnimatorController)animator.runtimeAnimatorController;
Now you can get all its information. In your case you can probably use the default layer (layers[0]) and its stateMachine and according to your image retrieve the defaultState:
var state = controller.layers[0].stateMachine.defaultState;
or find it using Linq FirstOrdefault (put using System.Linq; at the top of the script) like e.g.
var state = controller.layers[0].stateMachine.states.FirstOrDefault(s => s.state.name.Equals("SwimmingAnim")).state;
if (state == null)
{
Debug.LogError("Couldn't get the state!");
return;
}
Finally assign the AnimationClip to this state using SetStateEffectiveMotion
controller.SetStateEffectiveMotion(state, clip);
Note however that even though you can write individual animation curves for an animation clip using SetCurve unfortunately it is not possible to read them properly so it will be very difficult to do what you want
copy animations from one object, process the animation e.g. adding offset rotation, then add to another object.
You will have to go through AnimationUtility.GetCurveBindings which gets quite complex ;)
Good Luck!

Related

Unity: Trying to load and run an fbx animation

I am trying to load an animation from an fbx file and have it play on a GameObject:
TestObject.AddComponent<Animation>();
animation_handler = TestObject.GetComponent<Animation>();
walking_anim = Resources.Load("fbx_anims/walking_anim_test", typeof(AnimationClip)) as AnimationClip;
if(walking_anim == null)
{
Debug.Log("walking anim not found");
}
walking_anim.legacy = true;
animation_handler.AddClip(walking_anim, "walking");
animation_handler.wrapMode = WrapMode.Loop;
In the game loop, I tried using this:
if (Input.GetKeyDown(KeyCode.W))
{
if (!(animation_handler.IsPlaying("walking")))
{
animation_handler.clip = walking_anim;
animation_handler.Play("walking");
}
}
It doesnt give any errors, yet it doesn't work either. Anything I'm missing?
EDIT: For clarification: The model stays in the default T-Pose, after pressing 'W'. After inserting Debug.Logs at different points, I can confirm that the Play function is getting called only once, after which IsPlaying always returns true. Yet the "playing" animation causes no visual changes in the model (yes, the bone names are the same).
You don't want to use the Animation component, it is an old legacy component that has been replaced by the much improved Animator component. There are a lot of good posts on the Internet on how to use it - no need to repeat it here. The important steps are:
Add the Animator (not Animation) component to the model.
Create an "Animator Controller" in your project and add the clips (like the "walking_anim"). Here you can have a lot of different clips and tell Unity how to interpolate between them by using different parameters.
Add the "Animator Controller" to your "Animator" component.
Add an "avatar" of your model (usually created when the model is imported).
By code alter the parameters of the Animator Controller to tell it which animation clips to play.
It may look like a lot of steps, but it is not so hard and you will quickly have a walking, running, jumping creature on your screen. Good luck!

How to control the placement of GameObject in a scene programmatically in Unity

I have created a code file in Unity and assigned it to an empty GameObject I have placed in the scene:
var obj = new GameObject("Sample");
obj.transform.position = new Vector3(0, 0, 0);
var text = obj.AddComponent<TextMesh>();
text.text = "Hello world";
When I run the scene, I can see the text. And that is my problem: I did not specify anywhere in code to add obj to the scene, but it gets placed automatically apparently.
This can be a problem if I want to introduce an object later than instantiation time.
What am I doing wrong? How can this be achieved? What are the patterns/best-practices here?
Immediate fix:
Use obj.SetActive(false) to temporarily disable the object and then use obj.SetActive(true) when you need the object to be active.
Other solutions / best practices:
Create the object you desire in the scene, save it as a prefab (prefabricated object) and then only instantiate it when you need it. Here's a link for further reading into the prefab system. https://docs.unity3d.com/Manual/Prefabs.html
Object pooling is typically used when you will have a bunch of the same objects (like lasers, bullets, etc). Watching this video may be of help: https://www.youtube.com/watch?v=tdSmKaJvCoA

Best practice to to access components using scripts in Unity?

I am new to Unity so go easy on me. :)
I added an game object with a text field component (via TextMeshProUGUI) to my heads up display in my scene. I want to use this to display various statistics on it for debugging purposes during game play.
I then created a script which I added as a component to the same game object that holds my text component. Is this the best practice? Not sure how else I would get the script to execute.
Once I had my script created, I needed to find the text component as well as some other components in my scene so I could display the debug information. Below you can see how I did it... it feels a little dirty to be searching the entire scene to find these things. Would love some insight on how long-time Unity programmers go about this!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PlayerDebugStatistics:MonoBehaviour {
TextMeshProUGUI playerDebugStatisticsText;
PlayerCharacterController playerCharacterController;
Health playerHealth;
private void Start() {
// First find the object in the scene named "PlayerDebugStatisticsText" and then get it's TextMeshProUGUI component
this.playerDebugStatisticsText = GameObject.Find("PlayerDebugStatisticsText").GetComponent<TextMeshProUGUI>();
// Get the player character controller
this.playerCharacterController = GameObject.FindObjectOfType<PlayerCharacterController>();
// Get the player health from the player character controller
this.playerHealth = playerCharacterController.GetComponent<Health>();
}
void Update() {
// Update the text every frame
this.playerDebugStatisticsText.SetText(string.Format("{0:N2}", this.playerHealth.currentHealth));
}
}
3 ways
Create an inspector reference to the other object and access it from your script - a public or private (with [SerializeField]) attribute in your script - and drag the component in like in this video: https://youtu.be/dMgQOP7kdxg?t=425
Use the singleton pattern: https://www.youtube.com/watch?v=5p2JlI7PV1w
Use dependency injection - https://github.com/modesttree/Zenject
Your way isn't terrible if you don't do it in the Update() loop, and cache your objects (like it appears you are doing), but if you need that performance in Start(), the above options can help.

Can I programatically load scenes in the Unity editor?

I'm using the A* pathfinding algorithm for my 2D game (from my understanding, Unity Nav Meshes don't work in 2D). I would like to be able to pre-calculate navigation grids for all of my scenes, and save them in resource files that can be loaded whenever the player enters a new scene. Rather than having to remember to click "calculate" for every scene -- and remember to recalculate all of my scenes if I make a change to my navigation grids -- I want to be able to programatically have the Unity Editor iterate though each scene and calculate the grids.
Is there a way to create a command in the Unity editor that will iteratively open each scene in the editor and run a method on a MonoBehaviour that's in the scene? Alternatively, is there another way to accomplish what I'm trying to do?
Yes you can!
In editmode you can't use SceneManager but have to use the EditorSceneManager.
First of all you need the scenes you want to iterate.
Could be e.g. a public static field with a list of SceneAsset in the Inspector where you simply reference the scenes
public static List<SceneAsset> Scenes = new List<SceneAsset>();
or you could get them by script e.g. for only use the scenes added to the build settings using EditorBuildSettings.scenes
List<EditorBuildSettingsScene> Scenes = EditorBuildSettings.scenes;
For both you can get a list of the scene paths e.g. using LinQ Select (this is basically a kind of shortcut for a foreach loop) and AssetDatabase.GetAssetPath like
List<string> scenePaths = Scenes.Select(scene => AssetDatabase.GetAssetPath(scene)).ToList();
for the EditorBuildSettingsScene from EditorBuildSettings.scenes you can also simply use
List<string> scenePaths = Scenes.Select(scene => scene.path).ToList();
Now you can iterate over them all and do your stuff by using EditorSceneManager.OpenScene, EditorSceneManager.SaveScene and EditorSceneManager.CloseScene (and if you need it AssetDatabase.SaveAssets)
foreach(string scenePath in scenePaths)
{
// Open a scene e.g. in single mode
var currentScene = EditorSceneManager.OpenScene(scenePath);
/* Do your calculation for currentScene */
// Don't know if it makes changes to your scenes .. if not you can probably skip this
EditorSceneManager.SaveScene(currentScene);
// Finally Close and remove the scene
EditorSceneManager.CloseScene(currentScene, true);
}
// you might be doing changes to an asset you want to save when done
AssetDatabase.SaveAssets();
Before starting you should probably ask to save the current open scene(s) using EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo
if(EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
// Saved => the foreach loop here
}
else
{
// aborted => do nothing
}
Than in order to finally start that method the simplest would be to add a [MenuItem]
public static class Calculation
{
[MenuItem("YourMenu/RunCalculation")]
public static void RunCalculation()
{
// all the before mentioned snippets here
// depending how exactly you want to do it
}
}
This will add a new menu YourMenu with one entry RunCalculation to the top menubar of the Unity Editor.
Note:
Since this uses a lot of types (EditorSceneManager etc) that only exist in the UnityEditor namespace you should either place the whole script in an Editor folder (so it is ignored in the final build) or use pre-processors like
#if UNITY_EDITOR
// ... code here
#endif
so in the build the code is also ignored.
Note that I'm assuming so far you also did your calculation in editmode. The thing is if this calculation relies somewhere aon any Start or Awake method you have to call it manually from that editorscript before running the calculation.

How to detect position of Vive base stations?

So I'm completely new to Unity and VR but for a project I need to detect the positions of the base stations.
I tried googling, but since I don't know all the lingo I don't really know where and what to look for.
All I can find is how to detect the controllers.
Here's one way, all with Unity code:
var nodeStates = new List<XRNodeState>();
InputTracking.GetNodeStates(nodeStates);
foreach (var trackedNode in nodeStates.Where(n => n.nodeType == XRNode.TrackingReference))
{
bool hasPos = trackedNode.TryGetPosition(out var position);
bool hasRot = trackedNode.TryGetRotation(out var rotation);
}
In OpenVR, base stations are "tracked devices", just like the controllers and HMD. The standard SteamVR plugin for Unity already has a way to get the position of any tracked device, see for example how the controllers are implemented in the standard [CameraRig] prefab.
The only problem is that you need to provide the "index" of the device, which may change every time you reconnect your headset. SteamVR plugin handles this with the SteamVR_ControllerManager component, but as the name suggests - it handles only controllers. You should be able to implement something similar, or just edit the script and find the lines
if (deviceClass == ETrackedDeviceClass.Controller ||
deviceClass == ETrackedDeviceClass.GenericTracker)
and add ETrackedDeviceClass.TrackingReference to this list. You should then be able to copy the controller objects and attach them in the "additional objects" array in SteamVR_ControllerManager to have the base stations appear in your scene.