Peter Hopkins
Peter Hopkins

Reputation: 33

How do I call a Co-Routine in a Scriptable Object [Unity3D]

I have a coroutine which I'm trying to make a dash script from (using the unity New Input System & standard unity CharacterController) and calling the Coroutine in the scriptable object makes unity seize up.

If I were to put the controller.Move() in Activate() it would work but blink instead of dash. I've tried to use a coroutine to avoid this, but I can't use coroutines in ScriptableObjects so instead I made a blank MonoBehaviour called MonoInstance (which you can see below). I use this solely to call the StartCoroutine function from.

This crashes Unity, now my head hurts. Help


Tl;Dr - Unity crash. How make not crash. Brain hurt.


Ability

public class Ability : ScriptableObject
{
    public new string name;
    public float cooldownTime;
    public float activeTime;

    public virtual void Activate(GameObject parent) { }
}

DashAbility

[CreateAssetMenu]
public class DashAbility : Ability
{
    private PlayerController movement;
    private CharacterController controller;

    public float dashVelocity;

    public override void Activate(GameObject parent)
    {
        movement = parent.GetComponent<PlayerController>();
        controller = parent.GetComponent<CharacterController>();

        MonoInstance.instance.StartCoroutine(Dashing());
    }

    IEnumerator Dashing()
    {
        float startTime = Time.time;
     
        while(Time.time < startTime + activeTime)
        {
            controller.Move(movement.move.normalized * dashVelocity * Time.deltaTime);
        }
     
        yield return null;
    }
}

MonoInstance

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

public class MonoInstance : MonoBehaviour
{
    public static MonoInstance instance;

    private void Start()
    {
        MonoInstance.instance = this;
    }
}

Upvotes: 1

Views: 2908

Answers (3)

Everts
Everts

Reputation: 10701

Here's how you'd have the same result, but you would not use any new WaitForXXX of any kind.

IEnumerator Dashing(Action onComplete)
{
    float timer = 0f;
    while (timer < activeTime)
    {
        timer += Time.deltaTime;
        controller.Move(movement.move.normalized * dashVelocity * Time.deltaTime);
        yield return null
    }
    onComplete?.Invoke();
}

This returns to the program on the yield until the timer is greater than the activeTime.

I added a delegate to call when the run is over. You may want to block some actions from happening while you do this, say while dashing the player should not be able to dash again, like this:

private bool dashing;
void Update()
{
    if(Input.GetKeyDown(KeyCode.Space) && dashing == false)
    {
         StartCoroutine(Dashing(ResetDashing));
    }
} 

void ResetDashing()
{
     dashing = false;
}

Above is a simple case where you cannot dash again until the Dashing coroutine is done looping and then will call the ResetDashing callback.

Upvotes: 0

KiynL
KiynL

Reputation: 4266

In fact, your code should be written this way. Because 0.01 seconds does not show the duration of 1 frame.

IEnumerator Dashing()
{
    var startTime = Time.time;

    while (Time.time < startTime + activeTime)
    {
        controller.Move(movement.move.normalized * dashVelocity * Time.deltaTime);
        yield return new WaitForEndOfFrame();
    }
}

Upvotes: 1

Peter Hopkins
Peter Hopkins

Reputation: 33

I hate unity, fix & explanation down below:

DashAbility

    IEnumerator Dashing()
    {
        float startTime = Time.time;

        while (Time.time < startTime + activeTime)
        {
            controller.Move(movement.move.normalized * dashVelocity * Time.deltaTime);
            yield return new WaitForSeconds(0.01f);
        }
     
        yield return null;
    }

Unity tried to do too much at once, and called the time while loop thousands of times per millisecond where the character controller (probably) wouldn't even have moved yet (because Time.deltaTime was 0, since the time difference in frames is nothing). This made unity cry. This made me cry.

Adding a WaitForSeconds(?.??f) made unity not do so much, and now it doesn't crash.


Tl;dr - Told unity to take a chill pill and it stopped seizing up

Upvotes: 1

Related Questions