├── LICENSE
├── README.md
├── Unity.xcconfig
├── UnityProjectRefresh.sh
└── objc
├── UnityBridge.h
├── UnityUtils.h
└── UnityUtils.mm
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 BLITZ
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # How to use Unity 3D within an iOS app
2 |
3 | This is going to appear to be complicated based on the length of this article
4 | it's really not. I try to fully show some examples here, and provide some images
5 | for those who may not know where certain things are in xcode.
6 |
7 |
8 | This would not be possible without [www.the-nerd.be], Frederik Jacques.
9 | All of the settings in the xcconfig file, the `UnityProjectRefresh.sh`
10 | script and the project import are directly derieved from his work. The video
11 | he made in the provided link is worth watching.
12 |
13 | This covers Unity 5+. At the time of this writing this has been
14 | successfully used with Unity `5.5.2f1` and `Swift 3.1` under `Xcode 8.3.2`.
15 |
16 | This works with storyboards.
17 |
18 | You only get **ONE** unity view. You **CANNOT** run multiple Unity
19 | Views in your application at once. You will also need a way to
20 | communicate to <-> from your unity content to your iOS app.
21 | I would recommend an event bus in both your Unity code and
22 | your iOS code. AKA one central place on both sides to emit events
23 | to and listen to events on each side.
24 |
25 | In other words you will need 2 busses, 1 on the Unity side that you can
26 | call into to emit events from on the iOS side, and one on the iOS side that
27 | Unity can call into to emit events on.
28 |
29 | You can read more about communication between the 2 worlds from
30 | the following links:
31 |
32 | **More about embedding**
33 |
34 | http://forum.unity3d.com/threads/unity-appcontroller-subclassing.191971/
35 |
36 | Specifically there is a bit on commuicating here with some sample code.
37 | Note, this is not for UNITY 5, but it shows the samples in OverlayUI related
38 | making functions available to the Objective-C side of things to be called from
39 | your Unity Code.
40 |
41 | http://forum.unity3d.com/threads/unity-appcontroller-subclassing.191971/#post-1341666
42 |
43 |
44 | **Communicating from Unity -> ObjC**
45 |
46 | http://blogs.unity3d.com/2015/07/02/il2cpp-internals-pinvoke-wrappers/
47 |
48 | http://forum.unity3d.com/threads/unity-5-2-2f1-embed-in-ios-with-extern-dllimport-__internal-methods-fails-to-compile.364809/
49 |
50 |
51 | **Communicating from Unity <-> ObjC**
52 |
53 | http://alexanderwong.me/post/29861010648/call-objective-c-from-unity-call-unity-from
54 |
55 |
56 | ## Lets get started.
57 |
58 | ### From Unity
59 |
60 | First you need to have a project in unity, and you need to build it for iOS.
61 |
62 | Under Unity 5 the project's scripting backend is already set to `il2cpp` so you
63 | pretty much just have to :
64 |
65 | - `File -> Build Settings`
66 | - Select your scene(s)
67 | - Press the build button
68 | - Remember the folder you built the project too.
69 |
70 |
71 | ### From Xcode
72 |
73 | There is a bit more to do here, but ideally the `Unity.xcconfig` and
74 | the `UnityProjectRefresh.sh` script make this easier.
75 |
76 | Setting expectations, the project import process here takes some time,
77 | it's not instant, Unity generates a lot of files and Xcode has to import them
78 | all. So expect to stare a beachball for a few minuts while it does it's thing.
79 |
80 | Ok! Fire up Xcode and create a new `Swift` project or open an existing
81 | `Swift` project.
82 |
83 | Here is what we will be doing, this will seem like a lot, but it's pretty straight
84 | forward. You will fly through these steps minus the unity project import/cleanup
85 | which is not diffiucilt, it's just time consuming given the number of files.
86 |
87 | - Add the Unity.xcconfig file provided in this repo
88 | - Adjust 1 project dependent setting
89 | - Add a new `run script` build phase
90 | - Import your unity project
91 | - Clean up your unity project
92 | - Add the `objc` folder in this repo with the new custom unity init and obj-c bridging header
93 | - Rename `main` in `main.mm` to anything else
94 | - Wrap the UnityAppController into your application delegate
95 | - Adjust the `GetAppController` function in `UnityAppController.h`
96 | - Go bananas, you did it! Add the unity view wherever you want!
97 |
98 | #### Add the Unity.xcconfig file provided in this repo
99 |
100 | Drag and drop the `Unity.xcconfig` file into your Xcode project.
101 | Set the project to use those settings.
102 |
103 |
104 |
105 | #### Adjust 1 project dependent setting
106 | So that does a lot for you in terms of configuration, now we need to adjust 1 setting in it.
107 | Since we don't know where you decided to export your unity project too, you need to configure that.
108 |
109 |
110 | Open up your project's build settings and scroll all the way to bottom, you will see:
111 |
112 | ```
113 | UNITY_IOS_EXPORT_PATH
114 | ```
115 |
116 | Adjust that path to point to your ios unity export path
117 |
118 |
119 |
120 |
121 | You can also adjust your
122 |
123 | ```
124 | UNITY_RUNTIME_VERSION
125 | ```
126 |
127 | If you are not using `5.5.2f1`.
128 |
129 |
130 | #### Add a new `run script` build phase
131 |
132 | Now we need to ensure we copy our fresh unity project on each build, so we add a
133 | new run script build phase.
134 |
135 | Select Build Phases from your project settings to add a new build phase.
136 |
137 | Copy the contents of the UnityProjectRefresh.sh script into this phase.
138 |
139 |
140 |
141 |
142 | #### Import your unity project
143 |
144 | This is outlined in this [www.the-nerd.be] video at around 5:35 - 7:30 as well, but it's now time to import our Unity project.
145 |
146 | Create a new group and call it `Unity`, the name doesn't matter it's just helpful to name things so you know what they are).
147 |
148 |
149 | You will need to open the folder you built your Unity iOS project into. It will be the same folder you
150 | specified for the `UNITY_IOS_EXPORT_PATH` above.
151 |
152 | Do 1 folder at a time, this will take a minute or more to do, there are lots of files.
153 |
154 | We are going to drag in the following folders (You don't need to copy them):
155 |
156 | - `/your/unity/ios/export/path/Classes`
157 | - `/your/unity/ios/export/path/Libraries`
158 |
159 |
160 | #### Clean up your unity project
161 |
162 | This is all in the [www.the-nerd.be] video as well 7:35 -
163 | There is two location we will clean up for convenience. For both of these we
164 | *ONLY WANT TO REMOVE REFERENCES DO NOT MOVE TO TRASH*
165 |
166 | We don't need the `Unity/Classes/Native/*.h` and we don't need `Unity/Libraries/libl2cpp/`.
167 |
168 | The Unity.xcconfig we applied knows where they are for compiling purposes.
169 |
170 | - Remove `Unity/Libraries/libl2cpp/` 7:35 - 7:50 in [www.the-nerd.be] video.
171 | - Remove `Unity/Classes/Native/*.h` 7:55- 8:44 in [www.the-nerd.be] video.
172 |
173 |
174 | #### Add the `objc` folder in this repo
175 |
176 | You can copy these if you want, they are tiny.
177 |
178 | - `UnityBridge.h` is the `SWIFT_OBJC_BRIDGING_HEADER` specified in `Unity.xcconfig`
179 | - `UnityUtils.h/mm` is our new custom init function.
180 |
181 | The new custom unity init function is pulled directly our of the main.mm file in your unity project.
182 | Swift does not have the same initialization convention as an objecitve-c app, so we are going to
183 | tweak things slightly.
184 |
185 | #### Rename `main` in `main.mm` to anything else
186 |
187 | In your xcode project under `Unity/Classses` locate the `main.mm` file. Within that file locate
188 |
189 | ```cpp
190 | int main(int argc, char* argv[])
191 | ```
192 | Once you find that you can go ahead and see that `UnityUtils.mm`, which we imported
193 | above, is effectively this function. Should Unity change this initialization you will need
194 | to update your `UnityUtils.mm` file to match their initialization. Note that we don't
195 | copy the `UIApplicationMain` part. Swift will handle that.
196 |
197 | Anyway, we need to rename this function to anything but `main`:
198 |
199 |
200 | ```cpp
201 | int main_unity_default(int argc, char* argv[])
202 | ```
203 |
204 | #### Wrap the UnityAppController into your application delegate
205 |
206 | We are taking away control from the unity generated application delegate, we
207 | need to act as a proxy for it in our `AppDelegate`.
208 |
209 | First add the following variable to your `AppDelegate`
210 |
211 | ```swift
212 | var currentUnityController: UnityAppController!
213 | ```
214 | Now we need to initialize and proxy through the calls to the `UnityAppController`.
215 |
216 | All said and done you will be left with the following:
217 |
218 | ```swift
219 | //
220 | // AppDelegate.swift
221 | //
222 | // Created by Adam Venturella on 10/28/15
223 | //
224 | // Updated by Martin Straub on 15/03/2017.
225 | // Added some stuff to pause unity in order to stop consuming cpu cylces and battery life, when not being displayed.
226 | // Indeed, unity will still sit in memory all the time, but that seems to be a more complex thing to solve.
227 | // Just use `startUnity` and `stopUnity` for running/pausing unity (see also ViewController example below).
228 | //
229 |
230 | import UIKit
231 |
232 | @UIApplicationMain
233 | class AppDelegate: UIResponder, UIApplicationDelegate {
234 | var currentUnityController: UnityAppController?
235 | var application: UIApplication?
236 | var isUnityRunning = false
237 |
238 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
239 | self.application = application
240 | unity_init(CommandLine.argc, CommandLine.unsafeArgv)
241 | currentUnityController = UnityAppController()
242 | currentUnityController!.application(application, didFinishLaunchingWithOptions: launchOptions)
243 |
244 | // first call to startUnity will do some init stuff, so just call it here and directly stop it again
245 | startUnity()
246 | stopUnity()
247 |
248 | return true
249 | }
250 |
251 | func applicationWillResignActive(_ application: UIApplication) {
252 | if isUnityRunning {
253 | currentUnityController?.applicationWillResignActive(application)
254 | }
255 | }
256 |
257 | func applicationDidEnterBackground(_ application: UIApplication) {
258 | if isUnityRunning {
259 | currentUnityController?.applicationDidEnterBackground(application)
260 | }
261 | }
262 |
263 | func applicationWillEnterForeground(_ application: UIApplication) {
264 | if isUnityRunning {
265 | currentUnityController?.applicationWillEnterForeground(application)
266 | }
267 | }
268 |
269 | func applicationDidBecomeActive(_ application: UIApplication) {
270 | if isUnityRunning {
271 | currentUnityController?.applicationDidBecomeActive(application)
272 | }
273 | }
274 |
275 | func applicationWillTerminate(_ application: UIApplication) {
276 | if isUnityRunning {
277 | currentUnityController?.applicationWillTerminate(application)
278 | }
279 | }
280 |
281 | func startUnity() {
282 | if !isUnityRunning {
283 | isUnityRunning = true
284 | currentUnityController!.applicationDidBecomeActive(application!)
285 | }
286 | }
287 |
288 | func stopUnity() {
289 | if isUnityRunning {
290 | currentUnityController!.applicationWillResignActive(application!)
291 | isUnityRunning = false
292 | }
293 | }
294 | }
295 |
296 | ```
297 |
298 | #### Adjust the `GetAppController` function in `UnityAppController.h`
299 |
300 | Locate the file `UnityAppController.h` in the xcode group `Unity/Classes/`
301 |
302 | Find the following function:
303 |
304 | ```objc
305 | inline UnityAppController*GetAppController()
306 | {
307 | return (UnityAppController*)[UIApplication sharedApplication].delegate;
308 | }
309 | ```
310 |
311 | Comment that out. You will end up with this:
312 |
313 | ```objc
314 | //inline UnityAppController*GetAppController()
315 | //{
316 | // return (UnityAppController*)[UIApplication sharedApplication].delegate;
317 | //}
318 | ```
319 |
320 | Now we need to add a new version of this function:
321 |
322 | ```objc
323 | NS_INLINE UnityAppController* GetAppController()
324 | {
325 | NSObject* delegate = [UIApplication sharedApplication].delegate;
326 | UnityAppController* currentUnityController = (UnityAppController *)[delegate valueForKey:@"currentUnityController"];
327 | return currentUnityController;
328 | }
329 | ```
330 |
331 |
332 | #### Go bananas, you did it! Add the unity view wherever you want!
333 |
334 | I happen to do this in a stock, single view application, so xcode generated a `ViewController.swift`
335 | file for me attached to a storyboard. Here is how I hooked up my little demo:
336 |
337 | ```swift
338 | //
339 | // ViewController.swift
340 | //
341 | // Created by Adam Venturella on 10/28/15.
342 | // Updated by Martin Straub on 15/03/2017.
343 | //
344 |
345 | import UIKit
346 |
347 | class ViewController: UIViewController {
348 | var unityView: UIView?
349 |
350 | @IBAction func startUnity(sender: AnyObject) {
351 | let appDelegate = UIApplication.shared.delegate as! AppDelegate
352 | appDelegate.startUnity()
353 |
354 | unityView = UnityGetGLView()!
355 |
356 | self.view!.addSubview(unityView!)
357 | unityView!.translatesAutoresizingMaskIntoConstraints = false
358 |
359 | // look, non-full screen unity content!
360 | let views = ["view": unityView]
361 | let w = NSLayoutConstraint.constraints(withVisualFormat: "|-20-[view]-20-|", options: [], metrics: nil, views: views)
362 | let h = NSLayoutConstraint.constraints(withVisualFormat: "V:|-75-[view]-50-|", options: [], metrics: nil, views: views)
363 | view.addConstraints(w + h)
364 | }
365 |
366 | @IBAction func stopUnity(sender: AnyObject) {
367 | let appDelegate = UIApplication.shared.delegate as! AppDelegate
368 | appDelegate.stopUnity()
369 | unityView!.removeFromSuperview()
370 | }
371 | }
372 |
373 | ```
374 |
375 | [www.the-nerd.be]: http://www.the-nerd.be/2015/08/20/a-better-way-to-integrate-unity3d-within-a-native-ios-application/ "The Nerd"
376 |
--------------------------------------------------------------------------------
/Unity.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Unity.xcconfig
3 | //
4 | // Created by Adam Venturella on 10/28/15.
5 | // Settings from http://www.the-nerd.be/2015/08/20/a-better-way-to-integrate-unity3d-within-a-native-ios-application/
6 |
7 | UNITY_RUNTIME_VERSION = 5.5.2f1;
8 | UNITY_SCRIPTING_BACKEND = il2cpp;
9 | UNITY_IOS_EXPORT_PATH = /Path/To/Your/Unity/iOS/Build;
10 | GCC_PREFIX_HEADER = $(UNITY_IOS_EXPORT_PATH)/Classes/Prefix.pch;
11 |
12 | OTHER_LDFLAGS = -weak-lSystem -weak_framework CoreMotion -weak_framework GameKit -weak_framework iAd -framework CoreGraphics -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework SystemConfiguration -framework CoreLocation -framework MediaPlayer -framework CFNetwork -framework AudioToolbox -framework OpenAL -framework QuartzCore -framework OpenGLES -framework UIKit -framework Foundation -liconv.2 -liPhone-lib;
13 |
14 | HEADER_SEARCH_PATHS = "$(UNITY_IOS_EXPORT_PATH)/Classes" "$(UNITY_IOS_EXPORT_PATH)/Classes/Native" "$(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include";
15 | LIBRARY_SEARCH_PATHS = "$(UNITY_IOS_EXPORT_PATH)/Libraries" "$(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include";
16 |
17 | ENABLE_BITCODE = NO;
18 |
19 | SWIFT_OBJC_BRIDGING_HEADER = UnityBridge.h;
20 |
21 | OTHER_CFLAGS = -DINIT_SCRIPTING_BACKEND=1;
22 | CLANG_CXX_LANGUAGE_STANDARD = c++11;
23 | CLANG_CXX_LIBRARY = libc++;
24 | CLANG_ENABLE_MODULES = NO;
25 | CLANG_WARN_BOOL_CONVERSION = NO;
26 | CLANG_WARN_CONSTANT_CONVERSION = NO;
27 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
28 | CLANG_WARN_EMPTY_BODY = NO;
29 | CLANG_WARN_ENUM_CONVERSION = NO;
30 | CLANG_WARN_INT_CONVERSION = NO;
31 | CLANG_WARN_OBJC_ROOT_CLASS = YES;
32 | CLANG_WARN_UNREACHABLE_CODE = NO;
33 | CLANG_WARN__DUPLICATE_METHOD_MATCH = NO;
34 | GCC_C_LANGUAGE_STANDARD = c99;
35 | GCC_ENABLE_OBJC_EXCEPTIONS = NO;
36 | GCC_ENABLE_CPP_RTTI = NO;
37 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
38 | GCC_THUMB_SUPPORT = NO;
39 | GCC_USE_INDIRECT_FUNCTION_CALLS = NO;
40 | GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
41 | GCC_WARN_64_TO_32_BIT_CONVERSION[arch=*64] = YES;
42 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
43 | GCC_WARN_UNDECLARED_SELECTOR = NO;
44 | GCC_WARN_UNINITIALIZED_AUTOS = NO;
45 | GCC_WARN_UNUSED_FUNCTION = NO;
46 |
--------------------------------------------------------------------------------
/UnityProjectRefresh.sh:
--------------------------------------------------------------------------------
1 | rm -rf "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data";
2 | cp -Rf "$UNITY_IOS_EXPORT_PATH/Data" "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data";
3 |
--------------------------------------------------------------------------------
/objc/UnityBridge.h:
--------------------------------------------------------------------------------
1 | //
2 | // UnityBridge.h
3 | //
4 | // Created by Adam Venturella on 10/28/15.
5 | //
6 |
7 | #ifndef UnityBridge_h
8 | #define UnityBridge_h
9 |
10 | #import
11 | #import "UnityUtils.h"
12 | #import "UnityAppController.h"
13 | #import "UnityInterface.h"
14 | #endif /* UnityBridge_h */
15 |
--------------------------------------------------------------------------------
/objc/UnityUtils.h:
--------------------------------------------------------------------------------
1 | //
2 | // UnityUtils.h
3 | //
4 | // Created by Adam Venturella on 10/28/15.
5 | //
6 |
7 | #ifndef UnityUtils_h
8 | #define UnityUtils_h
9 |
10 |
11 | void unity_init(int argc, char* argv[]);
12 |
13 | #endif /* UnityUtils_h */
14 |
--------------------------------------------------------------------------------
/objc/UnityUtils.mm:
--------------------------------------------------------------------------------
1 | //
2 | // UnityUtils.m
3 | //
4 | // Created by Adam Venturella on 10/28/15.
5 | //
6 | // this is taken directly from the unity generated main.mm file.
7 | // if they change that initialization, this will need to be updated
8 | // as well.
9 | //
10 | // Updated by Martin Straub on 03/15/17.
11 | //
12 | // updated to Unity 5.5.0f3 => working on Xcode 8.2.1 with Swift 3.0.2
13 |
14 |
15 | #include "RegisterMonoModules.h"
16 | #include "RegisterFeatures.h"
17 | #include
18 |
19 |
20 | // Hack to work around iOS SDK 4.3 linker problem
21 | // we need at least one __TEXT, __const section entry in main application .o files
22 | // to get this section emitted at right time and so avoid LC_ENCRYPTION_INFO size miscalculation
23 | static const int constsection = 0;
24 |
25 | void UnityInitTrampoline();
26 |
27 |
28 | extern "C" void unity_init(int argc, char* argv[])
29 | {
30 | @autoreleasepool
31 | {
32 | UnityInitTrampoline();
33 | UnityInitRuntime(argc, argv);
34 |
35 | RegisterMonoModules();
36 | NSLog(@"-> registered mono modules %p\n", &constsection);
37 | RegisterFeatures();
38 |
39 | // iOS terminates open sockets when an application enters background mode.
40 | // The next write to any of such socket causes SIGPIPE signal being raised,
41 | // even if the request has been done from scripting side. This disables the
42 | // signal and allows Mono to throw a proper C# exception.
43 | std::signal(SIGPIPE, SIG_IGN);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------