iOS Swift <-> Unity Plugin (1)
Swift와 Unity가 서로 통신할 수 있을까요?
결과는 조건부로 가능했습니다
Objective-C만으로 가능하지만 iOS 개발은 대부분 Swift로 이루어지기 때문에
Swift를 가급적 사용하고 싶었습니다
현재 Native(iOS) -> Unity로 신호를 보내는 건 Unity 공홈에서도 제공하는 기능이 있지만
굉장히 코스트가 많이 드는 메소드입니다
또한 공식 문서에는 Unity -> Swift로 가는 기능은 없기에 직접 연구/개발하여야 하는 상황입니다
저는 iOS 개발자라서 Unity는 빌드용으로 Xcode로 Export하는 것이 고작이라
Unity부분은 회사의 엔진개발자님이 많이 도와주셨습니다
Swift(Native) < - Objective-C(Framework) - > Unity(C#)을 통해서 이루어 질 수 있었습니다
핵심 코드들은 구글링에서 발견한 코드를 제 나름대로 조합해서 사용하는 방법이기에 저는 조립한 수준이고
워낙 자료도 적고해서 더 널리널리 퍼져서 각종 피드백을 받아보고자 이번 과정을 작성하게 되었습니다
일단 필요한 것
기계 : MAC
툴 : Xcode, Unity(iOS Target), VSCode(C#)
언어 : Swift(Native), Objective-C, C#
저는 Swift 말고는 기초도 겨우하는 수준이기에 비효율적으로 작성된 코드가 많은 점 미리 양해 바랍니다a
자세히 살펴보기 전에 Plugin의 개요
Plugin의 동작 원리를 요약하면
Objective-C 파일 중 m파일은 mm파일로 변경하면 Unity에서 c++파일로 인식되어 코드로 인식이 가능해집니다. 또한 Unity에서 이를 인식하여 접근 및 사용하기 위해서는
[DllImport("__Internal")]
라는 Unity에서 제공하는 코드를 이용하여 Xcode에 인식하게 하는 방법이 있습니다.
이 코드를 이용하면 Unity-> iOS에 신호를 보내어 수신하는게 가능해집니다
iOS -> Unity에 신호를 보내는 typedef 콜백함수를 선언한 후
필요할 때 마다 초기화하여 사용하는 방법으로 상호 통신망을 만들 수 있습니다
이 코드는 c++로 동작하기에 mm파일로 인식하게 한 다음 Unity와 iOS파일에서 동명의 함수를 선언하여
삽입하여 사용해야 합니다.
또한 해당 Plugin(Framework)는 Unity project, iOS project 둘 다 import 되어있어야 합니다
두 가지 경로를 통해 제작한 자체 SDK로 샘플 프로젝트 설명과 함께 진행하겠습니다
iOS <-> Unity 와 통신이 되는 근간에 대한 설명
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=lswcharming&logNo=220435074874
UnityViewController를 독립적으로 사용할 수 있는 코드
https://stackoverflow.com/questions/59428747/embed-unity-inside-ios-in-own-
제가 만든 Plugin의 근간이 되는 소중한 자료입니다!
https://github.com/500beckwon/UnityiOSProject
GitHub - 500beckwon/UnityiOSProject
Contribute to 500beckwon/UnityiOSProject development by creating an account on GitHub.
github.com
https://github.com/500beckwon/UnityPluginNative
GitHub - 500beckwon/UnityPluginNative: Swift <-> Unity Plugin with Objective-C Framework
Swift <-> Unity Plugin with Objective-C Framework. Contribute to 500beckwon/UnityPluginNative development by creating an account on GitHub.
github.com
일단 Xcode를 실행해서 App이 아닌 Framework를 골라야 합니다
프로젝트를 생성한 기본화면입니다 새로운 Group를 생성해주세요
통신할 코드가 들어있는 핵심 class
파일을 모아둘 Group입니다
샘플로 사용할 h파일과 짝이 될 m파일을 생성해주세요
여기서 중요한건 m파일 확장자를 mm로 바꾸어주어야 Unity가 인식할 수 있습니다
또한 h파일의 TargetMambership을 Project -> Public으로 변경해주셔야 합니다
프로젝트 생성할 때 부터 있는 맨 아래 prject이름.h 파일에 통신용 h파일을 import 해주세요
#ifndef TestManager_h
#define TestManager_h
#import <UnityiOSPlugin/UnityiOSPlugin.h>
#import <UIKit/UIKit.h>
typedef void (*testAction)(void);
typedef void (*testMessage)(const char*);
@interface TestManager : NSObject
{
testAction actionHandler;
testMessage messageHandler;
}
@property (assign)testAction actionHandler;
@property (assign)testMessage messageHandler;
+ (TestManager*)sharedInstance;
- (id)init;
- (void)dealloc;
@end
#endif /* TestManager_h */
h파일 코드는 내용입니다
#import <Foundation/Foundation.h>
#include "TestManager.h"
static TestManager * instance = [TestManager sharedInstance];
@implementation TestManager
@synthesize actionHandler;
@synthesize messageHandler;
+ (TestManager*)sharedInstance
{
return instance;
}
+ (void)initialize
{
if(!instance)
{
instance = [[TestManager alloc]init];
}
}
- (id)init
{
self = [super init];
if(!self)
return nil;
return self;
}
-(void)dealloc
{
}
@end
mm파일 코드는 이렇게 적어주세요
마지막으로 해당 클래스에 접근 할 h, mm 파일을 1개씩 만들겠습니다
PluginTest.h 파일과
#ifndef PluginTest_h
#define PluginTest_h
#import <Foundation/Foundation.h>
#endif /* PluginTest_h */
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <UnityiOSPlugin/UnityiOSPlugin.h>
#import "UnityiOSPlugin/UnityiOSPlugin-Swift.h"
//#import "UnityiOSPlugin.h"
extern "C" {
void SendTestActionCallBack(testAction handler)
{
[TestManager sharedInstance].actionHandler = handler;
}
void SendTestMessageCallBack(testMessage handler)
{
[TestManager sharedInstance].messageHandler = handler;
}
}
PluginTest.mm파일을 작성 해주세요
그리고 실행해주셔야합니다
에러가 없으면 프레임워크 프로젝트는 다 만들어졌습니다
이 프레임워크를 따로 뽑아서 유니티 프로젝트에 넣어야 합니다
Product -> Show Build Folder in Finder를 실행하면
project이름.framework으로 된 경로가 하나 보이는데 이게 실행한 프레임워크입니다
이제 유니티 프로젝트를 1개 생성하겠습니다
Plugin 이라는 폴더를 생성 후 여기에 프레임워크를 넣어주세요
그리고 마지막에 만든 PluginTest.h, PluginTest.mm 두개를 같이 넣어주세요
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonScript : MonoBehaviour
{
public void OnClickBasicButton()
{
Debug.Log("Touch Button!");
}
}
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using AOT;
using System;
public class DelegateManager : MonoBehaviour
{
public static DelegateManager delegateManager;
// Start is called before the first frame update
void Start()
{
}
private void Awake()
{
SendDelegates();
Debug.Log("잔송");
}
public void SendDelegates()
{
SendTestActionCallBack(ReceiveTestAction_Unity);
SendTestMessageCallBack(ReceiveTestMessage_Unity);
}
delegate void receiveTestAction();
[DllImport("__Internal")]
private extern static void SendTestActionCallBack(receiveTestAction callback);
[MonoPInvokeCallback(typeof(receiveTestAction))]
public static void ReceiveTestAction_Unity()
{
Debug.Log("TestAction!");
Debug.Log("신호수신");
}
delegate void receiveMessageAction(string text);
[DllImport("__Internal")]
private extern static void SendTestMessageCallBack(receiveMessageAction callback);
[MonoPInvokeCallback(typeof(receiveMessageAction))]
public static void ReceiveTestMessage_Unity(string text)
{
Debug.Log("TestMessage!");
Debug.Log(text);
}
}
DelegateManager.cs 파일입니다
이제 File->BuildSetting을 실행하신 후
Platform을 iOS로 설정해주셔야 합니다
그 다음 왼쪽 아래에 Player Setting을 누른 후
디바이스 실행인지, 시뮬레이터 실행인지 골라주셔야 합니다 저는 개발자 계정이 있어서 디바이스 실행으로 했습니다
이제 다시 Build Setting으로 돌아와서 Build를 누른 후 적당한 폴더를 고르거나 New Folder로 새로 영역을 할당해서 Choose를 눌러주세요
시간이 조금 걸리는 작업입니다
TestPlugin01라고 만든 폴더에 유니티가 xcode로 변환되어 나왔습니다
다음에는 이 프로젝트를 Framework화 시켜서 본격적인 통신을 해보겠습니다