Forum rules - please read before posting.

Help troubleshooting a custom action (check object rotation, rounding Vector3)

edited March 2020 in Technical Q&A

Hi, all,
I'm on my 2nd AC practice game, building my skills and trying to add more complexity.

I'm currently trying to build a custom action and am running into what I think is a small snag.

What I want to do is be able to rotate an object (with Action: Object Transform) 90 degrees and then check the rotation. This is part of a puzzle where the player rotates a few objects, and when they are all rotated the correct amount, the puzzle is solved.

The custom action seems to work fine in unit testing. I did this by rotating one object and having it give me a "Yes" or "No" message depending on whether the rotation matched the value entered. However, when I put it into the game for all the items, it isn't working.

I think the issue is that when I tell objects to rotate 90 degrees (ie 0 -> 90 -> 180 -> 270 -> 0), I notice in the Inspector that sometimes it is not exactly 90 (ie 0 -> 90.00001 -> 180 -> 270 -> 0). I can't figure out why this is happening, but I thought a good solution would be to round the Vector3 before the comparison. Then I can compare the rounded object to the entered value.

I've tried a bunch of different methods I found online, but can't find anything that works.

Any help either (1) rounding the Vector3 or (2) solving the comparison issue some other way.

Not sure best practice for sharing code, so I created a gist: https://gist.github.com/gaiusjaugustus/da50546f06d4970a158e7c9b9c5a5f04

Let me know what else I can provide to clarify what's going on.

Thanks in advance!

Comments

  • edited March 2020

    The rounding issue is a common problem with floats, or float-based variables like Vectors

    You might want to try the Unity function Mathf.Approximately

    It compares two floats and returns true if they're approximately equal. It can't be used to compare two Vectors, so you'd need to either compare all three component elements, or just the one you're interested in (I'd guess the y value)

    P.S. As a small word of warning, after some idle testing I'm not sure that the function works as expected under all possible conditions. Someone's written an alternative function over at stackoverflow, which might be worth using instead, as it allows you to specify the tolerance of the comparison

  • Thanks for the help!

    Going off of your suggestion, I converted both Vector3 Euler Angles to Quaternions and then used Quaternion.angle to get the angle between them. Then I was able to test whether that angle was below some tolerance.

    Go me for starting with rotation, which seems to be much more complicated than...say...position. Maybe not the most elegant solution, but it works until I learn more about Unity's C# quirks.

    Here's the updated code for the entire script:

    /*
     *
     *  Adventure Creator
     *  by Chris Burton, 2013-2020
     *  
     *  "ActionCheckTransform.cs"
     * 
     *  This is a blank action template, which has two outputs.
     * 
     */
    
    using UnityEngine;
    using System.Collections.Generic;
    #if UNITY_EDITOR
    using UnityEditor;
    #endif
    
    
    
    namespace AC
    {
    
        [System.Serializable]
        public class ActionCheckTransform : ActionCheck
        {
    
            // Declare variables here
            public GameObject objectToCheck;
            new Vector3 objectRotation;
            public Vector3 newVector;
            private float Distance;
    
    
            public ActionCheckTransform()
            {
                this.isDisplayed = true;
                category = ActionCategory.Object;
                title = "Check Object Transform";
                description = "This checks an objects transform properties (just rotation for now).";
            }
    
    
            public override bool CheckCondition ()
            {
                // Return 'true' if the condition is met, and 'false' if it is not met.
                if (objectToCheck != null)
                {
                    // Check rotation of object
                    objectRotation = objectToCheck.transform.eulerAngles;
                    //Convert Vector3 Euler angles to Quaternion
                    Quaternion a = Quaternion.Euler(newVector);
                    Quaternion b = Quaternion.Euler(objectRotation);
    
                     //Get angle between Quaternion values
                    float angle = Quaternion.Angle(a,b);
    
                    //Compare angle to some tolerance
                    return (angle < 1f);
                }
    
                return false;
            }
    
    
    #if UNITY_EDITOR
    
            public override void ShowGUI ()
            {
                // Action-specific Inspector GUI code here.  The "Condition is met" / "Condition is not met" GUI is rendered automatically afterwards.
                objectToCheck = (GameObject) EditorGUILayout.ObjectField ("GameObject to check:", objectToCheck, typeof (GameObject), true);
    
                newVector = EditorGUILayout.Vector3Field ("Check has rotation value:", newVector);
    
            }
    
    
            public override string SetLabel ()
            {
                // (Optional) Return a string used to describe the specific action's job.
    
                return string.Empty;
            }
    
    #endif
    
        }
    
    }
    
  • edited March 2020

    i'm a big fan of "it works"!

    (you may find that Chris comes up with a more elegant solution though. he usually does)

  • edited March 2020

    If it works, it works! It may be best to leave it at that.

    Though, I wonder it you could compare a Dot product of the two vectors instead. The more similar they are, the closer the result will be to 1:

    public override bool CheckCondition ()
    {
        // Return 'true' if the condition is met, and 'false' if it is not met.
        if (objectToCheck != null)
        {
            objectRotation = objectToCheck.transform.rotation.eulerAngles;
            float dotProduct = Vector3.Dot (objectRotation, newVector);
            return (dotProduct > 0.95f); // Or whatever threshold you want
        }
        return false;
    }
    

    Also, I'm not sure of the exact context, but this may be a good situation to rely on Draggable objects. When attached to a Track (e.g. a Hinge Track, so that it can only rotate), you can create "Snap points" so that it can only rotate to certain values. Which value it's currently at can then be checked with the Moveable: Check track position Action.

  • Thanks for the help, Chris.

    I played around with using Draggable objects in a 2D game in an earlier version of one of the puzzles for this game. Didn't work out for me (I just need to build up to it), but it's something I want to try again in my next project.

    I'll see if the dotProduct code can work. Just throwing it into the script did not, but I didn't do any troubleshooting with it. All I know is that the puzzle never made it to "solved".

    If I have a moment to play around with it and figure it out, I'll post that alternate code. I made a developer menu that spits out the rotation of the object, the rotation I want for the object, and the distance between them, which has been really helpful.

    Really excited to be moving along with my gamedev goals, all thanks to your AC development.

  • I played around with using Draggable objects in a 2D game in an earlier version of one of the puzzles for this game.

    Just to clarify: the Draggable system is for 3D only. To make a Draggable object in a 2D game, you'd have to override that scene's perspective when first creating the scene in the Scene Manager.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.