/** * TutorialOverlay.js * New player tutorial overlay shown on first play. * Displays 2-3 step instructions for controls. */ const { SCREEN_WIDTH, SCREEN_HEIGHT, MAP_OFFSET_X, MAP_WIDTH, } = require('../base/GameGlobal'); class TutorialOverlay { constructor() { this._active = false; this._step = 0; this._totalSteps = 3; this._steps = [ { title: '移动坦克', desc: '拖动左下角的摇杆\n控制坦克上下左右移动', highlight: 'joystick', }, { title: '发射子弹', desc: '点击右下角的按钮\n向前方发射子弹', highlight: 'fire', }, { title: '保护基地', desc: '消灭所有敌人\n不要让基地被摧毁!', highlight: 'base', }, ]; } /** * Show the tutorial. */ show() { this._active = true; this._step = 0; } /** * Hide the tutorial. */ hide() { this._active = false; } /** Whether the tutorial is active. */ get active() { return this._active; } /** * Handle touch to advance steps. * @returns {boolean} Whether the tutorial consumed the touch. */ handleTouch() { if (!this._active) return false; this._step++; if (this._step >= this._totalSteps) { this._active = false; } return true; } /** * Render the tutorial overlay. * @param {CanvasRenderingContext2D} ctx */ render(ctx) { if (!this._active) return; const step = this._steps[this._step]; // Semi-transparent overlay ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); const cx = SCREEN_WIDTH / 2; const cy = SCREEN_HEIGHT / 2; // Step indicator ctx.fillStyle = '#AAAAAA'; ctx.font = '12px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(`${this._step + 1} / ${this._totalSteps}`, cx, cy - 80); // Title ctx.fillStyle = '#FFD700'; ctx.font = 'bold 24px Arial'; ctx.fillText(step.title, cx, cy - 40); // Description (multi-line) ctx.fillStyle = '#FFFFFF'; ctx.font = '16px Arial'; const lines = step.desc.split('\n'); for (let i = 0; i < lines.length; i++) { ctx.fillText(lines[i], cx, cy + 10 + i * 24); } // Highlight area indicator this._drawHighlight(ctx, step.highlight); // Tap to continue ctx.fillStyle = '#888888'; ctx.font = '13px Arial'; ctx.fillText('点击屏幕继续', cx, SCREEN_HEIGHT - 60); } /** * Draw a highlight circle around the relevant UI element. * @private */ _drawHighlight(ctx, type) { ctx.save(); ctx.strokeStyle = '#FFD700'; ctx.lineWidth = 3; ctx.setLineDash([8, 4]); switch (type) { case 'joystick': ctx.beginPath(); ctx.arc(Math.floor(MAP_OFFSET_X / 2), SCREEN_HEIGHT - 100, 65, 0, Math.PI * 2); ctx.stroke(); break; case 'fire': { const rightAreaStart = MAP_OFFSET_X + MAP_WIDTH; ctx.beginPath(); ctx.arc(Math.floor(rightAreaStart + (SCREEN_WIDTH - rightAreaStart) / 2), SCREEN_HEIGHT - 100, 50, 0, Math.PI * 2); ctx.stroke(); break; } case 'base': { // Arrow pointing to base area const baseCx = SCREEN_WIDTH / 2; ctx.beginPath(); ctx.moveTo(baseCx, SCREEN_HEIGHT * 0.7); ctx.lineTo(baseCx, SCREEN_HEIGHT * 0.8); ctx.stroke(); ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.moveTo(baseCx - 8, SCREEN_HEIGHT * 0.8); ctx.lineTo(baseCx + 8, SCREEN_HEIGHT * 0.8); ctx.lineTo(baseCx, SCREEN_HEIGHT * 0.8 + 10); ctx.closePath(); ctx.fill(); break; } } ctx.setLineDash([]); ctx.restore(); } } module.exports = TutorialOverlay;