Skip to content

Commit 3191dae

Browse files
author
Philipp Molitor
authored
release 2020.2.0
release 2020.2.0
2 parents 29ea327 + 991786f commit 3191dae

File tree

17 files changed

+824
-332
lines changed

17 files changed

+824
-332
lines changed

.eslintrc.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ module.exports = {
33
browser: true,
44
es2021: true,
55
},
6-
extends: [
7-
'plugin:react/recommended',
8-
'airbnb-typescript',
9-
'prettier',
10-
'prettier/@typescript-eslint',
11-
'prettier/react',
12-
],
6+
extends: ['plugin:react/recommended', 'airbnb-typescript', 'prettier'],
137
plugins: [
148
'react',
159
'@typescript-eslint',
@@ -49,6 +43,7 @@ module.exports = {
4943
'max-classes-per-file': 'off',
5044
'import/prefer-default-export': 'off',
5145
'class-methods-use-this': 'off',
46+
'no-underscore-dangle': ['error', { allow: ['__UnityBridgeRegistry__'] }],
5247
'comma-dangle': [
5348
'error',
5449
{

.github/workflows/ci-dev.yaml

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build/CI
1+
name: CI
22

33
on:
44
push:
@@ -9,24 +9,31 @@ on:
99
jobs:
1010
test:
1111
runs-on: ubuntu-latest
12-
strategy:
13-
matrix:
14-
node-version: [14.x]
1512
steps:
1613
- name: checkout
1714
uses: actions/checkout@v2
1815

19-
- name: install node ${{ matrix.node-version }}
16+
- name: install node
2017
uses: actions/setup-node@v1
2118
with:
22-
node-version: ${{ matrix.node-version }}
19+
node-version: 14.x
2320

2421
- name: cache node_modules
2522
uses: actions/cache@v2
2623
with:
2724
path: '**/node_modules'
2825
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
2926

30-
- run: yarn --frozen-lockfile
31-
- run: yarn test
32-
- run: yarn build
27+
- name: install dependencies
28+
run: yarn --frozen-lockfile
29+
30+
- name: execute unit tests
31+
run: yarn test --verbose
32+
33+
- name: generate codecov report
34+
uses: codecov/codecov-action@v1
35+
with:
36+
fail_ci_if_error: true
37+
38+
- name: build dist
39+
run: yarn build

.github/workflows/release-npmjs.yaml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@ on:
88
jobs:
99
publish:
1010
runs-on: ubuntu-latest
11-
strategy:
12-
matrix:
13-
node-version: [14.x]
1411
steps:
1512
- name: checkout
1613
uses: actions/checkout@v2
1714

1815
- name: install node ${{ matrix.node-version }}
1916
uses: actions/setup-node@v1
2017
with:
21-
node-version: ${{ matrix.node-version }}
18+
node-version: 14.x
2219
registry-url: 'https://registry.npmjs.org'
2320

2421
- name: cache node_modules
@@ -28,7 +25,7 @@ jobs:
2825
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
2926

3027
- run: yarn --frozen-lockfile
31-
- run: yarn test
28+
- run: yarn test --verbose
3229
- run: yarn build
3330
- run: yarn publish --access public
3431
env:

.vscode/settings.json

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"editor.autoClosingBrackets": "always",
2+
"editor.autoClosingBrackets": "languageDefined",
33
"editor.linkedEditing": true,
44
"editor.semanticHighlighting.enabled": true,
55
"editor.codeActionsOnSave": {
@@ -8,13 +8,14 @@
88
"editor.quickSuggestions": {
99
"other": true,
1010
"comments": false,
11-
"strings": true
11+
"strings": false
1212
},
1313
"typescript.updateImportsOnFileMove.enabled": "always",
1414
"eslint.validate": [
1515
"html",
1616
"javascript",
1717
"javascriptreact",
18+
"typescript",
1819
"typescriptreact"
1920
],
2021
"[javascript]": {
@@ -25,14 +26,6 @@
2526
"source.fixAll.eslint": true
2627
}
2728
},
28-
"[javascriptreact]": {
29-
"editor.tabSize": 2,
30-
"editor.detectIndentation": true,
31-
"editor.defaultFormatter": "esbenp.prettier-vscode",
32-
"editor.codeActionsOnSave": {
33-
"source.fixAll.eslint": true
34-
}
35-
},
3629
"[typescript]": {
3730
"editor.tabSize": 2,
3831
"editor.detectIndentation": true,
@@ -57,31 +50,11 @@
5750
"source.fixAll.eslint": true
5851
}
5952
},
60-
"[css]": {
61-
"editor.tabSize": 2,
62-
"editor.detectIndentation": true,
63-
"editor.defaultFormatter": "esbenp.prettier-vscode"
64-
},
65-
"[less]": {
66-
"editor.tabSize": 2,
67-
"editor.detectIndentation": true,
68-
"editor.defaultFormatter": "esbenp.prettier-vscode"
69-
},
70-
"[scss]": {
71-
"editor.tabSize": 2,
72-
"editor.detectIndentation": true,
73-
"editor.defaultFormatter": "esbenp.prettier-vscode"
74-
},
7553
"[json]": {
7654
"editor.tabSize": 2,
7755
"editor.detectIndentation": true,
7856
"editor.defaultFormatter": "esbenp.prettier-vscode"
7957
},
80-
"[jsonc]": {
81-
"editor.tabSize": 2,
82-
"editor.detectIndentation": true,
83-
"editor.defaultFormatter": "esbenp.prettier-vscode"
84-
},
8558
"[yml]": {
8659
"editor.tabSize": 2,
8760
"editor.detectIndentation": true,

README.md

Lines changed: 103 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,29 @@
22

33
<p align="center">
44

5-
<img src="https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/ci-dev.yaml/badge.svg?branch=dev">
6-
<img src="https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/release-npmjs.yaml/badge.svg?event=release">
5+
<a href="https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/ci-dev.yaml">
6+
<img src="https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/ci-dev.yaml/badge.svg?branch=dev">
7+
</a>
8+
<a href="https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/release-npmjs.yaml">
9+
<img src="https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/release-npmjs.yaml/badge.svg">
10+
</a>
11+
12+
<a href="https://codecov.io/gh/PhilippMolitor/react-unity-renderer">
13+
<img src="https://codecov.io/gh/PhilippMolitor/react-unity-renderer/branch/dev/graph/badge.svg?token=4D72B9VWYK"/>
14+
</a>
15+
716
<img src="https://img.shields.io/npm/l/react-unity-renderer">
8-
<img src="https://img.shields.io/npm/dw/react-unity-renderer">
917
<img src="https://img.shields.io/github/stars/PhilippMolitor/react-unity-renderer">
10-
<img src="https://img.shields.io/npm/v/react-unity-renderer">
11-
<img src="https://img.shields.io/bundlephobia/minzip/react-unity-renderer">
12-
13-
</p>
1418

15-
> This project is heavily inspired by [react-unity-webgl](https://github.com/elraccoone/react-unity-webgl) made by Jeffrey Lanters.
16-
> This is a modernized, `FunctionComponent` based interpretation of his ideas.
19+
<a href="https://npmjs.com/package/react-unity-renderer">
20+
<img src="https://img.shields.io/npm/dw/react-unity-renderer">
21+
<img src="https://img.shields.io/npm/v/react-unity-renderer">
22+
<img src="https://img.shields.io/bundlephobia/minzip/react-unity-renderer">
23+
</a>
1724

18-
## Introduction
25+
</p>
1926

20-
TODO
27+
> This project is heavily inspired by [react-unity-webgl](https://github.com/elraccoone/react-unity-webgl) made by Jeffrey Lanters. This implementation uses function components + hooks, is getting tested continously and has strict linting and formatting rules which are always enforced.
2128
2229
## Installation
2330

@@ -49,7 +56,7 @@ import {
4956
UnityContext,
5057
UnityRenderer,
5158
UnityLoaderConfig,
52-
} from 'react-unity-webgl';
59+
} from 'react-unity-renderer';
5360

5461
// get those URLs from your Unity WebGL build.
5562
// you *could* put a JSON in your WebGL template containing this information
@@ -66,22 +73,16 @@ const config: UnityLoaderConfig = {
6673
companyName: '',
6774
productName: '',
6875
productVersion: '',
69-
modules: {},
7076
};
7177

7278
export const UnityGameComponent: VFC = (): JSX.Element => {
73-
// your need to construct a config or pass it from the props
79+
// You need to construct a config or pass it from the props:
7480
const [ctx] = useState<UnityContext>(new UnityContext(config));
7581

76-
// You can keep track of the game progress and ready state like this.
82+
// Keep track of the game progress and ready state like this:
7783
const [progress, setProgress] = useState<number>(0);
7884
const [ready, setReady] = useState<boolean>(false);
7985

80-
// Attach some event handlers to the context
81-
useEffect(() => {
82-
ctx.on('message', (m: string) => console.log(message));
83-
}, []);
84-
8586
return (
8687
<UnityRenderer
8788
context={ctx}
@@ -98,6 +99,8 @@ export const UnityGameComponent: VFC = (): JSX.Element => {
9899
};
99100
```
100101

102+
:warning: It is recommended to store the `UnityContext`, as well as the progress, ready and error states in a global state. This way you can keep track of the game state in every part of your application. Consider [zustand](https://github.com/pmndrs/zustand) as a lightweight alternative to Redux, MobX & co., as it has every feature needed for this use case and takes way less effort to implement.
103+
101104
## Mitigating the "keyboard capturing issue"
102105

103106
By default, Unity WebGL builds capture the keyboard as soon as they are loaded. This means that all keyboard input on the website is captured by the game, and rendering all `<input>`, `<textarea>` and similar input methods useless.
@@ -189,24 +192,93 @@ export async function fetchLoaderConfig(
189192

190193
You can then use it to construct a `UnityContext` and pass this context to your `UnityRenderer` via the `context` prop.
191194

192-
## Sending events from Unity
195+
## Receiving events from Unity
196+
197+
### On the Unity side
193198

194-
In order to send events from Unity to the react application, use the global method for that in your `*.jslib` mapping file:
199+
In order to send events from Unity to the React application, use the global method for that in your `*.jslib` mapping file:
195200

196201
```javascript
197202
mergeInto(LibraryManager.library, {
198-
RunSomeActionInJavaScript: function (message, number) {
203+
RunSomeActionInJavaScript: function (message, counter) {
199204
// surround with try/catch to make unity not crash in case the method is
200205
// not defined in the global scope yet
201206
try {
202-
window.UnityBridge('event-name')(Pointer_stringify(message), number);
207+
const messageString = Pointer_stringify(message);
208+
209+
// UnityBridge(event: string) returns a callback that calls
210+
// every registered event handler with the provided arguments.
211+
// It also handles unregistered events with a warning!
212+
window.UnityBridge('event-name')(messageString, number);
203213
} catch (e) {}
204214
},
205215
});
206216
```
207217

208218
If the event name has no registered event handlers, the `UnityBridge(event: string)` function will log a warning via `console.warn(...)`.
209219

220+
:warning: Please note that returning values from the `UnityBridge()` method is not supported, as it may call multiple event handlers internally from different `UnityContext`s that are listening for a certain event, e.g. when having two or more renderers in your application. The preferred way to handle this is to emit a message to the correct Unity instance, which this library also supports. This also helps making the communication paths simpler: **Events only go from Unity to JavaScript, Messages only go from JavaScript to Unity.**
221+
222+
### On the React side
223+
224+
```tsx
225+
import { VFC, useState } from 'react';
226+
import { UnityContext, UnityRenderer } from 'react-unity-renderer';
227+
228+
export const UnityGameComponent: VFC = (): JSX.Element => {
229+
const [ctx] = useState<UnityContext>(new UnityContext({ ... }));
230+
231+
// Register your handlers (make sure your context is valid!)
232+
useEffect(() => {
233+
// No context, no handlers!
234+
if(!ctx) return;
235+
236+
ctx.on('message', (m: string) => console.log(message));
237+
ctx.on('other-message', (n: number) => console.log(message));
238+
239+
// You can also unregister event handlers again!
240+
ctx.off('other-message');
241+
}, [ctx]);
242+
243+
return (
244+
<UnityRenderer context={ctx} />
245+
);
246+
};
247+
248+
```
249+
250+
## Emitting messages to Unity
251+
252+
While events are a way to handle actions that were initiated in the Unity game,
253+
messages are a way to communicate the other way, from JavaScript to Unity.
254+
255+
Messages are emitted from the `UnityContext`, the API for emitting then is the same as in the Unity WebGL documentation:
256+
257+
```tsx
258+
import { VFC, useState } from 'react';
259+
import { UnityContext, UnityRenderer } from 'react-unity-renderer';
260+
261+
export const UnityGameComponent: VFC = (): JSX.Element => {
262+
const [ctx] = useState<UnityContext>(new UnityContext({ ... }));
263+
264+
const [ready, setReady] = useState<boolean>(false);
265+
266+
// Listen for the Unity instance to be ready
267+
useEffect(() => {
268+
if(ready === true) {
269+
ctx.emit('GameObjectName', 'ScriptMethodName', 'StringOrNumberArgument');
270+
}
271+
}, [ready]);
272+
273+
return (
274+
<UnityRenderer
275+
context={ctx}
276+
onUnityReadyStateChange={(s) => setReady(s)}
277+
/>
278+
);
279+
};
280+
```
281+
210282
## Module augmentation
211283

212284
Take the following example:
@@ -227,23 +299,24 @@ In order to make use of TypeScript to its fullest extent, you can augment an Int
227299
Put this either in a file importing `react-unity-renderer` or create a new `unity.d.ts` somewhere in your `src` or (if you have that) `typings` directory:
228300

229301
```typescript
230-
// must be imported, else the module will be redefined,
231-
// and this causes all sorts of errors.
232-
import 'react-unity-renderer';
302+
// The "{} from" part just imports the TypeScript definitions, so
303+
// we do not re-define the whole module, but just augment it.
304+
import {} from 'react-unity-renderer';
233305

234306
// module augmentation
235307
declare module 'react-unity-renderer' {
236308
// this is the interface providing autocompletion
237309
interface EventSignatures {
238310
// "info" is the event name
239-
// the type on the right side is anything that would match TypeScript's
240-
// Parameters<> helper type
311+
// The type on the right side is anything that would match TypeScript's
312+
// Parameters<> helper type.
241313
info: [message: string];
242314

243315
// also possible:
244316
info: [string];
317+
// Note that all parameter names are just labels, so they are fully optional.
318+
// Though, they are displayed when autocompleting, so labels are quite helpful here.
245319
'some-event': [number, debug: string];
246-
// note that all parametrs names are just labels, so they are fully optional.
247320
}
248321
}
249322
```

0 commit comments

Comments
 (0)