Filter
Exclude
Time range
-
Near
chatGPT RPGツクールみたいなゲームが作りたいな 作って → import React, { useEffect, useMemo, useRef, useState } from "react"; const W = 18; const H = 12; const TILE = 32; const RAW_MAP = [ "##################", "#....g....T......#", "#..####..........#", "#..#..#....~~....#", "#..#..#....~~..C.#", "#..#..#..........#", "#..#..#####..#####", "#.................#", "#.....N.....T.....#", "#.................#", "#....S.......E....#", "##################", ]; const tileInfo = { "#": { name: "壁", walk: false, bg: "bg-stone-700", text: "" }, ".": { name: "草地", walk: true, bg: "bg-emerald-200", text: "" }, "g": { name: "花畑", walk: true, bg: "bg-lime-200", text: "✿" }, "T": { name: "木", walk: false, bg: "bg-green-700", text: "♣" }, "~": { name: "水", walk: false, bg: "bg-sky-300", text: "≈" }, "S": { name: "始まりの石", walk: true, bg: "bg-indigo-200", text: "◆" }, "E": { name: "出口", walk: true, bg: "bg-amber-200", text: "◎" }, "C": { name: "宝箱", walk: true, bg: "bg-yellow-200", text: "▣" }, "N": { name: "村人", walk: true, bg: "bg-rose-200", text: "!" }, }; const DIRS = { ArrowUp: [0, -1], ArrowDown: [0, 1], ArrowLeft: [-1, 0], ArrowRight: [1, 0], w: [0, -1], s: [0, 1], a: [-1, 0], d: [1, 0], W: [0, -1], S: [0, 1], A: [-1, 0], D: [1, 0], }; const enemyPool = [ { name: "ぷるスライム", hp: 18, atk: 5, exp: 8, gold: 5, art: "◌" }, { name: "夜のコウモリ", hp: 22, atk: 6, exp: 11, gold: 7, art: "⌁" }, { name: "影きのこ", hp: 26, atk: 7, exp: 14, gold: 10, art: "♟" }, ]; const initialState = { player: { x: 2, y: 10, dir: "up" }, hero: { name: "ミナト", lv: 1, hp: 36, maxHp: 36, mp: 10, maxMp: 10, atk: 8, exp: 0, next: 20, gold: 0 }, inventory: { potion: 2, herb: 0, key: 0 }, flags: { chest: false, npcTalked: false, cleared: false }, log: ["矢印キー/WASDで移動、Space/Enterで調べる。"], }; function clamp(n, min, max) { return Math.max(min, Math.min(max, n)); } function roll(min, max) { return Math.floor(Math.random() * (max - min 1)) min; } function isWalkable(x, y) { if (x < 0 || y < 0 || x >= W || y >= H) return false; const ch = RAW_MAP[y][x]; return tileInfo[ch]?.walk; } function getTile(x, y) { return RAW_MAP[y]?.[x] ?? "#"; } function addLog(state, line) { return { ...state, log: [line, ...state.log].slice(0, 6) }; } function levelUpIfNeeded(state) { let hero = { ...state.hero }; let logs = []; while (hero.exp >= hero.next) { hero.exp -= hero.next; hero.lv = 1; hero.next = Math.floor(hero.next * 1.45 8); hero.maxHp = 9; hero.maxMp = 3; hero.atk = 3; hero.hp = hero.maxHp; hero.mp = hero.maxMp; logs.push(`レベル${hero.lv}に上がった! HPとMPが全回復した。`); } return { ...state, hero, log: [...logs, ...state.log].slice(0, 6) }; } function StatusBar({ label, value, max }) { const pct = clamp((value / max) * 100, 0, 100); return ( <div className="space-y-1"> <div className="flex justify-between text-xs text-slate-700"> <span>{label}</span> <span>{value}/{max}</span> </div> <div className="h-2 overflow-hidden rounded-full bg-slate-200"> <div className="h-full rounded-full bg-slate-800 transition-all" style={{ width: `${pct}%` }} /> </div> </div> ); } export default function RPGTkoolLikeMiniGame() { const [game, setGame] = useState(() => { try { const saved = localStorage.getItem("mini-rpg-save-v1"); return saved ? JSON.parse(saved) : initialState; } catch { return initialState; } }); const [mode, setMode] = useState("field"); const [dialog, setDialog] = useState(null); const [battle, setBattle] = useState(null); const [shake, setShake] = useState(false); const [toast, setToast] = useState("小さな森のRPG"); const fieldRef = useRef(null); const currentTile = getTile(game.player.x, game.player.y); const save = () => { localStorage.setItem("mini-rpg-save-v1", JSON.stringify(game)); setToast("セーブしました"); }; const reset = () => { localStorage.removeItem("mini-rpg-save-v1"); setGame(initialState); setMode("field"); setDialog(null); setBattle(null); setToast("最初から始めました"); }; const openDialog = (lines, afterClose) => { setDialog({ lines, index: 0, afterClose }); setMode("dialog"); }; const closeDialog = () => { const afterClose = dialog?.afterClose; setDialog(null); setMode("field"); if (afterClose) afterClose(); }; const nextDialog = () => { if (!dialog) return; if (dialog.index < dialog.lines.length - 1) { setDialog({ ...dialog, index: dialog.index 1 }); } else { closeDialog(); } }; const startBattle = (enemy = enemyPool[roll(0, enemyPool.length - 1)]) => { setBattle({ enemy: { ...enemy, maxHp: enemy.hp }, turn: "hero", text: `${enemy.name}が現れた!` }); setMode("battle"); }; const maybeEncounter = (x, y) => { const ch = getTile(x, y); if ([".", "g"].includes(ch) && Math.random() < 0.12) { setTimeout(() => startBattle(), 80); } }; const move = (key) => { if (mode !== "field") return; const dir = DIRS[key]; if (!dir) return; const [dx, dy] = dir; const dirName = dx === -1 ? "left" : dx === 1 ? "right" : dy === -1 ? "up" : "down"; const nx = game.player.x dx; const ny = game.player.y dy; if (!isWalkable(nx, ny)) { setGame((s) => addLog({ ...s, player: { ...s.player, dir: dirName } }, "進めない。")); return; } setGame((s) => ({ ...s, player: { x: nx, y: ny, dir: dirName } })); maybeEncounter(nx, ny); }; const interact = () => { if (mode === "dialog") return nextDialog(); if (mode !== "field") return; const { x, y } = game.player; const targets = [ [x, y], [x, y - 1], [x 1, y], [x, y 1], [x - 1, y], ]; for (const [tx, ty] of targets) { const ch = getTile(tx, ty); if (ch === "N") { openDialog([ "村人:この森は、出口に近づくほど魔物が出るよ。", "村人:宝箱の薬草を取っておくと安心だ。", "村人:……あと、出口の石はなぜか人を見る。気まずい。", ]); setGame((s) => ({ ...s, flags: { ...s.flags, npcTalked: true } })); return; } if (ch === "C") { if (game.flags.chest) { openDialog(["宝箱はからっぽだ。からっぽにも、からっぽなりの圧がある。"]); } else { setGame((s) => ({ ...s, inventory: { ...s.inventory, potion: s.inventory.potion 1, herb: s.inventory.herb 2 }, flags: { ...s.flags, chest: true }, log: ["ポーション×1、薬草×2を手に入れた!", ...s.log].slice(0, 6), })); openDialog(["宝箱を開けた。", "ポーション×1、薬草×2を手に入れた!"]); } return; } if (ch === "S") { openDialog([ "石碑:小さな勇者よ。", "石碑:ゲームが完成するかは、最初の一歩を置けるかで決まる。", "石碑:つまり、もう半分できている。", ]); return; } if (ch === "E") { if (game.hero.lv >= 2 || game.flags.npcTalked) { setGame((s) => ({ ...s, flags: { ...s.flags, cleared: true } })); openDialog([ "出口の石が、低く鳴った。", "森の向こうに、次のマップの気配がある。", "クリア! ここから町、洞窟、仲間加入イベントを追加できます。", ]); } else { openDialog(["出口の石:まだ早い。", "出口の石:せめて村人の話を聞いてから行け。"]); } return; } } openDialog(["特に何もない。", "……いや、何もない場所があるから、道が見える。"]); }; const heroAttack = () => { if (!battle || battle.turn !== "hero") return; const dmg = roll(game.hero.atk - 2, game.hero.atk 4); const enemyHp = Math.max(0, battle.enemy.hp - dmg); const next = { ...battle, enemy: { ...battle.enemy, hp: enemyHp }, text: `${game.hero.name}の攻撃! ${dmg}ダメージ。`, turn: "enemy" }; setBattle(next); if (enemyHp <= 0) { const gained = { ...game.hero, exp: game.hero.exp battle.enemy.exp, gold: game.hero.gold battle.enemy.gold }; let nextGame = { ...game, hero: gained, log: [`${battle.enemy.name}を倒した! EXP${battle.enemy.exp} / ${battle.enemy.gold}G`, ...game.log].slice(0, 6), }; nextGame = levelUpIfNeeded(nextGame); setGame(nextGame); setBattle({ ...next, text: `${battle.enemy.name}を倒した!`, turn: "end" }); setTimeout(() => { setBattle(null); setMode("field"); }, 900); return; } setTimeout(enemyTurn, 700, next.enemy); }; const castHeal = () => { if (!battle || battle.turn !== "hero") return; if (game.hero.mp < 4) { setBattle({ ...battle, text: "MPが足りない。" }); return; } const heal = roll(12, 18); setGame((s) => ({ ...s, hero: { ...s.hero, mp: s.hero.mp - 4, hp: clamp(s.hero.hp heal, 0, s.hero.maxHp) }, })); const next = { ...battle, text: `ヒール! HPが${heal}回復した。`, turn: "enemy" }; setBattle(next); setTimeout(enemyTurn, 700, next.enemy); }; const usePotion = () => { if (!battle || battle.turn !== "hero") return; if (game.inventory.potion <= 0) { setBattle({ ...battle, text: "ポーションがない。" }); return; } setGame((s) => ({ ...s, inventory: { ...s.inventory, potion: s.inventory.potion - 1 }, hero: { ...s.hero, hp: clamp(s.hero.hp 24, 0, s.hero.maxHp) }, })); const next = { ...battle, text: "ポーションを使った。HPが24回復!", turn: "enemy" }; setBattle(next); setTimeout(enemyTurn, 700, next.enemy); }; const enemyTurn = (enemy) => { setShake(true); setTimeout(() => setShake(false), 180); const dmg = roll(Math.max(1, enemy.atk - 2), enemy.atk 3); setGame((s) => { const hp = Math.max(0, s.hero.hp - dmg); return { ...s, hero: { ...s.hero, hp } }; }); setBattle((b) => (b ? { ...b, text: `${enemy.name}の攻撃! ${dmg}ダメージ。`, turn: "hero" } : b)); }; useEffect(() => { if (mode === "battle" && game.hero.hp <= 0) { setBattle((b) => (b ? { ...b, text: "倒れてしまった……始まりの石へ戻る。", turn: "end" } : b)); setTimeout(() => { setGame((s) => ({ ...s, player: { x: 2, y: 10, dir: "up" }, hero: { ...s.hero, hp: Math.ceil(s.hero.maxHp / 2), mp: Math.ceil(s.hero.maxMp / 2) }, log: ["始まりの石で目を覚ました。", ...s.log].slice(0, 6), })); setBattle(null); setMode("field"); }, 1100); } }, [game.hero.hp, mode]); useEffect(() => { const onKey = (e) => { if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", " ", "Enter", "w", "a", "s", "d", "W", "A", "S", "D"].includes(e.key)) { e.preventDefault(); } if (e.key === " " || e.key === "Enter") interact(); else move(e.key); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }); const mapTiles = useMemo(() => { return RAW_MAP.flatMap((row, y) => row.split("").map((ch, x) => ({ ch, x, y, info: tileInfo[ch] }))); }, []); return ( <div className="min-h-screen bg-slate-100 p-4 text-slate-900"> <div className="mx-auto max-w-6xl space-y-4"> <header className="rounded-2xl bg-white p-4 shadow-sm"> <div className="flex flex-col gap-2 md:flex-row md:items-end md:justify-between"> <div> <p className="text-xs font-semibold uppercase tracking-[0.25em] text-slate-500">Mini RPG Prototype</p> <h1 className="text-2xl font-black tracking-tight">小さな森と始まりの石</h1> <p className="text-sm text-slate-600">RPGツクール風の見下ろし探索RPG。矢印キー/WASDで移動、Space/Enterで調べる。</p> </div> <div className="flex flex-wrap gap-2"> <button onClick={save} className="rounded-xl bg-slate-900 px-4 py-2 text-sm font-bold text-white shadow-sm active:scale-95">セーブ</button> <button onClick={reset} className="rounded-xl bg-white px-4 py-2 text-sm font-bold text-slate-900 ring-1 ring-slate-300 active:scale-95">リセット</button> </div> </div> </header> <main className="grid gap-4 lg:grid-cols-[1fr_320px]"> <section className="rounded-2xl bg-white p-4 shadow-sm"> <div className="mb-3 flex items-center justify-between"> <div className="rounded-full bg-slate-100 px-3 py-1 text-xs font-bold text-slate-600">現在地:{tileInfo[currentTile]?.name}</div> <div className="rounded-full bg-amber-100 px-3 py-1 text-xs font-bold text-amber-800">{toast}</div> </div> <div ref={fieldRef} className={`relative mx-auto overflow-hidden rounded-2xl border-4 border-slate-800 bg-slate-800 shadow-inner ${shake ? "translate-x-1" : ""}`} style={{ width: W * TILE, height: H * TILE }} > {mapTiles.map(({ ch, x, y, info }) => { const openedChest = ch === "C" && game.flags.chest; const clearedExit = ch === "E" && game.flags.cleared; return ( <div key={`${x}-${y}`} className={`absolute flex select-none items-center justify-center text-lg font-black ${info.bg} ${ch === "#" ? "text-slate-500" : "text-slate-800"}`} style={{ left: x * TILE, top: y * TILE, width: TILE, height: TILE }} > {openedChest ? "□" : clearedExit ? "★" : info.text} </div> ); })} <div className="absolute z-10 flex items-center justify-center rounded-full border-2 border-white bg-slate-950 text-lg font-black text-white shadow-lg transition-all duration-100" style={{ left: game.player.x * TILE 3, top: game.player.y * TILE 3, width: TILE - 6, height: TILE - 6 }} > {game.player.dir === "up" ? "▲" : game.player.dir === "down" ? "▼" : game.player.dir === "left" ? "◀" : "▶"} </div> {mode === "dialog" && dialog && ( <div className="absolute inset-x-4 bottom-4 z-20 rounded-2xl border-2 border-slate-900 bg-white/95 p-4 shadow-xl"> <p className="min-h-[48px] text-base font-bold leading-relaxed">{dialog.lines[dialog.index]}</p> <p className="mt-2 text-right text-xs text-slate-500">Space / Enterで次へ</p> </div> )} {mode === "battle" && battle && ( <div className="absolute inset-0 z-30 flex items-center justify-center bg-slate-950/80 p-5 text-white"> <div className="w-full max-w-md rounded-2xl border-2 border-white bg-slate-900 p-5 shadow-2xl"> <div className="mb-4 flex items-center justify-between"> <div> <p className="text-xs uppercase tracking-[0.2em] text-slate-400">Battle</p> <h2 className="text-2xl font-black">{battle.enemy.name}</h2> </div> <div className="text-5xl">{battle.enemy.art}</div> </div> <StatusBar label="Enemy HP" value={battle.enemy.hp} max={battle.enemy.maxHp} /> <div className="mt-4 rounded-xl bg-white p-3 font-bold text-slate-900">{battle.text}</div> <div className="mt-4 grid grid-cols-3 gap-2"> <button disabled={battle.turn !== "hero"} onClick={heroAttack} className="rounded-xl bg-white px-3 py-2 text-sm font-black text-slate-900 disabled:opacity-40">攻撃</button> <button disabled={battle.turn !== "hero"} onClick={castHeal} className="rounded-xl bg-white px-3 py-2 text-sm font-black text-slate-900 disabled:opacity-40">ヒール</button> <button disabled={battle.turn !== "hero"} onClick={usePotion} className="rounded-xl bg-white px-3 py-2 text-sm font-black text-slate-900 disabled:opacity-40">薬</button> </div> </div> </div> )} </div> <div className="mt-3 grid grid-cols-3 gap-2 md:hidden"> <div /> <button onClick={() => move("ArrowUp")} className="rounded-xl bg-slate-900 p-3 font-black text-white">↑</button> <div /> <button onClick={() => move("ArrowLeft")} className="rounded-xl bg-slate-900 p-3 font-black text-white">←</button> <button onClick={interact} className="rounded-xl bg-amber-400 p-3 font-black text-slate-950">調</button> <button onClick={() => move("ArrowRight")} className="rounded-xl bg-slate-900 p-3 font-black text-white">→</button> <div /> <button onClick={() => move("ArrowDown")} className="rounded-xl bg-slate-900 p-3 font-black text-white">↓</button> <div /> </div> </section> <aside className="space-y-4"> <section className="rounded-2xl bg-white p-4 shadow-sm"> <h2 className="mb-3 text-lg font-black">ステータス</h2> <div className="mb-3 rounded-2xl bg-slate-100 p-3"> <div className="flex items-center justify-between font-black"> <span>{game.hero.name}</span> <span>Lv {game.hero.lv}</span> </div> <div className="mt-3 space-y-3"> <StatusBar label="HP" value={game.hero.hp} max={game.hero.maxHp} /> <StatusBar label="MP" value={game.hero.mp} max={game.hero.maxMp} /> </div> </div> <div className="grid grid-cols-2 gap-2 text-sm"> <div className="rounded-xl bg-slate-50 p-2"><b>ATK</b><br />{game.hero.atk}</div> <div className="rounded-xl bg-slate-50 p-2"><b>EXP</b><br />{game.hero.exp}/{game.hero.next}</div> <div className="rounded-xl bg-slate-50 p-2"><b>GOLD</b><br />{game.hero.gold}G</div> <div className="rounded-xl bg-slate-50 p-2"><b>進行</b><br />{game.flags.cleared ? "クリア" : "探索中"}</div> </div> </section> <section className="rounded-2xl bg-white p-4 shadow-sm"> <h2 className="mb-3 text-lg font-black">もちもの</h2> <div className="space-y-2 text-sm"> <div className="flex justify-between rounded-xl bg-slate-50 p-2"><span>ポーション</span><b>{game.inventory.potion}</b></div> <div className="flex justify-between rounded-xl bg-slate-50 p-2"><span>薬草</span><b>{game.inventory.herb}</b></div> <div className="flex justify-between rounded-xl bg-slate-50 p-2"><span>鍵</span><b>{game.inventory.key}</b></div> </div> </section> <section className="rounded-2xl bg-white p-4 shadow-sm"> <h2 className="mb-3 text-lg font-black">ログ</h2> <div className="space-y-2 text-sm text-slate-700"> {game.log.map((line, i) => ( <p key={`${line}-${i}`} className="rounded-xl bg-slate-50 p-2">{line}</p> ))} </div> </section> <section className="rounded-2xl bg-slate-900 p-4 text-white shadow-sm"> <h2 className="mb-2 text-lg font-black">改造ポイント</h2> <p className="text-sm leading-relaxed text-slate-200"> RAW_MAPを書き換えるとマップを変更できます。N/C/E/Sを増やせば、NPC・宝箱・出口・石碑イベントを追加できます。 </p> </section> </aside> </main> </div> </div> ); }

2
440
!setgame Hentai @RoslenVLR
3
9
3,297
🟢 !SETGAME just chatting
6
2,416
!setgame just chatting
5
2,708
!setgame just chatting
1
10
7,293
!setgame just chatting
2
8
4,536
untrue I didn't know how to do !setgame 😂😂
2
2
51
6 Aug 2025
Replying to @Volpoune
Je ne suis plus Carlos le calamar.. !setgame Just Chialling vas y c'est bon la... Tes mots me vont droit au cœur ❤️ merci pour tout ❤️🥹
1
3
36
Replying to @23ricoy
!setgame Rust por favor🙏
1
3
537
Thích một mình - Nhưng chẳng sợ cô đơn. Từ hồi vô Nhóm rồi bạo dạng làm Leader Khởi Hưng tham gia Game của @duongvuong2803 . Mình trở về đứa trẻ say mê với trò chơi quên trưa, quên cơm để Setgame cho mọi người tối nai.
17
54
6,217
!SETGAME outbrk
13
6,249
I just came SO CLOSE to breaking my previous Set Game record of 37.689 seconds. Just found all 6 sets in 42.868 seconds. #SetGame
I just broke my previous Set Game record of 48 seconds with 37.689 seconds! Took me over a year, one puzzle per day. setgame.com/set/puzzle #SetGame
4
1,328
Replying to @Poppielala
Après le !setgame
1
3
124
✅[พร้อมส่ง] —Kit 7 🔖set All Photobook setgame poster 250 /set ฟตก 300 📮รวมส่ง สนใจเด็มเลยน๊า♡ ᴍᴀᴋᴇ •.★⋆ ᴡɪsʜ #ตลาดนัดไอยู #ตลาดนัดiu
5
1,102
!setgame just chatting
7
10,212
Replying to @chippu98
That's so shallow if you're crying about being bad at Valorant. Btw i dont think you should stream at twitch, your channel sucks, everyone in your community is trash. Go back to FB gaming, !setgame #yousuckatvalorant
2
575
10 May 2024
Twitch streamers, ever wish you could quickly change category by typing, for example, !MW2 instead of !setgame call of duty: modern warfare II? If so, spend 1 minute watching this.. #twitch #streaming #streamer #Tips youtu.be/jT5EEkvcjIY?si=Nl5v…
2
5
175
Couldn't sleep, argued with ChatGPT. We made the game Set, with a lot of bugs. isaacdozier.github.io/Set/ Eh... Going to bed now. #setgame
2
4
183
Replying to @Dean_of_math
Return to our learning goal to see where we are and what needs to happen next time. Shout-outs by me and the students for awesome moments and contributions. If a few minutes to spare...can we figure out the all ten, countle, setgame, etc before time is up?
1
16
2,165
12 Apr 2024
Replying to @Rotworks
!setgame Just Chatting 😏😏
1
3
45