2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsとp5.jsを使って簡単にクソゲーをWebアプリにして公開してみる

Posted at

p5.jsを使うと簡単にゲームが作れることを知ったので、Next.jsで動かしてみたという備忘録です。

Webエディタが用意されているので、簡単に触ってみるといいかも知れません。:point_down:

前提条件

  • WSL2がインストールされているWindowsまたはLinux環境
  • npmが使えること(Node.jsがインストールされている)

1. Next.js プロジェクトの作成

まず、create-next-app を使って新しいNext.jsのプロジェクトを作成します。

npx create-next-app@latest my-game-app

create-next-app のオプションについて

以下のようなオプションが表示されるので、適宜選択します。

  1. Would you like to use TypeScript with this project?Yes
  2. Would you like to use ESLint with this project?Yes
  3. Would you like to use Tailwind CSS with this project?No(今回は不要)
  4. Would you like to use src/ directory with this project?No
  5. Would you like to use experimental app/ directory with this project?Yes

選択が完了すると、my-game-app というディレクトリにNext.jsプロジェクトが作成されます。

cd my-game-app

npm install react-p5

これでNext.jsとp5.jsが入りました
準備完了です。

2. components フォルダを作成し、p5.tsx を作成

mkdir components
touch components/p5.tsx

p5.tsx にはp5.jsを使った描画処理を記述します。
今回は下記の記事を参考に作ってみました。ありがとうございます:pray:

// components/p5.tsx
"use client";

import dynamic from "next/dynamic";
import p5Types from "p5";

const Sketch = dynamic(() => import('react-p5'), {
    loading: () => null,
    ssr: false
});

const P5Component = () => {
    let basketX: number;
    let basketY: number;
    let basketWidth: number;
    let basketHeight: number;
    let score: number = 0;
    let basketImg: p5Types.Image;
    let acornImg: p5Types.Image;
    let isTouching: boolean = false;
    const acorns: { x: number, y: number }[] = [];

    // 画像などのロードを行う
    const preload = (p5: p5Types) => {
        basketImg = p5.loadImage('/images/basket.png'); // カゴの画像
        acornImg = p5.loadImage('/images/acorn.png'); // 🌰の画像
    };

    // 初期処理
    const setup = (p5: p5Types, canvasParentRef: Element) => {
        p5.createCanvas(p5.windowWidth, p5.windowHeight).parent(canvasParentRef);
        basketWidth = 100;
        basketHeight = 50;
        basketX = p5.windowWidth / 2 - basketWidth / 2;
        basketY = p5.windowHeight - basketHeight - 10;
    };

    // 1フレームごとの処理
    const draw = (p5: p5Types) => {
        p5.background(255, 255, 255);

        // カゴの描画
        p5.image(basketImg, basketX, basketY, basketWidth, basketHeight);

        // 🌰の描画と落下
        for (let i = acorns.length - 1; i >= 0; i--) {
            const acorn = acorns[i];
            acorn.y += 5;
            p5.image(acornImg, acorn.x, acorn.y, 20, 20);

            // カゴに入ったかどうかの判定
            if (acorn.y > basketY && acorn.x > basketX && acorn.x < basketX + basketWidth) {
                acorns.splice(i, 1);
                score++;
            } else if (acorn.y > p5.windowHeight) {
                acorns.splice(i, 1);
            }
        }

        // スコアの表示
        p5.fill(0);
        p5.textSize(32);
        p5.text(`Score: ${score}`, 10, 30);

        // 新しい🌰を追加
        if (p5.frameCount % 15 === 0) {
            acorns.push({ x: p5.random(20, p5.windowWidth - 20), y: 0 });
        }

        // カゴの移動(キーボード操作)
        if (p5.keyIsDown(p5.LEFT_ARROW)) {
            basketX -= 10;
        }
        if (p5.keyIsDown(p5.RIGHT_ARROW)) {
            basketX += 10;
        }

        // カゴの移動(マウス操作)
        if (p5.mouseIsPressed) {
            basketX = p5.mouseX - basketWidth / 2;
        }
    };

    // タッチ操作の開始
    const touchStarted = (p5: p5Types) => {
        isTouching = true;
        return false;
    };

    // タッチ操作の終了
    const touchEnded = (p5: p5Types) => {
        isTouching = false;
        return false;
    };

    // タッチ操作の移動
    const touchMoved = (p5: p5Types) => {
        if (isTouching && p5.touches.length > 0) {
            const touch = p5.touches[0] as Touch;
            basketX = touch.clientX - basketWidth / 2;
        }
        return false;
    };

    // コンポーネントのレスポンシブ化
    const windowResized = (p5: p5Types) => {
        p5.resizeCanvas(p5.windowWidth, p5.windowHeight);
        basketY = p5.windowHeight - basketHeight - 10;
    };

    return (
        <Sketch
            preload={preload}
            setup={setup}
            draw={draw}
            touchStarted={touchStarted}
            touchEnded={touchEnded}
            touchMoved={touchMoved}
            windowResized={windowResized}
        />
    );
};

export default P5Component;

3. app/page.tsx を編集する

次に、app/page.tsx を編集して、p5.tsx を読み込むようにします。

// app/page.tsx
import P5Component from '@/components/p5'

export default function Home() {
  return (
    <>
      <P5Component />
    </>
  )
}

これで、p5.tsxHome コンポーネントで表示する準備が整いました。

4. ローカルで確認する

プロジェクトのルートディレクトリに戻り、開発サーバーを起動します。

npm run dev

ブラウザで http://localhost:3000 にアクセスし、ページが表示されることを確認しましょう。

5. Vercel にデプロイ

Next.jsはVercelとの相性が非常に良いため、簡単にデプロイできます。

Vercel にデプロイする手順

  1. Vercelの公式サイト にアクセスします。
  2. Log in をクリックし、GitHubアカウントなどでログインします。
  3. ログイン後、New Project をクリックします。
  4. GitHubと連携し、デプロイしたいリポジトリを選択します。
  5. 設定を確認し、Deploy ボタンを押します。
  6. 数分でデプロイが完了し、公開URLが発行されます。

このURLを開いて、ゲームがWeb上に公開されたことを確認しましょう。

まとめ

  1. create-next-app で Next.js のプロジェクトを作成
  2. components フォルダを作成し p5.tsx を追加
  3. app/page.tsx を編集して p5.tsx を読み込む
  4. npm run dev でローカル確認
  5. Vercelを使ってデプロイ

これで、簡単にクソゲーをWebアプリにして公開できましたー!🚀

今回作ったGitHubリポジトリ

デプロイしたWebアプリ(クソゲー)

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?