-
-
Notifications
You must be signed in to change notification settings - Fork 1
interface
Pixi’VN offers the possibility of adding an HTML Element with the same dimensions as the PixiJS Canvas to add an UI with JavaScript frameworks.
By "UI" is meant the elements that are above the canvas, such as buttons, forms, etc.
This allows the use of systems such as React, Vue, Angular, etc. to create much more complex UI screens with excellent performance.
UI and canvas are two different things. The UI is above the canvas and is used to create buttons, forms, etc. The canvas is used to display images, videos, etc.
All canvas information is included in saves and Pixi’VN manages going back and forth between the different steps. The UI is not included in the saves and is not managed by Pixi’VN, so you have to manage it yourself saving information you care about in game storage or browser storage.
In the canvas you can add elements during each step. In the UI you can't do that, you can create several "screens" and navigate between them.
In the canvas you can only add PixiJS elements, they are usually composed of images and are very simple. In the UI you can add any HTML element or use any UI component library, so you can create much more complex interfaces.
To switch between UI screens (without interrupting the canvas), you can use popups and modals, or navigate between different paths/routes.
The URL Path is the part of the URL that comes after the domain. For example, in the URL https://example.com/path/to/page
, the path is /path/to/page
.
A routering system can be used to manage navigation between URL Paths. For example you can use:
By default, all HTML elements of the UI have the pointer-events: none
style.
The reason is that because the html UI is above the canvas, all clicks are intercepted by the UI and not by the canvas.
So you must set the pointer-events: auto
style only for the elements (example a button, a form, etc...) that you want to interact with the user.
What is an Atom? An Atom (or Atomics) is a JavaScript object which gives atomic tasks to proceed as static strategies. Much the same as the strategies for Math object, the techniques, and properties of Atomics are additionally static. Atomics are utilized with SharedArrayBuffer objects.
There are more npm packages that can be used to manage the UI with the game storage, such as: Recoil, Redux, MobX, etc. We will use Recoil because it has "Selectors" which is right for us.
The purpose of the Atom + Selector will be to set the Atom and game storage to a value changed by the UI, and update the Atom when the value in game storage is updated.
Here is an example:
In our example we would have a variable text
saved in the game storage, this variable will be updated either by an input in the UI or by a label step.
Taking into account that a storage variable can only be changed during a next step, go back, run label or loading a save (outside the interface), we will create an Atom called "reloadInterfaceDataEventAtom" that will be updated after each next step, go back, run label or loading a save. reloadInterfaceDataEventAtom
will be used to trigger the update of olther Atoms.
// reloadInterfaceDataEventAtom.ts
import { atom } from "recoil";
export const reloadInterfaceDataEventAtom = atom<number>({
key: 'reloadInterfaceDataEventAtom',
default: 0,
});
We will create a Selector called textSelector
that will be used to set and get the value of the text
variable in the game storage.
// textSelectorState.ts
import { storage } from "@drincs/pixi-vn";
import { atom, selector } from "recoil";
import { reloadInterfaceDataEventAtom } from "./reloadInterfaceDataEventAtom";
// questo atom attiverà l'aggiornamento di textSelectorState quando modificherò il valore di textSelectorState
const textAtom = atom<string>({
key: 'textAtom',
default: "",
});
export const textSelectorState = selector<string>({
key: 'textSelector',
get: ({ get }) => {
// This will trigger the update of the Atom when the value in game storage is updated
get(reloadInterfaceDataEventAtom)
get(textAtom)
return storage.getVariable<string>("text") || "";
},
set: ({ set }, newValue) => {
set(textAtom, newValue);
storage.setVariable("text", newValue as string);
},
});
Remember to update the reloadInterfaceDataEventAtom
after each next step, go back or loading a save.
Even after use run label out of the step label.
This seems like a very time consuming process, but in reality if you have designed your game well or used a template, you will only need to update the Atom in one place.
import { useSetRecoilState } from 'recoil';
import { reloadInterfaceDataEventAtom } from './reloadInterfaceDataEventAtom';
import { narration } from '@drincs/pixi-vn'
const notifyReloadInterfaceDataEvent = useSetRecoilState(reloadInterfaceDataEventAtom);
narration.goNext({})
.then((result) => {
notifyReloadInterfaceDataEvent((value) => value + 1);
});
narration.goBack({})
.then((result) => {
notifyReloadInterfaceDataEvent((value) => value + 1);
});
loadSaveJson(jsonString, navigate)
.then(() => {
notifyReloadInterfaceDataEvent((value) => value + 1);
})
// only if you are not in a label step
narration.callLabel("myLabel", {})
.then((result) => {
notifyReloadInterfaceDataEvent((value) => value + 1);
});
Ok now we have the Atom and Selector, we can use the textSelector
in the UI.
// react example
import { useRecoilState } from 'recoil';
import { textSelectorState } from './textSelectorState';
import { Input } from '@mui/joy';
export function MyComponent() {
const [text, setText] = useRecoilState(textSelectorState);
return (
<Input
sx={{
pointerEvents: "auto",
}}
value={text}
onChange={(e) => setText(e.target.value)}
/>
);
}