作为一个文科生,从来没想过自己能写出能运行的游戏代码。
上学的时候最头疼的就是计算机课,看见满屏幕的英文字母和符号就头大,工作之后更是和编程八竿子打不着关系。但是最近AI大火,看着朋友圈里各种程序员靠着AI提高效率,我心里也痒痒——像我这样完全零基础的门外汉,真的能靠AI做出东西来吗?
抱着试一试的心态,我决定体验一把最近很火的 "vibe coding" —— 不用懂复杂的架构设计,不用记语法,只需要把你脑子里模糊的想法告诉AI,AI就能帮你生成可运行的代码。没想到,我第一天接触,只用了半小时,就做出了两个完整可玩的小游戏:俄罗斯方块和2048!而且UI风格我还能自定义,常用功能完全符合我的使用习惯。
今天就把我的体验过程和prompt分享出来,和我一样零基础的朋友也能跟着试一试,五分钟就能体验到创造的乐趣~
什么是vibe coding?文科生也能玩得转
先给和我一样零基础的朋友科普一下,最近爆火的 vibe coding 到底是什么?
简单说,vibe coding就是程序员把大概的感觉(vibe)告诉AI,AI就能理解你的想法,大批量短时间帮你生成代码。放在以前,你想做一个游戏,得先学几个月编程语言,懂数据结构,懂图形渲染,可能光环境配置就能卡住你一周。但现在不一样了,你只需要:
1. 脑子里有一个模糊的想法:"我想要一个复古风格的俄罗斯方块"
2. 把这个想法用大白话告诉AI
3. 等待个一两分钟,就能得到一个打开就能玩的游戏
就这么简单!真的,我一个完全不懂代码的文科生,都能一遍就跑通,你说神奇不神奇?
我为什么会突然想尝试AI编程呢?其实就是想拓展一下自己的技能池。现在这个时代,AI就是最好的工具,就算你不做程序员工作,多会用一个工具,说不定哪天就能帮到你。而且,看着自己脑子里的想法从一句话,变成一个能实实在在玩的游戏,那种成就感真的太爽了!
===============================================================================
事前准备:为什么选了AiPy?
在尝试AiPy之前,我其实也把市面上常见的Agent AI都试用了个遍。有几点体验给大家分享一下:
- ChatGPT本身:能力很强,但对普通人来说门槛还是有点高,很多时候生成的代码有各种依赖问题,环境配置就能烦死你,而且需要你自己去调试,不懂代码根本改不动。
- 一些低代码平台:倒是不用写代码,但模板化太严重,想做一点点自定义修改都很麻烦,很多功能都受限。
- 国外一些新兴的AI编码Agent:对英语要求太高了,而且网络不稳定,付费也麻烦,对国内用户太不友好。
试了一圈下来,我发现 AiPy 真的是对入门期小白最友好的一个平台了。它好在哪里呢?
1. 完全中文环境:不用你蹩脚写英文提示词,大白话直接说需求,AI就能听懂,对国人太友好了
2. 对技术知识要求极低:你不需要懂什么是依赖,什么是环境变量,AiPy都帮你处理好了,你只需要说你想要什么,剩下的交给AI就行
3. 可以一步步迭代:做出来不满意,直接告诉AI你想要改哪里,比如"把背景颜色改成深蓝色","把方块变大一点",AI马上就改好了,完全不用你碰代码
4. 本地运行更安全:代码都在你自己电脑上运行,不用担心数据泄露问题
作为纯小白,第一次打开AiPy的时候我都没紧张,界面很简洁,跟着引导走两步就知道怎么用了,这点必须给个好评。
第一个游戏:零基础搞定经典俄罗斯方块
话不多说,直接进入实战环节!我第一个做的就是大家都耳熟能详的俄罗斯方块。这算是经典中的经典了,规则简单,做出来就能玩,很适合练手。
我的原始prompt(直接抄作业!)
我一开始也不知道怎么写prompt,就试着用大白话把我的需求说出来,没想到AI直接就听懂了。我给AI的指令原文是这样的:
我想做一个网页版的俄罗斯方块游戏,要求:
-
使用HTML+JavaScript+CSS实现,打开HTML文件就能直接玩,不需要安装任何软件
-
UI风格使用现代简约风,配色要舒服,深蓝色主题,方块用不同的鲜艳颜色区分
-
包含完整的游戏功能:旋转、移动、加速下落、消行得分、下一个方块预览
-
要有开始按钮、暂停按钮、分数显示、最高分记录
-
支持键盘方向键控制,也要支持手机触摸滑动控制
-
代码注释清晰,结构简单,我作为初学者能看懂大致结构
-
游戏要响应式,在不同大小的屏幕上都能正常显示
就是这么简单的一段描述,你猜怎么着?不到两分钟,AI就把完整的HTML文件生成出来了!我当时都惊讶了,这速度也太快了吧。
生成过程和微调
AI第一次生成完,我打开就能玩!真的,直接双击HTML文件,浏览器打开就进去了,一点问题都没有。功能都齐全,就是有一点点我不太满意:
1. 方块稍微有点小,我近视眼看着有点费劲
2. 预览区位置我觉得放在右边不太舒服,想放在下面
3. 背景颜色我想要更深一点的蓝
我直接把这些要求告诉AiPy,原话是:
帮我修改一下:
-
把方块格子整体调大20%
-
把下一个方块预览区从右侧移动到底部居中
-
把背景深蓝色改得更深一点,对比度调高一点,保护眼睛
然后不到一分钟,AI就改好了!我重新打开一看,完美!就是我想要的样子。整个过程我一行代码都没写,也没改,全都是AI处理,我只用大白话提需求就行。
最终成品代码
经典俄罗斯方块
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #0a192f;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
min-height: 100vh;
}
h1 {
color: #64ffda;
margin-bottom: 20px;
text-shadow: 0 0 10px rgba(100, 255, 218, 0.5);
}
.game-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
.game-info {
display: flex;
gap: 30px;
color: white;
font-size: 18px;
margin-bottom: 10px;
}
.info-box {
background-color: #112240;
padding: 10px 20px;
border-radius: 8px;
border: 1px solid #64ffda;
}
.info-box span {
color: #64ffda;
font-weight: bold;
}
#game-board {
border: 3px solid #64ffda;
border-radius: 4px;
background-color: #112240;
display: grid;
grid-template-columns: repeat(10, 30px);
grid-template-rows: repeat(20, 30px);
gap: 1px;
padding: 2px;
}
.next-container {
background-color: #112240;
border: 2px solid #64ffda;
border-radius: 8px;
padding: 15px;
margin-top: 15px;
}
.next-container h3 {
color: #64ffda;
text-align: center;
margin-bottom: 10px;
}
#next-board {
display: grid;
grid-template-columns: repeat(4, 25px);
grid-template-rows: repeat(2, 25px);
gap: 1px;
background-color: #0a192f;
padding: 2px;
}
.controls {
display: flex;
gap: 15px;
margin-top: 15px;
}
button {
background-color: #64ffda;
color: #0a192f;
border: none;
padding: 12px 30px;
font-size: 16px;
font-weight: bold;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
}
button:hover {
background-color: #52d8b8;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(100, 255, 218, 0.4);
}
.cell {
width: 100%;
height: 100%;
background-color: #0a192f;
border-radius: 2px;
}
.cell.I { background-color: #00f0f0; border: 2px solid #00cccc; }
.cell.J { background-color: #0000f0; border: 2px solid #0000cc; }
.cell.L { background-color: #f0a000; border: 2px solid #cc8800; }
.cell.O { background-color: #f0f000; border: 2px solid #cccc00; }
.cell.S { background-color: #00f000; border: 2px solid #00cc00; }
.cell.T { background-color: #a000f0; border: 2px solid #8800cc; }
.cell.Z { background-color: #f00000; border: 2px solid #cc0000; }
.instructions {
margin-top: 20px;
color: #8892b0;
text-align: center;
max-width: 500px;
line-height: 1.6;
}
.instructions h3 {
color: #64ffda;
margin-bottom: 10px;
}
@media (max-width: 600px) {
#game-board {
grid-template-columns: repeat(10, 25px);
grid-template-rows: repeat(20, 25px);
}
}
🎮 经典俄罗斯方块
开始游戏
暂停
操作说明
⬅️ ➡️ 方向键左右移动 | ⬆️ 方向键旋转 | ⬇️ 方向键加速下落
手机端: 滑动控制移动,点击旋转
// 游戏配置
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 30;
// 方块形状定义
const SHAPES = {
I: [
[1, 1, 1, 1]
],
J: [
[1, 0, 0],
[1, 1, 1]
],
L: [
[0, 0, 1],
[1, 1, 1]
],
O: [
[1, 1],
[1, 1]
],
S: [
[0, 1, 1],
[1, 1, 0]
],
T: [
[0, 1, 0],
[1, 1, 1]
],
Z: [
[1, 1, 0],
[0, 1, 1]
]
};
// 颜色对应
const COLORS = {
I: 'I',
J: 'J',
L: 'L',
O: 'O',
S: 'S',
T: 'T',
Z: 'Z'
};
// 游戏状态
let board = [];
let currentShape = null;
let currentColor = null;
let currentX = 0;
let currentY = 0;
let nextShape = null;
let nextColor = null;
let score = 0;
let lines = 0;
let highScore = localStorage.getItem('tetrisHighScore') || 0;
let gameInterval = null;
let isGameOver = false;
let isPaused = false;
// DOM元素
const gameBoard = document.getElementById('game-board');
const nextBoard = document.getElementById('next-board');
const scoreElement = document.getElementById('score');
const highScoreElement = document.getElementById('high-score');
const linesElement = document.getElementById('lines');
const startBtn = document.getElementById('start-btn');
const pauseBtn = document.getElementById('pause-btn');
// 初始化最高分显示
highScoreElement.textContent = highScore;
// 初始化游戏面板
function initBoard() {
gameBoard.innerHTML = '';
board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.id = cell-${y}-${x};
gameBoard.appendChild(cell);
}
}
}
// 初始化预览面板
function initNextBoard() {
nextBoard.innerHTML = '';
for (let y = 0; y < 2; y++) {
for (let x = 0; x < 4; x++) {
const cell = document.createElement('div');
cell.className = 'cell';
nextBoard.appendChild(cell);
}
}
}
// 随机生成新方块
function randomShape() {
const keys = Object.keys(SHAPES);
const randomKey = keys[Math.floor(Math.random() * keys.length)];
return {
shape: SHAPES[randomKey],
color: COLORS[randomKey]
};
}
// 绘制当前方块
function drawCurrentShape() {
currentShape.forEach((row, y) => {
row.forEach((value, x) => {
if (value) {
const boardY = currentY + y;
const boardX = currentX + x;
if (boardY >= 0) {
const cell = document.getElementById(cell-${boardY}-${boardX});
if (cell) cell.classList.add(currentColor);
}
}
});
});
}
// 清除当前方块
function clearCurrentShape() {
currentShape.forEach((row, y) => {
row.forEach((value, x) => {
if (value) {
const boardY = currentY + y;
const boardX = currentX + x;
if (boardY >= 0) {
const cell = document.getElementById(cell-${boardY}-${boardX});
if (cell) cell.classList.remove(currentColor);
}
}
});
});
}
// 绘制预览方块
function drawNextShape() {
// 清除预览
for (let i = 0; i < nextBoard.children.length; i++) {
nextBoard.children[i].className = 'cell';
}
// 居中绘制
const offsetX = Math.floor((4 - nextShape[0].length) / 2);
const offsetY = Math.floor((2 - nextShape.length) / 2);
nextShape.forEach((row, y) => {
row.forEach((value, x) => {
if (value) {
const index = (y + offsetY) * 4 + (x + offsetX);
nextBoard.children[index].classList.add(nextColor);
}
});
});
}
// 检查碰撞
function checkCollision(shape, offsetX, offsetY) {
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x]) {
const boardX = offsetX + x;
const boardY = offsetY + y;
if (boardX < 0 || boardX >= COLS || boardY >= ROWS) {
return true;
}
if (boardY >= 0 && board[boardY][boardX]) {
return true;
}
}
}
}
return false;
}
// 旋转方块
function rotate() {
const rotated = currentShape[0].map((_, i) =>
currentShape.map(row => row[i]).reverse()
);
if (!checkCollision(rotated, currentX, currentY)) {
clearCurrentShape();
currentShape = rotated;
drawCurrentShape();
}
}
// 移动方块
function move(dx, dy) {
if (!checkCollision(currentShape, currentX + dx, currentY + dy)) {
clearCurrentShape();
currentX += dx;
currentY += dy;
drawCurrentShape();
return true;
}
if (dy > 0) {
lockShape();
return false;
}
return false;
}
// 锁定方块到面板
function lockShape() {
currentShape.forEach((row, y) => {
row.forEach((value, x) => {
if (value) {
const boardY = currentY + y;
const boardX = currentX + x;
if (boardY >= 0) {
board[boardY][boardX] = currentColor;
const cell = document.getElementById(cell-${boardY}-${boardX});
cell.classList.add(currentColor);
}
}
});
});
clearLines();
spawnShape();
}
// 清除满行
function clearLines() {
let linesCleared = 0;
for (let y = ROWS - 1; y >= 0; y--) {
if (board[y].every(cell => cell !== 0)) {
// 移除该行
board.splice(y, 1);
// 在顶部添加新行
board.unshift(Array(COLS).fill(0));
linesCleared++;
y++; // 重新检查当前位置
}
}
if (linesCleared > 0) {
lines += linesCleared;
score += linesCleared * linesCleared * 100;
linesElement.textContent = lines;
scoreElement.textContent = score;
// 更新最高分
if (score > highScore) {
highScore = score;
highScoreElement.textContent = highScore;
localStorage.setItem('tetrisHighScore', highScore);
}
// 重绘面板
redrawBoard();
}
}
// 重绘整个面板
function redrawBoard() {
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
const cell = document.getElementById(cell-${y}-${x});
cell.className = 'cell';
if (board[y][x]) {
cell.classList.add(board[y][x]);
}
}
}
}
// 生成新方块
function spawnShape() {
currentShape = nextShape || randomShape().shape;
currentColor = nextColor || randomShape().color;
const next = randomShape();
nextShape = next.shape;
nextColor = next.color;
drawNextShape();
currentX = Math.floor((COLS - currentShape[0].length) / 2);
currentY = 0;
if (checkCollision(currentShape, currentX, currentY)) {
gameOver();
}
}
// 游戏结束
function gameOver() {
isGameOver = true;
clearInterval(gameInterval);
gameInterval = null;
alert(游戏结束!你的分数是: ${score});
}
// 下落一步
function drop() {
if (!isPaused && !isGameOver) {
move(0, 1);
}
}
// 硬降(直接落到底)
function hardDrop() {
while (move(0, 1)) {}
}
// 开始新游戏
function startGame() {
initBoard();
score = 0;
lines = 0;
isGameOver = false;
isPaused = false;
scoreElement.textContent = score;
linesElement.textContent = lines;
nextShape = null;
nextColor = null;
spawnShape();
if (gameInterval) clearInterval(gameInterval);
gameInterval = setInterval(drop, 1000);
}
// 暂停游戏
function pauseGame() {
if (isGameOver) return;
isPaused = !isPaused;
pauseBtn.textContent = isPaused ? '继续' : '暂停';
}
// 键盘控制
document.addEventListener('keydown', (e) => {
if (isGameOver || !gameInterval) return;
switch(e.key) {
case 'ArrowLeft':
if (!isPaused) move(-1, 0);
break;
case 'ArrowRight':
if (!isPaused) move(1, 0);
break;
case 'ArrowDown':
if (!isPaused) move(0, 1);
break;
case 'ArrowUp':
if (!isPaused) rotate();
break;
case ' ':
e.preventDefault();
if (!isPaused) hardDrop();
break;
}
});
// 触摸控制
let touchStartX = 0;
let touchStartY = 0;
document.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
});
document.addEventListener('touchend', (e) => {
if (isGameOver || isPaused || !gameInterval) return;
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const diffX = touchEndX - touchStartX;
const diffY = touchEndY - touchStartY;
if (Math.abs(diffX) > Math.abs(diffY)) {
// 水平滑动
if (diffX > 30) {
move(1, 0);
} else if (diffX < -30) {
move(-1, 0);
}
} else {
// 垂直滑动
if (diffY > 30) {
move(0, 1);
} else if (diffY < -30) {
rotate();
}
}
});
// 绑定按钮事件
startBtn.addEventListener('click', startGame);
pauseBtn.addEventListener('click', pauseGame);
// 初始化
initBoard();
initNextBoard();
你没看错,就是这么完整的一个文件,AI直接生成好了。你只要把上面这段代码复制保存成一个.html文件,双击就能玩。我作为一个文科生,现在想想都觉得不可思议——我竟然"做"出了一个俄罗斯方块!
===============================================================================
第二个游戏:趁热打铁,再来一个2048
第一个俄罗斯方块做的这么顺利,我信心一下子就上来了。反正都花了十几分钟了,干脆一鼓作气,再做一个经典的——2048!这个更简单,规则大家都懂,AI做起来应该更快。
我的prompt分享
有了第一次的经验,第二次写prompt我就更顺手了,直接告诉AI我想要什么:
继续帮我做一个网页版的2048游戏,要求:
-
同样使用HTML+JavaScript+CSS,单文件就能运行
-
UI风格要和刚才的俄罗斯方块统一,也是深蓝色主题,现代简约风
-
包含完整功能:随机生成方块、滑动合并、分数计算、最高分记录、重来按钮
-
支持键盘方向键滑动,也支持手机触摸滑动
-
方块颜色要根据数字大小渐变,好看清晰
-
游戏获胜条件是得到2048,可以选择继续玩或者重新开始
-
响应式设计,手机上也要玩得舒服
看到了吗,就是这么简单,需求说清楚,AI就懂了。不到一分钟,AI就把代码生成出来了!还是老样子,直接双击打开就能运行,一点问题都没有。
微调过程
第一次生成的2048也很棒,就是有一点我觉得可以改进:方块间距有点小,我手指粗,手机上玩容易点错。我就告诉AI:
帮我把方块间距调大一点,整体格子也稍微放大一点,方便手机触摸操作;然后把数字字体也调大一些,看着更清楚。
分分钟就改好了,完美!真的,这种迭代体验太爽了,你不用懂代码,就告诉AI你想要改什么,分分钟给你搞定。
最终成品代码
经典2048
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #0a192f;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
min-height: 100vh;
}
h1 {
color: #64ffda;
margin-bottom: 10px;
text-shadow: 0 0 10px rgba(100, 255, 218, 0.5);
}
.game-description {
color: #8892b0;
margin-bottom: 20px;
text-align: center;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
width: 100%;
}
.score-container {
display: flex;
gap: 10px;
}
.score-box {
background-color: #112240;
padding: 10px 15px;
border-radius: 8px;
border: 1px solid #64ffda;
text-align: center;
min-width: 80px;
}
.score-box .label {
color: #8892b0;
font-size: 12px;
}
.score-box .value {
color: #64ffda;
font-size: 20px;
font-weight: bold;
}
#game-board {
background-color: #112240;
border-radius: 10px;
padding: 15px;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
border: 2px solid #64ffda;
width: 400px;
height: 400px;
}
.tile {
background-color: #0a192f;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
font-weight: bold;
transition: all 0.2s ease;
}
.tile-2 { background-color: #eee4da; color: #776e65; }
.tile-4 { background-color: #ede0c8; color: #776e65; }
.tile-8 { background-color: #f2b179; color: #f9f6f2; }
.tile-16 { background-color: #f59563; color: #f9f6f2; }
.tile-32 { background-color: #f67c5f; color: #f9f6f2; }
.tile-64 { background-color: #f65e3b; color: #f9f6f2; }
.tile-128 { background-color: #edcf72; color: #f9f6f2; }
.tile-256 { background-color: #edcc61; color: #f9f6f2; }
.tile-512 { background-color: #edc850; color: #f9f6f2; }
.tile-1024 { background-color: #edc53f; color: #f9f6f2; font-size: 22px; }
.tile-2048 { background-color: #edc22e; color: #f9f6f2; font-size: 22px; }
.tile-4096 { background-color: #3c3a32; color: #f9f6f2; font-size: 22px; }
.tile-8192 { background-color: #3c3a32; color: #f9f6f2; font-size: 22px; }
.controls {
display: flex;
gap: 15px;
margin-top: 20px;
}
button {
background-color: #64ffda;
color: #0a192f;
border: none;
padding: 12px 30px;
font-size: 16px;
font-weight: bold;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
}
button:hover {
background-color: #52d8b8;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(100, 255, 218, 0.4);
}
.instructions {
margin-top: 20px;
color: #8892b0;
text-align: center;
max-width: 400px;
line-height: 1.6;
}
.instructions h3 {
color: #64ffda;
margin-bottom: 10px;
}
.game-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(10, 25, 47, 0.8);
display: none;
align-items: center;
justify-content: center;
z-index: 100;
}
.message-box {
background-color: #112240;
padding: 40px;
border-radius: 10px;
border: 2px solid #64ffda;
text-align: center;
}
.message-box h2 {
color: #64ffda;
margin-bottom: 15px;
}
.message-box p {
color: #8892b0;
margin-bottom: 20px;
}
@media (max-width: 500px) {
#game-board {
width: 320px;
height: 320px;
gap: 10px;
padding: 10px;
}
.tile {
font-size: 22px;
}
.tile-1024, .tile-2048, .tile-4096, .tile-8192 {
font-size: 18px;
}
}
🎮 经典2048
合并方块,得到2048!
重新开始
操作说明
⬅️ ➡️ ⬆️ ⬇️ 方向键滑动移动
手机端: 手指滑动控制方向
相同数字的方块会合并,目标是拼出2048!
// 游戏配置
const GRID_SIZE = 4;
let grid = [];
let score = 0;
let bestScore = localStorage.getItem('2048BestScore') || 0;
let gameOver = false;
let gameWon = false;
// DOM元素
const gameBoard = document.getElementById('game-board');
const scoreElement = document.getElementById('score');
const bestScoreElement = document.getElementById('best-score');
const gameOverlay = document.getElementById('game-overlay');
const gameTitle = document.getElementById('game-title');
const gameMessage = document.getElementById('game-message');
const newGameBtn = document.getElementById('new-game-btn');
const continueBtn = document.getElementById('continue-btn');
const restartBtn = document.getElementById('restart-btn');
// 初始化最高分
bestScoreElement.textContent = bestScore;
// 初始化游戏面板
function initBoard() {
gameBoard.innerHTML = '';
grid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
const tile = document.createElement('div');
tile.className = 'tile';
tile.id = tile-${y}-${x};
gameBoard.appendChild(tile);
}
}
}
// 随机添加一个新方块
function addRandomTile() {
const emptyCells = [];
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
if (grid[y][x] === 0) {
emptyCells.push({x, y});
}
}
}
if (emptyCells.length === 0) return false;
const {x, y} = emptyCells[Math.floor(Math.random() * emptyCells.length)];
grid[y][x] = Math.random() < 0.9 ? 2 : 4;
updateBoard();
return true;
}
// 更新面板显示
function updateBoard() {
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
const tile = document.getElementById(tile-${y}-${x});
const value = grid[y][x];
tile.className = tile tile-${value};
tile.textContent = value === 0 ? '' : value;
}
}
scoreElement.textContent = score;
if (score > bestScore) {
bestScore = score;
bestScoreElement.textContent = bestScore;
localStorage.setItem('2048BestScore', bestScore);
}
}
// 移动一行
function moveRow(row) {
// 移除零
let newRow = row.filter(num => num !== 0);
let merged = false;
for (let i = 0; i < newRow.length - 1; i++) {
if (newRow[i] === newRow[i + 1] && !merged) {
newRow[i] *= 2;
score += newRow[i];
newRow.splice(i + 1, 1);
merged = true;
// 检查是否得到2048
if (newRow[i] === 2048 && !gameWon) {
gameWon = true;
showWinMessage();
}
} else {
merged = false;
}
}
// 补零
while (newRow.length < GRID_SIZE) {
newRow.push(0);
}
return newRow;
}
// 向左移动
function moveLeft() {
let moved = false;
for (let y = 0; y < GRID_SIZE; y++) {
const originalRow = [...grid[y]];
grid[y] = moveRow(grid[y]);
if (JSON.stringify(originalRow) !== JSON.stringify(grid[y])) {
moved = true;
}
}
return moved;
}
// 向右移动
function moveRight() {
let moved = false;
for (let y = 0; y < GRID_SIZE; y++) {
const originalRow = [...grid[y]];
grid[y] = moveRow([...grid[y]].reverse()).reverse();
if (JSON.stringify(originalRow) !== JSON.stringify(grid[y])) {
moved = true;
}
}
return moved;
}
// 向上移动
function moveUp() {
let moved = false;
for (let x = 0; x < GRID_SIZE; x++) {
let column = [];
for (let y = 0; y < GRID_SIZE; y++) {
column.push(grid[y][x]);
}
const originalColumn = [...column];
column = moveRow(column);
if (JSON.stringify(originalColumn) !== JSON.stringify(column)) {
moved = true;
}
for (let y = 0; y < GRID_SIZE; y++) {
grid[y][x] = column[y];
}
}
return moved;
}
// 向下移动
function moveDown() {
let moved = false;
for (let x = 0; x < GRID_SIZE; x++) {
let column = [];
for (let y = 0; y < GRID_SIZE; y++) {
column.push(grid[y][x]);
}
const originalColumn = [...column];
column = moveRow(column.reverse()).reverse();
if (JSON.stringify(originalColumn) !== JSON.stringify(column)) {
moved = true;
}
for (let y = 0; y < GRID_SIZE; y++) {
grid[y][x] = column[y];
}
}
return moved;
}
// 检查游戏是否结束
function checkGameOver() {
// 还有空格
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
if (grid[y][x] === 0) return false;
}
}
// 还有可以合并的相邻格子
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
const current = grid[y][x];
if (x < GRID_SIZE - 1 && grid[y][x + 1] === current) return false;
if (y < GRID_SIZE - 1 && grid[y + 1][x] === current) return false;
}
}
return true;
}
// 显示获胜消息
function showWinMessage() {
gameTitle.textContent = '🎉 恭喜你!';
gameMessage.textContent = '成功拼出了2048!是否继续游戏?';
gameOverlay.style.display = 'flex';
}
// 显示游戏结束消息
function showGameOverMessage() {
gameTitle.textContent = '游戏结束';
gameMessage.textContent = 你的最终分数是: ${score};
gameOverlay.style.display = 'flex';
gameOver = true;
}
// 继续游戏
function continueGame() {
gameOverlay.style.display = 'none';
}
// 重新开始
function newGame() {
grid = [];
score = 0;
gameOver = false;
gameWon = false;
gameOverlay.style.display = 'none';
initBoard();
addRandomTile();
addRandomTile();
updateBoard();
}
// 处理移动
function handleMove(direction) {
if (gameOver) return;
let moved = false;
switch(direction) {
case 'left':
moved = moveLeft();
break;
case 'right':
moved = moveRight();
break;
case 'up':
moved = moveUp();
break;
case 'down':
moved = moveDown();
break;
}
if (moved) {
addRandomTile();
updateBoard();
if (checkGameOver()) {
showGameOverMessage();
}
}
}
// 键盘控制
document.addEventListener('keydown', (e) => {
switch(e.key) {
case 'ArrowLeft':
e.preventDefault();
handleMove('left');
break;
case 'ArrowRight':
e.preventDefault();
handleMove('right');
break;
case 'ArrowUp':
e.preventDefault();
handleMove('up');
break;
case 'ArrowDown':
e.preventDefault();
handleMove('down');
break;
}
});
// 触摸控制
let touchStartX = 0;
let touchStartY = 0;
document.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
});
document.addEventListener('touchend', (e) => {
if (gameOver) return;
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const diffX = touchEndX - touchStartX;
const diffY = touchEndY - touchStartY;
if (Math.abs(diffX) > Math.abs(diffY)) {
// 水平滑动
if (Math.abs(diffX) > 20) {
handleMove(diffX > 0 ? 'right' : 'left');
}
} else {
// 垂直滑动
if (Math.abs(diffY) > 20) {
handleMove(diffY > 0 ? 'down' : 'up');
}
}
});
// 绑定事件
newGameBtn.addEventListener('click', newGame);
continueBtn.addEventListener('click', continueGame);
restartBtn.addEventListener('click', newGame);
// 开始游戏
initBoard();
newGame();
拿到这个文件我直接就玩了好几局,停不下来。配色和俄罗斯方块保持了统一,操作流畅,该有的功能一个不少,最高分还存在本地,刷新页面都不会丢,太赞了。
使用体验
讲完了两个游戏的制作过程,我给大家聊聊作为一个完全不懂编程的文科生,这半个小时体验下来,我的真实感受是什么。
首先,颠覆认知。我以前一直觉得编程是聪明人、理科生才能干的活,像我这种看到代码就头疼的人,这辈子都沾不上边。但这次体验下来,我发现AI真的把编程的门槛拉到了地板上。你不需要懂什么是变量,什么是循环,什么是函数,你只需要会说中文,能讲清楚你想要什么,就行了。AI都帮你搞定了。
其次,AiPy真的对小白太友好了。我也用过别的AI编程产品,有的生成出来代码一堆错,运行不起来,还得你自己装依赖,改bug,我哪会这个啊。AiPy就不一样了,它生成的代码我第一次打开就能运行,就算我有不满意的地方,直接用大白话告诉它改哪里,它分分钟就改好了,全程我一行代码都不用碰。这种体验对于零基础小白来说太重要了——你刚入门,信心比什么都重要,要是第一次就遇到一堆bug,估计你直接就放弃了。就算担心token不够用也没关系,就邀请码填c8W3,会有两百万的token。
第三,效率真的太高了。我做这两个完整可玩的游戏,加上微调,总共才花了半个小时。放在以前,你就算是专业程序员,从零开始做两个这样的游戏,没有大半天也搞不定吧?我一个零基础小白,半个小时就搞定了,这就是AI的力量啊。
第四,原来创造这么简单。这种把你脑子里想法变成实实在在东西的感觉,真的太爽了。我原来就是刷手机,看别人做这个做那个,现在我发现,哦,原来我也可以啊!只要我有想法,AI就能帮我实现。这种成就感真的很难形容,你得自己体验一次才知道。
给同样零基础想尝试的朋友一些建议
看了我的经历,你是不是也心动了?我给和我一样零基础的朋友几点建议:
1. 别怕,大胆去试。你不用懂代码,真的,就像我,纯文科生都能做出来,你怕什么?想到什么就直接说,AI现在理解能力很强的。
2. prompt写清楚你的需求。不用太复杂,就用大白话,把你想要的功能、风格、要求一条一条列出来,AI更容易理解,生成出来的东西也更符合你的预期。
3. 不满意就改。不用第一次就要求完美,做出来之后,哪里不满意直接告诉AI,让它改,多改两次就是你想要的了。AI不会嫌你麻烦的。
4. 从简单的小项目开始。就像我,先做两个经典小游戏,难度不大,容易成功,能快速建立信心。信心有了,你再去尝试更复杂的东西。
5. 选对工具很重要。我试用了这么多,真心推荐零基础小白从AiPy开始,中文环境,对技术要求低,不容易踩坑,能让你快速体验到成功的乐趣。
总结:AI时代,每个人都可以是创造者
我原来一直觉得,"编程"、"创造"这些词都是属于技术人员的,和我这种文科生没关系。但这次半个小时做两个游戏的经历,真的改变了我的想法。
在AI时代,你不需要掌握每一项技能,你只需要有想法,知道怎么用AI工具帮你实现就行了。AI就是你的左膀右臂,它帮你处理那些技术细节,你只需要负责创意和想法。
vibe coding真的没有你想象的那么难,就像我,编程零基础,半个小时都能做出两个可玩的小游戏,你为什么不试一试呢?说不定你脑子里那个放了很久的想法,现在就能用AI实现出来了。
最后,把我用到的prompt都放在文章里了,你直接拿去用就行,改一改就能做出你自己风格的游戏。快去试一试吧,期待你也能体验到创造的乐趣!
PS:文章里的两个HTML文件都可以直接复制保存,双击就能玩,拿走不谢~如果你也做出来有意思的东西,欢迎在评论区分享呀!