Sunday 30 November 2014

'Look at Object' mechanic - using the Dot Product

As I mentioned in the presentation I used the dot product to multiply two vectors together. To find out at which angle I was viewing a game object (b) from a different game object (a).

I first found this solution by searching around on the internet. I found some psueodo code (which I will post at the end) which made mention that the dot product would be ideal for this, as such I started reading into the math some more. I did this as I feel that I could learn a lot about how this mechanic would work if I delved into the details a bit. Also I am not overly confident on my math skills so I always try and sponge up as much as I can/when I can.

The Dot product is defined as:


We do not list (ax . by) or (bx . ay)  as these equate to 0.

By rotating the vector b to a baseline (to 0 or the x-axis) we end up with:




We have the vector a multiplied by the vector b which is equal to the magnitude (length) of vector a multiplied by vector b multiplied by the cosine (the angle between the two vectors).

This can be calculated and will results in a scalar value (a single number). Which we then use in some way which I will describe in my code below.

I'm pretty happy I spent some time reading up on sin and cos as I mentioned in a previous blog post. It seems that they are used often in 3D calculation in games and simulations. From what I have seen over the last few weeks I'm going to need to spend some more time brushing up on my 3D math skills. At least I learnt SohCahToa! So I know that we could calculate the cos in the formula above by multiplying the Adjacent with the Hypotenuse of the angle between the two vectors! Isn't it amazing what you can learn with google? 

Even though I understand the fundamentals of the math behind this I still would like to spend some time reading up on this. Unfortunately this is only one mechanic and there are many more I need to develop! So I will have to make a note of this and put it on the backburner for now. The sites I used to research Dot product were Wikipedia, Better Explained, Mathisfun and the Unity API reference.

Now that I had the dot product I looked at what vectors I would need to use. According to the API reference I would use the .forward of the gameobject I wanted to have look around (so in this case the camera). Additionally I would need to get the direction of the target object from my (camera's) current position.

This made sense when comparing it to the psuedo code I found online. The only difference was the psuedo code normalized the value (so between 1 and 0) after working out the direction of the view object from my camera. This was done probably for ease of use when it came to comparisons (if greater than 0, less than 0.5 etc).

The psuedo code can be found here and I will list it below as well:


  • Vec3 dirFromMeToObject = (objPos - myPos).normal
  • Vec3 myCurrentFacingDir = trans.forward
  • if (Vec3.Dot(dirFromMeToObject, myCurrentFacingDir) > 0)
  • //object is with a 180 degree arc in front of us
  • if (Vec3.Dot(dirFromMeToObject, myCurrentFacingDir) > 0.5)
  • //object is with a 90 degree arc in front of us
  • if (Vec3.Dot(dirFromMeToObject, myCurrentFacingDir) > 0.75)
  • //object is with a 45 degree arc in front of us

  • Understanding the psuedo code was trivial after learning what a Dot product was. Now that I understood this solution I could start implementing it. It would require some set-up first however. Before manipulating the vectors (which would be Vector3s) I would need to get the coordinates from transforms and store them in variables, then build the required Vector3s based on those coordinates.

    So in closing I came up with the following code solution:

    using UnityEngine;
    using System.Collections;

    /*This script will return output based on the angle an object is being viewed at from a different object (in this case the camera)
    It does this by using the dot product to multiply the two Vector3's of the two game objects.
    Taken from the API reference, the dot product is a float value (a scalar value) equal to the magnitudes of the two vectors multiplied together
    and then multiplied by the cosine of the angle between them. (The cos would be the angle between the two Vectors) */


    public class LookAtObject : MonoBehaviour {

        //Initialising some vars to store coordinates of the objects vectors
        Vector3 myCurrentFront;
        Vector3 planet1Pos;
        Vector3 currentPos;
        Vector3 dirMeToObj;
        Transform tmpStore;
        Transform tmpMyVec;

        float tmpStoreX;
        float tmpStoreY;
        float tmpStoreZ;

        float tmpMyVecX;
        float tmpMyVecY;
        float tmpMyVecZ;

        //If we are viewing the secondary object from x angle this is toggled and we have output
        public bool IsActive;

        // Use this for initialization
        void Start () {
            IsActive = false;
        }
       
        // Update is called once per frame
        void Update () {
            calcLookAt();
        }

        void calcLookAt(){
            //We get the current facing of the primary game object
            //We use this to ensure the other game object is infront of us
            myCurrentFront = gameObject.transform.forward;
           
            //Temp store for the other objects transform (Planet1)
            tmpStore = GameObject.Find("Planet1").gameObject.transform;
            //Primary game object's (camera) transform is stored in here
            tmpMyVec = gameObject.transform;

            //Storing each separate coordinate for a vector, from both primary and secondary game objects
            //This is to build two Vector3's to be used in the Dot Product calculation
            tmpMyVecX = tmpMyVec.position.x;
            tmpMyVecY = tmpMyVec.position.y;
            tmpMyVecZ = tmpMyVec.position.z;
           
            tmpStoreX = tmpStore.position.x;
            tmpStoreY = tmpStore.position.y;
            tmpStoreZ = tmpStore.position.z;

            //Building the new Vectors to be used in the comparison calculation
            planet1Pos = new Vector3(tmpStoreX, tmpStoreY, tmpStoreZ);
            currentPos = new Vector3(tmpMyVecX, tmpMyVecY, tmpMyVecZ);

            //Gets the magnitude of the primary object to the secondary one and then normalizes it
            dirMeToObj = (planet1Pos - currentPos).normalized;

            //180 degrees infront of us - performs the .Dot product calculation between the direction value and the front facing part of the primary objects transform
            if(Vector3.Dot (dirMeToObj, myCurrentFront) > 0){
                //Debug.Log ("Looking at Planet Area at 180 degrees");
            }

            //90 degrees infront of us
            if(Vector3.Dot (dirMeToObj, myCurrentFront) > 0.5){
                //Debug.Log ("Looking at Planet Area at 90 degrees");
            }

            //45 degrees infront of us
            if(Vector3.Dot (dirMeToObj, myCurrentFront) > 0.75){
                Debug.Log ("Looking at Planet Area at 45 degrees");
                Debug.Log (Vector3.Dot (dirMeToObj, myCurrentFront)); //used for debugging
                IsActive = true;
            }else{
                IsActive = false;
            }
        }

        public bool getIsActive(){
            return IsActive;
        }

    }


    This all works pretty well (as I demonstrated in the presentation). However as a few developers informed me, it would be much easier to just use Raycasting. With that in mind, I looked into it and I'll show my findings in my next post.

    No comments:

    Post a Comment