概要
小一時間でゲームをつくる ──7つの定番ゲームのプログラミングを体験を買ったので写経してみる。
ただ写経ではつまらないので、React + Typescriptで書いてみた。
ここでは差分についてメモを残す。
レトロゲーム風フォント
8×8 ドット日本語フォント「美咲フォント」の代わりに、google fonts の DotGothic16を使用。
コンソール
コンソールの代わりにdev要素にテキストを配置して実装。
文字入力
キーボードイベントとして取得。
src/hooks/useReactHook.ts
import { useCallback, useEffect } from 'react'
export const useKeyDown = (handler: (key: string) => void) => {
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
handler(event.key)
},
[handler],
)
useEffect(() => {
document.addEventListener('keydown', handleKeyDown)
return () => {
document.removeEventListener('keydown', handleKeyDown)
}
}, [handleKeyDown])
}
ゲームループ
ジェネレータ関数を使用。
function* battleLoop(characters: Character[]) {
const [player, monster] = characters
player.target = 1
monster.target = 0
while (true) {
let commandSelecting = true
while (commandSelecting) {
commandSelecting = yield {
message: `${selectCommandMessage(player.command, commandNames).join(
'\n',
)}`,
player,
}
}
for (const c of characters) {
switch (c.command) {
case commands.COMMAND_FIGHT: {
yield { message: `${c.name} の攻撃!`, player }
const damage = Math.floor(1 + Math.random() * c.attack)
const target = characters[c.target]
target.hp = target.hp - damage < 0 ? 0 : target.hp - damage
yield {
message: `${target.name} に${damage}のダメージ!`,
player,
}
if (target.hp === 0) {
return {
message:
player.name === 'ゆうしゃ'
? 'あなたは しにました'
: `${target.name} をたおした!`,
player,
}
}
break
}
}
}
}
}
hooksから呼び出す。 const [gen] = useState(battleLoop(characters))
const useHooks = (characters: Character[]) => {
const [player, monster] = characters
const [gen] = useState(battleLoop(characters))
const [state, setState] = useState({
message: `${monster.name} があらわれた!`,
player,
})
const selectCommand = (key: string) => {
switch (key) {
case 'Enter': {
const nextState = gen.next(false).value
if (nextState) setState(nextState)
return
}
}
}
useKeyDown(selectCommand)
return { state, selectCommand }
}