ボスとのバトルフィールドを作ろう
💥 ボスと戦う
前回のレッスンで、シナリオの1と2を実装しました。
✅ シナリオ 1. ユーザーが Web アプリケーションにログインしていない場合
👉 WEBアプリ上に、"Connect Wallet to Get Started" ボタンを表示します。
✅ シナリオ 2. ユーザーは Web アプリケーションにログインしており、かつ NFT キャラクターを持っていない場合
👉 WEBアプリ上に、SelectCharacter コンポーネントを表示します。
これから、ボスとのバトルフィールド「Arena」を作成し、シナリオ3を実装していきます。
🔥 シナリオ 3. ユーザーは Web アプリケーションにログインしており、かつ NFT キャラクターを持っている場合
👉 WEBアプリ上に、「Arena Component」を表示します。
- 「Arena Component」は、プレイヤーがボスと戦う場所です。
まず、ターミナル上でclient/src/Components/Arenaフォルダに移動して、index.jsという名前の新しいファイルを作成しましょう。
ArenaフォルダにはArena.cssファイルが含まれています。
Webアプリケーションの構築が完了したら、CSSのスタイリングを楽しんでください ✨
🏰 Arenaを作成する
次に、client/src/Components/Arena/index.jsを開き、下記のコードを貼り付けましょう。
import React, { useEffect, useState } from "react";
import { ethers } from "ethers";
import { CONTRACT_ADDRESS, transformCharacterData } from "../../constants";
import myEpicGame from "../../utils/MyEpicGame.json";
import "./Arena.css";
// フロントエ ンドにNFTキャラクターを表示するため、characterNFTのメタデータを渡します。
const Arena = ({ characterNFT }) => {
// コントラクトのデータを保有する状態変数を初期化します。
const [gameContract, setGameContract] = useState(null);
// ページがロードされると下記が実行されます。
useEffect(() => {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const gameContract = new ethers.Contract(
CONTRACT_ADDRESS,
myEpicGame.abi,
signer
);
setGameContract(gameContract);
} else {
console.log("Ethereum object not found");
}
}, []);
return (
<div className="arena-container">
{/* ボス */}
<p>ボスを表示します。</p>
{/* NFT キャラクター */}
<p>NFT キャラクターを表示します。</p>
</div>
);
};
export default Arena;
Arenaコンポーネント準備ができたので、App.jsに戻って、シナリオ3を実装していきます。
シナリオ 3. ユーザーは Web アプリケーションにログインしており、かつ NFT キャラクターを持っている場合
👉 WEBアプリ上に、「Arena Component」を表示します。
- 「Arena Component」は、プレイヤーがボスと戦う場所です。
まず、Arenaをインポートするため、App.jsの先頭に、下記を追加しましょう。
import Arena from "./Components/Arena";
次に、renderContent関数を下記のように更新しましょう。
// レンダリングメソッド
const renderContent = () => {
// シナリオ1.
// ユーザーがWEBアプリにログインしていない場合、WEBアプリ上に、"Connect Wallet to Get Started" ボタンを表示します。
if (!currentAccount) {
return (
<div className="connect-wallet-container">
<img src="https://i.imgur.com/TXBQ4cC.png" alt="LUFFY" />
<button
className="cta-button connect-wallet-button"
onClick={connectWalletAction}
>
Connect Wallet to Get Started
</button>
</div>
);
// シナリオ2.
// ユーザーはWEBアプリにログインしており、かつ NFT キャラクターを持っていない場合、WEBアプリ上に、"SelectCharacter Component" を表示します。
} else if (currentAccount && !characterNFT) {
return <SelectCharacter setCharacterNFT={setCharacterNFT} />;
// シナリオ3.
// ユーザーはWEBアプリにログインしており、かつ NFT キャラクターを持っている場合、
// Arena でボスと戦います。
} else if (currentAccount && characterNFT) {
return <Arena characterNFT={characterNFT} />;
}
};
Webアプリケーションを更新すると、「アリーナ」コンポーネントに直接移動します。
フロントエンドが下記のように表示されていれば、ここまでの実装は成功です。

😈 スマートコントラクトからボスを取得する
それでは、Arenaコンポーネントに、ボスのデータを取得していきましょう。
SelectCharacterコンポーネントで、NFTキャラクターのデータを取得した方法と同じ要領で進めていきます。
まず、Arenaコンポーネントの中のconst [gameContract, setGameContract] = useState(null);の直下に下記を追記してください。
// ボスのメタデータを保存する状 態変数を初期化します。
const [boss, setBoss] = useState(null);
// ページがロードされると下記が実行されます。
useEffect(() => {
// コントラクトからボスのメタデータを取得し、bossを設定する非同期関数 fetchBoss を設定します。
const fetchBoss = async () => {
const bossTxn = await gameContract.getBigBoss();
console.log("Boss:", bossTxn);
setBoss(transformCharacterData(bossTxn));
};
if (gameContract) {
// コントラクトの準備ができたら、ボスのメタデータを取得します。
fetchBoss();
}
}, [gameContract]);
上記の実装が完了したら、Webアプリケーション上でConsoleを開いて、ボスのデータが読み込まれていることを確認しましょう。
🙀 ボスをフロントエンドにレンダリングする
まず、Arena/index.jsに向かい、const [boss, setBoss] = useState(null);の直下に下記を追加しましょう。
// NFTキャラクターがボスを攻撃する際に使用する関数を定義します。
const runAttackAction = async () => {};
次に、Arena/index.jsのreturn();の中身を下記のように更新しましょう。
return (
<div className="arena-container">
{/* ボスをレンダリングします */}
{boss && (
<div className="boss-container">
<div className={`boss-content`}>
<h2>🔥 {boss.name} 🔥</h2>
<div className="image-content">
<img src={boss.imageURI} alt={`Boss ${boss.name}`} />
<div className="health-bar">
<progress value={boss.hp} max={boss.maxHp} />
<p>{`${boss.hp} / ${boss.maxHp} HP`}</p>
</div>
</div>
</div>
<div className="attack-container">
<button className="cta-button" onClick={runAttackAction}>
{`💥 Attack ${boss.name}`}
</button>
</div>
</div>
)}
{/* NFT キャラクター */}
<p>NFT キャラクターを表示します。</p>
</div>
);
ローカルサーバーで、Webアプリケーションを開き、下記のようにボスがArenaにレンダリングされていることを確認してください。

🛡 NFT キャラクターをArenaにレンダリングする
ボスとのバトルフィールドであるArenaに、NFTキャラクターをレンダリングしましょう。
Arena/index.jsのreturn();の中身を下記のように更新しましょう。
return (
<div className="arena-container">
{/* ボスをレンダリングします */}
{boss && (
<div className="boss-container">
<div className={`boss-content`}>
<h2>🔥 {boss.name} 🔥</h2>
<div className="image-content">
<img src={boss.imageURI} alt={`Boss ${boss.name}`} />
<div className="health-bar">
<progress value={boss.hp} max={boss.maxHp} />
<p>{`${boss.hp} / ${boss.maxHp} HP`}</p>
</div>
</div>
</div>
<div className="attack-container">
<button className="cta-button" onClick={runAttackAction}>
{`💥 Attack ${boss.name}`}
</button>
</div>
</div>
)}
{/* NFT キャラクター をレンダリングします*/}
{characterNFT && (
<div className="players-container">
<div className="player-container">
<h2>Your Character</h2>
<div className="player">
<div className="image-content">
<h2>{characterNFT.name}</h2>
<img
src={characterNFT.imageURI}
alt={`Character ${characterNFT.name}`}
/>
<div className="health-bar">
<progress value={characterNFT.hp} max={characterNFT.maxHp} />
<p>{`${characterNFT.hp} / ${characterNFT.maxHp} HP`}</p>
</div>
</div>
<div className="stats">
<h4>{`⚔️ Attack Damage: ${characterNFT.attackDamage}`}</h4>
</div>
</div>
</div>
</div>
)}
</div>
);
ローカルサーバーで、Webアプリケーションを開き、下記のようにあなたのNFTキャラクターがArenaにレンダリングされていることを確認してください。
