capture-fish / index.html
GeminiLight's picture
Add 3 files
79050de verified
Raw
History Blame Contribute Delete
39.8 kB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>欢乐捕鱼</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes bubbleFloat {
0% { transform: translateY(0) scale(0.8); opacity: 0; }
50% { opacity: 0.8; }
100% { transform: translateY(-100px) scale(1.2); opacity: 0; }
}
@keyframes fishSwim {
0% { transform: translateX(0) rotateY(0deg); }
50% { transform: translateX(10px) rotateY(0deg); }
51% { transform: translateX(10px) rotateY(180deg); }
100% { transform: translateX(0) rotateY(180deg); }
}
.bubble {
animation: bubbleFloat 2s forwards;
}
.fish-animation {
animation: fishSwim 3s infinite;
}
#gameCanvas {
background: linear-gradient(to bottom, #1a237e, #00bcd4);
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
}
.wave {
background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 1200 120" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none"><path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.21,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z" opacity=".25" fill="%2300bcd4"/><path d="M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z" opacity=".5" fill="%2300bcd4"/><path d="M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z" fill="%2300bcd4"/></svg>');
background-size: cover;
height: 60px;
position: absolute;
bottom: 0;
width: 100%;
}
.powerup {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.hook-line {
transition: all 0.1s ease-out;
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full">
<header class="flex justify-between items-center mb-6">
<h1 class="text-4xl font-bold text-blue-300 flex items-center">
<i class="fas fa-fish mr-3"></i> 欢乐捕鱼
</h1>
<div class="flex items-center space-x-4">
<div class="bg-blue-900 bg-opacity-50 px-4 py-2 rounded-lg flex items-center">
<span class="text-yellow-300 mr-2">分数:</span>
<span id="score" class="font-bold text-xl">0</span>
</div>
<div class="bg-blue-900 bg-opacity-50 px-4 py-2 rounded-lg flex items-center">
<span class="text-green-300 mr-2">金币:</span>
<span id="coins" class="font-bold text-xl">100</span>
</div>
<div class="bg-blue-900 bg-opacity-50 px-4 py-2 rounded-lg flex items-center">
<span class="text-red-300 mr-2">时间:</span>
<span id="time" class="font-bold text-xl">60</span>
</div>
</div>
</header>
<div class="relative">
<canvas id="gameCanvas" width="800" height="500" class="w-full border-4 border-blue-800 relative overflow-hidden"></canvas>
<div class="wave"></div>
<!-- 游戏开始界面 -->
<div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-70 rounded-lg">
<div class="text-center p-8 bg-blue-900 bg-opacity-80 rounded-xl max-w-md">
<h2 class="text-3xl font-bold text-blue-300 mb-4">欢乐捕鱼</h2>
<p class="mb-6 text-blue-100">使用鼠标移动鱼钩,点击放下鱼钩捕鱼。收集金币升级装备,捕获稀有鱼类获得高分!</p>
<div class="mb-6">
<label for="difficulty" class="block text-blue-200 mb-2">选择难度:</label>
<select id="difficulty" class="bg-blue-800 text-white rounded px-3 py-2 w-full">
<option value="easy">简单</option>
<option value="medium" selected>中等</option>
<option value="hard">困难</option>
</select>
</div>
<button id="startButton" class="bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-8 rounded-full text-lg transition-all transform hover:scale-105">
<i class="fas fa-play mr-2"></i> 开始游戏
</button>
</div>
</div>
<!-- 游戏结束界面 -->
<div id="gameOverScreen" class="absolute inset-0 hidden flex-col items-center justify-center bg-black bg-opacity-70 rounded-lg">
<div class="text-center p-8 bg-blue-900 bg-opacity-80 rounded-xl max-w-md">
<h2 class="text-3xl font-bold text-red-400 mb-2">游戏结束</h2>
<p class="text-xl mb-1">最终得分: <span id="finalScore" class="text-yellow-300">0</span></p>
<p class="text-lg mb-1">捕获鱼类: <span id="totalFish" class="text-blue-300">0</span></p>
<p class="text-lg mb-6">获得金币: <span id="totalCoins" class="text-green-300">0</span></p>
<div class="flex space-x-4 justify-center">
<button id="restartButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-full transition-all transform hover:scale-105">
<i class="fas fa-redo mr-2"></i> 重新开始
</button>
<button id="menuButton" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-6 rounded-full transition-all transform hover:scale-105">
<i class="fas fa-home mr-2"></i> 主菜单
</button>
</div>
</div>
</div>
<!-- 升级商店 -->
<div id="upgradeScreen" class="absolute inset-0 hidden flex-col items-center justify-center bg-black bg-opacity-70 rounded-lg">
<div class="text-center p-6 bg-blue-900 bg-opacity-80 rounded-xl max-w-2xl w-full">
<h2 class="text-2xl font-bold text-blue-300 mb-4">渔具商店</h2>
<div class="grid grid-cols-3 gap-4 mb-6">
<div class="bg-blue-800 bg-opacity-60 p-4 rounded-lg">
<h3 class="font-bold text-yellow-300 mb-2">鱼钩尺寸</h3>
<p class="text-sm text-blue-100 mb-2">当前: <span id="hookSizeLevel" class="text-white">1级</span></p>
<p class="text-xs text-blue-200 mb-3">增加捕获大鱼几率</p>
<button class="upgrade-btn bg-blue-600 hover:bg-blue-700 text-white text-sm py-1 px-3 rounded" data-upgrade="hookSize">
升级 (<span class="cost" data-cost="50">50</span>金币)
</button>
</div>
<div class="bg-blue-800 bg-opacity-60 p-4 rounded-lg">
<h3 class="font-bold text-yellow-300 mb-2">鱼线长度</h3>
<p class="text-sm text-blue-100 mb-2">当前: <span id="lineLengthLevel" class="text-white">1级</span></p>
<p class="text-xs text-blue-200 mb-3">可以到达更深水域</p>
<button class="upgrade-btn bg-blue-600 hover:bg-blue-700 text-white text-sm py-1 px-3 rounded" data-upgrade="lineLength">
升级 (<span class="cost" data-cost="40">40</span>金币)
</button>
</div>
<div class="bg-blue-800 bg-opacity-60 p-4 rounded-lg">
<h3 class="font-bold text-yellow-300 mb-2">鱼饵质量</h3>
<p class="text-sm text-blue-100 mb-2">当前: <span id="baitQualityLevel" class="text-white">1级</span></p>
<p class="text-xs text-blue-200 mb-3">吸引更多稀有鱼类</p>
<button class="upgrade-btn bg-blue-600 hover:bg-blue-700 text-white text-sm py-1 px-3 rounded" data-upgrade="baitQuality">
升级 (<span class="cost" data-cost="60">60</span>金币)
</button>
</div>
</div>
<button id="continueButton" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-full">
<i class="fas fa-arrow-right mr-2"></i> 继续游戏
</button>
</div>
</div>
</div>
<div class="mt-6 flex justify-between items-center">
<div class="bg-blue-900 bg-opacity-50 px-4 py-3 rounded-lg">
<h3 class="text-lg font-semibold text-blue-300 mb-2">稀有鱼类</h3>
<div class="flex space-x-4">
<div class="flex items-center">
<div class="w-6 h-6 bg-yellow-400 rounded-full mr-2"></div>
<span>小黄鱼 (10分)</span>
</div>
<div class="flex items-center">
<div class="w-6 h-6 bg-red-500 rounded-full mr-2"></div>
<span>红鲤鱼 (30分)</span>
</div>
<div class="flex items-center">
<div class="w-6 h-6 bg-purple-500 rounded-full mr-2"></div>
<span>紫金鱼 (50分)</span>
</div>
</div>
</div>
<div class="flex space-x-4">
<button id="upgradeButton" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-6 rounded-full hidden">
<i class="fas fa-store mr-2"></i> 升级渔具
</button>
<button id="soundButton" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-6 rounded-full">
<i class="fas fa-volume-up mr-2"></i> 音效
</button>
</div>
</div>
</div>
<!-- 游戏音效 -->
<audio id="castSound" src="https://assets.mixkit.co/sfx/preview/mixkit-arrow-whoosh-1491.mp3" preload="auto"></audio>
<audio id="catchSound" src="https://assets.mixkit.co/sfx/preview/mixkit-winning-chimes-2015.mp3" preload="auto"></audio>
<audio id="coinSound" src="https://assets.mixkit.co/sfx/preview/mixkit-coins-handling-1939.mp3" preload="auto"></audio>
<audio id="upgradeSound" src="https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3" preload="auto"></audio>
<audio id="gameOverSound" src="https://assets.mixkit.co/sfx/preview/mixkit-sad-game-over-trombone-471.mp3" preload="auto"></audio>
<audio id="bgMusic" loop src="https://assets.mixkit.co/music/preview/mixkit-relaxing-waves-1391.mp3" preload="auto"></audio>
<script>
// 游戏状态
const gameState = {
score: 0,
coins: 100,
time: 60,
gameRunning: false,
soundEnabled: true,
paused: false,
casting: false,
hookDepth: 0,
maxDepth: 300,
fishCaught: 0,
upgrades: {
hookSize: { level: 1, cost: 50, value: 1 },
lineLength: { level: 1, cost: 40, value: 300 },
baitQuality: { level: 1, cost: 60, value: 1 }
},
difficulty: 'medium'
};
// 获取DOM元素
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const coinsElement = document.getElementById('coins');
const timeElement = document.getElementById('time');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const upgradeScreen = document.getElementById('upgradeScreen');
const finalScoreElement = document.getElementById('finalScore');
const totalFishElement = document.getElementById('totalFish');
const totalCoinsElement = document.getElementById('totalCoins');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const menuButton = document.getElementById('menuButton');
const upgradeButton = document.getElementById('upgradeButton');
const continueButton = document.getElementById('continueButton');
const soundButton = document.getElementById('soundButton');
const difficultySelect = document.getElementById('difficulty');
// 升级按钮元素
const hookSizeLevel = document.getElementById('hookSizeLevel');
const lineLengthLevel = document.getElementById('lineLengthLevel');
const baitQualityLevel = document.getElementById('baitQualityLevel');
const upgradeButtons = document.querySelectorAll('.upgrade-btn');
// 获取音效元素
const castSound = document.getElementById('castSound');
const catchSound = document.getElementById('catchSound');
const coinSound = document.getElementById('coinSound');
const upgradeSound = document.getElementById('upgradeSound');
const gameOverSound = document.getElementById('gameOverSound');
const bgMusic = document.getElementById('bgMusic');
// 游戏对象
const player = {
x: canvas.width / 2,
y: 30,
hookX: canvas.width / 2,
hookY: 30,
rodLength: 150,
hookSize: 10,
lineCast: false,
lineRetracting: false,
fishOnLine: null
};
const fishes = [];
const bubbles = [];
let gameTimer;
let countdownTimer;
// 鱼类数据
const fishTypes = [
{ name: '小黄鱼', color: '#FBBF24', score: 10, speed: 1.5, size: 20, rarity: 0.6 },
{ name: '蓝刀鱼', color: '#60A5FA', score: 20, speed: 2, size: 25, rarity: 0.3 },
{ name: '红鲤鱼', color: '#F87171', score: 30, speed: 1.8, size: 30, rarity: 0.2 },
{ name: '绿草鱼', color: '#34D399', score: 15, speed: 1.2, size: 35, rarity: 0.5 },
{ name: '紫金鱼', color: '#A78BFA', score: 50, speed: 2.5, size: 40, rarity: 0.1 }
];
// 事件监听器
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', startGame);
menuButton.addEventListener('click', showMenu);
upgradeButton.addEventListener('click', showUpgradeScreen);
continueButton.addEventListener('click', resumeFromUpgrade);
soundButton.addEventListener('click', toggleSound);
// 升级按钮事件
upgradeButtons.forEach(button => {
button.addEventListener('click', function() {
const upgradeType = this.getAttribute('data-upgrade');
purchaseUpgrade(upgradeType);
});
});
canvas.addEventListener('mousemove', (e) => {
if (!gameState.gameRunning || gameState.paused) return;
const rect = canvas.getBoundingClientRect();
player.x = e.clientX - rect.left;
if (!player.lineCast) {
player.hookX = player.x;
}
});
canvas.addEventListener('click', (e) => {
if (!gameState.gameRunning || gameState.paused) return;
if (!player.lineCast && !player.lineRetracting) {
castLine();
} else if (player.lineCast && !player.lineRetracting && player.hookDepth >= player.hookY) {
// 尝试收线
startReeling();
}
});
// 游戏函数
function startGame() {
// 设置难度
gameState.difficulty = difficultySelect.value;
// 重置游戏状态
gameState.score = 0;
gameState.coins = 100;
gameState.time = getInitialTime();
gameState.gameRunning = true;
gameState.paused = false;
gameState.casting = false;
gameState.hookDepth = 0;
gameState.fishCaught = 0;
// 重置玩家状态
player.x = canvas.width / 2;
player.hookX = canvas.width / 2;
player.hookY = 30;
player.lineCast = false;
player.lineRetracting = false;
player.fishOnLine = null;
// 清空游戏对象
fishes.length = 0;
bubbles.length = 0;
// 更新UI
scoreElement.textContent = gameState.score;
coinsElement.textContent = gameState.coins;
timeElement.textContent = gameState.time;
// 隐藏界面
startScreen.classList.add('hidden');
gameOverScreen.classList.add('hidden');
upgradeScreen.classList.add('hidden');
upgradeButton.classList.remove('hidden');
// 开始背景音乐
if (gameState.soundEnabled) {
bgMusic.currentTime = 0;
bgMusic.play();
}
// 开始游戏计时
clearInterval(gameTimer);
clearInterval(countdownTimer);
gameTimer = setInterval(spawnFish, 1000);
countdownTimer = setInterval(updateCountdown, 1000);
// 开始游戏循环
requestAnimationFrame(gameLoop);
}
function getInitialTime() {
switch (gameState.difficulty) {
case 'easy': return 90;
case 'medium': return 60;
case 'hard': return 45;
default: return 60;
}
}
function showMenu() {
gameState.gameRunning = false;
clearInterval(gameTimer);
clearInterval(countdownTimer);
gameOverScreen.classList.add('hidden');
upgradeScreen.classList.add('hidden');
startScreen.classList.remove('hidden');
upgradeButton.classList.add('hidden');
if (gameState.soundEnabled) {
bgMusic.pause();
}
}
function showUpgradeScreen() {
gameState.paused = true;
updateUpgradeDisplay();
upgradeScreen.classList.remove('hidden');
}
function resumeFromUpgrade() {
gameState.paused = false;
upgradeScreen.classList.add('hidden');
}
function updateUpgradeDisplay() {
hookSizeLevel.textContent = `${gameState.upgrades.hookSize.level}级`;
lineLengthLevel.textContent = `${gameState.upgrades.lineLength.level}级`;
baitQualityLevel.textContent = `${gameState.upgrades.baitQuality.level}级`;
// 更新升级按钮成本和状态
upgradeButtons.forEach(button => {
const upgradeType = button.getAttribute('data-upgrade');
const cost = gameState.upgrades[upgradeType].cost;
const costElement = button.querySelector('.cost');
costElement.textContent = cost;
costElement.setAttribute('data-cost', cost);
// 禁用不够钱的升级
if (gameState.coins < cost) {
button.disabled = true;
button.classList.add('opacity-50');
button.classList.remove('hover:bg-blue-700');
} else {
button.disabled = false;
button.classList.remove('opacity-50');
button.classList.add('hover:bg-blue-700');
}
});
}
function purchaseUpgrade(type) {
const upgrade = gameState.upgrades[type];
if (gameState.coins >= upgrade.cost) {
gameState.coins -= upgrade.cost;
coinsElement.textContent = gameState.coins;
upgrade.level++;
// 根据升级类型增加效果
switch (type) {
case 'hookSize':
upgrade.value = upgrade.level;
player.hookSize = 10 + (upgrade.level - 1) * 3;
break;
case 'lineLength':
upgrade.value = 300 + (upgrade.level - 1) * 50;
gameState.maxDepth = upgrade.value;
break;
case 'baitQuality':
upgrade.value = upgrade.level;
break;
}
// 增加下一次升级成本
upgrade.cost = Math.floor(upgrade.cost * 1.5);
// 更新显示
updateUpgradeDisplay();
if (gameState.soundEnabled) {
upgradeSound.currentTime = 0;
upgradeSound.play();
}
}
}
function toggleSound() {
gameState.soundEnabled = !gameState.soundEnabled;
if (gameState.soundEnabled) {
soundButton.innerHTML = '<i class="fas fa-volume-up mr-2"></i> 音效';
if (gameState.gameRunning && !gameState.paused) {
bgMusic.play();
}
} else {
soundButton.innerHTML = '<i class="fas fa-volume-mute mr-2"></i> 音效';
bgMusic.pause();
}
}
function gameOver() {
gameState.gameRunning = false;
clearInterval(gameTimer);
clearInterval(countdownTimer);
finalScoreElement.textContent = gameState.score;
totalFishElement.textContent = gameState.fishCaught;
totalCoinsElement.textContent = gameState.coins;
gameOverScreen.classList.remove('hidden');
upgradeButton.classList.add('hidden');
if (gameState.soundEnabled) {
bgMusic.pause();
gameOverSound.play();
}
}
function updateCountdown() {
gameState.time--;
timeElement.textContent = gameState.time;
if (gameState.time <= 0) {
gameOver();
}
}
function castLine() {
player.lineCast = true;
player.hookY = 30;
player.hookDepth = 0;
if (gameState.soundEnabled) {
castSound.currentTime = 0;
castSound.play();
}
}
function startReeling() {
player.lineRetracting = true;
}
function spawnFish() {
if (!gameState.gameRunning || gameState.paused) return;
// 根据难度调整生成频率
let spawnChance = 0.7; // 默认中等难度
if (gameState.difficulty === 'easy') spawnChance = 0.9;
if (gameState.difficulty === 'hard') spawnChance = 0.5;
if (Math.random() > spawnChance) return;
// 根据鱼饵质量调整稀有鱼出现几率
const baitFactor = gameState.upgrades.baitQuality.value * 0.1;
// 选择鱼类型,考虑稀有度
let availableFish = fishTypes.map(fish => {
// 提高稀有鱼的几率基于鱼饵质量
const adjustedRarity = Math.min(1, fish.rarity + baitFactor);
return { ...fish, selectionWeight: adjustedRarity };
});
// 根据权重随机选择鱼
const totalWeight = availableFish.reduce((sum, fish) => sum + fish.selectionWeight, 0);
let random = Math.random() * totalWeight;
let selectedFish;
for (const fish of availableFish) {
if (random < fish.selectionWeight) {
selectedFish = fish;
break;
}
random -= fish.selectionWeight;
}
// 确保总是选择一种鱼
selectedFish = selectedFish || availableFish[0];
// 随机位置 (从左右两侧出现)
const side = Math.random() > 0.5 ? 1 : -1;
const x = side > 0 ? -selectedFish.size : canvas.width + selectedFish.size;
const y = 100 + Math.random() * (canvas.height - 200);
// 随机速度 (基于难度)
let speed = selectedFish.speed;
if (gameState.difficulty === 'easy') speed *= 0.8;
if (gameState.difficulty === 'hard') speed *= 1.3;
fishes.push({
x: x,
y: y,
width: selectedFish.size,
height: selectedFish.size / 2,
speed: speed * side,
color: selectedFish.color,
score: selectedFish.score,
name: selectedFish.name,
direction: side,
value: selectedFish.score,
wiggle: Math.random() * 5
});
}
function spawnBubble(x, y, size) {
bubbles.push({
x: x,
y: y,
size: size,
speed: 1 + Math.random() * 2,
opacity: 0.3 + Math.random() * 0.7
});
}
function updatePlayer() {
// 更新鱼钩位置
if (player.lineCast && !player.lineRetracting) {
// 放下鱼线
player.hookY += 3;
player.hookDepth = player.hookY;
// 随机生成气泡
if (Math.random() < 0.05) {
spawnBubble(
player.hookX + (Math.random() * 20 - 10),
player.hookY,
3 + Math.random() * 7
);
}
// 检查是否到达最大深度
if (player.hookY >= gameState.maxDepth) {
startReeling();
}
} else if (player.lineRetracting) {
// 收回鱼线
player.hookY -= 5;
// 如果有鱼在钩上,一起拉上来
if (player.fishOnLine) {
player.fishOnLine.y -= 5;
// 检查是否到达水面
if (player.hookY <= 30) {
// 捕获成功
gameState.score += player.fishOnLine.value;
gameState.coins += Math.floor(player.fishOnLine.value / 2);
gameState.fishCaught++;
scoreElement.textContent = gameState.score;
coinsElement.textContent = gameState.coins;
// 从鱼数组中移除
const index = fishes.indexOf(player.fishOnLine);
if (index !== -1) {
fishes.splice(index, 1);
}
player.fishOnLine = null;
if (gameState.soundEnabled) {
catchSound.currentTime = 0;
catchSound.play();
coinSound.currentTime = 0;
coinSound.play();
}
}
}
// 检查是否完全收回
if (player.hookY <= 30) {
player.lineCast = false;
player.lineRetracting = false;
player.hookY = 30;
}
}
}
function updateFishes() {
for (let i = fishes.length - 1; i >= 0; i--) {
const fish = fishes[i];
// 更新鱼的位置
fish.x += fish.speed;
fish.wiggle += 0.1;
// 随机生成气泡
if (Math.random() < 0.01) {
spawnBubble(
fish.x - fish.width/2 + (Math.random() * fish.width),
fish.y + (Math.random() * fish.height) - fish.height/2,
2 + Math.random() * 5
);
}
// 检查是否游出屏幕
if ((fish.direction > 0 && fish.x > canvas.width + fish.width) ||
(fish.direction < 0 && fish.x < -fish.width)) {
fishes.splice(i, 1);
}
// 检查是否咬钩
if (player.lineCast && !player.lineRetracting && !player.fishOnLine) {
const hookSize = player.hookSize * gameState.upgrades.hookSize.value;
// 简单碰撞检测
if (Math.abs(fish.x - player.hookX) < fish.width/2 + hookSize/2 &&
Math.abs(fish.y - player.hookY) < fish.height/2 + hookSize/2) {
// 根据鱼的大小和钩的大小计算捕获几率
const catchChance = Math.min(0.8, hookSize / fish.width);
if (Math.random() < catchChance) {
player.fishOnLine = fish;
// 改变鱼的颜色表示被捕获
fish.color = '#FFFFFF';
}
}
}
}
}
function updateBubbles() {
for (let i = bubbles.length - 1; i >= 0; i--) {
const bubble = bubbles[i];
bubble.y -= bubble.speed;
// 移除超出屏幕的气泡
if (bubble.y < -bubble.size) {
bubbles.splice(i, 1);
}
}
}
function drawBackground() {
// 绘制渐变背景
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient.addColorStop(0, '#1a237e');
gradient.addColorStop(1, '#00bcd4');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制海底
ctx.fillStyle = '#5C4033';
ctx.beginPath();
ctx.moveTo(0, canvas.height);
// 创建波浪形海底
for (let x = 0; x <= canvas.width; x += 20) {
const y = canvas.height - 30 + Math.sin(x * 0.02) * 15;
ctx.lineTo(x, y);
}
ctx.lineTo(canvas.width, canvas.height);
ctx.closePath();
ctx.fill();
// 绘制一些海底植物
drawSeaPlants();
}
function drawSeaPlants() {
// 海草
ctx.save();
for (let i = 0; i < 10; i++) {
const x = Math.random() * canvas.width;
const height = 30 + Math.random() * 70;
ctx.fillStyle = `hsl(${100 + Math.random() * 40}, 70%, 40%)`;
ctx.beginPath();
ctx.moveTo(x, canvas.height);
// 弯曲的海草
for (let y = canvas.height; y >= canvas.height - height; y -= 5) {
const offset = Math.sin((canvas.height - y) * 0.1) * 15;
ctx.lineTo(x + offset, y);
}
ctx.lineTo(x, canvas.height);
ctx.closePath();
ctx.fill();
}
ctx.restore();
}
function drawPlayer() {
ctx.save();
// 绘制钓竿
ctx.strokeStyle = '#78350F';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(player.x, 0);
ctx.lineTo(player.x, player.rodLength);
ctx.stroke();
// 绘制鱼线
ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(player.x, player.rodLength);
ctx.lineTo(player.hookX, player.hookY);
ctx.stroke();
// 绘制鱼钩
ctx.fillStyle = '#D4D4D8';
ctx.beginPath();
ctx.arc(player.hookX, player.hookY, player.hookSize/2, 0, Math.PI * 2);
ctx.fill();
// 如果有鱼在钩上,绘制鱼
if (player.fishOnLine) {
const fish = player.fishOnLine;
drawFish(fish, true);
}
ctx.restore();
}
function drawFishes() {
for (const fish of fishes) {
drawFish(fish);
}
}
function drawFish(fish, onHook = false) {
ctx.save();
if (onHook) {
// 被钩住的鱼
ctx.translate(player.hookX, player.hookY);
ctx.rotate(Math.PI / 2);
} else {
// 游动的鱼
ctx.translate(fish.x, fish.y);
if (fish.direction < 0) {
ctx.scale(-1, 1);
}
// 摆动效果
ctx.rotate(Math.sin(fish.wiggle) * 0.1);
}
// 鱼身
ctx.fillStyle = fish.color;
ctx.beginPath();
ctx.ellipse(0, 0, fish.width / 2, fish.height / 2, 0, 0, Math.PI * 2);
ctx.fill();
// 鱼尾
ctx.beginPath();
ctx.moveTo(-fish.width / 2, 0);
ctx.lineTo(-fish.width / 2 - 10, -fish.height / 2);
ctx.lineTo(-fish.width / 2 - 10, fish.height / 2);
ctx.closePath();
ctx.fill();
// 鱼眼
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(fish.width / 4, -fish.height / 4, fish.width / 8, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(fish.width / 4, -fish.height / 4, fish.width / 16, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
function drawBubbles() {
ctx.save();
for (const bubble of bubbles) {
ctx.fillStyle = `rgba(173, 216, 230, ${bubble.opacity})`;
ctx.beginPath();
ctx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2);
ctx.fill();
// 气泡高光
ctx.fillStyle = `rgba(255, 255, 255, ${bubble.opacity * 0.7})`;
ctx.beginPath();
ctx.arc(bubble.x - bubble.size/3, bubble.y - bubble.size/3, bubble.size/4, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
function drawDepthMeter() {
if (!player.lineCast) return;
ctx.save();
// 背景
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(20, 50, 20, gameState.maxDepth);
// 当前深度
const depthPercentage = player.hookDepth / gameState.maxDepth;
ctx.fillStyle = '#3B82F6';
ctx.fillRect(20, 50 + depthPercentage * gameState.maxDepth, 20, 5);
// 文本
ctx.fillStyle = 'white';
ctx.font = '12px Arial';
ctx.fillText('深度', 25, 40);
ctx.restore();
}
// 游戏循环
function gameLoop() {
if (!gameState.gameRunning || gameState.paused) return;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制背景
drawBackground();
// 更新游戏状态
updatePlayer();
updateFishes();
updateBubbles();
// 绘制游戏对象
drawFishes();
drawBubbles();
drawPlayer();
drawDepthMeter();
requestAnimationFrame(gameLoop);
}
// 初始显示开始界面
showMenu();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=GeminiLight/capture-fish" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>