After feedback from Remco I decided that I wanted to redo some of my code to make it more reusable and logical. I wanted to do this because he said that game development students most of the time always do things that they already can do and thus do not get a high mark on improving their development skills. One of things I think I can improve on is writing better code and not just doing it the easy way, because the easy way always turns into a hard time later on. Things I did were making better debug-logs, removing redundant scripts because they were only one method (and belonged in another file).
Duplicate by grab
To make the clothing dress-up work better I made it so that you have two types of gameObjects. One is for in the environment, for example on Bob or in the carousel and the other is the one you can use. They both have the same script named ClothingPieceHandler and this script controls objects from the script ClothingPiece.
Once you try to grab a clothing piece that is of the environment type there is a duplicate made and this is the one you can throw on the avatar to dress it. Once a throwable type object hits the avatar (or the clothing rack) the environment type object that is already on the (but inactive) will be set active. At the same time the throwable piece will disappear and the object on the avatar will have its collider turned on. Otherwise if all colliders are always on you get problems with the throwable piece bouncing off of the avatar.
I also made this because it made it easier to respawn things on the carousel at the correct place and it made it easier to make the clothing on the avatar grabable. This was easier because the objects will always stay at the same place.
String manipulation
I have only done string manipulation in C++ and web-based languages and have never actually needed it in C# before, so when I needed it this time I was struck with: “I know how but I don’t”, so this was definitely something new for me and something that is useful in my opinion, because I used it for searching for the carousels and for searching for clothing pieces. I learned how to do this from here and here.
private string RemoveEndOfString(string stringToTrim, string removeThis) {
string outputString = stringToTrim;
int positionWordToRemove = stringToTrim.IndexOf(removeThis);
if(positionWordToRemove >= 0) {
outputString = outputString.Remove(positionWordToRemove);
outputString.TrimEnd();
}
return outputString;
}
Moving code to methods and removing redundant scripts
Because of working without thinking I made some tiny script that were only one method and I moved those to the ClothingPiece class, this class has everything that has something to do with the clothing pieces like respawning on the carousels and despawning after a certain amount of time.
Clothing rack
I made the code for the clothing rack in the same way as the code for the dressing of the avatar, but this time I made it independent of the type of clothing/if something was already there, because all clothing pieces should be able to hand on the rack at the same time.
Better debugs
I had a lot of debug.logs running when making the dress-up system, but eventually I did not know what log was coming from where so I decided to give them structure by having the file-name in the message and using the same format every time. I also changed some messages to be more clear and not show a single variable but also type what is being tested.
For example, from the ClothingPiece class:
Debug.Log("FILE NAME: ClothingPiece.cs " + "MESSAGE: --- " + "Duplicate of " + this.FindRealName() + " is made");
Conventions and consistency
One of the things Remco said that really stood out to me was having better names etcetera in our project, these are things like variable names but also names in the scene. The issues were that some names meant nothing (for example a bush prefab was named “we”), capitalization and consistency in language (English/Dutch). This was actually feedback for the whole group, because I just didn’t pay attention enough to how the others were doing it, but I took it upon myself to fix it. I also send the group a message to ask them to please pay attention to it, to again tell them how important this is (I also did this at the start of the project but understandably people forgot it because working in Unity/Git/Etc was very new for them). I also put some other things in that message, because these were some reasons that our git repository broke. (I explained more in detail in a group call.)
Reflection
I learned a lot of techniques that I can start using from the start of every project. For instance the debug messages and better naming. I still haven’t used enough comments in my code in my opinion, so this is something I will make a learning goal in a next project. One thing I still have trouble with is I don’t want to have to continuously nag everyone in the group about things like naming/code conventions (which is why I tried to do that not as much during this project) but it always turns out that it would have been better if I did.
The code
using UnityEngine;
public class ClothingPieceHandler : MonoBehaviour {
private ClothingPiece clothingPiece/* = new ClothingPiece()*/;
[SerializeField]
private bool setIsThrowable;
[SerializeField]
private GameObject setThrowableVersion;
// Start is called before the first frame update
void OnEnable() {
clothingPiece = new ClothingPiece(gameObject, setIsThrowable, setThrowableVersion);
}
private void Update() {
if(clothingPiece.IsCounting) {
clothingPiece.TimerCheck();
}
}
public string GetRealName() {
return clothingPiece.PieceName;
}
public void SetActiveness(bool value) {
clothingPiece.SetOnOff(value);
}
public void RespawnOnCarousel(string searchName) {
clothingPiece.CarouselRespawn(searchName);
}
public void ToDoWhenEnterGrab() {
clothingPiece.EnterGrab();
}
public void ToDoWhenExitGrab() {
clothingPiece.ExitGrab();
}
}
using System;
using UnityEngine;
[Serializable]
public class ClothingPiece {
private string _environmentPieceIdentifierString = "Environment";
private string _throwableIdentifierString = "Throwable";
private string _cloneIdentifierString = "(Clone)";
private string _space = " ";
private string _pieceName;
private GameObject _thisGameObject;
private bool _isThrowable;
private GameObject _throwableVersion;
private string _carouselName = "Carousel";
private GameObject _correspondingCarousel;
/// <summary>
/// Variables for despawning the object when it has not been in hand for X seconds
/// </summary>
//TODO: maybe make this editable
private int _amountOfSecondsTillDespawn = 10;
//Timer
private float _timer = 0.0f;
private int _zero = 0;
//TODO: make these two in own function
private int _timerInSeconds = 0;
private int _amountOfMSInASecond = 60;
private bool _isCounting = false;
public string PieceName {
get {
//return _pieceName;
return FindRealName();
}
}
public bool IsCounting {
get {
return _isCounting;
}
}
public ClothingPiece(GameObject givenGameObject, bool isThrowable, GameObject throwableVersion) {
_thisGameObject = givenGameObject;
//ond werkt niet
_pieceName = FindRealName();
_correspondingCarousel = FindCarousel();
_isThrowable = isThrowable;
_throwableVersion = throwableVersion;
}
public void EnterGrab() {
if(_isThrowable) {
ResetTimer();
TurnKinematicOffOrOn(true);
} else {
if(!CheckIfDuplicateExists() && CheckIfChildActive()) {
Debug.Log("FILE NAME: ClothingPiece.cs " + "MESSAGE: --- " + "Duplicate of " + this.FindRealName() + " is made");
CreateDuplicate();
}
}
}
public void ExitGrab() {
if(_isThrowable) {
TurnKinematicOffOrOn(false);
StartTimer();
}
}
private void StartTimer() {
_isCounting = true;
}
private void ResetTimer() {
_isCounting = false;
_timer = _zero;
_timerInSeconds = _zero;
}
public void TimerCheck() {
_timer += Time.deltaTime;
_timerInSeconds = (int) _timer % _amountOfMSInASecond;
if(_timerInSeconds >= _amountOfSecondsTillDespawn) {
Despawn();
}
}
public void SetOnOff(bool value) {
GetChildObject().SetActive(value);
GetBoxCollider().enabled = value;
}
private GameObject GetChildObject() {
return _thisGameObject.transform.GetChild(0).gameObject;
}
private Collider GetBoxCollider() {
return _thisGameObject.GetComponent<BoxCollider>();
}
private void Despawn() {
string destroyedName = FindRealName();
GameObject.Destroy(_thisGameObject);
//TODO: test if next line still works
CarouselRespawn(destroyedName);
}
public void CarouselRespawn(string carouselPieceName) {
//IDK!?
_correspondingCarousel = FindCarousel();
_correspondingCarousel.GetComponent<RespawnClothing>().CheckIfPieceNeedsActivation(carouselPieceName);
}
private void TurnKinematicOffOrOn(bool value) {
_thisGameObject.GetComponent<Rigidbody>().isKinematic = value;
}
private bool CheckIfChildActive() {
bool returnBool = false;
if(GetChildObject().activeInHierarchy) {
returnBool = true;
} else {
returnBool = false;
}
return returnBool;
}
private void CreateDuplicate() {
GameObject duplicateGameObject = GameObject.Instantiate(_throwableVersion);
duplicateGameObject.transform.position = _thisGameObject.transform.position;
Debug.Log("FILE NAME: ClothingPiece.cs " + "MESSAGE: --- " + "Making a duplicate");
SetOnOff(false); //Does this work?
}
private bool CheckIfDuplicateExists() {
bool doesItExist = false;
string nameToSearchFor = _pieceName + _throwableIdentifierString + _cloneIdentifierString;
if(GameObject.Find(nameToSearchFor) == null) {
doesItExist = false;
} else {
doesItExist = true;
}
return doesItExist;
}
public string FindRealName() {
string realName = _thisGameObject.name;
if(_isThrowable) {
string throwableEndName = _throwableIdentifierString + _cloneIdentifierString;
realName = RemoveEndOfString(realName, throwableEndName);
} else {
realName = RemoveEndOfString(realName, _environmentPieceIdentifierString);
}
return realName;
}
public GameObject FindCarousel() {
string nameToSearchFor =/* _thisGameObject.tag + _space +*/ _carouselName;
Debug.Log("FILE NAME: ClothingPiece.cs " + "MESSAGE: --- " + "Name of the carousel we are searching for: " + nameToSearchFor);
GameObject foundCarousel = GameObject.Find(nameToSearchFor);
return foundCarousel;
}
private string RemoveEndOfString(string stringToTrim, string removeThis) {
string outputString = stringToTrim;
int positionWordToRemove = stringToTrim.IndexOf(removeThis);
if(positionWordToRemove >= 0) {
outputString = outputString.Remove(positionWordToRemove);
outputString.TrimEnd();
}
return outputString;
}
}