본문 바로가기
마블 VR 프로젝트 제작

[마블 VR] 근접 공격 튜토리얼 완료 -> 적 생성

by 노재두내 2023. 12. 29.

1. 간단하게 trigger버튼을 눌렀다가 떼면 튜토리얼이 완료되도록 구현되어 있는 스크립트

==> 실제로 trigger 버튼을 누르고 위에서 아래로 찍으면 완료되도록 수정하기

<MeleeAttackUI >

    IEnumerator CoGrab()
    {
        while (true)
        {

            if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.LTouch))
            {
                Debug.Log("step1완료");
                this.step1High.gameObject.SetActive(false);
                this.setp1.SetActive(true);
                this.step2High.gameObject.SetActive(true);
                this.setp2.SetActive(false);

            }
            if (OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.LTouch))
            {
                //어차피 그랩했다 뗏을 때 사라지는
                this.step2High.gameObject.SetActive(false);
                this.setp2.SetActive(true);
                Destroy(this.gameObject, 0.2f);

                //적 생성
                this.gameSceneMain.isFirstEnemy = false;
                this.gameSceneMain.enemyNum = 0;
                //길 없애기
                this.gameSceneMain.pathGo[0].SetActive(false);
            }

            yield return null;
        }
    }

 

 

근접공격 스크립트 내에서 튜토리얼이 넘어가는것을 구현하면 잘 넘어가짐

 

PlayerController에서 근접공격이 구현되어 있기 때문에 (index trigger을 누르고, 찍기) 

index trigger을 누르면 찍기 튜토리얼의 step1이 넘어가고, step2가 활성화

찍으면 step2가 넘어가고 튜토리얼은 사라지는것을 구현해야함

 

But

넘어가는 부분을 구현하면 로그도 잘찍히는데 넘어가질 않음 ㅜㅜ

이미 setActive(true)한거를 다른곳에서  SetActive(false) 하나 확인해봤으나 실행 순서에는 문제가 없었다.

 

그래서 eventdispatcher를 이용해 문제를 해결했다.

using Meta.WitAi.Events;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MeleeAttackUI : MonoBehaviour
{
    public GameObject step1High;
    public GameObject step2High;
    public GameObject setp1;
    public GameObject setp2;

    private Animator anim;
    private GameSceneMain gameSceneMain;

    public void SetInActiveStep1High(short evtType)
    {
        if (evtType == (short)PlayerController.eEventType.INDEX_TRIGGER_PULL_WHEN_ENEMY_DETECTED)
        {
            this.step1High.gameObject.SetActive(false);
            this.setp1.SetActive(true);
            this.step2High.gameObject.SetActive(true);
            this.setp2.SetActive(false);

            EventDispatcher.instance.RemoveEventHandler((short)PlayerController.eEventType.INDEX_TRIGGER_PULL_WHEN_ENEMY_DETECTED, this.SetInActiveStep1High);

            Debug.Log("<color=lime>step1High를 비활성화 하고 이벤트를 제거합니다.</color>");
        }
        
    }

    public void TutorialClear(short evtType)
    {
        if (evtType == (short)PlayerController.eEventType.COLLISION_WITH_PLANE)
        {
            this.step2High.gameObject.SetActive(false);
            this.setp2.SetActive(true);
            Destroy(this.gameObject, 0.2f);
            this.gameSceneMain.isFirstEnemy = false;
            this.gameSceneMain.enemyNum = 0;
            //길 없애기
            this.gameSceneMain.pathGo[0].SetActive(false);

            EventDispatcher.instance.RemoveEventHandler((short)PlayerController.eEventType.COLLISION_WITH_PLANE, this.TutorialClear);

            Debug.Log("<color=lime>step1High를 비활성화 하고 이벤트를 제거합니다.</color>");
        }

    }

    private void Start()
    {
        EventDispatcher.instance.AddEventHandler((short)PlayerController.eEventType.INDEX_TRIGGER_PULL_WHEN_ENEMY_DETECTED, this.SetInActiveStep1High);
        EventDispatcher.instance.AddEventHandler((short)PlayerController.eEventType.COLLISION_WITH_PLANE, this.TutorialClear);

        Invoke("Active", 2f);
        this.gameSceneMain = GameObject.FindObjectOfType<GameSceneMain>();
        this.anim = GetComponent<Animator>();
    }


    private void Active()
    {
        this.step1High.SetActive(true);
        this.setp1.SetActive(false);
        
    }
}
public class PlayerController : MonoBehaviour
{
    public enum eEventType
    { 
        INDEX_TRIGGER_PULL_WHEN_ENEMY_DETECTED,
        COLLISION_WITH_PLANE
    }

 

트리거를 눌렀을 때와 , 찍었을때 이벤트를 보내도록 구현하였다.

 

전체코드

using Meta.WitAi;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    public enum eEventType
    { 
        INDEX_TRIGGER_PULL_WHEN_ENEMY_DETECTED,
        COLLISION_WITH_PLANE
    }

    [SerializeField]
    private PathMain pathMain;
    [SerializeField]
    private GameObject[] indicatorGo; 
    [SerializeField]
    private Transform[] pos;
    [SerializeField] Transform leapPos;
    [SerializeField] ParticleSystem warp;
    [SerializeField] private GameObject leapTutorial;
    [SerializeField] private GameObject meleeAttackUI;
    [SerializeField] private GameObject tutorialAerialEnemy;
    public float ms = 0.025f;
    
    private Coroutine coroutine;
    private bool isSecond;
    //float curTime = 0;
    //float warpTime = 0.5f;
    void Start()
    {
        this.Move();
        StartCoroutine(CoDetectEnemy());
        
    }

    public void Move()
    {
        this.coroutine = this.StartCoroutine(this.CoMove());
    }

    public void SecondMove()
    { 
        this.coroutine = this.StartCoroutine(this.CoSecondMove());
    }

    private IEnumerator CoMove()
    {
        yield return new WaitForSeconds(1.3f);
        while (true)
        {
            var dis = Vector3.Distance(this.transform.position, indicatorGo[0].transform.position);

            if (dis < 9f)
            {
                var dir = pos[1].position - this.transform.position;
                this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 0.2f);
                this.transform.position = Vector3.MoveTowards(this.transform.position, pos[1].position, ms);
            }
            else
            {
                this.transform.position = Vector3.MoveTowards(this.transform.position, pos[0].position, ms);
            }
            yield return null;
        }
    }

    private IEnumerator CoSecondMove()
    {
        yield return new WaitForSeconds(1.3f);
        while (true)
        {
            var dis = Vector3.Distance(this.transform.position, indicatorGo[1].transform.position);
            if (dis < 7f)
            {
                var dir = pos[4].position - this.transform.position;
                this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 0.3f);
                this.transform.position = Vector3.MoveTowards(this.transform.position, pos[4].position, ms);
            }
            else if(dis<11f&&dis>7f)
            {
                var dir = pos[3].position - this.transform.position;
                this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 0.2f);
                this.transform.position = Vector3.MoveTowards(this.transform.position, pos[3].position, ms); 
            }
            else
            {
                var dir = pos[2].position - this.transform.position;
                this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 0.2f);
                this.transform.position = Vector3.MoveTowards(this.transform.position, pos[2].position, ms);
            }
            yield return null;
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Point"))
        {
            Debug.Log("목적지에 도착했습니다");
            //인디케이터 지우고 이동 멈추기
            this.indicatorGo[0].SetActive(false);
            
            if (this.coroutine != null) StopCoroutine(this.coroutine);

            //근거리 공격 튜토리얼
            Instantiate(this.meleeAttackUI);
            //튜토리얼용 적 생성
            this.tutorialAerialEnemy.SetActive(true);
        }
        else if (other.CompareTag("SecondPoint"))
        {
            this.indicatorGo[1].SetActive(false);
            if (this.coroutine != null) StopCoroutine(this.coroutine);
            //리프 튜토리얼 나오기
            Instantiate(this.leapTutorial);
        }
    }





    //----------------------------------------------근거리 공격---------------------------------------------------

    Dictionary<GameObject, float> dic = new Dictionary<GameObject, float>();
    Queue<float> posQueue = new Queue<float>();
    private bool isGrabIndexTrigger = false;
    private float delta = 0f;
    private float av;
    private int radius=6;
    [SerializeField] private GameObject shieldEff;
    //[SerializeField] private GameObject hitEff; //바닥 이펙트
    [SerializeField] private Shield shield;
    [SerializeField] private ParticleSystem hitParticle;
    public Transform detectPlane;
    [SerializeField] private AerialEnemy aerialEnemy;
    [SerializeField] private TutorialAerialEnemy tutorialEnemy;

    public IEnumerator CoDetectEnemy()
    {
        while (true)
        {

            Collider[] enemyCols = Physics.OverlapSphere(this.transform.position, this.radius, 1 << 6);
            this.dic.Clear();
            var velocity = OVRInput.GetLocalControllerVelocity(OVRInput.Controller.LTouch); //.normalized;
            this.av = Mathf.Abs((float)Math.Truncate(((velocity.x + velocity.y + velocity.z) / 3.0f) * 100f) / 100f);

            for (int i = 0; i < enemyCols.Length; i++)
            {
                var col = enemyCols[i];
                this.dic.Add(col.gameObject, Vector3.Distance(col.transform.position, this.shield.lHandAnchor.position));
            }

            if (this.dic.Count > 0)
            {
                var nearestDistance = this.dic.Values.Min();
                var target = this.dic.FirstOrDefault(x => x.Value == nearestDistance).Key;
                var dir = target.transform.position - this.transform.position;
                Ray ray = new Ray(this.transform.position, dir);
                Debug.DrawRay(ray.origin, ray.direction * 1000f, Color.red);

                if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.LTouch))
                {

                    //그랩했을 때 위치값 재기
                    this.isGrabIndexTrigger = true;
                    this.shieldEff.gameObject.SetActive(true);
                    this.detectPlane.gameObject.SetActive(true);

                    EventDispatcher.instance.SendEvent((short)eEventType.INDEX_TRIGGER_PULL_WHEN_ENEMY_DETECTED);

                }
                else if (OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.LTouch))
                {
                    this.isGrabIndexTrigger = false;
                    this.shieldEff.gameObject.SetActive(false);
                    this.detectPlane.gameObject.SetActive(false);
                    Debug.Log("버튼 뗏다");
                }

                //적이 감지된 ui를 띄우던 , 바닥을 찍기, 
                //바닥을 찍었다
                this.shield.onCollisionPlane = () =>
                {
                    if (this.posQueue.Count == 0)   //이전 위치가 없다
                    {
                        Debug.Log("이전 위치가 없습니다.");
                        return;
                    }

                    if (this.posQueue.Peek() > this.detectPlane.transform.position.y && this.av > 1f)
                    {
                        Debug.Log("====> 위에서 찍음");

                        //큐를 비움 
                        this.posQueue.Clear();
                        this.delta = 0;
                        this.isGrabIndexTrigger = false;

                        if (target.tag == "TutorialEnemy")
                        {
                            this.tutorialEnemy.GetHit();
                        }
                        else
                        {
                            this.aerialEnemy.GetHit(target, -100f);
                        }
                        EventDispatcher.instance.SendEvent((short)eEventType.COLLISION_WITH_PLANE);
                        this.shieldEff.gameObject.SetActive(false);
                        this.hitParticle.gameObject.SetActive(true);
                        this.detectPlane.gameObject.SetActive(false);
                        //this.hitEff.SetActive(true);
                        //햅틱 넣기
                        OVRInput.SetControllerVibration(0.9f, 0.5f, OVRInput.Controller.LTouch);
                        OVRInput.SetControllerVibration(0.9f, 0.5f, OVRInput.Controller.RTouch);

                        Invoke("TurnOff", 1f);
                    }
                    else
                    {
                        Debug.Log("===> 아래에서 찍음");
                    }

                };
            }
            else
            {
                this.shieldEff.gameObject.SetActive(false);
            }

            if (this.isGrabIndexTrigger)
            {
                this.delta += Time.deltaTime;
                if (delta > 1f)
                {
                    delta = 0;
                    Debug.LogFormat("==> {0}", this.shield.lHandAnchor.transform.position.y);
                    if (this.posQueue.Count > 10)
                    {
                        this.posQueue.Dequeue();
                    }
                    this.posQueue.Enqueue(this.shield.lHandAnchor.transform.position.y);
                }
            }

            yield return null;
        }
    }

    private void TurnOff()
    {
        //this.hitEff.SetActive(false);
        this.hitParticle.gameObject.SetActive(false);
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, this.radius);
    }
}

 

결과

 


+ 아쉬운 점 너무 빨리 사라지고 적도 너무 빨리 생성된다.

 

그래서 적 생성하는 부분만 함수로 만들어서 

Invoke("CreateEnemy", 0.5f); 0.5초 뒤에 생성하게 했더니 , 생성되지 않음

아마 이벤트 디스패처가 그전에 romove 되어서 그런거같다.

 

romove하는 부분을 주석처리하고 해도 생성안됨

                    if (this.posQueue.Peek() > this.detectPlane.transform.position.y && this.av > 1f)
                    {
                        Debug.Log("====> 위에서 찍음");

                        //큐를 비움 
                        this.posQueue.Clear();
                        this.delta = 0;
                        this.isGrabIndexTrigger = false;

                        if (target.tag == "TutorialEnemy")
                        {
                            this.tutorialEnemy.GetHit();
                        }
                        else
                        {
                            this.aerialEnemy.GetHit(target, -100f);
                        }
                        EventDispatcher.instance.SendEvent((short)eEventType.COLLISION_WITH_PLANE);
                        this.shieldEff.gameObject.SetActive(false);
                        this.hitParticle.gameObject.SetActive(true);
                        this.detectPlane.gameObject.SetActive(false);
                        //this.hitEff.SetActive(true);
                        //햅틱 넣기
                        OVRInput.SetControllerVibration(0.9f, 0.5f, OVRInput.Controller.LTouch);
                        OVRInput.SetControllerVibration(0.9f, 0.5f, OVRInput.Controller.RTouch);

                        Invoke("TurnOff", 1f);
                        Invoke("CreateEnemy", 2.5f);
                    }
                    else
                    {
                        Debug.Log("===> 아래에서 찍음");
                    }

플레이어 컨트롤러에서 invoke를 써줬더니 잘 동작한다.

하지만 이러니까 또 invoke가 계속 호출된다.

 else
            {
                if (enemyNum < 2)
                {
                    for (int i=3;i<5; i++)
                    {
                        Instantiate(this.aerialEnemyPrefab, this.points[i].position, Quaternion.identity);
                        enemyNum++;
                        CancelInvoke("CreateEnemy");
                    }
                }

플레이어 컨트롤러에서 cancelInvoke를 해주었다.

하지만 왜 PlayerController에서 호출하면 동작하지 않는지 원인을 찾지 못했다. 계속 원인을 찾아보긴 해야할거같다.