こんにちは、AM0500です。
今回は、AI「Claude(claude 4 sonnet)」に指示を出して作ってみた、LINEツムツムのような?落ち物パズルゲームをご紹介します。ソースコードも紹介していますので是非作成を試してみてくださいね。
🔗 実際にプレイしてみる
▼ ブラウザでそのまま遊べます(PC推奨):
👉 LINEツムツムをイメージしたゲームをプレイする (外部ページに移動します)
作成に使用したツールはこちら
👉 使用しているAI Claude(claude 4 sonnet)はこちら
🎮 ゲーム画面(スクリーンショット)


📝 一言コメント
LINEのツムツムというゲームをAIが認識できるか。そしてどんなゲームになるのかを試した作品になります。本家とはちょっと違う感じにはなっていますが、思ったよりシンプルだけどおもしろいゲームになりました。
ちなみに200コンボくらいまでコンボ繋がりました。意外とアリです。
🧠 使用したClaudeへのプロンプト
以下のような指示で、今回のゲームが生成されました。
LINEツムツムのように、爽快感のある消しもの落ちパズルゲームを作ってください。このようなプロンプトで作成されたゲームになります。
説明文を読む限り、LINEツムツムというゲームを認識している感じでした。
🕹️ Claudeが返してくれた説明文
ゲームの特徴:
🎮 基本システム
同じ色のツムを3つ以上つなげてクリックで消去
8×12のグリッドで6色のかわいいツム
重力でツムが落下し、新しいツムが補充される
✨ 爽快感要素
連鎖システム:消した後に自動的に新しいマッチを探して連続消去
コンボシステム:連続で消すとスコアにボーナス
滑らかなアニメーション(落下、消去、選択エフェクト)
スコアエフェクト:点数が飛び出すアニメーション
🎨 視覚効果
グラデーション背景とグラスモーフィズムデザイン
選択時のグロー効果
消去時のスケールアニメーション
絵文字を使った可愛いツムデザイン
🎯 ゲームプレイ
戦略的思考:大きなグループを作ってより多くのポイント獲得
連鎖を狙って高得点を目指す
消せる組み合わせがなくなるとゲームオーバー
操作は簡単で、同じ色のツムが3つ以上つながっている部分をクリックするだけです。連鎖が発生すると自動的に消去が続き、非常に爽快感があります!ぜひプレイしてみてください!
💡 ソースコード(HTML)
ソースコード全体は少し長いです。これを一度に作成してくれます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ツムツム風パズルゲーム</title>
<style>
body {
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 50%, #fecfef 100%);
font-family: 'Comic Sans MS', cursive;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.game-container {
background: rgba(255, 255, 255, 0.9);
border-radius: 20px;
padding: 20px;
backdrop-filter: blur(10px);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
border: 3px solid #fff;
}
.game-info {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
font-size: 18px;
font-weight: bold;
color: #333;
}
.score-info {
background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
color: white;
padding: 10px 15px;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
}
.combo-info {
background: linear-gradient(45deg, #4ecdc4, #44a08d);
color: white;
padding: 10px 15px;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(78, 205, 196, 0.3);
}
canvas {
border: 3px solid #fff;
border-radius: 15px;
background: #f8f9fa;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
cursor: pointer;
}
.instructions {
text-align: center;
margin-top: 15px;
color: #666;
font-size: 14px;
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 30px;
border-radius: 20px;
text-align: center;
font-size: 24px;
display: none;
z-index: 1000;
}
.restart-btn {
background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
color: white;
border: none;
padding: 15px 30px;
border-radius: 25px;
margin-top: 15px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
transition: transform 0.2s;
}
.restart-btn:hover {
transform: scale(1.05);
}
.effect-text {
position: absolute;
font-size: 20px;
font-weight: bold;
color: #ff6b6b;
pointer-events: none;
z-index: 100;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
</style>
</head>
<body>
<div class="game-container">
<div class="game-info">
<div class="score-info">
スコア: <span id="score">0</span>
</div>
<div class="combo-info">
コンボ: <span id="combo">0</span>
</div>
</div>
<canvas id="gameCanvas" width="400" height="600"></canvas>
<div class="instructions">
同じ色のツムを3つ以上つなげてクリック!
</div>
<div class="game-over" id="gameOver">
<div id="gameOverText">ゲームオーバー</div>
<div style="font-size: 16px; margin: 10px 0;">
最終スコア: <span id="finalScore">0</span>
</div>
<button class="restart-btn" onclick="restartGame()">もう一度プレイ</button>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const comboElement = document.getElementById('combo');
const gameOverElement = document.getElementById('gameOver');
const finalScoreElement = document.getElementById('finalScore');
// ゲーム設定
const GRID_WIDTH = 8;
const GRID_HEIGHT = 12;
const TSUM_SIZE = 45;
const COLORS = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7', '#dda0dd'];
const EMOJI_TSUMS = ['🐭', '🐱', '🐶', '🐰', '🐸', '🐷'];
// ゲーム状態
let gameState = {
score: 0,
combo: 0,
grid: [],
selectedTsums: [],
isAnimating: false,
gameRunning: true
};
// ツムのクラス
class Tsum {
constructor(x, y, color, emoji) {
this.x = x;
this.y = y;
this.color = color;
this.emoji = emoji;
this.scale = 1;
this.targetY = y;
this.velocity = 0;
this.isSelected = false;
this.isDisappearing = false;
this.disappearProgress = 0;
}
draw() {
ctx.save();
const centerX = this.x * TSUM_SIZE + TSUM_SIZE / 2;
const centerY = this.y * TSUM_SIZE + TSUM_SIZE / 2;
// 消える効果
if (this.isDisappearing) {
ctx.globalAlpha = 1 - this.disappearProgress;
this.scale = 1 + this.disappearProgress * 0.5;
}
// 選択時の効果
if (this.isSelected) {
ctx.shadowColor = this.color;
ctx.shadowBlur = 20;
this.scale = 1.1;
}
ctx.translate(centerX, centerY);
ctx.scale(this.scale, this.scale);
// ツムの背景円
ctx.beginPath();
ctx.arc(0, 0, TSUM_SIZE * 0.4, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
// 外枠
ctx.strokeStyle = '#fff';
ctx.lineWidth = 3;
ctx.stroke();
// 絵文字
ctx.font = `${TSUM_SIZE * 0.5}px serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#fff';
ctx.fillText(this.emoji, 0, 0);
ctx.restore();
}
update() {
if (this.isDisappearing) {
this.disappearProgress += 0.1;
return this.disappearProgress >= 1;
}
// 落下アニメーション
if (this.y < this.targetY) {
this.velocity += 0.5;
this.y += this.velocity;
if (this.y >= this.targetY) {
this.y = this.targetY;
this.velocity = 0;
}
}
// スケールを元に戻す
if (this.scale > 1 && !this.isSelected) {
this.scale -= 0.05;
if (this.scale < 1) this.scale = 1;
}
return false;
}
}
// グリッドを初期化
function initGrid() {
gameState.grid = [];
for (let y = 0; y < GRID_HEIGHT; y++) {
gameState.grid[y] = [];
for (let x = 0; x < GRID_WIDTH; x++) {
const colorIndex = Math.floor(Math.random() * COLORS.length);
gameState.grid[y][x] = new Tsum(x, y, COLORS[colorIndex], EMOJI_TSUMS[colorIndex]);
}
}
}
// マウスクリック処理
canvas.addEventListener('click', (e) => {
if (!gameState.gameRunning || gameState.isAnimating) return;
const rect = canvas.getBoundingClientRect();
const x = Math.floor((e.clientX - rect.left) / TSUM_SIZE);
const y = Math.floor((e.clientY - rect.top) / TSUM_SIZE);
if (x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT) {
selectConnectedTsums(x, y);
}
});
// 接続されたツムを選択
function selectConnectedTsums(startX, startY) {
const targetTsum = gameState.grid[startY][startX];
if (!targetTsum) return;
// 既存の選択をクリア
clearSelection();
const visited = new Set();
const connected = [];
function dfs(x, y) {
const key = `${x},${y}`;
if (visited.has(key)) return;
if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return;
const tsum = gameState.grid[y][x];
if (!tsum || tsum.color !== targetTsum.color) return;
visited.add(key);
connected.push(tsum);
// 隣接するセルを探索
dfs(x + 1, y);
dfs(x - 1, y);
dfs(x, y + 1);
dfs(x, y - 1);
}
dfs(startX, startY);
if (connected.length >= 3) {
gameState.selectedTsums = connected;
connected.forEach(tsum => tsum.isSelected = true);
// 少し遅延してから消去
setTimeout(() => {
removeTsums();
}, 300);
}
}
// 選択をクリア
function clearSelection() {
gameState.selectedTsums.forEach(tsum => tsum.isSelected = false);
gameState.selectedTsums = [];
}
// ツムを消去
function removeTsums() {
if (gameState.selectedTsums.length === 0) return;
gameState.isAnimating = true;
// スコア計算
const count = gameState.selectedTsums.length;
const baseScore = count * 10;
const comboBonus = gameState.combo * 5;
const totalScore = baseScore + comboBonus;
gameState.score += totalScore;
gameState.combo++;
// エフェクトテキストを表示
showScoreEffect(totalScore, count);
// ツムを消去アニメーション開始
gameState.selectedTsums.forEach(tsum => {
tsum.isDisappearing = true;
tsum.isSelected = false;
});
setTimeout(() => {
// グリッドからツムを削除
gameState.selectedTsums.forEach(tsum => {
const x = tsum.x;
const y = tsum.y;
gameState.grid[y][x] = null;
});
gameState.selectedTsums = [];
// ツムを落下させる
dropTsums();
// 新しいツムを補充
setTimeout(() => {
fillEmptySpaces();
gameState.isAnimating = false;
// 自動的に消せる組み合わせをチェック
setTimeout(() => {
checkForAutoMatches();
}, 500);
}, 300);
}, 500);
}
// ツムを落下させる
function dropTsums() {
for (let x = 0; x < GRID_WIDTH; x++) {
// 下から上へ空きを詰める
let writeIndex = GRID_HEIGHT - 1;
for (let y = GRID_HEIGHT - 1; y >= 0; y--) {
if (gameState.grid[y][x]) {
if (writeIndex !== y) {
gameState.grid[writeIndex][x] = gameState.grid[y][x];
gameState.grid[writeIndex][x].targetY = writeIndex;
gameState.grid[y][x] = null;
}
writeIndex--;
}
}
}
}
// 空きスペースを補充
function fillEmptySpaces() {
for (let x = 0; x < GRID_WIDTH; x++) {
for (let y = 0; y < GRID_HEIGHT; y++) {
if (!gameState.grid[y][x]) {
const colorIndex = Math.floor(Math.random() * COLORS.length);
gameState.grid[y][x] = new Tsum(x, -1, COLORS[colorIndex], EMOJI_TSUMS[colorIndex]);
gameState.grid[y][x].targetY = y;
}
}
}
}
// 自動マッチをチェック
function checkForAutoMatches() {
let foundMatch = false;
for (let y = 0; y < GRID_HEIGHT && !foundMatch; y++) {
for (let x = 0; x < GRID_WIDTH && !foundMatch; x++) {
const tsum = gameState.grid[y][x];
if (tsum) {
const connected = getConnectedTsums(x, y, tsum.color);
if (connected.length >= 3) {
gameState.selectedTsums = connected;
connected.forEach(t => t.isSelected = true);
setTimeout(() => removeTsums(), 300);
foundMatch = true;
}
}
}
}
if (!foundMatch) {
gameState.combo = 0;
checkGameOver();
}
}
// 接続されたツムを取得
function getConnectedTsums(startX, startY, color) {
const visited = new Set();
const connected = [];
function dfs(x, y) {
const key = `${x},${y}`;
if (visited.has(key)) return;
if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return;
const tsum = gameState.grid[y][x];
if (!tsum || tsum.color !== color) return;
visited.add(key);
connected.push(tsum);
dfs(x + 1, y);
dfs(x - 1, y);
dfs(x, y + 1);
dfs(x, y - 1);
}
dfs(startX, startY);
return connected;
}
// スコアエフェクトを表示
function showScoreEffect(score, count) {
const effectDiv = document.createElement('div');
effectDiv.className = 'effect-text';
effectDiv.textContent = `+${score}`;
effectDiv.style.left = Math.random() * 200 + 200 + 'px';
effectDiv.style.top = Math.random() * 100 + 150 + 'px';
document.body.appendChild(effectDiv);
// アニメーション
let opacity = 1;
let y = 0;
const animate = () => {
opacity -= 0.02;
y -= 2;
effectDiv.style.opacity = opacity;
effectDiv.style.transform = `translateY(${y}px)`;
if (opacity > 0) {
requestAnimationFrame(animate);
} else {
document.body.removeChild(effectDiv);
}
};
animate();
}
// ゲームオーバーチェック
function checkGameOver() {
let hasValidMove = false;
for (let y = 0; y < GRID_HEIGHT && !hasValidMove; y++) {
for (let x = 0; x < GRID_WIDTH && !hasValidMove; x++) {
const tsum = gameState.grid[y][x];
if (tsum) {
const connected = getConnectedTsums(x, y, tsum.color);
if (connected.length >= 3) {
hasValidMove = true;
}
}
}
}
if (!hasValidMove) {
gameState.gameRunning = false;
finalScoreElement.textContent = gameState.score;
gameOverElement.style.display = 'block';
}
}
// ゲームを再開
function restartGame() {
gameState.score = 0;
gameState.combo = 0;
gameState.gameRunning = true;
gameState.isAnimating = false;
gameOverElement.style.display = 'none';
clearSelection();
initGrid();
}
// 描画
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// グリッドの背景
ctx.fillStyle = '#f8f9fa';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// ツムを描画
for (let y = 0; y < GRID_HEIGHT; y++) {
for (let x = 0; x < GRID_WIDTH; x++) {
const tsum = gameState.grid[y][x];
if (tsum) {
tsum.draw();
}
}
}
}
// 更新
function update() {
let needsUpdate = false;
for (let y = 0; y < GRID_HEIGHT; y++) {
for (let x = 0; x < GRID_WIDTH; x++) {
const tsum = gameState.grid[y][x];
if (tsum) {
const shouldRemove = tsum.update();
if (shouldRemove) {
gameState.grid[y][x] = null;
}
needsUpdate = true;
}
}
}
return needsUpdate;
}
// UI更新
function updateUI() {
scoreElement.textContent = gameState.score;
comboElement.textContent = gameState.combo;
}
// メインゲームループ
function gameLoop() {
update();
draw();
updateUI();
requestAnimationFrame(gameLoop);
}
// ゲーム開始
initGrid();
gameLoop();
</script>
</body>
</html>🔔 補足・注意事項
このコードや説明は、Claudeのバージョンや環境によって出力内容が異なる可能性があります。
同じ命令をしても、まったく違うコードが返ってくることもあるので、再現性は参考程度にご覧ください。

0 件のコメント:
コメントを投稿