Golem Collection Revisited

I made a lot of progress visually and mechanically. I have refactored a lot of code and systems. Splitting scripts apart into single function scripts. I created lines of communication between scripts and objects. And finally started to populate the level with assets.

Assets


Updating the assets really brought the game to life. All of the objects were made in 3d Studio Max and brought into Unity. For now I will be sticking with the low poly look with solid colors.

playerbehavior.cs


One of the first major refactors to the code was to separate scripts. Moving from two main scripts to five. I moved a lot of code out of the player controller and into a player behavior script. The player behavior interacts with the game world and updates components.

It is a cleaner go between for accessing information in other scripts and components. If a script needs to access another script on the player, it runs through the player behavior.

  public void SetMaxScore(int maxScore) {
    UIAPI.SetMaxScore(maxScore);
  }
  public void UsePowerUp() {
    PowerUpCount--;
    UIAPI.SetPowerUp(PowerUpCount);
  }
  public void AddPowerUp() {
    PowerUpCount++;
    UIAPI.SetPowerUp(PowerUpCount);
  }
  public int GetPowerUpCount() {
    return PowerUpCount;
  }
  public void AddScore() {
    PlayerScore++;
    UIAPI.UpdateScore(PlayerScore);
  }
  public void HitPlayer() {
    PlayerScore -= HitPlayerCost;
    UIAPI.UpdateScore(PlayerScore);
  }
  public void HitByPlayer() {
    PlayerScore -= PlayerHitCost;
    UIAPI.UpdateScore(PlayerScore);
  }

colorchange.cs


Initially I handled the players score by keeping track of how long the player moved. This was always temporary. The color change script was talking top three others and overly complicated as you can see from the old code above. In combination with the player behavior script to communicate with other scripts, the old code is much cleaner.

The old code…

void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
      FillLevel -= JumpCost;
      UpdateColor();
    }
    if (Input.GetKey(KeyCode.W)) {
      FillLevel += FillSpeed;
      UpdateColor();
    }
  }

public void UpdateColor(int playerScore) {
    if (FillLevel >= 1.0f) {
      // temp to prevent nullreference
      if (ObjFillIndex < ObjCount - 1) {
        Debug.Log(ObjFillIndex);
        ObjFillIndex++;
        UIAPI.AddScore();
      } else {
        ObjFillIndex = ObjCount - 1;
        Debug.Log("Player won!");
        return;
      }
      FillLevel = 0f;
    }
    GScript.GolemPieces[ObjFillIndex].GetComponent<Renderer>().material.color = 
    Color.Lerp(StartColor, EndColor, FillLevel);
    
    MP.GrowProgressBar(ObjFillIndex + 1);
  }

public void HitByPlayer() {
    Debug.Log("Player Hit!");
    int NewFillIndex = ObjFillIndex - PlayerHitPenalty;
    Debug.Log(NewFillIndex);

    while (ObjFillIndex > NewFillIndex && ObjFillIndex >= 0) {
      GScript.GolemPieces[ObjFillIndex].GetComponent<Renderer>().material.color = StartColor;
      ObjFillIndex--;
      UIAPI.SubtractScore();

      MP.GrowProgressBar(ObjFillIndex + 1);
    }
    FillLevel = 0f;
  }

The new code…

public void SetScorePerChunk(int scorePC) {
    scorePerChunk = scorePC;
  }
public void UpdateColor(int playerScore) {
    fillIndex = playerScore / scorePerChunk;
    fillLevel = playerScore % scorePerChunk;
    GScript.GolemPieces[fillIndex].GetComponent<Renderer>().material.color 
              = Color.Lerp(StartColor, EndColor, fillLevel);
  }

Two functions and no longer using the update method. All updates are called in the player behavior.

uiapi.cs


I created a script to be a communicator between the UI and the player. Action goes into the player behavior and the UI elements are updated from there.

  private void Awake() {
    UI = GameObject.Find("Canvas").GetComponent<Canvas>();
    txt = UI.transform.Find("PowerUpCounter").GetComponent<Text>();
    MP = UI.transform.Find("ProgressBar").GetComponent<MossProgress>();
    txt.text = "0";
  }

  public void SetMaxScore(int MaxScore) {
    MP.SetMaxValue(MaxScore);
  }

  public void UpdateScore(int score) {
    MP.UpdateScore(score);
  }

  public void SetPowerUp(int PowerUpCount) {
    txt.text = PowerUpCount.ToString();
  }

Player Color Change and Grass Collection


The grass that the player rolls over and collects is a prefab of objects with only mesh renderers. The prefab has a capsule collider that acts as trigger when the player is detected. The code is similar to the power up. Using the OnTriggerEnter function, if the object tag matches the player, it disables the mesh renderer, and begins a coroutine to reappear. Eventually I would like it to reappear while scaling to look like its regrowing.

private void Awake() {
    Active = true;
    numMeshes = transform.childCount;
    MR = new MeshRenderer[numMeshes];

    for (int i = 0; i < numMeshes; i++) {
      MR[i] = transform.GetChild(i).GetComponent<MeshRenderer>();
    }
  }

  private void OnTriggerEnter(Collider other) {
    if (other.gameObject.tag == "Player") {
      if (Active) {
        PlayerBehavior playerBehavior = other.GetComponent<PlayerBehavior>();
        Debug.Log("Grass");
        for (int i = 0; i < numMeshes; i++) {
          MR[i].enabled = false;
        }
        Active = false;
        StartCoroutine(Dissappear());
        playerBehavior.AddScore();
      }
    }
  }

  IEnumerator Dissappear() {
    yield return new WaitForSeconds(GrassRespawnTime);
    for (int i = 0; i < numMeshes; i++) {
      MR[i].enabled = true;
    }
    Active = true;
  }

Because the prefab was made of multiple objects, I made an array of mesh renderers. When the collision is detected, the function loops through the array and disables them.

This is not quite what I had in mind at first. Initially I wanted to change the material using vertex shaders, but after hours of research I could not figure out a good solution. I am happy with what I have so far and will continue to optimize and refactor.

Project Links


You can follow my project on GitHub here: https://github.com/Scottin3d/GolemPlayground