LoginSignup
11
13

More than 3 years have passed since last update.

Unity Native Pluginの作り方(iOS編)

Last updated at Posted at 2018-02-11

概要

iOS向けに開発したSDKをUnityアプリに対応させる場合、プラグインを作ってSDKとUnityアプリの橋渡しをしてやる必要があります。本記事ではUnity側のコードをC#、プラグイン側のコードをObjective-C++ (.mm)で記載するケースについて紹介します。

補足

iOS, Android共通で使用するコードについては共通編を参考にして下さい。

プラグイン側

まずはプラグイン側のコードの一例です。Sample, SampleDelegateといったクラスはSDK側で定義されており、それらのメソッドにUnity側からアクセスするために各Wrapperメソッドを実装しています。このコードはUnityプロジェクトのAssets/Plugins/iOSディレクトリ配下に入れておくと良いでしょう。

SamplePlugin.mm
#include <stdio.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <SampleSDK/SampleSDK.h>
#import <SapleSDK/SampleSDK-Swift.h>

#define kSample_DefaultCallbackGameObjectName @"Main Camera"

@interface SamplePlugin: NSObject
@end

SamplePlugin *__unityClientSharedInstance;

@interface SamplePlugin() <SampleDelegate>
@property(nonatomic, strong) NSString *callbackGameObjectName;

- (void)invokeUnityGameObjectMethod:(NSString *)methodName message:(NSString *)message;

@end

@implementation SamplePlugin

+ (SamplePlugin *)sharedInstance {
    if (!__unityClientSharedInstance) {
        __unityClientSharedInstance = [[SamplePlugin alloc] init];
    }

    return __unityClientSharedInstance;
}

# pragma mark - SampleDelegate

- (void)didStatusChange:(enum SampleStatus)status {
    NSString *statusStr = [NSString stringWithFormat:@"%ld", status];
    [self invokeUnityGameObjectMethod:@"_Sample_didStatusChange" message:statusStr];
}

# pragma mark - Private

- (void)invokeUnityGameObjectMethod:(NSString *)methodName message:(NSString *)message {
    NSString *gameObject = self.callbackGameObjectName == nil ?
    kSample_DefaultCallbackGameObjectName : self.callbackGameObjectName;

    const char *gameObjectStr = [gameObject cStringUsingEncoding:NSUTF8StringEncoding];
    const char *methodNameStr = [methodName cStringUsingEncoding:NSUTF8StringEncoding];
    const char *messageStr = [message cStringUsingEncoding:NSUTF8StringEncoding];

    UnitySendMessage(gameObjectStr, methodNameStr, messageStr);
}

@end

// Declare each wrapper methods
extern "C" {
    void SPStart(const char *sampleCode);
    void SPDoSomething();
    int SPGetStatus();
    int SPGetScore();
    bool SPIsAvailable();
    const char* SPGetSomeText();
}

extern UIViewController* UnityGetGLViewController();
extern void UnitySendMessage(const char* obje, const char* method, const char* msg);

void SPSetCallbackGameObjectName(char *gameObjectName) {
    NSString *name = [NSString stringWithCString:gameObjectName encoding:NSUTF8StringEncoding];
    SamplePlugin *client = [SamplePlugin sharedInstance];
    client.callbackGameObjectName = name;
}

void SPStart(const char *sampleCode) {
    NSString *sampleCodeStr = [NSString stringWithCString:sampleCode encoding:NSUTF8StringEncoding];

    [[Sample sharedInstance] startWithSampleCode: sampleCodeStr];
    [Sample sharedInstance].delegate = [SamplePlugin sharedInstance];
}

void SPDoSomething() {
    [[Sample sharedInstance] doSomething];
}

int SPGetStatus() {
    return [[Sample sharedInstance] getStatus];
}

int SPGetScore() {
    return [[Sample sharedInstance] getScore];
}

bool SPIsAvailable() {
    return [[Sample sharedInstance] isAvailable];
}

const char* SPGetSomeText() {
    NSString *textStr = [[Sample sharedInstance] getSomeText];
    const char *textChar = [textStr cStringUsingEncoding:NSUTF8StringEncoding];

    return textChar ? strdup(textChar) : NULL;
}

Unity側

Sample_iOS.cs
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

#if UNITY_IOS

public class Sample_iOS : ISample {
    private Sample sampleGameObject;
    private SampleEventListener listener;

    [DllImport ("__Internal")]
    protected static extern void SPSetCallbackGameObjectName(string gameObjectName);

    public Sample_iOS(Sample sampleParent) {
        sampleGameObject = sampleParent;

        if (sampleParent.iosAppCode != null) {
            Start(null);
        }

        CreateListenerObject();
    }

    private void CreateListenerObject() {
        listener = sampleGameObject.gameObject.AddComponent<SampleEventListener>();
        listener.SetNativeParent(this);

        SPSetCallbackGameObjectName(sampleGameObject.gameObject.name);
    }

    [DllImport("__Internal")]
    private static extern void SPStart(string sampleCode);
    public void Start(string sampleCode) {
        if (sampleGameObject.iosSampleCode != null) {
            SPStart(sampleGameObject.iosSampleCode);
        } else if (sampleCode != null) {
            SPStart(sampleCode);
        }
    }

    [DllImport("__Internal")]
    private static extern void SPDoSomething();
    public void DoSomething() {
        SPDoSomething();
    }

    [DllImport("__Internal")]
    private static extern int SPGetStatus();
    public SampleStatus GetStatus() {
        return (SampleStatus)SPGetStatus();
    }

    [DllImport("__Internal")]
    private static extern int SPGetScore();
    public int GetScore() {
        return SPGetScore();
    }

    [DllImport("__Internal")]
    private static extern bool SPIsAvailable();
    public bool IsAvailable() {
        return SPIsAvailable();
    }

    [DllImport("__Internal")]
    private static extern string SPGetSomeText();
    public string GetSomeText() {
        return SPGetSomeText();
    }
}

#endif

参考

11
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13