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

 

코로나도 이제 조금씩은 진정되고 있는 오늘

 

유니티로 감자를 자라게 해보겠습니다.

 

우선 영상을 보겠습니다.

https://youtu.be/G_78W35p164

 

<참고>

<window 10 동영상 찍은 방법>

win + G을 누르면 윈도 화면을 찍을 수 있는 툴이 나옵니다. 그것을 이용해

영상을 촬영했습니다. 그리고 밑의 편집기를 이용해 용량을 줄였습니다. 용량이 21메가에서 0.22로 줄었습니다.

용량이 98 퍼나 줄어드네요 

 

https://www.videosmaller.com/ko/

 

온라인으로 동영상 파일 크기 줄이기, 온라인으로 동영상 압축하기 (MP4, AVI, MOV, MPEG) | VideoSmaller

온라인으로 동영상 파일 크기를 줄이고, 동영상을 압축해 보세요. 화질 저하 없이 MP4 동영상 크기를 줄이고, 동영상을 더 작게 만들어 보세요.

www.videosmaller.com

 

우선,첫 번째로 유니티의 hierarchy 창을 보겠습니다.

 

 

 

potatos란 감자들의 집합이고

potato_번호 가 감자 객체입니다.

그 안에 있는 potato_25, potato_45, potato_90 은 감자의 형태입니다.

뒤의 숫자는 감자를 심고 지난날입니다. 

 

감자 집합(potatos)에는 Check_Today라는 스크립트를 사용합니다. 

위의 스크립트(Check_Today) 특징으로 Today라는 변수를 static을 사용하여 프로그램 초기부터 객체가 생성되게 하였고

public을 사용하여 외부 모듈들도 위의 변수를 참조할 수 있게 하였습니다.

Today 변수는 모든 감자들의 성장에 영향을 미칩니다.

 

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 check_Today : MonoBehaviour
{
    public static int Today;
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("Today: " + Today);
    }
 
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Today += 5;
            KeyDown_Space();
        }
    }
    private void KeyDown_Space()
    {
        Debug.Log("Today: " + Today);
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

 

다음으로 감자 객체(potato_01, ... , potato_06)에 Grow_up_potato라는 스크립트를 사용했습니다.

위 스크립트는 Check_Today.Today의 변수 값에 따라서 식물을 자라게 만드는 스크립트입니다.

하나의 감자 객체는 25일 된 날 감자, 45일 된날 감자, 90일 된날 감자가 들어있습니다.

초기에는 45, 90일 된 감자들은 비활성화하여 보이지 않게 하였습니다.

그리고 today값이 45가 되기 전까지는 25일 된 날 감자 크기를 키워주며

45일이 되면  45일 된 감자 형태를 활성화하고 25일 된 감자를 비활성화합니다.

이후 90일이 되기 전까지 이번에는 y축을 조금씩 성장시킵니다. 

그리고 90일이 되면 감자가 최종 성장 형태가 됩니다. 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Grow_up_potato : MonoBehaviour
{
    int today;
    GameObject potato_25;
    GameObject potato_45;
    GameObject potato_90;
 
    // Start is called before the first frame update
    void Start()
    {
        today = 0;
        potato_25 = transform.GetChild(0).gameObject;
        potato_45 = transform.GetChild(1).gameObject;
        potato_90 = transform.GetChild(2).gameObject;
    }
 
    // Update is called once per frame
    void Update()
    {
        today = check_Today.Today;
        growing_up(today);
     
    }
 
    void growing_up(int today)
    {
        //if (today < 20)
        //{
        //    if (potato_25.activeSelf == true)
        //        potato_25.SetActive(false);
        //}
        if (today >= 0 && today < 45)
        {
            if (potato_25.activeSelf == false)
                potato_25.SetActive(true);
            float size = (float)today * 0.1f;
            float size_y = (float)today * 0.2f;
            potato_25.transform.localScale = new Vector3(size, size_y, size);
 
        }
        else if (today >= 45 && today < 90)
        {
            if (potato_25.activeSelf == true)
                potato_25.SetActive(false);
            if (potato_45.activeSelf == false)
                potato_45.SetActive(true);
 
            float size_y = ((float)today * 0.01f) + 0.3f;
            potato_45.transform.localScale = new Vector3(1, size_y, 1);
 
        }
        else if (today >= 90)
        {
            // 여기서 potato_25의 상태를 비활성화 하지 않는 이유는
            // 45~90 동안 이미 비활성화가 되었기 때문이다.
            if (potato_45.activeSelf == true)
                potato_45.SetActive(false);
            if (potato_90.activeSelf == false)
                potato_90.SetActive(true);
        }
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

감자 식물은 

현재 졸업 작품 준비중므로 공유할 수 없습니다.

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

 

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 기본 환경 세팅이 완성되었습니다.

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

 

본 내용은 위 강의의 내용을 참조하였습니다.

 

이번에는 리사이클 뷰의 아이템을 클릭 시 

아이템 내용에 대해 구체적인 설명을 하는 액티비티를 생성하는 

기능을 추가해보겠습니다. 

 

 

우선 클릭시 등장하는 액티비티를 만들어 보겠습니다.

 

 

empty 액티비티 하나를 생성합니다. 

 

컴포넌트 트리에 위와 같이 이미지 뷰와 텍스트 뷰 2개를 등록합니다.

ConstraintLayout은 이제 어느 정도 하실 수 있을 겁니다.

혹시 안된다면 위의 동영상을 참조해주세요

 

 

 

이런 느낌으로 만들어 주세요

전 이미지가 커서 이미지 뷰 가로 세로 크기를

100dp로 고정시켰습니다.

다음으로 my_row로 돌아가서 카드뷰가 부모로 하는 ConstraintLayout의 아이디를 만들어주세요

클릭 리스너에 등록을 위해 필요합니다. 

아이디 등록이 끝났다면, 어댑터의 뷰 홀더로 돌아가서 등록합니다.

 

 

아이템이 클릭되면  자세한 설명이 있는 액티비티로 넘기기 위해 아래와 같이

layout에 리스너를 설정합니다.

 

자세한 설명이 있는 액티비티로 누른 아이템의 정보도 같이 putExtra로 담아줍니다.

 

이제 자세한 설명이 있는 액티비티로 넘어옵니다.

코드는 아래와 같이 작성합니다.

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
package com.example.recyclerview;
 
 
 
public class Main2Activity extends AppCompatActivity {
 
    ImageView mainImageView;
    TextView title, description;
 
    String data1, data2;
    int myImage;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
 
        mainImageView = findViewById(R.id.imageView2);
        title = findViewById(R.id.textView3);
        description = findViewById(R.id.textView4);
 
        getData();
        setData();
    }
 
    private void getData(){
        if(getIntent().hasExtra("images"&&
                getIntent().hasExtra("title"&& getIntent().hasExtra("description"))
        {
            data1 = getIntent().getStringExtra("title");
            data2 = getIntent().getStringExtra("description");
            myImage = getIntent().getIntExtra("images"1);
 
        } else{
            Toast.makeText(this,"No data", Toast.LENGTH_SHORT).show();
        }
 
 
    }
 
    private void setData() {
        title.setText(data1);
        description.setText(data2);
        mainImageView.setImageResource(myImage);
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
 

이 코드의 핵심은 getData함수 소스라 할 수 있습니다.

getIntent.hasExtra()를 통해 인텐트에 담겨온 데이터가 있는지 여부를 판별합니다.

있다면 getStringExtra와 getIntExtra를 통해 데이터를 받아옵니다.

이제 받아온 데이터를 setData()함수를 통해서 값을 넣어줍니다.

 

강아지를 클릭해보겠습니다.

 

소스코드는 깃허브에 있습니다.

https://github.com/3210439/RecyclerView

https://www.youtube.com/watch?v=18VcnYN5_LM

본 내용은 위의 강의를 참조하였습니다.

 

오늘은 위의 이미지와 같은 리사이클 뷰를 만들어 보겠습니다.

 

리사이클 뷰에 대해 소개하겠습니다.

리사이클 뷰는 사용자가 관리하는 수의 데이터 집합을 개별 아이템 단위로 구성하여

화면에 출력하는 뷰 그룹이며, 한 화면에 표시되기 힘든 많은 수의 데이터를 스크롤 가능한 리스트로

표시해주는 위젯 이라고 합니다.

 

또한, 리사이클 뷰는 기존에 사용되던 listView에 유연함과 성능을 더한 것으로

구글에서도 이제 listView보다는 리사이클 뷰를 사용할 것을 권고한다고 하네요

 

코딩으로 넘어가겠습니다.

프로젝트를 생성 하였다면 

리사이클 뷰를 레이아웃 디자인에서 RecyclerView 검색을 통해

레이아웃 안에 넣어줍니다.

 

위 화면에서 OK를 누르게 되면 자동적으로, RecylerView에 필요한 라이브러리를 

추가해줍니다. 

 

 

그래들 앱 수준에서 보면

recylerview:1.1.0이 추가된 것을 확인할 수 있습니다.

 

다음으로, string 배열을 생성 합니다.

strings.xml로 이동합니다.

 

 

위와 같이 리사이클 뷰의 아이템 이름과, 설명의 배열을

생성하였습니다.

 

다음으로는 이미지를 준비합시다. 

저 같은 경우는 동물 이미지가 있기 때문에 

활용하도록 하겠습니다. 

 

 

이미지들을 Ctrl+c, Ctrl+v를 통해 drawable 폴더에 넣어줍니다.

 

 

이제, 메인 액티비티에서 배열들을 생성해줍니다.

참고로 이미지 배열에 들어가는 R 주소들은

string-array에 생성했던 동물들 순서로 해주시기 바랍니다.

일치하지 않을 경우 이미지는 고양이인데 이름은 고양이가 아닐 수 도 있습니다.

 

 

다음으로 리사이클 뷰를 위한 어뎁터 클래스를 생성합니다.

 

위와 같이 MyAdapter라는 자바 파일을 생성하고

RecyclerView.Adapter를 부모로 합니다.

빨간 부분은 alt+enter를 눌러서

Create class 'MyViewHolder'를 선택하면 됩니다.

 

 

계속해서 위와 같이 작성합니다.

 

빨간 줄은 빨간줄 선택 후 alt+enter에서

Implement methods를 선택하여 해결합니다.

다음으로 어댑터에 데이터를 전달하기 위해서

생성자를 생성하고

매개변수는 아까 만들어두었던 배열들을 전달하기 위한

형태로 만듭니다.

 

메인 액티비티로 돌아가서 어댑터를 생성합니다.

 

 

이제

리사이클 뷰의 아이템의 형태를 만들어야 합니다.

layout폴더에 xml 파일을 생성합니다.

 

LinearLayout을 ConstraintLayout으로 변경합니다.

그리고 CardView를 넣어줍니다.

 

자동으로 라이브러리를 임포트

 

카드뷰에 레이아웃을 넣어주고

레이아웃 안에 이미지 뷰, 텍스트뷰1, 텍스트뷰2를 넣어줍니다.

 

다음으로 레이아웃을

이렇게 배치하겠습니다.

우선 이미지를 클릭합니다

그러면 우측에

 

화면이 뜹니다.

파란색 원안에 +표시가 돼있는 4개의 원을

모두 클릭합니다.

동쪽 방향 원은 다시 풀고 클릭 후 누른 상태를 유지한 상태에서

오른쪽 끝에다 가져다 둡니다.

 

 

이 상태에서

아래의 값이 50이 되어있을 것인데 그것을 0으로 바꿔주면

 

위와 같이 이미지가 왼쪽에 붙게 됩니다.

 

이번엔 텍스트 뷰를 조정합니다.

위 이미지처럼 만들기 위해

우선 위쪽 텍스트를 클릭해 +표시 파랑원을 모두 클릭하고

다음 밑에 텍스트도 같은 작업을 반복합니다.

그리고 2개의 수치를

이렇게 맞춰주세요

근대 텍스트 간 간격이 8+8이되

너무 큽니다.

아래 텍스트의 북쪽 margin 값을 0으로 줍니다.

 

이제 제목은 굵은 글씨로 만들고 크게 만듭시다.

 

위 텍스트뷰 속성에

위 이미지 설정을 추가해주세요

 

그러면

 

아래와 같이 됩니다. 텍스트 값으로 Title과 description을 각각 텍스트뷰에 넣어주세요 

 

이제 거의 다 됐습니다.

카드뷰가 부모로 하는

Contraintlayout의 height를 wrap_parent로 바꿔주세요

안 그러면 화면에 한 개의 행 밖에 안 보여 준다네요 

그리고, height 아래 android:layout_margin="10dp"

을 추가해주세요

 

다음으로 위의 카드뷰 +원을 모두 클릭하여 주세요 그러면 빨간 줄이 사라집니다.

 

이제 다시 어댑터로 돌아옵니다.

이제 현재 만들어준 아이템을 휴대폰에 띄우기 위해서 사용대는 메서드

onCreateViewHolder를 아래와 같이 작성합니다.

인플레이터는 부풀리는 것이라는 뜻으로 해석해 볼 수 있는데

이 친구가 리사이클 뷰에 속해있는 아이템을 휴대폰에 그리는 역할을 합니다.

이제 이미지가 생성되었으니 

이미지에 값을 넣기 위해서 아이디 변수를 생성 후 아이디를 각각 넣어줍시다.

이 작업은 ViewHolder에서 진행됩니다.

이제 값을 전달할 수 있게 되었습니다.

값 전달은 onBindViewHolder에서 진행합니다.

bind란 묶다란 뜻으로 텍스트에 값을 넣겠다란 뜻으로 해석할 수 있을 거 같습니다.

 

마지막으로, getItemCount 아이템의 개수를 알려주는 메서드를 설정합시다.

어댑터 설정이 끝났으니 리사이클 뷰에 어댑터를 연결합시다.

 

 

이런 뭔가가 잘못됐군요 이미지 크기가 매우 크게 나온 것으로 보아

my_row로 돌아가서 이미지 뷰 크기를 wrap_content에서 60dp로 

수정하겠습니다.

 

 

이제 잘되는군요

 

소스코드는 깃허브에 올려 두었습니다.

https://github.com/3210439/RecyclerView

 

3210439/RecyclerView

Contribute to 3210439/RecyclerView development by creating an account on GitHub.

github.com

 

안녕하세요! 여러분들 오늘은 SharedPreference에 대해 알아보아요

간단한 로그인 기능을 구현해볼 것입니다.

 

1) 프로젝트를 생성합니다.

empty 설정 해주세요

 

2) xml 코드를 작성합니다.

코드를 작성하다가 android:onClick="erase" 에서는 붉은 줄이 생길 텐데요,

그러면 alt + enter 키로 자바 폴더에 생성하기 를 하시면 간단하게 

터치 기능을 사용할 수 있습니다.

 

 

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
<?xml version="1.0" encoding="utf-8"?>
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
 
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="32dp"
        android:layout_marginLeft="32dp"
        android:layout_marginTop="60dp"
        android:layout_marginEnd="32dp"
        android:layout_marginRight="32dp"
        android:layout_marginBottom="32dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
 
 
        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="로그인"
            android:textStyle="bold"
            android:gravity="center"
            android:textSize="25sp"/>
 
        <EditText
            android:layout_marginTop="30dp"
            android:id="@+id/editText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="ID 입력"
            android:inputType="textPersonName" />
 
        <EditText
            android:id="@+id/editText2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="비밀번호 입력"
            android:inputType="textPersonName" />
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
 
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="erase"
                android:text="지우기" />
 
            <Button
                android:id="@+id/button2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="confirm"
                android:text="로그인" />
 
        </LinearLayout>
 
        <CheckBox
            android:layout_marginTop="10dp"
            android:id="@+id/checkBox"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="아이디 저장하기"
            />
 
    </LinearLayout>
 
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

 

따라 해 보시면 위의 이미지와 같이 됩니다. 

위의 이미지를 보면 아이디 저장하기라는 체크 박스가 있습니다.

체크가 된 상태인지 onStop메서드가 호출될 때 확인할 것이며

체크가 되었다면 sharedPreference를 사용하여 File이라는

파일에 아이디를 저장해 둘 것입니다. 

 

3) Java파일 코드를 작성합니다.

이번엔 기능을 넣기 위해 자바 파일을 설정합니다.

 

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
package com.example.test_sharedpreference;
 
import android.content.SharedPreferences;
 
 
public class MainActivity extends AppCompatActivity {
 
    private EditText editText1;
    private EditText editText2;
    private CheckBox checkBox;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText1 = findViewById(R.id.editText);
        editText2 = findViewById(R.id.editText2);
        checkBox = findViewById(R.id.checkBox);
 
        //File이란 파일로 저장해둔 값을 가져오기위한 설정
        SharedPreferences sf = getSharedPreferences("File",MODE_PRIVATE);
        //text1에 값이 있으면 가져오고 두번째 인자는 없을경우 가져오는 값이다.
        String text1 = sf.getString("text1","");
 
        // text!가 그냥 공백이 아니라면 아이디 저장이된 상태이기 때문에 체크박스를
        // 체크 상태로 변환한다.
        if(!(text1.equals("")))
            checkBox.setChecked(true);
 
        editText1.setText(text1);
 
 
    }
 
    public void erase(View view) {
        editText1.setText("");
        editText2.setText("");
    }
 
    public void confirm(View view) {
 
    }
 
    @Override
    protected void onStop() {
        super.onStop();
 
        SharedPreferences sharedPreferences = getSharedPreferences("File",MODE_PRIVATE);
        //저장을 하기위해 editor를 이용하여 값을 저장시켜준다.
 
        //체크 박스에 체크가 됬다면 아이디를 저장한다.
        if(checkBox.isChecked()) {
            String text1 = editText1.getText().toString();
            editor.putString("text1", text1);
        }
        else
        {
            editor.putString("text1""");
        }
 
        // 값을 다 넣었으면 commit으로 완료한다.
        editor.commit();
    }
}
 
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

이상으로 sharedPreference를 사용해 보았습니다. 

혹시 궁금한 것이 있다면 댓글로 남겨주세요 

 

소스코드는 깃허브에 있습니다.

https://github.com/3210439/test_sharedPreference

 

3210439/test_sharedPreference

Contribute to 3210439/test_sharedPreference development by creating an account on GitHub.

github.com

 

+ Recent posts