개발단에 가입하여 베르의 게임 개발 유튜브를 후원해주세요! 

 

베르의 게임 개발 유튜브

안녕하세요! 여러분들과 함께 게임 개발을 공부하는 베르입니다! 게임 개발에 도움이 되는 강좌들을 올리는 채널입니다! [투네이션 후원] https://toon.at/donate/637735212761460238 [유니티 어필리에이트

www.youtube.com

안녕하세요! 여러분들과 함께 게임 개발을 공부하는 베르입니다!

이번에는 언리얼 C++에서 만든 함수를 블루프린트에서 호출할 수 있게 만드는 방법을 알아봅시다!

 

사용 엔진 버전 : 5.0.2

 

타임라인

0:00 인트로

0:10 프로젝트 및 클래스 생성

0:47 함수 복습

1:35 함수를 블루프린트에서 호출할 수 있게 만들기

2:53 C++ 클래스 기반으로 블루프린트 클래스 만들기

4:09 함수를 블루프린트에서 호출하기

5:15 아웃트로

스크립트

인트로

안녕하세요. 여러분들과 함께 게임 개발을 공부하는 베르입니다.

이번에는 C++ 클래스에서 만든 함수를 블루프린트에서 호출하는 방법을 알아보도록 하겠습니다.

프로젝트 생성 및 클래스 생성

그럼 이제 언리얼 엔진에서 함수를 만들어보기 위해서 언리얼 엔진을 실행하고 새 프로젝트를 생성합니다.

카테고리는 게임으로 하고, 다른 코드 없이 완전히 비어있는 기본 템플릿을 선택하겠습니다.

그리고 프로젝트 타입을 C++로 변경한 다음 프로젝트를 생성합니다.

프로젝트가 생성되면 Actor 클래스를 상속받아서 C++ 클래스를 하나 생성합니다.

클래스의 이름은 언리얼 엔진이 기본으로 추천해주는 MyActor를 그대로 쓰도록 하겠습니다.

함수 복습

클래스가 생성되고 나면 먼저 MyActor의 헤더 파일로 이동해서 저번 영상의 복습으로 간단한 함수를 만들어 보겠습니다.

일단 bool 타입으로 키가 눌렸음을 알리는 bPressedKey 변수를 선언합니다.

그리고 InputSpaceKey 함수와 MoveUp 함수를 선언합니다.

그 다음에는 소스 파일로 이동해서 함수를 구현해줍니다.

먼저 InputSpaceKey 함수에서는 매개변수를 가져와서 bPressedKey를 설정해줍니다.

그리고 MoveUp 함수에서는 키가 눌려서 bPressedKey 변수가 true가 되었을 때 액터의 위치 값을 가져와서 상승시키도록 계산한 뒤에 다시 액터의 위치로 설정하도록 코드를 작성합니다.

함수를 블루프린트에서 호출할 수 있게 만들기

함수를 완성한 다음에는 이 함수들을 블루프린트에서 호출해서 사용할 계획입니다.

다만 이렇게 그냥 만들어진 함수들은 기본적으로 블루프린트에서 호출하려고 해도 컨텍스트 메뉴에는 보이지 않습니다.

그럼 이제 C++ 클래스에서 직접 만든 함수들을 블루프린트 클래스에서 호출할 수 있도록 만들어 보겠습니다.

언리얼 C++ 변수 영상에서 프로퍼티를 언리얼 에디터에서 볼 수 있게 하기 위해서 UPROPERTY 매크로를 사용한 것을 기억할 겁니다.

그와 대응되는 기능으로 함수에서는 UFUNCTION 매크로를 사용하면 함수를 블루프린트에서 사용할 수 있게 설정하는 등의 기능을 지정자로 넣을 수 있습니다.

함수들에 UFUNCTION 매크로를 추가해주고 지정자는 BlueprintCallable과 Category="Move"로 입력하겠습니다.

UFUNCTION 매크로에서 BlueprintCallable 지정자를 넣어주면 해당 함수를 블루프린트에서 사용할 수 있게 됩니다.

그리고 블루프린트에서 사용할 모든 함수에는 카테고리를 할당해줘야만 정상적으로 호출 할 수 있게 됩니다.

그렇기 때문에 함수를 블루프린트에서 사용하려면 UFUNCTION 매크로에 반드시 카테고리 지정자를 넣어줘야 합니다.

UFUNCTION 매크로를 모두 작성하면 저장하고 언리얼 에디터로 돌아가서 명령 콘솔 창에 LiveCoding.Complie을 입력해서 수정 사항을 컴파일 해줍니다.

C++ 클래스 기반으로 블루프린트 클래스 만들기

그 다음에는 만든 함수가 블루프린트에 잘 호출되는지 확인하기 위해서 MyActor 클래스를 기반으로 블루프린트 클래스를 만들어야 합니다.

콘텐츠 브라우저에서 MyActor C++ 클래스 에셋을 우클릭하고 [MyActor 기반 블루프린트 클래스 생성]을 선택하면 블루프린트 클래스 추가 창이 뜹니다.

블루프린트 클래스의 이름을 BP_MyActor로 입력한 다음 블루프린트 클래스 생성 버튼을 누릅니다.

블루프린트 클래스가 생성되고 나면 생성된 블루프린트 클래스를 대상으로 블루프린트 에디터가 열립니다.

그 다음에는 이 액터가 키 입력을 받아서 움직이는 것을 잘 보이게 만들기 위해서 컴포넌트 패널의 [추가] 버튼을 누르고 Sphere 컴포넌트를 찾아서 추가해줍니다.

그리고 추가된 Sphere 컴포넌트를 DefaultSceneRoot에 드래그&드롭해서 루트 컴포넌트로 만들어 줍니다.

그리고 Sphere 컴포넌트를 선택하고 디테일 패널의 피직스 카테고리에서 피직스 시뮬레이트에 체크해줍니다.

블루프린트 세팅의 마지막 과정으로는 이 액터가 입력을 받을 수 있도록 설정할 차례입니다.

블루프린트 에디터에서 이벤트 그래프 패널로 이동합니다.

그 다음에는 Begin play 이벤트 근처의 그래프 빈 영역에 우클릭하고 컨텍스트 메뉴에서 Get Player Controller와 Enable Input 함수를 찾아서 노드를 배치해줍니다.

그리고 Begin Play 이벤트 노드와 연결해줍니다.

이렇게 하면 이 액터가 입력을 받을 수 있도록 설정됩니다.

함수를 블루프린트에서 호출하기

기본 세팅을 끝냈으니 이제 앞에서 만든 함수들을 블루프린트에서 호출해볼 차례입니다.

그래프의 빈 영역에 우클릭하고 컨텍스트 메뉴에서 스페이스 바 이벤트를 찾아서 추가해줍니다.

그 다음에 다시 빈 영역에 우클릭하고 컨텍스트 메뉴를 살펴보면 Move 카테고리에 우리가 만든 함수를 찾을 수 있습니다.

먼저 InputSpaceKey 함수 노드를 두 개 추가하고 각각 Pressed 핀과 Released 핀에 연결해줍니다.

그리고 Pressed 핀에 연결된 노드에는 Pressed가 true가 되게 체크해줍니다.

그 다음에는 MoveUp 함수 노드를 추가하고 틱 이벤트와 연결해줍니다.

블루프린트 작성이 모두 끝난 다음에는 블루프린트 에디터의 컴파일 버튼과 저장 버튼을 차례대로 누른 뒤 블루프린트 에디터를 닫아줍니다.

그 다음에는 테스트를 위해서 BP_MyActor를 레벨에 배치하고 게임을 플레이시켜줍니다.

이제 스페이스 바를 누르면 액터가 떠오르고 손을 떼면 다시 중력의 영향을 받아 떨어지는 모습을 볼 수 있습니다.

이런 방식으로 프로그래머가 블루프린트에서 호출할 수 있게 핵심 기능을 가진 함수를 만들면 디자이너는 블루프린트에서 함수를 조합해서 게임 기능을 만들면 됩니다.

아웃트로

이번 영상에서는 C++에서 만든 함수를 블루프린트에서 호출하는 방법에 대해서 알아보았습니다.

이 강좌는 시청자 여러분들의 시청과 후원으로 제작되었습니다.

이상 베르의 게임 개발 유튜브였습니다. 감사합니다.

 

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

개발단에 가입하여 베르의 게임 개발 유튜브를 후원해주세요!

 

베르의 게임 개발 유튜브

안녕하세요! 여러분들과 함께 게임 개발을 공부하는 베르입니다! 게임 개발에 도움이 되는 강좌들을 올리는 채널입니다! [투네이션 후원] https://toon.at/donate/637735212761460238 [유니티 어필리에이트

www.youtube.com

 

안녕하세요! 여러분들과 함께 게임 개발을 공부하는 베르입니다!

이번에는 프로그래밍의 함수에 대해서 알아보고 언리얼 프로그래밍에서 함수를 만들고 사용하는 방법을 배워봅시다.

 

사용 엔진 버전 : 5.0.1

 

타임라인

0:00 인트로

0:12 함수란?

2:05 프로젝트 및 클래스 생성

2:39 변수 선언

3:36 함수 선언 및 구현

4:29 함수 만들어보기

5:15 함수 호출하기

7:52 아웃트로

스크립트

인트로

안녕하세요. 여러분들과 함께 게임 개발을 공부하는 베르입니다.

이번 영상에서는 함수의 기본적인 내용과 함께 언리얼 프로그래밍에서 함수를 만들고 호출하는 방법을 알아보도록 합시다.

함수란?

프로그래밍에서 함수는 일정한 동작을 수행하는 코드들을 묶어놓은 것을 이야기하며 이 함수는 반환형, 이름, 매개변수, 몸체를 가집니다.

차례대로 살펴보자면 먼저 반환형은 이 함수가 모든 과정을 수행하고 나서 어떤 타입의 결과값을 돌려주는지 알려주는 부분입니다.

반환형이 int로 되어있다면 이 함수가 모든 동작을 끝내고나서 돌려줄 결과 값의 타입이 정수형이라는 뜻입니다.

언리얼 프로그래밍에서 사용되는 모든 자료형은 반환형으로 사용할 수 있습니다.

또한 함수가 동작을 끝내고 나서 결과값을 돌려줄 필요가 없다면 void라는 타입으로 결과값을 돌려주지 않을 것임을 알려줄 수도 있습니다.

그리고 함수 이름은 이 함수를 호출하는데 사용되는 것으로 개발자가 원하는 대로 이름을 지어주면 됩니다.

다만 함수의 이름을 지을 때는 이 함수가 어떤 작업을 하는 함수인지 이름만 보고도 알 수 있게 하는 것이 좋습니다.

매개변수 부분은 괄호로 묶여있는데 이 부분을 통해서 함수가 동작하는데 필요한 변수를 함수 외부에서 받아와서 함수 내부에서 사용할 수 있게 됩니다.

이렇게 매개변수 괄호가 비어있을 때는 함수에 필요한 외부 변수가 없다는 뜻이 됩니다.

외부에서 변수를 받아와서 사용할 필요가 있을 때는 이렇게 매개변수의 타입과 이름을 넣어주면 됩니다.

만약 여러 개의 매개변수가 필요하다면 끝에 쉼표를 찍고 다음 매개변수의 타입과 이름을 적어주면 됩니다.

마지막으로 함수의 몸체는 이 함수가 실제로 해야할 일을 처리하는 코드를 작성하는 부분입니다.

예를 들어 캐릭터를 원하는 위치로 이동시키는 함수를 구현한다고 가정하면 반환형으로는 남은 거리를 돌려주기 위해서 float으로 하고 함수 이름은 Move로 정하게 될겁니다.

그리고 매개변수는 목적지의 좌표를 받게 되겠죠.

그 다음 함수의 몸체에서는 함수가 호출될 때마다 목적지를 향해서 캐릭터의 이동속도와 시간을 이용해서 조금씩 이동시키고 남은 거리를 반환하는 코드를 작성하게 될 겁니다.

이렇게 프로그래밍에서 함수는 어떠한 작업을 하나의 묶음으로 만들어서 필요할 때마다 재사용할 수 있게 되는데, 작업 효율을 위해서는 필요한 기능 별로 함수를 잘 만들어서 사용해야 합니다.

프로젝트 생성 및 클래스 생성

그럼 이제 언리얼 엔진에서 함수를 만들어보기 위해서 언리얼 엔진을 실행하고 새 프로젝트를 생성합니다.

카테고리는 게임으로 하고, 다른 코드 없이 완전히 비어있는 기본 템플릿을 선택하겠습니다.

그리고 프로젝트 타입을 C++로 변경한 다음 프로젝트를 생성합니다.

프로젝트가 생성되면 지난 영상인 언리얼 프로그래밍 입문 1편에서 배운 것처럼 Actor 클래스를 상속받아서 C++ 클래스를 하나 생성합니다.

클래스의 이름은 언리얼 엔진이 기본으로 추천해주는 MyActor를 그대로 쓰도록 하겠습니다.

변수 선언

클래스 생성이 끝나고 나면 헤더 파일로 가서 몇 가지 변수를 선언해주겠습니다.

int32 타입으로 TotalDamage를 선언하고 UPROPERTY 매크로를 붙여준 뒤 지정자로 EditAnywhere, BlueprintReadWrite, Category=”Damage”를 넣어줍니다.

그리고 float 타입으로 DamageTimeInSeconds와 DamagePerSecond 변수를 선언하고 UPROPERTY 매크로 붙여주되 DamageTimeInSeconds에는 TotalDamage 변수와 같은 지정자를 넣어주고 DamagePerSecond에는 지정자로 BlueprintReadOnly, VisibleAnywhere, Transient, Category="Damage”를 넣어줍니다.

그리고 생성자 함수로 가서 간단하게 변수들의 기본값을 지정해줍니다.

하지만 이중에서 DamagePerSecond 프로퍼티는 기본값을 넣지 않고 그냥 놔두겠습니다.

함수 선언 및 구현

그 다음 클래스에 함수를 만들기 위해서는 두 단계의 과정을 거쳐야 합니다.

바로 함수 선언과 구현입니다.

자세하게 설명해보자면 우선 헤더 파일에서 이 클래스에 새로 생성할 함수의 원래 형태, 즉 원형을 적어서 알려야 합니다.

이것을 함수의 선언이라고 합니다.

다시 말해서 함수 원형 선언은 이 클래스에 이런 함수가 있음을 알리는 것으로 함수의 반환형, 이름, 매개변수까지만 적고 세미콜론을 찍어주면 됩니다.

그리고 소스 파일로 가서 함수를 구현해야 합니다.

함수를 구현할 때는 반환형과 이 함수를 소유하고 있는 클래스의 이름을 적은 뒤 콜론을 두 개 찍어주고 함수 이름을 적습니다.

그리고 매개변수를 적은 뒤 중괄호를 열어서 함수의 몸체를 만들고 이 몸체 안에 실제 함수가 동작할 코드를 작성해야 합니다.

함수 만들어보기

그럼 이제 간단한 함수를 만들어 봅시다.

우선 앞에서 변수들에 기본 값을 넣어주면서 DamagePerSecond는 제외한 것을 기억할 겁니다.

TotalDamage 변수와 DamageTimeInSeconds 변수를 이용해서 이 DamagePerSecond 변수의 값을 바꿔주는 함수를 만들어 보겠습니다.

헤더 파일에서 CalculateDPS라는 이름으로 함수의 원형을 선언합니다.

함수의 원형을 완성하고 나면 소스 파일로 이동해서 함수를 실제로 구현해야 합니다.

소스 파일에서는 앞에서 한 번 이야기한 것처럼 반환형, 클래스 이름, 함수 이름, 괄호와 매개변수를 입력하고 중괄호로 함수의 몸체를 만들어주면 됩니다.

함수의 내용에는 TotalDamage를 DamageTimeInSeconds로 나눠서 DamagePerSecond 변수에 넣어주도록 코드를 작성합니다.

함수 호출하기

이렇게 함수를 작성하고 나면 이 함수가 특정한 시점에 자동으로 호출되게 해주겠습니다.

그 시점으로 알맞은 것은 TotalDamage나 DamageTimeInSeconds와 같은 변수가 초기화되거나 변경될 때 일 겁니다.

언리얼 엔진에서 오브젝트의 변수가 초기화될 때 호출되는 함수는 PostInitProperties이고 변수가 수정될 때 호출되는 함수는 PostEditChangeProperty입니다.

AMyActor의 헤더 파일로 가서 두 함수의 원형을 작성합니다.

이 두 함수는 AMyActor의 부모 클래스인 AActor에서 상속받는 함수이기 때문에 부모 클래스의 함수를 자식 클래스인 AMyActor에서 덮어쓴다는 의미로 virtual 키워드와 override 키워드를 사용해줘야 합니다.

이렇게 두 함수의 원형을 선언하고 나면 다시 소스 파일로 가서 함수를 구현해야 하는데 아까 전의 CalculateDPS 함수처럼 직접 작성할 수도 있지만, 초록색 밑줄이 그어진 함수 이름에 커서를 두고 [Ctrl + .] 단축키를 누르고 [PostInitProperties에서 'AMyActor.cpp' 정의 만들기]를 선택하면 빈 함수의 구현을 스크립트 에디터가 자동으로 해줍니다.

PostInitProperties 함수의 내용은 간단하게 Super::PostInitProperties 함수를 호출한 다음에 CalculateDPS 함수를 호출하게 만들어 줍니다.

참고로 언리얼 C++에서 Super 키워드는 클래스가 상속받은 부모 클래스에 있는 원본 프로퍼티나 함수를 가져오는데 사용되는 키워드입니다.

AMyActor에 override로 선언한 PostInitProperties 함수는 부모 클래스인 AActor 클래스의 PostInitProperties를 덮어씌워서 만든 것이기 때문에 이렇게 AMyActor에서 만든 PostInitProperties 함수에서 부모 클래스의 PostInitProperties를 다시 호출해주지 않으면 부모 클래스의 PostInitProperties에서 실제로 처리하는 작업이 실행되지 않아서 문제가 발생할 수도 있습니다.

그래서 이렇게 부모 클래스의 함수를 덮어씌워서 만드는 경우에는 Super 키워드를 이용해서 부모 클래스의 원본 함수를 한 번 실행시켜주는 것이 좋습니다.

PostInitProperties 함수를 모두 작성하고 나면 PostEditChangeProperty 함수도 역시 똑같이 작성해줍니다.

다만 PostEditChangeProperty 함수에서는 원본 PostEditChangeProperty 함수보다 CalculateDPS 함수를 먼저 호출해주도록 작성하겠습니다.

코드를 모두 작성하면 변경사항을 저장하고 에디터로 돌아가서 [Ctrl + Alt + F11] 단축키를 이용해 수정사항을 컴파일 해줍니다.

코드가 컴파일되고 나서 MyActor 클래스를 레벨에 배치해보면 디테일 패널에서 DamagePerSecond의 값이 계산되어 표시되는 것을 볼 수 있습니다.

그리고 TotalDamage 프로퍼티와 DamageTimeInSeconds 프로퍼티의 값을 변경하면 DamagePerSecond의 값도 곧바로 변경되는 것을 볼 수 있습니다.

아웃트로

이번 영상에서는 함수가 무엇인지 배우고 언리얼 프로그래밍에서 함수를 만들고 사용하는 방법을 알아보았습니다.

이 강좌는 시청자 여러분들의 시청과 후원으로 제작되었습니다.

이상 베르의 게임 개발 유튜브였습니다. 감사합니다.

 

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

언리얼 C++ 클래스에서 함수를 만드는 방법과 UFUNCTION 매크로에 대해서 알아봅시다.

 

타임라인

0:00 개요

0:10 함수란?

2:18 새 프로젝트 생성

2:26 C++ 클래스 생성과 프로퍼티 선언

3:17 C++ 함수 만들기

7:19 C++ 함수 블루프린트에서 호출하기

10:16 블루프린트에서 구현한 기능을 C++에서 호출하기 1

11:58 블루프린트에서 구현한 기능을 C++에서 호출하기 2

13:55 블루프린트에서 구현한 기능을 C++에서 호출하기 3

14:53 마무리

 

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

Tutorial (8) 

스크립트 작업 기초

 

작성 기준 버전 :: 2019.2

 

[본 튜토리얼의 내용을 유튜브 영상을 통해서 확인하실 수도 있습니다]

 

이번 섹션에서는 스크립트 작업으로 기초적인 커스텀 컴포넌트를 만드는 법을 배워보자.

 

본격적인 섹션 진행에 앞서 게임 오브젝트와 컴포넌트에 관련된 지식이 필요하다면 이 포스트를 참고해보자.

 

또한 이번 섹션을 진행하기 위해서는 C# 프로그래밍에 대한 기초적인 지식을 필요로 한다.

 

커스텀 컴포넌트 생성

 

[그림 1]

 

우선 커스텀 컴포넌트를 만들기 위해서 C# 스크립트를 하나 생성해보자. 프로젝트 뷰에 우클릭하여 [Create > C# Script] 항목을 선택한다.

 

 

그렇게하면 NewBehaviourScript라는 이름으로 C# 스크립트 파일이 하나 생성된다.

 

 

바로 엔터 키를 누르지 말고 파일의 이름을 ScriptingTest로 변경하고 엔터 키를 누르도록 하자. C# 스크립트 파일은 제일 처음 이름이 정해질 때, 스크립트 파일 내부의 클래스 이름이 정해지며, 스크립트 파일의 이름과 클래스의 이름이 일치하는 것을 권장하기 때문에 클래스의 이름을 처음에 제대로 정하는 것이 나중에 수정하는 것보다 좋다. 특히 나중에 파일의 이름을 바꾸면 내부의 클래스의 이름도 수동으로 바꿔야하므로 굉장히 번거롭다.

 

 

그리고 생성된 스크립트 파일을 더블클릭하면 비주얼 스튜디오가 열립니다.

 

모노비헤이비어 클래스 상속

 

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

public class ScriptingTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

 

최초로 생성된 기본 코드는 위와 같다. 먼저 생성된 ScriptingTest 클래스가 모노비헤이비어(MonoBehaviour) 클래스를 상속받고 있는 것을 볼 수 있다. 이 유니티로 게임을 제작할 때 사용되는 C# 클래스는 이 모노비헤이비어를 상속받는 클래스과 상속받지 않는 클래스로 크게 나누어진다.

 

 

모노비헤이비어 상속 여부에 따른 차이는, 모노비헤이비어를 상속받지 않은 클래스는 게임 오브젝트에 컴포넌트로써 부착되지 못한다는 것에 있다. 때문에 컴포넌트로써 게임 오브젝트에 부착되어서 씬 내부에 존재해야하는 클래스는 모노비헤이비어를 상속받는게 필수이고, 씬에 컴포넌트로 배치되지 않고 코드 내부에서 개념적으로만 존재할 클래스는 모노비헤이비어를 상속받지 않아야 한다.

 

모노비헤이비어의 라이프 사이클

 

 

 

 

모노비헤이비어를 상속받아서 게임 오브젝트에 부착되어 동작하는 스크립트를 잘 활용하려면 모노비헤이비어의 라이프 사이클에 대해서 잘 알아두는 것이 좋다. 모노비헤이비어를 상속받는 컴포넌트는 생성되어 게임 오브젝트에 부착되는 순간부터 위의 이미지와 같은 과정을 거친다.

 

그리고 위의 모노비헤이비어 상속 파트에서 본 코드 블럭을 보면 Start() 함수와 Update() 함수가 구현되어 있는 것을 볼 수 있다. 이와 같이 거치는 과정의 이름으로 함수를 만들어두면 해당 과정을 거칠 때, 그 함수가 실행되는 구조이다.

 

그럼 각 과정이 언제 호출되는지 어떻게 구현하면 되는지에 대해서 하나씩 알아보자.

 

Awake

 

private void Awake()
{
    Debug.Log("Awake");   
}

 

Awake 과정은 스크립트 인스턴스가 로딩될 때 단 한 번 호출되는 함수이다. 컴포넌트에 대한 초기화가 필요한 경우에 사용된다. 참고로 모노비헤이비어를 상속받는 클래스는 생성자 대신에 Awake() 함수를 구현해서 사용해야 한다.

 

OnEnable

 

private void OnEnable()
{
    Debug.Log("OnEnable");   
}

 

OnEnable 과정은 모노비헤이비어를 상속받은 컴포넌트가 부착된 게임 오브젝트가 활성화될 때마다 호출되는 함수이다.

 

 

에디터의 씬에서 게임 오브젝트를 선택하면 인스펙터 뷰에서 선택한 게임 오브젝트에 대한 정보를 볼 수 있는데, 이 중에 게임 오브젝트 이름 앞에 체크박스가 있다. 이 체크박스를 클릭해보면 체크박스 상태에 따라서 게임 오브젝트가 활성화되었다 비활성화되었다하는 것을 볼 수 있다. 이렇게 게임 오브젝트가 활성화될 때마다 OnEnable() 콜백 함수가 호출되는 것이다. 참고로 게임 오브젝트가 비활성화된 상태에서는 해당 게임 오브젝트에 부착된 모든 컴포넌트가 동작을 멈춘다.

 

Start

 

private void Start()
{
    Debug.Log("Start");   
}

 

Start 과정은 Update 과정이 실행되기 직전에 단 한 번 호출된다. 모노비헤이비어의 라이프 사이클 중에 단 한 번 호출된다는 점이 Awake와 같지만 Start는 게임 오브젝트가 활성화된 경우에만 호출된다는 차이점이 있다.

 

Update

 

private int i = 5;
private void Update()
{
    i--;
    if(i >= 0)
    {
        Debug.Log("Update :: " + i);
    }
    else
    {
        Destroy(gameObject);
    }
}

 

Update 과정은 모노비헤이비어가 활성화된 상태에서 매 프레임마다 호출된다. 대부분의 게임의 동작 처리는 이 Update() 함수에서 수행되는 경우가 많다. 다만, 이 Update() 함수는 프레임마다 호출되기 때문에 프레임 드랍이 발생하는 경우에는 호출 횟수가 줄어든다. 프레임과 상관 없이 코드가 작동하기 원한다면 FixedUpdate() 함수를 사용해야 한다.

 

Update() 함수는 OnEnable() 함수를 설명하면서 이야기했듯이 게임 오브젝트가 비활성화된 상태에서는 동작하지 않는다.

 

LateUpdate

 

private void LateUpdate()
{
    Debug.Log("LateUpdate");   
}

 

LateUpdate는 단어 그대로 늦은 업데이트로 Update() 함수가 실행된 직후에 실행되는 업데이트 함수이다. Update() 함수에서 게임 로직을 처리한 직후에 처리하고 싶은 로직이 있다면 이곳에서 처리하면 된다.

 

FixedUpdate

 

private void FixedUpdate()
{
    Debug.Log("FixedUpdate");   
}

 

FixedUpdate는 매 프레임마다 호출되는 Update와 달리 지정된 시간마다 호출되는 업데이트 함수이다. 때문에 프레임이 들쭉날쭉한 상황에서도 일정한 시간마다 호출된다. 주로 호출 시간에 따라서 결과가 달라지면 안되는 물리적인 계산에 사용된다.

 

OnDisable

 

private void OnDisable()
{
    Debug.Log("OnDisable");   
}

 

OnDisable 과정은 모노비헤이비어가 비활성화되는 경우에 사용된다. 그리고 오브젝트가 삭제되는 경우에도 호출된다.

 

OnDestroy

 

private void OnDestroy()
{
    Debug.Log("OnDisable");   
}

 

OnDestory 과정은 모노비헤이비어가 제거될 때 호출된다.

 

 

위의 코드를 모두 ScriptingTest 클래스에 작성하고 플레이시켜보면 위의 이미지와 같은 순서로 로그가 발생하는 것을 볼 수 있다.

 

 

 

 

 

변수

 

우리가 게임을 만들면서 사용될 값, 공격력, 방어력, 공격속도, 이동속도, HP 등의 데이터나 정보를 담아둘 것을 변수라고 부른다. 유니티 엔진에서 스크립트를 작성하는 C#은 담고자하는 값의 종류에 따라서 변수의 종류가 나누어진다. 그럼 이 변수의 종류에 대해서 알아보도록 하자.

 

정수(int)

 

int i = 10;

 

첫 번째 변수 유형은 정수형이다. 정수형 변수 int는 0과 양의 정수, 음의 정수를 담기 위한 변수로, -2,147,483,648부터 2,147,483,647까지 담을 수 있다. 

 

남아있는 라이프의 갯수, 현재 생산된 인구 수 등의 정수로 딱 떨어지는 곳에서 사용될 수 있다.

 

실수(float)

 

float f = 3.14159f;

 

두 번째 변수 유형은 실수형이다. 실수형 변수 float은 소수를 담기 위한 변수로 일반적으로 소수점 다섯 번째자리 0.00001까지 정확도를 표현할 수 있다.

 

주로 1.2초 같은 시간이나 20.25%와 같은 확률 등을 표현할 때, 주로 사용된다.

 

문자열(string)

 

string str = "hello";

 

세 번째 변수 유형은 문자열입니다. 문자열 변수 string은 말그대로 문자들의 집합인 문자열을 담는 변수이다.

 

주로 캐릭터나 아이템의 이름, 설명, 게임에서 사용되는 대사 자막 등의 데이터를 담는데 사용된다.

 

논리값(bool)

 

bool isMoveable = true;

 

네 번째 변수 유형은 논리값이다. 논리값 변수 bool은 참(true) 혹은 거짓(false)의 상태를 가지는 변수로 주로 조건을 처리할 때 사용된다.

 

이 외에도 각 종류의 변수를 묶음 단위로 취급하는 배열 등이 있고, 일반 C# 클래스나 모노비헤이비어를 상속받은 클래스 역시 변수가 될 수 있다.

 

 

함수

 

함수는 게임 기능을 수행하기 위한 작업을 하나의 블록으로 묶은 것을 의미한다. 모노비헤이비어의 라이프 사이클에 대해서 설명하면서 본 Awake, OnEnable, Start, Update, OnDisable, OnDestroy 역시 함수이다. 일반적으로 함수는 하나의 기능 단위로 작성되는 경우가 많다.

 

int attackDamage = 10;

public bool Attack(Monster monster)
{
    monster.hp -= attackDamage;
    return monster.hp <= 0;
}

 

위의 예시 코드는 몬스터를 공격해서 체력을 공격력만큼 깎고, 몬스터의 체력이 0 이하가 되면 true를 반환하도록 코드가 작성되어 있다. 이렇게 하면 Attack() 함수를 호출하여 몬스터의 체력을 깎고 공격한 몬스터가 죽었는가에 따라서 여러가지 처리를 할 수 있게 된다.

 

 

공개 수준 결정

 

개발자는 코드를 작성하면서 변수나 함수에 대해서 공개 수준을 결정할 수 있다.

 

public int i;

protected float f;

private string str;
 
public void Function1() { }
 
protected void Function2() { }
 
private void Function3() { }

 

변수와 함수의 공개 수준은 앞에 표시된 public, protected, private 키워드를 통해서 결정된다. 이러한 공개 수준은 일반적인 C# 프로그래밍에서와 같이 public은 클래스 외부에서 접근이 가능하고 protected는 해당 클래스를 상속받은 클래스에서만 접근이 가능하다. 그리고 private는 해당 클래스의 내부에서만 사용 가능하다.

 

public class ScriptingTest : MonoBehaviour
{
    public int attackDamage = 10;
}

 

그리고 유니티 엔진만의 특징으로는 모노비헤이비어 클래스를 상속받은 클래스에서 public으로 설정된 변수는 에디터의 인스펙터 뷰에서 바로 보고 수정할 수 있다는 장점이 있다.

 

 

이러한 방식의 장점은 매번 게임의 수치가 바뀔 때마다 프로그래머가 코드를 수정하고 새로 빌드 과정을 거칠 필요없이 게임 디자이너가 에디터에서 즉석으로 값을 바꿀 수 있다는 것이다.

 

하지만 인스펙터 뷰에서 보이게 하고자 하는 모든 변수를 public으로 설정하면 코드 내부에서 어떤 클래스에서던지 접근이 가능해진다. 이런 경우를 방지하고자 protected나 private로 설정한 채로 인스펙터 뷰에 공개하고 싶을 수도 있다.

 

[SerializeField]
private int attackDamage = 10;

 

그럴 때는 SerializeField라는 어트리뷰트를 해당 변수 앞에 명시해주면 private나 protected로 둔 상태로도 인스펙터 뷰에 변수를 공개할 수 있다.

 

[HideInInspector]
public int attackDamage = 10;

 

그와 반대로 변수를 public으로 둔 상태로 인스펙터 뷰에 공개하고 싶지 않다면 HideInInspector 어트리뷰트를 붙여주면 된다.

 

모노비헤이비어 클래스를 상속받아서 만들어진 컴포넌트는 클래스를 기반으로 변수를 어떻게 구성하고 함수를 어떻게 구현하느냐에 따라서 그 컴포넌트의 기능과 역할이 정해진다. 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형
  1. 료용 2020.01.26 23:22 신고

    베르님 글보고 맨날 이상한닉으로 질문하다가 결국 티스토리 가입했습니다 ㅋㅋㅋ

    기본적인설명을 심플하게 잘쓰셔놧네요

    • wergia 2020.01.27 03:48 신고

      고정으로 들어오시게되었군요!
      감사합니다 료옹님! 언제나 제가 다시봐도 이해하기 쉽게 쓰려고 노력중입니다 ㅎㅎ
      근데 다시 옛날 글보니까 오타도 많고 그렇더라구요 나중에 수정을 좀 해야겠어요

    • wergia 2020.01.27 03:49 신고

      아 료용님이구나 ㅎㅎ
      새벽에 잠이 안와서 반쯤 깬 상태로 보는거라 닉네임을 잘못봤네요

  2. 료용 2020.02.03 02:18 신고

    좋은글들 잘보고있습니다.

    동방프로젝트게임을 친구가하는걸 보고있는데 전에했던플레이를 다시 볼수있는 기능이있던데

    베르님 혹시 리플레이영상 만드실수있으신가요? 저는 도저히 감도안오더라고요 어떻게시작해야될지도...

    • wergia 2020.02.03 03:50 신고

      리플레이 기능을 만드는걸 이야기 하시는 건가요?
      이 부분은 나중에 포스트로 한번 써봐야겠네요.
      다만, 우선 먼저 말씀 드리자면 리플레이 기능은 여러방법으로 만들 수 있는데, 첫
      번째는 게임 내의 오브젝트의 위치와 컴포넌트가 가지는 값을 모두 기록하고 있다가 그 기록을 따라 생성된 리플레이 오브젝트들이 따라가게 만드는 방법이 있고,
      두번째 방법은 사용자의 입력을 기록하여, 그 기록대로 움직이게 하는 방법,
      세번째 방법은 카메라로 바라보는 장면을 렌더텍스쳐로 모조리 따서 영상으로 만들어서 실시간으로 저장하는 방법 정도가 있겠네요.
      가장 최적의 방법은 두번째 방안이 되겠습니다. 자세한 설명은 포스트에서 뵙죠.

  3. 료용 2020.02.03 23:00 신고

    답변고맙습니다. 근데 탄까지 쏘는 그런게임인데 그렇다면 카메라자체를 저장하는 세번째방법이 맞지않나요? 두번째방법에서 적이쏘는 탄막같은것을 담아낼수있나요??

    • wergia 2020.02.04 09:22 신고

      간단하게 말해서 사용자의 입력을 기록한다고 했는데, 음.. 좀 더 정확하게 말하면 이벤트를 기록하는 겁니다. 사용자나 적의 비행체가 움직이는 이벤트, 탄을 발사하는 이벤트, 데미지를 입는 이벤트 등을 기록하는 겁니다.
      탄막 같은것도 발사되는 이벤트랑 탄에서 또 다른 탄이 생성되는 이벤트를 기록하면 됩니다.
      카메라 자체를 저장하는 건 사실 그렇게 추천하지는 않습니다. 게임 프레임을 로직이랑 실제 렌더링 돌리는데 30프레임 60프레임 방어하기도 바쁜데 실시간으로 렌더링하는걸 영상으로 저장하는 것도 생각보다 무거운 작업입니다.

  4. 료용 2020.02.07 17:09 신고

    그렇게해서 결국 했던걸 다시 행동하게 한다는거죠? 근데 왜이렇게 어려워보이죠 ㅋㅋㅋㅋㅋㅋㅋㅋ

    • wergia 2020.02.08 12:55 신고

      처음에 설계를 잘하고 들어가야되는 부분이기는 합니다. 애초에 리플레이를 생각안하고 짠 코드에 리플레이 기능을 추가하려면 크게 고생하게 되더라구요. 난이도로 치자면 카메라를 그대로 녹화하는게 제일 쉽기는 합니다. 다만, 게임 성능 문제도 있고 그냥 영상으로 녹화된 리플레이는 배틀그라운드처럼 막 게임 속을 자유롭게 이동하면서 리플레이를 감상하는게 불가능해지죠. 근데 고사양의 게임이 아니고, 게임 리플레이 속을 자유롭게 돌아다닐 필요가 없다면 카메라를 그대로 녹화하는 방법도 나쁘지는 않습니다.

static 

정적 변수와 정적 함수 그리고 정적 클래스


static 키워드는 변수나 함수, 클래스에 정적 속성을 부여하는 것으로 클래스로부터 객체를 생성하지 않고 변수나 함수를 호출할 수 있도록 해주는 것이다.



정적 변수


public class StaticTestClass

{

    public static int score;

}


정적 변수를 선언하기 위해서는 위의 예시 코드와 같이 static 키워드를 붙여서 변수를 정의하면 된다. 이렇게 선언한 정적 변수는 클래스로부터 객체를 생성하지 않아도 [클래스명.변수이름]의 형식으로 곧바로 사용할 수 있게 된다. 


public class MainClass

{

    public void Main()

    {

        StaticTestClass.score = 10;

    }

}


클래스의 일반 멤버 변수는 클래스의 객체가 생성될 때, 각 객체마다 따로 생기지만, 정적 변수는 해당 클래스가 처음으로 사용되는 때에 한 번만 초기화되어 계속 동일한 메모리를 사용하게 된다.

 

 

도식으로 보면 위의 그림과 같다. 정적 변수를 포함한 클래스 A의 객체를 두 개를 생성하여 각 이름을 object1, object2라고 했을 때, 각 인스턴스에는 정적 변수가 포함되지 않으며, 일반 멤버 변수만 포함된다. 클래스 A의 정적 변수는 클래스 A가 처음 사용되는 시점에 별도의 메모리 공간에 할당된다.


 

생성된 객체에 정적 변수가 포함되지 않는 것은 실제로 객체를 생성해서 멤버 변수를 찾았을 때, 목록에 나오지 않는 것을 보면 확인할 수 있다.



정적 함수


public class StaticTestClass

{

    public static int score;


    public int memberInt;


    public static void StaticFunction()

    {

        score = 10 // static 변수는 호출할 수 있다.

        memberInt = 10// static 함수 내에서 멤버변수는 호출할 수 없다.

    }

}


public class MainClass

{

    public void Main()

    {

        StaticTestClass.score = 10;

        StaticTestClass.StaticFunction();

    }

}


함수를 선언할 때, static 키워드를 붙여서 함수를 정의하면 정적 함수를 만들 수 있다. 이 정적 함수 역시 [클래스명.함수이름]의 형식으로 객체를 생성하지 않고 곧바로 호출할 수 있다.


단, 정적 함수는 객체가 생성되기 전에 호출이 가능하기 때문에, 정적 함수 내에서는 정적 변수가 아닌 일반 멤버 변수를 호출할 수 없다.



정적 클래스


public static class StaticTestClass

{

    public static int score;


    static StaticTestClass()

    {

        score = 10;

    }


    public static void StaticFunction()

    {

        score = 20;

    }

}


정적 클래스는 모든 멤버가 정적 변수 혹은 정적 함수로 이루어진 것으로 객체를 생성할 수 없는 클래스이다. 모든 정적 멤버 변수 및 정적 멤버 함수는 [클래스명.변수이름] 혹은 [클래스명.함수이름]으로 호출된다.


정적 클래스는 정적 생성자를 가질 수 있는데 이 정적 생성자는 public, protected, private 등의 액세스 한정자를 사용할 수 없으며, 매개변수 역시 가질 수 없다.

반응형

+ Recent posts