Tutorials
2. Show player at position, click to move

2. Show player at position, click to move

The component and system we just created will become much more real once we can see them in action. Let's set up the client to use them.

Client-side component

Before we can use the new Position component on the client, we need to define it in our components.ts file. This will allow MUD's network layer to decode component value updates in the contract for us to query in the client.

(We can remove the Counter component for now, too.)

import { defineCoordComponent } from "@latticexyz/std-client";
import { world } from "./world";
 
export const contractComponents = {
  Position: defineCoordComponent(world, {
    metadata: {
      contractId: "component.Position",
    },
  }),
};

Render the world

Let's create a new React component called GameBoard that represents the world. We'll start by rendering a simple 20×20 grid, filled with lime green as the "grass".

(The CSS class names come from Tailwind. Don't worry if they don't make much sense. It just lets us write styled markup that is easy to copy-and-paste.)

export const GameBoard = () => {
  const rows = new Array(20).fill(0).map((_, i) => i);
  const columns = new Array(20).fill(0).map((_, i) => i);
 
  return (
    <div className="inline-grid p-2 bg-lime-500">
      {rows.map((y) =>
        columns.map((x) => (
          <div
            key={`${x},${y}`}
            className="w-8 h-8"
            style={{
              gridColumn: x + 1,
              gridRow: y + 1,
            }}
          ></div>
        ))
      )}
    </div>
  );
};

Then render the new GameBoard component from our base App component, replacing the previous counter increment logic.

import { GameBoard } from "./GameBoard";
 
export const App = () => {
  return (
    <div className="w-screen h-screen flex items-center justify-center">
      <GameBoard />
    </div>
  );
};

And a small tweak to index.html to give the page a black background and make it feel more like a game console.

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>a minimal MUD client</title>
  </head>
  <body class="bg-black text-white">
    <div id="react-root"></div>
    <script type="module" src="/src/index.tsx"></script>
  </body>

Move the player

There's no player on the game board because it doesn't have a position yet. Wire up a click handler on each tile to set the player position (using our Move system). Once a tile is clicked, the player appears and we can continue moving it around the map by clicking other tiles.

import { useComponentValue } from "@latticexyz/react";
import { useMUD } from "./MUDContext";
 
export const GameBoard = () => {
  const rows = new Array(20).fill(0).map((_, i) => i);
  const columns = new Array(20).fill(0).map((_, i) => i);
 
  const {
    components: { Position },
    systems,
    playerEntity,
  } = useMUD();
 
  const playerPosition = useComponentValue(Position, playerEntity);
 
  return (
    <div className="inline-grid p-2 bg-lime-500">
      {rows.map((y) =>
        columns.map((x) => (
          <div
            key={`${x},${y}`}
            className="w-8 h-8 flex items-center justify-center cursor-pointer hover:ring"
            style={{
              gridColumn: x + 1,
              gridRow: y + 1,
            }}
            onClick={(event) => {
              event.preventDefault();
              systems["system.Move"].executeTyped({ x, y });
            }}
          >
            {playerPosition?.x === x && playerPosition?.y === y ? <>🤠</> : null}
          </div>
        ))
      )}
    </div>
  );
};