見出し画像

[WeeklyゲームDIY道:第0回] - サクッとWebゲームが作れる環境をフルスクラッチで作ろう!

新年だから、ってわけではないんですが、急に人類普遍の欲求「ゲーム作りてえ欲」を満たしたくなりまして。

とはいえ、なんかルールを課して習慣化しないと、またいつものようにずるずるとなんもしねえな、というのと、最近AI周りの新しい情報に振り回されてあんましまともなアウトプット出せてないぞという焦りもあり。

なので、
週いち、3時間以内にアイディア出しからリリースまで持っていく
をゴールとし、毎週新作を開発公開しようぜというチャレンジ企画をやっていこうと思います!

今年40だからね。ここで止まったら人間としての終わり、という確信めいたものも芽生えてきましたので、今日は初回ですから環境構築からちくちく始めていきましょうかと思います。

環境構築アイディア

サービスのアイディアはあるんだけど、まず動くとこまで持ってくのが地味に大変なんですよソフトウェア開発って、というのはまあ全プログラマのあるあるですよね?

毎週やるなら、その辺ある程度すっ飛ばせるようにしとかないと多分すぐ零日坊主になるので、ちょこちょこっと最低限の準備だけしておきます。

とはいえ、40のおじさんが手持ちの武器だけブンブン振るっても人生になんの進展もありませんので、ちょいちょい使ったことない技術にも触れていこうかなと。この辺のモチベとスピード感のバランスは未来永劫の課題ですね。

インフラ

一応出自はUnityエンジニアですんで、マジでゲーム作ろうと思ったらそこら辺なんですが「ちょちょっとアイディア試す」には、若干ワタクシには重いんですよね。絵が描けたり3Dモデルゴリゴリいける方には、Unity最強だと思います。が「ただ点を出す」とかが地味にめんどい。

諸々鑑みて、今回はWebで行きます。バズったらUnityなりUnrealなりに移植すればいいじゃない。

あと、しばらくはBackendでの処理はなしでお願いします。ブラウザのキャッシュ飛ばしたらリセットです。時代はエッジコンピューティングですからね。
AI系のAPIとか使いたくなったら考えようかな。

この辺の組み合わせで行こうかな

使ったことないやつでいうと、

  • preact

  • web worker

  • Processing (p5js)

  • Threejs

  • wasm

この辺は触ってみたい。ProcessingとかThreejsとかグラフィック系はあんまり調べてないのでまだ使えるのかわからん。
Web workerとかBrowserでのマルチスレッディングは触ったことがないんですが、昔react一発でゲーム作ったらそれはそれは激オモくんだったので、ゲーム開発Webでやるなら必須では?

あと、どうせバックグラウンド実行でbundleからソースコード分離しちゃうならwasmもやってみたいよね。Rustとかで。
週一でサクッとというコンセプトに早くも暗雲垂れ込めてますが、これもモチベ管理の一環なわけですね(共感の嵐)。

環境構築やるぞ

構築したテンプレート環境はこちらで公開しておりますのでご自由にご利用ください。変な動作不良あればPR大歓迎です🙏

結構細かく何をやったか書いていきますので、結論だけ知りたい方は上記repositoryをチェックアウトいただければ事足ります、というか今日の記事全部読まなくていいです。

レポジトリ作り

兎にも角にも猫も杓子もまずはこれ。僕はnpm派です。

git init
npm init

あと地味な話ですが、nodeのバージョンが混乱してハマることが結構ありますので、nodeのバージョンマネージャ導入しておきましょう。
n を使うことが多かったのですが、Voltaというのが最近のイケてるバージョンマネージャぽいので、これを入れます。

curl https://get.volta.sh | bash

# zsh: command not found: volta とか出た場合はパス通しましょう
echo 'VOLTA_HOME=$HOME/.volta' >> ~/.zshrc
echo 'export PATH=$VOLTA_HOME/bin:$PATH' >> ~/.zshrc
source ~/.zshrc

# チェック
volta -v
# 現時点LTSをインストール
volta pin node@20

volta pin すると、package.jsonにバージョンが書き込まれます。

Typescript環境構築 & エディタ周り

気持ちよく書けるってすごく大事なんですよ。特に私のような静的型付け原理主義おじさんにとっては、型補完とlintは命の次に大切です。ここら辺は普通に宗教戦争の範疇なので、異論は受け付けません。

というわけで、まずは気持ちよくTypescriptを書けるようになるまでやりましょう。

VS Codeの設定

今回は一人なのでどっちでもいいですが、workspaceの設定として持ち回るのが吉です。

// .vscode/setting.json

{
  "editor.codeActionsOnSave": {
    "source.fixAll": true
  },
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "eslint.format.enable": false,
  "editor.formatOnSave": true,
  "editor.lineNumbers": "on",
  "editor.rulers": [160],
  "editor.wordWrap": "on",
  "files.insertFinalNewline": true,
  "files.trimTrailingWhitespace": true,
  "npm.packageManager": "npm",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "editor.insertSpaces": true,
  "editor.tabSize": 2,
  "javascript.validate.enable": false,
  "typescript.tsdk": "node_modules/typescript/lib"
}
// .vscode/extensions.json

{
  // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
  // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp

  // List of extensions which should be recommended for users of this workspace.
  "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"],
  // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
  "unwantedRecommendations": []
}

Typescriptのインストール

Nodeなのかブラウザなのかで微妙に設定変わりますので注意です。Web workerの取り扱いはまだ謎なので、ブラウザ用設定をまずやります。

数多の先人のお知恵をお借りしつつ、

npm i -D typescript
npx tsc --init
// tsconfig.json

{
  "compilerOptions": {
    "target": "ES2021",
    "module": "ESNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "strict": true
  }
}

デフォルトで吐き出されるtsconfig.jsonから、

  • target: "ES2021", # 書き換え

  • "module": "ESNext", # 書き換え

  • "moduleResolution": "NodeNext", # 追記

  • "outDir": './dist' # 追記

という塩梅です。ここまでで、型チェックはすでに行われる状態になっていると思われます。

適当にsrc/index.tsとか作ってみる
# ここまでのフォルダ構成

.
├── .vscode
│   ├── extensions.json
│   └── settings.json
├── node_modules
│   └── typescript
├── package-lock.json
├── package.json
├── src
│   └── index.ts
└── tsconfig.json

lint/formatterの設定

すでにvscodeのsettingsに書かれているように、prittierとeslintの組み合わせでやります。おじさんの世代だとformattingはprittierに、コーディング規約はeslintにという分業がナウかったのですが、最近はどうなんでしょう?

こちらなどを参考にさくさく設定していきましょう。init一発で色々やってくれて本当にいい時代。

eslintの設定。今回は以下のように。

npm init @eslint/config
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ What format do you want your config file to be in? · JavaScript
The config that you've selected requires the following dependencies:

@typescript-eslint/eslint-plugin@latest eslint-plugin-react@latest @typescript-eslint/parser@latest
✔ Would you like to install them now? · No / Yes
✔ Which package manager do you want to use? · npm
# Airbnbのスタイルガイド
npx install-peerdeps --dev eslint-config-airbnb-base

prittierの設定

npm i -D prettier eslint-config-prettier eslint-plugin-prettier
// .prettierrc

{
  "singleQuote": true,
  "trailingComma": "all",
  "semi": false,
  "printWidth": 160
}

eslintの設定ファイルにextendsを追記。記述の順序が大事だそう。

// .eslintrc.js

  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "airbnb-base", # 追記
    "plugin:prettier/recommended", # 追記
    "prettier/@typescript-eslint", # 追記
  ],

ここまでで、ファイルの保存時にお好みの設定で自動フォーマットがかかればtypescriptの準備は万端です!お疲れ様でした。

.gitignore

あと、忘れないうちに.gitignore入れておきましょう

// .gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
// ここまでのフォルダ構成
% tree -aL 2 .
.
├── .eslintrc.js
├── .git
    (略)
├── .gitignore
├── .prettierrc
├── .vscode
│   ├── extensions.json
│   └── settings.json
├── node_modules
│   ├── .bin
    (略)
│   └── yocto-queue
├── package-lock.json
├── package.json
├── src
│   └── index.ts
└── tsconfig.json

こっから徐々にアプリの動く気配がし始めます。

prectのインストール

npm i preact

preact用にtsconfigに設定を追加します。

// tsconfig.json

{
  "compilerOptions": {
    "target": "ES2021",
    "module": "ESNext",
    "moduleResolution": "NodeNext",
    "useDefineForClassFields": true, # 追記
    "lib": ["ES2021", "DOM", "DOM.Iterable"], # 追記
    "paths": { # 追記
      "react": ["./node_modules/preact/compat/"],
      "react-dom": ["./node_modules/preact/compat/"]
    },
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "jsx": "react-jsx", # 追記
    "jsxImportSource": "preact" #追記
  }
}

以下のようにjsxを配置して、エラーが出なければOK

// src/index.tsx

import { render } from 'preact'
import { useState } from 'preact/hooks'

export const App = () => {
  const [count, setCount] = useState(0)
  return (
    <>
      <h1>hello world</h1>
      <button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
    </>
  )
}
render(<App />, document.getElementById('app')!)

index.htmlを配置しましょう

// index.html 以下のバンドラの都合でroot直下におきます
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ゲームDIY道テンプレ</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/index.tsx"></script>
  </body>
</html>

JSバンドラの導入

ではいよいよ、トランスパイルして、開発サーバーでアプリが立ち上がるところまでいきましょう。

いつものようにWebpackサクッと入れて。。。と思って調べてみたらWebpackディスコンなの!?

おじさんになったことを痛感いたしました。。。

こちらのTurbopackは今の所Next.jsプロジェクト用のツールの位置付けのようなので、もう一つ最近流行りのVite(ゔぃーと)で構築していきたいと思います。

npm i -D vite @preact/preset-vite 

tsconfig.jsonを以下のように書き換えます

// tsconfig.json

{
  "compilerOptions": {
    "target": "ES2021",
    "module": "ESNext",

    /* Bundler mode */
    "moduleResolution": "bundler", # 書き換え
    "allowImportingTsExtensions": true, # 追記
    "resolveJsonModule": true, # 追記
    "isolatedModules": true, # 追記
    "noEmit": true, # 追記

    "useDefineForClassFields": true,
    "lib": ["ES2021", "DOM", "DOM.Iterable"],
    "paths": {
      "react": ["./node_modules/preact/compat/"],
      "react-dom": ["./node_modules/preact/compat/"]
    },
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  },
  "references": [{ "path": "./tsconfig.node.json" }] # 追記
}

tsconfig.node.jsonとvite.config.tsを配置します

// tsconfig.node.json
{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}
import { defineConfig } from 'vite'
import preact from '@preact/preset-vite'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [preact()],
})

ここまでで設定はOKなはずなので、以下のコマンドで立ち上げます。

npx tsc && npx vite build && npx vite preview
  ➜  Local:   http://localhost:4173/
  ➜  Network: use --host to exposepress h + enter to show help

http://localhost:4173/ にアクセスすると以下のようなwebアプリが見えているはず

ボタンをクリックするとカウンターがインクリメントするよ

🎉🎉🎉
これでアプリケーション開発のスタートラインに立ちました。

もう3時間経っちゃったよ。。。

// ここまでのディレクトリ構成
% tree -aL 2 .
.
├── .eslintrc.js
├── .git
│   ├── COMMIT_EDITMSG
    (略)
├── .gitignore
├── .prettierrc
├── .vscode
│   ├── extensions.json
│   ├── settings.json
│   └── tasks.json
├── dist
│   ├── assets
│   └── index.html
├── index.html
├── node_modules
│   ├── .bin
    (略)
│   └── yocto-queue
├── package-lock.json
├── package.json
├── src
│   └── index.tsx
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

ちなみに、viteには便利なcreate appコマンドが用意されているようなので面倒臭い方はそちらで一発構築できます。

僕が性格的に、ボトムアップに構築しながら理解しないと頭こんがらがって手が止まってしまうタイプなので、今回はスクラッチで構築しました。tsconfigの設定などは、npm create viteで出てきたテンプレートを参考にしつつ必要な部分を持ってきたという感じです。

次回に続く

これだけだとただのWebアプリテンプレートなので、ゲームっぽいことをやれるようまだまだ構築せねばならんものが山の如しです。。。

というわけで、第0.5回をお待ちください(^人 ^)

この記事が気に入ったらサポートをしてみませんか?