Incrementing Variable in Update - Complete Beginner following a "Flappy Bird" Tutorial but doing my own thing as I go

I am trying to get the variable pipeMoveSpeed to increment slowly as the game progresses. It is the script component for a pipe prefab that gets instantiated by a spawner object. I am 100% sure there is an answer somewhere to this, but I have spent 2 days searching, and I don’t know what question to search to get an answer.

This is my best attempt, but the pipeMoveSpeed variable ramps up exponentially and I just want it to increment 1 unit every x seconds.

Here is the script for the pipe prefab:

using UnityEngine;
using UnityEngine.InputSystem.Controls;

public class PipeScript : MonoBehaviour
{
    public float pipeMoveSpeed = 10;
    public float deadZone = -45;
    public LogicScript logic;//reference pipeIncrementer from logicScript in Logic Script Game Object

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //Increment pipeMoveSpeed when pipeIncrementer increments
        pipeMoveSpeed += GameObject.FindGameObjectWithTag("Logic").GetComponent<LogicScript>().pipeIncrementer;
        //Debug.Log($"pipeMoveSpeed is: {pipeMoveSpeed.ToString()}");

        //move pipe prefab at set rate based on pipeMoveSpeed at time of initialization
        transform.position += (pipeMoveSpeed * Vector3.left * Time.deltaTime);

        //call Destroy() function when pipes exit game screen
        if (transform.position.x < deadZone)
        {
            Destroy();
        }
    }
    //Destroy GameObject
    void Destroy()
    {
        Debug.Log("Pipe Deleted");
        Destroy(gameObject);
    }
}

and here is the Logic Script:

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Runtime.CompilerServices;

public class LogicScript : MonoBehaviour
{
    public int playerScore;
    public Text Score;
    public float gameClock;
    public TMP_Text GameClock;
    public float pipeIncrementer;

    void Update()
    {
        float pipeTimer = gameClock % 5;
        Debug.Log($"pipetimer: = {pipeTimer.ToString()}");

        if (pipeTimer >= 1)
        {
            pipeIncrementer++;
            Debug.Log($"pipeIncrementer Incremented to: {pipeIncrementer}");
            pipeTimer = 0;
        }

        gameClock += Time.deltaTime;
        int minutes = Mathf.FloorToInt(gameClock / 60F);
        int seconds = Mathf.FloorToInt(gameClock - minutes * 60);

        GameClock.text = string.Format("{0:0}:{1:00}", minutes, seconds);
    }

    [ContextMenu("Add Score")]
    public void AddScore()
    {
        playerScore++;
        Score.text = playerScore.ToString();
    }
}

I assume you’re following Game Maker’s Toolkit’s tutorial?

What would work better is to move the responsibility of setting the speed of the pipes to the object that spawns them. Since this will also live in the same scene as your LogicScript (not a good script name), it can reference it via the inspector and read the necessary values to determine its movement speed.

Or the spawner component can handle this internally itself.

1 Like

Yeah that’s the one!

I wondered about moving the velocity transform into the spawner object, but I am trying to follow the tutorial while branching out and building upon it to learn new concepts, and re-inforce what I’m learning through the tutorial.

I am still stuck with how to increment the pipeMoveSpeed variable in conjunction with Time.deltatime.

It seema like delta time is a powerful tool in setting things up for an uncapped framerare and syncing different objects to each other. Am I inccorect in this assumption?

Time.deltaTime is indeed used to step logic in a way that is independant from framerate, even when Unity’s Update functions on a per-frame basis.

Your timer code is fine. The issue is how you’re applying the pipeIncrementer value. Instead of ramping up your base movement speed, you’ll need to calculate the necessary value on an as-need basis.

So forgoing the idea of moving this into the spawner, you’d do something like this:

public class PipeScript : MonoBehaviour
{
	public float pipeMoveSpeed = 10;
	public float deadZone = -45;
	
	private LogicScript _gameLogic;

	void Start()
	{
		_gameLogic = Object.FindFirstObjectByType<LogicScript>();
	}

	void Update()
	{
		float moveSpeed = _gameLogic.pipeIncrementer + pipeMoveSpeed;

		transform.position += (moveSpeed * Vector3.left * Time.deltaTime);

		if (transform.position.x < deadZone)
		{
			Destroy();
		}
	}

	void Destroy()
	{
		Debug.Log("Pipe Deleted");
		Destroy(gameObject);
	}
}

Idea being we calculate a movement speed based on the defined movement speed and the incremented value.

Some notes, if you need to look up something, do it once. Here I do it in Start(), and also use the strongly typed FindFirstObjectByType<T> method to avoid magic strings being a factor in our logic. Note that this is still not the best practice, and any dependencies should be provided by the object spawning these, but that’s outside the scope of this thread.

1 Like

Thank you for taking the time to respond and give me advice – lots of good nuggets and terminology to help me on my way!

I guess I was confused by referencing the game logic in start, and then getting my variable from it updated every frame. I assumed it would only reference the variable as it was the moment Start() was called.

As far as FindFirstObjectByType, how does it differ from FindFirstObjectByTag? As in, what benefit does it give me over the latter, or is it just a more standard/more powerful way of looking up information in a seperate game object? Also, is it more resource intensive to stop my reference at the entire game object, or to further clarify what it is I’m looking up in the game object? I.e:

_gameLogic = Object.FindFirstObjectByType<LogicScript>();
         ///entire component LogicScript

vs

_gameLogic = Object.FindFirstObjectByType<LogicScript>().pipeIncrementer;
          ///one variable pipeIncrementer

or am I misunderstanding or something?

Oh, and I see you use _gameLogic for the variable name. Is this a thing of style and how you were taught/learned, or is there a specific reason you used it instead of just gameLogic (which is a better variable name for sure, and since I use parts of my gameLogic script in other scripts as well, I should probably even go further and name it like _pipeCloneGameLogic – right?).

Lastly, do you have a recommendation for where I should go next as far as learning? Paid information is probably not possible - so I am looking for free tutorials/guides/etc. to help me on my way.

Thanks again for your help!

Doing tutorials and then extending and adding features is an AWESOME way to learn.

Just make sure you have a solid understanding of what all the parts do before you go on trying new stuff.

Also, try and avoid code like this:

_gameLogic = Object.FindFirstObjectByType<LogicScript>();

and instead drag it in. Here’s why:

Keep in mind that using GetComponent() and its kin (in Children, in Parent, plural, etc) to try and tease out Components at runtime is definitely deep into super-duper-uber-crazy-Ninja advanced stuff.

Here’s the bare minimum of stuff you absolutely MUST keep track of if you insist on using these crazy Ninja methods:

  • what you’re looking for:
    → one particular thing?
    → many things?
  • where it might be located (what GameObject?)
  • where the Get/Find command will look:
    → on one GameObject? Which one? Do you have a reference to it?
    → on every GameObject?
    → on a subset of GameObjects?
  • what criteria must be met for something to be found (enabled, named, etc.)
  • if your code expects one instance and later you have many (intentional or accidental), does it handle it?

If you are missing knowledge about even ONE of the things above, your call is likely to FAIL.

This sort of coding is to be avoided at all costs unless you know exactly what you are doing.

Botched attempts at using Get- and Find- are responsible for more crashes than useful code, IMNSHO.

If you run into an issue with any of these calls, start with the documentation to understand why.

There is a clear set of extremely-well-defined conditions required for each of these calls to work, as well as definitions of what will and will not be returned.

In the case of collections of Components, the order will NEVER be guaranteed, even if you happen to notice it is always in a particular order on your machine.

It is ALWAYS better to go The Unity Way™ and make dedicated public fields and drag in the references you want.

1 Like

Cool, thanks for the info!

The reason we use the GetComponent() in this tutorial is because the objects are spawning in as a clone of a prefab from a spawner, and are then responsible for getting information from our logic script.

As spiney199 recommended above - it probably would make more sense for the spawner to keep track of the specifics and pass them on to the clones as the game runs.

I will try to re-write this with spiney199’s method once I complete the tutorial, and then I can drag and drop variable references like you suggest!

You care more about the component, not the tag. So you may as well go straight for the component, and then the tag becomes superfluous. Getting a game object via tag still means you have to GetComponent<T> for it.

Game objects are just containers for components. They don’t do anything on their own. You generally always want a reference to a component on a game object, and not the game object itself.

Tags are a feature from Unity’s very early days and honestly a crutch for new users. Focus on components, forget tags. Consider them just an old legacy feature.

We’re referencing the component not the game object. Note the word reference is important here. We have a pointer to the component’s place in memory, so we can access it as we need, which will reflect any internal changes in the object.

If we only stored the pipeIncrementer integer, which is a value type, then we only have a copy, and not a reference, and won’t see any changes in the data.

Further reading:

Just a coding style I picked up. I use it for private fields to make it easier to access them via the code editor, as they all show up in intellisense when you just enter and underscore.

This is where I started: Junior Programmer Pathway - Unity Learn

Kurt if you read the thread you’d realise these pipes are being spawned at runtime, so dragging in a reference to a game object already in a scene isn’t going to work.

2 Likes

Thank you so much for taking the time to answer my questions so in-depth! Have a happy Thanksgiving!

1 Like