参考:http://blog.youkuaiyun.com/pz789as/article/details/52604075
今天突然想到在RN里面展示一些3D模型,这样可以增加软件的表现力。
我首先想到的是Unity3D,因为以前做过Unity3D的项目,它使用简单,最主要是用C#语言,对于我这个以前用C/C++的人来,是一个很不错的开发工具。
使用的版本:Xcode:7.3.1 Unity3D:5.3.5f1 RN:React:15.3.2,React-Native:0.33.0
那么开始记录我的做法:
首先,分别创建RN项目(Jicheng5)和Unity3D项目(RunInRN),我这里Unity3D项目没做什么特别的,只在场景里面放了一个Cube,然后加了一个脚本,让它自动旋转。
第二步,导出Unity项目,当然我们要选择导出ios才可以,打开file -》build settings 选中iOS,然后可以设置,其他设置不多说了,可以在网上看教程,我都是默认设置。需要注意5点:
1.Auto Graphic API: 去掉钩,然后选择OpenGLES2
2.Scripting Backend:选择IL2CPP
3.Target Device: 根据需要选择,我这里是默认的iPhone+iPad
4.Target SDK:一般默认是Device SDK,如果是这个,那么导出之后只能在真机上看效果,我现在是这个,如果你希望能在模拟器上看效果,可以选择Simulator SDK,不过这样你就不能发布了。
5.Target minimum iOS Version:选择8.0或以上。
以上这些设置之后,就可以点build按钮,选择路径到我们RN的根目录下面去,然后导出(unityVIew)
第三步,看到上面导出的目录结构没,现在我们把unityView里面的Classes、Data、Libraries、MapFileParser、MapFileParser.sh拷贝到ios目录下面去
打开ios里面的xcode工程,导入Classes和Libraries,导入时选择Create groups
而导入Data时,需要选择Create folder reference:
第四步:设置Xcode的参数:
1,选中Jicheng5 -》 build Phases,左上有个“+”号,点击添加run script,shell下面添加 "$PROJECT_DIR/MapFileParser.sh"
2,选择Link Binary With Libraries,将截图里面缺少的framework都加上:
3,选择Build Settings,搜索到Search Paths,在Header Search Paths中添加:注意引号
"$(SRCROOT)"
"$(SRCROOT)/Classes"
$(SRCROOT)/Classes/Native
$(SRCROOT)/Libraries/bdwgc/include
$(SRCROOT)/Libraries/libil2cpp/include
在Library Search Paths中添加:
"$(SRCROOT)"
"$(SRCROOT)/Libraries"
4,修改main.m,将Classes里面的main.mm中的所有内容拷贝至自己项目的main.m中,可以先将main.m里面的注释掉,然后粘贴拷贝的内容,把自己项目下的main.m重命名为main.mm,最后把Classes里面的main.mm删除。选择remove reference即可。
把mian.mm里面的修改一下:
void UnityInitTrampoline();
// WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value)
const char* AppControllerClassName = "AppDelegate";//将原来的UnityAppController改为AppDelegate
#import <UIKit/UIKit.h>//额外添加的
#import "AppDelegate.h"//<span style="font-family: Arial, Helvetica, sans-serif;">额外添加的</span>
int main(int argc, char* argv[])
{
@autoreleasepool
6,还是Build Settings中搜索Other C Flags,修改值为-DINIT_SCRIPTING_BACKEND=1
7,搜索Bitcode,将值设置为NO。
8,修改Classes中的UnityAppController.h中的内容:
inline UnityAppController* GetAppController()
{
// return (UnityAppController*)[UIApplication sharedApplication].delegate;
return (UnityAppController*)[[UIApplication sharedApplication] valueForKeyPath:@"delegate.unityAppController"];
}
这里是把UnityAppController保存在AppDelegate中了,所以只要从delegate中获取就可以了。
9,在AppDelegate.h文件中添加UnityAppController
#import <UIKit/UIKit.h>
#import "UnityAppController.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) UnityAppController* unityAppController;
@end
然后在.m中初始化unity
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"Jicheng5"
initialProperties:nil
launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
// [self.window makeKeyAndVisible];
_unityAppController = [[UnityAppController alloc]init];//这里初始化unity
[_unityAppController application:application didFinishLaunchingWithOptions:launchOptions];
// self.window = _unityAppController.window;
[self.window makeKeyAndVisible];
return YES;
}
补充unity在app中其他状态的代码:
-(void)applicationWillResignActive:(UIApplication *)application{
[_unityAppController applicationWillResignActive:application];
}
-(void)applicationDidEnterBackground:(UIApplication *)application{
[_unityAppController applicationDidEnterBackground:application];
}
-(void)applicationWillEnterForeground:(UIApplication *)application{
[_unityAppController applicationWillEnterForeground:application];
}
-(void)applicationDidBecomeActive:(UIApplication *)application{
[_unityAppController applicationDidBecomeActive:application];
}
-(void)applicationWillTerminate:(UIApplication *)application{
[_unityAppController applicationWillTerminate:application];
}
至此,我们所有必要条件都达成了,如果想看效果,可以在上面的application中,将RN的初始化注释掉,然后把
self.window = _unityAppController.window;
这样就可以看到unity页面了。
但是我们还是希望可以向组件一下显示它,怎么做呢?这里用到我之前的文章RN自定义显示view了,哈哈,没想到这么快就用到刚刚学会的知识了。
在xcode里面创建RCTUnityView 和 RCTUnityViewManager两个类:
接下来不多说了,直接看代码:
//
// RCTUnityView.h
// Jicheng5
//
// Created by guojicheng on 16/9/20.
// Copyright © 2016年 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "UnityAppController.h"
@interface RCTUnityView : UIView
@property (nonatomic, strong) UIView* uView;
@end
//
// RCTUnityView.m
// Jicheng5
//
// Created by guojicheng on 16/9/20.
// Copyright © 2016年 Facebook. All rights reserved.
//
#import "RCTUnityView.h"
@implementation RCTUnityView
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self){
self.uView = (UIView*)GetAppController().unityView;
}
return self;
}
-(void)layoutSubviews
{
[super layoutSubviews];
[self.uView removeFromSuperview];
self.uView.frame = self.bounds;
[self insertSubview:self.uView atIndex:0];
[self.uView setNeedsLayout];
}
@end
//
// RCTUnityViewManager.h
// Jicheng5
//
// Created by guojicheng on 16/9/20.
// Copyright © 2016年 Facebook. All rights reserved.
//
#import "RCTViewManager.h"
@interface RCTUnityViewManager : RCTViewManager
@end
//
// RCTUnityViewManager.m
// Jicheng5
//
// Created by guojicheng on 16/9/20.
// Copyright © 2016年 Facebook. All rights reserved.
//
#import "RCTUnityViewManager.h"
#import "RCTUIManager.h"
#import "RCTUnityView.h"
@implementation RCTUnityViewManager
RCT_EXPORT_MODULE();
@synthesize bridge = _bridge;
- (UIView *)view
{
return [[RCTUnityView alloc] init];
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
@end
好了,原生端已经准备完毕,在JS那边也要写一个组件,看代码:UnityView.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
'use strict'
import React, { Component } from 'react';
import { View } from 'react-native';
import { requireNativeComponent } from 'react-native';
class UnityView extends Component {
setNativeProps(props){
this.root.setNativeProps(props);
}
render() {
return (
<UnityViewNative
{...this.props}
style={[
{backgroundColor: 'transparent'},
this.props.style,
]}
ref={(r)=>{this.root = r}}
/>
);
}
}
UnityView.propTypes = {
};
const UnityViewNative = requireNativeComponent('RCTUnityView', UnityView);
export default UnityView;
至此组件做好咯,是不是很简单啊~哈哈哈哈。。
然后我们在index里面使用一下看看效果哈,有点小激动:
<span style="color:#ff0000;">import UnityView from './UnityView';</span>
class Jicheng5 extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
<span style="color:#ff0000;"><UnityView style={{width:300, height:200}}/></span>
</View>
);
}
}
嘿嘿嘿,没错误,最后给大家看看运行效果,由于我导出的是Device SDK所以只能在真机上看:
立方体在自动旋转哦!~~
资料参考:AR学院-Unity导出的项目整合到已有的IOS项目中
将Unity页面添加到iOS APP中
unity项目整合嵌入iOS主工程
找了好几个,综合测试得到自己想要的了。。另外有英语好的,也可以看看他们最后参考的歪果仁写的教程。