柴犬育成ゲーム お好きな柴ちゃんを選んで育てていきます
このゲームはブラウザゲームで、ダウンロードなしでプレイできる仕様となっています。ただ、作成者MOMOは、この柴っちGO!ゲームをWEBに公開していません。なので本ゲームプログラムの参考コードを公開して、どなたでもUIのデザイン変更・コードの追加、削除、修正等をご自由に変更してもらい、開発者様が独自のゲーム開発を目的として、参考になればと思う次第です。また、部分的な関数コードや全体もコピペ等の制限は一切ございません。
開発者様がコードを参考にしてWEBで公開された場合、著作権の帰属は開発者様となりますので、ご安心してください。
ただし、全体を変更せずに丸ごとWEB上に公開する事はご縁了ください。個人で使用する限りでは制限はありません。
画像の見本(一部)









| 仕様 |
| A 各ボタン・表示 |
| 1.柴犬選択 2.愛犬名の入力 3.家族に迎える 4.わんちゃんを変更する 5.家族になった日 6.愛犬名 7.愛犬年齢 8.体力・体調バー 9.薬残量バー 10.ごはん・おやつ・おみず・さんぽの各バー 11.柴ちゃんの現在の調子表示 12.ごはん・おやつ・水・クスリ・散歩・お手・お座りの各アイテム 13.柴ちゃんのメッセージ表示 14.成長に従って柴ちゃん・背景を変更 15.終了・継続ボタン |
| B 保存方式 |
| 1.ローカルストレージ採用 ※キャッシュ等のクリアを考慮した場合はサーバー保存を推奨 |
| C ゲームの流れ |
| 1.3種類の中の柴ちゃんを選択する 2.生後2か月で育成スタート 実際のPCのカレンダーと同期して、その日を色々とお世話をする。柴犬のメッセージに従って各アイテムをクリックして体力や体調を維持する。当日23:59分までお世話ができるけど、日付が変わると当日分はお世話できない。 3.1年後は成犬画像に変更 4.1年2か月後は老犬表示 5.1年3か月後は愛犬の老衰でEND ※1か月を犬年齢7年として経過を考慮 6.なので、このゲームは通算で1年3か月を要します ※ゲーム開始から途中でキャンセルする仕様は設けていません。理由としては、折角家族として迎えた柴ちゃんを放棄するような事なので、最後まで面倒を見る事が大事と考えました。なので、最後には”これでお別れにしたい””もう一度家族に迎い入れたい”の選択ボタンを設けています。 |
| D 画像使用 |
| 1.APNG画像対応。フレームレート(fps)を利用してアニメーションを表示、静止画像もOK 24種類の柴犬画像を使用 2.背景はバックグランド・キャンバス内を含んで3種類 |
| E その他 |
| 1. 任意の日付変更抑止(日付を改ざんした場合のエラーメッセージ) |
| 概要 |
| 1.ゲーム構成 |
| 現在のゲーム構成は柴犬の選択(ステージ1)・育成(ステージ2)からスタートして、中間を省略してEND構成にしていますが、switchでステージ3・4・5まで想定した仮のコードを書いていますので、成長に従ってステージを進めると、よりリアルになると思います。また、1年3か月でゲームENDとしていますが、期間の調整や体力・体調が0の状態でENDにするなどの工夫も良いかもしれません。 |
| 2.コード |
| JSコードの行数が多くなっていますが、繰り返しコードの関数化、case文の縮小化(関数作成)、1行横並び表示の書き方など工夫をすると、かなりの行数が短縮できます。また、アルゴリズムの変更でさらによりリアルな育成ゲームになると思います。 |
| 3.使用画像 |
| 使用している画像については、APNG画像(静止画を含む)で単純な動作アニメです。画像のフレーム数を多くして作成すると、柴犬のリアル感がでるので、作成できる方は取り入れた方が良いと思います ※このページの最後に使用画像の内容やダウンロードを設けていますので必要であればどうぞ。 |
| 4.データ管理 |
| 育成データの保存については、ブラウザ上で本アプリは起動しているため、ブラウザ内のローカルストレージで保存処理を行っています。履歴データの削除、キャッシュの削除などの操作を行った場合は、育成途中でもデータがクリアされ初期状態に戻ります。これらを回避するにはデータをサーバーで管理するのを推奨します。 |
| 5.参考コードについて |
| 下記のコードの使用は、全てコードの変更・修正・追加が可能です。 ※コードのバグ、動作不具合等は一応確認済みですが、万が一不具合が発生した場合は開発者様で修正をお願いします。 |
| 各コードの紹介 順番にHTML JS CSS |
| HTML(メインはキャンバスで表示) |
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>柴っちGO!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="uiContainer">
<div id="gameContainer">
<div id="startTitle">柴っちGO!</div>
</div>
</div>
<div id="gameArea">
<div id="topUI">
<div id="nameInputArea" style="display:none;">
<input id="dogName" placeholder="愛犬の名前(全角6文字迄)">
<button id="startGameBtn">家族に迎える</button>
</div>
<button id="changeSelectBtn" style="display:none;">わんちゃんを変更する</button>
</div>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<img id="dogAnim"
style="
position:absolute;
left:240px;
top:180px;
width:320px;
display:none;
pointer-events:none;
z-index:10;
">
<div id="dogMessageBox"
style="
position:absolute;
width:360px;
height:60px;
left:50%;
transform:translateX(-50%);
background:rgba(245, 233, 192, 0.322);
color: rgb(49, 11, 46);
border-radius:18px;
padding:6px;
font-size:20px;
display:none;
z-index:20;
">
</div>
<div id="endingButtons">
<button id="goodbyeBtn" class="endingBtn goodbye">
🌈 これでお別れにしたい
</button>
<button id="restartBtn" class="endingBtn restart">
🐾 もう一度家族にしたい
</button>
</div>
<p id="copyright"> 2026 MOMO PLAN</p>
</div>
<script src="script.js"></script>
</body>
</html>
| JS |
// 画像プリロード
let currentDogImage = null;
const preloadList = [
'img/start_a.png', //スタート画像
'img/start_k.png',
'img/start_s.png'
];
let preloadCount = 0;
function preloadImages(callback) {
preloadList.forEach(src => {
const img = new Image();
img.onload = () => {
preloadCount++;
if (preloadCount === preloadList.length) {
callback(); //全画像ロード完了 → 描画開始
}
};
img.src = src;
});
}
//起動時:画像プリロード → 初期 or ステージ2
window.onload = () => {
preloadImages(() => {
const save = localStorage.getItem("shibacchi_save");
if (!save) {
// セーブデータなし
drawSelectScreen();
drawBottomFrame(
"私たちとお約束してください",
[
"私たちと一緒になった瞬間、ご主人様と一生過ごします🐶",
"ただ、ごはんやお水がもらえないと、私は力が尽きてしまいます🐶",
"体調が悪くなったらお薬をください。お薬が貰えないと、私は力が尽きてしまいます🐶",
"でも、毎日触れ合っても、何年か先はお別れの日が必ず来ます🐶",
"その時は感謝を込めて・・・・・🐶"
]
);
return;
}
// セーブデータあり
const state = JSON.parse(save);
switch (Number(state.stage)) {
case 2:
startStage2(state);
break;
case 3:
startStage3(state); //追加枠
break;
case 4:
startStage4(state); //追加枠
break;
case 5:
startStage5(state); //追加枠
break;
default:
drawSelectScreen();
}
});
};
// loadImg
function loadImg(src) {
const img = new Image();
img.src = src;
return img;
}
// 犬画像セット
const dogImages = {
aka: {
start: loadImg('img/start_a.png'), //スタート
puppy: loadImg('img/a01.png'), //子犬
adult: loadImg('img/a02.png'), //成犬
puppyOte:loadImg('img/a01_ote.png'), //お手
puppyOsuwari:loadImg('img/a01_osuwari.png'), //お座り
old: loadImg('img/a03.png'), //老犬
sick: loadImg('img/shiba15.png'), //病気
end: loadImg('img/r_end_shiba.png') //終了
},
kuro: {
start: loadImg('img/start_k.png'),
puppy: loadImg('img/k01.png'),
puppyOte:loadImg('img/k01_ote.png'),
puppyOsuwari:loadImg('img/k01_osuwari.png'),
adult: loadImg('img/k02.png'),
old: loadImg('img/k03.png'),
sick: loadImg('img/shiba17.png'),
end: loadImg('img/b_end_shiba.png')
},
shiro: {
start: loadImg('img/start_s.png'),
puppy: loadImg('img/s01.png'),
puppyOte:loadImg('img/s01_ote.png'),
puppyOsuwari:loadImg('img/s01_osuwari.png'),
adult: loadImg('img/s02.png'),
old: loadImg('img/s03.png'),
sick: loadImg('img/shiba16.png'),
end: loadImg('img/w_end_shiba.png')
}
};
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let selectedDogId = null;
let dogSelectAreas = [];
// 選択画面描画
const bgCanvas = loadImg("img/bg_room.png");
const bgPark = loadImg("img/dog_park.png");
function drawSelectScreen() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Canvas 背景画像
if (bgCanvas) {
ctx.drawImage(bgCanvas, 0, 0, canvas.width, canvas.height);
}
ctx.fillStyle = "#333";
ctx.font = "28px sans-serif";
ctx.fillText("🐶家族にしたい柴犬を選んでね🐶", 200, 60);
const dogs = [
{ id: 'aka', img: dogImages.aka.start },
{ id: 'kuro', img: dogImages.kuro.start },
{ id: 'shiro',img: dogImages.shiro.start }
];
const imgW = 200;
const imgH = 200;
const startX = 80;
const y = 150;
dogSelectAreas = [];
dogs.forEach((dog, i) => {
const x = startX + i * 240;
ctx.drawImage(dog.img, x, y, imgW, imgH);
// 犬の種類ごとに文字色を変える
ctx.font = "20px sans-serif";
ctx.fillStyle =
dog.id === 'aka' ? "#694f07bb" : // 赤柴
dog.id === 'kuro' ? "#000" : // 黒柴
"#fff"; // 白柴
const label =
dog.id === 'aka' ? '赤柴(女の子)' :
dog.id === 'kuro' ? '黒柴(男の子)' :
'白柴(女の子)';
ctx.fillText(label, x + 40, y + imgH + 30);
dogSelectAreas.push({ id: dog.id, x, y, w: imgW, h: imgH });
});
if (selectedDogId) {
const dog = dogSelectAreas.find(d => d.id === selectedDogId);
if (dog) highlightSelectedDog(dog);
}
}
// 案内
function drawBottomFrame(title, lines) {
ctx.save();
// 1行あたりの高さ
const lineHeight = 26;
// 最終行の下に余白を追加
const bottomPadding = 20;
// タイトル + 行数に応じて高さを自動計算
const frameHeight = 40 + (lines.length * lineHeight) + bottomPadding;
//柴犬の描画範囲より下に固定(安全領域)
const y = canvas.height - frameHeight;
// 半透明の黒帯
ctx.fillStyle = "rgba(22, 22, 22, 0.45)";
ctx.fillRect(0, y, canvas.width, frameHeight);
// 白枠線
ctx.strokeStyle = "#fff";
ctx.lineWidth = 2;
ctx.strokeRect(0, y, canvas.width, frameHeight);
// タイトル(中央)
ctx.fillStyle = "#ffffff";
ctx.font = "18px sans-serif";
ctx.textAlign = "center";
ctx.fillText(title, canvas.width / 2, y + 30);
// 箇条書き(左寄せ)
ctx.textAlign = "left";
ctx.font = "16px sans-serif";
lines.forEach((line, i) => {
ctx.fillText(
"・ " + line,
20,
y + 60 + i * lineHeight
);
});
ctx.restore();
}
// クリック処理(選択したら他は無効)
function getPointerPosition(e) {
const rect = canvas.getBoundingClientRect();
// タッチ操作の場合
if (e.touches && e.touches.length > 0) {
return {
x: e.touches[0].clientX - rect.left,
y: e.touches[0].clientY - rect.top
};
}
// マウス操作の場合
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
// PC・スマホ共通イベント
canvas.addEventListener("click", handleCanvasClick);
canvas.addEventListener("touchstart", handleCanvasClick);
function handleCanvasClick(e) {
// タッチ時のスクロール防止
if (e.cancelable) e.preventDefault();
const save = localStorage.getItem("shibacchi_save");
// セーブデータがない=犬選択画面
if (!save) {
handleSelect(e);
return;
}
const state = JSON.parse(save);
switch (state.stage) {
case 2:
//handleStage2Click(e, state);
break;
case 3:
//handleStage3Click(e, state);
break;
case 4:
//handleStage4Click(e, state);
break;
case 5:
// 必要になったら追加
break;
}
}
function handleSelect(e) {
if (selectedDogId) return;
const pos = getPointerPosition(e);
const mx = pos.x;
const my = pos.y;
for (const dog of dogSelectAreas) {
if (mx >= dog.x && mx <= dog.x + dog.w &&
my >= dog.y && my <= dog.y + dog.h) {
selectedDogId = dog.id;
document.getElementById('nameInputArea').style.display = 'block';
document.getElementById('changeSelectBtn').style.display = 'inline-block';
drawSelectScreen();
break;
}
}
}
const dogFrameColors = {
aka: "#ac730b", // 赤
kuro: "#000000", // 黒
shiro: "#ffffff" // 白
};
function highlightSelectedDog(dog) {
ctx.strokeStyle = dogFrameColors[dog.id];
ctx.lineWidth = 5;
ctx.strokeRect(dog.x, dog.y, dog.w, dog.h);
}
document.getElementById('changeSelectBtn').addEventListener('click', () => {
selectedDogId = null;
document.getElementById('nameInputArea').style.display = 'none';
document.getElementById('changeSelectBtn').style.display = 'none';
drawSelectScreen();
});
// 名前決定&保存
function showCanvasMessage(text) {
ctx.save();
// 半透明の黒帯
ctx.fillStyle = "rgba(0,0,0,0.6)";
ctx.fillRect(0, canvas.height - 120, canvas.width, 120);
// メッセージ文字
ctx.fillStyle = "#fff";
ctx.font = "24px sans-serif";
ctx.textAlign = "center";
const lines = text.split("\n");
lines.forEach((line, i) => {
ctx.fillText(line, canvas.width / 2, canvas.height - 80 + i * 30);
});
ctx.restore();
}
document.getElementById('startGameBtn').addEventListener('click', () => {
const raw = document.getElementById('dogName').value.trim();
if (!raw) {
showCanvasMessage("私の名前を入れて..🐶");
return;
}
//全角換算で6文字までかチェック
let count = 0;
for (const ch of raw) {
const isFull = /[^\x00-\x7F]/.test(ch); // ASCII以外を全角扱い
count += isFull ? 1 : 0.5;
}
if (count > 6) {
showCanvasMessage("名前は全角6文字までだよ🐾");
return;
}
const name = raw; // ここまで来たらOK
if (!selectedDogId) {
showCanvasMessage("柴犬を選んでください");
return;
}
document.getElementById('nameInputArea').style.display = "none";
document.getElementById('changeSelectBtn').style.display = "none";
drawSelectedDogCenter(selectedDogId);
let message = "";
if (selectedDogId === "aka" || selectedDogId === "shiro") {
message = `私を選んでくれてありがとう…\n私の名前は ${name}。\nこれからよろしくね。`;
} else {
message = `僕を選んでくれてありがとう…\n僕の名前は ${name}。\nこれからよろしくね。`;
}
showCanvasMessage(message);
const today = getTodayJP();
const state = {
id: selectedDogId,
name: name,
adoptedDate: today,
lastPlayDate: today,
stage: 2,
isSick: false,
lastFeedDate: null,
lastFeedCountToday: 0,
lastWaterDate: null,
lastMedicineDate: null,
lastWalkDate: null,
healthStatus: 'normal',
deathReason: null
};
localStorage.setItem('shibacchi_save', JSON.stringify(state));
setTimeout(() => {
startStage2(state);
}, 4000);
});
// 日付取得
function getTodayJP() {
const d = new Date();
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
// 日付変換
function formatDateJP(dateStr) {
const [year, month, day] = dateStr.split("-");
return `${year}年${month}月${day}日`;
}
// 愛犬取得
function drawAdoptedDate(date) {
ctx.save();
const jpDate = formatDateJP(date);
//白背景(白膜ゼロ)
ctx.fillStyle = "rgba(255, 255, 255, 0.53)";
ctx.roundRect(20, 20, 170, 55, 12);
ctx.fill();
// 枠線
ctx.strokeStyle = "rgba(255, 255, 255, 0.9)";
ctx.lineWidth = 1;
ctx.roundRect(20, 20, 170, 55, 12);
ctx.stroke();
// テキスト(影なし)
ctx.shadowColor = "transparent";
ctx.fillStyle = "#4e1cd8";
ctx.font = "bold 20px sans-serif";
ctx.fillText("家族になった日", 35, 45);
ctx.fillStyle = "#0b5e04";
ctx.font = "bold 18px sans-serif";
ctx.fillText(jpDate, 35, 65);
ctx.restore();
}
// 愛犬名
function drawDogName(name) {
ctx.save();
// 影なし
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 枠線だけ(紫)
ctx.strokeStyle = "#eeeeee";
ctx.lineWidth = 1;
ctx.roundRect(200, 20, 170, 55, 12);
ctx.stroke();
// タイトル
ctx.fillStyle = "#4e1cd8";
ctx.font = "bold 20px sans-serif";
ctx.fillText("愛犬名", 215, 45);
// 名前
ctx.fillStyle = "#744508";
ctx.font = "bold 18px sans-serif";
ctx.fillText(name, 215, 65);
ctx.restore();
}
function drawDogAge(adoptedDateStr) {
ctx.save();
const ageText = calcDogAgeText(adoptedDateStr);
// 完全に影なし
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 枠線
ctx.strokeStyle = "#eeeeee";
ctx.lineWidth = 1;
ctx.roundRect(380, 20, 170, 55, 12);
ctx.stroke();
// タイトル
ctx.fillStyle = "#4e1cd8";
ctx.font = "bold 20px sans-serif";
ctx.fillText("愛犬年齢", 395, 45);
// 年齢表示
ctx.fillStyle = "#9c0790";
ctx.font = "bold 18px sans-serif";
ctx.fillText(ageText, 395, 65);
ctx.restore();
}
// 該当画像演出
function drawSelectedDogCenter(dogId) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 背景
ctx.drawImage(bgCanvas, 0, 0, canvas.width, canvas.height);
// 選んだ柴犬の画像
const img = dogId === "aka" ? dogImages.aka.start
: dogId === "kuro" ? dogImages.kuro.start
: dogImages.shiro.start;
const w = 300;
const h = 300;
const x = (canvas.width - w) / 2;
const y = (canvas.height - h) / 2 - 40;
ctx.drawImage(img, x, y, w, h);
}
function getDogAgeMonths(adoptedDateStr) {
const start = new Date(adoptedDateStr);
const today = new Date();
start.setHours(0,0,0,0);
today.setHours(0,0,0,0);
let months =
(today.getFullYear() - start.getFullYear()) * 12 +
(today.getMonth() - start.getMonth());
//生後2か月スタート分を加算
months += 2;
return months;
}
// 現在表示画像を決定
function redrawStage2(state) {
const ageMonths = getDogAgeMonths(state.adoptedDate);
// エンディング判定
// 15か月(1年3か月)以上
if (isGameEnd(state)) {
// 初回だけ終了処理
if (!state.gameEnd) {
state.gameEnd = true;
localStorage.setItem("shibacchi_save",JSON.stringify(state));
showDogMessage(`${state.name} は今までありがとう🐶🌈\n`+ `ずっと大切な家族でした・・・😢`);
}
document.getElementById("endingButtons").style.display = "block";
currentDogImage =dogImages[state.id].end;
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(bgStage2,0,0,canvas.width,canvas.height);
drawStage2Dog(state.id,state);
return;
}
let stageKey;
if (ageMonths >= 14) {
stageKey = "old";
} else if (ageMonths >= 12) {
stageKey = "adult";
} else {
stageKey = "puppy";
}
if (state.isSick) {
currentDogImage = dogImages[state.id].sick;
} else if (
state.stage === 2 && state.dogAction === "ote"
) {
currentDogImage = dogImages[state.id].puppyOte;
} else if (
state.stage === 2 && state.dogAction === "osuwari"
) {
currentDogImage = dogImages[state.id].puppyOsuwari;
} else {
currentDogImage = dogImages[state.id][stageKey];
}
// 描画
ctx.clearRect(0,0,canvas.width,canvas.height);
// 年齢による背景変更
// 0〜11か月 : 子犬 → 部屋
// 12〜13か月 : 成犬 → 公園
// 14か月以上 : 老犬 → 部屋
let bg;
if (ageMonths >= 14) {
// 老犬
bg = bgStage2;
} else if (ageMonths >= 12) {
// 成犬
bg = bgPark;
} else {
// 子犬
bg = bgStage2;
}
ctx.drawImage(bg,0,0,canvas.width,canvas.height);
drawAdoptedDate(state.adoptedDate);
drawDogName(state.name);
drawDogAge(state.adoptedDate);
drawStage2Dog(state.id,state);
drawStage2Items();
drawCareBars(state);
drawProgressBar(30,110,state.power,10,"体力");
drawProgressBar(30,150,state.health,10,"体調");
drawDogMood(state);
}
function showDogMessage(text) {
const box = document.getElementById("dogMessageBox");
box.innerText = text;
positionDogMessageBox(); // 位置をCanvasに合わせる
box.style.display = "block";
}
function hideDogMessage() {
const box = document.getElementById("dogMessageBox");
box.style.display = "none";
}
function drawProgressBar(x, y, value, maxValue, label) {
const width = 200;
const height = 10;
// 枠
/*ctx.strokeStyle = "#fff";
ctx.lineWidth = 2;
ctx.strokeRect(x, y, width, height);*/
//色を条件で変更
let barColor = "lime"; // 通常色
if (value <= 5) {
if(label === "体力")
barColor = "orange";
if(label === "体調")
barColor = "orange";
}
if(value <= 2){
if(label === "体力")
barColor = "red";
if(label === "体調")
barColor = "red";
}
// 内側(割合)
const ratio = value / maxValue;
ctx.fillStyle = barColor;
ctx.fillRect(x, y, width * ratio, height);
const displayValue = Math.floor(value);
// ラベル
ctx.fillStyle = "#91108a";
ctx.font = "16px sans-serif";
ctx.fillText(
`${label}: ${displayValue}/${maxValue}`,
x,
y - 5
);
//4以下なら ⚠️ を左側に表示
if (value <= 4) {
ctx.fillStyle = "yellow";
ctx.font = "20px sans-serif";
ctx.fillText("⚠️", x - 30, y + height - 2);
}
}
// 柴犬の気持ち表示
function getDogMood(state) {
const mood =
(state.power + state.health) / 2;
if (state.isSick) {
return "少しつらそう…🐶";
}
if (mood >= 8) {
return "満足しているよ🐶✨";
}
if (mood >= 5) {
return "やや満足しているよ🐶";
}
if (mood >= 3) {
return "少し不満そう…🐶";
}
return "元気がないみたい…🐶";
}
function drawDogMood(state) {
const moodText = getDogMood(state);
const text =
`${state.name} の気持ちは${moodText}`;
ctx.fillStyle = "#251c31";
ctx.font = "bold 16px sans-serif";
//元気がない時だけ点滅
const blink =
Math.floor(Date.now() / 1000) % 2 === 0;
if (
moodText.includes("元気がないみたい…🐶") ||
moodText.includes("少しつらそう…🐶")
) {
if (blink) {
ctx.fillText(
text,
210,
200
);
}
} else {
ctx.fillText(
text,
210,
200
);
}
}
// ステージ2(仮表示)
const bgStage2 = loadImg("img/bg_room.png");
function startStage2(state) {
const today = getTodayJP();
// 初期データ確認
state.power ??= 10;
state.health ??= 10;
state.dogAction ??= "normal";
// ===== 要求回数 =====
state.dailyCare ??= {date: today,food: 0,snack: 0,water: 0,walk: 0};
// ===== 各バー値 =====
state.careBar ??= {food: 2,snack: 3,water: 3,walk: 3,medicine: 1};
// 日付チェック
state.lastPlayDate ??= today;
const now = new Date(today);
const last = new Date(state.lastPlayDate);
// 時計を戻した場合
if (now < last) {
showDogMessage(
`${state.name} は何才かわからなくなっちゃった。\nお世話になりました・・・🐶`
);
return;
}
//未来ジャンプ確認
const diff =
(now - last) / (1000 * 60 * 60 * 24);
if (diff > 2) {
showDogMessage(
`${state.name} の時間が急に進みました🐶`
);
return;
}
// 日付変更処理
if (state.dailyCare.date !== today) {
// 前日の世話不足を反映
checkDailyPenalty(state);
// 自然病気チェック
checkNaturalSick(state);
// 今日の要求回数リセット
state.dailyCare = {
date: today,
food: 0,
snack: 0,
water: 0,
walk: 0
};
// おねだり終了状態解除
state.todayCareFinished = false;
}
// 最終保存日更新
state.lastPlayDate = today;
localStorage.setItem(
"shibacchi_save",
JSON.stringify(state)
);
// 画像設定
const ageMonths = getDogAgeMonths(state.adoptedDate);
let stageKey;
if (ageMonths >= 14) {
stageKey = "old";
} else if (ageMonths >= 12) {
stageKey = "adult";
} else {
stageKey = "puppy";
}
if (state.isSick) {
currentDogImage = dogImages[state.id].sick;
} else if (
state.stage === 2 &&
state.dogAction === "ote"
) {
currentDogImage = dogImages[state.id].puppyOte;
} else if (
state.stage === 2 &&
state.dogAction === "osuwari"
) {
currentDogImage = dogImages[state.id].puppyOsuwari;
} else {currentDogImage = dogImages[state.id][stageKey];
}
// 描画
ctx.clearRect(0,0,canvas.width,canvas.height);
//年齢で背景変更
const bg =
getDogAgeYears(state.adoptedDate) >= 1
? bgPark
: bgStage2;
ctx.drawImage(bg,0,0,canvas.width,canvas.height);
drawAdoptedDate(state.adoptedDate);
drawStage2Dog(state.id,state);
drawDogName(state.name);
drawDogAge(state.adoptedDate);
drawStage2Items();
drawCareBars(state);
drawDogMood(state);
drawProgressBar(30, 110, state.power, 10, "体力");
drawProgressBar(30, 150, state.health, 10, "体調");
if (!state.pendingMessage) {
drawBottomFrame(
"子犬期の案内",
[
"食事と水をしっかり与えてね🐾",
"具合が悪そうだったらクスリをあげてね🐾",
"散歩も定期的に連れてってあげてね、それと遊びとスキンシップが大切だよ🐶🐾"
]
);
}
if (state.pendingMessage) {
showDogMessage(state.pendingMessage);
state.pendingMessage = null;
localStorage.setItem("shibacchi_save",JSON.stringify(state));
}
startRandomDogMessages(state);
}
let dogMessageTimer = null;
// 待機フラグ
let waitingForFood = false;
let waitingForSnack = false;
let waitingForWater = false;
let waitingForMedicine = false;
let waitingForWalk = false;
// タイマーID
let foodTimeoutId = null;
let snackTimeoutId = null;
let waterTimeoutId = null;
let medicineTimeoutId = null;
let walkTimeoutId = null;
function startRandomDogMessages(state) {
if (dogMessageTimer) clearInterval(dogMessageTimer);
dogMessageTimer = setInterval(() => {
const state =
JSON.parse(localStorage.getItem("shibacchi_save"));
//ゲーム終了なら何もしない
if (state.gameEnd) {
clearInterval(dogMessageTimer);
dogMessageTimer = null;
return;
}
// 日付が変わったら回数リセット
const today = new Date().toLocaleDateString("ja-JP");
state.dailyCare ??= {
date: today,
food: 0,
snack: 0,
water: 0,
walk: 0
};
if (state.dailyCare.date !== today) {
state.dailyCare = {
date: today,
food: 0,
snack: 0,
water: 0,
walk: 0
};
localStorage.setItem("shibacchi_save", JSON.stringify(state));
}
// 病気中はクスリ要求固定
if (state.isSick) {
redrawStage2(state);
showDogMessage(`${state.name} はクスリを欲しがっています💊\nすぐに飲ませてください🐶`);
return;
}
// 今日まだ表示できるイベントを作成
const events = [];
if (state.dailyCare.food < 2)
events.push("food");
if (state.dailyCare.snack < 3)
events.push("snack");
if (state.dailyCare.water < 3)
events.push("water");
if (state.dailyCare.walk < 3)
events.push("walk");
//全て終了なら今日のおねだり終了
if (events.length === 0) {
if (!state.todayCareFinished) {
state.todayCareFinished = true;
localStorage.setItem("shibacchi_save",JSON.stringify(state));
showDogMessage(
`${state.name} は今日のおねだりを終えました🐶\nまた明日よろしくね`
);
}
return;
}
//ランダムに処理
const event =
events[Math.floor(Math.random() * events.length)];
// 案内を消す
const guide = document.getElementById("stage2Guide");
if (guide) guide.style.display = "none";
redrawStage2(state);
switch (event) {
// ごはん
case "food":
waitingForFood = true;
//今日の表示回数をカウント
state.dailyCare.food++;
localStorage.setItem("shibacchi_save", JSON.stringify(state));
showDogMessage(`${state.name} はごはんを食べたいみたい🐶`);
foodTimeoutId = setTimeout(() => {
if (waitingForFood) {
//最新のセーブデータを取得
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
save.careBar.food = Math.max(0, save.careBar.food - 1);
save.power = Math.max(0, save.power - 1);
waitingForFood = false;
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
}, 15000);
break;
// おやつ
case "snack":
waitingForSnack = true;
state.dailyCare.snack++;
localStorage.setItem("shibacchi_save", JSON.stringify(state));
showDogMessage(`${state.name} はおやつを食べたそうにしています🍪`);
snackTimeoutId = setTimeout(() => {
if (waitingForSnack) {
//最新のセーブデータを取得
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
save.careBar.snack = Math.max(0, save.careBar.snack - 1);
waitingForSnack = false;
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
}, 15000);
break;
// 水
case "water":
waitingForWater = true;
state.dailyCare.water++;
localStorage.setItem("shibacchi_save", JSON.stringify(state));
showDogMessage(`${state.name} は水を飲みたいみたい🐶`);
waterTimeoutId = setTimeout(() => {
if (waitingForWater) {
//最新のセーブデータを取得
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
save.careBar.water = Math.max(0, save.careBar.water - 1);
save.health = Math.max(0, save.health - 1);
waitingForWater = false;
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
}, 15000);
break;
// クスリ
case "medicine":
waitingForMedicine = true;
state.isSick = true;
//クスリバーを0にする
state.careBar.medicine = 0;
// 病気画像へ切替
currentDogImage = dogImages[state.id].sick;
localStorage.setItem("shibacchi_save", JSON.stringify(state));
redrawStage2(state);
showDogMessage(`${state.name} は具合が悪そう…💊`);
medicineTimeoutId = setTimeout(() => {
if (waitingForMedicine) {
//最新のセーブデータを取得
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
//クスリバー減少
save.careBar.medicine =
Math.max(0, save.careBar.medicine - 1);
//体調減少
save.health =
Math.max(0, save.health - 1);
waitingForMedicine = false;
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
}, 15000);
break;
// 散歩
case "walk":
waitingForWalk = true;
state.dailyCare.walk++;
localStorage.setItem("shibacchi_save", JSON.stringify(state));
showDogMessage(`${state.name} は散歩に行きたいみたい🚶♂️`);
walkTimeoutId = setTimeout(() => {
if (waitingForWalk) {
//最新のセーブデータを取得
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
save.careBar.walk = Math.max(0, save.careBar.walk - 1);
save.power = Math.max(0, save.power - 1);
waitingForWalk = false;
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
}, 15000);
break;
}
}, 16000);
}
canvas.addEventListener("click", (e) => {
const pos = getPointerPosition(e);
const mx = pos.x;
const my = pos.y;
const startX = 680;
const w = 110;
const h = 50;
const startY = 26;
const gap = 60;
// ごはん
if (
mx >= startX && mx <= startX + w &&
my >= startY && my <= startY + h
) {
giveFood();
return;
}
// おやつ
if (
mx >= startX && mx <= startX + w &&
my >= startY + gap &&
my <= startY + gap + h
) {
giveSnack();
return;
}
// 水
if (
mx >= startX && mx <= startX + w &&
my >= startY + gap * 2 &&
my <= startY + gap * 2 + h
) {
giveWater();
return;
}
// クスリ
if (
mx >= startX && mx <= startX + w &&
my >= startY + gap * 3 &&
my <= startY + gap * 3 + h
) {
giveMedicine();
return;
}
// 散歩
if (
mx >= startX && mx <= startX + w &&
my >= startY + gap * 4 &&
my <= startY + gap * 4 + h
) {
giveWalk();
return;
}
// お手
if (
mx >= startX && mx <= startX + w &&
my >= startY + gap * 5 &&
my <= startY + gap * 5 + h
) {
giveOte();
return;
}
// お座り
if (
mx >= startX && mx <= startX + w &&
my >= startY + gap * 6 &&
my <= startY + gap * 6 + h
) {
giveOsuwari();
return;
}
});
function giveFood() {
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
//おねだり中以外は無効
if (!waitingForFood) {
return;
}
waitingForFood = false;
if (foodTimeoutId) {
clearTimeout(foodTimeoutId);
foodTimeoutId = null;
}
save.power = Math.min(10, save.power + 1);
//ごはんバー回復
save.careBar.food = 2;
showDogMessage(`${save.name} はごはんを食べました🍚`);
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
function giveWater() {
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
//おねだり中以外は無効
if (!waitingForWater) {
return;
}
waitingForWater = false;
if (waterTimeoutId) {
clearTimeout(waterTimeoutId);
waterTimeoutId = null;
}
//体調回復
save.health = Math.min(10, save.health + 1);
//水バー回復
save.careBar.water = 3;
showDogMessage(
`${save.name} は水を飲みました💧`
);
localStorage.setItem(
"shibacchi_save",
JSON.stringify(save)
);
redrawStage2(save);
}
function giveSnack() {
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
//おねだり中以外は無効
if (!waitingForSnack) {
return;
}
waitingForSnack = false;
if (snackTimeoutId) {
clearTimeout(snackTimeoutId);
snackTimeoutId = null;
}
//おやつバー回復
save.careBar.snack = 3;
showDogMessage(`${save.name} はおやつを食べました🍪`);
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
function giveMedicine() {
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
// 病気ではない時は使用不可
if (!save.isSick) {
return;
}
// クスリがない場合
if (save.careBar.medicine <= 0) {
showDogMessage(
"クスリがありません💊"
);
return;
}
waitingForMedicine = false;
if (medicineTimeoutId) {
clearTimeout(medicineTimeoutId);
medicineTimeoutId = null;
}
//病気解除
save.isSick = false;
save.healthStatus = "normal";
//体調回復
save.health = Math.min(10, save.health + 1);
//クスリ消費
save.careBar.medicine =
Math.max(0, save.careBar.medicine - 1);
//ステージ画像へ戻す
const ageMonths = getDogAgeMonths(save.adoptedDate);
let stageKey;
if (ageMonths >= 14) {
stageKey = "old";
} else if (ageMonths >= 12) {
stageKey = "adult";
} else {
stageKey = "puppy";
}
currentDogImage =
dogImages[save.id][stageKey];
showDogMessage(
`${save.name} はクスリを飲みました💊`
);
localStorage.setItem(
"shibacchi_save",
JSON.stringify(save)
);
redrawStage2(save);
}
function giveWalk() {
const save = JSON.parse(localStorage.getItem("shibacchi_save"));
//おねだり中以外は無効
if (!waitingForWalk) {
return;
}
waitingForWalk = false;
if (walkTimeoutId) {
clearTimeout(walkTimeoutId);
walkTimeoutId = null;
}
showDogMessage(`${save.name} は散歩に行ってきました🚶♂️`);
save.careBar.walk = 3;
localStorage.setItem("shibacchi_save", JSON.stringify(save));
redrawStage2(save);
}
function giveOte(){
const save =
JSON.parse(localStorage.getItem("shibacchi_save"));
if(save.dogAction === "ote"){
save.dogAction = "normal";
}else{
save.dogAction = "ote";
}
localStorage.setItem("shibacchi_save",JSON.stringify(save));
redrawStage2(save);
}
function giveOsuwari(){
const save =
JSON.parse(
localStorage.getItem("shibacchi_save")
);
if(save.dogAction === "osuwari"){
save.dogAction = "normal";
} else {
save.dogAction = "osuwari";
}
localStorage.setItem("shibacchi_save",JSON.stringify(save));
redrawStage2(save);
}
function checkDailyPenalty(state) {
// ごはん
if (state.careBar.food === 0) {
state.power = Math.max(0, state.power - 1);
}
// おみず
if (state.careBar.water === 0) {
state.health = Math.max(0, state.health - 1);
}
// さんぽ
if (state.careBar.walk === 0) {
state.power = Math.max(0, state.power - 0.5);
}
// おやつ
if (state.careBar.snack === 0) {
state.health = Math.max(0, state.health - 0.2);
}
// 体調0なら病気になる
if (state.health <= 0) {
state.health = 0;
state.isSick = true;
state.healthStatus = "sick";
//後で表示する
state.pendingMessage =
`${state.name} は少し具合が悪そうです🐶`;
}
if (
getDogAgeYears(state.adoptedDate) >= 1 &&
state.stage === 2
) {
state.stage = 3;
if (!state.pendingMessage) {
state.pendingMessage =
`${state.name} は立派な成犬になりました🐶✨`;
}
}
// 翌日用にバーを満タンへ
state.careBar.food = 2;
state.careBar.snack = 3;
state.careBar.water = 3;
state.careBar.walk = 3;
state.careBar.medicine = 1;
localStorage.setItem("shibacchi_save", JSON.stringify(state));
}
function checkNaturalSick(state) {
// すでに病気なら不要
if (state.isSick) {
return;
}
// 年齢による病気率
let sickRate = 0.005; // 子犬 0.5%
if (state.stage === 3) {
sickRate = 0.002; // 成犬 0.2%
}
if (state.stage >= 4) {
sickRate = 0.02; // 老犬 2%
}
// 体調が低い場合は少し上昇
if (state.health <= 3) {
sickRate += 0.03;
}
// ランダム判定
if (Math.random() < sickRate) {
state.isSick = true;
state.healthStatus = "sick";
state.health = Math.max(
0,
state.health - 3
);
state.pendingMessage =
`${state.name} は少し具合が悪そうです🐶`;
}
}
// アイテム
function drawItemBox(label, x, y) {
ctx.save();
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 枠線だけ
ctx.strokeStyle = "#0d942e";
ctx.lineWidth = 1;
ctx.roundRect(x, y, 110, 50, 12);
ctx.stroke();
// 絵文字+テキスト
ctx.font = "bold 18px sans-serif";
ctx.fillStyle = "#423908"; // 通常色
ctx.font = "bold 18px sans-serif";
ctx.fillText(label, x + 15, y + 32);
ctx.restore();
}
function drawStage2Items() {
const startX = 680;
let startY = 26;
const gap = 60;
drawItemBox("🍚 ごはん", startX, startY);
startY += gap;
drawItemBox("🍪 おやつ", startX, startY);
startY += gap;
drawItemBox("💧 水", startX, startY);
startY += gap;
drawItemBox("💊 クスリ", startX, startY);
startY += gap;
drawItemBox("🚶♂️ 散歩", startX, startY);
startY += gap;
drawItemBox("🐾 お手", startX, startY);
startY += gap;
drawItemBox("🐾 お座り", startX, startY);
}
function drawCareBars(state) {
const y1 = 120; // 1行目
const y2 = 160; // 2行目
const y3 = 200; // 3行目
drawOneCareBar("🍚 ごはん", state.careBar.food, 2, 250, y1);
drawOneCareBar("💧 おみず", state.careBar.water, 3, 460, y1);
drawOneCareBar("🍪 おやつ", state.careBar.snack, 3, 250, y2);
drawOneCareBar("🚶 さんぽ", state.careBar.walk, 3, 460, y2);
drawOneCareBar("💊 薬残量", state.careBar.medicine, 1, 2, y3);
}
function drawOneCareBar(title, value, max, x, y) {
ctx.font = "16px sans-serif";
if (title === "💊 薬残量") {
ctx.shadowColor = 'rgba(8, 8, 8, 0.5)';
ctx.shadowOffsetX = 6;
ctx.shadowOffsetY = 6;
ctx.shadowBlur = 4;
// 図形の描画
ctx.fillStyle = '#d9ff00';
} else {
ctx.fillStyle = "#220433";
}
ctx.fillText(title, x, y);
const barX = x + 80;
const barY = y - 14;
const barWidth = 80;
const barHeight = 14;
// 枠
/*ctx.strokeStyle = "#acacac";
ctx.strokeRect(barX, barY, barWidth, barHeight);*/
// 塗り
ctx.fillStyle = "#3cb371";
ctx.fillRect(
barX,
barY,
barWidth * (value / max),
barHeight
);
// 数値
ctx.fillStyle = "#5c52ec";
ctx.fillText(`${value}/${max}`, barX + 90, y);
}
// ステージ2選択された画像表示
const DOG_X = 220;
const DOG_Y = 220;
const DOG_W = 260;
const DOG_H = 260;
let currentDogImageType = "normal";
function drawStage2Dog(dogId, state) {
const dog = document.getElementById("dogAnim");
if (dog.src !== currentDogImage.src) {
dog.src = currentDogImage.src;
}
dog.style.left = DOG_X + "px";
dog.style.top = DOG_Y + "px";
dog.style.width = DOG_W + "px";
dog.style.height = DOG_H + "px";
dog.style.objectFit = "contain";
dog.style.display = "block";
}
function hideSelectUI() {
document.getElementById('nameInputArea').style.display = 'none';
document.getElementById('changeSelectBtn').style.display = 'none';
}
// 月齢表示
function calcDogAgeText(adoptedDateStr) {
const start = new Date(adoptedDateStr);
const today = new Date();
start.setHours(0,0,0,0);
today.setHours(0,0,0,0);
let years = today.getFullYear() - start.getFullYear();
let months = today.getMonth() - start.getMonth();
let days = today.getDate() - start.getDate();
if (days < 0) {
months--;
const prevMonth =
new Date(today.getFullYear(), today.getMonth(), 0);
days += prevMonth.getDate();
}
if (months < 0) {
years--;
months += 12;
}
// 初期2か月を加算
months += 2;
if (months >= 12) {
years += Math.floor(months / 12);
months = months % 12;
}
if (years === 0) {
return `${months}か月と${days}日`;
}
return `${years}才${months}か月と${days}日`;
}
function getDogAgeYears(adoptedDateStr) {
const start = new Date(adoptedDateStr);
const today = new Date();
start.setHours(0,0,0,0);
today.setHours(0,0,0,0);
let years = today.getFullYear() - start.getFullYear();
let months = today.getMonth() - start.getMonth();
let days = today.getDate() - start.getDate();
if (days < 0) {
months--;
days += new Date(
today.getFullYear(),
today.getMonth(),
0
).getDate();
}
if (months < 0) {
years--;
months += 12;
}
// 初期2か月を加算
months += 2;
if (months >= 12) {
years += Math.floor(months / 12);
}
return years;
}
//終了判定
function isGameEnd(state) {
const ageMonths =
getDogAgeMonths(state.adoptedDate);
return ageMonths >= 15;
}
// 終了ボタン
document.getElementById("goodbyeBtn").onclick = () => {
localStorage.removeItem("shibacchi_save");
window.close();
};
// 再開処理
document.getElementById("restartBtn").onclick = () => {
localStorage.removeItem("shibacchi_save");
location.reload();
};
//年齢判定
function drawAdultScreen(data) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#333";
ctx.font = "28px sans-serif";
ctx.fillText("ステージ3(成犬画面)※まだ未実装", 200, 300);
}
function drawOldScreen(data) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#333";
ctx.font = "28px sans-serif";
ctx.fillText("ステージ4(老犬画面)※まだ未実装", 200, 300);
}
function drawDeadScreen(data) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#333";
ctx.font = "28px sans-serif";
ctx.fillText("ステージ5(寿命)※まだ未実装", 200, 300);
}
const MSG_W = 440; //メッセージの幅をここで決める
function positionDogMessageBox() {
const box = document.getElementById("dogMessageBox");
box.style.width = MSG_W + "px";
const x = DOG_X - (MSG_W - 620); //幅に合わせて中央に配置
const y = DOG_Y + DOG_H + 50;
box.style.left = x + "px";
box.style.top = y + "px";
}
window.addEventListener("resize", positionDogMessageBox);
| CSS |
body {
margin: 0;
background-image: url("img/bg_park.png");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
html {
touch-action: manipulation;
}
canvas {
background: transparent;
border: 4px solid #12c43f;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.6);
border-radius: 12px;
}
canvas {
touch-action: manipulation;
}
button {
touch-action: manipulation;
font-size: 16px;
height: 40px;
}
input {
font-size: 16px;
padding: 10px;
}
#startTitle {
color: #fff;
font-size: 32px;
text-align: center;
margin-bottom: 20px;
text-shadow: 0 0 8px rgba(0,0,0,0.6);
}
#startMessage {
color: #ccc;
font-size: 20px;
text-align: center;
margin-bottom: 30px;
}
#uiContainer {
position: absolute;
top: 60px;
width: 100%;
text-align: center;
pointer-events: none;
}
#uiContainer button,
#uiContainer input {
pointer-events: auto;
}
#nameInputArea {
margin-top: 20px;
text-align: center;
}
#dogName {
width: 200px;
border-radius: 6px;
border: 2px solid #12c43f;
background-color: #ffffff;
color: #690ee0;
}
#startGameBtn {
width: 200px;
border-radius: 6px;
border: 2px solid #12c43f;
background-color: #222;
color: #fff;
}
#changeSelectBtn {
margin-top: 20px;
background-color: #692ef3;
color: #fff;
border: 2px solid #12c43f;
border-radius: 6px;
}
#gameContainer {
position: fixed;
left: 50%;
top: 48%;
width: 800px;
height: 600px;
transform: translate(-50%, -50%);
}
#startTitle {
position: absolute;
top: -42px;
left: 0;
width: 100%;
text-align: center;
font-size: 32px;
font-weight: bold;
color: #fff;
text-shadow: 2px 2px 4px #000;
pointer-events: none;
}
#gameCanvas {
display: block;
}
#topUI {
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
width: 800px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
pointer-events: auto;
z-index: 30;
}
#gameArea {
position: fixed;
left: 50%;
top: 50%;
width: 800px;
height: 600px;
transform: translate(-50%, -50%);
}
#copyright{
color: #fff;
font-size: 12px;
text-align: center;
margin-bottom: 20px;
text-shadow: 0 0 8px rgba(0,0,0,0.6);
}
#endingButtons {
display:none;
position:absolute;
left:50%;
top:430px;
width:560px;
transform:translateX(-50%);
flex-direction:row;
gap:20px;
align-items:center;
z-index:50;
}
.endingBtn {
display: inline-block;
padding: 0 20px;
height:55px;
border:none;
border-radius:30px;
font-size:18px;
font-weight:bold;
cursor:pointer;
letter-spacing:1px;
box-shadow:0 5px 10px rgba(0,0,0,0.25);
transition:transform 0.2s, box-shadow 0.2s;
}
/* ホバー */
.endingBtn:hover {
transform:translateY(-5px);
box-shadow:
0 10px 18px rgba(0,0,0,0.3);
}
/* お別れ */
.goodbye {
background:#fff0f5;
color:#8b3a62;
border:2px solid #e6a7bf;
}
/* 再スタート */
.restart {
background:#fff8dc;
color:#8a5a00;
border:2px solid #e7c86e;
}以上です。
画像に付いてですが、全ての画像が圧縮ファイル(ing.zip)に入っていますが、こちらの画像を使用してWEBで公開した場合はクレジット表記が必要です。コード開発用として試験的に使用される場合は除きます。
圧縮ファイルの内容(柴犬画像・背景画像)
- imgフォルダー (a01.png a01_osuwari.png a01_ote.png a02.png a03.png b_end_shiba.png bg_park.png bg_room.png dog_park.png k01.png k01_osuwari.png k01_ote.png k02.png k03.png r_end_shiba.png s01.png s01_osuwari.png s01_ote.png s02.png s03.png shiba15.png shiba16.png shiba17.png start_k.png start_s.png w_end_shiba.png)
- ファイルの階層(配置)
shiba
|
|------index.html
|------script.js
|------style.css
|--img
|
|--a01.png
・
・
・
