0
\$\begingroup\$

As of now, I'm getting a C# error (CS1624) stating that:

The body of PlayerMovement.Update() cannot be an iterator block because void is not an iterator interface type.

The error is within the void Update section. I believe it started when I added lines 34 - 37, the second "if" clause.

Here's the code:

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

public class PlayerMovement : MonoBehaviour
{

    Animator m_Animator;
    bool m_Jump;

    public CharacterController2D controller;

    public float runSpeed = 40f;

    float horizontalMove = 0f;
    bool jump = false;

    void Start()
    {
        m_Animator = gameObject.GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;

        if (Input.GetButtonDown("Jump"))
        {
            jump = true;
            m_Animator.SetBool("Jump", true);
        }

        // These are lines 34-37 that cause the error, I think.    
        if (m_Animator.GetBool("Jump") == true)
        {
            yield return new WaitForSeconds(3);
            m_Animator.SetBool("Jump", false);
        }
    }

    void FixedUpdate()
    {
        controller.Move(horizontalMove * Time.fixedDeltaTime, false, jump);
        jump = false;
    }
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ yield return new WaitForSeconds(3); won't work as the error says. And you do not want your update to pause for 3sec in the first place, your whole Character would stop responsding. \$\endgroup\$
    – Zibelas
    Feb 19, 2021 at 7:56
  • \$\begingroup\$ I think you can either keep a float variable to use as a timer, which decreases by Time.deltaTime every frame, and when it elapses you can set your bool to false. yield return [something] only works within a Coroutine but I don't know if you'd want that in this scenario. If you are curious about it, I'd suggest you read the Unity documentation about Coroutines or checkout DMGregory's answer on gamedev.stackexchange.com/questions/189246/… \$\endgroup\$
    – D.Kallan
    Feb 19, 2021 at 8:21

2 Answers 2

1
\$\begingroup\$

When you want a method that can pause its execution and resume later in Unity, that's called a Coroutine, and it needs to be set up in a specific way. The return type has to be IEnumerator, not void.

That's why the compiler complains when you try to put a yield return statement inside a void Update - "void" is not a type that can be used as an iterator, which is what Unity's coroutines are built on.

If you change the return type to IEnumerator, Unity will tell you that the Update method is forbidden from being used as a coroutine. But that's OK, we can start our own named whatever we like.

Here's a version of your script with working syntax:

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

public class PlayerMovement : MonoBehaviour
{

    Animator m_Animator;
    bool m_Jump;

    public CharacterController2D controller;

    public float runSpeed = 40f;

    float horizontalMove = 0f;
    bool jump = false;

    void Start()
    {
        m_Animator = gameObject.GetComponent<Animator>();

        // Start our coroutine when the MonoBehaviour activates.
        StartCoroutine(JumpPoll());
    }

    IEnumerator JumpPoll()
    {
        while(true) {
            // If you don't want to lock horizontal input while jumping,
            // then move this line into FixedUpdate. It's OK to read axes there.
            horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;

            if (Input.GetButtonDown("Jump"))
            {
                jump = true;
                m_Animator.SetBool("Jump", true);

                // Pause this loop for 3 seconds while the rest of the game proceeds.
                yield return new WaitForSeconds(3);

                m_Animator.SetBool("Jump", false);
            }
            // Let the game run one frame, 
            // and resume next frame just like Update() does.
            yield return null;
        }
    }

    void FixedUpdate()
    {
        // Time.deltaTime automatically returns fixedDeltaTime
        // when accessed from FixedUpdate, so it's good to just use that
        // (less risk of errors when moving code or calling it from other places).
        controller.Move(horizontalMove * Time.deltaTime, false, jump);
        jump = false;
    }
}
\$\endgroup\$
0
\$\begingroup\$

Quick solution but for other wait operations you might want to use a Coroutine. Invoke is not a real timer or counter but the second parameter allows you to specify the time amount when the call should be executed. In this case the function ResetJump will run 3 seconds later and reset your jump.

void Update() {
    horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
    if ((Input.GetButtonDown("Jump")) && (jump == false))
    {
        jump = true;
        m_Animator.SetBool("Jump", true);
        Invoke("ResetJump", 3f);
    }
}

private void ResetJump() {
    jump = false;
    m_Animator.SetBool("Jump", false);
}  
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .