Profile

대혐수의 뒷동산

대혐수

[유니티] 주먹구구 탐정게임 만들기 - 장소이동 메뉴 고치기

 

 UI만 NGUI에서 UGUI로 고치면 되는데 왜 문제가 생기느냐...라고 생각했는데, 점검해보니 이전부터 있었던 문제를 내가 미처 알지 못했을 뿐이었던 것 같다.

 

 장소이동 메뉴는 위와 같이 되어있다. 이미지들은 임시용이거나 예전에 만든 비주얼노벨 이미지 재활용인데, 오른쪽 위의 인물을 여기서 쓰는 건 엄밀히 말해 무단도용-_-;;; 이기 때문에, 출처를 밝혀둔다. 저 그림 그리신 분은 구름과자 작가님으로, 해당 그림은 나와 협업한 《슬픔을 나에게》에서 사용되었다.

   

  저 장소이동 메뉴의 작동방식은 이렇다.

 

1.메뉴 가운데의 이미지는 이동하고자 하는 장소를 미리 보여주는 이미지다.

2.메뉴 내용 갱신하기 : 메뉴 좌 / 우의 화살표 버튼을 누르면 이동 가능한 장소를 이전/다음으로 넘긴다(가운데 장소 미리보기 이미지와 장소 이름, 장소 설명이 바뀜)

3.메뉴 좌하단 "이동한다"를 클릭하면 메뉴상에서 미리 보여지는 장소로 이동된다. "그만둔다"를 누르면 이동 메뉴를 끈다.

 

  지금 문제가 되는 건 두 번째 부분인 "메뉴 내용 갱신하기"이다. 이동 가능한 장소는 장소정보class 배열로 기록되어있다.  "이동하기"가 눌려지는 순간 현재 가리키고 있는 배열의 장소정보class에 담긴 장소명을 가져와

Instantiate(Resources.Load("Locations/" + 장소명).....

  위와 같이 불러내는 방식이다. 그러니까 이전/이후 버튼을 누를 때마다 참조(이 용어가 맞나?)하고자 하는 배열 인덱스를 앞 뒤로 옮겨가게 되는 건데, 여기서 문제가 생긴다. 다음 규칙 때문이다.

 

  -이전/다음 버튼이 눌렸을 때 가리킨 주소의 장소로 아직 이동할 수 없거나(장소 클래스의 bool변수로 관리되는데, 배열이 퍼블릭으로 선언되어있는 관계로 인스펙터에서 컨트롤 가능), 현재 플레이어가 위치한 장소인 경우 그 다음 인덱스에 접근한다.

  -그 인덱스도 마찬가지 조건에 걸리면 또 다음 인덱스로 넘어간다. 조건이 클리어될 때 까지 반복.

  -만약 첫 번째 인덱스/마지막 인덱스에 접근하는 상황에서 이전/다음 인덱스로 넘어가게 되면, 배열의 마지막/첫 번째 인덱스를 가리킬것. 단, 이 때에도 아직 이동할 수 없는 장소거나 현재 플레이어가 위치한 장소인 경우 이전/다음으로 넘어간다.

 

  발생한 문제는 1.현 위치의 장소가 메뉴상에 표시됨. 2.아직 막혀있어야 할 장소가 메뉴에 나옴.

 

  즉 총체적 난관이다...

 

  거두절미하고, 끙끙거리며 원인을 알아보니 다음과 같은 문제가 있었다.

  일단 기존에 사용한 처리 방식은, 버튼이 눌리면 접근하는 배열 인덱스 값을 --히거나 ++한 뒤, "현 위치인가? || 아직 이동 불가능한 장소인가?"를 if문으로 검사해서, 그렇다고 하면 조건에 맞는 인덱스가 나올 때 까지 while문으로 인덱스 값을 올리게 되어있다. 그런데 오작동이 난 이유는...

 

  1.이동 가능한 장소가 "현 위치"딱 하나 밖에 없는 경우를 따로 처리하지 않았음 : 이동가능 장소 첫 번째에는 탐정사무소가 있다. 즉 이동 메뉴를 열면 탐정사무소부터 나온다. 내 생각에 탐정사무소는 항상 이동가능한 장소일 테니까, 메뉴를 열자마자 "여기가 이동 가능한 장소인가 아닌가?"를 체크할 필요는 없다. 하지만 이동 가능한 장소가 탐정사무소밖에 없는 경우(게임이 막 시작했을 때에는 탐정사무소 이외의 장소로는 갈 일이 없을 것이다)뿐만 아니라 이동 자체가 금지되어 있는 경우(강제 이벤트로 인하여 현재 장소를 이탈하면 안 되는 경우)도 있을 것이기 때문에, 이와 같은 조건에서는 이동 메뉴가 아예 열리지 않도록 처리하는 게 좋겠다.

 

2.처음에 버튼을 클릭한 후 if문으로 현 위치인지, 아직 이동이 불가능한 장소인지 여부를 검사하지만, while문에서는 이동 가능한 장소인지 여부만 검사하고 현 위치인지 여부는 검사를 안 함.

 

3.while문 도중 첫 페이지 이전으로 가는 경우/마지막 페이지를 넘어가는 경우를 따로 처리해두지 않음.

 

 

  솔직히 말해 NGUI버전 당시 왜 문제가 없다고 생각했었는지 이해가 안 갈 정도이다.

 

 

  아무튼 1번 문제는 "이동메뉴가 안 열리도록 처리하는 함수"를 만들어서 해결하기로 했다. 아마 이동 메뉴 대신 "지금은 이동할 상황이 아니다"같은 독백대사가 나오도록 하면 될 것 같다. 탐정사무소 이외에 이동할 장소가 없거나, 강제이벤트로 이동이 금지된 경우 이 함수를 호출하면 될 것이다.

 

  2번 문제는, 생각해보니 처음에 if문으로 검사할 필요가 없다. 그냥 while문에서 "이동 가능한가?" "현 위치인가?"를 검사하면 그만이라는 걸 깨달았다. 3번 문제는 while문 안에 if문으로 첫/마지막 인덱스를 넘어가버렸는지 여부를 체크하면 될 거 같고.

 

  그래서 아래와 같이 해봄.

 

//코드 안의 한글은 변수명이나 메서드를 실제 스크립트에서의 명칭이 아니라
//역할 설명용으로 적절히 고쳐놓은 것임

 public void 이동메뉴열기()
    {              

        if (이동메뉴OnOff)  //장소이동 메뉴가 켜있다면(true)
        {
            이동메뉴오브젝트.gameObject.SetActive(false); //메뉴를 끈다.
            destinationMenuOnOff = false;
            //그리고 메뉴를 생성하면서 변경되었던 변수들을 재초기화 해야한다.            
            이동메뉴페이지번호 = 0; //페이지는 다시 첫페이지(0)를 가리키도록...          

        }
        else
        {
            이동메뉴오브젝트.gameObject.SetActive(true); //반대의 경우 메뉴를 켠다.
            액션메뉴이동메서드();  //액션목록 메뉴는 도로 넣는다.
            이동메뉴OnOff = true;
            //여기서 펼쳐지는 것은 꺼져있던 이동메뉴가 열릴 때의 과정이다. 
            //다음, 만약 이제 펼쳐질 페이지가 현재 장소와 같다면(=현위치가 사무실이라면) 다음 페이지가 펼쳐지도록 조치. 
            if (장소목록배열[이동메뉴페이지번호].장소이름문자열 == 현위치이름)
            {
                다음페이지로넘기기체크메서드(); //이 메서드가 실행되면 
                //이동 가능한 장소의 페이지번호에 도달할 때 까지 페이지번호가 계속 증가된다.
            }              
            
            장소이동메뉴갱신메서드(); //결정된 페이지번호에 따라 장소이동 메뉴의 내용을
            //갱신한다.
            
        }
    }
    
    
    public void 이동메뉴다음페이지버튼메서드() 
    {
        
        if (이동메뉴페이지번호 >= 장소목록배열.Length - 1)
        { //일단 버튼이 눌린 시점에 마지막페이지라면 다시 첫 페이지로 돌린다. 
            이동메뉴페이지번호 = 0; //첫페이지, 즉 사무실은 항상 해금되어있으므로 장소체크는 필요없다.            
        }

        else 이동메뉴페이지번호++;  

        다음페이지로넘기기체크메서드(); 
        장소이동메뉴갱신메서드();    
    }
    
    
    void 다음페이지로넘기기체크메서드()  
    {
        while (장소목록배열[이동메뉴페이지번호].해금여부 == false || 장소목록배열[이동메뉴페이지번호].장소명 == 현위치이름) 
        {//접근하려는 인덱스가 해금되지 않았거나 현위치인 한 계속해서...
            이동메뉴페이지번호++; //하나 올림 
            
            if (이동메뉴페이지번호 >= 장소이동목록배열.Length - 1)
            { //그렇게 올라간 값이 막페이지를 넘기면 다시 처음으로 돌려놓음 
               이동메뉴페이지번호 = 0; //첫페이지, 즉 사무실은 항상 해금되어있으므로 별도의 처리가 필요없다.            
            }

        }
    }
    
    
    public void 이동메뉴이전페이지버튼메서드() 
    {
        if (이동메뉴페이지번호 == 0)  //버튼이 눌린 시점에 페이지번호가 0이라면
        {
            이동메뉴페이지번호 = 장소목록배열.Length - 1; //목록의 마지막을 가리키도록함.            
           //마지막 페이지의 장소는 아직 해금되지 않았을수도 있으므로 반드시 췍해줘야 한다.
        }

        else 이동메뉴페이지번호--;
        
        이전페이지로넘기기체크메서드(); 
        
       

        장소이동갱신메서드(); 

        void 이전페이지로넘기기체크메서드() //이 메서드는 다른 데서 쓸 일이 없을 듯 해 메서드 안 메서드로 처리 
        {
            while (장소목록배열[이동메뉴페이지번호].해금여부 == false || 장소목록배열[이동메뉴페이지번호].장소명 == 현위치이름) 
            {
                이동메뉴페이지번호--;
                //그런데, 만약 현위치가 사무실(=첫 인덱스)이라면, 조건에 의해 while문이 실행되어 페이지번호가 0이하로 떨어지게 된다.
                //이대로 가면 오류가 나므로..
                if (이동메뉴페이지번호 <= 0)
                {
                    이동메뉴페이지번호 = 장소목록배열.Length - 1; //목록의 마지막을 가리키도록함. ;
                }                   
                                
            }
        }
    }

    

 

  결론적으로 말하자면 다 해결했다. 

  문과뇌로 수고 많았다. 나를 칭찬해~

 

  다만 코딩의 재미가 기능 하나하나 구현해낼 때의 "해냈다!"감성인데, 진도는 못 나가고 기존에 짠 코드나 쪼물딱하니 영 보람이 없다.

 

  아직 "이동 가능한 곳이 사무실밖에 없는 경우"를 어떻게 판단할지 방법을 떠올리지 못했다. 그리고 이동불가 메서드도 안 만들었고.