// pages/game/index.js - 游戏主逻辑(纯原生实现) const app = getApp(); const audioManager = require('../../utils/AudioManager'); // 水果配置 const FRUIT_CONFIGS = [ { level: 1, name: '果切丁', radius: 18, color: '#FF6B6B', score: 1 }, { level: 2, name: '西瓜片', radius: 25, color: '#4ECDC4', score: 3 }, { level: 3, name: '紫葡萄', radius: 32, color: '#9B59B6', score: 9 }, { level: 4, name: '黄柠檬', radius: 40, color: '#F1C40F', score: 27 }, { level: 5, name: '红苹果', radius: 48, color: '#E74C3C', score: 81 }, { level: 6, name: '甜橙', radius: 56, color: '#F39C12', score: 243 }, { level: 7, name: '粉桃子', radius: 64, color: '#FF69B4', score: 729 }, { level: 8, name: '金菠萝', radius: 76, color: '#FFD700', score: 2187 }, { level: 9, name: '报恩榴莲', radius: 90, color: '#FFA500', score: 6561 } ]; // 关卡配置 const LEVEL_CONFIGS = [ { level: 1, bossName: '贪吃喵星人', targetLevel: 3, feedCount: 3, dropMin: 1, dropMax: 2 }, { level: 2, bossName: '吃货柴犬', targetLevel: 4, feedCount: 4, dropMin: 1, dropMax: 3 }, { level: 3, bossName: '美食家熊猫', targetLevel: 5, feedCount: 5, dropMin: 2, dropMax: 4 }, // ... 更多关卡可后续添加 ]; Page({ data: { currentLevel: 1, score: 0, combo: 0, bossName: '', fedCount: 0, targetFeed: 0, feedProgress: 0, items: { bomb: 3, freeze: 2 }, showReviveBtn: false, isPaused: false, showResult: false, isClear: false, finalScore: 0, finalCombo: 0, finalTime: 0, showGoldenEffect: false }, // 游戏状态 gameState: { fruits: [], // 场上的水果数组 nextFruitLevel: 1, // 下一个掉落的水果等级 fruitIdCounter: 0, // 水果ID计数器 dropCooldown: 0, // 掉落冷却时间 failTimer: 0, // 失败计时器 startTime: 0, // 游戏开始时间 sessionMaxCombo: 0, // 本局最高连击 mergeCore: null // 合成核心引擎 }, // Canvas 上下文 gameCanvas: null, previewCanvas: null, ctx: null, previewCtx: null, onLoad() { // 初始化音频管理器 audioManager.init(); // 初始化游戏 this.initGame(); // 获取 Canvas 上下文 this.gameCanvas = wx.createCanvasContext('gameCanvas', this); this.previewCanvas = wx.createCanvasContext('previewCanvas', this); }, onReady() { // 页面渲染完成后启动游戏循环 this.startGameLoop(); }, /** * 初始化游戏 */ initGame() { const levelConfig = LEVEL_CONFIGS[this.data.currentLevel - 1] || LEVEL_CONFIGS[0]; this.setData({ bossName: levelConfig.bossName, targetFeed: levelConfig.feedCount, fedCount: 0, feedProgress: 0 }); // 重置游戏状态 this.gameState = { fruits: [], nextFruitLevel: levelConfig.dropMin, fruitIdCounter: 0, dropCooldown: 0, failTimer: 0, startTime: Date.now(), sessionMaxCombo: 0, mergeCore: new MergeCore() }; // 生成第一个预览水果 this.generateNextFruit(); // 更新预览 this.drawPreview(); // 播放背景音乐 this.playBGM(); }, /** * 生成下一个水果 */ generateNextFruit() { const levelConfig = LEVEL_CONFIGS[this.data.currentLevel - 1] || LEVEL_CONFIGS[0]; const min = levelConfig.dropMin; const max = levelConfig.dropMax; this.gameState.nextFruitLevel = Math.floor(Math.random() * (max - min + 1)) + min; this.drawPreview(); }, /** * 绘制预览水果 */ drawPreview() { if (!this.previewCtx) return; const config = FRUIT_CONFIGS[this.gameState.nextFruitLevel - 1]; const centerX = 30; const centerY = 30; const radius = Math.min(config.radius, 25); // 限制预览大小 // 清空画布 this.previewCtx.clearRect(0, 0, 60, 60); // 绘制水果 this.previewCtx.beginPath(); this.previewCtx.arc(centerX, centerY, radius, 0, 2 * Math.PI); this.previewCtx.fillStyle = config.color; this.previewCtx.fill(); // 绘制边框 this.previewCtx.strokeStyle = '#333'; this.previewCtx.lineWidth = 2; this.previewCtx.stroke(); // 绘制等级 this.previewCtx.fillStyle = 'white'; this.previewCtx.font = 'bold 16px Arial'; this.previewCtx.textAlign = 'center'; this.previewCtx.textBaseline = 'middle'; this.previewCtx.fillText(config.level.toString(), centerX, centerY); this.previewCtx.draw(); }, /** * 触摸开始事件 - 掉落水果 */ onTouchStart(e) { if (this.data.isPaused) return; if (this.gameState.dropCooldown > 0) return; const touch = e.touches[0]; const x = touch.x; // 限制在容器范围内 const clampedX = this.clampToContainer(x); // 掉落水果 this.dropFruit(clampedX, this.gameState.nextFruitLevel); // 设置冷却 this.gameState.dropCooldown = 300; // 300ms // 生成下一个 this.generateNextFruit(); }, /** * 掉落水果 */ dropFruit(x, level) { const config = FRUIT_CONFIGS[level - 1]; const fruitId = `fruit_${++this.gameState.fruitIdCounter}`; // 创建水果对象 const fruit = { id: fruitId, level: level, x: x, y: 50, // 从顶部开始 vx: 0, vy: 0, radius: config.radius, color: config.color, score: config.score, isMerging: false }; this.gameState.fruits.push(fruit); // 播放音效(可选) this.playSound('drop'); }, /** * 限制X坐标到容器内 */ clampToContainer(x) { const containerWidth = 360; // 容器宽度 const halfWidth = containerWidth / 2; return Math.max(halfWidth - 20, Math.min(halfWidth + 20, x)); }, /** * 游戏主循环 */ startGameLoop() { const loop = () => { if (!this.data.isPaused) { this.update(); this.render(); } requestAnimationFrame(loop); }; loop(); }, /** * 更新游戏逻辑 */ update() { const dt = 16; // 假设60fps,每帧约16ms // 更新掉落冷却 if (this.gameState.dropCooldown > 0) { this.gameState.dropCooldown -= dt; } // 更新水果物理 this.updateFruits(dt); // 检测合成 this.checkMerges(); // 检测失败条件 this.checkFailCondition(dt); }, /** * 更新水果物理 */ updateFruits(dt) { const gravity = 0.5; const damping = 0.98; const bounce = 0.3; const groundY = 600; // 地面Y坐标 for (let i = this.gameState.fruits.length - 1; i >= 0; i--) { const fruit = this.gameState.fruits[i]; if (fruit.isMerging) continue; // 应用重力 fruit.vy += gravity; // 更新位置 fruit.y += fruit.vy; fruit.x += fruit.vx; // 地面碰撞 if (fruit.y + fruit.radius > groundY) { fruit.y = groundY - fruit.radius; fruit.vy *= -bounce; fruit.vx *= damping; // 如果速度很小,停止弹跳 if (Math.abs(fruit.vy) < 1) { fruit.vy = 0; } } // 墙壁碰撞 if (fruit.x - fruit.radius < 0) { fruit.x = fruit.radius; fruit.vx *= -bounce; } else if (fruit.x + fruit.radius > 360) { fruit.x = 360 - fruit.radius; fruit.vx *= -bounce; } } }, /** * 检测并处理合成 */ checkMerges() { const fruits = this.gameState.fruits; const merged = new Set(); for (let i = 0; i < fruits.length; i++) { if (merged.has(i) || fruits[i].isMerging) continue; for (let j = i + 1; j < fruits.length; j++) { if (merged.has(j) || fruits[j].isMerging) continue; const f1 = fruits[i]; const f2 = fruits[j]; // 检查是否相同等级且距离足够近 if (f1.level === f2.level && !f1.isMerging && !f2.isMerging) { const dx = f1.x - f2.x; const dy = f1.y - f2.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < f1.radius + f2.radius - 5) { // 触发合成 this.mergeFruits(i, j); merged.add(i); merged.add(j); break; } } } } }, /** * 合并两个水果 */ mergeFruits(index1, index2) { const f1 = this.gameState.fruits[index1]; const f2 = this.gameState.fruits[index2]; // 计算新等级 const newLevel = f1.level + 1; if (newLevel > 9) { // 已经是最高级,不合成 return; } // 标记为正在合成 f1.isMerging = true; f2.isMerging = true; // 计算质心位置 const newX = (f1.x + f2.x) / 2; const newY = (f1.y + f2.y) / 2; // 移除旧水果 this.gameState.fruits = this.gameState.fruits.filter((f, idx) => idx !== index1 && idx !== index2 ); // 创建新水果 const config = FRUIT_CONFIGS[newLevel - 1]; const newFruit = { id: `merged_${++this.gameState.fruitIdCounter}`, level: newLevel, x: newX, y: newY, vx: 0, vy: -2, // 轻微向上弹起 radius: config.radius, color: config.color, score: config.score, isMerging: false }; this.gameState.fruits.push(newFruit); // 更新分数 const addScore = f1.score + f2.score + config.score; this.setData({ score: this.data.score + addScore }); // 更新连击 const newCombo = this.gameState.mergeCore.incrementCombo(); this.setData({ combo: newCombo }); if (newCombo > this.gameState.sessionMaxCombo) { this.gameState.sessionMaxCombo = newCombo; } // 播放音效 this.playSound('merge'); // 检查是否为金色传说(合成出Lv9) if (newLevel === 9) { this.triggerGoldenLegend(); } // 尝试投喂Boss this.tryFeedBoss(newLevel); }, /** * 投喂Boss */ tryFeedBoss(fruitLevel) { const levelConfig = LEVEL_CONFIGS[this.data.currentLevel - 1]; if (!levelConfig) return; if (fruitLevel >= levelConfig.targetLevel) { const newFedCount = this.data.fedCount + 1; const progress = (newFedCount / levelConfig.feedCount) * 100; this.setData({ fedCount: newFedCount, feedProgress: progress }); // 检查是否通关 if (newFedCount >= levelConfig.feedCount) { this.handleLevelClear(); } } }, /** * 处理通关 */ handleLevelClear() { console.log('[Game] 通关!'); const elapsed = (Date.now() - this.gameState.startTime) / 1000; this.setData({ isClear: true, finalScore: this.data.score, finalCombo: this.gameState.sessionMaxCombo, finalTime: elapsed.toFixed(1), showResult: true }); // 保存进度 app.saveGameData(); // 播放通关音效 this.playSound('clear'); }, /** * 触发金色传说特效 */ triggerGoldenLegend() { console.log('[Game] ✨ 金色传说!✨'); this.setData({ showGoldenEffect: true }); // 播放金色传说音效 this.playSound('golden'); // 3秒后隐藏特效 setTimeout(() => { this.setData({ showGoldenEffect: false }); }, 3000); }, /** * 检测失败条件 */ checkFailCondition(dt) { const warningY = 150; // 警戒线Y坐标 let hasFruitAboveWarning = false; for (const fruit of this.gameState.fruits) { if (fruit.isMerging) continue; if (fruit.y - fruit.radius < warningY) { hasFruitAboveWarning = true; break; } } if (hasFruitAboveWarning) { this.gameState.failTimer += dt; if (this.gameState.failTimer >= 3000) { // 3秒后失败 this.handleGameOver(); } } else { this.gameState.failTimer = 0; } }, /** * 处理游戏失败 */ handleGameOver() { console.log('[Game] 游戏失败'); const elapsed = (Date.now() - this.gameState.startTime) / 1000; this.setData({ isClear: false, finalScore: this.data.score, finalCombo: this.gameState.sessionMaxCombo, finalTime: elapsed.toFixed(1), showResult: true, showReviveBtn: true }); // 播放失败音效 this.playSound('gameover'); }, /** * 渲染游戏画面 */ render() { if (!this.gameCanvas) return; const ctx = this.gameCanvas; const width = 360; const height = 600; // 清空画布 ctx.clearRect(0, 0, width, height); // 绘制背景 ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; ctx.fillRect(0, 0, width, height); // 绘制警戒线 ctx.strokeStyle = '#ff4444'; ctx.lineWidth = 3; ctx.setLineDash([10, 5]); ctx.beginPath(); ctx.moveTo(0, 150); ctx.lineTo(width, 150); ctx.stroke(); ctx.setLineDash([]); // 绘制地面 ctx.strokeStyle = '#4CAF50'; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(0, 600); ctx.lineTo(width, 600); ctx.stroke(); // 绘制所有水果 for (const fruit of this.gameState.fruits) { if (fruit.isMerging) continue; // 绘制水果圆形 ctx.beginPath(); ctx.arc(fruit.x, fruit.y, fruit.radius, 0, 2 * Math.PI); ctx.fillStyle = fruit.color; ctx.fill(); // 绘制边框 ctx.strokeStyle = '#333'; ctx.lineWidth = 2; ctx.stroke(); // 绘制等级数字 ctx.fillStyle = 'white'; ctx.font = `bold ${Math.max(12, fruit.radius / 3)}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(fruit.level.toString(), fruit.x, fruit.y); } ctx.draw(); }, /** * 播放音效 */ playSound(type) { audioManager.playSFX(type); }, /** * 播放背景音乐 */ playBGM() { audioManager.playBGM('bgm_main'); }, /** * 停止背景音乐 */ stopBGM() { audioManager.stopBGM(); }, /** * 使用道具 - 炸弹 */ useBomb() { if (this.data.items.bomb <= 0) { wx.showToast({ title: '炸弹不足', icon: 'none' }); return; } // 移除最低等级的水果 let lowestLevel = 999; let lowestIndex = -1; for (let i = 0; i < this.gameState.fruits.length; i++) { if (this.gameState.fruits[i].level < lowestLevel && !this.gameState.fruits[i].isMerging) { lowestLevel = this.gameState.fruits[i].level; lowestIndex = i; } } if (lowestIndex >= 0) { this.gameState.fruits.splice(lowestIndex, 1); this.setData({ 'items.bomb': this.data.items.bomb - 1 }); wx.showToast({ title: '使用炸弹', icon: 'success' }); } }, /** * 使用道具 - 冰冻 */ useFreeze() { if (this.data.items.freeze <= 0) { wx.showToast({ title: '冰冻不足', icon: 'none' }); return; } // 暂停所有水果运动3秒 for (const fruit of this.gameState.fruits) { fruit.vx = 0; fruit.vy = 0; } this.setData({ 'items.freeze': this.data.items.freeze - 1 }); wx.showToast({ title: '冰冻3秒', icon: 'success' }); setTimeout(() => { // 恢复物理 }, 3000); }, /** * 看视频复活 */ revive() { app.showRewardedAd(() => { // 奖励:移除最上层30%的水果 const fruits = this.gameState.fruits; fruits.sort((a, b) => a.y - b.y); const removeCount = Math.ceil(fruits.length * 0.3); this.gameState.fruits.splice(0, removeCount); this.setData({ showReviveBtn: false, showResult: false }); wx.showToast({ title: '复活成功', icon: 'success' }); }, (success) => { if (!success) { wx.showToast({ title: '广告未看完', icon: 'none' }); } }); }, /** * 下一关 */ nextLevel() { if (this.data.currentLevel < 20) { this.setData({ currentLevel: this.data.currentLevel + 1, showResult: false, score: 0, combo: 0 }); this.initGame(); } else { wx.showToast({ title: '恭喜通关全部关卡!', icon: 'success' }); this.returnHome(); } }, /** * 重新开始 */ restart() { this.setData({ showResult: false, score: 0, combo: 0 }); this.initGame(); }, /** * 返回主页 */ returnHome() { wx.navigateBack({ delta: 1 }); }, /** * 暂停/继续 */ togglePause() { const isPaused = !this.data.isPaused; this.setData({ isPaused: isPaused }); // 暂停时停止BGM,继续时恢复播放 if (isPaused) { this.stopBGM(); } else { this.playBGM(); } }, onUnload() { // 页面卸载时保存游戏数据并停止音频 app.saveGameData(); audioManager.destroy(); } }); /** * 简单的合成核心引擎 */ class MergeCore { constructor() { this.combo = 0; this.lastMergeTime = 0; } incrementCombo() { const now = Date.now(); if (now - this.lastMergeTime < 2000) { // 2秒内连续合成,连击+1 this.combo++; } else { // 超过2秒,重置连击 this.combo = 1; } this.lastMergeTime = now; return this.combo; } reset() { this.combo = 0; this.lastMergeTime = 0; } }