실감 기본편 언리얼 엔진 블루프린트 활용
GCC 사관학교
안녕하세요
블루프린트 강의를 담당하게 된 탁광욱입니다
이번에는 블루프린트를 활용해서
간단한 기능을 제작해 보겠습니다
이번 시간에는 실습 예제를 통해서 기능을 설계하고
설계한 작업이 제대로 작동하는지 검증하고
그리고 그 기능을 최종적으로 완료하는
일련의 방식에 대해서 알아보도록 하겠습니다
Collision의 역할 및 작동 원리
이번에는 앞서 배운 블루프린트 기초 이론을 이용해서
간단하게 기능을 만들어 볼 건데요
어떤 기능이냐 하면 이렇게 하얀색 발판이 있고
이 하얀색 발판에 플레이어가 올라가게 되면
이 하얀색 발판이 버튼이 되어서
이 앞에 있던 문이 열리는 기능을 만들어 보려고 합니다
그러기 위해서 몇 가지 준비 사항이 필요해요
먼저 문을 설치를 해야 될 것 같아요
문은 StarterContent에 있는 애셋을 이용해 볼 거예요
Content Drawer에 보면
StarterContent라고 하는 폴더가 있을 겁니다
이것은 프로젝트를 처음 생성했을 때
포함시켜서 설치를 하겠느냐 안 하겠느냐를
설정을 할 수가 있어요
만약에 없다면 그때 설정하지 않았다면
이 + 버튼을 눌러서 상단에 보시면 이렇게
Add Feature or Content Pack이라고 하는
항목이 있습니다
이 항목을 클릭을 하면 여러 가지 컨텐츠 항목들을
추가를 할 수가 있는데요
그 중에서 Content라고 하는 항목이 있어요
여기에 보면 Starter Content가 있습니다
이 Starter Content를 선택하고
Add to Project를 누르면
좀 전에 봤었던 이 StarterContent 폴더가 생성이 되면서
그 안에 있는 여러 가지 애셋들을 사용할 수가 있게 돼요
그 안에 Props이라고 하는 항목 중에 보시면
여러 가지 애셋들이 있죠
그 중에 보면 SM_Door라고 하는 애셋이 있을 겁니다
애셋을 배치를 시켜 보도록 할게요
우리가 원하는 것은 여기 중간에 어떤 발판을 놓고
이 발판에 플레이어가 이 발판을 누르게 되면
Z축으로 이렇게 회전하는 것을 만들어 볼 거예요
플레이 버튼을 누르게 되면, 이렇게 플레이를 하게 되면
이렇게 캐릭터가 나오지 않죠
이렇게 돌아다닐 수만 있어요
우리가 원하는 것은 캐릭터를 직접 조작을 해서
그 캐릭터가 버튼을 누르는 것을 원하는데
캐릭터가 지금 나오지 않기 때문에
캐릭터가 나오는 시스템을 설치를 해줘야 됩니다
이렇게 나오는 이유는 처음에 생성할 때
3인칭 모드로 만든 것이 아니기 때문에
이런 시스템이 자동적으로 탑재되지 않았을 거예요
그러기 위해서는 일단 취소를 하고
역시 마찬가지 Content Drawer의 상단에
Add 버튼을 클릭을 하고
Add Feature or Content Pack을 클릭을 하면
좀 전에 우리 컨텐츠를 클릭을 했었죠
여기 보시면 블루프린트 항목에
여러 가지 모드들을 설치할 수 있게끔
이렇게 모아져 있어요
굳이 우리가 프로젝트를 생성하기 전에
이걸 선택하지 않았다고 해서
굳이 프로젝트를 다시 생성할 필요는 없다는 거죠
이렇게 추가적으로 넣을 수도 있게 되는 겁니다
우리는 간단하게 Third Person을 클릭한 상태에서
Add to Project를 클릭을 하면
이렇게 설치가 된 걸 볼 수가 있고요
Content Browser가 따로 튀어나와 있네요
다시 닫고
Content Drawer를 클릭하면 이제
여러 가지 폴더들이 생겼을 겁니다
Characters 폴더, LevelPrototyping,
Third Person이라고 하는 폴더가 생성이 됐을 거예요
이렇게 되면 플레이를 한다고 해서
바로 적용이 되는 건 아니고요
일정한 설정을 좀 해줘야 돼요
Edit→Project Settings에서
좌측에 보시면 프로젝트 항목에
Maps&Modes라고 하는 항목이 있습니다
Maps&Modes 항목에 보시면
이 모드를 바꿀 수가 있어요
여기서 우리가 받았던 Third Person 모드로
바꿔주시면 됩니다
GameModeBase를 클릭
상단에 보시면
BP_ThirdPersonGameMode라고 하는 항목이 있죠
이 항목을 선택을 해주시면
아래에 있는 것도 알아서 착착착착
적용이 된 것을 볼 수가 있을 거예요
이렇게 적용이 됐다면
Project Settings 창을 끄고
다시 플레이를 하게 되면
이런 식으로 캐릭터가 나오게 됩니다
캐릭터가 나오는 지점은
이런 식으로 이 깃발 표시 있죠
이게 PlayerStart라고 하는 액터인데
이 액터가 있는 곳에서
캐릭터가 등장하게끔
그렇게 지정을 해주는 역할을 하는 액터예요, 이 액터는
그래서 플레이를 하게 되면
캐릭터는 여기에 나오게 됩니다
만약에 이 PlayerStart 위치를
뒤에다가 이렇게 빼놓으면
플레이를 하면 그 위치에다가
좀 더 뒷부분에 캐릭터가 생성되는 것을 볼 수가 있죠
그러면 문에서 조금 한 발짝 멀어진 상태에서
캐릭터가 등장한 걸 볼 수가 있죠
좀 더 뒤에다 놔도 괜찮을 것 같네요
좀 더 뒤에다 놓을게요
이렇게 뒤에다 놓고 플레이를 해보면 좀 더 먼 곳에서
이렇게 시작하는 것을 볼 수가 있습니다
이제 준비는 다 됐네요
문도 준비가 됐고 플레이어도 준비가 됐으니까
문을 열 수 있는 버튼 블루프린트를 만들어 보도록 할게요
Content Drawer를 다시 열어서
이 BP 폴더 안에다가 만들어 보도록 할게요
우클릭, Blueprint Class를 클릭
역시 이번에도 마찬가지, 액터 클래스를 상속받아서
이 레벨에다가 배치할 수 있게끔 해볼게요
그래서 액터를 클릭
이름은 BP_Door로 할게요
더블 클릭해서 블루프린트 안으로 들어가 보도록 하겠습니다
역시 마찬가지 이 패널은 레벨 옆에다가 이렇게 붙고
Basic은 필요 없으니까 닫도록 하겠습니다
먼저 외형부터 좀 간단하게 만들어 보도록 할게요
먼저 바닥이라는 여기가 버튼이라고 하는
표현을 하기 위해서
간단한 박스를 이용해서 버튼을 만들어 보도록 할게요
실제 버튼을 누르는 버튼은 아니고
여기가 발판이야라고 하는
시각적인 표시라고 생각하시면 좋을 것 같아요
그래서 Components에서 + 버튼을 눌러
Cube라고 검색을 해보면 이렇게
큐브 셰이프를 꺼낼 수가 있게 됩니다
클릭해서 큐브를 꺼내 볼게요
이것은 큐브 모양의 외형을 갖도록 하는 컴포넌트에요
단순히 외형의 모습만을 담당하죠
그리고 여기에는 Static Mesh가
Cube라고 하는 Static Mesh가 들어가 있어서
이렇게 사각형의 모습을 하고 있는 거예요
이 사각형의 모습을 조금 변형시켜서 납작하게 만들어 볼게요
Z축으로 0.1 정도로 이렇게 납작하게 만들어 주면
좀 더 납작해도 되겠네요 0.05 정도로
납작하게 이렇게 만들어 주도록 하겠습니다
이렇게 되면 캐릭터가 위에 딱 밟을 수 있게끔
간단한 표시가 됐겠죠
다음, 큐브라고 하는 발판은
직접적으로 눌려지는 것이 아니에요
직접 누르는 것이 아니라
그냥 여기는 누를 수 있는 위치야라고
표시만 되는 거라고 이야기를 했었죠
우리가 캐릭터를 이동해서 이 발판 위에 올라서서
이 발판을 누른다는 개념은
사실상 여기에 어떤 감지를 할 수 있는
캐릭터를 감지할 수 있는 어떤 영역이 있어서
캐릭터가 그 공간 안으로 들어와서 충돌되었을 때
오버랩 되었을 때 눌렀다 혹은
누른 이벤트가 발생했다고 볼 수 있는 거죠
그래서 중요한 것은 캐릭터가 충돌할 수 있는
캐릭터가 들어갈 수 있는 영역이 필요합니다
그래서 캐릭터와 충돌할 수 있는 그 영역
실질적으로 캐릭터와 충돌하게 할 수 있는
이 영역을 무엇이라고 하냐면
Collision이라고 이야기를 합니다
그래서 Collision을 설치를 해줘야 돼요
역시 마찬가지 Components에 + 버튼을 눌러서
Collision이라고 검색을 하면
여러가지 형태의 Collision들을 꺼낼 수가 있게 돼요
그 중에서 Box Collision을 선택을 해 볼게요
이름은 그냥 Box로 지어주도록 하겠습니다
이렇게 Box Collision 컴포넌트를 만들게 되면
이렇게 노란색의 와이어프레임만 있는
도형이 생성된 걸 볼 수가 있습니다
이게 영역을 의미하는 거예요
충돌 영역을 표시한 겁니다
이 영역을 위로 올리거나
혹은 사이즈를 이용해서, Scale을 이용하든지
아니면 그 밑에 있는 Box Extent라고 하는 항목이 있죠
이 Box Extent를 이용해서 수치대로 조정을 할 수 있어요
사이즈에 맞춰서 조금 조정을 해 보도록 하겠습니다
이것을 50 정도로
50, 50으로 맞춰 놓도록 하겠습니다
그럼 밑에 있는 하얀색 박스 모양과 거의 일치가 될 거예요
그리고 벗어나지 않게끔 위에다가 설치를 해 보도록 하죠
이렇게 영역을 표시를 하게 되면
캐릭터가 이 하얀색 발판을 보고 밟으러 오게 되면
캐릭터가 이 Collision Box와 중첩이 되면서
캐릭터가 이 영역 안으로 들어왔다고 인지를 하게 되면
이제 그 인지됨을 통해서 문을 열리게끔 실행하게 되는 거죠
그러면 유저들은 발판을 눌렀구나라고 생각하게 되는 겁니다
그래서 이 영역은 캐릭터가 이 공간에 들어왔다
라는 것을 인지하는 공간
감지 공간이라고 생각하시면 좋아요
그게 바로 Collision입니다
일단 외형만 이렇게 놓고 컴파일을 하고 저장을 한 다음에
여기다가 설치를 한번 해 볼게요
Content Drawer에서 블루프린트를
드래그&드롭해서 이렇게 얹어 볼게요
이렇게 얹어 놓으면 하얀색 발판과 함께
그리고 노란색 Collision 영역까지
표현되는 걸 볼 수가 있죠
그러면 캐릭터가 여기에 이렇게
발판을 밟으러 왔을 때 잘 감지가 되는지
우리가 Hello World를 출력했었던 것처럼
감지되었습니다를 출력을 해서 감지가 되는지 안 되는지
작동이 잘 되는지 안 되는지 확인해 보도록 하겠습니다
다시 BP_Door로 들어와서
이번에는 출력 기능을 만들어야 되니까
상단에 Event Graph 항목으로 가야 되겠죠
Event Graph를 클릭
일단은 Print String
Print String을
Print String 노드를 선택을 하고요
이 Print String 노드에서
감지되었습니다를 출력하도록
이렇게 설정을 해 볼게요
이 노드는 어디에 연결을 해야 될까요
어떤 이벤트 노드에 연결을 해야 될까요
시작할 때? 살아있을 때? 혹은 액터와 겹쳐질 때?
당연히 액터와 겹쳐질 때겠죠
우리가 만들었던 이 Collision이라고 하는 것이
바로 이 ActorBeginOverlap과 연관이 되어 있어요
즉 BeginOverlap, 이 블루프린트가
외부의 어떤 액터와 Overlap
중첩이 됐을 때, 겹쳐졌을 때
어떠한 이벤트를 발생하라고 하는 노드예요
그때 중첩이 되는 것을 어떻게 알아차리느냐
그것이 바로 이 박스, Collision의 역할인 거죠
Collision과 캐릭터가 중첩이 됐을 때
겹쳐졌을 때가 바로
ActorBeginOverlap이 발생됐을 때
실행됐을 때를 의미하는 겁니다
그래서 이 ActorBeginOverlap의 실행 핀을
Print String에다가 이렇게 연결을 해주면
액터가 Collision에 부딪혔을 때
Print String 노드를 실행하게 될 거예요
한번 봅시다
Compile, 저장, 돌아와서 플레이를 해 볼게요
지금 보면 이렇게 캐릭터가 있죠
이 발판으로 가보겠습니다
이렇게 가면 좌측 상단에
감지되었습니다라는 문구가 뜬 걸 보셨나요?
조금 더 길게 할게요
10초로 하고 Compile, 저장, 다시 플레이를 해보면
캐릭터를 움직여서 발판으로 가볼게요
가면 좌측 상단에 감지되었습니다
라고 하는 문구가 나타난 걸 볼 수가 있죠
그렇다는 것은 잘 작동을 한다는 것을
알 수가 있게 되는 거죠
그러면 우리는 이제 이 ActorBeginOverlap에서
어떠한 기능들을 추가하면 될 거예요
잠시 Print String을 지우고요
이 노드를 통해서 우리는 무엇을 하냐
이 레벨에 있는 이 문을 회전시켜서 열 거예요
노드를 하나 추가를 하도록 하겠습니다
우클릭을 해서 Add Actor World Rotation
이라고 하는 노드를 꺼내 보도록 하겠습니다
이 노드는 액터를 회전시켜주는 노드예요
그래서 보시면 필요한 데이터 핀들이
왼쪽에 두 가지가 있죠
하나는 타깃이 누구냐
지금 self라고 하면 BP_Door 본인이에요
즉 무슨 얘기냐면 이것 자체가 돌아가겠다
이 발판 자체가 돌아가겠다는 건데
우리는 이걸 원하진 않아요
그래서 우리가 원하는 것은 이 문이니까
다른 액터죠
그래서 이 다른 액터를 변수에 담아서
그 액터를 여기다가 연결해서
외부에 있는 액터를 회전시킬 수 있게끔
그렇게 만들어줄 거예요
그래서 변수를 일단 만들어줄 거예요
VARIABLES에서 + 버튼을 눌러서
이름은 Door라고 할게요
외부에 있는 액터를 변수로 담아둬야 되기 때문에
액터를 담는 그릇, 즉 자료형이 Actor여야 할 거예요
분류는 큰 선택을 해서 여기서 Actor라고 검색을 해 볼게요
그러면 Actor의 오브젝트 타입 중에
Actor에 Object Reference라고 하는 항목이 있습니다
이 항목을 클릭을 해주면
액터 클래스를 담을 수 있는 변수로 만들어지게 된 거예요
액터 자료형은 우리가 자료형을 배울 때
기본 자료형과 사용자 정의 자료형
두 가지가 있다고 했었죠
이 액터 자료형은 두 번째
사용자 정의 자료형에 속해요
특히 이 액터는 언리얼에서 만들어진 사용자 자료형이죠
그래서 액터라고 하는 클래스를 담을 수 있도록
미리 만들어둔 클래스 자료형이라고 보시면 돼요
이 변수에다가 우리는 외부에서 문을 집어 넣어야 하니까
눈을 켜서, 혹은 Instance Editable 항목을 체크를 해서
외부로 뺄 수 있도록 설정을 해 보도록 하겠습니다
그리고 이 Door 변수는 이제 여기다가 연결을 해야겠죠
드래그&드롭해서 Get Door를 클릭을 해서
변수를 가져와야겠죠
이 변수를 가지고 와서 이 변수를
Target에다가 이렇게 이어줘요
컴파일을 하고 저장하고 잠시 볼까요
그러면 이 블루프린트를 선택을 해서 Details 창을 보시면
이렇게 중간에 Door라고 하는 변수가 지금 보이게 되죠
그리고 이 Door는 지금 아무것도 담겨 있지 않은데
이 슬롯에다가 이 Door를 지정만 해주면 돼요
지정하는 방법은 이 None 표시를 클릭을 해서
직접 SM_Door를 선택할 수도 있고요
혹은 우측에 스포이드 버튼을 클릭해서
직접 이 오브젝트를 선택해서 가져오는 방법도 있습니다
이렇게요
그러면 들어간 걸 볼 수가 있죠
그럼 이렇게 선택이 된 거예요
그럼 이제 다시 돌아오면
이제 이 변수에는 Door 액터가
담겨있는 걸 알 수가 있겠죠
그 Door가 회전할 거야
회전할 거야라고 우리는 기능을 넣어주는 거예요
그럼 보시면 이제 밑에 여기 항목이 있어요
얼마만큼 돌려줄 거야를
이 밑에 있는 항목에서 제어를 하게 됩니다
보시면 Delta Rotation이 되어 있고
그 다음에 X, Y, Z 라고 되어 있습니다
즉 Delta라고 하는 건 가변 값이라고 생각하시면 돼요
Delta Rotation은 얼마만큼 회전시킬 거니
얼마만큼 회전시킬 거니?라고 하는 거예요
X축으로는 얼마만큼, Y축으로는 얼마만큼,
Z축으로는 얼마만큼 회전시킬 거니
얼마만큼 회전시킬까를 설정해준다고 보시면 돼요
이 항목은 어떤 개념이냐면
Door 액터를 선택한 상태에서
이 Rotation을 보시면 X, Y, Z가 있죠
이 X, Y, Z의 각도를 지정해 준다고 보시면 돼요
우리가 원하는 것은 보시면 알겠지만
이렇게 화살표가 이렇게 X, Y, Z로 되어 있어요
기즈모가 월드 기준으로 되어있죠
월드 기준으로 회전을 할 예정이니까
우리는 월드를 기준으로 놓고
보시면 파란색 화살표 Z축을 기준으로 해서
이렇게 움직인다는 것을 볼 수가 있을 거예요
그래서 이렇게 90도만큼 이렇게 더해주면 되겠죠
다시 돌려 놓고
여기다가 Z축에만 90도를 입력하면 되겠죠
그러기 위해서는 지금 다 합쳐져 있으니까
이 합쳐져 있는 Input Pin을
입력 핀을 풀어주면 좋겠어요
그래서 입력 핀을 우클릭을 할게요
그러면 Split Struct Pin이라고 하는 항목이 있습니다
이 항목을 선택을 해줄게요
그러면 이렇게 X, Y, Z로 분리가 되는 걸 볼 수가 있어요
X, Y 는 영영 그대로고 우리가 원하는 것은 Z축이죠
Z축으로 90도만큼, 이런 식으로 입력을 해주면 돼요
그런 다음에 우리는 ActorBeginOverlap에다가
이렇게 연결을 해주면 되겠죠
그러면 오버랩이 되는 순간
Door 액터에 Z축으로 90도를 더해서
회전시키겠다는 기능이 되는 거예요
한번 해볼게요
컴파일과 저장을 누르고요
다시 돌아와서 플레이 버튼을 누르려고 하는데
아 여기서 잠깐만
지금 현재 이 문은 아마 움직이지 않을 거예요
플레이하면 움직이지 않을 거예요
그 이유는 뭐냐면 이 메시의 속성이 움직이지 마
즉 멈춰 있어, 움직이지 않은 존재야라고 정해져 있어요
Details 창을 보시면, Details 창에
이 문을 선택한 상태에서 Details 창을 보시면
Transform 하단에 Mobility라고 하는 항목이 있습니다
이 Mobility 항목이 바로 그 항목이거든요
거기가 지금 Static이라고 체크가 되어 있을 겁니다
Static이 된다는 게 뭐냐면 이 액터는
절대 움직이지 않는 존재라고 박아버린 거예요
이 Static을 Movable로 바꿔주셔야 됩니다
그래야 움직일 수 있어요
이렇게 한 상태에서 플레이 버튼을 눌러보면
캐릭터가 있고 발판으로 가볼게요
이렇게 발판을 밟게 되면 이런 식으로 탁 하고
열리게 되는 걸 볼 수가 있을 겁니다
그런데 보시면 아시겠지만
플레이를 했을 때 발판을 눌렀는데
탁 하고 열리는 게 영 맘에 들진 않아요
점점 부드럽게 이렇게 슥 열리면 좋겠는데
탁 하고 열리니까 자연스럽진 못하죠
그래서 우리가 만들었던 이 노드를
좀 더 부드럽게 문을 열 수 있게끔 만들어볼 거예요
그러면 어떻게 하는 게 좋을까요
매 프레임마다 조금씩 조금씩 조금씩 더해서
회전시키는 게 좋겠죠
OK, 그러면 이 노드를 틱에다가 연결을 한번 해볼게요
아마 90도로
매 프레임마다 지금 90도로 돌아가게 되니까
이걸 풀고요
매 프레임마다 90도로 돌아가니까
굉장히 많이 돌아갈 것 같아요
천천히, 매 프레임이니까
1도씩만 움직일 수 있게끔 이렇게 해볼게요
그럼 어떻게 되느냐, 컴파일하고 저장을 해볼게요
플레이를 해보면
플레이를 해보면 이렇게
잘 돌아가는 것처럼 보이긴 해요
잘 돌아가는 것처럼 보이는데
여긴 문제가 있어요
우리가 발판을 밟지도 않았는데
문이 회전을 하고 있는 걸 볼 수가 있죠
우리가 원하는 것은 발판을 밟을 때죠
밟을 때 움직여야 되는데 밟지도 않는데 움직이고 있어요
그래서 우리는 기능을 조금 더 추가를 해줘야 됩니다
이 캐릭터가 이 발판을 밟았다 혹은 밟지 않았다를
설정할 수 있는 기능을 넣어줘야 돼요
이렇게 한번 해보도록 할게요
블루프린트로 돌아와서 우리가 버튼을 밟았는지
캐릭터가 버튼을 밟았는지 안 밟았는지를
어떻게 하면 좋을까요
저는 Boolean 변수를 하나 만들 거예요
참과 거짓을 담을 수 있는 그릇을 하나 만들고
ActorBeginOverlap, 즉 충돌이 됐을 때, 밟았을 때
그 변수를 열린 상태라고 참으로 바꿔주면
열린 상태라고 알려줄 수 있게 되겠죠
그 알려준 변수의 결과에 따라서
이 Rotation 노드가 작동되도록
그렇게 한번 해볼 거예요
그래서 일단 변수를 하나 만들어야겠죠
그래서 VARIABLES에 + 버튼을 눌러서
이름은 isOpen이라고 할게요
열렸니? 라는 정보를 담는 거죠
그래서 자료형은 Boolean으로 해줄게요
이 변수는 외부에 공개할 필요는 없죠
이 안에서만 정보를 갖고 있으면 되니까
굳이 오픈할 필요는 없을 것 같고요
컴파일, 저장을 통해서
기본적인 이 Value 값을 비활성화 시켜줄게요
즉 오픈되어 있지 않다, 열려 있지 않다는 것을
비활성화 된 것으로 알려주는 거예요
이제 ActorBeginOverlap이 실행이 되면
즉 부딪혀서 이벤트가 발생을 했다면
이 isOpen 변수를 체크하도록 이렇게 세팅을 해줘야 돼요
이렇게 어떻게 하냐면 isOpen을 변수를 가지고 올 때
우리는 여태까지 Get isOpen 가져오기만 했었죠
이번에는 Set isOpen을 선택을 할 거예요 이렇게
그래서 ActorBeginOverlap의 실행 핀을
Set 실행 핀에다가 연결을 해줍니다
그리고 이 Set 노드의 Is Open을 체크를 해줄게요
이렇게 되면 ActorBeginOverlap 이벤트가 발생했을 때
즉 캐릭터가 Collision과 부딪혔을 때 실행을 해라
어떤 실행을? Set isOpen 변수를 설정을 해
어떻게? 체크하도록 설정을 하라고 기능을 추가한 거예요
작동이 잘 되는지 한번 Print 노드를 이용해서
Print 노드를 이용해서 확인을 한번 해볼게요
잠시 이 친구들은 이렇게 내리고
여기에는 열렸습니다라고
문구를 집어 넣으면, 만약에 잘 작동이 된다면
Is Open이 체크가 되고
열렸습니다라는 문구까지 출력이 되겠죠
한번 확인해 볼게요
컴파일, 저장, 플레이를 해보고
발판으로 가보도록 할게요
발판에 이렇게 가서 올라가면 열렸습니다가 나오게 되죠
그러면 잘 작동이 된 걸 볼 수가 있습니다, 됐죠?
그러면 이제부터 해야 될 일은 뭐냐면
이 Tick 이라고 하는 이 이벤트에서
Add Actor World Rotation 문을 회전시키는
이 기능을 계속하는 것이 아니라
이 isOpen 변수의 상황에 따라서
즉 Is Open이 참일 때만 이 작업을 해
라는 기능을 추가하면 되겠죠
이제 우리는 여기다가
이 Tick 이라고 하는 이벤트를 발생을 할 때
조건식을 하나 달아두도록 할게요
우리는 앞서서 Branch라고 하는 노드를 배웠을 겁니다
Branch
Branch 노드를 이용해서
이 Tick에서 Branch를 연결을 하고요
이 Branch의 이 조건식의 결과에 따라서
참이면 무엇을, 거짓이면 무엇을 이렇게 실행하는 거죠
우리의 조건식은 뭐냐면 이 isOpen 변수가
True일 때가 바로 열린 상태가 되는 거겠죠
그래서 이 변수를 드래그&드롭해서
Get isOpen 가지고 와서
이 Condition에다가 이렇게 연결을 해줍니다
이렇게 되면 Is Open이 참이라면
Is Open에 담겨있는
이 변수에 담겨있는 값이 참이라면 True
True라면 True 실행 핀이
False, 거짓이라면 False 실행 핀이 작동하게 될 거예요
우리는 이 Is Open이 참일 때
열렸을 때 무엇을 하냐? 회전하게 된다고 그랬죠
그래서 True 핀을 여기다가 이렇게 연결해 주면 돼요
간단하죠? False일 때는 일을 하지 않아도 되니까
아무것도 연결할 필요는 없을 것 같아요
이 상태에서 컴파일, 저장하고 한번 확인해 볼게요
플레이를 하게 되면 지금 상태에서는 문이 움직이지 않죠
그 이유는 지금 Is Open의 값이 False이기 때문이죠
이제 발판으로 다가가서
isOpen 변수를 참으로 만들어 주면 움직일 거예요
가볼게요
착! 열려지죠
열려지죠
다시 플레이를 해보고 이 발판으로 캐릭터가 다가가게 되면
그제서야 이렇게 움직이는 걸 확인해 볼 수가 있어요
됐죠? 여기 또 문제점이 있네요
문이 계속 회전하는 것을 볼 수가 있어요
이렇게 돌다가 각도가 90도가 되면 딱 멈춰야 되겠죠
이 Door의 이 Rotation 값의 이 Z값이
90도보다 크다면 멈추면 되겠죠
그렇죠? 이거는 여기서 할게요
돌고 있는데
여기서 다시 한번 조건식을 만들어 주는 거예요
Branch
Branch인데 조건식은 뭘까요?
Door의 Z값이 90보다 크다면 되겠죠
즉 이 Door죠 이 Door 변수
Door 변수를 일단 가지고 올게요
Get Door, 이 Door가 바로 이 Door죠
이 Door의 Rotation 값을 가지고 옵니다
즉 Transform에 있는 Rotation Z 값을 가져올 거예요
그래서 이렇게 쭉 빼서
이 Door 변수가 갖고 있는 속성 혹은 기능
여러가지 값들 중에서 Get, 가져와, 무엇을?
Actor Rotation
즉 액터의 로테이션 값을 가지고 오라고 하는 겁니다
그러면 이 Door의 로테이션 값을 가지고 온 거예요
그 정보가 지금 여기에 담겨 있어요
역시 마찬가지 리턴의 값을 뺄 수가 있는데
지금 같은 경우에는 값이 X, Y, Z 모두 다 들어 있어요
그래서 우리는 여기서 Z값만 가져오면 되죠
그래서 여기서 우클릭해서
Split Struct Pin을 클릭을 하면
쫘락하고 풀어집니다
우리는 이 중에서 이 값만 비교해 사용하면 돼요
그래서 이 값이, 지금 이 Z값이 90도보다 크다 하면이죠
그래서 우클릭
크다
Greater 비교 연산자를 가지고 옵니다
그리고 이 Z값을 비교 연산자 위에다 놓고요
이 아래쪽에는 얼마보다 클 거니?라는 걸 정할 수가 있는데
일반적인 숫자를 집어넣을 수도 있지만
일단 여기다 그냥 바로 90이라는
숫자를 넣어도 되기는 해요
그러면 90도보다 이 Z값이 크다 하면
90도보다 크다 하면
이렇게 성립이 되는 겁니다
그 결과 값을 이제 이 Branch에다가
이렇게 연결을 해주는 거예요
그러면 이 결과 값이 90도보다 크다 하면 멈춰야겠죠
멈춰야 될 거예요
Door의 Z값이 90도보다 크다 하면
True 값으로 진행이 되겠죠
그때 isOpen 변수를 False로 다시 바꿔주는 거예요
바꿔주게 된다면
매 프레임마다 이 Tick 이벤트가 발생을 한다고 했었죠
그때 다시 한번 발생했을 때
isOpen 변수가 비활성이네? False네?
그러면 True로 가지 말고 False로 가야겠다라고 해서
일을 안 하게 되는 겁니다
즉 멈추게 된다는 거죠
이 모두가 멈추게 되는 거예요
그래서 여기다가 이렇게 작업을 해 볼게요
isOpen 변수를 가지고 와서
Set isOpen 모드를 열어줄 거예요
그래서 90도보다 클 때, 즉 참일 때
Is Open 변수를 비활성화 시켜줘라
라고 실행을 한 거예요
됐죠?
그러고 나서 다시 한번 볼까요
여기 테스트 한번 해볼게요
Print String을 통해서
이제는 멈췄습니다라고 출력해 보도록 할게요
만약에 이게 잘 작동이 된다면
Is Open이 비활성화 되고
멈췄다라는 출력도 나오게 되겠죠
그러면 아 잘 작동을 했겠구나
우리는 알 수 있게 될 거니까요
멈췄습니다는 한
10회 정도, 길게 해보도록 하겠습니다
지금 Open, 열렸습니다가
열렸습니다도 지금 2초로 돼있으니까
금방 사라지니까 10초로 맞춰놓도록 할게요
컴파일, 저장하고 다시 플레이를 해보면
이렇게 버튼을 눌러 이렇게 가면
열렸습니다라는 메시지와 함께 멈췄습니다
이번엔 끝났죠
다 열리고 나서 90도가 넘으니까
멈췄습니다라고 하는 출력이 되면서
문도 자연스럽게 멈춘 걸 볼 수가 있어요
그럼 이번에는 열리는 속도가 조금은 느린 것 같아서
적당한 것을 좀 찾아볼까 해요
지금 1이라고 되어 있는데
변수를 하나 만들어서 외부에서 수정할 수 있도록
그렇게 만들어 보도록 하죠
그래서 VARIABLES을 클릭을 해서
변수를 하나 만들게요
이름은 Speed라고 할게요
값은 Float로 소수점까지 찍어보도록 할게요
그리고 이 변수를 Get Speed로 변수를 빼내와서
변수를 Delta Rotation에 이렇게
집어넣도록 하겠습니다
컴파일을 누르고 저장을 눌러서
Speed에 대한 기본값을 일단은 1로 둬야겠죠
그리고 Instance Editable을 체크를 해서
외부에서도 수정할 수 있게끔 해 뒀고요
컴파일, 저장을 눌러서 이번에는 밖으로 나가면
이 Door가 아니죠, 그리고 이 블루프린트
우리가 만든 블루프린트를 클릭을 해 보면
Door Speed까지 이렇게 설정을 할 수 있는 항목이
노출되어 있는 걸 알 수가 있죠
지금 1로 되어 있는데 조금 더 빠르게 해볼게요
10 정도로 해볼까요
이렇게 있을 경우에 플레이 해볼게요
이렇게 가면
아 이거 너무 빠르네요 그렇죠?
한 3 정도로만 해보겠습니다
플레이를 해보면 이렇게
샥 하고 열리는 걸 볼 수가 있습니다
너무 빠른 것 같다 그러면 더 줄여도 되고요
0.5로 더 줄여도 상관없겠죠
이렇게 천천히 열리는 걸 볼 수가 있습니다
이렇게 Speed 변수를 이용해서
회전문의 속도를 조정을 했었죠
그래서 매 프레임마다 일정한 값이 계속 늘어나게끔
그렇게 기능을 만들어 줬어요
델타 세컨드
그런데 이 기능은 치명적인 오류를 담고 있어요
지금 플레이하고 있는 이 PC에서는
현 시점에서는 오류가 아니에요
하지만 델타 세컨드라고 하는 개념을 여러분들이 알게 되면
이 모드는 커다란 오류에 빠져있다는 걸 알게 됩니다
델타 세컨드에 대해서 알아보도록 할게요
먼저 2대의 PC가 있다고 할게요
철수와 영희, 2명의 플레이어가
2대의 컴퓨터를 놓고 달리기 게임을 한다고 할게요
이 달리기 게임은 하나의 프레임을 읽으면
1m씩 가게끔 이렇게 짜여 있어요
철수의 컴퓨터는 좀 더 좋아서
한 프레임을 읽는데 0.25초가 걸린다고 합시다
그리고 영희의 컴퓨터 같은 경우는
하나의 프레임을 읽는데 0.5초가 걸린다고 했을 때
1초가 되는 시점에는 철수의 PC는 4번 읽게 되고
프레임을 읽을 때마다 1m씩, 1m씩, 1m씩, 1m씩 이동하니까
철수는 결과적으로 4m를 이동하게 될 거고요
영희의 PC는 1초에 2번의 프레임을 읽게 되겠죠
0.5초를 한 프레임에 읽으니까
그러면 한 프레임을 읽을 때 1m씩
한 프레임을 읽을 때 1m씩 하게 되니까
결과적으로는 1초가 됐을 때
2m를 움직이는 결과가 생기게 될 거예요
즉 철수보다 영희는 항상 뒤처질 수밖에 없죠
이것은 PC가 좋고 낮음에 따라서
달리기 게임의 결과가 달라진다는 것을 의미해요
이렇게 되면 형평성이 어긋나죠
그래서 어떤 방법을 쓰게 되냐면
바로 여기서 델타 세컨드
즉 변위 시간을 각각 적용하게 돼요
한 번 프레임을 읽는 시간을 곱해줘서
일정 시간으로 유지시켜 주는 거예요
즉 1초가 지났을 때 1m가 움직이게끔
강제화 시켜주는 거죠
그래서 매 프레임마다 일정한 시간을
철수는 조금 더 작은 숫자의 0.25라는 시간을 곱해주고
영희의 컴퓨터에서는 0.5라고 하는 시간
조금 다른 시간이죠
철수와 영희의 컴퓨터가 컴퓨터에 따라서
조금은 다른 시간을 곱해주는 거죠
결국 이 두 플레이어는 1초가 됐을 때 똑같이
1m가 이렇게 움직일 수 있는 결과값을
얻을 수가 있게 있을 수 있다는 얘깁니다
그렇게 되면 둘 다 형평성에 어긋나지 않겠죠
이 Speed 값에 이 델타 세컨드라고 하는
이 프레임을 읽는 시간의 값을 곱해줌으로 해서
전체 시간을 고정시켜 주는 것은
프로그래밍 할 때 가장 기본이 되는
실시간 콘텐츠를 제작을 할 때
가장 기본이 되는 요소라고 생각하시면 좋아요
그래서 그 작업을 좀 해줄 거예요
그래서 이 빈 공간에서 우클릭을 한 다음에
변위 시간, Get World delta Seconds라고 검색을 해보시면
이런 노드가 나오게 됩니다
이 델타 세컨드는 PC마다 다르고요
그 상황마다 다르게 돼요
이것은 엔진에서 계산해서 보내주는 정보이기 때문에
이것은 그냥 가져와서 사용하기만 하면 됩니다
그래서 이 Delta Seconds를
이 Speed 값에 곱해 주셔야 돼요
그래서 Multiply
이 Speed 값과 이 Delta Seconds를 곱해 주고
이 곱해준 결과값을 이렇게
Delta Rotation에 연결을 해주면 됩니다
이렇게 되어야 디바이스 간의 형평성이 어긋나지 않게 돼요
플레이를 해 보겠습니다
그러면 이제 발판을 누르게 되면
아마 지금은 거의 움직이는 것처럼 보이지 않을 텐데
F8을 눌러서 잠깐 나오게 되면
아마 아주 미세하게, 그렇죠?
계속 올라가는 걸 볼 수가 있을 겁니다
움직이고 있다는 얘기에요
이렇게 작아진 이유는 델타 세컨드를 곱해줌으로 해서
이 매 프레임마다 움직이는 숫자가
굉장히 낮아졌기 때문이에요
그래서 이때는 이 속도 값을
이 Speed 값을 아주 많이 올려줘야 돼요
0.5로 많이 낮아졌었네요
이때는 한 10 정도로 좀 올려 볼게요
플레이를 해 보면
아까보단 조금 더 빨라졌죠
좀 더 빨리 해야 되겠네요
100 정도로 넣어보도록 하겠습니다
플레이해서 가보면
결과는 똑같습니다, 발판을 밟으면 열린다는 것은 똑같지만
기본적으로 우리는 디바이스 간의 형평성을 위해서
델타 세컨드라는 걸 적용시켰다는 것이
지금의 시스템에서는 달라진 거예요
그래서 지금 이 PC에서, 혹은 다른 PC에서
이 기능을 작동시켰을 때 똑같이 이제 움직여질 겁니다
그러면 블루프린트를 이용해서 문을 여는
간단한 기능을 한번 만들어 봤어요
수고하셨습니다
그럼 이번 시간에 학습한 내용을 정리해 보겠습니다
콜리전의 역할 및 작동 원리 자동문 발판에 올라가면 문이 회전하면서 열리는 자동문 기능을 구현 StarterContent→Props의 SM_Door 애셋을 드래그해 월드에 배치
BP 폴더 생성→Actor 클래스 상속받은 BP_Door 블루프린트 생성 후 레벨에 배치
블루프린트 스크립팅 컴포넌트창 +Add를 이용해 Cube 컴포넌트 추가 캐릭터와의 충돌을 위한 Box Collision 컴포넌트 추가
[Event ActorBeginOverlap]에 Print String 노드를 이용해 충돌 감지 테스트
문 회전 설정 Add Actor World Rotation 노드를 이용해 작동 회전할 대상을 담은 Door 변수 생성 뒤 Target 입력 핀에 연결
ActorBeginOverlap 노드의 실행 핀을 Add Actor World Rotation 노드와 연결 SM_Door의 Mobility를 Movable로 변경
Event Tick 노드와 Add Actor World Rotation 노드 연결 후 Delta Rotation Z값 설정 isOpen 변수 생성 후 Boolean 자료형 선택
ActorBeginOverlap 실행 시 isOpen 변수가 활성화되도록 설정 isOpen 변수가 True일 때만 문이 회전하도록 설정
SM_Door의 Rotation Z값이 90보다 클 때 문의 회전이 멈추도록 설정
문 회전 속도 조절 변수 생성 후 Speed로 이름 설정하고 Float 자료형 선택
Speed 변수를 그래프창으로 드래그 후 Get Speed 선택하고 Delta Rotation Z 핀에 연결 Instance Editable을 활성화해 외부에서 수정 가능하게 함
델타 세컨드 프레임의 이해 1프레임마다 일정한 거리로 이동하는 코드일 경우 디바이스의 성능에 따라 거리 차이가 발생
디바이스 사양에 따라 게임 겨로가가 달라지게 되며, 이는 형평성에 어긋남
델타 세컨드 디바이스간의 형평성을 위해서 1프레임을 읽는 시간인 변위 시간 Delta Second를 적용 델타 세컨드를 적용하면 같은 시간에 같은 거리를 이동하게 조절이 가능함
델타 세컨드 적용 Get World delta Seconds 노드 생성 Multiply 생성 후 Speed와 Get World Delta Seconds 노드 연결
Multiply 결과를 Delta Rotation Z 핀에 연결 Speed 값에 따라 문 회전 속도가 달라지는 것 확인