[VR] GroundZero 개발

[4Idle - Gazzlers] Player 이동 기능 + 손 기능 합치기

meltingmelvin 2023. 12. 1. 16:44

1차적으로 R&D한 구현 내용을 GameScene을 생성해 기능을 통합시키려 한다.

-이 포스팅에서는 Player가 Vehicle을 타고 이동하는 기능과 손 기능을 합칠 것이다.

 

-먼저 TestHands 씬을 복사하여 네임스페이스가 없도록 스크립트를 모두 새로 생성하고,  GameScene을 새로 생성해 똑같은 환경을 구성하여 미리 만들어둔 손 기능이 똑같이 동작하는지 확인하였다.

https://made-myblog.tistory.com/58

 

Gazzlers 개발일지 - Map생성(3)

현재 구현상태에서는 아직 player가 깔아놓은 rail을 따라가지 못하고 있다. player의 경로 이동을 구현하기 위해 각각의 railPrefab에 경로를 지정한 뒤 그 경로를 player에게 전달하여 player가 이를 따라

made-myblog.tistory.com

 

 

-namespace가 설정되지 않도록 스크립트들을 새로 생성해주었다.

Rail.cs

-Rail.cs와 RailPoolManager.cs생성

=> Player의 경로 이동을 구현하기 위해 각각의 railPrefab에 경로를 지정한 뒤 그 경로를 player에게 전달하여 player가 이를 따라가도록 하였다.

=> railPrefab에는 Rail.cs가 부착되며, 이 스크립트를 통해 경로를 지정해준다.

=> Rail.cs에는 각각의 rail 종류를 알려주는 railType과 그들이 가지고 있는 경로에 대한 정보를 저장하는 routeTrans 배열이 있다.

=> GetRoute는 CamMove.cs가 player가 지나갈 경로를 얻기 위한 메서드이다.

 

MapController

=> player의 경로 갱신을 위해 MapController가 Rail을 설치할 때마다 TestMapMain에게 Rail 정보를 전달한다.

GameMain 수정

 - Main은 camMove에게 rail에 저장되어있는 route 배열들을 전달한다.(118라인)

CamMove

- CamMove.cs의 UpdateRoute는 경로를 전달받는 메서드이다.

=> 전달받은 경로가 queueRouteTrans에 enqueue되어 관리된다.
- Move는 player의 움직임을 관리하는 method로 player는 지정해놓은 경로를 향해 회전하면서 이동한다.

- 지정해놓은 경로의 position.z값이 player의 position.z값보다 클 경우(player가 경로를 지나칠 경우) queueRouteTrans.Dequeue를 하여 지나간 경로를 제외시킨다.

 

-StructurePoolManager.cs, SturctureManager.cs 생성

StructureManager.cs

- Structure들을 저장해놓은 List들을 Queue로 관리하기 위해 qStructures를 선언하였다.

- Queue로 관리하는 이유는 앞선 코드들처럼 먼저 저장한 structure(시야에서 멀어진)를 제거하고 새로 생성하기 위함이다.

StructureManager.cs

 

- InstallTree 메서드는 나무를 설치한다. tree가 rail을 따라 일정 간격으로 생성된다.

- offset은 position.z 간격(+ position.x 간격)을 xOffset은 LRail 혹은 RRail의 모양에 맞게끔 설치되기 위해 조정되어야 하는 position.x 간격을 의미한다.

- 따라서 switch문에서 rail의 type에 따라 xOffset의 값이 달라진다.

- listStructure는 qStructures에 enqueue하여 관리하기 위해 선언된 list로 listStructure 안에는 floor 간격마다 설치되는 Structure들이 저장되어 있다.

- for문은 tree를 설치하는 부분으로, 한 floor당 rail 양 옆으로 3개씩, 총 6개의 tree가 설치된다.

- leftTree와 rightTree가 설치될 위치는 앞서 지정해놓은 offset값과 설치된 rail의 위치를 토대로 계산되어진다.

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

-PlayerCar, 즉 탈것의 자식으로 OVRCameraRig를 넣어준다.

-빈 오브젝트로 CamMove, Map Controller,Rail Pool Manager, StructurePoolManager, Structure Manager를 생성하고 각각의 스크립트를 붙여준다.

 

-StructurePoolManager에 들어가는 프리팹들은 Structure 스크립트를 부착해주어야한다.

 

- Player를 이동시키기 시작했더니 문제가 생겼다. 레이저는 제자리에서 생성되기 때문에 날아가 버리는 것처럼 보인다.

-레이저 빔 오브젝트의 위치가 고정되어서 생기는 문제였으므로 위치를 update에서 받도록 코드를 수정하였다.

-생성되는 시간을 0.3에서 0.2로 줄여주었다.

수정 후 결과

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


    public class Structure : MonoBehaviour
    {
        public enum eType
        {
            Tree, Bench, Lamp, Clock, Sign, AdPole, ElecPole, FireHydrant, Architecture
        }
        public eType type;

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


    public class StructurePoolManager : MonoBehaviour
    {
        public static StructurePoolManager Instance;

        [SerializeField] private GameObject[] arrStructurePrefabs;
        private List<List<GameObject>> listStructures = new List<List<GameObject>>();
        public int count = 10;
        private void Awake()
        {
            if (Instance == null)
                Instance = this;
        }
        //void Start()
        //{
        //    this.InitStructure();
        //}

        public void InitStructure()
        {
            for (int i = 0; i < arrStructurePrefabs.Length; i++)
            {
                listStructures.Add(new List<GameObject>());
                for (int j = 0; j < this.count; j++)
                {
                    GameObject go = this.GenerateStructure(i);
                    go.transform.SetParent(this.transform);
                    go.SetActive(false);
                }
            }
        }

        private GameObject GenerateStructure(int idx)
        {
            GameObject go = Instantiate(this.arrStructurePrefabs[idx]);
            listStructures[idx].Add(go);
            return go;
        }

        public GameObject EnableStructure(int idx)
        {
            GameObject structure = null;
            for (int i = 0; i < this.listStructures[idx].Count; i++)
            {
                GameObject go = this.listStructures[idx][i];
                if (go.activeSelf == false)
                {
                    go.SetActive(true);
                    go.transform.SetParent(null);
                    structure = go;
                    break;
                }
            }
            if (structure == null)
            {
                structure = this.GenerateStructure(idx);
            }
            return structure;
        }

        public void DisableStructure(GameObject go)
        {
            go.transform.SetParent(this.transform);
            go.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
            go.SetActive(false);
        }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;



  
    public class StructureManager : MonoBehaviour
    {
        private Queue<List<Structure>> qStructures = new Queue<List<Structure>>();
        private Rail rail;

        public void InstallStructure(Rail rail)
        {
            //Debug.LogFormat("railposition: {0}", rail.transform.position);
            //Debug.LogFormat("<color=red>rail type: {0}</color>", rail.type);
            List<Structure> listStructure = new List<Structure>();
            this.rail = rail;
            this.InstallTree(listStructure);
        }

        public void UninstallStructure()
        {
            List<Structure> list = qStructures.Dequeue();
            for (int i = 0; i < list.Count; i++)
            {
                StructurePoolManager.Instance.DisableStructure(list[i].gameObject);
            }
        }

        private void InstallTree(List<Structure> list)
        {
            float xOffset = 0f;
            float offset = 4.8f;
            switch (this.rail.type)
            {
                case Rail.eType.Left:
                    xOffset = 2f;
                    break;
                case Rail.eType.Right:
                    xOffset = -2f;
                    break;
            }

            qStructures.Enqueue(list);
            for (int i = 0; i < 3; i++)
            {
                GameObject leftTreeGo = StructurePoolManager.Instance.EnableStructure((int)Structure.eType.Tree);
                leftTreeGo.transform.position = this.rail.gameObject.transform.position + Vector3.left * (offset + 1f - i * xOffset) + Vector3.back * offset * (i + 1);
                list.Add(leftTreeGo.GetComponent<Structure>());

                GameObject rightTreeGo = StructurePoolManager.Instance.EnableStructure((int)Structure.eType.Tree);
                rightTreeGo.transform.position = this.rail.gameObject.transform.position + Vector3.right * (offset + 1f + i * xOffset) + Vector3.back * offset * (i + 1);
                list.Add(rightTreeGo.GetComponent<Structure>());
            }
        }

        private void InstallArchitecture(List<Structure> list)
        {
            int rand = Random.Range(0, 4);
            if (rand >= 3) return;

            float xOffset = 10f;
            float offset = 8f;

            GameObject go = StructurePoolManager.Instance.EnableStructure((int)Structure.eType.Architecture);



        }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RailPoolManager : MonoBehaviour
{
    public static RailPoolManager Instance;

    [SerializeField] private GameObject[] arrRailPrefabs;

    private List<List<GameObject>> listRail = new List<List<GameObject>>();

    private void Awake()
    {
        if (Instance == null)
            Instance = this;
    }

    public void Init(int count)
    {
        this.GenerateRail(count);
    }

    private void GenerateRail(int count)
    {
        for (int i = 0; i < arrRailPrefabs.Length; i++)
        {
            listRail.Add(new List<GameObject>());
            for (int j = 0; j < count; j++)
            {
                GameObject go = Instantiate(arrRailPrefabs[i], this.transform);
                listRail[i].Add(go);
                go.SetActive(false);
            }
        }
    }

    public GameObject EnableRail(int idx)
    {
        GameObject result = null;
        for (int i = 0; i < listRail[idx].Count; i++)
        {
            GameObject go = listRail[idx][i];
            if (go.activeSelf == false)
            {
                go.SetActive(true);
                go.transform.SetParent(null);
                result = go;
                break;
            }
        }
        return result;
    }

    public void DisableRail(GameObject go)
    {
        go.transform.SetParent(this.transform);
        go.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
        go.SetActive(false);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


    public class Rail : MonoBehaviour
    {
        public enum eType
        {
            Left = 0, Middle = 1, Right = 2
        }
        private int value;
        public int Value { get { return value; } }

        public eType type;
        [SerializeField] Transform[] arrRoute;

        void Start ()
        {
            switch (type)
            {
                case eType.Left:
                    value = 4;
                    break;
                case eType.Middle:
                    value = 0;
                    break;
                case eType.Right:
                    value = -4;
                    break;
            }
        }

        public Transform[] GetRoute()
        {
            return this.arrRoute;
        }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

    public class MapController : MonoBehaviour
    {
        [SerializeField] private GameObject floorPrefab;
        [SerializeField] private Transform playerTrans;
        [SerializeField] private StructureManager structureManager;
        private Queue<GameObject> queueFloor = new Queue<GameObject>();
        private Queue<Rail> queueRail = new Queue<Rail>();
        private Stack<Rail.eType> stackRail = new Stack<Rail.eType>();
        private int maxStackSize = 3;
        public float offset = 112f;
        public float dis = 16f;
        private int count;
        private Transform lastFloorTrans;
        private Vector3 railPos;
        public System.Action<Vector3> onInformInitPos;
        public System.Action<Rail> onInformRoute;

        public void Init()
        {
            StructurePoolManager.Instance.InitStructure();
            this.count = (int)(offset / dis * 2) + 1;
            this.InitFloor();
            this.InitRail();
        }

        void Update()
        {
            this.UpdateMap();
        }

        private void UpdateMap()
        {
            while (queueFloor.Count > 0 && queueFloor.Peek().transform.position.z > +playerTrans.position.z + this.offset + this.dis)
            {
                GameObject floorGo = queueFloor.Dequeue();
                this.UnInstallRail();
                Vector3 pos = lastFloorTrans.position + Vector3.forward * -this.dis;
                floorGo.transform.position = pos;
                railPos = pos + Vector3.right * this.CalculateRailOffset();
                //Debug.LogFormat("<color=cyan>railPos: {0}</color>", railPos);
                this.InstallRail();
                queueFloor.Enqueue(floorGo);
                lastFloorTrans = floorGo.transform;
            }
        }

        private void InitFloor()
        {
            for (int i = 0; i < count; i++)
            {
                GameObject floorGo = Instantiate(floorPrefab);
                floorGo.transform.position = playerTrans.position + Vector3.forward * (offset - i * this.dis);
                queueFloor.Enqueue(floorGo);
                if (i == count - 1)
                    lastFloorTrans = floorGo.transform;
            }
        }

        private void InitRail()
        {
            RailPoolManager.Instance.Init(this.count);
            for (int i = 0; i < this.count; i++)
            {
                float railOffset = this.CalculateRailOffset();
                railPos = playerTrans.position + Vector3.forward * (offset - i * this.dis) + Vector3.right * railOffset;
                if (railPos.z == 0)
                    this.onInformInitPos(railPos);
                this.InstallRail();
            }
        }
        private float CalculateRailOffset()
        {
            float railOffset = stackRail.Count;
            if (railOffset > 0)
            {
                if (stackRail.Peek() == Rail.eType.Left)
                {
                    railOffset *= 4;
                }
                else
                {
                    railOffset *= -4;
                }
            }
            return railOffset;
        }

        private void InstallRail()
        {
            int rand = Random.Range(0, 10);
            if (rand < 2)                   //LRail
            {
                if (this.stackRail.Count == this.maxStackSize && this.stackRail.Peek() == Rail.eType.Left)
                {
                    //Debug.Log(stackRail.Peek());
                    this.InstallRRail();
                }
                else
                    this.InstallLRail();
            }
            else if (rand < 4)              //RRail
            {
                if (this.stackRail.Count == this.maxStackSize && this.stackRail.Peek() == Rail.eType.Right)
                    this.InstallLRail();
                else
                    this.InstallRRail();
            }
            else                            //MRail
            {
                this.InstallMRail();
            }
        }

        private void InstallLRail()
        {
            GameObject go = RailPoolManager.Instance.EnableRail((int)Rail.eType.Left);
            Rail rail = go.GetComponent<Rail>();
            queueRail.Enqueue(rail);
            go.transform.position = this.railPos;
            //structure 생성
            structureManager.InstallStructure(rail);

            //루트 알려주기
            this.onInformRoute(rail);

            //stack에 저장
            if (this.stackRail.Count == 0 || this.stackRail.Peek() == Rail.eType.Left)
            {
                stackRail.Push(Rail.eType.Left);
            }
            else if (this.stackRail.Peek() == Rail.eType.Right)
            {
                stackRail.Pop();
            }
            //Debug.LogFormat("<color=yellow>stack count:{0}</color>", stackRail.Count);
        }

        private void InstallMRail()
        {
            GameObject go = RailPoolManager.Instance.EnableRail((int)Rail.eType.Middle);
            Rail rail = go.GetComponent<Rail>();
            queueRail.Enqueue(rail);
            go.transform.position = this.railPos;

            structureManager.InstallStructure(rail);

            this.onInformRoute(rail);
            //Debug.LogFormat("<color=yellow>stack count:{0}</color>", stackRail.Count);
        }

        private void InstallRRail()
        {
            GameObject go = RailPoolManager.Instance.EnableRail((int)Rail.eType.Right);
            Rail rail = go.GetComponent<Rail>();
            queueRail.Enqueue(rail);
            go.transform.position = this.railPos;

            structureManager.InstallStructure(rail);

            this.onInformRoute(rail);
            if (this.stackRail.Count == 0 || this.stackRail.Peek() == Rail.eType.Right)
            {
                stackRail.Push(Rail.eType.Right);
            }
            else if (this.stackRail.Peek() == Rail.eType.Left)
            {
                stackRail.Pop();
            }
            //Debug.LogFormat("<color=yellow>stack count:{0}</color>", stackRail.Count);
        }

        private void UnInstallRail()
        {
            Rail rail = queueRail.Dequeue();
            RailPoolManager.Instance.DisableRail(rail.gameObject);
            structureManager.UninstallStructure();
        }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

 public class CamMove : MonoBehaviour
    {
        [SerializeField] private GameObject player;
        private Queue<Transform> queueRouteTrans = new Queue<Transform>();
        private Transform curDestTrans;
        private bool isReady;
        public float rotSpeed = 1f;
        public float moveSpeed = 1.5f;
        // Update is called once per frame

        public void Init(Vector3 pos)
        {
            this.player.transform.position = pos;
            this.player.transform.rotation = Quaternion.Euler(Vector3.up * 180);
            isReady = true;
        }

        void Update()
        {
            if (!isReady) return;
            this.Move();
        }

        private void Move()
        {
            Vector3 destPos = this.queueRouteTrans.Peek().position;
            while (destPos.z > this.player.transform.position.z)
            {
                Transform trans = this.queueRouteTrans.Dequeue();
                //Debug.Log(trans.gameObject.name);
                destPos = this.queueRouteTrans.Peek().position;
                curDestTrans = this.queueRouteTrans.Peek();
            }
            //this.player.transform.LookAt(curDestTrans);
            Vector3 dir = curDestTrans.position - this.player.transform.position;
            this.player.transform.localRotation = Quaternion.Slerp(this.player.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * this.rotSpeed);
            this.player.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
        }

        public void UpdateRoute(Transform[] routes)
        {
            foreach (var route in routes)
            {
                queueRouteTrans.Enqueue(route);
            }
        }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class GameMain : MonoBehaviour
    {
        [SerializeField] EnemyGenerator enemyGen;
        [SerializeField] Canvas worldCanvas;
        [SerializeField] GameObject uiHPBarPrefab;
        [SerializeField] GameObject uiDamageTextPrefab;
        [SerializeField] RightHandController rightHand;

 
        private GameObject hpBarUIGo;
        private GameObject damageTextUIGo;
        private EnemyGenerator enemyGenerator;
        private string currGunType;
        private int currGunDamage;

        private List<EnemyController> enemyList = new List<EnemyController>();
        private List<GameObject> enemyHpBarPools = new List<GameObject>();
        private List<GameObject> activeHPBar = new List<GameObject>();


    //----------------------------------SJY Map Main-----------------------------------------------------------
    [SerializeField] private MapController mapController;
    [SerializeField] private CamMove camMove;

    private void Awake()
        {
            this.enemyGenerator = enemyGen;
            //----------------------------------Generate Enemy-----------------------------------------------------------
            this.enemyList.Add(this.enemyGenerator.Generate(GameEnums.eEnemyType.FrankenStein, new Vector3(-0.09f, -1.35f, 4.3f)));
            this.enemyList.Add(this.enemyGenerator.Generate(GameEnums.eEnemyType.FrankenStein, new Vector3(2.41f, -1.35f, 4.3f)));
        }
        // Start is called before the first frame update
        void Start()
        {

           
            DataManager.Instance.LoadPlayerDatas();
            DataManager.Instance.LoadGunrDatas();
            DataManager.Instance.LoadEnemyDatas();

            List<PlayerData> playerData = DataManager.Instance.GetPlayerDatas();
            List<GunData> gunData = DataManager.Instance.GetGunDatas();
            List<EnemyData> enemyData = DataManager.Instance.GetEnemyDatas();

            var playerGunId = playerData[0].currGunId;

            // Debug.Log(playerGunId);
            for (int i = 0; i < gunData.Count; i++)
            {
                if (playerGunId == gunData[i].gunId)
                {//Find player's current gun's Data on GunData 
                    Debug.Log(gunData[i].gunType);
                    this.currGunType = gunData[i].gunType;
                    this.currGunDamage = gunData[i].gunDamage;
                   //gunData[i].gunDamage;
                }
            }


            //this.hpBarUIGo = Instantiate(this.uiHPBarPrefab,this.worldCanvas.transform);
            this.enemyHpBarPool();

            for(int i=0; i < this.enemyList.Count; i++)
            {
                this.activeHPBar.Add(this.CreateHpBar(this.enemyList[i].hpBarPoint.position));
               // enemyList[i].gameObject.
               
              for(int j = 0; j < enemyData.Count; j++)
                {
                    if (this.enemyList[j].name == enemyData[j].enemyType)
                    {
                        //Debug.Log(enemyData[j].enemyType);
                        var slider = this.activeHPBar[i].GetComponent<Slider>();
                        slider.maxValue = enemyData[j].enemyHp;
                        Debug.Log(slider.maxValue);
                        slider.value = slider.maxValue;//reset slider value as full state
                    }
                }
            }

       

            this.damageTextUIGo = Instantiate(this.uiDamageTextPrefab, this.worldCanvas.transform);
            // this.hpBarUIGo.SetActive(false);
            this.damageTextUIGo.SetActive(false);

          
            this.rightHand.OnHitEnemy = (hitPos,hitObject) =>
            {
               // Debug.LogFormat("Hit Enemy! Point: {0}", hitPos);
                StartCoroutine(this.CoShowDamageText(hitPos));
                
                for (int i = 0; i < this.enemyList.Count; i++)
                {
                    if (hitObject == this.enemyList[i].gameObject)
                    {
                        Debug.LogFormat("Object: {0} , Damage : {1}",i,currGunDamage);
                        this.activeHPBar[i].GetComponent<Slider>().value -= currGunDamage;
                    }
            }
             
            };

        //----------------------------------SJY Map Main-----------------------------------------------------------

        Vector3 camPos = Vector3.zero;
        this.mapController.onInformInitPos = (pos) =>
        {
            camPos = pos;
        };
        this.mapController.onInformRoute = (rail) =>
        {
            this.camMove.UpdateRoute(rail.GetRoute());
        };
        this.mapController.Init();
        this.camMove.Init(camPos);


    }
        private void enemyHpBarPool()
        {
            for(int i = 0; i < this.enemyList.Count; i++)
            {
                GameObject go = Instantiate(this.uiHPBarPrefab, this.worldCanvas.transform);
                go.SetActive(false);
                this.enemyHpBarPools.Add(go);
            }
        }
        private GameObject GetEnemyHpBarInPool()
        {
            foreach(GameObject hpBar in enemyHpBarPools)
            {
                if(hpBar.activeSelf == false)
                {
                    return hpBar;               
                }
            }
            return null;
        }
        private GameObject CreateHpBar(Vector3 position)
        {
            GameObject go = this.GetEnemyHpBarInPool();
            go.transform.position = position;
            go.SetActive(true);
            return go;
        }
        private void UpdateEnemyHPBar(Vector3 worldPos)
        {
            //----------------if Canvas Render Mode is Overlay-------------------------------
            //var screenPos = RectTransformUtility.WorldToScreenPoint(Camera.main, worldPos);
            //Vector2 localPos;
            //RectTransformUtility.ScreenPointToLocalPointInRectangle(
            //    (RectTransform)this.canvas.transform, screenPos, this.uiCam, out localPos);
            //Debug.Log(localPos);
            //this.hpBarUIGo.GetComponent<RectTransform>().localPosition = localPos;
            //-------------------------------------------------------------------------------------
            //---------------if world Position (VR always need world Pos)--------------------------
            // this.hpBarUIGo.transform.position = worldPos;
           
            //-------------------------------------------------------------------------------------
        }
        private IEnumerator CoShowDamageText(Vector3 UIPos)
        {
            this.damageTextUIGo.transform.position = UIPos;
            this.damageTextUIGo.SetActive(true);

            var text = this.damageTextUIGo.GetComponent<TextMeshProUGUI>();
            text.text = string.Format("{0}",this.currGunDamage);
            yield return new WaitForSeconds(0.3f);
            this.damageTextUIGo.SetActive(false);
        }
        // Update is called once per frame
        void Update()
        {
            for (int i = 0; i < this.enemyList.Count; i++)
            {
                //  this.UpdateEnemyHPBar(this.enemyList[i].hpBarPoint.position);
                this.activeHPBar[i].transform.position = this.enemyList[i].hpBarPoint.position;
                

            }
        }
    }