Skip to content

Commit 43044db

Browse files
author
Philipp Molitor
committed
improve JSDoc and code conciseness
1 parent 5dae45a commit 43044db

File tree

1 file changed

+56
-42
lines changed

1 file changed

+56
-42
lines changed

src/components/UnityRenderer.ts

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,81 @@
11
import '../global';
2-
import {
3-
createElement,
4-
HTMLAttributes,
5-
useLayoutEffect,
6-
useState,
7-
VFC,
8-
} from 'react';
92

10-
import { UnityLoaderService } from '../loader';
3+
import { createElement, HTMLAttributes, useEffect, useState, VFC } from 'react';
114

125
import { UnityContext } from '..';
136

14-
export type UnityRendererProps = HTMLAttributes<
15-
Omit<HTMLCanvasElement, 'ref'>
7+
import { UnityLoaderService } from '../loader';
8+
9+
export type UnityRendererProps = Omit<
10+
HTMLAttributes<HTMLCanvasElement>,
11+
'ref'
1612
> & {
1713
context: UnityContext;
1814
onUnityProgressChange?: (progress: number) => void;
1915
onUnityReadyStateChange?: (ready: boolean) => void;
2016
};
2117

18+
/**
19+
* A components that renders a Unity WebGL build from a given configuration
20+
* context. Allows bidirectional communication and loading progress tracking.
21+
*
22+
* @param {UnityRendererProps} props Configurtion context, Unity-specific
23+
* callback handlers and default React props for a `HTMLCanvasElement`.
24+
* Note that `ref` is not available due to internal use.
25+
* @returns {(JSX.Element | null)} A `JSX.Element` containing the renderer,
26+
* or `null` if not initialized yet.
27+
*/
2228
export const UnityRenderer: VFC<UnityRendererProps> = ({
2329
context,
2430
onUnityProgressChange,
2531
onUnityReadyStateChange,
2632
...canvasProps
27-
}): JSX.Element | null => {
28-
const [lastState, setLastState] = useState<boolean>(false);
33+
}: UnityRendererProps): JSX.Element | null => {
34+
const [service] = useState<UnityLoaderService>(new UnityLoaderService());
35+
36+
// We cannot actually render the `HTMLCanvasElement`, so we need the `ref`
37+
// for Unity and a `JSX.Element` for React rendering.
2938
const [canvas, setCanvas] = useState<JSX.Element>();
3039
const [renderer, setRenderer] = useState<HTMLCanvasElement>();
31-
const [service] = useState<UnityLoaderService>(new UnityLoaderService());
40+
41+
// This is the last state the game was in, either ready or not ready.
42+
// It is used to trigger `onUnityReadyStateChange` reliably.
43+
const [lastReadyState, setLastReadyState] = useState<boolean>(false);
3244

3345
/**
3446
* The callback which will be called from the `unityInstance` while
3547
* the game is loading.
36-
* @param {number} progress
48+
* @param {number} progress The progress ranging from `0.0` to `1.0`
3749
*/
3850
function onUnityProgress(progress: number): void {
3951
if (onUnityProgressChange) onUnityProgressChange(progress);
4052

4153
// if loading has completed, change ready state
42-
if (lastState === false && progress >= 1.0) {
54+
if (lastReadyState === false && progress >= 1.0) {
4355
if (onUnityReadyStateChange) onUnityReadyStateChange(true);
44-
setLastState(true);
45-
} else if (lastState === true) {
56+
setLastReadyState(true);
57+
} else if (lastReadyState === true) {
4658
// if ready state changed back to false, trigger again
4759
if (onUnityReadyStateChange) onUnityReadyStateChange(false);
48-
setLastState(false);
60+
setLastReadyState(false);
4961
}
5062
}
5163

52-
/**
53-
* Creates the `<canvas>` element in which the unity build will be rendered.
54-
*/
55-
function createRendererCanvas(): void {
56-
const c = createElement('canvas', {
57-
...canvasProps,
58-
ref: (r: HTMLCanvasElement) => setRenderer(r),
59-
});
60-
setCanvas(c);
61-
}
62-
6364
/**
6465
* Uses the native Unity loader method to attach the Unity instance to
6566
* the renderer `canvas`.
67+
*
68+
* @returns {Promise<void>} A Promise resolving on successful mount of the
69+
* Unity instance.
6670
*/
6771
async function mountUnityInstance(): Promise<void> {
6872
if (!renderer) return;
6973

74+
// get the current loader configuration from the UnityContext
7075
const c = context.getConfig();
7176

72-
// attach
77+
// attach Unity's native JavaScript loader
7378
await service.attachLoader(c.loaderUrl);
74-
7579
const nativeUnityInstance = await window.createUnityInstance(
7680
renderer,
7781
{
@@ -86,30 +90,40 @@ export const UnityRenderer: VFC<UnityRendererProps> = ({
8690
(p) => onUnityProgress(p)
8791
);
8892

93+
// set the instance for further JavaScript <--> Unity communication
8994
context.setInstance(nativeUnityInstance);
9095
}
9196

97+
// on canvas change
98+
useEffect(() => {
99+
if (renderer)
100+
mountUnityInstance().catch((e) => {
101+
// eslint-disable-next-line no-console
102+
console.error('failed to mount unity instance: ', e);
103+
});
104+
}, [renderer]);
105+
92106
// on mount
93-
useLayoutEffect(() => {
94-
createRendererCanvas();
107+
useEffect(() => {
108+
// create the renderer and let the ref callback set its handle
109+
setCanvas(
110+
createElement('canvas', {
111+
ref: (r: HTMLCanvasElement) => setRenderer(r),
112+
...canvasProps,
113+
})
114+
);
95115

96116
// on unmount
97117
return () => {
98118
context.shutdown(() => {
119+
// remove the loader script from the DOM
99120
service.detachLoader();
121+
// reset progress / ready state
122+
if (onUnityProgressChange) onUnityProgressChange(0);
100123
if (onUnityReadyStateChange) onUnityReadyStateChange(false);
101124
});
102125
};
103126
}, []);
104127

105-
// on canvas change
106-
useLayoutEffect(() => {
107-
if (renderer)
108-
mountUnityInstance().catch((e) => {
109-
// eslint-disable-next-line no-console
110-
console.error('failed to mount unity instance: ', e);
111-
});
112-
}, [renderer]);
113-
114128
return canvas || null;
115129
};

0 commit comments

Comments
 (0)