簡體   English   中英

一旦玩家不再看任何目標,我怎樣才能在中間立即停止協程?

[英]How can i stop the coroutine immediately in the middle once the player is not looking at any target anymore?

當玩家注視目標時,ui 文本啟用 true 並顯示一些文本。

void OnAnimatorIK()
    {
        if (lookObjs != null)
        {
            lookObjs.RemoveAll(x => x == null);

            InteractableItem primaryTarget = null;

            float closestLookWeight = 0;

            // Here we find the target which is closest (by angle) to the players view line
            allDetectedItems.Clear();
            foreach (InteractableItem target in lookObjs)
            {
                if (target.enabledInteraction == false)
                {
                    continue;
                }

                Vector3 lookAt = target.transform.position - transform.position;
                lookAt.y = 0f;

                // Filter out all objects that are too far away
                if (lookAt.magnitude > target.distance) continue;

                RaycastHit hit;
                if (Physics.Raycast(playerEyes.transform.position, target.transform.position - playerEyes.transform.position, out hit, target.distance, ~LayerMask.GetMask("kid_from_space")))
                {
                    if (hit.collider.gameObject == target.gameObject) 
                    {
                        float dotProduct = Vector3.Dot(new Vector3(transform.forward.x, 0f, transform.forward.z).normalized, lookAt.normalized);
                        float lookWeight = Mathf.Clamp(dotProduct, 0f, 1f);

                        if (lookWeight > 0.1f && lookWeight > closestLookWeight)
                        {
                            closestLookWeight = lookWeight;
                            primaryTarget = target;
                            if (showText && primaryTarget.description != "")
                            {
                                StartCoroutine(WaitBeforeShowingText(primaryTarget));
                                showText = false;
                            }
                        }
                        
                        allDetectedItems.Add(target);
                    }
                    else
                    {
                        showText = true;
                        text.text = "";
                        descriptionTextImage.SetActive(false);
                    }
                }
            }

            InteractWithTarget(primaryTarget, closestLookWeight);
        }
    }

此行啟動顯示文本的協程:

StartCoroutine(WaitBeforeShowingText(primaryTarget));

在協程里面

IEnumerator WaitBeforeShowingText(InteractableItem primaryTarget)
    {
        yield return new WaitForSeconds(1f);

        descriptionTextImage.SetActive(true);
        text.text = primaryTarget.description;
    }

我這樣做是因為我想在玩家注視目標時顯示文本之前有一些延遲。

問題是,如果協程啟動了,但在顯示文本之前,我正在將玩家旋轉到不看任何目標,但因為協程已經啟動,即使玩家不再看任何目標,它也會顯示文本。

所以協程必須以某種方式在中間停止並且不顯示任何文本。

另一個可能的問題是,如果有兩個近距離目標並且我移動/旋轉玩家,那么他正在看的目標對於協程來說變化太快怎么辦?

這是完整的代碼:

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

[RequireComponent(typeof(Animator))]
public class IKControl : MonoBehaviour
{
    public List<InteractableItem> lookObjs = new List<InteractableItem>();
    public TextMeshProUGUI text;
    public float weightDamping = 1.5f;
    public bool RightHandToTarget = false;
    public GameObject descriptionTextImage;
    public float duration;

    private List<InteractableItem> allDetectedItems;
    private Animator animator;
    private InteractableItem lastPrimaryTarget;
    private float lerpEndDistance = 0.1f;
    private float finalLookWeight = 0;
    private bool transitionToNextTarget = false;
    private float t;
    private bool showText = true;
    private GameObject playerEyes;

    void Start()
    {
        playerEyes = GameObject.Find("rig_head");//"rig_eye.L");
        animator = GetComponent<Animator>();
        allDetectedItems = new List<InteractableItem>();
        t = 0;
    }

    // Callback for calculating IK
    void OnAnimatorIK()
    {
        if (lookObjs != null)
        {
            lookObjs.RemoveAll(x => x == null);

            InteractableItem primaryTarget = null;

            float closestLookWeight = 0;

            // Here we find the target which is closest (by angle) to the players view line
            allDetectedItems.Clear();
            foreach (InteractableItem target in lookObjs)
            {
                if (target.enabledInteraction == false)
                {
                    continue;
                }

                Vector3 lookAt = target.transform.position - transform.position;
                lookAt.y = 0f;

                // Filter out all objects that are too far away
                if (lookAt.magnitude > target.distance) continue;

                RaycastHit hit;
                if (Physics.Raycast(playerEyes.transform.position, target.transform.position - playerEyes.transform.position, out hit, target.distance, ~LayerMask.GetMask("kid_from_space")))
                {
                    if (hit.collider.gameObject == target.gameObject) 
                    {
                        // First object hit was the target so there is a clear line of sight

                        //Debug.DrawRay(playerEyes.transform.position, Vector3.forward, Color.red);

                        float dotProduct = Vector3.Dot(new Vector3(transform.forward.x, 0f, transform.forward.z).normalized, lookAt.normalized);
                        float lookWeight = Mathf.Clamp(dotProduct, 0f, 1f);

                        if (lookWeight > 0.1f && lookWeight > closestLookWeight)
                        {
                            closestLookWeight = lookWeight;
                            primaryTarget = target;
                            if (showText && primaryTarget.description != "")
                            {
                                StartCoroutine(WaitBeforeShowingText(primaryTarget));
                                showText = false;
                            }
                        }

                        allDetectedItems.Add(target);
                    }
                    else
                    {
                        showText = true;
                        text.text = "";
                        descriptionTextImage.SetActive(false);
                    }
                }
            }

            InteractWithTarget(primaryTarget, closestLookWeight);
        }
    }

    private void InteractWithTarget(InteractableItem primaryTarget, float closestLookWeight)
    {
        if (primaryTarget != null)
        {
            if ((lastPrimaryTarget != null) && (lastPrimaryTarget != primaryTarget) && (finalLookWeight > 0f))
            {
                // Here we start a new transition because the player looks already to a target but
                // we have found another target the player should look at
                transitionToNextTarget = true;
            }
        }

        // The player is in a neutral look position but has found a new target
        if ((primaryTarget != null) && !transitionToNextTarget)
        {
            if (primaryTarget.IsAnyAction())
            {
                RightHandToTarget = true;
            }

            lastPrimaryTarget = primaryTarget;
            finalLookWeight = Mathf.Lerp(finalLookWeight, 1f, Time.deltaTime * weightDamping);
            float bodyWeight = finalLookWeight * .1f;
            animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
            animator.SetLookAtPosition(primaryTarget.transform.position);

            if (RightHandToTarget && primaryTarget.IsAnyAction())
            {
                Vector3 relativePos = primaryTarget.transform.position - transform.position;
                Quaternion rotationtoTarget = Quaternion.LookRotation(relativePos, Vector3.up);

                if (primaryTarget.interactableMode == InteractableItem.InteractableMode.ActionWithoutThrow)
                {
                    animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
                    animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
                    animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 1f * closestLookWeight);
                    animator.SetIKPosition(AvatarIKGoal.RightHand, primaryTarget.transform.position);
                }

                if (primaryTarget.interactableMode == InteractableItem.InteractableMode.Action)
                {
                    animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
                    animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
                    animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 0.1f * closestLookWeight);
                    animator.SetIKPosition(AvatarIKGoal.RightHand, primaryTarget.transform.position);
                }
            }
        }

        // Let the player smoothly look away from the last target to the neutral look position
        if ((primaryTarget == null && lastPrimaryTarget != null) || transitionToNextTarget)
        {
            finalLookWeight = Mathf.Lerp(finalLookWeight, 0f, t / duration);//Time.deltaTime * weightDamping);
            t += Time.deltaTime;

            float bodyWeight = finalLookWeight * .1f;

            animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
            animator.SetLookAtPosition(lastPrimaryTarget.transform.position);

            if (RightHandToTarget)
            {
                Vector3 relativePos = lastPrimaryTarget.transform.position - transform.position;
                Quaternion rotationtoTarget = Quaternion.LookRotation(relativePos, Vector3.up);
                animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
                animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
                animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 0.5f * closestLookWeight);
                animator.SetIKPosition(AvatarIKGoal.RightHand, lastPrimaryTarget.transform.position);
            }

            if (finalLookWeight < lerpEndDistance)
            {
                showText = true;
                text.text = "";
                descriptionTextImage.SetActive(false);

                transitionToNextTarget = false;
                finalLookWeight = 0f;
                lastPrimaryTarget = null;
                transform.rotation = Quaternion.Euler(0, transform.eulerAngles.y, 0);
            }
        }
    }

    IEnumerator WaitBeforeShowingText(InteractableItem primaryTarget)
    {
        yield return new WaitForSeconds(1f);

        descriptionTextImage.SetActive(true);
        text.text = primaryTarget.description;
    }
}

要停止協程,請調用 StopCoroutine(WaitBeforeShowingText);

這是文檔: https ://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM