Trae SOLO 游戏 —— 🐾🐱🐾猫咪追蝌蚪🐸

用Trae做一个游戏给猫咪玩 ------ 追蝌蚪

目标用户

  • 爱猫人士:为家中猫咪提供互动娱乐
  • 宠物主人:想通过屏幕游戏与宠物互动的主人
  • 休闲游戏玩家:喜欢轻松治愈系游戏的用户

猫蝌蚪 介绍

一款专为猫咪设计的网页互动游戏,屏幕中会出现游动的虚拟蝌蚪,猫咪可以用爪子拍打屏幕与蝌蚪互动,蝌蚪会根据互动做出逼真的反应。

🎮 游戏机制

  • 智能蝌蚪AI:蝌蚪会自主游泳,遇到边界时会自动转向
  • 逃跑行为:点击蝌蚪附近时,它会发出"尖叫"并快速逃离
  • 边界检测:蝌蚪不会游出屏幕边界,会智能地转向安全区域

🎨 视觉效果

  • 渐变背景:模拟水中环境的蓝色渐变背景
  • 游泳动画:蝌蚪有尾巴摆动的游泳动画效果
  • 平滑转向:转向时有平滑的角度过渡动画

🔊 音效系统

  • 游泳音效:每秒播放"哒哒"声模拟游泳声音
  • 碰撞音效:撞到边界时的反弹声音
  • 尖叫音效:被点击时的惊吓声音
  • 使用Web Audio API生成程序化音效

📱 交互控制

  • 速度滑块:可以调节蝌蚪的游泳速度(0.5-5倍速)
  • 震动反馈:支持移动设备的震动反馈(可开关)
  • 实时调试信息:显示游戏状态的调试面板

如何与 猫蝌蚪 互动

  1. 将设备放在猫咪常活动区域
  2. 开启全屏模式,调整合适音量
  3. 初始会显示1-3只游动的蝌蚪
  4. 猫咪可用爪子触碰屏幕与蝌蚪互动
  5. 游戏会自动记录互动数据

开发项目的心路历程

html 复制代码
<!-- 画布 -->
<canvas id="gameCanvas"></canvas>

<div id="debug"></div>
<div id="controls">
    <div class="control-group">
        <label for="speedSlider">游泳速度</label>
        <input type="range" id="speedSlider" class="slider" min="0.5" max="5" step="0.1" value="2">
        <div class="speed-display">速度: <span id="speedValue">2.0</span></div>
    </div>
    <div class="checkbox-group">
        <input type="checkbox" id="vibrationToggle" checked>
        <label for="vibrationToggle">震动反馈</label>
        <span id="vibrationStatus" class="vibration-status">检测中...</span>
    </div>
</div>

1、canvas -> 画布。画布用来绘制游戏的图形。

2、class为debugdiv -> 调试信息显示区域

3、class为controlsdiv -> 控制面板容器。其中包括:(1)、可调节游泳速度;(2)、支持手机震动;(3)、显示位置、角度、速度等信息。

js 复制代码
/**
 * 蝌蚪游戏主类
 * 负责管理整个游戏的逻辑、渲染和交互
 */
class TadpoleGame {
    /**
     * 构造函数 - 初始化游戏
     * 设置画布、音频上下文、游戏状态和蝌蚪属性
     */
    constructor() {
        // 获取画布元素和2D渲染上下文
        this.canvas = document.getElementById('gameCanvas');
        this.ctx = this.canvas.getContext('2d');
        this.audioContext = null;  // Web Audio API上下文,用于播放声音
        this.gameStarted = false;  // 游戏是否已开始的标志

        // 蝌蚪对象的属性配置
        this.tadpole = {
            x: 0,                    // X坐标位置
            y: 0,                    // Y坐标位置
            angle: 0,                // 游泳方向角度(弧度)
            baseSpeed: 2,            // 基础游泳速度
            currentSpeed: 2,         // 当前实际速度
            size: 20,                // 蝌蚪头部大小
            tailLength: 8,           // 尾巴段数
            tailSegments: [],        // 尾巴各段的位置数组
            escaping: false,         // 是否处于逃跑状态
            escapeTime: 0            // 开始逃跑的时间戳
        };

        // 鼠标/触摸位置追踪
        this.mouse = { x: 0, y: 0 };
        this.vibrationEnabled = true;  // 震动反馈是否启用

        // 初始化各个组件
        this.setupCanvas();
        this.setupControls();
        this.setupEventListeners();
        this.initTadpole();
    }

    /**
     * 设置画布尺寸和响应式调整
     * 使画布填满整个窗口,并在窗口大小改变时自动调整
     */
    setupCanvas() {
        // 设置画布尺寸为窗口大小
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;

        // 监听窗口大小变化事件,动态调整画布尺寸
        window.addEventListener('resize', () => {
            this.canvas.width = window.innerWidth;
            this.canvas.height = window.innerHeight;
        });
    }

    /**
     * 设置游戏控制面板的交互逻辑
     * 包括速度滑块和震动开关的事件处理
     */
    setupControls() {
        // 获取控制元素
        const speedSlider = document.getElementById('speedSlider');
        const speedValue = document.getElementById('speedValue');
        const vibrationToggle = document.getElementById('vibrationToggle');
        const vibrationLabel = document.getElementById('vibrationLabel');

        // 速度滑块变化事件 - 调整蝌蚪游泳速度
        speedSlider.addEventListener('input', (e) => {
            this.tadpole.baseSpeed = parseFloat(e.target.value);
            speedValue.textContent = e.target.value;
        });

        // 震动开关切换事件 - 启用/禁用震动反馈
        vibrationToggle.addEventListener('change', (e) => {
            this.vibrationEnabled = e.target.checked;
            vibrationLabel.textContent = e.target.checked ? '✅' : '❌';
            document.getElementById('vibrationStatus').textContent = e.target.checked ? '开启 ✓' : '关闭';
        });
    }

    /**
     * 设置游戏的事件监听器
     * 包括游戏启动、鼠标移动和触摸事件
     */
    setupEventListeners() {
        const clickHint = document.getElementById('clickHint');

        // 游戏启动函数 - 初始化音频并开始游戏循环
        const startGame = async () => {
            if (!this.gameStarted) {
                await this.initAudio();           // 初始化音频上下文
                this.gameStarted = true;          // 标记游戏已开始
                clickHint.classList.add('hidden'); // 隐藏点击提示
                this.gameLoop();                  // 开始游戏主循环
            }
        };

        // 点击事件监听 - 启动游戏
        clickHint.addEventListener('click', startGame);
        document.addEventListener('click', startGame);

        // 鼠标移动事件 - 追踪鼠标位置
        document.addEventListener('mousemove', (e) => {
            this.mouse.x = e.clientX;
            this.mouse.y = e.clientY;
        });

        // 触摸移动事件 - 追踪触摸位置(移动设备支持)
        document.addEventListener('touchmove', (e) => {
            e.preventDefault();  // 防止页面滚动
            const touch = e.touches[0];
            this.mouse.x = touch.clientX;
            this.mouse.y = touch.clientY;
        });
    }

    /**
     * 初始化Web Audio API
     * 创建音频上下文,用于播放游戏音效
     */
    async initAudio() {
        try {
            // 创建音频上下文(兼容不同浏览器)
            this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            await this.audioContext.resume();  // 恢复音频上下文(Chrome需要用户交互后才能播放音频)
        } catch (error) {
            console.log('音频初始化失败:', error);
        }
    }

    /**
     * 播放音效
     * @param {number} frequency - 音频频率(Hz),默认440Hz
     * @param {number} duration - 播放时长(毫秒),默认100ms
     */
    playSound(frequency = 440, duration = 100) {
        if (!this.audioContext) return;  // 如果音频上下文未初始化则返回

        // 创建振荡器(音频发生器)
        const oscillator = this.audioContext.createOscillator();
        const gainNode = this.audioContext.createGain();  // 创建音量控制节点

        // 连接音频节点:振荡器 -> 音量控制 -> 输出
        oscillator.connect(gainNode);
        gainNode.connect(this.audioContext.destination);

        // 设置音频参数
        oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime);
        oscillator.type = 'sine';  // 使用正弦波

        // 设置音量渐变效果(避免爆音)
        gainNode.gain.setValueAtTime(0.1, this.audioContext.currentTime);
        gainNode.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + duration / 1000);

        // 播放音频
        oscillator.start(this.audioContext.currentTime);
        oscillator.stop(this.audioContext.currentTime + duration / 1000);
    }

    /**
     * 触发设备震动
     * 在支持的设备上产生触觉反馈
     */
    vibrate() {
        // 检查震动是否启用且设备支持震动API
        if (this.vibrationEnabled && navigator.vibrate) {
            navigator.vibrate(50);  // 震动50毫秒
        }
    }

    /**
     * 初始化蝌蚪的位置和状态
     * 将蝌蚪放置在屏幕中央,设置随机方向,初始化尾巴段
     */
    initTadpole() {
        // 将蝌蚪放置在屏幕中央
        this.tadpole.x = this.canvas.width / 2;
        this.tadpole.y = this.canvas.height / 2;
        this.tadpole.angle = Math.random() * Math.PI * 2;  // 随机初始方向

        // 初始化尾巴段数组,每段都从头部位置开始
        for (let i = 0; i < this.tadpole.tailLength; i++) {
            this.tadpole.tailSegments.push({
                x: this.tadpole.x,
                y: this.tadpole.y
            });
        }
    }

    /**
     * 更新蝌蚪的位置、状态和行为
     * 这是游戏的核心逻辑,处理移动、逃跑、边界检测等
     */
    updateTadpole() {
        // 计算蝌蚪与鼠标之间的距离
        const distance = Math.sqrt(
            Math.pow(this.mouse.x - this.tadpole.x, 2) +
            Math.pow(this.mouse.y - this.tadpole.y, 2)
        );

        // 检测是否需要进入逃跑状态(鼠标靠近且未在逃跑)
        if (distance < 100 && !this.tadpole.escaping) {
            this.tadpole.escaping = true;                    // 进入逃跑状态
            this.tadpole.escapeTime = Date.now();           // 记录逃跑开始时间
            this.playSound(800, 150);                       // 播放逃跑音效(高频率)
            this.vibrate();                                 // 触发震动反馈
            document.getElementById('status').textContent = '逃跑!';  // 更新状态显示
        }

        // 逃跑状态管理
        if (this.tadpole.escaping) {
            // 检查是否应该结束逃跑状态(2秒后)
            if (Date.now() - this.tadpole.escapeTime > 2000) {
                this.tadpole.escaping = false;
                document.getElementById('status').textContent = '游泳';
            }

            // 计算逃离方向(远离鼠标)
            const escapeAngle = Math.atan2(
                this.tadpole.y - this.mouse.y,  // Y方向差值
                this.tadpole.x - this.mouse.x   // X方向差值
            );
            this.tadpole.angle = escapeAngle;                    // 设置逃跑方向
            this.tadpole.currentSpeed = this.tadpole.baseSpeed * 3;  // 逃跑时速度加倍
        } else {
            // 正常游泳状态 - 随机改变方向(2%概率)
            if (Math.random() < 0.02) {
                this.tadpole.angle += (Math.random() - 0.5) * 0.3;  // 随机转向
            }
            this.tadpole.currentSpeed = this.tadpole.baseSpeed;  // 使用基础速度
        }

        // 根据当前角度和速度移动蝌蚪
        this.tadpole.x += Math.cos(this.tadpole.angle) * this.tadpole.currentSpeed;
        this.tadpole.y += Math.sin(this.tadpole.angle) * this.tadpole.currentSpeed;

        // 边界碰撞检测和反弹
        if (this.tadpole.x < 0 || this.tadpole.x > this.canvas.width) {
            this.tadpole.angle = Math.PI - this.tadpole.angle;  // 水平反弹
        }
        if (this.tadpole.y < 0 || this.tadpole.y > this.canvas.height) {
            this.tadpole.angle = -this.tadpole.angle;           // 垂直反弹
        }

        // 确保蝌蚪位置在画布范围内
        this.tadpole.x = Math.max(0, Math.min(this.canvas.width, this.tadpole.x));
        this.tadpole.y = Math.max(0, Math.min(this.canvas.height, this.tadpole.y));

        // 更新尾巴段位置(在数组开头添加当前位置)
        this.tadpole.tailSegments.unshift({
            x: this.tadpole.x,
            y: this.tadpole.y
        });
        // 移除多余的尾巴段(保持固定长度)
        if (this.tadpole.tailSegments.length > this.tadpole.tailLength) {
            this.tadpole.tailSegments.pop();
        }

        // 更新信息面板显示
        this.updateInfo();
    }

    /**
     * 更新游戏信息面板的显示内容
     * 显示蝌蚪的位置、角度、速度等实时数据
     */
    updateInfo() {
        document.getElementById('posX').textContent = Math.round(this.tadpole.x);
        document.getElementById('posY').textContent = Math.round(this.tadpole.y);
        document.getElementById('angle').textContent = Math.round(this.tadpole.angle * 180 / Math.PI);  // 弧度转角度
        document.getElementById('baseSpeed').textContent = this.tadpole.baseSpeed.toFixed(1);
        document.getElementById('currentSpeed').textContent = this.tadpole.currentSpeed.toFixed(1);
    }

    /**
     * 绘制蝌蚪(包括尾巴、头部、眼睛)
     * 根据当前状态使用不同颜色,实现渐变尾巴效果
     */
    drawTadpole() {
        const ctx = this.ctx;

        // 设置尾巴颜色(逃跑时为红色,正常时为青色)
        ctx.strokeStyle = this.tadpole.escaping ? '#ff6b6b' : '#4ecdc4';
        ctx.lineWidth = 8;
        ctx.lineCap = 'round';   // 圆形线帽
        ctx.lineJoin = 'round';  // 圆形连接

        // 绘制尾巴(如果有多个段)
        if (this.tadpole.tailSegments.length > 1) {
            ctx.beginPath();
            ctx.moveTo(this.tadpole.tailSegments[0].x, this.tadpole.tailSegments[0].y);

            // 绘制每个尾巴段,越往后透明度越低,线条越细
            for (let i = 1; i < this.tadpole.tailSegments.length; i++) {
                const segment = this.tadpole.tailSegments[i];
                const alpha = 1 - (i / this.tadpole.tailSegments.length);  // 计算透明度
                ctx.globalAlpha = alpha * 0.8;  // 设置透明度
                ctx.lineWidth = 8 * alpha;       // 设置线条粗细
                ctx.lineTo(segment.x, segment.y);
            }

            ctx.stroke();        // 绘制路径
            ctx.globalAlpha = 1; // 重置透明度
        }

        // 绘制头部(圆形)
        ctx.fillStyle = this.tadpole.escaping ? '#ff4757' : '#2ed573';  // 逃跑时红色,正常时绿色
        ctx.beginPath();
        ctx.arc(this.tadpole.x, this.tadpole.y, this.tadpole.size, 0, Math.PI * 2);
        ctx.fill();

        // 绘制眼睛(白色圆圈)
        ctx.fillStyle = 'white';
        const eyeOffset = this.tadpole.size * 0.3;  // 眼睛距离头部中心的距离
        const eyeAngle1 = this.tadpole.angle + 0.5; // 第一只眼睛的角度
        const eyeAngle2 = this.tadpole.angle - 0.5; // 第二只眼睛的角度

        // 绘制第一只眼睛
        ctx.beginPath();
        ctx.arc(
            this.tadpole.x + Math.cos(eyeAngle1) * eyeOffset,
            this.tadpole.y + Math.sin(eyeAngle1) * eyeOffset,
            4, 0, Math.PI * 2
        );
        ctx.fill();

        // 绘制第二只眼睛
        ctx.beginPath();
        ctx.arc(
            this.tadpole.x + Math.cos(eyeAngle2) * eyeOffset,
            this.tadpole.y + Math.sin(eyeAngle2) * eyeOffset,
            4, 0, Math.PI * 2
        );
        ctx.fill();

        // 绘制瞳孔(黑色小圆点)
        ctx.fillStyle = 'black';
        // 第一只眼睛的瞳孔
        ctx.beginPath();
        ctx.arc(
            this.tadpole.x + Math.cos(eyeAngle1) * eyeOffset,
            this.tadpole.y + Math.sin(eyeAngle1) * eyeOffset,
            2, 0, Math.PI * 2
        );
        ctx.fill();

        // 第二只眼睛的瞳孔
        ctx.beginPath();
        ctx.arc(
            this.tadpole.x + Math.cos(eyeAngle2) * eyeOffset,
            this.tadpole.y + Math.sin(eyeAngle2) * eyeOffset,
            2, 0, Math.PI * 2
        );
        ctx.fill();
    }

    /**
     * 绘制背景和水波纹效果
     * 清除画布并绘制围绕蝌蚪的动态水波纹
     */
    drawBackground() {
        // 清除整个画布
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

        // 绘制动态水波纹效果
        const time = Date.now() * 0.001;  // 获取当前时间(秒)
        this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';  // 半透明白色
        this.ctx.lineWidth = 1;

        // 绘制5个同心圆水波纹
        for (let i = 0; i < 5; i++) {
            this.ctx.beginPath();
            // 计算波纹半径(基础半径 + 间距 + 时间动画)
            const radius = 50 + i * 30 + Math.sin(time + i) * 10;
            this.ctx.arc(this.tadpole.x, this.tadpole.y, radius, 0, Math.PI * 2);
            this.ctx.stroke();
        }
    }

    /**
     * 游戏主循环
     * 每帧调用一次,负责更新游戏状态和重绘画面
     */
    gameLoop() {
        if (!this.gameStarted) return;  // 如果游戏未开始则返回

        // 按顺序执行每帧的操作
        this.drawBackground();  // 绘制背景
        this.updateTadpole();   // 更新蝌蚪状态
        this.drawTadpole();     // 绘制蝌蚪

        // 请求下一帧动画(60FPS)
        requestAnimationFrame(() => this.gameLoop());
    }
}

效果:

相关推荐
bug菌14 分钟前
当产品迭代遇上AI编程,Trae能否重新定义“快速交付“?
aigc·ai编程·trae
aneasystone本尊17 分钟前
学习 Coze Studio 的工作流执行逻辑
人工智能
aneasystone本尊25 分钟前
再学 Coze Studio 的智能体执行逻辑
人工智能
xuanwuziyou28 分钟前
LangChain 多任务应用开发
人工智能·langchain
新智元1 小时前
一句话,性能暴涨 49%!马里兰 MIT 等力作:Prompt 才是大模型终极武器
人工智能·openai
猫头虎1 小时前
猫头虎AI分享|一款Coze、Dify类开源AI应用超级智能体Agent快速构建工具:FastbuildAI
人工智能·开源·github·aigc·ai编程·ai写作·ai-native
新智元1 小时前
AI 版华尔街之狼!o3-mini 靠「神之押注」狂赚 9 倍,DeepSeek R1 最特立独行
人工智能·openai
bug菌1 小时前
还在为多平台开发头疼?看Trae如何让你一人顶一个团队!
aigc·ai编程·trae
天下弈星~1 小时前
GANs生成对抗网络生成手写数字的Pytorch实现
人工智能·pytorch·深度学习·神经网络·生成对抗网络·gans
bug菌1 小时前
还在为团队协作扯皮?看字节Trae如何让代码评审变成"享受"!
aigc·ai编程·trae