[졸업프로젝트] 트러블 슈팅 및 총 정리

kkily·2022년 5월 16일
0

졸업프로젝트를 진행하면서 기억에 남는 문제들과 해결 방법, 게임 개발에 기본이 됐던 유니티 활용 기술들에 대해 정리했다.


<오큘러스 컨트롤러와 유니티를 연결하는 방법>

우리 팀은 Oculus rift를 활용해 졸업프로젝트를 진행하고 있다.
rift와 유니티를 연결하는 데에만 꽤 오랜시간이 걸렸어서 우리가 어떻게 연결했는지 알려주려고 한다.

실패한 방법

https://www.youtube.com/watch?v=NunJ1o6lwBk
전체적으로는 이 영상을 따라했다.(물론 이외에도 여러가지 블로그, 영상들을 참고했음에도 불구하고 실패했다..)
순서를 보면
오큘러스 펌웨어 다운 -> 개발자용 오큘러스 유틸리티 다운 -> 오큘러스 펌웨어의 setting>general>unknown sources 체크 -> unity 실행 -> 프로젝트 실행 -> oculus utiliy 파일 import -> 확인, restart 버튼 누르기 -> 프로젝트 창에서 Assets>Oculus>Prefabs>OVRPlayerController를 hierarchy 창에 불러오기 -> Edit 창의 ProjectSettings>XR Settings>Virtual Reality Supported를 체크
순서이다.

이렇게 했는데도 안돼서 다른 블로그들을 보고 개발자 계정 가입도 해보고 휴대폰에 앱도 깔아보는 등 많은 시도를 해봤지만 다 안됐다.. 그래서 아예 새로 시작해보기로 했다!

성공한 방법

성공한 방법은 https://youtu.be/JyxbA2bm7os 이 영상을 따라했다!

여기서는 플러그인과 툴키트를 쓴다.

아래에 쓰는 건 참고용이라 위 영상링크로 들어가 영상을 보는게 더 자세하고 도움이 될 것이다.
오큘러스 펌웨어는 기본으로 다운 받은 상태로 시작하는 것 같다.
먼저 유니티를 깔고,새 3D 프로젝트를 만든 후에 Edit>Project Settings> XR Plugin Management에 가서 install을 한다. 설치가 다 되면 oculus를 체크한다.

다음으로 File>Build Setting에 가서 Android로 switch platform을 한다.(꽤 시간이 오래 걸린다.)
Player setting에 간 후에 minimum api level을 Android 6.0 Marshmallow로 바꾼다.

Window>Package Manager에 가서 XR interaction toolkit를 다운받는다. 이걸 다운 받아야 컨트롤러로 방향 조절이나 시야 조절이 가능하다. toolkit의 move,turn,left controller,right controller기능을 모두 add 한다.

이제 시뮬레이션을 해보는 건데 우리는 이미 만들어놓은 씬이 있어서 거기서 진행했다. hierarchy창에서 오른쪽 마우스 버튼을 눌러 xr> xr origin을 넣는다. 영상에서는 xr rig로 나오는데 그 사이에 업데이트가 됐나보다. xr origin에 Input Action Manager라는 컴포넌트를 넣고 그 컴포넌트의 action asset에 xri default input actions를 넣는다.

다음으로는 hierarchy 창에 locomotion system을 넣어야한다. 이동과 관련이 있는 시스템인 것 같다. 기본적으로 snap turn provider가 들어있는데 continuous move provider 컴포넌트도 추가해준다. snap에서는 left와 right 중 right(의 use reference)만 체크하고 move에서는 left만 체크하면 오른쪽 컨트롤러의 조이스틱에서는 snap turn이 가능하고 왼쪽 컨트롤러를 이용하면 move가 가능하다. 이제 게임을 시작해보면 잘 된다!

어떤 블로그에서 자기도 다 실패해서 플러그인을 썼다는 글을 봤었는데 우선 그 글을 무시하고 실패한 방법을 사용해봤었는데 그러지말았어야했다.. 이 영상을 보니 진짜 15분만에 돼서 너무 허무했다.. 하지만 기뻤다 ㅋㅋ ㅜㅜ

이 시행착오를 겪으면서 느꼈던 점은 초기 setup은 블로그보다는 유튜브를 따라하자라고 느꼈다. 또, 자료가 영어라고 피하지말자 라는 교훈을 얻었다..

아무튼 우리 팀은 oculus 연결을 마치고 개발을 잘 하고있다!

+) 추가적으로 vr 장비는 웬만한 노트북으로는 돌아가지않는다..
그래픽카드가 매우 좋아야해서 vr을 돌려보고싶다면 미리 사양을 확인해봐야한다!
우리는 학교에서 대여했다😊

+)씬이 바뀌었을 때 xr origin을 문제없이 쓰려면 아래 사진의 표시한 부분은 무조건 같이 들고오는 것이 좋다. 이게 없으면 버튼이 클릭이 되지 않거나 컨트롤러를 눌러도 움직이지않는다.


<locomotion system에 대해 알아보기>

locomotion system에는 여러 컴포넌트들이 있지만 snap turn provider와 continuous move provider에 집중하면 좋다. 우선, snap turn provider를 보면 right hand snap turn action의 use reference 체크가 되어있다. 이는 오른쪽 컨트롤러를 방향을 회전하는 컨트롤러로 사용하겠다는 이야기다. 또한, turn amount를 활용해 회전 각도도 정할 수 있다. continuous move provider의 Left hand move action의 use reference를 보면 역시 왼쪽 컨트롤러를 이동하는 컨트롤러로 사용하겠다는 의미이고, 이동속도와 중력에 영향을 받는지 등을 정할 수 있다.


<점수 취합 및 확인>

우리는 퀘스트 형식으로 게임이 진행되기 때문에, 각 퀘스트마다 환경오염도(점수)가 증가하거나 감소하게 된다. 이를 취합하고 ui로 확인하기 위해 점수와 관련한 스크립트를 짰다.

  • Scoring 스크립트
    : 전체 스코어가 저장되는 스크립트이다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Scoring : MonoBehaviour
{
    public static int totalScore;

}
  • ScoreManager 스크립트
    : 텍스트가 보이도록 start 함수에서 TextMeshProUGUI컴포넌트를 가져온다. 그리고 받은 점수를 계속 업데이트해서 pointText에 넣어준다.
    IncreaseScore 함수는 앞서 말한 totalScore를 5점 증가시키고 pointText에 증가된 점수를 넣어준다. ToggleIncreaseScore와 GetScore는 특정한 상황에서 쓰기위해 만들어 두었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;


public class ScoreManager : MonoBehaviour
{
   
    public TextMeshProUGUI pointText;
    //private int resource;
    void Start()
    {
        pointText = gameObject.GetComponent<TextMeshProUGUI>();
        //resource = 500;
    }
    
    void Update()
    {
        //pointText.text = resource.ToString();
        int receivedPoints = Scoring.totalScore;
        //Debug.Log(receivedPoints);
        pointText.text = receivedPoints.ToString();

    }

    public void IncreaseScore()
    {
        Scoring.totalScore += 5;
        Debug.Log(message: Scoring.totalScore);
        pointText.text = Scoring.totalScore.ToString();
    }

    public void ToggleIncreaseScore()
    {
        if (Toggles.totalToggle < 3)
        {
            IncreaseScore();
        }
        
    }

    public void GetScore()
    {
        int receivedPoints = Scoring.totalScore;
        pointText.text = receivedPoints.ToString();
    }
}

덕분에 이렇게 점수가 잘 보인다!

+) 우리는 점수를 보이게 하는 방법으로 지구가 플레이어를 따라다니게 해두었는데 점수가 보여야하기 때문에 지구가 투명해야한다. 지구와 같은 3D 오브젝트를 투명하게 만드는 방법은 Material의 shader을 아래와 같게 바꿔야한다.
그 후 색을 클릭해 A값을 낮춰준다면 위 사진처럼 3d 오브젝트가 투명해진다!

<오큘러스 컨트롤러를 이용해 버튼을 클릭하는 방법>

우선, 컨트롤러 내에서 캔버스를 보이게하는 것 부터 어려움을 겪었다.

버튼의 등장할 캔버스의 transform을 위 사진과 같이 설정하는 것을 추천한다. 그래야 유니티 내에서 캔버스가 어디에 있는지 찾기 쉽다. 또한, 캔버스의 렌더 모드는 항상 world space로 설정해야 vr내에 캔버스가 보인다! 버튼을 클릭하는 방법은 어렵지 않다. 일반적인 유니티 프로젝트 내에서 만들듯이 버튼을 만든 후 버튼이 들어있는 캔버스에 tracked device graphic raycaster을 넣어주면 클릭이 된다.

버튼의 Image는 버튼이 보였으면 하는 외형을 사진이나 그림파일로 넣어주면 된다. 또한, 버튼의 On Click() 이벤트는 그 버튼을 클릭했을 때 나타나는 변화를 의미하는데 popup이라는 오브젝트를 active하게 만들었고 SeeQuest스크립트의 DoClick이라는 함수를 실행했다. 또한, 퀘스트지구라는 오디오파일을 한번 들리게 했다.


<오큘러스 컨트롤러의 그립 버튼을 이용해 물건을 잡는 방법>

오큘러스 컨트롤러를 처음 활용해봤기 때문에 그립 버튼을 이용해 잡는 것도 쉽지 않았다. 이는 내가 잡고 싶은 물건에 XR Grab Interactable을 넣어주면 된다. 다만, 이는 내가 위에 쓴 Oculus 연결법을 사용했을 때 작동하는 것으로 보인다. 다른 연결방식을 사용하면 아마 작동하지 않을 것이다.

+) 나중에 발견한 유튜브인데 오큘러스 사용 초기에 보면 좋을 것 같다. 사용이 서툴다면 꼭 보길 바란다. 우리 팀도 미리 발견했으면 좋았을 것 같은 아쉬움이 있다. 유튜브 링크

<한 씬에서 다른 씬으로 씬 전환하는 방법>

유니티에서는 한 씬에서 다른 씬으로 씬을 전환해야할 때가 여러 번 있다. 특히 규모가 큰 프로젝트에서는 더욱 말이다. 우리 팀도 씬 전환을 여러번 진행했다. 씬 전환을 위해서는 우선, File>Build Settings>Scenes in Build에 전환 전 씬과 후 씬 모두 들어가있어야한다.

저 부분에 씬을 드래그하거나 Add Open Scenes를 이용해 씬을 추가할 수 있다. 우리는 대부분 콜라이더와 충돌을 이용해 씬을 이동했는데, 코드는 다음과 같다.

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

public class ContactController5 : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision)
    {

        Debug.Log("충돌 시작!");
        SceneManager.LoadScene("lv1_Restaurant");


    }
}

씬 전환을 위해서는 using UnityEngine.SceneManagement를 초기에 써줘야하고, 목표지점 씬의 이름을 SceneManager.LoadScene();의 괄호안에 ""를 붙여 넣어주면 된다. 충돌 인식에 문제 없다면 된다면 씬의 전환은 수월하게 진행될 것이다.


<몬스터가 물건을 흘리고 다니게 하기>

우리 게임에서는 플레이어가 퀘스트를 하면서 쓰레기를 흘리며 환경을 파괴하는 쓰레기 괴물을 처치해야한다. 이를 구현하기 위해 몬스터가 쓰레기를 흘리고다니는 코드를 짰다.

DropItems 스크립트:

우선 흘리고다닐 아이템들을 itemList에 넣기 위해 itemList를 만든다. 나는 아이템 리스트에 5개의 아이템을 넣었기 때문에 if문의 랜덤수를 80이상, 60이상 80 미만, 40이상 60 미만, 20이상 40미만, 20미만으로 해 짰는데 이것은 자유롭게 변형하면 된다. Instantiate를 이용해 itemList의 itemNum번째 아이템을 나타나게 하고 환경오염도를 5점 증가시킨 후 코루틴을 이용해 2초 기다리게 했다. 즉, 몬스터가 2초마다 쓰레기를 버리고 그 쓰레기로 인해 환경오염도가 5점씩 증가하는 것이다.

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

public class DropItems : MonoBehaviour
{
    [SerializeField]
    private GameObject[] itemList;
    private int itemNum;
    private int randNum;
    private Transform Epos;
    public bool check = true;
    
    // Start is called before the first frame update
    void Start()
    {
        Epos = GetComponent<Transform>();
        Debug.Log(itemList);
        
    }

    // Update is called once per frame
    void Update()
    {
        randNum = Random.Range(0, 101);
        
        if (randNum>=80&check)
        {
            check= false;
            itemNum = 1;
            Instantiate(itemList[itemNum], Epos.position, Quaternion.identity);
            Scoring.totalScore += 5;
            Debug.Log(message: Scoring.totalScore);
            StartCoroutine(WaitForIt());
        }

        if (randNum >= 60 & randNum<80 & check)
        {
            check = false;
            itemNum = 2;
            Instantiate(itemList[itemNum], Epos.position, Quaternion.identity);
            Scoring.totalScore += 5;
            Debug.Log(message: Scoring.totalScore);
            StartCoroutine(WaitForIt());
        }
        if (randNum >= 40 & randNum < 60 & check)
        {
            check = false;
            itemNum = 3;
            Instantiate(itemList[itemNum], Epos.position, Quaternion.identity);
            Scoring.totalScore += 5;
            Debug.Log(message: Scoring.totalScore);
            StartCoroutine(WaitForIt());
        }
        if (randNum >= 20 & randNum < 40 & check)
        {
            check = false;
            itemNum = 4;
            Instantiate(itemList[itemNum], Epos.position, Quaternion.identity);
            Scoring.totalScore += 5;
            Debug.Log(message: Scoring.totalScore);
            StartCoroutine(WaitForIt());
        }
        
        if (randNum >= 0 & randNum < 20 & check)
        {
            check = false;
            itemNum = 5;
            Instantiate(itemList[itemNum], Epos.position, Quaternion.identity);
            Scoring.totalScore += 5;
            Debug.Log(message: Scoring.totalScore);
            StartCoroutine(WaitForIt());
        }

    }

    IEnumerator WaitForIt()
    {
        yield return new WaitForSeconds(2.0f);
        check= true;
    }
}

이 코드를 변형해서 몬스터가 죽을 때 아이템을 흘리게한다거나 하는 코드로 발전시킬 수 있을 것이다.
아이템 리스트는 아래 사진처럼 인스펙터창에서 원하는 오브젝트를 넣으면 된다.


<xr origin에 direct interactor와 ray interactor 한꺼번에 넣는 법>

이건 쓰기도 조금 민망하지만, 그냥 넣으면 된다. 하지만, 중요한 것은 한 인스펙터창에 넣으면 안된다는 것이다.

이 위의 인스펙터 창에는 direct interactor가 이미 있는 상태이다. 여기서 ray interactor를 넣으려면 이미 인터랙터가 있기 때문에, 넣을 수 없다고 뜬다. 이 과정에서, 우리는 총을 들어야하기 때문에 direct interactor가 필요했고, 버튼을 ray로 클릭해야하기 때문에 ray interactor가 필요했다. 하지만, 넣을 수 없다고 떠서, 총으로 쏴서 버튼을 클릭해야하나,. 하는 생각까지 했었다. 그런데 허무하게도 그냥 hierarchy창에 빈오브젝트를 만들어서 그 안에 ray interactor를 넣어서 추가하면 된다!

빨간 색 밑줄이 direct interactor가 들어있는 object이고 파란색 밑줄이 ray interactor가 들어있는 object이다.
혹시 각 오브젝트에 어떤 컴포넌트가 들어있는지 궁금한 사람이 있을까해 사진도 첨부한다.

  • direct interactor
  • ray interactor
profile
낄리의 개발 블로그╰(*°▽°*)╯

0개의 댓글