0
\$\begingroup\$
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class MouthSpeechController : MonoBehaviour
{
    public TMP_Text[] texts;
    public bool startTalking = false;
    public float talkTime;
    public float duration;
    [Range(0, 100)]
    public float valueRange;
    private SkinnedMeshRenderer bodySkinnedMeshRenderer;
    private bool isTalking = true;

    // Start is called before the first frame update
    void Start()
    {
        bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        if (startTalking && isTalking)
        {
            StartCoroutine(AnimateMouth());
            StartCoroutine(TalkTime());

            isTalking = false;
        }

        if (startTalking == false && isTalking == false)
        {
            isTalking = true;
        }
    }

    //Lerp between startValue and endValue over 'duration' seconds
    private IEnumerator LerpShape(float startValue, float endValue, float duration)
    {
        float elapsed = 0;
        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            float value = Mathf.Lerp(startValue, endValue, elapsed / duration);
            bodySkinnedMeshRenderer.SetBlendShapeWeight(0, value);
            yield return null;
        }
    }

    //animate open and closed, then repeat
    public IEnumerator AnimateMouth()
    {
        while (startTalking == true)
        {
            yield return StartCoroutine(LerpShape(0, valueRange, duration));
            yield return StartCoroutine(LerpShape(valueRange, 0, duration));
        }
    }

    public IEnumerator TalkTime()
    {
        yield return new WaitForSeconds(talkTime);

        startTalking = false;
    }
}

The script is working but to get it working from other scripts I need first to make a reference for example :

public PlayerMouthSpeechController playermouthspeechcontroller;

Then somewhere for example in the Start to set the time :

private void Start()
    {
        playermouthspeechcontroller.talkTime = 10;
    }

Then in somewhere else and it's all in the same script I need to make it start talking :

playermouthspeechcontroller.startTalking = true;

Instead maybe to make an option using maybe a public static method that all I will need to do is to type something like this in every script I want it to talk :

MouthSpeechController.StartTalking(10f);

One line and it will start talking for 10 seconds. To leave also the other options if I want to set the time in someplace and start in other place but also to make something public static I think it's good logic to start the talking with one line.

I tried this. Added instance static variable init the instance in the Awake and added the static method at the bottom of the script. At the Start, I tried to test it but it does nothing.

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

public class MouthSpeechController : MonoBehaviour
{
    public TMP_Text[] texts;
    public bool startTalking = false;
    public float talkTime;
    public float duration;
    [Range(0, 100)]
    public float valueRange;
    private SkinnedMeshRenderer bodySkinnedMeshRenderer;
    private bool isTalking = true;
    private static MouthSpeechController instance;

    private void Awake()
    {
        instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();

        StartTalking(10f);
    }

    // Update is called once per frame
    void Update()
    {
        if (startTalking && isTalking)
        {
            StartCoroutine(AnimateMouth());
            StartCoroutine(TalkTime(talkTime));

            isTalking = false;
        }

        if (startTalking == false && isTalking == false)
        {
            isTalking = true;
        }
    }

    //Lerp between startValue and endValue over 'duration' seconds
    private IEnumerator LerpShape(float startValue, float endValue, float duration)
    {
        float elapsed = 0;
        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            float value = Mathf.Lerp(startValue, endValue, elapsed / duration);
            bodySkinnedMeshRenderer.SetBlendShapeWeight(0, value);
            yield return null;
        }
    }

    //animate open and closed, then repeat
    public IEnumerator AnimateMouth()
    {
        while (startTalking == true)
        {
            yield return StartCoroutine(LerpShape(0, valueRange, duration));
            yield return StartCoroutine(LerpShape(valueRange, 0, duration));
        }
    }

    public IEnumerator TalkTime(float TalkTime)
    {
        yield return new WaitForSeconds(TalkTime);

        startTalking = false;
    }

    public static void StartTalking(float TalkingTime)
    {
        instance.StartCoroutine(instance.AnimateMouth());
        instance.StartCoroutine(instance.TalkTime(TalkingTime));
    }
}
\$\endgroup\$
6
  • 3
    \$\begingroup\$ What problems have you encountered adding a method that encapsulates the steps you've listed? \$\endgroup\$
    – Pikalek
    Commented May 21, 2021 at 20:11
  • \$\begingroup\$ @Pikalek The problem is that you can't StartCoroutine inside static method. \$\endgroup\$
    – Daniel Lip
    Commented May 21, 2021 at 21:03
  • \$\begingroup\$ @Pikalek I updated my question with what I tried using instance variable for the static problem/s and try to test it in the Start but it's not working nothing happens. \$\endgroup\$
    – Daniel Lip
    Commented May 21, 2021 at 21:12
  • \$\begingroup\$ Ok, it's working now. Using instance public static method and also had to set the two flags in the static method. and it's working perfectly. \$\endgroup\$
    – Daniel Lip
    Commented May 21, 2021 at 22:46
  • 1
    \$\begingroup\$ I don't understand why you're trying to use a static method at all. playermouthspeechcontroller is an instance variable & all the stuff you listed involves manipulating that instance. \$\endgroup\$
    – Pikalek
    Commented May 21, 2021 at 22:49

2 Answers 2

0
\$\begingroup\$

The problem is that you can't StartCoroutine inside static method.

Singleton pattern to the rescue.

A singleton is an object which exists exactly once in your scene and offers a way to obtain that one instance via a static method. The most barebone implementation of a singelton MonoBehaviour looks like this:

public class MouthSpeechController : MonoBehaviour
{
      public static MouthSpeechController instance;
      
      Awake() 
      {
          instance = this;
      }
}

When you place a gameObject in your scene with this script on it, then you can access that instance from anywhere with MouthSpeechController.instance. You now have a script object on a gameObject and can do what you want with it.

When you look for MonoBehaviour singleton implementations on the web (like on the question here), then you are going to find a lot more convoluted versions which handle error situations like the object not existing in the scene or more than one of them existing or prevent other scripts from changing the .instance to something else. Using scripts like that might not be a bad idea in a larger project, but for a small project it's usually overengineering.

\$\endgroup\$
-1
\$\begingroup\$
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class MouthSpeechController : MonoBehaviour
{
    public TMP_Text[] texts;
    public static bool startTalking = false;
    public float talkTime;
    public float duration;
    [Range(0, 100)]
    public float valueRange;
    private SkinnedMeshRenderer bodySkinnedMeshRenderer;
    private static bool isTalking = true;
    private static MouthSpeechController instance;

    private void Awake()
    {
        instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();

        StartTalking(10f);
    }

    // Update is called once per frame
    void Update()
    {
        if (startTalking && isTalking)
        {
            StartCoroutine(AnimateMouth());
            StartCoroutine(TalkTime(talkTime));

            isTalking = false;
        }

        if (startTalking == false && isTalking == false)
        {
            isTalking = true;
        }
    }

    //Lerp between startValue and endValue over 'duration' seconds
    private IEnumerator LerpShape(float startValue, float endValue, float duration)
    {
        float elapsed = 0;
        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            float value = Mathf.Lerp(startValue, endValue, elapsed / duration);
            bodySkinnedMeshRenderer.SetBlendShapeWeight(0, value);
            yield return null;
        }
    }

    //animate open and closed, then repeat
    public IEnumerator AnimateMouth()
    {
        while (startTalking == true)
        {
            yield return StartCoroutine(LerpShape(0, valueRange, duration));
            yield return StartCoroutine(LerpShape(valueRange, 0, duration));
        }
    }

    public IEnumerator TalkTime(float TalkTime)
    {
        yield return new WaitForSeconds(TalkTime);

        startTalking = false;
    }

    public static void StartTalking(float TalkingTime)
    {
        isTalking = false;
        startTalking = true;

        instance.StartCoroutine(instance.AnimateMouth());
        instance.StartCoroutine(instance.TalkTime(TalkingTime));
    }
}

This line should be removed from the Start it's only for testing :

StartTalking(10f);

now you can call it from any script and start the talking with a single simple line this way :

MouthSpeechController.StartTalking(10f);
\$\endgroup\$
1
  • \$\begingroup\$ This is similar to the approach I would recommend for this situation as well, but you could explain it a lot better. It is difficult to see for anyone who reads your question and answer that you added the static keyword at a few selected places in this very long script. And someone who is not yet that familiar with the C# programming language might not even know what that keyword means. A better answer would use a more concise use-case and would explain better what the Singleton pattern is in general. \$\endgroup\$
    – Philipp
    Commented Jun 21, 2021 at 15:09

You must log in to answer this question.

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