Wiring up progress bars for points and timer

Hi there. I have only been exploring Unity for a few weeks, so this question might be a simple one. I am making a platformer game, where a monster character eats fruit to gain points before the time runs out. I have made some progress bars but don’t know how to rig them up to talk to each other. I have used a few different tutorials, so I’m a little muddled of where to put things properly. Right now, when I press play, the timer counts down but the bar goes directly to zero. Ideally, I would like the bar to read Time Remaining: (min:sec). For the points bar, I think I just don’t know where to hook up the score value. I have a PlayerController, but the code for it doesn’t update the points bar. I would be eternally grateful for any help.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BarScript : MonoBehaviour {
   

    private float fillAmount;

    [SerializeField]
    private float lerpSpeed;

    [SerializeField]
    private Image content;

    [SerializeField]
    private Text valueText;

    [SerializeField]
    private Color fullColor;

    [SerializeField]
    private Color lowColor;

    [SerializeField]
    private bool lerpColors;

    public float MaxValue { get; set; }

    public float Value{
        set{
            string[] tmp = valueText.text.Split (':');
            valueText.text = tmp [0] + ": " + value;
            fillAmount = Map (value, 0, MaxValue, 0, 1);
        }
    }

    // Use this for initialization
    void Start () {
        if(lerpColors){
            content.color = fullColor;
        }
       
    }
   
    // Update is called once per frame
    void Update () {
        HandleBar ();
       
    }

    private void HandleBar(){
        if (fillAmount != content.fillAmount) {
            content.fillAmount = Mathf.Lerp (content.fillAmount, fillAmount, Time.deltaTime * lerpSpeed);
        }
        if (lerpColors) {
            content.color = Color.Lerp (lowColor, fullColor, fillAmount);
        }

    }

    private float Map(float value, float inMin, float inMax, float outMin, float outMax){
        return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

[Serializable]
public class Stat  {

    [SerializeField]
    private BarScript bar;

    [SerializeField]
    private float maxVal;

    [SerializeField]
    private float currentVal;

    public float CurrentVal{

        get{
            return currentVal;
        }

        set{
           
            this.currentVal = Mathf.Clamp(value,0, MaxVal);
            bar.Value = currentVal;
        }
    }

    public float MaxVal {

        get{
            return maxVal;
        }

        set{

            this.maxVal = value;
            bar.MaxValue = maxVal;
        }
    }

    public void Initialize(){
        this.MaxVal = maxVal;
        this.CurrentVal = currentVal;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CountDownTimer : MonoBehaviour {

    public string levelToLoad;
    private float timer = 180f;
    private Text timerSeconds;


    // Use this for initialization
    void Start () {
        timerSeconds = GetComponent<Text> ();
       
    }
   
    // Update is called once per frame
    void Update () {
        timer -= Time.deltaTime;
        timerSeconds.text = timer.ToString ("f2");
        if (timer <= 0) {
            Application.LoadLevel (levelToLoad);
        }
       
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour {

    public Vector2 moving = new Vector2 ();

    [SerializeField]
    private Text scoreText;

    private int score;


    // Use this for initialization
    void Start () {
        score = 0;
        UpdateScore ();
    }
   
    // Update is called once per frame
    void Update () {
   
        moving.x = moving.y = 0;

        if (Input.GetKey ("right")) {
            moving.x = 1;
        } else if (Input.GetKey ("left")) {
            moving.x = -1;
        }

        if (Input.GetKey ("up")) {
            moving.y = 1;
        } else if (Input.GetKey ("down")) {
            moving.y = -1;
        }
    }

    public void AddScore(int newScoreValue){
        score += newScoreValue;
        UpdateScore ();
    }

    void UpdateScore (){
        scoreText.text = "Fruit Points: " + score;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Collectable : MonoBehaviour {
    public int scoreValue;
    private PlayerController playerController;

    // Use this for initialization
    void Start () {
        GameObject playerControllerObject = GameObject.FindWithTag ("PlayerController");
        if (playerControllerObject != null) {
            playerController = playerControllerObject.GetComponent<PlayerController>();
        }
        if (playerController == null) {
            Debug.Log ("Cannot find 'PlayerController' script");
        }
    }
   
    // Update is called once per frame
    void Update () {
       
    }
    void OnTriggerEnter2D(Collider2D target) {
        if (target.gameObject.tag == "Player") {
            playerController.AddScore (scoreValue);
            Destroy (gameObject);
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerBar : MonoBehaviour {

    [SerializeField]
    private Stat fruitPoints;

    [SerializeField]
    private Stat timeLeft;

    private float CountDownTimer;
    private float currentTime = 180f;

    private int ScoreValue;
    private int currentPoints = 0;

    private void Awake(){
        fruitPoints.Initialize ();
        timeLeft.Initialize ();

    }


   
    // Update is called once per frame
    void Update () {
        if (ScoreValue > currentPoints) {
            fruitPoints.CurrentVal += 5;
            currentPoints += 5;
        }
        if (CountDownTimer < currentTime) {
            timeLeft.CurrentVal -= 1;
            currentTime -= 1;
        }
   
    }
}

I’m mostly trying to update the values of the bar in PlayerBar script. Perhaps there is a way of combining these scripts together or I have to put them in a different place? I’m so new that I don’t know exactly how to do that without breaking it yet.

I’m on my phone so it’s hard to copy and paste your code with my suggestion added. But on script PlayerBar you could simply add: public Image scoreImage; and assign your score bar to it in the inspector.
Then where you currently are checking to see if it changed :

if (ScoreValue > currentPoints)
{
        fruitPoints.CurrentVal += 5;
        currentPoints += 5;
        scoreImage.fillAmount = currentPoints / 100
}

As fillAmount is based between 0 and 1 you need to divide your current score by max score. So for example if you were trying to reach a score of 100 you would add the above code. Using this technique you can do the same for time.
Perhaps have a float called myTimer that increases every FixedUpdate that you would then divide by maxTime. You would then take 1 - that number because we want it to decrease from a full bar. So:

private float myTimer = 0f;
private float maxTime = 180f;
public Image timeImage;


public FixedUpdate()
{
      myTimer += Time.deltaTime;
      timeImage.fillAmount = 1 - (maxTime / myTimer)
}

Thanks for the reply! I really appreciate it!
The explanation of the math function makes a lot of sense. I’m happy that that line of thinking was clarified.

Still not updating though, on either of them. Perhaps I should call scoreUpdate() in the Player bar as well as the Collections script? Would that make a difference? Also, MaxVal class won’t import. Changing it to a public variable in my stat script doesn’t seem to work.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerBar : MonoBehaviour {

    public Image scoreImage;
    public Image timeImage;

    private MaxVal maxVal;

    [SerializeField]
    private Stat fruitPoints;

    [SerializeField]
    private Stat timeLeft;

    private float CountDownTimer;
    private float myTimer = 0f;
    private float maxTime = 180f;


    private int ScoreValue;
    private int currentPoints = 0;

    private void Awake(){
        fruitPoints.Initialize ();
        timeLeft.Initialize ();

    }

    public void FixedUpdate(){
        myTimer += Time.deltaTime;
        timeImage.fillAmount = 1 - (maxTime / myTimer);
    }


   
    // Update is called once per frame
    void Update () {
        if (ScoreValue > currentPoints) {

            fruitPoints.CurrentVal += 5;
            currentPoints += 5;
            scoreImage.fillAmount = currentPoints / maxVal;
            //call scoreUpdate() here?

        }
   
    }
}

Thank you for introducing the concept of FixedUpdate too! That is something that I will definitely put in my tool belt.

Something’s definitely wrong with your logic there. Both ScoreValue and currentPoints are private variables, so we know nobody else is messing with them… but there’s nothing in this code that ever changes either of them, except inside the if block on line 42, but that will never execute because ScoreValue will never be > currentPoints.

What exactly are you trying to accomplish with that Update method? And how do you expect your ScoreValue and currentPoints to get set or changed?

Am I importing ScoreValue variable from the Collectible script by declaring it that way at the top of the PlayerBar code, ie:
private int ScoreValue;

or is that not how this works? I’m sorry that I’m so new to this. I’ve tried to Frankenstein a few tutorials together, and it’s coming around to bite me. But basically, that’s how I want it to update. It would be the variable from the Collectable script, that is called and updated in there, and brought over to PlayerBar. And then on line 45,
currentPoints += 5;

means that currentPoints would get changed by incrementing by 5?
That’s where my logic was, but I would love insight into how to fix it.

That is not how it works. You’re not importing anything. You’re declaring a new, private ScoreValue that no other object in the game has access to (because it’s private). And because you never assign to it, its value will always be 0.

And because it’s always 0, your line 45 will never run (the condition checked on line 42 will never be true). So it doesn’t matter what line 45 says.

I’m afraid I still don’t understand what PlayerBar is supposed to be doing. You already have CountdownTimer showing the time, and PlayerController updating the score, so what more is PlayerBar supposed to do, exactly? It sort of looks like it’s trying to do both those things, but that doesn’t make sense to me.

Ahhhh…That makes sense. I will try with the scoreValue public variable from the collectables script.
So PlayerBar is the UI component. It has a slider bar and text value that displays the points to the player. Every time the player jumps on a fruit sprite, it is supposed to give the player 5 points, and this will show on the sliderbar.

but sadly, it stays at 0.
same with the timer. The timer records the time, but the UI component, like the one above, just goes to 0, and doesn’t visually represent the time going down from full. so I need the scripts to talk to each other, so that the ui component visually represents your progress.
full bar before I press play:

when I press play:

not only does ‘time left’ disappear, but the green bar instantly goes to zero, even though there is plenty of time left. I want the bar to show the progression of the time counting down, but am having trouble.

OK, it seems like you have several issues; let’s try to focus on just one at a time. How about the timer first.

I see your CountdownTimer script above. It looks fine to me, though of course it only updates a Text, not the bar. But let’s go with it anyway. Do you actually have this script attached to a Text in your UI? What happens?

If it doesn’t work, can you verify that there isn’t something else also mucking with that same Text? Maybe just create an entirely new Text on your canvas somewhere, and attach CountdownTimer to it. Does that work?

If we get that working, we’ll talk about bars next… and I’ll encourage you to throw out your Stat class, and probably your Bar class too. :slight_smile: Trying to do too much at once, especially as a beginner, is a recipe for failure. Always best to start as simple as possible, and then add functionality bit by bit, making sure everything still works at each step.