Pull to refresh

О подключении самописного Objective-C плагина к игровому движку Unity 3D

Reading time7 min
Views10K


Доброго всем дня.

Начнем собственно с того что такое Unity 3d В кратце это трехмерный игровой движок. На официальном сайте доступна бесплатная версия, pro версия и версия с возможностью заливать приложения на мобильные платформы уже стоят денег.

У данного движка богатые возможности, но всего он не умеет и некоторые вещи все же придется делать нативно. Например у вас возникла задача отображение какой либо веб страницы (например вашей личной странице при переходе по ссылке с главного меню игры). Для работы с сетью в unity предназначен класс WWW но он обладает возможностью только обмениваться данными с веб страницами, с задачей рэндера он справиться не может. Вы перебираете все варианты, и решаете что лучше отобразить веб страницу с помощью нативного iOS WebView (сразу оговорюсь, что статья не о том, как написать Web View на Objective-C, а скорее о том, как его подружить с Unity). Для начала вам необходимо открыть xCode (или AppCode) и создать пустое приложение (тут следует учесть, что проект создаваемый Unity не поддерживает ARC , по этому очищать за собой память придется ручками). Нам будет необходимо два Objective-C класса. Назовем их WebController и WebViewCaller. Соответственно создастся четыре файла WebController.h, WebController.m, WebViewCaller.h и WebViewCaller.m. Файлы с расширением .h это заголовочные файлы, там должны быть описаны все методы класса, файлы .m это их непосредственная реализация. Для начала мы определимся что мы хотим от нашего web view. Мы хотим что бы класс принимал два параметра NSString, первый — это страница на которую мы должны перейти если соединение с сетью есть, и второй, это путь в файловой системе до html страницы, которую мы хотим загружать по умолчанию, если соединения с интернетом нет. Для WebController я приведу только .h файл, на WebViewCaller мы остановимся подробнее.

Итак, с помощью хабра, гугла и stackoverflow мы написали класс создающий вэб вью. .h файл выглядит у нас примерно так:

@interface WebController : UIViewController <UIWebViewDelegate>

	- (id)initWithOnlineHtml:(NSString*) _onlineHtml withOfflineHtml:(NSString*) _offlineHtml;

@end


Это фактически сигнатура public конструктора с именем initWithOnlineHtml:withOfflineHtml: и двумя строковыми параметрами _onlineHtml и _offlineHtml, предназначение которых думаю должно быть всем понятно.

Далее нам надо добавить этот вью в наше приложение. Это мы будем делать в нашем WebVievCaller классе. Он должен рисовать нащ веб вью, и рисовать кнопку назад, которая нас вернет назад в нашу игру. Для начала наш класс будет выглядеть так (.h файл)

#import <Foundation/Foundation.h>
#import "TEWebController.h"

@interface WebViewCaller : NSObject

-(IBAction)quitBack:(id)sender;
-(void) callViewWithOnlineHtml:(NSString*) _onlineHtml
               withOfflineHtml:(NSString*) _offlineHtml;

@end


quitBack это функция, которая будет вызываться нажатием кнопки, callViewWithOnlineHtml:withOfflineHtml: будет собственно добавлять наш WebView к текущему окну и рисовать кнопку. Итак наш класс выглядит следующим образом:

@implementation WebViewCaller

UIWindow* window;

-(IBAction)quitBack:(id)sender
{
    NSLog(@"%d",clicked.tag);
}

-(void) callViewWithOnlineHtml:(NSString*) _onlineHtml
               withOfflineHtml:(NSString*) _offlineHtml
{
    TEWebController* webController = [[TEWebController alloc] initWithOnlineHtml:_onlineHtml withOfflineHtml:_offlineHtml];
    
    window = [[UIApplication sharedApplication] keyWindow]; //1
    [window setRootViewController:webController]; //2
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; //3
    [button addTarget:self
               action:@selector(quitBack:)
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"<<" forState:UIControlStateNormal];
    button.frame = CGRectMake(0, 0, 160.0, 40.0);
    [window addSubview:button]; //4
    
    [webController release];
}

@end


Тут можно остановиться немного подробнее. Мы создаем наш веб вью с пришедшими из unity параметрами, строкой 1 мы получаем текущее окно, строкой 2 мы устанавливаем наш веб вью как контроллер отображения, со строки 3 по строку 4 мы создаем кнопку, которая пока просто выводит свой тег в консоль и, наконец строкой 4 мы добавляем нашу кнопку на экран.
Далее начинается то, ради чего это статья задумывалась. Нам надо добавить всю эту радость в unity и сделать кнопку назад.
Для начала нам надо добавть наши классы в папку "/pathToYourProject/Assets/Plugins/iOS/" (дальнейшие вложения не допустимы). Зачем мы это сделали, наверное спросите вы. Unity при сборке создает проект xCode, и xCode подхватывает файлы лежащие в этой папке, и добавляет в свой проект. В xCode проекте они лежат в папке Libraries. Для подклучения плагина функции должны быть объявлены в «C» синтаксисе. То есть нашу функцию мы должны объявить в виде
void _CalliOSWebView(const char *onlineHtml, const char *offlineHtml); 

(типу string в «C#»" соответствует const char * в «C», так что нам еще надо будет привести наши const char * переменные в NSString и вызвать callViewWithOnlineHtml:withOfflineHtml: с данными параметрами). Для этого вне класса после пишем функцию в «C» стиле преобразующую входные параметры в NSString и вызывающую callViewWithOnlineHtml:withOfflineHtml:.

Если вы попытаетесь подключить Objective-C метод, вы получите ошибку вида:

Undefined symbols for architecture armv7: "__CalliOSWebView", referenced from: RegisterMonoModules() in RegisterMonoModules.o ld: symbol(s) not found for architecture armv7 clang: error: linker command failed with exit code 1 (use -v to see invocation)


Внесем необходимые измененмя в наш класс. Теперь наш .m файл должен выглядеть так:

@implementation WebViewCaller

UIWindow* window;

-(IBAction)quitBack:(id)sender
{
    NSLog(@"%d",clicked.tag);
}

-(void) callViewWithOnlineHtml:(NSString*) _onlineHtml
               withOfflineHtml:(NSString*) _offlineHtml
{
    TEWebController* webController = [[TEWebController alloc] initWithOnlineHtml:_onlineHtml withOfflineHtml:_offlineHtml];
    
    window = [[UIApplication sharedApplication] keyWindow]; //1
    [window setRootViewController:webController]; //2
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; //3
    [button addTarget:self
               action:@selector(quitBack:)
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"<<" forState:UIControlStateNormal];
    button.frame = CGRectMake(0, 0, 160.0, 40.0);
    [window addSubview:button]; //4
    
    [webController release];
}

@end

void _CalliOSWebView(const char *onlineHtml, const char *offlineHtml)
{
    NSString *onlineHtmlString = [[NSString alloc] initWithUTF8String:onlineHtml];
    NSString *offlineHtmlString = [[NSString alloc] initWithUTF8String:offlineHtml];
    WebViewCaller *caller = [[WebViewCaller alloc] init];
    [caller callViewWithOnlineHtml:onlineHtmlString withOfflineHtml:offlineHtmlString];
    
    [onlineHtmlString release];
    [offlineHtmlString release];
}



также надо добавить описание нашей функции в .h файл
#import <Foundation/Foundation.h>
#import "WebController.h"

@interface WebViewCaller : NSObject

-(IBAction)quitBack:(id)sender;
-(void) callViewWithOnlineHtml:(NSString*) _onlineHtml
               withOfflineHtml:(NSString*) _offlineHtml;

@end

#ifdef __cplusplus
extern "C" {
#endif
    void _CalliOSWebView(const char *onlineHtml, const char *offlineHtml);
#ifdef __cplusplus
}
#endif


Теперь мы можем использовать наш плагин. Для этого в Unity используется [DllImport («PluginName»)], но так как мы работаем с iOS где плагины статически включены в сборку, мы будем использовать [DllImport ("__Internal")].
Код в unity будет выглядеть так:

public static void CallForWebPage()
{
	if (Application.platform == RuntimePlatform.IPhonePlayer)
	{
			_CalliOSWebView("http://habrahabr.ru", Application.dataPath + "/Raw/test.html");		
	}
}


думаю тут все должно быть понятно, кроме разве что второго аргумента в вызове _CalliOSWebView. Он ссылается на файл, сохраненный в файловой системе iOS устройства. Но как мы его туда положим? Для этого в unity существует StreamingAssets (файлы помещенные в эту папку останутся без изменений после сборки проекта). Создадим эту папку и положим туда наш test.html. Теперь для отображения веб вью надо будет вызвать из юнити CallForWebPage() но останется вопрос с кнопкой назад. Его можно решить следующим образом. Сохраняем вью юнити, подкладываем наш веб вью, по нажатию кнопки восстанавливаем вью юнити. Единственная проблема в том, что юнити не знает об этом, и игра, звуки продолжаются. Но это легко решаемо при желаниии. Реализация кнопки представлена ниже:

@implementation WebViewCaller

UIViewController *unityViewController;
UIWindow* window;

-(IBAction)quitBack:(id)sender
{
    [window setRootViewController:unityViewController];
}

-(void) callViewWithOnlineHtml:(NSString*) _onlineHtml
               withOfflineHtml:(NSString*) _offlineHtml
{
    WebController* webController = [[TEWebController alloc] initWithOnlineHtml:_onlineHtml withOfflineHtml:_offlineHtml];
    
    window = [[UIApplication sharedApplication] keyWindow];
    unityViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
    [window setRootViewController:webController];
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button addTarget:self
               action:@selector(quitBack:)
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"<<" forState:UIControlStateNormal];
    button.frame = CGRectMake(0, 0, 160.0, 40.0);
    [window addSubview:button];
    
    [webController release];
}

@end

void _CalliOSWebView(const char *onlineHtml, const char *offlineHtml)
{
    NSString *onlineHtmlString = [[NSString alloc] initWithUTF8String:onlineHtml];
    NSString *offlineHtmlString = [[NSString alloc] initWithUTF8String:offlineHtml];
    WebViewCaller *caller = [[WebViewCaller alloc] init];
    [caller callViewWithOnlineHtml:onlineHtmlString withOfflineHtml:offlineHtmlString];
    
    [onlineHtmlString release];
    [offlineHtmlString release];
}


Единственный оставшийся вопрос в том, как это все дебажить. Я для себя решил его следующим образом. Заходим в папку куда мы собирали iOS проект. Открываем его из xCode, правим, дебажим и делаем все что хотим (напомню, что наши плагины лежат в папке Libraries). Потом копипастим изменения в наши классы лежащие в папке "/pathToYourProject/Assets/Plugins/iOS/".

Вот собственно и все. Если класс WebController написан правильно, то все запустится и будет работать. До этого программированием на Objective-C не занимался, и некоторые вещи могут показаться профессионалам топорными и кривыми, за что я извиняюсь, но надеюсь что основную мысль о том, как подключать плагины я донести смог, и надеюсь что смог сохранить хоть кому-нибудь несколько часов работы. Спасибо за внимание.

Кому интересен Unity могу посоветовать следующие ресурсы:

unity3d.com — официальный сайт;
answers.unity3d.com — вопросы и ответы. Самый, на мой взгляд, полезный ресурс;
forum.unity3d.com — форум о юнити;
docs.unity3d.com — официальная документация.
Tags:
Hubs:
+11
Comments15

Articles

Change theme settings