Class Project 1

The concept of this assignment was familiarize yourself with a few of Unity’s core functions. It explored the transform component that all game object have and how to utilize it to move object without the standard library of tools or the help of the rigidbody.

The sample scene of the assignment.

Target

The target is a black primitive sphere that moves when the user left mouse clicks. The constraints where:

  1. Can only move on the designated plane (grey)
  2. Has to be above the plane at all times
  3. Will not move if user clicks on object other than the designated plane

To achieve this the script was fairly simple, two functions were called in Update, Click() and MoveTarget(). If there was a valid click, MoveTarget() would be called to move the target sphere to the new valid position.

    void Update() {
        //if click,
        if (Input.GetMouseButtonDown(0)) {
            click();
        }
    }
    void click() {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit)) {
            if (!EventSystem.current.IsPointerOverGameObject()) {
                // object? delete
                if (hit.transform.gameObject.CompareTag("object")) {
                    Destroy(hit.transform.gameObject);
                } else {
                    // plane? move object
                    if (hit.transform.name == "CreationPlane") {
                        Debug.Log("Hit Plane");
                        MoveTarget(hit.point);
                    }
                }
            }
        }
    }

    void MoveTarget(Vector3 pos) {
        // 0.25f offset above plane
        pos.y += 0.25f;
        transform.position = pos;
    }

One bug that I had to fix was the target sphere jumping behind the dropdown menu when clicked on an option. The because I was clicking, the script was shooting a raycast to that position. So in the Click() function, I added a check (EventSystem.current.IsPointerOverGameObject()) to see if the ray passes through a UI element before hitting the plane


Spawn Objects

To spawn objects using the drop down menu, I created an object that holds the target sphere and a list of prefabs to instantiate in the same index order of the drop down menu.

Object spawn

The drop down menu on the canvas had four indices, as describe in the assignment. The first was to be always on top. For the On Value Changed event, I attached the spawn script and called the spawn object function.

Dropdown menu.
public class SpawnObjectScript : MonoBehaviour {
    public GameObject target;
    public Vector3 spawnPosition;
    public Dropdown dropDown;

    [SerializeField]
    List<GameObject> spawnPrefabs;

    public void SpawnObject() {
        // find spawn point
        //spawnPosition = target.transform.position;
        // instantiate prfab
        spawnPosition = target.transform.position;
        if (dropDown.value > 0) { 
            GameObject obj = Instantiate(spawnPrefabs[dropDown.value - 1], 
                                         spawnPosition, Quaternion.identity);
        }
        // reset menu
        dropDown.value = 0;
    } 
}

Some values are public for visualization in the editor and for the assignment.


Moving the Objects

Each primitive type moves and rotates on a different axis. However, they share many values. So I wrote a parent class, ObjectBehavior that handled most of the functionality. The subclasses would then use the slightly different inputs and move and rotate accordingly.

    private void Awake() {
        boundToggle = GameObject.Find("Bounded").GetComponent<Toggle>();
        boundToggle.isOn = isBound;
    }

    private void FixedUpdate() {
        // cahnge color
        ChangeColor();
        // rotation
        Rotate();
        isBound = boundToggle.isOn;
    }

    public void Move(Vector3 dir) {
        transform.Translate(dir * speed * Time.deltaTime);
    }

    // color change of obj
    public void ChangeColor() {
        material.color = (posDir) ? color : colorChange;
    }

    public void Rotate() {
        // cube - about y-axis, 90-degrees per second
        transform.rotation *= Quaternion.AngleAxis(rotationSpeed * Time.deltaTime, Vector3.up);
    }

    public void BindObjectBounds(bool b) {
        isBound = b;
    }

I chose to use the Awake() function in the parent class so that I could use the Start() in the subclasses. As you can see, the parent class handles the functionality of the object, but the subclass calculate the differences in movement and rotation then call the super.

    private void Start() {
        spawnPosition = transform.position;

        offset = scale / 2f;
        transform.position = new Vector3(transform.position.x, offset, transform.position.z);

        material = GetComponent<MeshRenderer>().material;
        material.color = color;
    }
    private void Update() {
        // get direction vector
        moveDirection = posDir ? Vector3.up : Vector3.down;

        // move
        Move(moveDirection);

        // update movement direction
        if (transform.position.y >= rangeLimit + offset) {
            posDir = false;
        }

        if (transform.position.y <= offset) {
            posDir = true;
        }
    }

In the subclass, the key difference is the boundary calculations since each primitive has a different set of limitations.


Extras

We had the liberty to add to the assignment once we finished. I added a pause time button that simple set the time scale to 0/1.

I also add a two toggles: one, the ability to unbound the objects from the assignment bounds and make it based off of their spawned location, and two, the ability to toggle a visual of the assignment boundary for the objects 0 < x,y < 5;


Source Code

You can download or follow this project on Github here.