Rotation Not Working for GameObject Collections

Hello,
I am using a very simple script to rotate a object in the Update() on Hololens using Voice command.

This script works on a mesh/object directly, but does not work on a collection of meshes/objects.

I create an empty GameObject and insert prefabs and 3D meshes in this GameObject (So it’s the parent) . I am trying to rotate the GameObject - the parent. It doesn’t.

My Voice command to ‘spin object’ works only directly on a mesh, not a collection of meshes.
Any ideas?

—SPEECH MANGER–
keywords.Add(“Spin Diamond”, () =>
{
var focusObject = GazeGestureManager.Instance.FocusedObject;
if (focusObject != null)
{
// Call the OnDrop method on just the focused object.
focusObject.SendMessage(“StartRotate”);
}
});

–SPIN OBJECT–
// Called by SpeechManager when the user says the “Spin Object” command
void StartRotate()
{
rotate = true;
}
// Called by SpeechManager when the user says the “Stop Spinning” command
void StopRotate()
{
rotate = false;
}
// Update is called once per frame
void Update () {
if (rotate)
{
// Rotate Object
this.transform.parent.Rotate(Vector3.up, Time.deltaTime * 10);
}
}

Hello,

The other thing you need to do is to setup a Ray-cast from your camera so you can hit the collider of the object and spin it.

Here is an example

RaycastHit raycastHit;
var cameraTransform = Camera.main.transform;

if (Physics.Raycast(cameraTransform.position, cameraTransform.forward, out raycastHit))
{
//Do somehting here with the object you hit
//SetCurrentCollider(raycastHit.collider);
}
else
{
SetCurrentCollider(null);
}

Thank you,
Wesley

Hi Wesley,
Yes I saw this in the examples in the Holographic Academy. I will implement. Thank you!!

Hi Wesley,
It still doesn’t spin my collections of objects - only the ones with direct mesh. I did like you mentioned. I also looked over the API reference. I want the object to spin continuously even when not gazing at it.
Thanks for your help.

using UnityEngine;
using System.Collections;

public class SpinObject : MonoBehaviour {

bool rotate;

// Use this for initialization
void Start () {

}

// Called by SpeechManager when the user says the “Spin Object” command
void StartRotate()
{

rotate = true;
}

// Called by SpeechManager when the user says the “Stop Spinning” command
void StopRotate()
{
rotate = false;
}

// Update is called once per frame
void Update () {

if (rotate)
{
RaycastHit raycastHit;
var cameraTransform = Camera.main.transform;

if (Physics.Raycast(cameraTransform.position, cameraTransform.forward, out raycastHit))
{
// Rotate Object
this.transform.Rotate(Vector3.up, Time.deltaTime * 50);
}
}
}
}

It looks like the issue is that you are rotating the object only when you are looking at it because you have it in the if(Raycast) loop.

I think what you want to do is ray-cast out and get the collider from the hit info, then outside of the loop check use if(rotate) loop and use the collider to spin the game object.

Here is an example of what could work.

using UnityEngine;
using System.Collections;

public class SpinObject : MonoBehaviour {

bool rotate = false;
Collider Target;

// Use this for initialization
void Start () {

}

// Called by SpeechManager when the user says the “Spin Object” command
void StartRotate()
{

rotate = true;
}

// Called by SpeechManager when the user says the “Stop Spinning” command
void StopRotate()
{
rotate = false;
}

// Update is called once per frame
void Update () {

RaycastHit raycastHit;
var cameraTransform = Camera.main.transform;

if (Physics.Raycast(cameraTransform.position, cameraTransform.forward, out raycastHit))
{
//Store the collider
m_SpinTarget = raycastHit.collider;
}

if (rotate)
{
m_SpinTarget.gameObject.transform.Rotate(Vector3.up,Time.deltaTime * 50);
}
}
}

Hi Wesley,
Thanks for the response. I switched the script around. Now I’m using click gesture to spin the objects and they all spin at the collection level.

My voice commands do not work at the collection level. Only at the mesh level. Basically, same problem as before. This demonstrates it’s not an issue with ray-casting inside or outside the scope as you posted.

I can still only activate voice command on the mesh. I’m confused.

Here’s the script… any ideas

using UnityEngine;

public class DiamondCommands : MonoBehaviour
{
//spin
bool spinObject = false;
bool moveObject = false;

AudioSource audioSource = null;
AudioClip tapSound = null;

void Start()
{
// Add an AudioSource component and set up some defaults
audioSource = gameObject.AddComponent();
audioSource.playOnAwake = false;
audioSource.spatialize = true;
audioSource.spatialBlend = 1.0f;
audioSource.dopplerLevel = 0.0f;
audioSource.rolloffMode = AudioRolloffMode.Custom;

// Load the Sphere sounds from the Resources folder
tapSound = Resources.Load(“Impact”);
}

// Called by GazeGestureManager when the user performs a Select gesture
void OnSelect()
{
// On each Select gesture, toggle whether the user is in placing mode.
spinObject = !spinObject;

audioSource.clip = tapSound;
audioSource.Play();
}

// Called by SpeechManager when the user says the “Move Diamond” command
void Move()
{
moveObject = true;
SpatialMapping.Instance.DrawVisualMeshes = true;
audioSource.clip = tapSound;
audioSource.Play();
}

// Called by SpeechManager when the user says the “Place Diamond” command
void Place()
{
moveObject = false;
SpatialMapping.Instance.DrawVisualMeshes = false;
audioSource.clip = tapSound;
audioSource.Play();
}

// Update is called once per frame
void Update()
{

// Do a raycast into the world that will only hit the Spatial Mapping mesh.
var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;

if (moveObject)
{
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo, 30.0f, SpatialMapping.PhysicsRaycastMask))
// Move this object’s parent object to
// where the raycast hit the Spatial Mapping mesh.
this.transform.parent.position = hitInfo.point;

// Rotate this object’s parent object to face the user.
Quaternion toQuat = Camera.main.transform.localRotation;
toQuat.x = 0;
toQuat.z = 0;
this.transform.parent.rotation = toQuat;
}

if (spinObject)
{
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
// Rotate Object
this.transform.parent.Rotate(Vector3.up, Time.deltaTime * 50);
}
}
}

Take a look at these scripts, this is what we use for testing this sort of thing. I use the scripts to spin an object I’m gazing at, or to move an object I’m gazing at. These are a little more complicated because they are designed to work in the editor as well.

Setup Help on how to use these scripts. Using a simple scene with a cube 3 meters(z) from the main camera.

Description:
I have a scene with a simple cube in it, with my camera.

On the Cube:
Add the Keyword Responder and the Shape Controller Script.
Don’t forget to setup the Keywords

On the Main Camera put the Speech Controller script on it.

This is Test scripts so my apologies is it is hard to read.

2762641–199514–KeywordActionPair.cs (201 Bytes)
2762641–199515–KeywordResponder.cs (485 Bytes)
2762641–199516–ShapeController.cs (1.45 KB)
2762641–199517–SpeechController.cs (8.08 KB)

Hi Welsey,
It’s got to be bug in Unity with Speech Commands.

Here’s a script that works on collections and mesh with click commands.
When modified to accept voice commands it only works on direct mesh.
All I did was change the input method, nothing else - Click to Voice -

using UnityEngine;

public class SpeakToPlaceParent : MonoBehaviour
{
bool move = false;

AudioSource audioSource = null;
AudioClip tapSound = null;

void Start()
{
// Add an AudioSource component and set up some defaults
audioSource = gameObject.AddComponent();
audioSource.playOnAwake = false;
audioSource.spatialize = true;
audioSource.spatialBlend = 1.0f;
audioSource.dopplerLevel = 0.0f;
audioSource.rolloffMode = AudioRolloffMode.Custom;

// Load the Sphere sounds from the Resources folder
tapSound = Resources.Load(“Impact”);
}

// Called by SpeechManager when the user says the “Spin Object” command
void Move()
{
move = true;
SpatialMapping.Instance.DrawVisualMeshes = true;
audioSource.clip = tapSound;
audioSource.Play();
}

// Called by SpeechManager when the user says the “Stop Spinning” command
void Place()
{
move = false;
SpatialMapping.Instance.DrawVisualMeshes = false;
audioSource.clip = tapSound;
audioSource.Play();
}

// Update is called once per frame
void Update()
{
// If the user is in placing mode,
// update the placement to match the user’s gaze.

if (move)
{
// Do a raycast into the world that will only hit the Spatial Mapping mesh.
var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;

RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo,
30.0f, SpatialMapping.PhysicsRaycastMask))
{
// Move this object’s parent object to
// where the raycast hit the Spatial Mapping mesh.
this.transform.parent.position = hitInfo.point;

// Rotate this object’s parent object to face the user.
Quaternion toQuat = Camera.main.transform.localRotation;
toQuat.x = 0;
toQuat.z = 0;
this.transform.parent.rotation = toQuat;
}
}
}
}

By the way, I’m building off of the code from the Origami demo.
I’m trying to get your demo running - can I use the World Cursor from the Origami demo for Gaze?

How come in my script above I can modify collections with Click input, but cannot with Voice Input?

This is what I mean by collection. It’s actually a model with a bunch of meshes.

Hi @spinteractive ,

I put together a simple example that does what you are trying to do. I have attached my project to this post.

Below is the result that I get with my implementation.

I created a parent object in my scene. I created 3 cubes as children of the parent object. I wrote a “Rotater” script that rotates any GameObject. Then I attached the Rotater script to each cube. The Rotater script has a boolean variable called “Rotate”. When it is true, the GameObject will rotate. When it is false, it will stop rotating.

Lastly I have a “Commander” script. I added the Commander component to my Main Camera. The Commander script will set a Rotater component’s Rotate variable to true or false. However the rotate variable will only be changed on a GameObject that is in the Camera’s gaze or line of sight. In editor, you can change the rotation of the camera in the scene view to point at a cube and then press the “A” key to rotate it or the “Z” key to stop its rotation. On the HoloLens, you can look at a cube and say “start spinning” or “stop spinning”.

The following is a visual snapshot of my scene setup.

The following is the source code for both scripts.

Rotater.cs

using UnityEngine;
using System.Collections;

public class Rotater : MonoBehaviour
{
    public bool Rotate = false;
    public float SecondsToCompleteRotation = 5.0f;

    float timer = 0.0f;
    Vector3 startingEulerAngles;
    Vector3 endingEulerAngles;

    void Start ()
    {
        startingEulerAngles = this.transform.localRotation.eulerAngles;
        endingEulerAngles = new Vector3(360.0f, 0.0f, 360.0f);
    }

    void Update ()
    {
        if(!Rotate)
        {
            return;
        }

        timer += Time.deltaTime;
        float t = Mathf.Clamp01(timer/SecondsToCompleteRotation);

        Vector3 currentEulerAngles = Vector3.Slerp(startingEulerAngles, endingEulerAngles, t);

        Quaternion newRotation = Quaternion.Euler(currentEulerAngles);
        this.transform.localRotation = newRotation;

        if(t >= 0.99f)
        {
            timer = 0.00f;
        }
    }
}

Commander.cs

using UnityEngine;
using UnityEngine.Windows.Speech;

public class Commander : MonoBehaviour
{
    private static readonly string VC_START_SPINNING = "start spinning";
    private static readonly string VC_STOP_SPINNING = "stop spinning";

    KeywordRecognizer keywordRecognizer = null;

    void Start ()
    {
        string[] keywords = new string[]
        {
            VC_START_SPINNING,
            VC_STOP_SPINNING
        };

        keywordRecognizer = new KeywordRecognizer(keywords, ConfidenceLevel.Medium);
        keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        keywordRecognizer.Start();
    }

    void OnDestroy()
    {
        keywordRecognizer.Stop();
    }

    private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
    {
        GameObject focusObject = null;

        if(string.Equals(args.text, VC_START_SPINNING))
        {
            focusObject = GetGameObjectInFocus();
            OnStartSpinning(focusObject);
        }
        else if(string.Equals(args.text, VC_STOP_SPINNING))
        {
            focusObject = GetGameObjectInFocus();
            OnStopSpinning(focusObject);
        }
    }

    void Update ()
    {
#if UNITY_EDITOR
        GameObject focusObject = null;
        if(Input.GetKeyDown(KeyCode.A))
        {
            focusObject = GetGameObjectInFocus();
            OnStartSpinning(focusObject);
        }
        else if(Input.GetKeyDown(KeyCode.Z))
        {
            focusObject = GetGameObjectInFocus();
            OnStopSpinning(focusObject);
        }
#endif

    }

    GameObject GetGameObjectInFocus()
    {
        Ray ray = new Ray(  this.transform.position,
                            this.transform.forward);

        RaycastHit hitInfo;
        if(!Physics.Raycast(ray,out hitInfo,float.MaxValue))
        {
            return null;
        }

        if(hitInfo.collider == null)
        {
            return null;
        }

        return hitInfo.collider.gameObject;
    }

    void OnStartSpinning(GameObject gameObjectToSpin)
    {
        if(gameObjectToSpin == null)
        {
            return;
        }

        Rotater rotater = gameObjectToSpin.GetComponent<Rotater>();
        if(rotater == null)
        {
            return;
        }

        rotater.Rotate = true;
    }

    void OnStopSpinning(GameObject gameObjectToSpin)
    {
        if(gameObjectToSpin == null)
        {
            return;
        }

        Rotater rotater = gameObjectToSpin.GetComponent<Rotater>();
        if(rotater == null)
        {
            return;
        }

        rotater.Rotate = false;
    }
}

I hope that helps!

2763843–199625–SpinningCubes.zip (338 KB)

Thank you for the thorough response.
Can you put that rotator script on the parent and spin all 3?
And also move the entire collection to another point on the spatial mesh with a gaze-click.
That’s where I’m having problems.
I can only Spin or Move a collection. Not both…

Hi OK, heres a link to my project - Microsoft OneDrive

There’s a GestureManager & SpeechManager.
There’s a script called DiamondCommands.cs
It let’s you air-click objects to SPIN, and say ‘MOVE DIAMOND / PLACE DIAMOND’ to move & place objects.

On the left object, it moves and places this object as I need.
On the right object, it only Spin’s the object - move does not work.

The only difference I see is:
On the left object, the script is being called on the mesh directly and it works both SPIN AND MOVE.
On the right object , the script is being called on the parent - it only SPINs.

Why is this happening? I need to move and spin collection of objects (parents).

Thank you for your assistance and time I really appreciate :slight_smile:

Hi @spinteractive ,

In the following example, I am transforming the parent game object instead of directly transforming the three cubes.

I added the the functionality to move a game object to the Rotater script. I also removed the box collider and rotater script from each of my cubes. Then I added a box collider to my parent game object. I made the box collider large enough to contain all three of the child cubes. Finally I added the rotater script to the parent object.

Below is a snapshot of my scene setup.

The following is the updated source code for both scripts.

Rotater.cs

using UnityEngine;
using System.Collections;

public class Rotater : MonoBehaviour
{
    public bool Rotate = false;
    public bool Move = false;
    public float SecondsToCompleteRotation = 5.0f;
    public float SecondsToCompleteMovement = 5.0f;

    float rotateTimer = 0.0f;
    float moveTimer = 0.0f;

    Vector3 startingEulerAngles;
    Vector3 endingEulerAngles;

    Vector3 startingPosition;
    Vector3 endingPosition;

    void Start ()
    {
        startingEulerAngles = this.transform.localRotation.eulerAngles;
        endingEulerAngles = new Vector3(360.0f,0.0f,360.0f);

        startingPosition = this.transform.localPosition;
        endingPosition = startingPosition + (this.transform.localRotation * Vector3.up * 0.5f);
    }

    void Update ()
    {
        RotateObject();
        MoveObject();
    }

    void RotateObject()
    {
        if(!Rotate)
        {
            return;
        }

        rotateTimer += Time.deltaTime;
        float t = Mathf.Clamp01(rotateTimer/SecondsToCompleteRotation);

        Vector3 currentEulerAngles = Vector3.Slerp(startingEulerAngles,endingEulerAngles,t);

        Quaternion newRotation = Quaternion.Euler(currentEulerAngles);
        this.transform.localRotation = newRotation;

        if(t >= 0.99f)
        {
            rotateTimer = 0.00f;
        }
    }

    void MoveObject()
    {
        if(!Move)
        {
            return;
        }

        moveTimer += Time.deltaTime;
        float x = moveTimer/SecondsToCompleteMovement;
        // Use an S curve to smooth out the motion.
        float xPrime = x*x*(3.0f-2.0f*x);
        float t = Mathf.Clamp01(xPrime);

        Vector3 newPosition = Vector3.Lerp(startingPosition, endingPosition, t);
        this.transform.localPosition = newPosition;

        if(t >= 0.99f)
        {
            // Swap the position vectors around so we don't need
            // to change the lerp code.
            Vector3 tempPosition = startingPosition;
            startingPosition = endingPosition;
            endingPosition = tempPosition;

            moveTimer = 0.00f;
        }
    }
}

Commander.cs

using UnityEngine;
using UnityEngine.Windows.Speech;

public class Commander : MonoBehaviour
{
    private static readonly string VC_START_SPINNING = "start spinning";
    private static readonly string VC_STOP_SPINNING = "stop spinning";
    private static readonly string VC_START_MOVING = "start moving";
    private static readonly string VC_STOP_MOVING = "stop moving";

    KeywordRecognizer keywordRecognizer = null;

    void Start ()
    {
        string[] keywords = new string[]
        {
            VC_START_SPINNING,
            VC_STOP_SPINNING,
            VC_START_MOVING,
            VC_STOP_MOVING
        };

        keywordRecognizer = new KeywordRecognizer(keywords, ConfidenceLevel.Medium);
        keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        keywordRecognizer.Start();
    }

    void OnDestroy()
    {
        keywordRecognizer.Stop();
    }

    private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
    {
        GameObject focusObject = null;

        if(string.Equals(args.text, VC_START_SPINNING))
        {
            focusObject = GetGameObjectInFocus();
            OnStartSpinning(focusObject);
        }
        else if(string.Equals(args.text, VC_STOP_SPINNING))
        {
            focusObject = GetGameObjectInFocus();
            OnStopSpinning(focusObject);
        }
        else if(string.Equals(args.text,VC_START_MOVING))
        {
            focusObject = GetGameObjectInFocus();
            OnStartMoving(focusObject);
        }
        else if(string.Equals(args.text,VC_STOP_MOVING))
        {
            focusObject = GetGameObjectInFocus();
            OnStopMoving(focusObject);
        }
    }

#if UNITY_EDITOR
    void Update ()
    {
        GameObject focusObject = null;
        if(Input.GetKeyDown(KeyCode.A))
        {
            focusObject = GetGameObjectInFocus();
            OnStartSpinning(focusObject);
        }
        else if(Input.GetKeyDown(KeyCode.Z))
        {
            focusObject = GetGameObjectInFocus();
            OnStopSpinning(focusObject);
        }
        else if(Input.GetKeyDown(KeyCode.S))
        {
            focusObject = GetGameObjectInFocus();
            OnStartMoving(focusObject);
        }
        else if(Input.GetKeyDown(KeyCode.X))
        {
            focusObject = GetGameObjectInFocus();
            OnStopMoving(focusObject);
        }
    }
#endif

    GameObject GetGameObjectInFocus()
    {
        Ray ray = new Ray(  this.transform.position,
                            this.transform.forward);

        RaycastHit hitInfo;
        if(!Physics.Raycast(ray,out hitInfo,float.MaxValue))
        {
            return null;
        }

        if(hitInfo.collider == null)
        {
            return null;
        }

        return hitInfo.collider.gameObject;
    }

    void OnStartSpinning(GameObject gameObject)
    {
        if(gameObject == null)
        {
            return;
        }

        Rotater rotater = gameObject.GetComponent<Rotater>();
        if(rotater == null)
        {
            return;
        }

        rotater.Rotate = true;
    }

    void OnStopSpinning(GameObject gameObject)
    {
        if(gameObject == null)
        {
            return;
        }

        Rotater rotater = gameObject.GetComponent<Rotater>();
        if(rotater == null)
        {
            return;
        }

        rotater.Rotate = false;
    }

    void OnStartMoving(GameObject gameObject)
    {
        if(gameObject == null)
        {
            return;
        }

        Rotater rotater = gameObject.GetComponent<Rotater>();
        if(rotater == null)
        {
            return;
        }

        rotater.Move = true;
    }

    void OnStopMoving(GameObject gameObject)
    {
        if(gameObject == null)
        {
            return;
        }

        Rotater rotater = gameObject.GetComponent<Rotater>();
        if(rotater == null)
        {
            return;
        }

        rotater.Move = false;
    }
}

Attached is the update Unity project.

I hope that helps!

2765187–199760–SpinningCubesPart2.zip (332 KB)

Hi Brandon.
Thanks for the awesome demo. really cool!
OK, one additional thing I am doing, that your are not, is ray-casting to the objects, and to the spatial mesh.
Maybe that it preventing something.
I still haven’t figured out my problem. It’s driving me nuts.

I can only make them move and rotate with gestures - my speech doesn’t work.

Move:

Rotate:

Looking at your code and project. Thanks.
Alex

Here the speech manager and command script are on the mesh. This works with gesture and speech. The speech command is heard and gestures works. This is good but it’s only affecting the bottom mesh (it needs to work on the parent)

Here the speech manager and command script are on the parent. This only works with gesture. The speech command does not work. WHY NOT??? (Is it a collider issue?) I NEED THIS TO WORK LIKE ABOVE - BUT IT MUST BE ON THE PARENT TO AFFECT THE ENTIRE OBJECT

Here’s the video:

I figured out my problem. If putting a mesh collider on a parent, you need to assign a mesh to it, or use some other type of collider - for voice commands. For some reason, the gestures work as any level in the hierarchy.

IT WORKS!!!

1 Like