MVC를 사용한 Unity: 게임 개발 수준을 높이는 방법

게시 됨: 2022-03-11

처음 프로그래머는 일반적으로 고전적인 Hello World 프로그램으로 거래를 배우기 시작합니다. 거기에서 더 크고 더 큰 임무가 따를 수밖에 없습니다. 각각의 새로운 도전은 다음과 같은 중요한 교훈을 가져다줍니다.

프로젝트가 클수록 스파게티도 커집니다.

곧 크든 작든 팀에서 원하는 대로 무모하게 할 수 없다는 것을 쉽게 알 수 있습니다. 코드는 유지 관리되어야 하며 오랫동안 지속될 수 있습니다. 당신이 일했던 회사는 당신의 연락처 정보를 조회하고 코드베이스를 수정하거나 개선하기를 원할 때마다 당신에게 물어볼 수 없습니다(당신도 원하지 않습니다).

이것이 소프트웨어 디자인 패턴이 존재하는 이유입니다. 그들은 소프트웨어 프로젝트의 전체 구조를 지시하는 간단한 규칙을 부과합니다. 그들은 한 명 이상의 프로그래머가 대규모 프로젝트의 핵심 부분을 분리하고 표준화된 방식으로 구성하는 데 도움을 주어 코드베이스의 일부 익숙하지 않은 부분이 발생할 때 혼란을 제거합니다.

모든 사람이 이 규칙을 따를 때 레거시 코드를 더 잘 유지 관리하고 탐색하고 새 코드를 더 신속하게 추가할 수 있습니다. 개발 방법론을 계획하는 데 소요되는 시간이 줄어듭니다. 문제는 한 가지 맛으로 오지 않기 때문에 은색 총알 디자인 패턴이 없습니다. 각 패턴의 장단점을 신중하게 고려하고 당면한 과제에 가장 적합한 것을 찾아야 합니다.

이 튜토리얼에서는 인기 있는 Unity 게임 개발 플랫폼과 게임 개발을 위한 MVC(Model-View-Controller) 패턴에 대한 제 경험을 설명하겠습니다. 7년 간의 개발 기간 동안 게임 개발 스파게티에 대한 공평한 몫과 씨름하면서 이 디자인 패턴을 사용하여 뛰어난 코드 구조와 개발 속도를 달성했습니다.

Unity의 기본 아키텍처인 Entity-Component 패턴에 대해 설명하는 것으로 시작하겠습니다. 그런 다음 MVC가 그 위에 어떻게 맞는지 설명하고 약간의 모의 프로젝트를 예로 들어 설명하겠습니다.

동기 부여

소프트웨어 문헌에서 많은 수의 디자인 패턴을 찾을 수 있습니다. 규칙 집합이 있더라도 개발자는 일반적으로 특정 문제에 패턴을 더 잘 적용하기 위해 약간의 규칙 조정을 수행합니다.

이 "프로그래밍의 자유"는 우리가 아직 소프트웨어를 설계하는 단일하고 결정적인 방법을 찾지 못했다는 증거입니다. 따라서 이 기사는 문제에 대한 궁극적인 해결책이 아니라 두 가지 잘 알려진 패턴인 Entity-Component 및 Model-View-Controller의 이점과 가능성을 보여주기 위한 것입니다.

엔티티 컴포넌트 패턴

Entity-Component(EC)는 애플리케이션을 구성하는 요소의 계층 구조(Entities)를 먼저 정의하고 나중에 각각에 포함될 기능과 데이터(Component)를 정의하는 디자인 패턴입니다. 좀 더 "프로그래머" 용어로 엔티티는 0개 이상의 구성요소 배열이 있는 객체일 수 있습니다. Entity를 다음과 같이 묘사해 보겠습니다.

 some-entity [component0, component1, ...]

다음은 EC 트리의 간단한 예입니다.

 - app [Application] - game [Game] - player [KeyboardInput, Renderer] - enemies - spider [SpiderAI, Renderer] - ogre [OgreAI, Renderer] - ui [UI] - hud [HUD, MouseInput, Renderer] - pause-menu [PauseMenu, MouseInput, Renderer] - victory-modal [VictoryModal, MouseInput, Renderer] - defeat-modal [DefeatModal, MouseInput, Renderer]

EC는 다중 상속 문제를 완화하기 위한 좋은 패턴입니다. 여기서 복잡한 클래스 구조는 동일한 기본 클래스 A를 가진 두 개의 클래스 B와 C를 상속하는 클래스 D가 충돌을 일으킬 수 있는 다이아몬드 문제와 같은 문제를 일으킬 수 있습니다. B와 C는 A의 기능을 다르게 수정합니다.

이미지: 다이아몬드 문제

이러한 종류의 문제는 상속이 자주 광범위하게 사용되는 게임 개발에서 일반적일 수 있습니다.

기능과 데이터 핸들러를 더 작은 구성 요소로 나누면 다중 상속에 의존하지 않고 다른 엔터티에 연결하고 재사용할 수 있습니다. ).

엔티티 구성 요소가 부족한 경우

OOP보다 한 단계 높은 EC는 조각 모음을 수행하고 코드 아키텍처를 더 잘 구성하는 데 도움이 됩니다. 그러나 대규모 프로젝트에서 우리는 여전히 "너무 자유롭고" "피처 오션"에 빠져 있어 올바른 엔티티와 컴포넌트를 찾거나 상호 작용 방식을 파악하는 데 어려움을 겪을 수 있습니다. 주어진 작업에 대해 엔터티와 구성 요소를 조합하는 방법은 무한합니다.

이미지: EC 기능 바다

혼란을 피하는 한 가지 방법은 Entity-Component 위에 몇 가지 추가 지침을 적용하는 것입니다. 예를 들어 소프트웨어에 대해 생각하는 한 가지 방법은 소프트웨어를 세 가지 범주로 나누는 것입니다.

  • 일부는 원시 데이터를 처리하여 생성, 읽기, 업데이트, 삭제 또는 검색할 수 있습니다(즉, CRUD 개념).
  • 다른 요소는 다른 요소가 상호 작용할 수 있도록 인터페이스를 구현하여 해당 범위와 관련된 이벤트를 감지하고 발생 시 알림을 트리거합니다.
  • 마지막으로, 일부 요소는 이러한 알림을 수신하고, 비즈니스 논리 결정을 내리고, 데이터를 조작하는 방법을 결정하는 역할을 합니다.

다행히도 우리는 이미 정확히 이와 같은 방식으로 동작하는 패턴을 가지고 있습니다.

모델-뷰-컨트롤러(MVC) 패턴

MVC(Model-View-Controller 패턴)는 소프트웨어를 모델(데이터 CRUD), 보기(인터페이스/탐지) 및 컨트롤러(결정/작업)의 세 가지 주요 구성 요소로 나눕니다. MVC는 ECS 또는 OOP 위에서도 구현될 수 있을 만큼 충분히 유연합니다.

게임 및 UI 개발에는 사용자의 입력 또는 기타 트리거 조건을 기다리고, 해당 이벤트에 대한 알림을 적절한 위치에 보내고, 응답으로 수행할 작업을 결정하고, 그에 따라 데이터를 업데이트하는 일반적인 워크플로가 있습니다. 이러한 작업은 이러한 응용 프로그램과 MVC의 호환성을 명확하게 보여줍니다.

이 방법론은 소프트웨어 계획에 도움이 될 또 다른 추상화 계층을 도입하고 새로운 프로그래머가 더 큰 코드베이스에서도 탐색할 수 있도록 합니다. 사고 과정을 데이터, 인터페이스, 의사결정으로 나누어 개발자는 기능을 추가하거나 수정하기 위해 검색해야 하는 소스 파일의 수를 줄일 수 있습니다.

유니티와 EC

먼저 Unity가 우리에게 무엇을 제공하는지 자세히 살펴보겠습니다.

Unity는 EC 기반 개발 플랫폼으로, 모든 Entities는 GameObject 의 인스턴스이며 이를 '표시', '이동 가능', '상호작용' 등으로 만드는 기능은 Component 를 확장하는 클래스에서 제공됩니다.

Unity 에디터의 Hierarchy PanelInspector Panel 은 애플리케이션을 어셈블하고, 컴포넌트를 연결하고, 초기 상태를 구성하고, 평소보다 훨씬 적은 소스 코드로 게임을 부트스트랩하는 강력한 방법을 제공합니다.

스크린샷: 계층 패널
오른쪽에 4개의 GameObject가 있는 계층 패널

스크린샷: 검사자 패널
GameObject의 구성 요소가 있는 인스펙터 패널

그러나 우리가 논의한 것처럼 "너무 많은 기능" 문제가 발생하고 기능이 도처에 흩어져 있는 거대한 계층 구조에 놓이게 되어 개발자의 삶을 훨씬 더 힘들게 만들 수 있습니다.

MVC 방식으로 생각하면 대신 기능에 따라 항목을 나누고 아래 예제와 같이 애플리케이션을 구조화하여 시작할 수 있습니다.

스크린샷: Unity MVC 예제 구조

게임 개발 환경에 MVC 적용하기

이제 일반 MVC 패턴에 두 가지 작은 수정 사항을 도입하여 MVC를 사용하여 Unity 프로젝트를 빌드하는 동안 마주한 고유한 상황에 적응하는 데 도움이 됩니다.

  1. MVC 클래스 참조는 코드 전체에 쉽게 흩어집니다. - Unity 내에서 개발자는 일반적으로 인스턴스를 드래그 앤 드롭하여 액세스할 수 있도록 하거나 GetComponent( ... ) 와 같은 성가신 find 문을 통해 접근해야 합니다. - Unity가 충돌하거나 일부 버그로 인해 드래그된 참조가 모두 사라지면 참조 손실 지옥이 발생합니다. - 이를 통해 애플리케이션 의 모든 인스턴스에 도달하고 복구할 수 있는 단일 루트 참조 개체가 있어야 합니다.
  2. 일부 요소는 재사용 가능성이 높고 Model, View 또는 Controller의 세 가지 주요 범주 중 하나에 자연스럽게 속하지 않는 일반 기능을 캡슐화합니다. 나는 단순히 Components 라고 부르는 것을 좋아합니다. 그것들은 또한 Entity-Component 의미에서 "구성 요소"이지만 MVC 프레임워크에서 단순히 도우미 역할을 합니다. - 예를 들어, 주어진 각속도로만 회전하고 아무것도 알리거나 저장하거나 결정하지 않는 Rotator 구성 요소.

이 두 가지 문제를 완화하는 데 도움이 되도록 AMVCC 또는 Application-Model-View-Controller-Component라고 하는 수정된 패턴을 생각해 냈습니다.

이미지: AMVCC 다이어그램

  • 애플리케이션 - 애플리케이션에 대한 단일 진입점 및 모든 중요한 인스턴스 및 애플리케이션 관련 데이터의 컨테이너입니다.
  • MVC - 당신은 지금쯤 이것을 알고 있을 것입니다. :)
  • 구성 요소 - 재사용할 수 있는 잘 포함된 작은 스크립트입니다.

이 두 가지 수정 사항은 내가 사용한 모든 프로젝트에 대한 요구 사항을 충족했습니다.

예: 10 바운스

간단한 예로 AMVCC 패턴의 핵심 요소를 사용할 10 Bounces 라는 작은 게임을 살펴보겠습니다.

게임 설정은 간단합니다. SphereColliderRigidbody 가 있는 Ball ("Play" 후에 떨어지기 시작함), 그라운드로 Cube 및 AMVCC를 구성하는 5개의 스크립트입니다.

계층

스크립팅하기 전에 저는 일반적으로 계층 구조에서 시작하여 클래스 및 자산의 개요를 만듭니다. 항상 이 새로운 AMVCC 스타일을 따릅니다.

스크린샷: 계층 구축

보시다시피 GameObject view 에는 모든 시각적 요소와 다른 View 스크립트가 포함된 요소가 포함되어 있습니다. 소규모 프로젝트의 경우 modelcontroller 게임 오브젝트에는 일반적으로 각각의 스크립트만 포함됩니다. 더 큰 프로젝트의 경우 더 구체적인 스크립트가 포함된 게임 오브젝트가 포함됩니다.

프로젝트를 탐색하는 사람이 액세스하려는 경우:

  • 데이터: application > model > ... 로 이동합니다.
  • 로직/워크플로: application > controller > ... 로 이동합니다.
  • 렌더링/인터페이스/탐지: application > view > ... 로 이동합니다.

모든 팀이 이 간단한 규칙을 따른다면 레거시 프로젝트가 문제가 되지 않을 것입니다.

우리가 논의한 것처럼 구성 요소 컨테이너는 더 유연하고 개발자의 여가 시간에 다른 요소에 연결할 수 있기 때문에 Component 컨테이너가 없습니다.

스크립팅

참고: 아래에 표시된 스크립트는 실제 구현의 추상 버전입니다. 자세한 구현은 독자에게 큰 도움이 되지 않을 것입니다. 그러나 더 자세히 알아보려면 Unity용 개인 MVC 프레임워크인 Unity MVC에 대한 링크를 참조하세요. 대부분의 애플리케이션에 필요한 AMVCC 구조적 프레임워크를 구현하는 핵심 클래스를 찾을 수 있습니다.

10 Bounces 에 대한 스크립트 구조를 살펴보겠습니다.

시작하기 전에 Unity의 워크플로에 익숙하지 않은 사용자를 위해 스크립트와 게임 오브젝트가 함께 작동하는 방식을 간략하게 설명하겠습니다. Unity에서 "Components"는 Entity-Component 의미에서 MonoBehaviour 클래스로 표현됩니다. 런타임 중에 존재하려면 개발자는 소스 파일을 GameObject(Entity-Component 패턴의 "Entity")에 AddComponent<YourMonobehaviour>() 명령을 사용해야 합니다. 그런 다음 스크립트가 인스턴스화되고 실행 중에 사용할 준비가 됩니다.

시작하려면 인스턴스화된 모든 게임 요소에 대한 참조를 포함하는 기본 클래스가 될 Application 클래스(AMVCC의 "A")를 정의합니다. 또한 Element 라는 도우미 기본 클래스를 만들어 Application 인스턴스와 해당 자식의 MVC 인스턴스에 액세스할 수 있습니다.

이를 염두에 두고 고유한 인스턴스를 가질 Application 클래스(AMVCC의 "A")를 정의해 보겠습니다. 그 안에는 model , viewcontroller 의 세 가지 변수가 런타임 동안 모든 MVC 인스턴스에 대한 액세스 지점을 제공합니다. 이러한 변수는 원하는 스크립트에 대한 public 참조가 있는 MonoBehaviour 여야 합니다.

그런 다음 Element 라는 도우미 기본 클래스도 만들어 Application 인스턴스에 액세스할 수 있습니다. 이 액세스를 통해 모든 MVC 클래스가 서로 연결할 수 있습니다.

두 클래스 모두 MonoBehaviour 를 확장합니다. 그것들은 GameObject "Entities"에 부착될 "Components"입니다.

 // BounceApplication.cs // Base class for all elements in this application. public class BounceElement : MonoBehaviour { // Gives access to the application and all instances. public BounceApplication app { get { return GameObject.FindObjectOfType<BounceApplication>(); }} } // 10 Bounces Entry Point. public class BounceApplication : MonoBehaviour { // Reference to the root instances of the MVC. public BounceModel model; public BounceView view; public BounceController controller; // Init things here void Start() { } }

BounceElement 에서 MVC 핵심 클래스를 만들 수 있습니다. BounceModel , BounceViewBounceController 스크립트는 일반적으로 보다 전문화된 인스턴스의 컨테이너 역할을 하지만 이것은 단순한 예이므로 View에만 중첩 구조가 있습니다. 모델과 컨트롤러는 각각에 대해 하나의 스크립트에서 수행할 수 있습니다.

 // BounceModel.cs // Contains all data related to the app. public class BounceModel : BounceElement { // Data public int bounces; public int winCondition; }
 // BounceView .cs // Contains all views related to the app. public class BounceView : BounceElement { // Reference to the ball public BallView ball; }
 // BallView.cs // Describes the Ball view and its features. public class BallView : BounceElement { // Only this is necessary. Physics is doing the rest of work. // Callback called upon collision. void OnCollisionEnter() { app.controller.OnBallGroundHit(); } }
 // BounceController.cs // Controls the app workflow. public class BounceController : BounceElement { // Handles the ball hit event public void OnBallGroundHit() { app.model.bounces++; Debug.Log(“Bounce ”+app.model.bounce); if(app.model.bounces >= app.model.winCondition) { app.view.ball.enabled = false; app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball OnGameComplete(); } } // Handles the win condition public void OnGameComplete() { Debug.Log(“Victory!!”); } }

모든 스크립트가 생성되면 첨부 및 구성을 진행할 수 있습니다.

계층 구조 레이아웃은 다음과 같아야 합니다.

 - application [BounceApplication] - model [BounceModel] - controller [BounceController] - view [BounceView] - ... - ball [BallView] - ...

BounceModel 을 예로 사용하면 Unity 에디터에서 어떻게 보이는지 확인할 수 있습니다.

스크린샷: 인스펙터의 바운스 모델
bouncesBounceModel 필드가 있는 winCondition .

모든 스크립트가 설정되고 게임이 실행되면 콘솔 패널 에서 이 출력을 얻어야 합니다.

스크린샷: 콘솔 출력

알림

위의 예에서 볼 수 있듯이 공이 지면에 닿으면 뷰에서 메서드인 app.controller.OnBallGroundHit() 가 실행됩니다. 애플리케이션의 모든 알림에 대해 그렇게 하는 것은 결코 "잘못"이 아닙니다. 그러나 내 경험상 AMVCC Application 클래스에 구현된 간단한 알림 시스템을 사용하여 더 나은 결과를 얻었습니다.

이를 구현하기 위해 BounceApplication 의 레이아웃을 다음과 같이 업데이트하겠습니다.

 // BounceApplication.cs class BounceApplication { // Iterates all Controllers and delegates the notification data // This method can easily be found because every class is “BounceElement” and has an “app” // instance. public void Notify(string p_event_path, Object p_target, params object[] p_data) { BounceController[] controller_list = GetAllControllers(); foreach(BounceController c in controller_list) { c.OnNotification(p_event_path,p_target,p_data); } } // Fetches all scene Controllers. public BounceController[] GetAllControllers() { /* ... */ } }

다음으로 모든 개발자가 실행 중에 전달할 수 있는 알림 이벤트의 이름을 추가할 새 스크립트가 필요합니다.

 // BounceNotifications.cs // This class will give static access to the events strings. class BounceNotification { static public string BallHitGround = “ball.hit.ground”; static public string GameComplete = “game.complete”; /* ... */ static public string GameStart = “game.start”; static public string SceneLoad = “scene.load”; /* ... */ }

이렇게 하면 개발자가 실행 중에 어떤 종류의 작업이 발생할 수 있는지 이해하기 위해 controller.OnSomethingComplexName 메서드에 대해 소스 코드 전체를 검색할 필요가 없기 때문에 코드 가독성이 향상되었음을 쉽게 알 수 있습니다. 하나의 파일만 확인하면 애플리케이션의 전반적인 동작을 이해할 수 있습니다.

이제 이 새로운 시스템을 처리하기 위해 BallViewBounceController 만 조정하면 됩니다.

 // BallView.cs // Describes the Ball view and its features. public class BallView : BounceElement { // Only this is necessary. Physics is doing the rest of work. // Callback called upon collision. void OnCollisionEnter() { app.Notify(BounceNotification.BallHitGround,this); } }
 // BounceController.cs // Controls the app workflow. public class BounceController : BounceElement { // Handles the ball hit event public void OnNotification(string p_event_path,Object p_target,params object[] p_data) { switch(p_event_path) { case BounceNotification.BallHitGround: app.model.bounces++; Debug.Log(“Bounce ”+app.model.bounce); if(app.model.bounces >= app.model.winCondition) { app.view.ball.enabled = false; app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball // Notify itself and other controllers possibly interested in the event app.Notify(BounceNotification.GameComplete,this); } break; case BounceNotification.GameComplete: Debug.Log(“Victory!!”); break; } } }

더 큰 프로젝트에는 많은 알림이 있습니다. 따라서 큰 스위치 케이스 구조를 피하려면 다른 컨트롤러를 만들고 다른 알림 범위를 처리하도록 하는 것이 좋습니다.

실제 세계의 AMVCC

이 예제는 AMVCC 패턴에 대한 간단한 사용 사례를 보여주었습니다. MVC의 세 가지 요소 측면에서 사고 방식을 조정하고 엔터티를 정렬된 계층 구조로 시각화하는 방법을 배우는 것은 연마해야 할 기술입니다.

더 큰 프로젝트에서 개발자는 더 복잡한 시나리오에 직면하고 무언가가 View여야 하는지 컨트롤러여야 하는지 또는 주어진 클래스를 더 작은 클래스로 더 철저히 분리해야 하는지에 대한 의구심에 직면할 것입니다.

경험의 법칙(Eduardo 작성)

"MVC 정렬을 위한 범용 가이드"는 어디에도 없습니다. 그러나 모델, 보기 또는 컨트롤러로 정의할지 여부와 주어진 클래스를 더 작은 조각으로 분할할 때를 결정하는 데 도움이 되는 몇 가지 간단한 규칙이 있습니다.

일반적으로 이것은 소프트웨어 아키텍처에 대해 생각하는 동안이나 스크립팅 중에 유기적으로 발생합니다.

클래스 정렬

모델

  • 플레이어 health 또는 총기 ammo 과 같은 애플리케이션의 핵심 데이터 및 상태를 유지합니다.
  • 유형 간 직렬화, 역직렬화 및/또는 변환.
  • 데이터 로드/저장(로컬 또는 웹).
  • 컨트롤러에게 작업 진행 상황을 알립니다.
  • 게임의 유한 상태 머신에 대한 게임 상태를 저장합니다.
  • 보기에 액세스하지 마십시오.

견해

  • 사용자에게 최신 게임 상태를 나타내기 위해 모델에서 데이터를 가져올 수 있습니다. 예를 들어 View 메서드 player.Run() 은 내부적으로 model.speed 를 사용하여 플레이어 능력을 나타낼 수 있습니다.
  • 모델을 변경해서는 안 됩니다.
  • 해당 클래스의 기능을 엄격하게 구현합니다. 예를 들어:
    • PlayerView 는 입력 감지를 구현하거나 게임 상태를 수정해서는 안 됩니다.
    • View는 인터페이스가 있고 중요한 이벤트를 알리는 블랙박스 역할을 해야 합니다.
    • 핵심 데이터(예: 속도, 건강, 생명 등)를 저장하지 않습니다.

컨트롤러

  • 핵심 데이터를 저장하지 마십시오.
  • 원하지 않는 보기에서 알림을 필터링할 수 있습니다.
  • 모델의 데이터를 업데이트하고 사용합니다.
  • Unity의 장면 워크플로를 관리합니다.

클래스 계층

이 경우 수행하는 단계가 많지 않습니다. 일반적으로 변수에 너무 많은 "접두사"가 표시되기 시작하거나 동일한 요소의 너무 많은 변형이 나타나기 시작하면(예: MMO의 Player 클래스 또는 FPS의 Gun 유형) 일부 클래스를 분할해야 한다고 생각합니다.

예를 들어 플레이어 데이터를 포함하는 단일 Model 에는 많은 playerDataA, playerDataB, playerDataA, playerDataB,... 가 있거나 플레이어 알림을 처리하는 Controller 에는 OnPlayerDidA,OnPlayerDidB,... 가 있습니다. 스크립트 크기를 줄이고 playerOnPlayer 접두사를 제거하려고 합니다.

데이터만 사용하는 것이 더 이해하기 쉽기 때문에 Model 클래스를 사용하여 설명하겠습니다.

프로그래밍하는 동안 저는 일반적으로 게임에 대한 모든 데이터를 보유하는 단일 Model 클래스로 시작합니다.

 // Model.cs class Model { public float playerHealth; public int playerLives; public GameObject playerGunPrefabA; public int playerGunAmmoA; public GameObject playerGunPrefabB; public int playerGunAmmoB; // Ops Gun[CDE ...] will appear... /* ... */ public float gameSpeed; public int gameLevel; }

게임이 복잡할수록 더 많은 변수가 발생한다는 것을 쉽게 알 수 있습니다. 충분히 복잡하면 model.playerABCDFoo 변수를 포함하는 거대한 클래스로 끝날 수 있습니다. 중첩 요소는 코드 완성을 단순화하고 데이터 변형 간에 전환할 수 있는 공간을 제공합니다.

 // Model.cs class Model { public PlayerModel player; // Container of the Player data. public GameModel game; // Container of the Game data. }
 // GameModel.cs class GameModel { public float speed; // Game running speed (influencing the difficulty) public int level; // Current game level/stage loaded }
 // PlayerModel.cs class PlayerModel { public float health; // Player health from 0.0 to 1.0. public int lives; // Player “retry” count after he dies. public GunModel[] guns; // Now a Player can have an array of guns to switch ingame. }
 // GunModel.cs class GunModel { public GunType type; // Enumeration of Gun types. public GameObject prefab; // Template of the 3D Asset of the weapon. public int ammo; // Current number of bullets public int clips; // Number of reloads possible }

이러한 클래스 구성을 통해 개발자는 소스 코드에서 한 번에 하나의 개념을 직관적으로 탐색할 수 있습니다. 무기와 그 구성이 정말 다양할 수 있는 1인칭 슈팅 게임을 가정해 봅시다. GunModel 이 클래스에 포함되어 있다는 사실을 통해 각 카테고리에 대해 Prefabs (게임 내에서 빠르게 복제 및 재사용할 수 있도록 미리 구성된 GameObjects) 목록을 만들고 나중에 사용하기 위해 저장할 수 있습니다.

대조적으로, 총 정보가 모두 gun0Ammo , gun1Ammo , gun0Clips 등과 같은 변수에 단일 GunModel 클래스에 함께 저장된 경우 사용자는 Gun 데이터를 저장해야 할 때 전체를 저장해야 합니다. 원하지 않는 Player 데이터가 포함된 Model 입니다. 이 경우 새 GunModel 클래스가 더 나을 것이 분명합니다.

이미지: 클래스 계층
클래스 계층 구조를 개선합니다.

모든 것이 그렇듯이 동전의 양면도 있습니다. 때로는 불필요하게 지나치게 세분화되어 코드 복잡성이 증가할 수 있습니다. 경험만이 프로젝트에 가장 적합한 MVC 정렬을 찾기에 충분한 기술을 연마할 수 있습니다.

새로운 게임 개발 특수 능력 잠금 해제: MVC 패턴의 Unity 게임.
트위터

결론

수많은 소프트웨어 패턴이 있습니다. 이번 포스트에서는 지난 프로젝트에서 가장 도움이 되었던 것을 보여드리려고 합니다. 개발자는 항상 새로운 지식을 흡수해야 하지만 항상 질문하기도 합니다. 이 튜토리얼이 새로운 것을 배우는 데 도움이 되는 동시에 자신만의 스타일을 개발하는 디딤돌이 되기를 바랍니다.

또한 다른 패턴을 연구하고 가장 적합한 패턴을 찾는 것이 좋습니다. 한 가지 좋은 출발점은 이 Wikipedia 기사로, 패턴과 그 특성에 대한 목록이 훌륭합니다.

AMVCC 패턴이 마음에 들고 테스트하고 싶다면 AMVCC 애플리케이션을 시작하는 데 필요한 모든 핵심 클래스가 포함된 Unity MVC 라이브러리를 사용해 보는 것을 잊지 마십시오.


Toptal 엔지니어링 블로그에 대한 추가 정보:

  • Unity AI 개발: 유한 상태 머신 튜토리얼