Iris Oostra
  • Home
  • Portfolio
    • Math Dungeon
    • VR Portfolio
      • Blendshapes
      • Concepting & Design
      • Group Coaching
      • Individual Coaching
      • Pressure Cooker
      • Programming
    • Pressure Cooker: Candy Sorter
    • Lava Shader
    • Bunny Platformer
    • Hero’s Crucible
    • DnD app
    • Zwangerschapsapp
  • Hobby’s
    • Naaiprojecten
    • Tekeningen
      • Alle tekeningen
      • Digitale tekeningen
      • Marker tekeningen
      • Potloodtekeningen
      • Verf
  • Contact
19 januari 2022

Carousels

Carousels
19 januari 2022

For the clothing in the scene we wanted to have carousels to display them. Each clothing category (shirts, pants, dresses, etc) would be one carousel and to have the carousels accommodate all of the clothing I designed it so that the carousel displays five objects at one time but to exchange the last one in the back for a new clothing piece each time it is rotated. You can read about my thought process here. I also thought of a way to show load only the carousel that’s in focus, but due to time constraints I haven’t implemented that.

Because I could only start programming on this in the last sprint I looked up code online, so that would save me some time. I immediately found what I was looking for and I only had to update it to make the exchanging I described above possible. I found this code via this site.

This is my version of the code after adding the exchange functionality. I put it at the end of this text.

The first thing I did to make the exchange possible was working with two LinkedLists, where the first one was the objects currently displayed in the carousel and the second one was the excess. Each time the carousel was rotated I would move the first one of the excess LinkedList to the end of the displayed LinkedList and move the first one of the displayed LinkedList to the end of the excess LinkedList. Eventually because of other needs (like reading the index in the list and trying to also have the right angles within the carousel rotation) I moved back to arrays. I chose for arrays because they are actually a bit better than lists since an array is a primitive (and there were no differences for me in this use case, because the size was fixed). I learned this from here.

The bug

After I finished the excess exchange part of the carousel I started testing it and while doing that I saw some things going wrong where clothing would spawn at the same spot as other clothing. I did not really see what went wrong exactly so I started testing by displaying UI-text with numbers on every clothing piece so that I could link them to their places in the array and in the carousel itself.

Testing with numbers on the clothing pieces in the carousel.

This really helped me and made me identify the first problem: every piece spawned at the same angle. This was before the screenshot above and all of the clothing pieces would load at the same spot. This meant that I changed the code so that every piece that is displayed the first time will have an angle 360/amountOfDisplayedObjects and then I made an array with the values of those angles and every time a new object would be displayed in the array it grabbed the next angle in the array and kept cycling through that every time.

After solving this issue and other issues something strange happened. Everything worked perfectly until you had seen all available objects and the cycle began again. From the second cycle on objects would load one spot too far. So if you have the angles: [72, 144, 216, 288, 360]: what should happen is that object 0: 72deg, object 1: 144 …… object 8: 288, object 10: 72, object 0: 144. This went well for the first cycle from 0-10 (or 1-11), but not afterwards.

I have debugged this and the angles seem to be correct and I have found nothing on Google. I tried resetting the count or dividing it by 2, but nothing helped. The closest I came to a solution was where it worked except for the first two objects. I even made an excel sheet displaying every carousel turn and the visible objects and their angles to make sure I wasn’t calculating anything wrong. But I still haven’t solved it.

The only thing I haven’t been able to try was changing things to how the rotation works (not rotating the carousel gameobject or only doing that). This was because there were other things with a higher priority and I had a temporary solution for this object: making one big carousel that does not exchange clothing but instead shows all clothing at the same time and has the user in the center of it.

My table in Excel displaying every state of the carousel (left: displayed piece index, right: angle of that object).

Reflection

What I learned from this is how you make a carousel, but also how to work with angles and this already helped me when I made a quick thing where the bee and its text follow the user around the scene. But I also learned that I should have asked for help earlier, as you can see with the problem above. I was stubborn and didn’t ask for help because I understood from teammates that when you ask for help the teachers told them ways to figure it out yourself but I didn’t want that because I did not have enough time for that. But when having individual coaching with Lisette she told me that if I had asked in a specific way I would have received the help I needed.

The code for the bee and text following the users rotation:

using UnityEngine;

public class FollowPlayerEyes : MonoBehaviour {
    [SerializeField]
    private GameObject playerVRCamera;

    private float previousRotation;
    private float currentRotation;

    private float turningRotation;

    // Start is called before the first frame update
    void Start() {
        currentRotation = playerVRCamera.transform.rotation.eulerAngles.y;
        previousRotation = currentRotation;
    }

    // Update is called once per frame
    void Update() {
        Debug.Log("Current: " + currentRotation + " Previous: " + previousRotation);
        currentRotation = playerVRCamera.transform.rotation.eulerAngles.y;
        turningRotation = currentRotation - previousRotation;
        this.transform.RotateAround(playerVRCamera.transform.position, new Vector3(0, 1, 0), turningRotation);
        previousRotation = currentRotation;
    }
}

Carousel code

using UnityEngine;

/*
* to use this script just place it on any gameobject in the scene, this element shouldn't have a collider so an empty game object is ideal
* add elements to the carouselObjects array, make sure these items have a collider
* make sure there are no elements with colliders in between the center and the carousel elements
*/
public class Carousel : MonoBehaviour {
    private GameObject[] carouselObjects;//the elements of the carousel

    public float distanceFromCenter = 0.4f;//the distance from the center of the carousel
    public int chosenObject = 0; //index of the object that is centered in the carousel
    public float speedOfRotation = 0.1f; //the speed in which the carousel rotates: values should be between 0.01f -> 1.0f, zero will stop the rotation

    private static float diameter = 360.0f; //the diameter is always 360 degrees
    private float angle = 0.0f; //the angle for each object in the carousel
    private float newAngle = 0.0f; //the calculated angle
    private bool firstTime = true; //used to calculate the offset for the first time

    //Iris Oostra
    [SerializeField]
    private int maximumObjects = 5;

    public OVRInput.Button button;
    public OVRInput.Controller controller;

    private float[] objectAngles;
    private int[] currentVisibleIndexes;
    private Vector3 positionCarousel;
    private Vector3 axisCarousel;
    int currentSpawnAngle;
    int amountOfCarouselObjects;
    bool isBiggerThanMax;

    void Start() {
        carouselObjects = new GameObject[transform.childCount];
        for(int i = 0; i < transform.childCount; i++) {
            carouselObjects[i] = transform.GetChild(i).gameObject;
        }
        currentSpawnAngle = 0;
        objectAngles = new float[maximumObjects];
        currentVisibleIndexes = new int[maximumObjects];
        positionCarousel = this.transform.position;
        axisCarousel = new Vector3(0, 1, 0);
        amountOfCarouselObjects = carouselObjects.Length;
        isBiggerThanMax = true;

        if(amountOfCarouselObjects >= maximumObjects) {
            isBiggerThanMax = true;
        } else {
            isBiggerThanMax = false;
        }

        Debug.Log("FILE NAME: Carousel.cs " + "MESSAGE: --- " + "Name of the first displayed object: " + carouselObjects[0].name);//just display the name of the first chosen element in the console

        if(isBiggerThanMax) {
            angle = diameter / (float) maximumObjects;
        } else {
            angle = diameter / (float) amountOfCarouselObjects;
        }

        for(int c = chosenObject; c < currentVisibleIndexes.Length; c++) {
            currentVisibleIndexes[c] = c;
        }//TODO: fix this loop

        float ObjectAngle = angle;//create a temp value that keeps track of the angle of each element

        //if(isBiggerThanMax) {
            for(int m = 0; m < amountOfCarouselObjects; m++) {
                objectAngles[m] = ObjectAngle;
                ObjectAngle += angle;
            }
        /*} else {
            for(int m = 0; m < amountOfCarouselObjects; m++) {
                objectAngles[m] = ObjectAngle;
                ObjectAngle += angle;
            }
        }*/
        

        for(int i = 0; i < amountOfCarouselObjects; i++) { //loop through the objects
            carouselObjects[i].transform.position = this.transform.position;//Reset objects to the postion of the carousel center
            carouselObjects[i].transform.rotation = Quaternion.identity; //make sure their rotation is zero
            carouselObjects[i].transform.parent = this.transform; // make the element child to the carousel center
            carouselObjects[i].transform.position = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z + distanceFromCenter);//move each carousel item from the center an amount of "DistanceFromCenter"

            if(i < maximumObjects) {
                carouselObjects[i].transform.RotateAround(positionCarousel, axisCarousel, objectAngles[i]);//position the element in their respective locations according to the center through rotation
            } else if(i >= maximumObjects && i < maximumObjects * 2) {
                carouselObjects[i].GetComponent<ClothingPieceHandler>().SetActiveness(false);
                carouselObjects[i].transform.RotateAround(positionCarousel, axisCarousel, objectAngles[i - maximumObjects]);
            } else {
                carouselObjects[i].GetComponent<ClothingPieceHandler>().SetActiveness(false);
                carouselObjects[i].transform.RotateAround(positionCarousel, axisCarousel, objectAngles[i -10]);
            }
        }

        //Make sure an element is perfectly centered.
        if(isBiggerThanMax) {
            if(maximumObjects % 2 != 0) {
                float rotateAngle = angle + angle / 2;
                transform.eulerAngles = new Vector3(transform.eulerAngles.x, rotateAngle, transform.eulerAngles.z);
                newAngle = rotateAngle;
            } else {
                transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle, transform.eulerAngles.z);
                newAngle = angle;
            }
        } else {
            if(amountOfCarouselObjects % 2 != 0) {
                float rotateAngle = angle + angle / 2;
                transform.eulerAngles = new Vector3(transform.eulerAngles.x, rotateAngle, transform.eulerAngles.z);
                newAngle = rotateAngle;
            } else {
                transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle, transform.eulerAngles.z);
                newAngle = angle;
            }
        }
    }

    // Update is called once per frame
    void Update() {
        Quaternion newRotation = Quaternion.AngleAxis(newAngle, Vector3.up); // pick the rotation axis and angle
        transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, speedOfRotation);  //animate the carousel

        if(OVRInput.GetDown(button, controller)) {
            Debug.Log("hey");
        }
    }

    public void rotateTheCarousel() {// call this function to rotate the carousel
        if(firstTime) {// if run the first time calcule the offset
            newAngle = transform.eulerAngles.y;
            newAngle -= angle;
            firstTime = false; // stop this piece of code from running in the future
        } else {
            newAngle -= angle; //calculate the new angle
        }

        //The code below was actually temporarily commented out by me because I was still working on fixing a bug in it before I came up with an alternate solution. I uncommented it for readability.
        if(isBiggerThanMax) {
            carouselObjects[currentVisibleIndexes[0]].GetComponent<ClothingPieceHandler>().SetActiveness(false);
            for(int i = 0; i < currentVisibleIndexes.Length; i++) {
                if(currentVisibleIndexes[i] >= amountOfCarouselObjects - 1) {
                    currentVisibleIndexes[i] = 0;
                } else {
                    currentVisibleIndexes[i]++;
                }
            }
            carouselObjects[currentVisibleIndexes[maximumObjects - 1]].GetComponent<ClothingPieceHandler>().SetActiveness(true);
            Debug.Log("FILE NAME: Carousel.cs " + "MESSAGE: --- " + "Carousel rotated to the right, current selected piece: " + carouselObjects[currentVisibleIndexes[0]].name); //show in the console the name of the selected element

            carouselObjects[currentVisibleIndexes[maximumObjects - 1]].transform.RotateAround(positionCarousel, axisCarousel, objectAngles[currentSpawnAngle]);
            Debug.Log("Current angle: " + objectAngles[currentSpawnAngle]);

            if(currentSpawnAngle >= maximumObjects - 1) {
                currentSpawnAngle = 0;
            } else {
                currentSpawnAngle++;
            } //TODO: make this neater

            if(currentVisibleIndexes[maximumObjects - 1] == amountOfCarouselObjects - 1) {
                //currentSpawnAngle = maximumObjects-1;
                //resetTime = true;
            }
        }
    }
}

Zoeken

Categorieën

  • Blendshapes
  • Concepting & Design
  • Group Coaching
  • Individual Coaching
  • Pressure Cooker
  • Programming

Meest recente berichten

Concepting & Design23 januari 2022
Introduction programming23 januari 2022
Individual coaching23 januari 2022
Group coaching23 januari 2022
Blend shapes23 januari 2022

Iris Oostra

LinkedIn – GitHub – GitHub(Oud) – DeviantArt

© 2024