Lattice Workshop - Step 6.6

Add React UI

Next step

To demonstrate the usage of React for overlays and UI, we'll add a simple full screen modal that pops up every time you lose an entity or lose the game.

All components and queries from @latticexyz/mobx-ecs are deeply integrated with mobx. Therefore we can use the mobx-react package to create React components that reactively update based on ECS component values.

For this game, we'll use a defineExitQuery and reaction to set a React state variable every time the OwnedBy component value of an entity that used to be owned by the player is removed. If the player doesn't own an entity with Heart component anymore, the game is over.

Files changed (1) hide show
  1. client/src/react/App.tsx +34 -3
client/src/react/App.tsx CHANGED
@@ -1,12 +1,43 @@
1
- import React from "react";
1
+ import React, { useEffect, useState } from "react";
2
2
  import { Context } from "../types";
3
3
  import styled from "styled-components";
4
4
  import { observer } from "mobx-react";
5
+ import { reaction } from "mobx";
6
+ import { defineExitQuery, defineQuery, exists, Has, HasValue } from "@latticexyz/mobx-ecs";
7
+ import { Modal } from "./Modal";
8
+
9
+ export const App: React.FC<{ context: Context }> = observer(({ context }) => {
10
+ const [modalText, setModalText] = useState("");
11
+
12
+ useEffect(() => {
13
+ const {
14
+ world,
15
+ components: { OwnedBy, Heart },
16
+ } = context;
17
+ const lostEntityQuery = defineExitQuery(world, [HasValue(OwnedBy, { value: context.signer.address })]);
18
+ const ownedByQuery = defineQuery([HasValue(OwnedBy, { value: context.signer.address })]);
19
+ return reaction(
20
+ () => lostEntityQuery.get(),
21
+ (lostEntities) => {
22
+ if (lostEntities.size == 0) return;
23
+ // If the player doesn't own a heart anymore, the game is lost
24
+ if (
25
+ ownedByQuery.get().size > 0 &&
26
+ !exists([Has(Heart), HasValue(OwnedBy, { value: context.signer.address })])
27
+ ) {
28
+ setModalText("Game over");
29
+ } else {
30
+ // Flash a quick modal if the player lost an entity
31
+ setModalText("Entity lost");
32
+ setTimeout(() => setModalText(""), 300);
33
+ }
34
+ }
35
+ );
36
+ }, []);
5
37
 
6
- export const App: React.FC<{ context: Context }> = observer(() => {
7
38
  return (
8
39
  <Container>
9
- <Inner></Inner>
40
+ <Inner>{modalText && <Modal text={modalText} fontSize={"80px"} bgColor={"rgb(255,0,0,0.2)"} />}</Inner>
10
41
  </Container>
11
42
  );
12
43
  });