Skip to content

Commit 3a2b96d

Browse files
authored
Merge pull request #112 from Jpoliachik/main
Add Expo Plugin and update README
2 parents 0ed1fbc + aa57034 commit 3a2b96d

File tree

6 files changed

+440
-63
lines changed

6 files changed

+440
-63
lines changed

README.md

+75-58
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
# @azesmway/react-native-unity
22

3-
The plugin that allows you to embed a UNITY project into the react native as a full-fledged component
3+
The plugin that allows you to embed a Unity project into React Native as a full-fledged component. The plugin now supports the new architecture.
44

5-
### ATTENTION!
5+
> [!IMPORTANT]
6+
> For iOS, it is no longer necessary to embed a project created with Unity. Only the built `UnityFramework` is used. It should be placed in the plugin folder at the path - `<YOUR_RN_PROJECT>/unity/builds/ios`
67
7-
The plugin now supports the new architecture as well.
8-
For iOS, it is no longer necessary to embed a project created with Unity. Only the built UnityFramework.framework is used. It should be placed in the plugin folder at the path -
9-
```<YOUR_PROJECT>/unity/builds/ios```.
10-
11-
### IOS ONLY!!! If you used a previous version of the plugin - then be sure to delete everything that was related to Unity in the main react native project!
8+
## Device Support:
129

10+
| Platform | Supported |
11+
| ---------------- | --------- |
12+
| iOS Simulator ||
13+
| iOS Device ||
14+
| Android Emulator ||
15+
| Android Device ||
1316

1417
# Installation
1518

16-
## RN
19+
## Install this package in your react-native project:
1720

1821
```sh
1922
npm install @azesmway/react-native-unity
@@ -23,16 +26,16 @@ or
2326
yarn add @azesmway/react-native-unity
2427
```
2528

26-
## Unity
27-
28-
1. Copy from folder "unity" to <Unity_Project_Name> folder and rebuild unity project.
29+
## Configure your Unity project:
2930

30-
#### OnEvent in Unity
31+
1. Copy the contents of the folder `unity` to the root of your Unity project. This folder contains the necessary scripts and settings for the Unity project. You can find these files in your react-native project under `node_modules/@azesmway/react-native-unity/unity`. This is necessary to ensure iOS has access to the `NativeCallProxy` class from this library.
3132

32-
Add this code:
33+
2. (optional) If you're following along with the example, you can add the following code to the `ButtonBehavior.cs` script in your Unity project. This allows the button press in Unity to communicate with your react-native app.
3334

34-
```js
35+
<details>
36+
<summary>ButtonBehavior.cs</summary>
3537

38+
```csharp
3639
using System;
3740
using System.Collections;
3841
using System.Collections.Generic;
@@ -66,58 +69,68 @@ public class ButtonBehavior : MonoBehaviour
6669
}
6770
}
6871
}
69-
7072
```
7173

72-
## iOS
74+
</details>
75+
76+
## Export iOS Unity Project:
77+
78+
After you've moved the files from the `unity` folder to your Unity project, you can export the iOS unity project by following these steps:
7379

74-
1. Build Unity project for ios in ANY folder - just not the main RN project folder!!!
75-
2. Open the created project in XCode
76-
3. Select Data folder and set a checkbox in the "Target Membership" section to "UnityFramework" ![image info](./docs/step1.jpg)
77-
4. You need to select the NativeCallProxy.h inside the `Unity-iPhone/Libraries/Plugins/iOS` folder of the Unity-iPhone project and change UnityFramework’s target membership from Project to Public. Don’t forget this step! ![image info](./docs/step2.jpg)
78-
5. If required - sign the project ```UnityFramework.framework``` and build a framework ![image info](./docs/step3.jpg)
79-
6. Open the folder with the built framework (by right-clicking) and move it to the plugin folder (```<YOUR_PROJECT>/unity/builds/ios```) ![image info](./docs/step4.jpg)
80-
7. Execute the command in the root of the main RN project ```rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install```
80+
1. Open your Unity project
81+
2. Build Unity project for ios in ANY folder - just not the main RN project folder!!!
82+
3. Open the created project in XCode
83+
4. Select Data folder and set a checkbox in the "Target Membership" section to "UnityFramework" ![image info](./docs/step1.jpg)
84+
5. You need to select the NativeCallProxy.h inside the `Unity-iPhone/Libraries/Plugins/iOS` folder of the Unity-iPhone project and change UnityFramework’s target membership from Project to Public. Don’t forget this step! (if you don't see these files in your Xcode project, you didn't copy over the `unity` folder to your Unity project correctly in previous steps) ![image info](./docs/step2.jpg)
85+
6. If required - sign the project `UnityFramework.framework` and build a framework ![image info](./docs/step3.jpg)
86+
7. Open the folder with the built framework (by right-clicking) and move it to the plugin folder (`<YOUR_RN_PROJECT>/unity/builds/ios`) ![image info](./docs/step4.jpg)
87+
8. Remove your `Pods` cache and lockfile with this command in the root of the main RN project `rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install`
8188

8289
### Android
8390

84-
1. Export Unity app to `[project_root]/unity/builds/android`
85-
2. Add the following lines to `android/settings.gradle`:
86-
```gradle
91+
1. Open your Unity project
92+
2. Export Unity app to `<YOUR_RN_PROJECT>/unity/builds/android`
93+
3. Remove `<intent-filter>...</intent-filter>` from `<YOUR_RN_PROJECT>/unity/builds/android/unityLibrary/src/main/AndroidManifest.xml` at unityLibrary to leave only integrated version.
94+
95+
If you're using expo, you're done. The built-in expo plugin will handle the rest. If you're not using expo, you'll need to follow the steps below.
96+
97+
1. Add the following lines to `android/settings.gradle`:
98+
```groovy
8799
include ':unityLibrary'
88100
project(':unityLibrary').projectDir=new File('..\\unity\\builds\\android\\unityLibrary')
89101
```
90-
3. Add into `android/build.gradle`
91-
```gradle
92-
allprojects {
93-
repositories {
94-
// this
95-
flatDir {
96-
dirs "${project(':unityLibrary').projectDir}/libs"
97-
}
98-
// ...
99-
```
100-
4. Add into `android/gradle.properties`
101-
```gradle
102-
unityStreamingAssets=.unity3d
103-
```
104-
5. Add strings to ``android/app/src/main/res/values/strings.xml``
105-
106-
```javascript
107-
<string name="game_view_content_description">Game view</string>
108-
```
109-
6. Remove `<intent-filter>...</intent-filter>` from ``<project_name>/unity/builds/android/unityLibrary/src/main/AndroidManifest.xml`` at unityLibrary to leave only integrated version.
102+
2. Add into `android/build.gradle`
103+
```groovy
104+
allprojects {
105+
repositories {
106+
// this
107+
flatDir {
108+
dirs "${project(':unityLibrary').projectDir}/libs"
109+
}
110+
// ...
111+
}
112+
}
113+
```
114+
3. Add into `android/gradle.properties`
115+
```gradle
116+
unityStreamingAssets=.unity3d
117+
```
118+
4. Add strings to `android/app/src/main/res/values/strings.xml`
119+
120+
```javascript
121+
<string name="game_view_content_description">Game view</string>
122+
```
110123

111124
# Known issues
112125

113-
- Works only on real iOS devices. Android emulators are capable of showing the UnityView.
114-
- On IOS the Unity view is waiting for a parent with dimensions greater than 0 (from RN side). Please take care of this because if it is not the case, your app will crash with the native message `MTLTextureDescriptor has width of zero`.
126+
- Does not work on the iOS simulator.
127+
- On iOS the Unity view is waiting for a parent with dimensions greater than 0 (from RN side). Please take care of this because if it is not the case, your app will crash with the native message `MTLTextureDescriptor has width of zero`.
115128

116129
# Usage
117130

118131
## Sample code
119132

120-
```js
133+
```jsx
121134
import React, { useRef, useEffect } from 'react';
122135

123136
import UnityView from '@azesmway/react-native-unity';
@@ -139,7 +152,11 @@ const Unity = () => {
139152
methodName: 'methodName',
140153
message: 'message',
141154
};
142-
unityRef.current.postMessage(message.gameObject, message.methodName, message.message);
155+
unityRef.current.postMessage(
156+
message.gameObject,
157+
message.methodName,
158+
message.message
159+
);
143160
}
144161
}, []);
145162

@@ -149,30 +166,30 @@ const Unity = () => {
149166
ref={unityRef}
150167
style={{ flex: 1 }}
151168
onUnityMessage={(result) => {
152-
console.log('onUnityMessage', result.nativeEvent.message)
169+
console.log('onUnityMessage', result.nativeEvent.message);
153170
}}
154171
/>
155172
</View>
156173
);
157174
};
158175

159176
export default Unity;
160-
161177
```
162178
163179
## Props
164-
- `androidKeepPlayerMounted?: boolean` - if set to true, keep the player mounted even when the view that contains it has lost focus. The player will be paused on blur and resumed on focus. **FOR ANDROID:** has no effect on iOS.
165-
- `fullScreen?: boolean` - defaults to true. If set to false, will not request full screen access. **ANDROID ONLY**
180+
181+
- `style: ViewStyle` - styles the UnityView. (Won't show on Android without dimensions. Recommended to give it `flex: 1` as in the example)
166182
- `onUnityMessage?: (event: NativeSyntheticEvent)` - receives a message from a Unity
167-
- `style: ViewStyle` - styles the UnityView. (Won't show on Android without dimensions. Recommended to give it `flex: 1` as in the example)
183+
- `androidKeepPlayerMounted?: boolean` - if set to true, keep the player mounted even when the view that contains it has lost focus. The player will be paused on blur and resumed on focus. **ANDROID ONLY**
184+
- `fullScreen?: boolean` - defaults to true. If set to false, will not request full screen access. **ANDROID ONLY**
168185
169186
## Methods
170-
- `postMessage(gameObject, methodName, message)` - sends a message to the Unity. **FOR IOS:** The native method of unity is used to send a message
171-
`sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg;`, more details can be found in the [documentation](https://docs.unity3d.com/2021.1/Documentation/Manual/UnityasaLibrary-iOS.html)
172187
188+
- `postMessage(gameObject, methodName, message)` - sends a message to the Unity. **FOR IOS:** The native method of unity is used to send a message
189+
`sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg;`, more details can be found in the [documentation](https://docs.unity3d.com/2021.1/Documentation/Manual/UnityasaLibrary-iOS.html)
173190
- `unloadUnity()` - the Unity is unloaded automatically when the react-native component is unmounted, but if you want to unload the Unity, you can call this method
174191
- `pauseUnity?: (pause: boolean)` - pause the Unity
175-
- `windowFocusChanged(hasFocus: boolean = false)` - simulate focus change (intended to be used to recover from black screen (not rendering) after remounting Unity view when `resumeUnity` does not work) **ANDROID ONLY**
192+
- `windowFocusChanged(hasFocus: boolean = false)` - simulate focus change (intended to be used to recover from black screen (not rendering) after remounting Unity view when `resumeUnity` does not work) **ANDROID ONLY**
176193
177194
# Contributing
178195

app.plugin.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./plugin/build');

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"devDependencies": {
5959
"@commitlint/config-conventional": "^17.0.2",
6060
"@evilmartians/lefthook": "^1.5.0",
61+
"@expo/config-plugins": "^7.9.1",
6162
"@react-native/eslint-config": "^0.72.2",
6263
"@release-it/conventional-changelog": "^5.0.0",
6364
"@types/jest": "^28.1.2",

plugin/src/index.ts

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {
2+
AndroidConfig,
3+
ConfigPlugin,
4+
withGradleProperties,
5+
withProjectBuildGradle,
6+
withSettingsGradle,
7+
withStringsXml,
8+
} from 'expo/config-plugins';
9+
10+
const withUnity: ConfigPlugin<{ name?: string }> = (
11+
config,
12+
{ name = 'react-native-unity' } = {}
13+
) => {
14+
config.name = name;
15+
config = withProjectBuildGradleMod(config);
16+
config = withSettingsGradleMod(config);
17+
config = withGradlePropertiesMod(config);
18+
config = withStringsXMLMod(config);
19+
return config;
20+
};
21+
22+
const REPOSITORIES_END_LINE = `maven { url 'https://www.jitpack.io' }`;
23+
24+
const withProjectBuildGradleMod: ConfigPlugin = (config) =>
25+
withProjectBuildGradle(config, (modConfig) => {
26+
if (modConfig.modResults.contents.includes(REPOSITORIES_END_LINE)) {
27+
// use the last known line in expo's build.gradle file to append the newline after
28+
modConfig.modResults.contents = modConfig.modResults.contents.replace(
29+
REPOSITORIES_END_LINE,
30+
REPOSITORIES_END_LINE +
31+
'\nflatDir { dirs "${project(\':unityLibrary\').projectDir}/libs" }\n'
32+
);
33+
} else {
34+
throw new Error(
35+
'Failed to find the end of repositories in the android/build.gradle file`'
36+
);
37+
}
38+
return modConfig;
39+
});
40+
41+
const withSettingsGradleMod: ConfigPlugin = (config) =>
42+
withSettingsGradle(config, (modConfig) => {
43+
modConfig.modResults.contents += `
44+
include ':unityLibrary'
45+
project(':unityLibrary').projectDir=new File('../unity/builds/android/unityLibrary')
46+
`;
47+
return modConfig;
48+
});
49+
50+
const withGradlePropertiesMod: ConfigPlugin = (config) =>
51+
withGradleProperties(config, (modConfig) => {
52+
modConfig.modResults.push({
53+
type: 'property',
54+
key: 'unityStreamingAssets',
55+
value: '.unity3d',
56+
});
57+
return modConfig;
58+
});
59+
60+
// add string
61+
const withStringsXMLMod: ConfigPlugin = (config) =>
62+
withStringsXml(config, (config) => {
63+
config.modResults = AndroidConfig.Strings.setStringItem(
64+
[
65+
{
66+
_: 'Game View',
67+
$: {
68+
name: 'game_view_content_description',
69+
},
70+
},
71+
],
72+
config.modResults
73+
);
74+
return config;
75+
});
76+
77+
export default withUnity;

plugin/tsconfig.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "expo-module-scripts/tsconfig.plugin",
3+
"compilerOptions": {
4+
"outDir": "build",
5+
"rootDir": "src"
6+
},
7+
"include": ["./src"],
8+
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
9+
}

0 commit comments

Comments
 (0)