GXUST AI通识课?ai编程!
这学期选了 AI 通识课,作业要求体验四个不同类别的 AI 工具。AI写作(已完成)AI图像(待更新)AI编程(本文主角:TRAE)AI聊天助手(待更新)今天写AI编程篇。我会完整展示三次基础提示词迭代一次二段跳升级的全过程,以及最终生成的完整游戏代码。TRAE(字节出品)中文场景适配:⭐⭐⭐⭐(中文原生,直接说中文需求)代码集成度:⭐⭐⭐⭐(生成即运行,不用复制粘贴)上下文管理:⭐⭐⭐⭐(超大上
🦖 GXUST AI通识课 | 从“一句话需求”到“可玩游戏”:我用TRAE复刻Chrome小恐龙(附完整代码 + 二段跳彩蛋)
本文适用对象:零编程基础小白 / AI编程工具尝鲜者 / 想做小游戏的实践党
工具:字节跳动出品 AI 编程工具 —— TRAE(https://www.trae.ai/)
游戏参考:Chrome 离线恐龙游戏
最终成果:一个完整的跳跃跑酷游戏 HTML 文件,支持二段跳,复制即玩
📌 写在前面
这学期选了 AI 通识课,作业要求体验四个不同类别的 AI 工具。我选了:
- AI写作(已完成)
- AI图像(待更新)
- AI编程(本文主角:TRAE)
- AI聊天助手(待更新)
今天写 AI编程篇。我会完整展示 三次基础提示词迭代 + 一次二段跳升级 的全过程,以及最终生成的完整游戏代码。
Part 1️⃣ 提示词工程:三次基础迭代
1.1 第一次尝试:过于简单,AI只给了一个“骨架”🤨
提示词(初版):
帮我写一个像 Chrome 小恐龙的游戏
结果诊断:
- ❌ 恐龙能出现,能跳跃,但没有障碍物
- ❌ 没有碰撞检测,没有分数
- ❌ 严格来说不算一个“游戏”
教训:把AI当“心有灵犀”的队友不现实。你不说清楚,它就只给最低标准的答案。
1.2 第二次尝试:加细节,效果立竿见影 ✅
提示词(进阶版):
用 HTML/CSS/JS 制作一个 Chrome 离线恐龙游戏的复刻版本。
要求:
1.使用 Canvas 作为游戏画布
2.恐龙用矩形代替,仙人掌用矩形代替,背景灰色/白色
3.按空格或上箭头让恐龙跳跃,有重力物理效果
4.随机生成仙人掌从右侧向左移动
5.碰撞检测:恐龙碰到仙人掌后游戏结束并显示分数
6.分数随时间累积,显示在右上角
7.游戏结束后按“R”键或点击“重新开始”可以重置游戏
改进点:
- ✅ 跳跃物理、仙人掌生成、分数累积都有了
- ✅ 游戏基本可玩
仍存在的问题:
- ⚠️ 跳跃手感偏硬
- ⚠️ 仙人掌间隔有时不合理
- ⚠️ 难度不会增长
1.3 第三次优化:给足“性能参数”,AI给出成品 🚀
提示词(终版):
1.游戏需要包含以下完整功能:
1.1 恐龙位置固定在左侧(x=100),矩形30x30
1.2 跳跃机制:重力0.6,跳跃初速度-10,落地判定
1.3 仙人掌15x30矩形,初始x=800,速度5向左移动
1.4 生成间隔:min80帧,max120帧(用计数器实现)
1.5 分数系统:每帧+1,碰撞时停止加分
1.6 碰撞检测:矩形重叠
1.7 游戏结束显示文字和分数,按R键重置
2.额外功能:
2.1 游戏开始前显示“按空格开始游戏”
2.2 最高分记录(localStorage存储)
2.3 游戏难度随分数提升:每增加500分,仙人掌速度+0.5
3.技术约束:
3.1 原生JS + Canvas,无第三方库
3.2 游戏循环使用requestAnimationFrame
3.3 禁止空格键滚动页面
效果:请看下方 Part 2 的成果展示,基本达到了我的预期。
Part 1.5 🐾 彩蛋升级:我给恐龙加了个二段跳
基础版玩了几把后,我觉得不过瘾——原版只能跳一次。于是我又给 TRAE 输入了新提示词:
提示词(二段跳版):
在上一个版本的小恐龙游戏基础上,增加二段跳功能:
恐龙在空中时可以再跳跃一次(总共可以跳两次)
落地后重置跳跃次数为2
在空中时,如果有剩余跳跃次数,按空格可以再次起跳
在恐龙头顶用一个小图标显示剩余跳跃次数
TRAE 自动分析了现有代码,帮我完成了:
- ✅ 新增
jumpsRemaining变量,初始值2 - ✅ 修改
jump()函数:只有剩余次数>0才能跳,每次消耗一次 - ✅ 落地时重置跳跃次数为2
- ✅ 绘制时在恐龙头顶显示剩余次数(橙色小翅膀图标)
全程没动一行代码,二段跳完美实现。
Part 2️⃣ 成果展示:TRAE生成的完整游戏代码(带二段跳)
将下面的代码复制到一个 index.html 文件中,双击用浏览器打开即可游玩。
游戏特色:
- 经典跑酷:跳跃躲避仙人掌
- 二段跳:空中可以再跳一次
- 动态难度:分数越高,仙人掌速度越快
- 最高分记录:关闭浏览器也不丢失
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Chrome Dino Game - 二段跳版</title>
<style>
* { user-select: none; -webkit-tap-highlight-color: transparent; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex; justify-content: center; align-items: center;
min-height: 100vh; margin: 0; padding: 20px;
font-family: 'Courier New', monospace;
}
.game-container {
background: #f7f7f7; padding: 20px; border-radius: 24px;
box-shadow: 0 20px 35px rgba(0,0,0,0.2);
}
canvas { display: block; margin: 0 auto; border-radius: 12px; cursor: pointer; }
.info {
margin-top: 15px; text-align: center; font-size: 14px; color: #555;
}
.info span {
background: #333; color: #ffd966; padding: 4px 10px;
border-radius: 20px; font-weight: bold; margin: 0 4px;
}
button {
margin-top: 15px; padding: 8px 24px; font-size: 16px;
font-family: inherit; font-weight: bold; background: #333;
color: white; border: none; border-radius: 40px; cursor: pointer;
}
button:active { transform: scale(0.96); }
@media (max-width: 860px) { canvas { width: 100%; height: auto; } }
</style>
</head>
<body>
<div>
<div class="game-container">
<canvas id="gameCanvas" width="800" height="200"></canvas>
<div class="info">
🎮 空格 / ↑ 跳跃(二段跳可用) |
💾 最高分:<span id="highScoreSpan">0</span> |
⚡ 当前速度倍率:<span id="speedFactorSpan">1.00</span>
</div>
<div style="text-align: center;">
<button id="restartButton">🔄 重新开始</button>
</div>
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let gameRunning = false;
let gameOverFlag = false;
let animationId = null;
const DINO_WIDTH = 30, DINO_HEIGHT = 30;
let dinoY = canvas.height - DINO_HEIGHT;
let dinoVelocity = 0;
const GRAVITY = 0.6, JUMP_POWER = -10;
let jumpsRemaining = 2;
const MAX_JUMPS = 2;
let obstacles = [];
const OBSTACLE_WIDTH = 15, OBSTACLE_HEIGHT = 30;
let spawnGapMin = 70, spawnGapMax = 110;
let nextSpawnCounter = 0;
let baseSpeed = 5, currentSpeed = baseSpeed;
let score = 0, highScore = 0;
const highScoreSpan = document.getElementById('highScoreSpan');
const speedFactorSpan = document.getElementById('speedFactorSpan');
const restartBtn = document.getElementById('restartButton');
try {
const saved = localStorage.getItem('dinoHighScore');
if (saved && !isNaN(parseInt(saved))) {
highScore = parseInt(saved);
highScoreSpan.innerText = highScore;
}
} catch(e) {}
function updateHighScoreUI() { highScoreSpan.innerText = highScore; }
function updateSpeedUI() { speedFactorSpan.innerText = (currentSpeed / baseSpeed).toFixed(2); }
function setNextSpawnCounter() {
const gap = spawnGapMin + Math.floor(Math.random() * (spawnGapMax - spawnGapMin));
nextSpawnCounter = gap;
}
function resetGame() {
dinoY = canvas.height - DINO_HEIGHT;
dinoVelocity = 0;
obstacles = [];
score = 0;
currentSpeed = baseSpeed;
gameRunning = true;
gameOverFlag = false;
jumpsRemaining = MAX_JUMPS;
setNextSpawnCounter();
updateSpeedUI();
if (animationId === null) startGameLoop();
}
function jump() {
if (!gameRunning) return false;
if (jumpsRemaining > 0) {
dinoVelocity = JUMP_POWER;
jumpsRemaining--;
return true;
}
return false;
}
function updateGame() {
if (!gameRunning) return;
dinoVelocity += GRAVITY;
dinoY += dinoVelocity;
if (dinoY >= canvas.height - DINO_HEIGHT) {
dinoY = canvas.height - DINO_HEIGHT;
dinoVelocity = 0;
jumpsRemaining = MAX_JUMPS;
}
if (dinoY < 0) { dinoY = 0; if (dinoVelocity < 0) dinoVelocity = 0; }
for (let i = 0; i < obstacles.length; i++) obstacles[i].x -= currentSpeed;
obstacles = obstacles.filter(obs => obs.x + OBSTACLE_WIDTH > 0);
if (nextSpawnCounter <= 0) {
obstacles.push({ x: canvas.width, width: OBSTACLE_WIDTH, height: OBSTACLE_HEIGHT, y: canvas.height - OBSTACLE_HEIGHT });
setNextSpawnCounter();
} else {
nextSpawnCounter--;
}
const dinoRect = { x: 100, y: dinoY, w: DINO_WIDTH, h: DINO_HEIGHT };
for (let obs of obstacles) {
const obsRect = { x: obs.x, y: obs.y, w: OBSTACLE_WIDTH, h: OBSTACLE_HEIGHT };
if (dinoRect.x < obsRect.x + obsRect.w && dinoRect.x + dinoRect.w > obsRect.x &&
dinoRect.y < obsRect.y + obsRect.h && dinoRect.y + dinoRect.h > obsRect.y) {
gameRunning = false;
gameOverFlag = true;
if (score > highScore) {
highScore = score;
updateHighScoreUI();
localStorage.setItem('dinoHighScore', highScore);
}
return;
}
}
score++;
let targetSpeed = baseSpeed + Math.floor(score / 500) * 0.5;
if (targetSpeed > baseSpeed * 3) targetSpeed = baseSpeed * 3;
currentSpeed = targetSpeed;
updateSpeedUI();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(0, canvas.height - 5);
ctx.lineTo(canvas.width, canvas.height - 5);
ctx.strokeStyle = "#888";
ctx.lineWidth = 2;
ctx.stroke();
for (let obs of obstacles) {
ctx.fillStyle = "#2d6a2f";
ctx.fillRect(obs.x, obs.y, OBSTACLE_WIDTH, OBSTACLE_HEIGHT);
ctx.fillStyle = "#1e4a1f";
ctx.fillRect(obs.x + 3, obs.y - 6, 4, 6);
ctx.fillRect(obs.x + 8, obs.y - 9, 4, 9);
}
ctx.fillStyle = "#3a3a3a";
ctx.fillRect(100, dinoY, DINO_WIDTH, DINO_HEIGHT);
ctx.fillStyle = "white";
ctx.fillRect(122, dinoY + 6, 6, 6);
ctx.fillStyle = "#000";
ctx.fillRect(124, dinoY + 8, 3, 3);
ctx.fillStyle = "#e05a5a";
ctx.fillRect(120, dinoY + 20, 8, 4);
if (gameRunning && jumpsRemaining > 0 && dinoY < canvas.height - DINO_HEIGHT - 1) {
ctx.font = "bold 14px 'Courier New'";
ctx.fillStyle = "#ff8800";
ctx.fillText("✈️ " + jumpsRemaining, 100, dinoY - 5);
}
ctx.font = "bold 20px 'Courier New'";
ctx.fillStyle = "#2c3e50";
ctx.fillText("Score: " + Math.floor(score), canvas.width - 130, 35);
ctx.font = "16px 'Courier New'";
ctx.fillStyle = "#555";
ctx.fillText("Best: " + highScore, canvas.width - 130, 65);
if (!gameRunning && !gameOverFlag) {
ctx.font = "bold 18px 'Courier New'";
ctx.fillStyle = "#111";
ctx.fillText("⚡ PRESS SPACE TO START ⚡", canvas.width/2 - 150, canvas.height/2);
} else if (gameOverFlag) {
ctx.font = "bold 26px 'Courier New'";
ctx.fillStyle = "#c0392b";
ctx.fillText("💀 GAME OVER 💀", canvas.width/2 - 120, canvas.height/2 - 20);
ctx.font = "16px 'Courier New'";
ctx.fillStyle = "#2c3e50";
ctx.fillText("Press SPACE or R to restart", canvas.width/2 - 135, canvas.height/2 + 20);
}
}
function gameLoop() { updateGame(); draw(); animationId = requestAnimationFrame(gameLoop); }
function startGameLoop() { if (animationId) return; animationId = requestAnimationFrame(gameLoop); }
function startGame() {
if (!gameRunning && gameOverFlag) resetGame();
else if (!gameRunning && !gameOverFlag) resetGame();
}
function handleKeydown(e) {
const key = e.key;
if (key === ' ' || key === 'ArrowUp' || key === 'Space') {
e.preventDefault();
if (!gameRunning) startGame();
else jump();
}
if (key === 'r' || key === 'R') { e.preventDefault(); resetGame(); }
}
function handleCanvasClick(e) {
e.preventDefault();
if (!gameRunning) startGame();
else jump();
}
restartBtn.addEventListener('click', () => resetGame());
window.addEventListener('keydown', handleKeydown);
canvas.addEventListener('click', handleCanvasClick);
window.addEventListener('keydown', function(e) { if (e.code === 'Space' && e.target === document.body) e.preventDefault(); });
gameRunning = false;
gameOverFlag = false;
dinoY = canvas.height - DINO_HEIGHT;
jumpsRemaining = MAX_JUMPS;
setNextSpawnCounter();
startGameLoop();
})();
</script>
</body>
</html>
Part 3️⃣ 总结测评
3.1 TRAE vs 通用聊天助手
TRAE(字节出品)
- 中文场景适配:⭐⭐⭐⭐(中文原生,直接说中文需求)
- 代码集成度:⭐⭐⭐⭐(生成即运行,不用复制粘贴)
- 上下文管理:⭐⭐⭐⭐(超大上下文,可管理整个项目)
- 交互友好度:⭐⭐⭐(有 IDE 学习成本,但 SOLO 模式简单)
- 价格:完全免费(国内版)
通用聊天助手(如 ChatGPT/豆包)
- 中文场景适配:⭐⭐⭐(需要翻译或手动调整)
- 代码集成度:⭐⭐(需手动复制到编辑器测试)
- 上下文管理:⭐⭐(窗口有限,大项目容易忘记需求)
- 交互友好度:⭐⭐⭐⭐⭐(对话即用,零门槛)
- 价格:部分免费/付费
3.2 个人评价
👍 优点
- 中文原生优化,直接打中文就能生成代码,不用翻译。
- SOLO 模式实现“动嘴编程”:AI 独立完成从需求到代码的全流程。
- 代码质量高:二段跳改动没有破坏原有任何功能,物理手感舒适。
- 完全免费:对学生党非常友好。
👎 不足
- 国内版模型与国际版不同(豆包 vs Claude),习惯 Claude 的用户可能需要适应。
- SOLO 模式有学习曲线:建议先搭建核心功能,再逐步叠加细节。
- 提示词依然最关键:你说得越细,AI 做得越好。
3.3 三条经验总结
- 分步走 —— 先做出最小可玩版本,再逐步叠加分数、难度、二段跳等细节。
- 给具体参数 —— 直接说“重力 0.6、跳跃初速度 -10”比“让跳跃自然一点”效果好 10 倍。
- 增加功能时,说清楚触发条件、次数限制和可视化反馈 —— 例如“在空中可以再跳一次,落地重置,头顶显示剩余次数”。
更多推荐




所有评论(0)