Goal
I would like to rotate a head of a humanoid character given x, y, z rotation values using a script (without using any other object in the scene as "look" direction).
What I want to do
When setting-up the rigging of a Humanoid character (humanoid prefab --> Rig --> Animation Type: Humanoid --> Configure --> Muscles & Settings), you see the following interface: https://docs.unity3d.com/Manual/MuscleDefinitions.html
In this menu, under Head, you can drag a slider to move e.g. your humanoid head up and down. I want to achieve the same with a script, but I don't know how to do this.
Resources
This question never received a proper answer with example code: https://answers.unity.com/questions/824628/can-we-using-script-to-moving-the-muscles-in-unity.html
I presume I have to do something with HumanPose.muscles (https://docs.unity3d.com/ScriptReference/HumanPose-muscles.html), however with the lack of code samples, I have no idea how to approach this.
Edit 3: This link has a code sample for HumanPose, but I didn't get it to work yet: https://forum.unity.com/threads/humanposehandler.430354/
Question
How to get the head muscles of a humanoid character and rotate them by giving values through a script? (Or any other way how to rotate the head using head rotation values, without another object in the scene). Any help would be appreciated.
Edit 1 & 2: Sample code
I receive a JSON formatted message from which I extract the radian values and change it into degrees:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json.Linq; // JSON reader; https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347
public class HeadRotator : MonoBehaviour {
Quaternion rotation = Quaternion.identity;
// Radians to degree
float Rad2Degree = 180 / Mathf.PI;
// Human muscle stuff
HumanPoseHandler humanPoseHandler;
HumanPose humanPose;
Animator anim;
//Transform head;
// Use this for initialization
void Start () {
// get attached Animator controller
anim = GetComponent<Animator>();
//head = anim.GetBoneTransform(HumanBodyBones.Head);
//Debug.Log (head);
}
// Use JSON message to set head rotation and facial expressions;
// IEnumerator to run in main thread (needed for SetBlendShapeWeight)
public IEnumerator RequestHeadRotation(JObject head_pose)
{
// rotate head of character with received x, y, z rotations in radian
List<float> head_rotation = new List<float> ();
foreach (KeyValuePair<string, JToken> pair in head_pose) {
//Debug.Log(pair);
// store head rotation in degrees
head_rotation.Add(float.Parse(pair.Value.ToString())*Rad2Degree*10);
}
Debug.Log ("" + head_rotation[0] + ", " + head_rotation[1] + ", " + head_rotation[2]);
//Quaternion rotation = Quaternion.Euler(new Vector3(head_rotation[0], head_rotation[1], head_rotation[2]));
//Debug.Log ("" + rotation[0] + ", " + rotation[1] + ", " + rotation[2] + ", " + rotation[3]);
// head.Rotate (rotation);
// https://docs.unity3d.com/ScriptReference/Quaternion-eulerAngles.html
rotation.eulerAngles = new Vector3 (head_rotation[0], head_rotation[1], head_rotation[2]);
// Animator.GetBoneTransform()
anim.SetBoneLocalRotation(HumanBodyBones.Head, rotation);
yield return null;
}
}
Head rotation data: 3.208564, 0.4583662, 0.1145916
Quaternion data: 0.0280001, 0.003970424, 0.0008876149, 0.9995997
I don't really know well how to set either the head bone or the muscles. Most examples provide only a snippet of the code, so I'm struggling in figuring out how it works.
Edit 2: I feel I'm getting close, but anim.SetBoneLocalRotation(HumanBodyBones.Head, rotation); seems to get ignored.
Edit 4: I put on Github a simple version of my head rotation attempt: https://github.com/NumesSanguis/Basic-Unity-Head-Rotation
After testing for a while, I figured it out.
Firstly, set the animator to IK Pass.
Then use SetBoneLocalRotation inside OnAnimatorIK method. You can know more about this method here: https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnAnimatorIK.html Following is the modified code that worked for me
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HeadRotator : MonoBehaviour
{
public Vector3 rot = new Vector3(20, 30, 10);
Animator anim;
// Bone stuff
Transform head;
// !!! Head bone rotation approach !!!
void Start () {
// get attached Animator controller
anim = GetComponent<Animator>();
head = anim.GetBoneTransform(HumanBodyBones.Head);
}
void OnAnimatorIK (int layerIndex) {
print ("OnAnimatorIK - running");
anim.SetBoneLocalRotation(HumanBodyBones.Head, Quaternion.Euler(rot));
}
}
Thanks to #ZayedUpal head bone rotation is working, and thanks to a friend, setting muscle values is also working!
The full Unity 3D project for both options can be found here: https://github.com/NumesSanguis/Basic-Unity-Head-Rotation
Code snippet of muscle values approach:
// !!! Human Pose approach !!!
void Start () {
// https://forum.unity.com/threads/humanposehandler.430354/
// get attached Animator controller
anim = GetComponent<Animator>();
// run this if you want the indexes to muscles on your character
LookUpMuscleIndex();
// TODO keeping body above plane
//Vector3 current_position = transform.position;
// get human pose handler
humanPoseHandler = new HumanPoseHandler(anim.avatar, transform);
// get human pose
humanPose = new HumanPose();
// TODO keeping body above plane
//humanPose.bodyPosition = current_position;
// reference pose to pose handler
humanPoseHandler.GetHumanPose(ref humanPose);
// set a specific musle; 9: Neck Nod Down-Up
humanPose.muscles[9] = -20f;
Debug.Log(humanPose.muscles[9]);
// use pose information to actually set the pose;
humanPoseHandler.SetHumanPose(ref humanPose);
}
Two addition to the muscle answer
1, To solve the problem:
The character to which it was applied moved quite far from where he was supposed to be when humanPoseHandler.SetHumanPose(ref humanPose); was set.
You need to add:
humanPose.bodyPosition = transform.position;
humanPose.bodyRotation = transform.rotation;
That will keep your character exactly where it is located.
2, Actually the muscle solution is still valid when an AnimatorController is attached to current humanoid character. You just need to switch Update Mode to Animate Physics and put your muscle code in Update(). That will make your muscle code (which executes in Update()) executes after the AnimationController effects (which executes in FixedUpdate(), which is called earlier than the Update()).
This approach will allow you directly control some muscle, and use Unity IK effect at the same time.
Related
hello I'm new to unity and game development in general, I have this task that requires me to move an object through vectors addition and subtraction, it is really simple and i understand the math behind it but I just cant understand how to use gameobject.find and transform.position of the object that has the script on it in the code.
this here is the task im trying to do :
Create two new GameObjects and position them some distance apart from each other (preferably they should have different Meshes so you can tell them apart).
Create a new script and attach it to one of the GameObjects
In this new script, make it so it can do this :
1-Find position of the other GameObject (you can use GameObject.Find to do this)
2-Use the other GameObject position and the position of itself (transform.position) to determine a direction from its position to the other GameObject.
3-Apply this direction to the transform.position of this object, so that it will immediately move to the position of the other object (this will happen in a single frame and as such appear to have “teleported”, but this is fine for now)
4-Make it so this happens on a Key Press
and this is the code I have written:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyVector3 : MonoBehaviour
{
public GameObject sphere;
private float x, y, z;
public MyVector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public MyVector3 SubVectors(MyVector3 P, MyVector3 G)
{
MyVector3 rv = new MyVector3(0, 0, 0);
rv.x = P.x - G.x;
rv.y = P.y - G.y;
rv.z = P.z - G.z;
return rv;
}
void Start()
{
MyVector3 P = new MyVector3(transform.position);
}
1-Find position of the other GameObject (you can use GameObject.Find to do this)
In your component, use transform.position.
2-Use the other GameObject position and the position of itself (transform.position) to determine a direction from its position to the other GameObject.
Assuming you created a reference to your gameobject :
public GameObject otherGameObject;
You can do something like this :
var direction = otherGameObject.transform.position - transform.position;
3-Apply this direction to the transform.position of this object, so that it will immediately move to the position of the other object (this will happen in a single frame and as such appear to have “teleported”, but this is fine for now)
To avoid teleportation, multiply your direction by Time.deltaTime
Doc to Time.deltaTime : https://docs.unity3d.com/ScriptReference/Time-deltaTime.html
4-Make it so this happens on a Key Press
Google is your friend :)
You'll find references to the old input system and new input system.
If you are new, I suggest you to use the old one since it is easier to learn and use.
See this tutorial as example : https://medium.com/geekculture/setting-up-the-legacy-input-system-in-unity-b4d685ca6150
I wanted to make a vertically scrolling background with 3D assets (2D pictures works fine, but i wanted the cool lighting effect), and i kept failing doing something i though would be so simple.
so here's my current progress:
public Vector3 target;
private Transform Top_Top_Left_Rescroll;
void Start (){
target = GameObject.FindGameObjectWithTag ("Top_Top_Left_Rescroll").GetComponent<Transform>();
}
void Update () {
if (gameObject.transform.position.y <= -12) {
gameObject.transform.position = new Vector3 (target.x, target.y, target.z);
}
}
}
The object resets it's position to 0 after the if statement (the rotation and scale weren't affected), and i ran out of ideas to do what i want.
You are passing a Transform to a Vector3.
try :
target = GameObject.FindGameObjectWithTag("Top_Top_Left_Rescroll").transform.position;
ps: I'm not sure if you really want your target position to never change, but you are passing it's value during Start() so you will always place your gameObject in every frame at the same initial position.
this is what i want to achieve
I am currently trying to build a RADAR sensor on unity. I am currently using spherecast. How do i set the view angle of the sphere cast and also how do i read the angle at which an object is present in front of it.
What i have used now is Vector3.angle but this shows 160 degrees if the object is directly infront of the radar instead it should be showing 90 degrees.
Ill paste the code that i have implemented below
Any guidance is appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spherecast : MonoBehaviour
{
Rigidbody rb;
public List<GameObject> CurrentHitObjects = new List<GameObject>();
//public GameObject curobject;
public float radius;
public float maxdist;
public LayerMask layermask;
public float velocity;
public Time deltatime;
public Vector3 previous;
private Vector3 origin;
private Vector3 direction;
private float hitdist;
// Use this for initialization
void Start()
{
foreach (GameObject cur in CurrentHitObjects)
{
previous = cur.transform.position;
}
}
// Update is called once per frame
void Update()
{
origin = transform.position;
direction = transform.forward;
hitdist = maxdist;
CurrentHitObjects.Clear();
RaycastHit[] hits = Physics.SphereCastAll(origin, radius, direction, maxdist, layermask, QueryTriggerInteraction.UseGlobal);
foreach (RaycastHit hit in hits)
{
Plane[] planes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
if (GeometryUtility.TestPlanesAABB(planes, hit.collider.bounds))
{
float angle = Vector3.Angle(transform.forward, hit.point.normalized);
float degree = Mathf.Acos(angle) * Mathf.Rad2Deg;
Vector3 pos = hit.point;
Debug.Log(hit.collider.name + "position =" + pos);
CurrentHitObjects.Add(hit.transform.gameObject);
hitdist = hit.distance;
Debug.Log(hit.transform.name + "Distance ="+ hitdist);
Debug.Log(hit.collider.name + "Angle = " + angle);
velocity = ((hit.transform.position - previous).magnitude) / Time.deltaTime;
previous = hit.transform.position;
Debug.Log(hit.transform.name + "Velocity =" + velocity);
}
else
{
return ;
}
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Debug.DrawLine(origin, origin + direction * hitdist);
Gizmos.DrawWireSphere(origin + direction * hitdist, radius);
}
}
As far as I can tell your code doesn't do anything. My first tip would be to remove all of your commented out code, but after that here is why your code does nothing at all:
You pass an array of objects to your script. Fine so far.
You take this entire array of objects 'CurrentHitObjects' and pass the transform.position of every single object to a single vector3. This means that all the values are overwritten except for the last one. This would be a big problem if you were trying to find the position of every single object. This would instead require vector3[]. But there is another bigger problem.
'previous', which holds transform.position of the objects is not used anywhere. So you are not actually finding the location of anything.
You use start() (which only runs once by the way) to iterate through your object array, but then you clear, CurrentHitObjects.Clear();, right at the beginning of update() (which runs many times per second by the way). The problem here, is that if you hoped to use CurrentHitObjects for anything in your code, you can't because you have wiped it before you even start doing anything with it.
Your raycast[] is shooting towards nothing. Seems to me like it just shoots forward.
You are finding the angle between the forward direction and the forward direction?
Honestly there are a lot of major problems with this code. I don't mean to be harsh, but it looks like you copy and pasted someone else's code and don't know how to use it. This needs a complete rework. If you know how to code I would throw it out and start over again. See my comment on your answer for a better way to do what you want.
If you don't know how to code, you should not be asking for freebie working code on stackoverflow. Try a unity forum instead. If you are trying to get better, see my above comments.
I have the following simple prefab:
When I add this to my scene, it looks like this:
Very neat!
Then I have the following script on my Character:
public class MageController : MonoBehaviour {
public GameObject Spell;
public float SpellSpeed;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.H)) {
GameObject newSpell = Instantiate(Spell);
newSpell.transform.position = transform.position;
newSpell.transform.rotation = Quaternion.LookRotation(transform.forward, transform.up);
Rigidbody rb = newSpell.GetComponent<Rigidbody>();
rb.AddForce(newSpell.transform.forward * SpellSpeed);
}
}
}
The goal is of course to make sure that the fireball is spawned correctly (with the tail behind it)
This works when I stand at 0.0.0; it looks like this:
However, if I turn around it looks like this:
As you can see, the rotation of the fireball is not correct (in the above incorrect image it is flying away from me, however, the tail is in front).
What am I doing wrong? How can I make sure that the tail is always correctly placed?
Update after following the guidance of PlantProgrammer
it still turns incorrectly :(
Look at the image below!
You want to use the forward direction of the player and not it's rotation, when instantiating the fireball. (Remember: transform in your script is the player transform not the fireball transform.) Check https://docs.unity3d.com/ScriptReference/Quaternion.LookRotation.html. LookRotation will return the rotation based on player's forward and up vectors.
GameObject newSpell = Instantiate(Spell);
newSpell.transform.position = transform.position;
newSpell.transform.rotation = Quaternion.LookRotation(transform.forward, transform.up);
Not part of your question, but I would also suggest letting the fireball fly in the forward direction of itself not the player (as this leaves more room for later modifications)
rb.AddForce(newSpell.transform.forward * SpellSpeed);
I am very new to Unity and just got done yesterday following the Roller Ball example on the learn page here at Unity3d.
To practice what I have learned I wanted to try and recreate something similar using my own art and making the game different. I have been playing around with Voxel Art and I am using MagicaVoxel to create my assests. I created the walls, the ground etc.. and all is well.
Then came the player object, the sphere. I created one as close to a sphere as possible with magicaVoxel and it rolls fine. However, when using a script to have the camera follow the object it runs into issues.
If I don't constrain the Y axis then I will get bouncing and as far as the x and z axis I get kind of a Flat Tire effect. Basically the camera doesn't follow smoothly it bounces around, stop go etc...
I have tried making the collider larger then the sphere and even using the position of the collider vs the object itself. I have also tried putting the code in Update / FixedUpdate / LateUpdate. What is the proper way to fix or address something like this? Here is my scripts below:
Camera Controller:
public class CamController : MonoBehaviour {
public GameObject player;
private Vector3 offset;
void Start ()
{
// Get the distance between the player ball and camera.
offset = this.transform.position - player.transform.position;
}
void LateUpdate ()
{
this.transform.position = player.transform.position + offset;
}
}
Player Controller:
public class PlayerController : MonoBehaviour {
public float _speed;
void FixedUpdate()
{
// Get input from keyboard.
float _hoz = Input.GetAxis("Horizontal");
float _ver = Input.GetAxis("Vertical");
// Create a vector3 based on input from keyboard.
Vector3 _move = new Vector3(_hoz, 0.0f, _ver);
// Apply force to the voxel ball
this.GetComponent<Rigidbody>().AddForce(_move * _speed);
}
}
Thanks for any help in advance.
You can use the SmoothFollow Script of Unity it self for getting smooth follow of camera.
Here are the steps how you can get the script:
1) Assets->Import Package->Scripts.
2) At the dialog that appears select all the scripts, or just the smooth follow one and hit Import button.
3) Now this script is in your project, and you can attach it to the camera.
Hope this will help you...
Best,
Hardik.