It looks like you're trying to use Update as a Coroutine - a procedure that can suspend itself midway-through and pass control back to the game loop to keep updating/rendering frames, then resume where it left off later.
You can tell something is a Coroutine if it uses the yield return
construct to pause itself like this anywhere.
To make a method a coroutine, you need to change its signature from:
void MethodName() {
...
to:
IEnumerator MethodName() {
...
The reason is that under the hood, running a Coroutine is actually creating an object called an iterator that implements the IEnumerator interface — something we can call MoveNext()
on to make it step through a sequence of values/actions. That's how the engine pumps the coroutine each time it needs to be resumed. Instead of returning nothing (void
), calling the method returns this new iterator object, so the return type needs to match this.
Now, there's a bit of a danger in calling Update as a coroutine: Update is already called every frame!
So if some of those calls don't end in the same frame, but continue/suspend for some time, they could pile up very quickly. In this example, if the player rapidly tapped their mouse button for a second at 60fps, you could have increased your size 30 times over and have 30 copies of the coroutine waiting to resume.
Fortunately, the engine knows what a disaster this would be, and won't let us do it. Many MonoBehaviour messages are allowed to be marked as IEnumerator and called as coroutines, but Update is not one of them.
MonoBehaviour messages that can't be Coroutines include:
- Initialization:
Awake
, OnEnable
- Core update loop:
Update
, LateUpdate
, FixedUpdate
, OnGUI
- Shutdown:
OnDisable
, OnDestroy
(Obviously, after calling one of these, the object is likely not eligible to keep running a Coroutine the next frame)
So, what can we do instead?
Well, since Update is already running every frame, we don't need a while
loop to keep iterating. We can do that with the game loop itself, changing our while
to a simple if
:
public float inflationSpeed = 1f;
void Update() {
if (Input.GetMouseButton(0)) {
Vector3 newScale = transform.localScale;
// Use deltaTime to grow smoothly, a little each frame.
newScale += inflationSpeed * Time.deltaTime * Vector3.one;
transform.localScale = newScale;
}
}
Or, if you really want to do it in Coroutine style, growing in steps of 1 every second rather than smoothly every frame, you can do it like so:
public Vector3 growthPerStap = new Vector3(1,1,1);
public float stepDuration = 1f;
Coroutine inflation;
// Define a Coroutine method that inflates periodically.
IEnumerator Inflate() {
while(true) {
transform.localScale += growthPerStep;
yield return new WaitForSeconds(stepDuration);
}
}
// Check input each frame and stop/start the coroutine as needed.
void Update() {
// Stop the coroutine if it's running and the mouse button is released.
if(inflation != null && Input.GetMouseButtonUp(0)) {
StopCoroutine(inflation);
inflation = null;
}
// Start the coroutine if it's not running and the mouse button is pressed.
if(inflation == null && Input.GetMouseButton(0))
inflation = StartCoroutine(inflation);
}