- 投稿日:2026/04/24
- 更新日:2026/04/24
YouTubeで、こんな声を見かけたことがあります。
「孫と一緒に使えるゲームをクロコで作った」
数日前の両学長の言葉「小さなものを完成させる」
以前、クロコで作成した算数復習ゲームを「Googleスプレッドシート」テンプレートにして作りなおしました。
スプレッドシートに問題を追加・編集すれば、いろんな用途のゲームが作れると思います。
初めてスプレッドシートでゲームを作ったので、質問には回答できませんが、このテンプレートを使って、新たなゲームをつくるキッカケになれば嬉しいです。
※公開済みにしてもしばらく記事の修正作業続くと思います(動画、スクショ追加など)。ブックマーク、👍もらえたら修正はかどるかもしれません。
何を作ればいいかわからない方に、『こんなものも作れるよ』という一例を届けたくて作りました。
遊んでみよう
まずは遊んでみよう!
▶︎ 遊ぶ
▶︎ スプレッドシートをコピーして遊ぶ
https://docs.google.com/spreadsheets/d/1PC09SN5fqc6Yhoe5NDRfgKMxOADDrAw8bNHeKz2hULk/copy
(出典 : perplexity.ai)
コピーのリンクをタップすると、このような画面が出ます。
→ 「コピーを作成」を押す

※「Apps Script(アップス スクリプト)」は、
スプレッドシートに住ませる小さなロボットみたいなものです。
メニューを押すと、このロボットが動いて、
スプレッドシートの問題を読んでゲーム画面を出してくれます✨
⚠️ 初回は警告が出ますが、これはみんな必ず出る確認画面です。
「自分の家に住ませるロボット、ほんとにこの子で合ってる?」という確認なので、
「詳細」→「(プロジェクト名)に移動」で進めばOKです。
ロボット本体(Code.gs)と、ゲーム画面の設計図(game.html)。
この2つがセットで動きます。
↓
スプレッドシートを開いたら、
上のメニューから「🎮 ゲーム」
→「ゲームスタート ▶」を押すとゲームが始まります。


※初回は警告が出るので、まずは遊ぶリンクでやってみるのがおすすめです。

これ、維持費0円で動いています。
使ったのは「Claude Code」「Claude Design」「Googleスプレッドシート」だけ。
私はコードをほぼ書いていません。指示通りにコピペしただけです。コピペする場所が初見では難しかったです💦
今回は、さすがにスマホ📱のみでいけませんでした。スプレッドシートはパソコン作業が必要になります。
しかし、ターミナル動いているMacBook Air💻は触っていません。
スマホ遠隔操作×Windows🖥️でスプレッドシートなどの作業をしてました。
🎮 何を作ったか
以前作成した算数復習をヒントに設計、スプレッドシート完結型の算数ゲームです。
・ 問題を出して、答えを選ぶ(4択)
・ 計算問題にヒントが出る(ドット絵みたいな視覚的なやつ)
・ 正解すると次の問題へ
問題はスプレッドシートで管理するので、誰でも自分の子・孫向けにカスタマイズできます。
学年が上がっても、問題を差し替えるだけで使い続けられます。

🧭 仕組みはシンプル
Googleスプレッドシート(問題データ)
↓
Apps Script(スクリプト2ファイル)
↓
スプレッドシートのメニューから起動
↓
ゲームが画面内に開く→遊ぶ
サーバーもDBも課金サービスも、何もいりません。
Googleアカウントがあれば今日から作れます
(=゚ω゚)ノ
📊 スプレッドシートの作り方
まずGoogleスプレッドシートを開いて、こんな列を作ります。
| A列 | B列 | C列 |
|---|---|---|
| もんだい(全角30文字以内) | こたえ | ヒント(y=ドットあり・n=ドットなし)|
| 3 + 4 は? | 7 | y |
| 15 - 8 は? | 7 | y |
| りんごが3こ みかんが4こ あわせていくつ? | 7 | n |
・ A列「もんだい」:問題文(全角30文字以内におさめると見やすいです)
・ B列「こたえ」:正解の数字
・ C列「ヒント」:y=ドット絵のヒントあり、n=ヒントなし
(文章問題など、計算式じゃないときは n にするとキレイです)
この表に問題を追加していくだけ。Excelっぽい操作なので、特別なスキルはいりません。

💾 Apps Scriptを貼り付ける
ここからの作業がスマホだけだと作れない工程になります。
ゲームする画面の見栄えを良くしたくて、トライアンドエラーを繰り返しました。
スプレッドシートのメニューから「拡張機能」→「Apps Script」を開きます。

ここに2つのファイルを作ります。Code.gs(ゲームを起動する処理)と game.html(ゲームの見た目)です。

「全文コピペ用のコードブロックにして」
game.htmlなどの中身はClaude.ai(相談役💬)
に
「全文コピペ用のコードブロックにして」
と頼んで受け取り、コードブロック右上のコピーボタンで一発コピーしました。
コードブロック右上のコピーボタンを押すだけ。

Code.gs

▼▼▼ ここから ▼▼▼
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('🎮 ゲーム')
.addItem('ゲームスタート ▶', 'openGame')
.addToUi();
}
function openGame() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const data = sheet.getDataRange().getValues();
const problems = [];
for (let i = 1; i < data.length; i++) {
const q = String(data[i][0] || '').trim();
const a = String(data[i][1] || '').trim();
const h = String(data[i][2] || '').trim().toLowerCase();
if (q && a) {
problems.push({ question: q, answer: a, hint: h !== 'n' });
}
}
const template = HtmlService.createTemplateFromFile('game');
template.problems = JSON.stringify(problems);
const html = template.evaluate()
.setWidth(500)
.setHeight(660)
.setTitle('まごといっしょ!もんだいゲーム');
SpreadsheetApp.getUi().showModalDialog(html, 'まごといっしょ!もんだいゲーム');
}
▲▲▲ ここまで ▲▲▲
game.htmlの作り方
ファイル欄の「+」ボタン → 「HTML」を選ぶ → 名前を「game」にする
これで、左側に「game.html」が追加されます✨
(拡張子「.html」は自動でつくので、名前は「game」だけでOKです)
▼▼▼ ここから ▼▼▼
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
background: #fff9f0;
font-family: 'Hiragino Sans', 'Meiryo', sans-serif;
display: flex; justify-content: center;
min-height: 100dvh;
padding: 0 16px;
}
#app {
width: 100%; max-width: 460px;
display: flex; flex-direction: column;
height: 100dvh;
}
.game {
flex: 1; width: 100%;
display: grid;
grid-template-rows:
auto
auto
minmax(8px, 1fr)
auto
28px
auto
minmax(16px, 2fr);
padding: 4px 0 max(16px, env(safe-area-inset-bottom));
overflow: hidden;
}
.game > .progress-wrap { grid-row: 1; }
.game > .num-label { grid-row: 2; }
.game > .mid-area { grid-row: 4; }
.game > .choices { grid-row: 6; }
.mid-area {
display: flex; flex-direction: column;
align-items: stretch;
gap: 14px;
min-height: 0;
}
.progress-wrap { display: flex; align-items: center; justify-content: space-between; margin-bottom: 2px; }
.bar-bg { flex: 1; height: 8px; background: #ddd5c8; border-radius: 4px; margin-right: 12px; overflow: hidden; }
.bar-fill { height: 8px; background: #e67e22; border-radius: 4px; transition: width 0.3s; }
.score { font-size: 20px; color: #e67e22; font-weight: bold; }
.num-label { font-size: 12px; color: #aaa; margin-bottom: 0; }
.question {
font-size: 44px; font-weight: bold; color: #222;
text-align: center; margin: 0;
line-height: 1.2;
}
.dots-wrap {
display: flex; justify-content: center; align-items: center;
gap: 8px;
}
.dots-wrap:empty { display: none; }
.dot-group { display: flex; flex-wrap: wrap; gap: 5px; max-width: 130px; justify-content: center; }
.dot { width: 18px; height: 18px; border-radius: 50%; }
.dot.blue { background: #2980b9; }
.dot.orange { background: #e67e22; }
.dot-plus { font-size: 24px; font-weight: bold; color: #555; line-height: 1; align-self: center; }
.choices {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px 14px;
flex-shrink: 0;
}
.choice-btn {
background: #2980b9; color: #fff;
font-size: 32px; font-weight: 900;
letter-spacing: 0.02em;
font-family: inherit;
border: none; border-radius: 14px;
padding: 18px 10px; cursor: pointer;
box-shadow: 0 4px 0 #1f618d, 0 6px 12px rgba(41,128,185,0.25);
text-shadow: 0 2px 3px rgba(0,0,0,0.18);
transition: opacity 0.1s, transform 0.1s, box-shadow 0.1s;
-webkit-text-stroke: 0.5px #fff;
}
.choice-btn:hover { opacity: 0.9; }
.choice-btn:active {
transform: translateY(3px);
box-shadow: 0 1px 0 #1f618d, 0 2px 6px rgba(41,128,185,0.25);
}
.feedback {
display: none; position: fixed; inset: 0;
flex-direction: column; justify-content: center; align-items: center;
gap: 14px; z-index: 100;
}
.feedback.show { display: flex; }
.feedback.ok { background: rgba(76,175,80,0.88); }
.feedback.ng { background: rgba(243,156,18,0.55); }
.fb-main { font-size: 42px; font-weight: bold; color: #fff; text-shadow: 0 2px 6px rgba(0,0,0,0.4); }
.fb-sub {
font-size: 28px; color: #444;
background: #fff; padding: 10px 24px;
border-radius: 10px;
display: none;
}
.result { display: none; text-align: center; }
.result.show {
flex: 1; display: flex; flex-direction: column;
justify-content: center; align-items: center;
padding: 24px 0;
}
.result-label { font-size: 20px; color: #888; margin-bottom: 10px; }
.result-score { font-size: 68px; font-weight: bold; color: #e67e22; }
.result-pct { font-size: 32px; color: #555; margin: 8px 0; }
.result-msg { font-size: 26px; font-weight: bold; color: #27ae60; margin: 14px 0 28px; }
.retry-btn {
background: #3498db; color: #fff;
font-size: 22px; font-weight: bold;
font-family: inherit;
border: none; border-radius: 10px;
padding: 16px 44px; cursor: pointer;
}
.retry-btn:hover { opacity: 0.85; }
</style>
</head>
<body>
<div id="app"></div>
<div class="feedback" id="feedback">
<div class="fb-main" id="fb-main"></div>
<div class="fb-sub" id="fb-sub"></div>
</div>
<script>
const ALL_PROBLEMS = <?!= problems ?>;
function shuffle(arr) {
const a = [...arr];
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function makeChoices(correct) {
const num = parseFloat(correct);
const set = new Set([correct]);
const dummies = [];
if (!isNaN(num)) {
for (const o of [-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10]) {
const v = num + o;
if (v >= 0) dummies.push(String(v));
}
} else {
dummies.push(...['1','2','3','4','5','6','7','8','9','10','11','12']);
}
const sd = shuffle(dummies);
for (const d of sd) { if (set.size >= 4) break; set.add(d); }
return shuffle([...set]);
}
function makeDotGroup(n, colorClass) {
const g = document.createElement('div');
g.className = 'dot-group';
for (let i = 0; i < Math.min(n, 10); i++) {
const d = document.createElement('div');
d.className = 'dot ' + colorClass;
g.appendChild(d);
}
return g;
}
let problems = [], index = 0, score = 0;
function startGame() {
problems = shuffle(ALL_PROBLEMS).slice(0, 5);
index = 0; score = 0;
renderQ();
}
function renderQ() {
const p = problems[index];
const total = problems.length;
const fillPct = Math.round(index / total * 100);
document.getElementById('app').innerHTML = `
<div class="game">
<div class="progress-wrap">
<div class="bar-bg"><div class="bar-fill" style="width:${fillPct}%"></div></div>
<div class="score">★ ${score}</div>
</div>
<div class="num-label">${index + 1} / ${total}</div>
<div class="mid-area">
<div class="question">${p.question}</div>
<div class="dots-wrap" id="dots"></div>
</div>
<div class="choices" id="choices"></div>
</div>`;
if (p.hint !== false) {
const nums = p.question.match(/\d+/g);
if (nums && nums.length >= 2) {
const area = document.getElementById('dots');
area.appendChild(makeDotGroup(parseInt(nums[0]), 'blue'));
const plus = document.createElement('span');
plus.className = 'dot-plus'; plus.textContent = '+';
area.appendChild(plus);
area.appendChild(makeDotGroup(parseInt(nums[1]), 'orange'));
}
}
const choices = makeChoices(String(p.answer));
const wrap = document.getElementById('choices');
choices.forEach(c => {
const btn = document.createElement('button');
btn.className = 'choice-btn';
btn.textContent = c;
btn.onclick = () => answer(c, String(p.answer));
wrap.appendChild(btn);
});
}
function answer(chosen, correct) {
const ok = chosen === correct;
if (ok) score++;
const fb = document.getElementById('feedback');
const main = document.getElementById('fb-main');
const sub = document.getElementById('fb-sub');
fb.className = 'feedback show ' + (ok ? 'ok' : 'ng');
main.textContent = ok ? '⭕ せいかい!' : 'もう1かい!';
sub.style.display = 'none';
sub.textContent = 'こたえは ' + correct;
if (!ok) setTimeout(() => { sub.style.display = 'block'; }, 1200);
setTimeout(() => {
fb.className = 'feedback';
index++;
if (index < problems.length) renderQ();
else renderResult();
}, ok ? 1000 : 2400);
}
function renderResult() {
const pct = Math.round(score / problems.length * 100);
const msg = pct === 100 ? 'かんぺき!すごい!!'
: pct >= 80 ? 'よくできました!'
: pct >= 60 ? 'もうすこし!'
: 'またやってみよう!';
document.getElementById('app').innerHTML = `
<div class="result show">
<div class="result-label">けっか</div>
<div class="result-score">${score} / ${problems.length}</div>
<div class="result-pct">${pct}%</div>
<div class="result-msg">${msg}</div>
<button class="retry-btn" onclick="startGame()">もういちど</button>
</div>`;
}
startGame();
</script>
</body>
</html>
▲▲▲ ここまで ▲▲▲
保存
Code.gs・game.htmlともにApps Scriptに貼り付けたら保存します。
💾 保存のショートカットキー
・ Mac:⌘ Command + S
・ Windows:Ctrl + S
画面上部のフロッピーアイコン(💾)をクリックしてもOKです。
※「デプロイ」という項目がメニューにありますが、
今回のゲームはスプレッドシートのメニューから起動するので、
デプロイは不要です。保存するだけでOKです✨
(冒頭の「▶︎ 遊ぶ」リンクはWebアプリとして公開しているので
デプロイしていますが、コピーして遊ぶ人は気にしなくて大丈夫です)
💡 自分のゲームもリンクで遊べるようにしたい方へ
💡 自分のゲームもリンクで遊べるようにしたい方へ
冒頭の「▶︎ 遊ぶ」みたいに、リンクから直接遊べるようにしたい場合は、
Apps Scriptの「デプロイ」という機能を使います。

右上の青い「デプロイ」ボタンから設定できます。
ちなみに私もたくさん試行錯誤しました(アーカイブの履歴が証拠です😅)
やり方がわからなくても大丈夫。Claudeにこう聞いてみてください👇
▼▼▼ ここから ▼▼▼
Google Apps Scriptで作ったゲームを、
Webアプリとして公開してURLで遊べるようにしたいです。
コードがわからないので、やさしく手順を教えてください
▲▲▲ ここまで ▲▲▲
「やさしく教えて」を付けるのがコツです✨
エラーが出たら、スクショを撮って見せれば解決してくれます。
🚀 スプレッドシートからゲームを起動する
Apps Scriptを保存してスプレッドシートを再読み込みすると、上のメニューに「🎮 ゲーム」が追加されます。
「ゲームスタート ▶」を押すと、ゲームが画面内に開きます✨
スプレッドシートを家族と共有すれば、同じようにメニューから遊べます。
🤖 開発中に失敗したこと
うまくいかないことの連続でした💦
ノウハウ記事を寄稿する、と
・つぶやき
・学長ライブ
などで宣言するようにして、横道にそれながらもなんとかテンプレートを完成することができました。
感謝✨
失敗1:Claudeに全部やらせようとしたらファイルが壊れた
最初、Claude CodeにGoogleドライブ上のファイルを直接操作させようとしました。
結果、ファイルが壊れました。
Claude Codeはローカル(自分のPC)のファイルを操作するのは得意ですが、Google DriveはAPIで繋がないと動かせません。「なんでも丸投げすればいい」はちょっと違いました。
失敗2:文字化けして白画面になった
ゲームのデザインファイルを取り込む処理で、base64という形式に変換する作業があったんですが、途中で文字化けして画面が真っ白になりました。
getElementByIdという関数名がgetElemQbyIdに壊れていたのが原因だとクロコが教えてくれたのですが…さっぱりわかりません。ただ、わかる人にはわかる失敗だと思うので、こちらに書いときます。
最終的に「Apps Scriptに直接HTMLを書く方式(`<?!= ?>`という特殊な書き方)」に切り替えてもらったら解決しました。Claude.aiに確認したら「そのまま残してOK」と教えてもらいました。
「エラーが出た→試す→別の方法を提案してもらう」の繰り返しです。
🧩 AIとの付き合い方がわかってきた
この開発を通じて、「役割を分けるといい」ということがわかりました。
🧑💻 Claude Code(クロコ):コード設計・ファイル管理・進捗記録
💬 Claude.ai(チャット版):エラーの相談・コードブロックで橋渡し
🎨 Claude Design:ゲームの見た目づくり・スマホ対応
最初は「クロコに全部やらせればいい」と思っていたんですが、得意なことが違います。
意思決定もAIに分担してもらう
方向性を決めるときは、こんな流れを作りました。
私はクロコ(Claude Code)に話しかけるだけ。
クロコが裏でリーに指示を出してリサーチさせ、
結果をまとめてジャッジして報告してくれます。
「クロコに相談する → クロコがチームを動かす → 私が決める」
これが今の基本ルーティンです。
以前までは色々指示してました
「(サブエージェント達で)ディスカッション、(必要な情報を)リー(がニーズをリサーチ)、クロコジャッジ(〇〇ユーザにとって役に立つか、クロコがベストだとおもう選択肢)、おまかせで」
今は面倒になり
「ディスカッション、リー、クロコジャッジ、おまかせで」と記入しています。
後で調べてわかったんですが、Anthropicがちょうど2026年4月に、複数のAIエージェントを連携・役割分担させる新機能を正式リリースしていました。
感覚でやっていたことが、公式の方向性とたまたま一致していて、ちょっと嬉しかったです。
人間のチームと一緒で、担当を分けたほうがうまく回ります✨
📱 応用編:スマホでレイアウトが崩れる問題
ここからはちょっと技術的な話です。
最初、クロコに見た目まで作ってもらったんですが、スマホで見ると崩れました💦
原因:
Claude Codeはプレビューできないので、ピクセル単位の空間バランスを「見ながら」調整できない。
解決策:
見た目は
クロコに最初作ってもらう
↓
Claude.aiにスクショを送りながら、直していく。
↓
これ以上、クロコ×Claude.ai での修正は無理だ…。と思ったら、Claude.aiにClaude Designへの指示書を書いてもらう。
↓
Claude Design × Claude.ai
Designのプレビュー画面をスクショして、Claude.aiに指示書かいてもらい、 Designへ入力して修正していく。
↓
修正していくと使用量がゴリゴリ削られるので、ある程度 できてきたら…
↓
本当に修正が必要かリサーチしてとClaude.aiに質問しました。
Claude Designで学んだレイアウトのコツ
クロコ×Claude.ai に今回の件で試行錯誤したことをまとめてもらいました。
さっぱりわかりませんが、たぶん必要な人がいるかも?と思いシェアします。
1. CSS Grid + minmax()で画面内に収める
▼▼▼ ここから ▼▼▼
.grid-template-rows:
auto /* 固定コンテンツ */
minmax(8px, 1fr) /* 上スペーサー */
auto /* メインコンテンツ */
28px /* ★固定ギャップ(絶対変えたくない距離)*/
auto /* ボタン */
minmax(16px, 2fr); /* 下スペーサー(上の2倍)*/
▲▲▲ ここまで ▲▲▲
2. 上下スペーサーの比率で「重心」をコントロール
1fr : 2frにすれば余白は上1:下2に分配 → 大画面でもボタンが親指の届く位置に残る。
3. 固定ギャップは必ず1箇所だけ
「この距離は絶対変えたくない」箇所にだけ固定値(28px)を入れる。他はすべてautoかminmax。
4. 空要素対策
▼▼▼ ここから ▼▼▼
.dots-wrap:empty { display: none; }
▲▲▲ ここまで ▲▲▲
min-heightは設定しないこと(あると`:empty`が効かなくなる)。
5. safe-areaを忘れない
▼▼▼ ここから ▼▼▼
padding-bottom: max(16px, env(safe-area-inset-bottom));
▲▲▲ ここまで ▲▲▲
6. プレビューは複数サイズを並べる
iPhone SE(375×667)とiPhone 17(393×852)を並べて両方確認。
やってはいけない3つ
・ `height: 100%`の連鎖 + `overflow: auto`で逃げる → スクロール前提の設計になる
・ `flex: 1`だけで余白を埋める → 大画面で問題文とボタンが離れすぎる
・ `min-height`で暫定回避 → `:empty`が効かなくなる
💡 失敗も全部、記録になる
今回いちばん気づいたのはここです。
エラーのスクショを撮って、Claudeに「これ、何が起きてる?」と聞くだけで進みます。
失敗した手順も、「こうするとダメだった」という一次情報になります。
エンジニアじゃなくても、試行錯誤の記録が「ノウハウ」になる。
コードがわからなくても、プロジェクトは動かせます✨
🎁 まとめ
💰 維持費:0円
🛠 使ったもの:Claude Code・Claude Design・Googleスプレッドシート
✋ 必要なスキル:コピペ・スプレッドシート操作
⏱ かかった時間:数日(失敗込み)
⌨️ コードを書いたか:ほぼ書いていない
問題を差し替えるだけで、どの学年でも使えると思います。
「子供用に何か作りたいけど、コードがわからない」という方、ぜひ試してみてください
(=゚ω゚)ノ
使ったツール:Claude Code / Claude.ai / Claude Design / Google Apps Script
----
シリーズ1本目:スマホだけでClaude Codeを操作する方法
シリーズ2本目:コードが読めない私のAIに「いつものやつ」が通じた7つの仕組み
シリーズ3本目:スマホからClaude Designでサイトをリデザインした話
シリーズ4本目:クロコが書いてくれた3行で、Canvaが Claude Code から操作できるようになった
シリーズ5本目:コードわからないままClaudeに丸投げして、算数ゲームをGoogleスプレッドシートの中に作った ←今ここ