오큘러스 고에서 검정 화면이 계속해서 나타났다.

시스템 오류로 인식하고 리셋 해버렸다

지금 보니 안면 인식 센서에서 얼굴 인식 실패 때문에 일어난 오류였다.

그래서 모든 세팅을 다시하고 유니티에서 빌드 하려는 순간 

유니티가 오큘러스 고를 찾지 못했다.

그 이유는 개발자 모드가 꺼저 있었기 때문이다. 

<개발자 모드 키는 방법>

오큘러스 고 앱 -> 설정 -> 연결된 기기 화살표 내림 -> 설정 더보기 -> 개발자 모드 

안녕하세요 이번 시간에는 저번 시간에 이어서 진행됩니다.

 

먼저 이번 시간을 통해 할 수 있는 것 들을 보여드리겠습니다.

 

 

위의 이미지에서 물뿌리개를 사용해 물을 주는 애니메이션과 비료통을 흔들어 비료를 주는 애니메이션을 

확인할 수 있습니다.

 

 

이번에는 목표지점으로 이동하기

 

위 xyz 화살표는 도착지라는 객체로 해당 지점으로 npc가 이동하게 됩니다. 

 

 

저번 시간에 만들었던 LookAtPlayer에 변화가 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class LookAtPlayer : MonoBehaviour
{
    // 밑의 변수로 플레이어가 근처에 있는지 알 수 있다.
    public static int player_in;
    public static bool off_panel;
    // 유니티 인스펙터 창에서 패널을 넣어주자
    public GameObject Intro_panel;
 
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player" && farmer_animator.IsMove == false)
        {
            player_in = 1;
            Intro_panel.SetActive(true);
        }
    }
    private void OnTriggerStay(Collider other)
    {
        if (other.tag == "Player" && farmer_animator.IsMove == false)
        {
            // 플레이어를 바라보게 만든다
            transform.LookAt(other.transform);
            Intro_panel.SetActive(true);
 
        }
        else
        {
            Intro_panel.SetActive(false);
        }
 
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "Player")
        {
            player_in = 0;
            Intro_panel.SetActive(false);
        }
    }
 
    
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

IsMove라는 bool 변수가 트리거의 조건에 추가되었습니다.

위의 변수는 NPC농부가 이동할 때  TRUE가 되는 값입니다.

NPC가 이동 중에는 플레이어에 반응 하지 않도록 하였습니다.

 

NPC의 이동을 보여드리겠습니다.

 

이동하는 모습을 보면 방향 커서를 따라 이동하는 것을 볼 수 있습니다.

커서는 empty object로 태그를 target이라고 정했습니다.

 

 

움짤을 만들어서 올리니 생각난게 npc에 새로운 애니메이션을 추가한 게 있습니다.

따라서 모델을 다시 한번더 올리도록 하겠습니다.

 

farmer_02.fbx
0.44MB

추가된 애니메이션으로는 뛰기, 비료주기, 삽질하기, 물 주기입니다.

 

비료 통이나, 삽 같은 오브젝트도 같이 사용해보려 해 보았지만

시간만 날렸네요 ㅠㅠ 유니티에 따로 추가하도록 하겠습니다.

 

비료주기 

 

 

 

밑의 코드는 automove라는 코드입니다. 

영어 뜻 그대로 자동 움직임이라는 뜻인데

도착지를 정하면 도착지로 이동하는 코드입니다.

farmer_animator라는 스크립트에 있는 변수를 사용하고 있어서

밑의 스크립트를 먼저 만들게 되면 오류가 생기게 될 수 있습니다.

지금은 무시하셔도 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class automove : MonoBehaviour
{
    public GameObject target;
 
    public GameObject Fertilize_box;
    public GameObject Water_box;
    public GameObject Shovel;
 
    // Start is called before the first frame update
    void Start()
    {
        
    }
 
    // Update is called once per frame
    void Update()
    {
        MoveToTarget();
        showing_Fertilize_Box();
        showing_Water_box();
    }
 
    void MoveToTarget()
    {
        if (target.transform.position != Vector3.MoveTowards(transform.position, target.transform.position, 1f))
        {
            transform.position = Vector3.MoveTowards(transform.position, target.transform.position, 1f); // 현위치, 도착점, 속도
 
            transform.LookAt(target.transform);
       
        }
        else
            farmer_animator.IsMove = false;
    }
 
    void showing_Fertilize_Box()
    {
        if (farmer_animator.Fertilize_on == true && Fertilize_box != null)
        {
            if (Fertilize_box.activeSelf == false)
                Fertilize_box.SetActive(true);
        }
        else
        {
            Fertilize_box.SetActive(false);
        }
    }
 
    void showing_Water_box()
    {
        if (farmer_animator.Water_on == true && Water_box != null)
        {
            if (Water_box.activeSelf == false)
                Water_box.SetActive(true);
        }
        else
        {
            Water_box.SetActive(false);
        }
    }
 
}
 
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs
 

우선 automove스크립트의 핵심 메서드 MoveToTarget에 대해 설명하겠습니다.

MoveToTarget은 영어 뜻 그대로 목표지점으로 이동하라는 메서드입니다.

핵심 코드를 살펴보겠습니다. 

 

transform.position = 

Vector3.MoveTowards(transform.position, target.transform.position, 1f); 

// 현 위치, 도착점, 속도

 

MoveTowards라는 내장 함수는 매개변수 값으로 받은 속도 값으로 도착점을 향해

지정된 오브젝트를 이동시킵니다.

그리고 밑에 LookAt은 npc가 이동할 때 목표점을 바라보며 이동할 수 있도록 해주기

위해서 추가되었습니다.

 

 

showing_Fertilize_box는 아까 비료주기 애니메이션에서 비료통이

없던걸 보셨습니다. 위의 함수는  

green_box라는 비료통을 애니메이션 실행에 맞추어

보여주는 함수입니다.

showing_Water_box()는 물뿌리개를 보여주는 함수입니다.

automove 스크립트는 farmer_controller에 추가해주세요

스크립트 설명은 여기까지 하고, 히어라키 창에 엠티 오브젝트를 생성하여서

목표점으로 사용하고 실행해서 되는지 안되는지 확인해주세요

 

green_box.fbx는 비료통입니다.  혹시 구현해볼 분은 사용해주세요

 

green_box.fbx
0.03MB

 

모델 안에 라이트랑 카메라가 포함돼있는데, 제거해주시면 됩니다.

 

<애니메이션 넣기>

 

 

이번엔 애니메이션을 넣도록 하겠습니다.

 

위의 이미지처럼 npc 모델 객체 자식으로 green_box와 water_can을 넣어주세요

water_can은 물뿌리개입니다. 물뿌리개는 없으면 큐브로 대체합니다.

 

애니메이션이 새로 추가되었기 때문에 새로운 모델을 사용해야 합니다.

기존 모델을 삭제하고 저번 시간에 했던 것처럼 Idle 애니메이션 까지는

이전 글을 참조해주세요

 

 

 

애니메이터에 애니메이션을 삽입하는 방법은 

프로젝트 창에서 모델 오른쪽 플레이 버튼을 클릭하여

모델을 펼치면 모델 구성 요소를 볼 수 있습니다.

거기서 애니메이션을 애니메이터에 드래그엔 드롭하시면 됩니다.

 

이번에는 애니메이터에 파라미터를 추가합니다.

 

 

애니메이터 좌측에 보면 파라미터를 추가할 수 있는 공간이

있습니다. 위의 이미지처럼 IsMove,..., Water_on을 bool 타입으로 추가해주세요

 

 

위의 이미지처럼 Armature|run 과 Idle 애니메이션 사이에 있는

화살표를 만들어 주세요 Idle 우클릭하면 make Transaction으로 추가 가능합니다.

다음으로 화살표를 더블클릭합니다.

인스펙터 창에 위와 같이 나타납니다.

Has Exit Time 체크를 해제합니다.

해제하는 이유는 체크돼있을 경우 실행되고 있는 애니메이션이 종료돼야

다음 애니메이션을 진행하게 됩니다.

예를 들어 목표점으로 이동하기 위해 뛰기 애니메이션을 실행해야 하는데

숨시기 애니메이션이 끝나지 않았으므로 끝날 때까지 기다린 후 

뛰기 애니메이션이 실행되게 됩니다. 그러면 npc가 이동 중인데

뛰지 않는 현상이 발생할 수도 있습니다.

has exit time 아래에 보면 조건을 지정할 수 있습니다.

Idle -> run 일 때는 IsMove값을 true

Idle <- run 일 때는 IsMove값을 false

넣어주시기 바랍니다.

마찬가지로

각 애니메이션에 화살표에도 이름에 해당하는 조건을 false로 지정해주세요

 

이제 run 애니메이션을 실행할 준비가 되었으니 밑의 코드를 통해서

애니메이션이 실행될 수 있도록 하겠습니다.

아래의 코드는 애니메이션을 수행하는 모델에 넣어주시기 바랍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class farmer_animator : MonoBehaviour
{
    Animator animator;
    public static bool IsMove = false;
    public static bool Fertilize_on = false;
    public static bool Water_on = false;
    public static bool Shovel_on = false;
 
 
    // Use this for initialization
    void Start()
    {
        animator = GetComponent<Animator>();
    }
 
    // Update is called once per frame
    void Update()
    {
        animator.SetBool("IsMove", IsMove);
        animator.SetBool("Fertilize_on", Fertilize_on);
        animator.SetBool("Water_on", Water_on);
        animator.SetBool("Shovel_on", Shovel_on);
 
        PlayAnimation();
    }
 
    void PlayAnimation()
    {
        if (IsMove == false && Fertilize_on == false && Water_on == false && Shovel_on == false)
        {
            if (Input.GetKeyDown(KeyCode.X))
            {
                animator.Play("Armature|Fertilize"-10);
                Fertilize_on = true;
            }
            else if (Input.GetKeyDown(KeyCode.Z))
            {
                animator.Play("Armature|water"-10);
                Water_on = true;
            }
        }
    }
 
    public void invisible_Fertilize_box()
    {
        if (Fertilize_on)
        {
            Fertilize_on = false;
        }
    }
    public void invisible_Water_box()
    {
        if (Water_on)
        {
            Water_on = false;
        }
    }
 
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

 

        animator.SetBool("IsMove", IsMove);

        animator.SetBool("Fertilize_on", Fertilize_on);

        animator.SetBool("Water_on", Water_on);

        animator.SetBool("Shovel_on", Shovel_on);

위의 코드는 아까 애니메이터에서 생성했던 파라미터의 값에 접근하게 해 줍니다.

 

여기서 run 애니메이션이 실행되는 원리를 말하자면 

automove 스크립트의 메서드인 MoveToTarget()에서 

이동시에 IsMove 값에 true를 넣게 됩니다. 그렇게 되면 

뛰기 애니메이션이 바로 실행 되게 됩니다.

목표점에 도달하거나 가만히 있게 되면 IsMove값에 false를 넣어 다시

숨쉬기 애니메이션으로 돌아오게 됩니다. 

 

다음으로, 메서드별로 설명을 하겠습니다.

첫 번째 메소드 PlayAnimation()은 실행되고 있는 애니메이션이 없을 때

애니메이션을 실행시키도록 하는 메서드입니다.

플레이어가 x를 눌렀을 때 실행되는 코드를 살펴보면

 animator.Play("Armature|Fertilize"-10);

이것은 

Armature|Fertilize라는 애니메이션을 실행하게 도와주는 메서드입니다.

 Fertilize_on = true; 이 코드를 실행시키면

비료 통이 보이게 됩니다.

그렇게 되면 비료주기 애니메이션이 실행되면서 비료통이 보이게 됩니다.

 

그다음에는

public void invisible_Fertilize_box() 라는 메서드가 있습니다.

밑의 애니메이션은 이제 비료주기 애니메이션이 끝났을 때

박스를 다시 안 보이는 상태로 변환하기 위해서 사용됩니다.

애니메이션이 끝났을 때 사용하기 위해서 콜백 메서드 형식으로 사용하게 됩니다.

 

<애니메이션에 특정 시점에 실행될 메소드 넣기>

다시 애니메이터로 돌아옵니다.

 

위의 버튼 Fertilize를 더블클릭하면

아래의 창이 나타납니다.

 

여기서 아래쪽으로 드래그합니다.

 

이벤트라는 항목이 있습니다.

펼쳐주고

위의 이모티콘을 눌러줍니다.

그러면 시간 줄에 커서 같은 게 생기는데 가장 오른쪽으로 당겨 둡니다.

그렇게 되면 애니메이션 종료 시점에서 이벤트가 실행되게 됩니다.

이번에는 function에다가 위의 이미지 함수 명처럼 넣어주세요

Float, Int, String은 매개변수가 있을 경우 사용합니다. 우리는 없으니 사용하지 않습니다.

이번에는 Object에다가 해당 메서드가 있는 스크립트를 넣습니다.

여기서 주의할 점은 스크립트가 애니메이션을 실행하는 모델에 속해있지

않으면 오류가 발생할 수 있습니다. 고로 해당 스크립트는 모델에 속해있어야 합니다.

이제 apply 버튼을 눌러 적용하면 됩니다.

invisible_Water_box()함수 또한 위처럼 적용해주시면 됩니다.

 

<비료통 & 물뿌리개 움직임 넣는 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class autoRotation : MonoBehaviour
{
 
    int i = 0;
    float x, y, z;
    // Start is called before the first frame update
    void Start()
    {
        x = transform.rotation.x;
        y = transform.rotation.y;
        z = transform.rotation.z;
    }
 
    // Update is called once per frame
    void Update()
    {
        if (i % 14 >= 7)
            transform.Rotate(00-4);
        else
            transform.Rotate(004);
        i++;
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

위의 코드를 비료통과, 물뿌리개에 적용 시켜주면 비료통과 물뿌리개가 움직이게 됩니다.

 

이제 모든 것이 완료되면

 

이렇게 됩니다.

https://www.youtube.com/watch?v=zBOEHxsSaLI

이번 시간에는 NPC와의 세세한 대화를 위해서 버튼을 클릭하여 여러 가지 대화가 가능하도록

해보았습니다.

 

간략하게 어떻게 하는지 설명하도록 하겠습니다.

 

오른쪽 히어라키 창을 보면 tip_1이라는 객체가 있습니다.

이는 panal이고 투명도를 255로 두었습니다.

투명도를 최대치로 맞춘 이유는 tip_1 패널이 Intro_panel에 속해있어서

Intro_panel 위에 패널을 새로 그리는 것이기 때문에 배경이 세로 생겨나게 되면 

배경이 진해지기 때문입니다. 

 

저번에 보여드린 text_log 의 코드 또한 여러 가지 대화가 추가되면서

많이 바뀌게 되었습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
[System.Serializable]
public class Dialogue
{
    // 여러줄을 쓸 수 있게 해준다.
    [TextArea]
    public string dialogue;
}
 
public class text_log : MonoBehaviour
{
    static public int npc_talk_int=0;
    public Text txt_Dialogue;
    public Button button_1;
    public Button button_2;
    public Button button_3;
    public GameObject panel;
 
    public Button tip_button_1;
    public Button tip_button_2;
    public Button tip_button_exit;
    // [SerializeField] 을 달면 유니티 inspector 창에서 해당 변수를 조작 할 수 있다.
 
    static public bool isDialogue = true;
 
    // 대화가 얼마나 진행 되었는지 확인
    private int count = 0;
 
    static private String[] dialogue; // 대화가 들어가는 배열
 
    private void Awake()
    {
        dialogue = new String[100];
 
        // 처음 intro 대사
        dialogue[0= "[농부]\n\n안녕하세요 플레이어님 반가워요";
        dialogue[1= "[농부]\n\n날씨가 아주 화창하군요! 감자 심기 좋은날이네요";
        dialogue[2= "[농부]\n\n그럼 지금 감자 심기를 배워 보도록 하겠습니다.";
        dialogue[3= "[농부]\n\n혹시 추가적으로 감자에 관해 알고 싶다면 앞에 저를 레이저 포인터로 클릭 해주세요";
        dialogue[4= "[농부]\n\n레이저 포인터는 뒤로가기 버튼을 클릭하면 켜집니다.";
        dialogue[5= "[농부]\n\n다시 누르면 꺼집니다.";
        dialogue[6= "[농부]\n\n레이저 포인터가 켜져있는 상태로, 리모컨의 큰 원을 클릭하세요 ";
        dialogue[7= "[농부]\n\n그럼 레이저 포인터가 가르키고 있는 대상과 상호 작용 할 수 있게 됩니다.";
        dialogue[8= "[농부]\n\n준비가 되었다면 저에게 알려주세요";
        dialogue[9= "[농부]\n\n준비가 되었다면 저에게 알려주세요";
        dialogue[10]  = "end";
      
        //main_대사
        dialogue[12= "[농부]\n\n무었을 도와드릴까요?";
 
        // 감자 특징 설명
        dialogue[15= "[농부]\n\n감자는 뿌리가 아닌 줄기가 비대 된 것으로 대표적인 구황작물(기후가 불순하여 흉년이 들어 곡식이 부족할 때 주곡 대신 소비할 수 있는 작물)입니다." +
         "\n감자는 재배기간이 짧아서 서늘한 이른 봄에 심어 여름 장마가 시작되기 전에 수확할 수 있습니다.";
        dialogue[16= "[농부]\n\n더워지면서 힘들고 지루해 질 수 있는 텃밭 활동에 감자를 수확함으로써 즐거움과 성취감을 느낄 수 있습니다." +
        "\n감자의 80 % 는 수분으로 이루어져 있으며 주성분은 전분이다.맛이 좋고, 포만감을 주어 다양한 요리에 쓰입니다.";
        dialogue[17= "[농부]\n\n또한 껍질은 주방에서 물때와 기름때, 옷에 묻은 얼룩을 쉽게 제거할 수 있습니다." +
        "\n씨감자는 일반 종묘상에서 구할 수 있는데 초보자는 크기가 큰 수미감자를 선택하는 것이 좋습니다.";
        dialogue[18= "[농부]\n\n수확한 감자를 씨감자로 사용할 수 있으나 바이러스 감염이나 수확량에 영향을 줄 수 있으므로 사용하지 않는 것이 좋습니다." +
        "\n휴면성이 있어서 수확 후 바로 싹이 나지 않으며 90~100일 정도 지나야 싹이 나므로 비교적 장기간 실온 보관을 할 수 있습니다.";
        dialogue[19= "[농부]\n\n수확한 감자를 씨감자로 사용할 수 있으나 바이러스 감염이나 수확량에 영향을 줄 수 있으므로 사용하지 않는 것이 좋습니다." +
        "\n휴면성이 있어서 수확 후 바로 싹이 나지 않으며 90~100일 정도 지나야 싹이 나므로 비교적 장기간 실온 보관을 할 수 있습니다.";
        dialogue[20= "end";
 
 
        dialogue[21= "";
        
        
        // 플레이어 이동 & 비료주기
        dialogue[26=  "<TIP>\n\n- 플레이어는 원형 버튼 터치를 이용하여 플레이어가 바라보는 방향으로 이동 가능합니다." +
        "\n- 플레이어는 빈 손 상태인 경우 아이템을 착용할 수 있습니다." +
        "\n- 아이템이 착용된 상태인 경우 “뒤로가기” 버튼을 누르면 해제됩니다." +
        "\n\n<HOW TO USE>\n- 비료가 담긴 바구니를 “트리거 버튼”을 이용하여 착용합니다. " +
        "\n- 지정된 트리거에 위치하여 “트리거 버튼”을 눌러 비료를 뿌립니다.";
        dialogue[27= "<TIP>\n\n- 플레이어는 원형 버튼 터치를 이용하여 플레이어가 바라보는 방향으로 이동 가능합니다." +
        "\n- 플레이어는 빈 손 상태인 경우 아이템을 착용할 수 있습니다." +
        "\n- 아이템이 착용된 상태인 경우 “뒤로가기” 버튼을 누르면 해제됩니다." +
        "\n\n<HOW TO USE>\n- 비료가 담긴 바구니를 “트리거 버튼”을 이용하여 착용합니다. " +
        "\n- 지정된 트리거에 위치하여 “트리거 버튼”을 눌러 비료를 뿌립니다.";
        dialogue[28= "end";
 
        // 삽 & 모종삽 사용하기
        dialogue[29= "<HOW TO USE>\n- 삽을 들고 “트리거 버튼”을 누르면 “두둑 트리거”가 활성화됩니다. "
        + "\n\n- 삽에 흙이 없는 상태인 경우\n   - “삽질용 트리거”만 활성화되며, “트리거 버튼”을 누르면 삽질 가능. "
        + "\n    - 특정 트리거에 삽질하면 해당 지점의 높이가 낮아지고, 삽에 흙이 쌓임."
        + "\n    - 기준치 이상 삽질하면 비활성화됨."
        + "\n\n- 삽에 흙이 있는 상태인 경우"
        + "\n    - “두둑용 트리거”만 활성화되며, “트리거 버튼”을 누르면 두둑 가능."
        + "\n    - 특정 트리거에 두둑하면 해당 지점의 높이가 높아지고, 삽에 흙 x."
        + "\n    - 기준치 이상 두둑하면 비활성화됨.";
        dialogue[30= "<HOW TO USE>\n"
            + "\n  - 모종삽을 들고 “트리거 버튼”을 누르면 “두둑 구멍 트리거”가 활성화됩니다."
            + "\n  - 모종삽을 든 상태에서 쌓인 흙을 향하여 “트리거 버튼”을 누르면 흙이 파집니다."
            + "\n  - 일정량 흙을 파게 될 경우 “두둑 구멍 트리거”가 비활성화됩니다.";
        dialogue[31= "<HOW TO USE>\n"
            + "\n  - 모종삽을 들고 “트리거 버튼”을 누르면 “두둑 구멍 트리거”가 활성화됩니다."
            + "\n  - 모종삽을 든 상태에서 쌓인 흙을 향하여 “트리거 버튼”을 누르면 흙이 파집니다."
            + "\n  - 일정량 흙을 파게 될 경우 “두둑 구멍 트리거”가 비활성화됩니다.";
        dialogue[32= "end";
 
 
        //씨감자 사용하기
        dialogue[33= "<HOW TO USE>\n" 
            + "\n  - 씨감자가 담긴 박스를 향하여 “트리거 버튼”을 누르면 일정량의 씨감자(10개)를 얻습니다."
            + "\n  - 씨감자를 드는 순간 “구멍 콜라이더”가 활성화됩니다."
            + "\n  - 씨감자를 든 상태에서 구멍 콜라이더를 향하여 “트리거 버튼”을 누르면 심어집니다.";
        dialogue[34= "<HOW TO USE>\n"
            + "- 모종삽을 든 상태에서 주변의 흙을 향하여 “트리거 버튼”을 누르면 흙이 생성됩니다."
            + "\n  - 흙이 담긴 모종삽을 이용하여 “구멍 콜라이더2”를 향하여 “트리거 버튼”을 누르면 흙이조금씩 올라갑니다."
            + "\n  - 총 3회 이상 흙을 주면 “구멍 콜라이더2”가 비활성화됩니다.";
        dialogue[35= "<HOW TO USE>\n"
            + "- 모종삽을 든 상태에서 주변의 흙을 향하여 “트리거 버튼”을 누르면 흙이 생성됩니다."
            + "\n  - 흙이 담긴 모종삽을 이용하여 “구멍 콜라이더2”를 향하여 “트리거 버튼”을 누르면 흙이조금씩 올라갑니다."
            + "\n  - 총 3회 이상 흙을 주면 “구멍 콜라이더2”가 비활성화됩니다.";
        dialogue[36= "end";
 
        // 호미삽으로 북주기
        dialogue[37= "<HOW TO USE>\n"
            + "\n  - 호미삽을 향하여 “트리거 버튼”을 누르면 장착됩니다."
            + "\n  - 호미삽을 드는 순간 “북주기 트리거”가 활성화됩니다."
            + "\n  - 호미삽을 든 상태로 “트리거 버튼”을 누르면 호미삽이 일정 거리 앞으로 향합니다."
            + "\n  - 만약 호미삽이 “북주기 트리거”에 닿을 경우 (Enter 호출될 시) 흙의 양이 소폭 증가합니다."
            + "\n  - 5번 정도 트리거에 닿을 경우 해당 “북주기 트리거”는 비활성화됩니다.";
        dialogue[38= "<HOW TO USE>\n"
            + "\n  - 호미삽을 향하여 “트리거 버튼”을 누르면 장착됩니다."
            + "\n  - 호미삽을 드는 순간 “북주기 트리거”가 활성화됩니다."
            + "\n  - 호미삽을 든 상태로 “트리거 버튼”을 누르면 호미삽이 일정 거리 앞으로 향합니다."
            + "\n  - 만약 호미삽이 “북주기 트리거”에 닿을 경우 (Enter 호출될 시) 흙의 양이 소폭 증가합니다."
            + "\n  - 5번 정도 트리거에 닿을 경우 해당 “북주기 트리거”는 비활성화됩니다.";
        dialogue[39= "end";
 
        // 벌레 잡기 & 감자 재배
        dialogue[40= "<HOW TO USE>\n"
            + "\n  - 맨손인 상태에서 “23점박이무당벌레”를 향하여 “트리거 버튼”을 누르면 병해충이 사라집니다."
            + "\n< HOW TO USE>\n"
            + "\n- 호미삽을 들고 다 자란 감자의 흙 부분에 있는 트리거를 5번 건들면 수확 완료";
        dialogue[41= "<HOW TO USE>\n"
            + "\n  - 맨손인 상태에서 “23점박이무당벌레”를 향하여 “트리거 버튼”을 누르면 병해충이 사라집니다."
            + "\n< HOW TO USE>\n"
            + "\n- 호미삽을 들고 다 자란 감자의 흙 부분에 있는 트리거를 5번 건들면 수확 완료";
        dialogue[42= "end";
 
 
 
        Button_SetActive(false);
    }
    private void Start()
    {
        NextDialogue();
    }
 
    public void talk_npc()
    {
        Button_SetActive(true);
        count =12;
        txt_Dialogue.text = dialogue[count];
    }
 
    private void NextDialogue()
    {
            txt_Dialogue.text = dialogue[count];
            count++;
    }
 
    public void onClick_feature()  // 감자 특징 알아보기 
    {
        isDialogue = true;
        Button_SetActive(false);
        count = 15;
        npc_talk_int = 0;
        if (count < dialogue.Length)
            NextDialogue();
 
    }
    public void onClick_learning() // 감자 키우는법 학습하기
    {
        Button_SetActive(false);
        npc_talk_int = 0;
        txt_Dialogue.text = "";
        panel.SetActive(true);
 
    }
    public void onClick_tip_1() // 플레이어 이동 & 비료주기
    {
        panel.SetActive(false);
        isDialogue = true;
        count = 26;
        npc_talk_int = 0;
        if (count < dialogue.Length)
            NextDialogue();
    }
 
    public void onClick_tip_2() // 삽 & 모종삽 사용하기
    {
        panel.SetActive(false);
        isDialogue = true;
        count = 29;
        npc_talk_int = 0;
        if (count < dialogue.Length)
            NextDialogue();
    }
    public void onClick_tip_exit() // 학습하기 창에서 나가기 클릭
    {
        panel.SetActive(false);
        npc_talk_int = 1;
    }
 
 
 
    public void Button_SetActive(bool set)
    {
        button_1.gameObject.SetActive(set);
        button_2.gameObject.SetActive(set);
        button_3.gameObject.SetActive(set);
    }
 
    void Update()
    {
        // 감자 키우는법 학습하기 클릭시
        if (Input.GetKeyDown(KeyCode.Keypad1))
        {
            if (button_1.gameObject.activeSelf == true)
            {
                button_1.onClick.Invoke();
            }
        }
        // 감자 특징 알아보기 클릭시
        else if (Input.GetKeyDown(KeyCode.Keypad3))
        {
            if (button_3.gameObject.activeSelf == true)
            {
                button_3.onClick.Invoke();
 
            }
        }
        // 플레이어 이동 & 비료주기
        else if (Input.GetKeyDown(KeyCode.Keypad7))
        {
            if (tip_button_1.gameObject.activeSelf == true && npc_talk_int !=1)
            {
                tip_button_1.onClick.Invoke();
 
            }
        }
        // 나가기
        else if (Input.GetKeyDown(KeyCode.Keypad8))
        {
            if (tip_button_exit.gameObject.activeSelf == true)
            {
                tip_button_exit.onClick.Invoke();
 
            }
        }
        // 모종삽 이용하기
        else if (Input.GetKeyDown(KeyCode.Keypad9))
        {
            if (tip_button_2.gameObject.activeSelf == true && npc_talk_int != 1)
            {
                tip_button_2.onClick.Invoke();
 
            }
        }
 
 
 
 
        if (npc_talk_int == 1)
        {
            isDialogue = false;
            talk_npc();
        }
 
 
        if (isDialogue && dialogue[count] != null)
        {
            
            if(dialogue[count].Equals("end"))
            {
                npc_talk_int = 1;
            }
            else if (Input.GetKeyDown(KeyCode.G))
            {
                if (count < dialogue.Length)
                    NextDialogue();
            }
 
 
            //if (OVRInput.GetDown(OVRInput.Button.One))
        }
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

위의 코드에서 아래의 변수가 추가되었습니다.

아래의 변수는 새로생긴 패널의 버튼입니다.

 

public Button tip_button_1; // 이동 방법 & 비료 뿌리기

public Button tip_button_2; // 삽 & 모종삽 사용하기

public Button tip_button_exit; // 나가기

 

 

3가지 버튼으로 재대로 코드가 작성되었는지 유니티를 실행하였을 때 되는지 확인하였습니다.

잘 되는 것으로 확인 되었고 나머지 버튼들을 눌렀을 때 함수를 구현하진 않았지만 배열에 필요한 팁의 내용들을 

포함하였습니다.

 

버튼들의 onClick()함수 적용을 위해 간단한 설명을 추가하겠습니다.

간단히 아래의 메소드에서 count 만 각 대본이 있는 위치로 변경하면 모든 버튼을 사용할 수 있습니다.

아래의 메소드의 count = 29; 는 삽 & 모종삽 사용하기 대화가 저장되어 있는 곳입니다.

대화가 끝나면 dialogue에 "end" 값이 들어가 있는데 이를 update에 있는 함수가 인식하여 대화가 자동으로

메인 창으로 가도록 설계되있습니다.

 

 public void onClick_tip_2() // 삽 & 모종삽 사용하기

    {

        panel.SetActive(false);

        isDialogue = true;

        count = 29;

        npc_talk_int = 0;

        if (count < dialogue.Length)

            NextDialogue();

    }

 

farmer.fbx
0.34MB

위의 fbx 파일은 제가 무료 blender 툴로 직접 제작한 캐릭터입니다.

학습용 도로 사용하도록 합시다.

 

<npc 숨쉬기 애니메이션 넣기>

먼저, 모델을 유니티로 가져온다.

유니티로 가져온 모델을 클릭하면 제일 위에 Model, Rig, Animation, Materials 가있다

애니메이션을 클릭한다. 

캐릭터의 기본 애니메이션이 계속 유지되기 위해 아래의 설정이 필요하다.

 

 

Idle 애니메이션을 Source Take 에서 선택한다. 

그리고 Loop Time을 체크해주면 해당 에니메이션은 무한 반복된다. 

 

모델을 클릭하여

 

해당 에니메이션을 클릭하면

 

 

Loop Time에 체크가 돼있는 것을 확인할 수 있다.

 

애니메이션을 적용하기 위해서 

 

프로젝트 창에서마우스 우클릭 -> Create -> Animator Controller를 클릭하여 애니메이터 컨트롤러를 생성한다.

 

 

애니메이터 컨트롤러를 더블클릭하면 애니메이터 화면이 나타난다.

그 후, 해당 애니메이션을 드래그 엔 드롭하게 되면 위 화면처럼 된다. 

지금 작업한 에니메이터 컨트롤러를 사용하기 위해서

 

우선, 엠티 오브젝트를 생성한다.

그 후 해당 모델을 엠티 오브젝트의 자식으로 한다.

주의사항으로 만약 블렌더 작업하고 모델을 유니티로 가져올 경우

블렌더 모델의 좌표를 0,0,0으로 저장한 후 해당 blender 파일을. fbx로 추출해야 한다.

유니티 게임을 실행시키면 블렌더 작업 당시 캐릭터 위치로 이동하게 된다. 

 

 

 

모델을 클릭한다.

Controller가 None으로 되어 있을 것이다. 우리가 작업해준 애니메이터 컨트롤러를 드래그 엔 드롭해주면 된다.

 

 

npc와 상호작용 하기 위한 캐릭터를 생성한다.

 

<플레이어 컨트롤>

NPC와 상호작용 하기 위해서 플레이어를 만들어 준다.

 

저자는, 캡슐을 생성하여 플레이어로 하였다.

아래의 소스코드는 플레이어의 움직임을 컨트롤하기 위한 소스코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Player : MonoBehaviour {
 
    float speed = 10f;
 
    Rigidbody rigidbody;
    Vector3 movement;
    float h, v;
 
    void Start()
    {
        rigidbody = GetComponent<Rigidbody>();
    }
    void Update()
    {
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");
    }
    void FixedUpdate()
    {
        movement = movement.normalized * speed * Time.deltaTime;
        rigidbody.MovePosition(transform.position + movement);
    }
    
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

플레이어를 만들었다면

메인 카메라를 플레이어의 자식 객체로 만든다.

그렇게 되면 플레이어 1인칭을 간단하게 구현할 수 있다.

그다음 카메라의 Transform을 아래와 같이 만든다.

그럼 이제 플레이어의 시각이 적당해진다.

 

 

<npc가 플레이어 바라보게 만들기>

다음으로 npc의 부모 엠티 객체에 Sphere Collider를 아래와 같이 설정한다.

트리거 내부에 플레이어가 감지되면 npc가 플레이어가 바라보도록 

transform.LookAt(target); 을 활용하겠다.

코드는 아래와 같이 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class LookAtPlayer : MonoBehaviour
{
    private void OnTriggerStay(Collider other)
    {
        if (other.tag == "Player")
        {
            transform.LookAt(other.transform);
        }
 
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

위의 코드를 보면 알 수 있지만 플레이어라고 생성한 객체의 Tag를 Player로 설정해주기 바란다.

위의 코드를 npc의 부모 엠티 객체에게 넣어준다.

그러면 이제 npc 주변으로 가면 npc가 플레이어를 바라보게 된다. 

 

 

의도한 것은 아니지만 기분 나쁘게 npc가 바라보는 거 같다.

블렌더에선 밝아 보였지만 유니티로 가져오니 모델이 명암이 더욱 어두워 보인다.

다음부터는 과다하게 밝게 모델링할 필요가 있을 것 같다.

 

<NPC 대화 상자 만들기>

 

이번엔 대화 상자를 만들어 보겠다.

 

히어라키 창에서 패널을 생성한다. 패널은 Create -> UI -> panel이다.

그 후, 같이 생성된 캔버스 통째로 npc 부모 객체의 자식으로 넣어준다.

캔버스의 Render Mode는 World Space로 바꿔준다.

그다음, 중요한 부분인데 scale을   

뒤의 이미지처럼 줄여준다. 이때 width, Height값을 줄이는 식으로 하면

글자가 안 나올 수 도 있으니 주의하도록 한다.

 

 

위의 느낌처럼 UI를 만든다.

 

이제 대화를 진행시키기 위해서 아래 스크립트를 사용한다.

아래 스크립트는 panel에 적용시킨다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
[System.Serializable]
public class Dialogue
{
    // 여러줄을 쓸 수 있게 해준다.
    [TextArea]
    public string dialogue;
}
 
public class text_log : MonoBehaviour
{
    static public int npc_talk_int=0;
    Boolean justOne = false;
    public Text txt_Dialogue;
    public Button button_1;
    public Button button_2;
    public Button button_3;
    // [SerializeField] 을 달면 유니티 inspector 창에서 해당 변수를 조작 할 수 있다.
 
    static public bool isDialogue = true;
 
    // 대화가 얼마나 진행 되었는지 확인
    private int count = 0;
 
    static private String[] dialogue; // 대화가 들어가는 배열
 
    private void Awake()
    {
        dialogue = new String[100];
        // 처음 intro 대사
        dialogue[0= "[농부]\n\n안녕하세요 플레이어님 반가워요";
        dialogue[1= "[농부]\n\n날씨가 아주 화창하군요! 감자 심기 좋은날이네요";
        dialogue[2= "[농부]\n\n그럼 지금 감자 심기를 배워 보도록 하겠습니다.";
        dialogue[3= "[농부]\n\n혹시 추가적으로 감자에 관해 알고 싶다면 앞에 저를 레이저 포인터로 클릭 해주세요";
        dialogue[4= "[농부]\n\n레이저 포인터는 뒤로가기 버튼을 클릭하면 켜집니다.";
        dialogue[5= "[농부]\n\n다시 누르면 꺼집니다.";
        dialogue[6= "[농부]\n\n레이저 포인터가 켜져있는 상태로, 리모컨의 큰 원을 클릭하세요 ";
        dialogue[7= "[농부]\n\n그럼 레이저 포인터가 가르키고 있는 대상과 상호 작용 할 수 있게 됩니다.";
        dialogue[8= "[농부]\n\n준비가 되었다면 저에게 알려주세요";
        // 대화 끝에 null을 넣어서 대화가 끝났음을 알린다.
        dialogue[9= "";
        dialogue[10= "end";
        dialogue[12= "[농부]\n\n무었을 도와드릴까요?";
        dialogue[13= "[농부]\n\n무었을 도와드릴까요?";
        dialogue[14= "[농부]\n\n무었을 도와드릴까요?";
 
    }
    private void Start()
    {
        button_1.gameObject.SetActive(false);
        button_2.gameObject.SetActive(false);
        button_3.gameObject.SetActive(false);
 
        ShowDialogue(0);
    }
 
    public void ShowDialogue(int count_)
    {
        count = count_;
        NextDialogue();
    }
    public void talk_npc()
    {
        button_1.gameObject.SetActive(true);
        button_2.gameObject.SetActive(true);
        button_3.gameObject.SetActive(true);
        count =12;
        txt_Dialogue.text = dialogue[count];
    }
 
    private void NextDialogue()
    {
        txt_Dialogue.text = dialogue[count];
        count++;
    }
 
 
 
    void Update()
    {
        if(npc_talk_int == 1&& justOne == false)
        {
            talk_npc();
            justOne = true;
        }
        else if(npc_talk_int == 2)
        {
 
            button_1.gameObject.SetActive(false);
            button_2.gameObject.SetActive(false);
            button_3.gameObject.SetActive(false);
            justOne = false;
        }
        // count 가 12면 선택지 대화 진행중이라 막아 둔다.
        if (isDialogue && count != 12)
        {
            if(dialogue[count].Equals("end"))
            {
                npc_talk_int = 1;
            }
            //if (OVRInput.GetDown(OVRInput.Button.One))
            if (Input.GetKeyDown(KeyCode.G))
            {
                if (count < dialogue.Length)
                    NextDialogue();
            }
        }
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

https://youtu.be/sOK_mCxvlSs

 

레이저 포인터에 이어서 이번엔 메뉴를 만들겠습니다.

 

Hierarchy에서 우클릭 후 UI -> Panel을 클릭합니다.

그러면 캔버스와 패널이 생성됩니다.

 

 

 

Hierarchy에 있는 캔버스나 패널을 더블클릭 합니다.

위와 같이 패널이 보이게 됩니다.

 

캔버스를 우클릭 하여 자식 객체로 버튼을 생성합니다.

 

 

 

캔버스를 확대하면 위와같이 버튼이 생성된 것을 확인 할 수 있습니다. 

저는 현재 VR 을 활용하여 졸업작품을 만들어야 합니다.

그래서 재가 만들 컨텐츠를 위한 메인 화면을 만들어 보겠습니다.

 

버튼 width 와 height를 300으로 만들겠습니다.

Hierarchy에서 버튼을 누르고 Ctrl+D를 하여 복사합니다.

 

위와 같이 3개의 버튼을 만듭니다.

중앙의 버튼의 x축을 0으로 두고

왼쪽 버튼의 x축 좌표는 -356

오른쪽은 + 356으로 설정합니다.

 

오른쪽 버튼의 height를 140으로 만듭니다.

y축 좌표 값을 80을 줍니다.

ctrl + d를 활용해 버튼을 더 만들고 y축을 -80으로 설정합니다.

그러면, 위의 화면처럼 되게 됩니다.

 

첫번째 버튼의 하위 객체 text에 접근합니다. inspector 창에서 font size 를 40을 주고 가이드라고 넣습니다.

 

나머지도 위의 설명의 방식대로 위의 이미지처럼 만듭니다.

 

버튼 생김새는 다 만들었습니다.

이번에는 레이저가 버튼을 가르킬 시 색이 노랑색으로 변하도록 만들 겠습니다.

버튼의 인스펙터 창에서 Highligted Color를 위와 비슷한 색으로 바꿔봅시다.

본인이 원하는 색으로 해도 됩니다.

 

 

 

 

각각의 버튼에 맞는 이름을 지어서 구분을 쉽게 할 수 있도록 합니다.

 

 

패널의 자식으로 버튼을 넣어줍니다.

패널을 끌어다 Prefabs 폴더에 넣어줍니다. 없으면 Asset폴더에 생성합니다.

만약, 캔버스도 프리팹에 포함하게 될 경우 오류가 생길 수 있습니다.

 

이름은 main_menu로 바꿉니다.

이걸로 메인 메뉴 하나를 완성하였습니다.

 

이번에는 가이드 버튼을 눌렀을 때 나타날 가이드 메뉴 패널을 만들겠습니다.

 

 

main_menu의 체크를 풀어줍니다. 그에 따라, 메인 메뉴가 히어라키 창에 존재하지만 invisible 상태가 됩니다.

객체의 이름은 원래, Panel이였는데 삭제하였다 프리팹스 폴더에서 다시 캔버스 하위 객체로 넣었습니다.

 

가이드 매뉴 객체를 만들겠습니다.

다시 패널을 캔버스 하위 객체로 생성합니다.

 

버튼을 아래와 같이 구상합니다.

버튼은 패널의 하위 객체로 설정하고 프리팹으로 만들기 위해 객체를 끌어다 프리팹스 폴더에 이동시킵니다.

끌어다 이동 시키기 전에 패널 이름을 guide_menu라 설정하겠습니다. 객체를 클릭하고 F2번을 누르면 됩니다.

 

 

가이드 메뉴도 체크를 풀어 투명한 상태로 만들겠습니다.

 

다음은 설정 버튼을 눌렀을 때 나타날 option_menu 입니다.

 

 

옵션 메뉴에서는 음악 크기를 줄였다 키윘다를 할 수 있어야합니다.

위의 조건을 만족시키기위해 Silder를 활용합니다.

Silder는 우클릭 -> UI -> Silder라고 있습니다.

 

슬라이더가 작다면 Scale을 키위주시기 바랍니다.

 

아래 화면처럼 option_menu를 만들어주세요.

텍스트 폰트 사이즈를 키웠는데 안보인다면

text의 Width, Height값을 키우면 보이게 됩니다.

 

 

 

 

이번에는 종료 버튼을 누르면 나타날 exit_menu를 만들겠습니다.

종료 메뉴는 크기가 작기 때문에 패널 크기를 x,y 축 둘다 0.5로 줄이겠습니다.

 

 

위의 이미지처럼 exit_menu를 꾸며줍니다.

 

이것으로 필요한 메뉴들을 다 만들었습니다.

온라인 체험 메뉴는 이 단계에서는 만들지 않겠습니다.

 

이번에는 메인 메뉴에서 버튼을 눌렀을 때 버튼에 맞는 메뉴가 등장 할 수 있도록,

클릭시 동작을 구현하겠습니다.

 

Scripts 폴더에 C#Script 하나 생성합니다. 이름은 onClick_MainMenu 라고 만들겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class onClick_MainMenu : MonoBehaviour
{
    public GameObject optionMenu;
    public GameObject ExitMenu;
    public GameObject mainMenu;
    public GameObject guideMenu;
 
    public void guide_btn_clicked()
    {
        mainMenu.SetActive(false);
        guideMenu.SetActive(true);
 
    }
    public void guide_back_clicked()
    {
        guideMenu.SetActive(false);
        mainMenu.SetActive(true);
 
    }
    public void ExitMenu_btn_clicked()
    {
        mainMenu.SetActive(false);
        ExitMenu.SetActive(true);
    }
    public void ExitMenu_back_clicked()
    {
        ExitMenu.SetActive(false);
        mainMenu.SetActive(true);
    }
    public void option_btn_clicked()
    {
        mainMenu.SetActive(false);
        optionMenu.SetActive(true);
    }
    public void option_back_clicked()
    {
        optionMenu.SetActive(false);
        mainMenu.SetActive(true);
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

SetActive() 메소드는 객체를 보이게 할 것이냐 아니냐를 선택할 수 있는 메소드입니다.

 

Canvas에 위의 스크립트를 넣어줍니다.

스크립트를 히어라키 창에있는 캔버스에 드래그엔 드랍을 하시면 됩니다.

 

넣어주게 되면 참조 객체 값이 None이라 됩니다.

 

히어라키 창에서 프리팹을 드래그 앤 드랍으로 가져오거나 옆에 동그라미를 클릭해서 객체를 검색하여

넣어줄 수 있습니다.

 

그런데 생각해보니 버튼들의 테그를 설정하지 않았습니다.

레이저 포인터에 충돌 발생시 태그가 Button이면 클릭이 진행 되게끔 코드를 만들었기 때문에

프리팹 수정이 필요합니다.

프로젝트에 들어가 우선 메인 메뉴의 프리팹에 들어갑니다.

 

guide_btn을 클릭합니다.

위의 이미지를 보면 태그가 Untagged라고 되있습니다. 클릭하고 재일 아레의 Add Tag를 클릭합니다.

List is Empty 아래 +를 클릭합니다.

Button 이라 치고 save 버튼을 누릅니다.

다시돌아오면 메뉴에 Button 이라는 태그가 생겼는데 태그를 클릭하여 위 화면처럼 수정합니다.

모든 메뉴 프리팹의 태그를 버튼으로 수정합니다.

 

이번엔 버튼에 박스 콜라이더를 설정해야 합니다.

모든 버튼의 컴포넌트에 박스 콜라이더를 넣고 x,y 스케일을 width, height의 크기로 맞춰 주시기 바랍니다.

 

 

확인해보면 프로젝트의 프리팹을 수정하면 히어라키 창에있는 프리팹에도 적용된다는 것을 알 수 있습니다.

 

이번엔 버튼을 클릭할 경우 액션을 주겠습니다.

 

프로젝트에서 메인 메뉴 프리팹을 더블클릭합니다.

 

guide_btn을 더블 클릭합니다. 밑에 Inspector 창의 아래에 보면

On Click() 이 있습니다. + 버튼을 눌러주세요

 

Runtime Only 아래에 Canvas를 넣어줍니다.

그리고 오른쪽 위의 버튼을 눌러  화면 처럼 만드시면 됩니다.

위에 넣어준 메소드 guide_btn_clicked()는 

메인 메뉴를 감추고 가이드 메뉴를 보여줍니다. 

 

나머지 버튼도 메소드들의 기능을 생각하여 각 버튼의 On Click()에 넣어줍니다.

 

다음으로 캔버스의 위치를 수정하겠습니다.

위 이미지 처럼 수정해주세요

 

캔버스의 속성을 바꾸겠습니다.

Render mode 를 World Space로 바꿔주세요

좌표 값도 위의 이미지처럼 변경해주세요

 

이로써 플레이어의 시각에는 항상 메뉴 판이 보이도록 하였습니다.

 

추가적으로 Slider를 조정 하는 기능을 넣어보겠습니다.

아쉽게도 Slider를 조작하는데 시간이 부족하여 버튼을 누르면 슬라이더를 

조작할 수 있게끔 만들겠습니다. 슬라이더를 좀더 재대로 만들고 싶다 하는 분은

https://developer.oculus.com/blog/easy-controller-selection/

 

Easy Controller Selection | Oculus

This blog provides the scripts needed for adding ray selection using a Gear VR Controller to any Unity project. The provided scripts mimic the behavior of the laser pointer in the Gear VR home environment for interacting with menus and the environment. If

developer.oculus.com

이곳을 참조해주세요

 

슬라이더를 버튼으로 조작할 수 있게끔 아래의 코드를 참조해주세요

핵심적으로, 한 가지만 말씀 드리면

slider.GetComponent<Slider>().value로 슬라이더를 조작할 수 있는 원의 위치를 변경 할

수 있습니다.

값의 범위는 0~1 까지이며 변수 타입은 float 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Slider_mover : MonoBehaviour
{
    public Slider slider;
    public Text text;
    private float Value = 0;
    private int p = 0;
 
 
    public void slider_plus()
    {
        if (Value < 1f)
        {
            Value += 0.1f;
            p += 10;
        }
        text.text = p.ToString()+"%";
        slider.GetComponent<Slider>().value = Value;
    }
    public void slider_minus()
    {
        if (Value > 0.01f)
        {
            Value -= 0.1f;
            p -= 10;
        }
        text.text = p.ToString() + "%";
        slider.GetComponent<Slider>().value = Value;
    }
 
    // Start is called before the first frame update
    void Start()
    {
        
    }
 
    // Update is called once per frame
    void Update()
    {
        
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

https://www.facebook.com/100009279421433/videos/2502245443428079/?id=100009279421433

 

김종완

메뉴를 열려면 alt + / 키 조합을 누르세요

www.facebook.com

 

오늘은 VR의 Menu 등을 조작하기 위한 레이저 포인트를 만들어 보도록

하겠습니다. 

레이저 포인트를 만들기 위한 방법이 2가지가 있습니다.

첫 번 째로 Cube 객체를 생성해서 길이만 길게 하고 몸통 너비는

작게 해서 레이저 처럼 보이게 하는 방법

두 번 째는 Line Renderer를 사용하는 방법입니다.

 

오늘은 Line Renderer를 활용해 보겠습니다. 

 

Line Renderer를 이용한 레이저 포인트를 만들기 위해

Script를 만들겠습니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class LayserPointer : MonoBehaviour
{
    private LineRenderer layser;        // 레이저
    private RaycastHit Collided_object; // 충돌된 객체
    private GameObject currentObject;   // 가장 최근에 충돌한 객체를 저장하기 위한 객체
 
    public float raycastDistance = 100f; // 레이저 포인터 감지 거리
 
    // Start is called before the first frame update
    void Start()
    {
        // 스크립트가 포함된 객체에 라인 렌더러라는 컴포넌트를 넣고있다.
        layser = this.gameObject.AddComponent<LineRenderer>();
 
        // 라인이 가지개될 색상 표현
        Material material = new Material(Shader.Find("Standard"));
        material.color = new Color(01952550.5f);
        layser.material = material;
        // 레이저의 꼭지점은 2개가 필요 더 많이 넣으면 곡선도 표현 할 수 있다.
        layser.positionCount = 2;
        // 레이저 굵기 표현
        layser.startWidth = 0.01f;
        layser.endWidth = 0.01f;
    }
 
    // Update is called once per frame
    void Update()
    {
        layser.SetPosition(0, transform.position); // 첫번째 시작점 위치
                                                   // 업데이트에 넣어 줌으로써, 플레이어가 이동하면 이동을 따라가게 된다.
        //  선 만들기(충돌 감지를 위한)
        Debug.DrawRay(transform.position, transform.forward * raycastDistance, Color.green, 0.5f);
 
        // 충돌 감지 시
        if (Physics.Raycast(transform.position, transform.forward, out Collided_object, raycastDistance))
        {
            layser.SetPosition(1Collided_object.point);
 
            // 충돌 객체의 태그가 Button인 경우
            if (Collided_object.collider.gameObject.CompareTag("Button"))
            {
                // 오큘러스 고 리모콘에 큰 동그라미 부분을 누를 경우
                if (OVRInput.GetDown(OVRInput.Button.One))
                {
                    // 버튼에 등록된 onClick 메소드를 실행한다.
                    Collided_object.collider.gameObject.GetComponent<Button>().onClick.Invoke();
                }
 
                else
                {
                    Collided_object.collider.gameObject.GetComponent<Button>().OnPointerEnter(null);
                    currentObject = Collided_object.collider.gameObject;
                }
            }
        }
 
        else
        {
            // 레이저에 감지된 것이 없기 때문에 레이저 초기 설정 길이만큼 길게 만든다.
            layser.SetPosition(1, transform.position + (transform.forward * raycastDistance));
 
            // 최근 감지된 오브젝트가 Button인 경우
            // 버튼은 현재 눌려있는 상태이므로 이것을 풀어준다.
            if(currentObject != null)
            {
                currentObject.GetComponent<Button>().OnPointerExit(null);
                currentObject = null;
            }
 
        }
        
    }
 
    private void LateUpdate()
    {
        // 버튼을 누를 경우        
        if (OVRInput.GetDown(OVRInput.Button.One))
        {
            layser.material.color = new Color(2552552550.5f);
        }
 
        // 버튼을 뗄 경우          
        else if (OVRInput.GetUp(OVRInput.Button.One))
        {
            layser.material.color = new Color(01952550.5f);
        }
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

코드 설명이 블로그에서 보면 흐릿하게 보이네요 

코드 설명을 구체적으로 하려고 하였습니다.

이해하는데 도움이 되면 좋겠네요

혹시 어려운 점이 생기신다면 댓글을 남겨주세요.

 

이제 빈 객체를 생성하고 이름을 layserPoint이라고 하겠습니다.

위의 스크립트를 layserPoint의 component로 넣어주세요

넣어주는 이유는, 레이저를 객체화 함으로써 레이저를 껏다 켯다 할 수 있기 때문입니다.

 

여기다 프리팹을 넣어주세요

프리팹의 좌표는 0, 0, 0 이어야합니다. 

 

참고한 자료 위치

https://github.com/baedi/TIL-Unity3D/blob/master/Script-VR/VR006/VRPointer.cs

 

baedi/TIL-Unity3D

Unity3D Study with C#. Contribute to baedi/TIL-Unity3D development by creating an account on GitHub.

github.com

 

이번에는 기본 동작을 구현하겠습니다.

기본적으로 오큘러스 고 빌드 환경이 준비되었다고 가정합니다.

 

Oculus를 Asset Store에서 검색합니다.

 

 

Oculus Integration을 다운로드합니다.

다음으로, 임포트 합니다.

 

 

Oculus 파일 체크를 풀어줍니다.

 

 

 

여기서 VR만 체크해주고 임포트 합니다.

이제, 기본적으로 필요한 것들은 다 갖춰졌습니다.

 

Plane 객체를 생성합니다. 

 

 

Hierarchy 창에서 우클릭 후 Empty Create를 눌러

빈 게임 오브젝트를 생성합니다.

이름은 Player라고 설정합니다.

 

Capsule Collider를 추가하고 Height 2, Position Y축을 1

설정합니다.

이제 플레이어가 어느 정도 형태가 생겼습니다.

 

다음으로, Oculus > VR > Prefabs 폴더에서

OVRCameraRig를 플레이어에 종속시킵니다.

이 객체는 우리의 눈이 되어줄 것입니다.

Position Y축에 0.8 값을 집어넣어 사람 눈높이로 설정합니다.

 

이제 우리의 눈이 생겨났으니, Main Camera는 제거합니다.

 

OVRCameraRig객체에 소속돼있는 객체들입니다.

지금, 여기서 주목할 객체는 2가지 LeftControllerAnchor, RightControllerAnchor가 있습니다.

 

이 곳에다 Oculus Go의 컨트롤러가 들어가는 위치입니다.

다르게 말하면 리모컨 위치라고도 할 수 있습니다.

 

 

 

위와 같이 컨트롤러 프리 팹을 왼쪽, 오른쪽 각각 넣어줍니다.

양쪽에 다 넣는 이유는 오른손잡이, 왼손잡이 둘 다를 위해서입니다.

 

왼쪽은 L Tracked Remote로 설정합니다.

 

이제 빌드하면 됩니다.

 

https://www.facebook.com/100009279421433/videos/2498086847177272/?id=100009279421433

 

보안 확인 필요

메뉴를 열려면 alt + / 키 조합을 누르세요

www.facebook.com

위의 영상은 빌드된 영상입니다.

안녕하세요!

오늘은 봄처럼 날씨가 맑고 따뜻하네요

Oculus go Unity 환경을 만들도록 하겠습니다.

기본적으로, 오큘러스고 개발자 모드 활성화 된 상태라 가정합니다.

글쓴이의 유니티 버전은 2019.2.17f1 버전입니다.

 

우선, 유니티 프로젝트를 생성합니다.

 

오큘러스 고에 빌드를 위해서 빌드 세팅을 합니다.

File -> Build Settings 로 가주세요

안드로이드를 선택합니다.

오큘러스 고는 플랫폼이 안드로이드이기 때문입니다.

Switch Plaform 을 눌러줍니다.

 

다음으로, Player Settings... 을 클릭합니다.

 

여기서 설정해야 하는 항목은 크게 3가지로 나눌 수 있습니다.

 

첫 번째

Company Name을 자신이 원하는 이름으로 변경합니다.

디폴트로 두면 빌드가 안될 수도 있습니다.

 

두 번째

 창을 내려 XR Settings에 들어갑니다.

 

 

Virtual Reality Supported를 체크합니다.

체크함으로써, 가상 환경 구축을 지원하겠다는 뜻을 유니티에 전달합니다.

 

체크하면, Vurtual Reality SDKs라는 항목이 생깁니다.

우측 하단의 +를 클립 합니다.

메뉴에서, 오큘러스를 선택합니다.

 

마지막 세 번째

 

Other Settings에서

Auto Graphics API를 체크합니다.

하지 않으면, VulKan API 관련해서 오류가 발생합니다.

Minimum API Level을 5.0 Lollipop으로 설정합니다.

 

 

이것으로 Oculus Go Unity 기본 환경 세팅이 완성되었습니다.

+ Recent posts