Byn's Research Note

AI based Mixed Reality, Human-Computer Interaction

↓ My Web CV & Portfolio 자세히보기

Development/3D Engine Programming

Unity Programming [1]: 병행성 추상화 프레임워크 (Concurrency Abstraction Framework)

JaehyeonByun 2024. 10. 28. 17:00

 

 

1. 스레드와 태스크

 

2. 플러그인 모듈화 (Plug-in Modularity)

Dynamic Link Library

 

Unity에서 관리되는 플러그인(Managed Plugin)은 .NET 언어(C#, VB.NET 등)로 작성된 외부 라이브러리를 Unity 프로젝트에 통합하여 활용할 수 있는 방식이다. 이러한 플러그인은 Visual Studio와 같은 외부 도구를 사용하여 작성되며, .dll 형식의 어셈블리로 컴파일된다. 관리되는 플러그인은 Unity의 스크립팅 환경과 직접적으로 호환되므로, 복잡한 기능을 모듈화하거나 타사 코드나 외부 라이브러리를 손쉽게 통합하는 데 유용하다.

관리되는 플러그인을 사용하는 첫 단계는 플러그인으로 사용할 .dll 파일을 생성하거나 다운로드받는 것이다. 직접 작성하려면 Visual Studio에서 Class Library 프로젝트를 생성한 후, 필요한 코드를 구현하고 .dll로 컴파일하면 된다. 이렇게 생성된 플러그인 파일을 Unity 프로젝트 내의 Assets 폴더에 복사하면 Unity가 이를 자동으로 인식한다. 플러그인을 사용하는 스크립트는 일반 C# 코드에서 라이브러리를 참조하듯이 using 구문을 통해 플러그인을 불러올 수 있다.

더보기

Unity에서 관리되는 플러그인을 사용할 때는 몇 가지 주의사항이 있다. 첫째, 플러그인은 Unity의 .NET 버전과 호환되어야 하며, 이를 위해 Unity 프로젝트의 API 호환성 수준이 플러그인이 사용하는 .NET 버전과 일치하도록 설정해야 한다. 둘째, 플랫폼별로 플러그인의 동작이 다를 수 있으므로, 특정 플랫폼에서만 사용 가능한 코드가 포함된 경우 플러그인의 인스펙터 설정에서 플랫폼별 포함 여부를 명시해야 한다. 마지막으로, 관리되는 플러그인은 기본적으로 C# 스크립트와 동일한 방식으로 실행되므로 Unity의 스크립트 라이프사이클에 영향을 받을 수 있음을 이해해야 한다. 이러한 과정을 통해 관리되는 플러그인은 Unity 프로젝트에서 재사용 가능한 모듈을 구현하거나, 외부 라이브러리를 효율적으로 활용하는 데 기여한다. 예를 들어, 데이터 처리, 네트워크 통신, 또는 알고리즘 연산과 같은 복잡한 작업을 캡슐화하여 Unity 코드베이스의 유지보수성과 확장성을 높일 수 있다.

 

관리되는 플러그인 - Unity 매뉴얼

관리되는 플러그인은 Visual Studio와 같은 툴로 Unity 외부에서 생성하여 DLL(Dynamically Linked Library)로 컴파일하는 .NET 어셈블리입니다.

docs.unity3d.com

 

Unity에서 관리되는 플러그인은 Unity 외부에서 Visual Studio와 같은 툴을 사용해 생성하고, DLL(Dynamically Linked Library)로 컴파일한 .NET 어셈블리다. 이는 Unity 프로젝트의 Assets 폴더에 저장되는 표준 C# 스크립트와 다르다. 표준 C# 스크립트는 변경될 때마다 Unity가 컴파일하지만, 관리되는 플러그인은 사전 컴파일되며 수정되지 않는다. 컴파일된 DLL 파일은 Unity 프로젝트에 추가하여 사용되며, 포함된 클래스를 표준 스크립트처럼 게임 오브젝트에 연결할 수 있다.

  • Unity에서 지원하지 않는 코드에 컴파일러를 사용해야 하는 경우.
  • 타사 .NET 코드를 추가하려는 경우.
  • 소스 코드 없이 Unity에 코드를 제공하려는 경우.

관리되는 플러그인을 만들려면 .dll 파일을 생성해야 하며, 이를 위해 Visual Studio, MsBuild, .NET SDK 같은 컴파일러가 필요하다. DLL에 Unity API 코드가 포함된 경우, Unity 자체 DLL 파일을 컴파일러에서 사용할 수 있도록 설정해야 한다. Unity DLL의 경로는 다음과 같다:

  • Windows: C:\Program Files\Unity\Hub\Editor\<version-number>\Editor\Data\Managed\UnityEngine
  • macOS: /Applications/Unity/Hub/Editor/<version-number>/Unity.app/Contents/Managed/UnityEngine

DLL을 컴파일하려면 Unity DLL을 참조하며, 사용하는 컴파일러에 따라 적절한 옵션을 설정해야 한다. 예를 들어, Roslyn 컴파일러를 사용하는 경우 다음 명령을 사용할 수 있다.

csc /r:<Unity DLL 경로> /target:library /out:MyManagedAssembly.dll /recurse:*.cs

 

컴파일된 .dll 파일은 Unity 프로젝트의 Assets 폴더로 드래그하면 사용할 수 있다. 후에는 다음과 같은 작업이 가능하다:

  • DLL 내부 클래스를 확장하여 개별 클래스를 확인.
  • MonoBehaviour에서 파생된 클래스를 게임 오브젝트에 드래그하여 연결.
  • 다른 스크립트에서 비-MonoBehaviour 클래스를 직접 사용.
더보기

DLL에 다음과 같은 클래스가 있다고 가정하자

namespace MyMathLibrary {
    public class MathOperations {
        public static int Add(int a, int b) {
            return a + b;
        }

        public static int Multiply(int a, int b) {
            return a * b;
        }
    }
}

 

Unity에서 해당 DLL을 Assets 폴더로 가져오고 다음과 같은 스크립트를 작성할 수 있다:

using UnityEngine;
using MyMathLibrary;

public class MathTest : MonoBehaviour {
    void Start() {
        int sum = MathOperations.Add(5, 3);
        int product = MathOperations.Multiply(4, 2);
        
        Debug.Log("Sum: " + sum);        // 출력: Sum: 8
        Debug.Log("Product: " + product); // 출력: Product: 8
    }
}

 

이 스크립트를 Unity의 게임 오브젝트에 연결하면, Play를 누를 때 콘솔에 결과값이 출력된다.

 

DLL에 MonoBehaviour를 상속받는 클래스가 포함되어 있다면, Unity에서 게임 오브젝트에 바로 연결하여 사용할 수 있다.

using UnityEngine;

namespace MyGameLibrary {
    public class Rotator : MonoBehaviour {
        public float speed = 10f;

        void Update() {
            transform.Rotate(Vector3.up * speed * Time.deltaTime);
        }
    }
}

 

결론적으로 Unity에서 DLL을 사용하면 프로젝트 구조를 모듈화하고 외부 라이브러리의 기능을 간단히 활용할 수 있다. MonoBehaviour를 상속받은 클래스를 게임 오브젝트에 연결하거나, 비-MonoBehaviour 클래스를 다른 스크립트에서 호출하여 기능을 사용할 수 있다. 이런 방식은 특히 반복적인 기능이나 복잡한 로직을 여러 프로젝트에서 재사용할 때 매우 유용하다.

Visual Studio에서 관리되는 플러그인을 생성하려면 다음 단계를 따른다:

  1. Visual Studio에서 새 프로젝트를 생성하고, .NET Standard의 Class Library 템플릿을 선택한다.
  2. 생성된 기본 클래스의 이름을 변경하고 코드를 작성한다. 예를 들어, 다음 코드를 사용할 수 있다:
using System;
using UnityEngine;

namespace DLLTest {
    public class MyUtilities {
        public int c;

        public void AddValues(int a, int b) {
            c = a + b;
        }

        public static int GenerateRandom(int min, int max) {
            System.Random rand = new System.Random();
            return rand.Next(min, max);
        }
    }
}

 

3. Unity DLL을 참조에 추가하고 프로젝트를 빌드해 .dll 파일을 생성한다.

 

생성된 DLL을 Unity에서 디버깅하려면 다음 단계를 따른다:

  1. Unity 프로젝트의 Assets 폴더에 .dll 파일을 복사한다.
  2. DLL의 기능을 테스트하는 스크립트를 작성한다. 예를 들어:
using UnityEngine;
using DLLTest;

public class Test : MonoBehaviour {
    void Start() {
        MyUtilities utils = new MyUtilities();
        utils.AddValues(2, 3);
        print("2 + 3 = " + utils.c);
    }

    void Update() {
        print(MyUtilities.GenerateRandom(0, 100));
    }
}

 

3. 작성된 스크립트를 게임 오브젝트에 연결하고 Play 버튼을 눌러 동작을 확인한다.

 

네이티브 코드 간의 상호 운용

System.Runtime.InteropServicesC#과 같은 관리 코드와 네이티브 코드 간의 상호 운용을 가능하게 하는 중요한 네임스페이스다. 이 네임스페이스는 관리 환경에서 실행되는 .NET 애플리케이션이 운영 체제의 네이티브 API, COM(구성 요소 객체 모델) 개체, 또는 외부 라이브러리와 통신할 수 있도록 다양한 클래스와 속성을 제공한다. 네이티브 코드는 일반적으로 .dll(Windows) 또는 .so(Linux) 파일로 제공된다.

 

이 네임스페이스에서 제공하는 대표적인 기능 중 하나는 DllImport 속성이다. DllImport는 특정 외부 DLL에 정의된 네이티브 함수를 가져오기 위해 사용된다. C# 코드에서 이를 사용하면, 마치 해당 네이티브 함수가 C#의 메서드인 것처럼 호출할 수 있다. 이를 위해 함수의 서명을 정확히 선언해야 하며, 함수가 요구하는 데이터 타입과 호출 규칙을 지정해야 한다.

예를 들어, 외부 DLL에 있는 MessageBox 함수를 호출하려면 다음과 같은 선언이 필요하다

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);

 

여기서 extern 키워드C# 코드에서 함수가 외부에서 정의되었음을 명시하는 데 사용된다. 이는 해당 함수의 실제 구현이 네이티브 DLL이나 다른 외부 라이브러리에서 이루어졌음을 의미하며, C# 코드에서는 단순히 함수의 선언만 제공한다. 이를 통해 C# 애플리케이션은 네이티브 코드를 호출할 수 있는 인터페이스를 제공받는다. 이러한 방식으로 선언된 함수는 일반적으로 DllImport 속성과 함께 사용되며, 이를 통해 특정 DLL에서 함수를 가져오는 작업이 이루어진다. 

 

전처리기 지시문

Unity의 전처리기 지시문은 컴파일러가 특정 조건에서만 코드를 포함하거나 제외할 수 있도록 지시하는 데 사용된다. 전처리기 지시문은 주로 플랫폼별 코드 실행, 디버그와 릴리스 빌드 간의 차별화된 동작 정의, 또는 특정 기능을 활성화/비활성화하는 데 사용된다. 이 지시문은 #if, #elif, #else, #endif 키워드를 포함하여 조건부로 코드를 구성한다.

#if 조건식
    // 조건이 참일 때 실행될 코드
#elif 다른_조건식
    // 다른 조건이 참일 때 실행될 코드
#else
    // 위 조건들이 모두 거짓일 때 실행될 코드
#endif

 

여기서 조건식은 Unity에서 제공하는 빌트인 기호(예: UNITY_EDITOR, UNITY_ANDROID, DEBUG) 또는 사용자가 정의한 기호로 구성된다. 빌트인 기호는 Unity의 특정 환경, 플랫폼, 또는 빌드 설정을 나타낸다.

 

더보기

예시 1: 플랫폼별 코드 분리

 

Unity에서 iOS와 Android 플랫폼에 따라 서로 다른 코드를 실행하려는 경우 다음과 같은 전처리기 지시문을 사용할 수 있다.

using UnityEngine;

public class PlatformSpecificCode : MonoBehaviour
{
    void Start()
    {
#if UNITY_IOS
        Debug.Log("This is running on iOS.");
#elif UNITY_ANDROID
        Debug.Log("This is running on Android.");
#else
        Debug.Log("This is running on an unsupported platform.");
#endif
    }
}

 

위 코드는 iOS 플랫폼에서 실행되면 "This is running on iOS." 메시지를 출력하며, Android에서는 "This is running on Android." 메시지를 출력한다. 다른 플랫폼에서는 기본 메시지가 출력된다.

 

예시 2 : 디버그와 릴리스 빌드 분리

 

디버그 빌드에서만 추가적인 정보를 출력하고 릴리스 빌드에서는 생략하려면 DEBUG 기호를 활용할 수 있다.

using UnityEngine;

public class DebugExample : MonoBehaviour
{
    void Update()
    {
#if DEBUG
        Debug.Log("This message is only shown in the debug build.");
#endif
    }
}

 

Unity의 Build Settings에서 "Development Build"를 활성화하면 DEBUG 기호가 자동으로 정의되며, 디버그 메시지가 출력된다. 릴리스 빌드에서는 DEBUG가 정의되지 않아 해당 코드가 실행되지 않는다.

 

예시 3 : 사용자 정의 기호 활용

 

사용자가 직접 전처리 기호를 정의할 수도 있다. Unity의 "Player Settings"에서 "Scripting Define Symbols" 필드에 기호를 추가하거나, 코드에서 #define 키워드를 사용할 수 있다.

#define CUSTOM_FEATURE

using UnityEngine;

public class CustomFeatureExample : MonoBehaviour
{
    void Start()
    {
#if CUSTOM_FEATURE
        Debug.Log("Custom feature is enabled.");
#else
        Debug.Log("Custom feature is disabled.");
#endif
    }
}

 

위 코드에서 CUSTOM_FEATURE 기호가 정의되어 있다면 "Custom feature is enabled."가 출력된다. 그렇지 않으면 다른 메시지가 출력된다.

 

 

 

 

 

Design Patterns - Unity Learn

By implementing common game programming design patterns in your Unity project, you can efficiently build and maintain a clean, organized, and readable codebase. Design patterns not only reduce refactoring and the time spent testing, but they also speed up

learn.unity.com