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.
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.
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;
}
}
}
}