震惊!可以用云开发AI助手Copilot生成2048小游戏
一、介绍
对于后端开发人员来说,处理数据这些不是什么难事;难得往往是界面设计上的问题,展示归展示,丑是真的丑。
还面临着,前端不熟悉,遇到问题到处询问的痛点。
但现在,大人,时代变了;我们有了微搭低代码平台,可以轻轻松松的将组件像搭积木一样搭成小程序和网页。
有人说,这种低代码学习成本,且可玩性不高。
以前我会说是的,但现在,不需要很懂,就能轻轻松松使用。
原因就是有了Copilot
,这是一个云开发低代码AI
助手
那么,我们先试试这个AI
助手有多么厉害吧
二、简单试试
首先打开云开发,低代码应用列表,点击从AI
创建
进入到页面,简简单单试下猫咪领养的小程序,输入下面这段提示词
生成一个流浪猫领养官网,支持领养、云领养、云投喂等功能
点击生成,静静等待
如果不满意,我们可以修改提示词继续让AI
生成;
我们这边,直接点击使用该生成效果保存精调,它会自动给我们创建一个应用,来搭载这个项目。
进入下面这个页面,可以在此进行调整
我什么修改都不做,直接发布一下,在右上角可以选择进行发布
可以选择小程序、web
网页应用
发布完成后,我手机扫码预览下效果
三、2048
上面只是开胃小菜,我们现在来试试使用Copilot
生成2048
小游戏,话不多说,直接展示
代码会程序生成,我们看看效果,结构有模有样
// @ts-ignore;
import React, { useState } from 'react';
// @ts-ignore;
import { Button, Card, CardContent, CardHeader, CardTitle, Grid, GridItem } from '@/components/ui';
const getRandomInt = (min, max) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
const startGame = (setBoard, setGameStarted) => {
let newBoard = Array.from({
length: 4
}, () => Array(4).fill(''));
let positions = [];
while (positions.length < 2) {
let row = getRandomInt(0, 3);
let col = getRandomInt(0, 3);
if (!positions.some(pos => pos[0] === row && pos[1] === col)) {
positions.push([row, col]);
newBoard[row][col] = Math.random() < 0.9 ? 2 : 4;
}
}
setBoard(newBoard);
setGameStarted(true);
};
const generateNewNumber = board => {
let emptyCells = [];
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
if (board[row][col] === '') {
emptyCells.push({
row: row,
col: col
});
}
}
}
if (emptyCells.length > 0) {
let randomIndex = Math.floor(Math.random() * emptyCells.length);
let selectedCell = emptyCells[randomIndex];
board[selectedCell.row][selectedCell.col] = Math.random() < 0.9 ? 2 : 4;
}
return board;
};
const handleMove = (direction, setBoard, setGameStarted) => {
// TODO: Implement the logic for moving the tiles
};
export default function Game2048(props) {
const [board, setBoard] = useState(Array.from({
length: 4
}, () => Array(4).fill('')));
const [gameStarted, setGameStarted] = useState(false);
return <Card className="p-4">
<CardHeader>
<div className="flex items-center space-x-4">
<CardTitle>2048 Game</CardTitle>
</div>
</CardHeader>
<CardContent>
<Grid className="grid-cols-4 gap-2">
{board.map((row, rowIndex) => <div key={rowIndex} className="flex flex-row">
{row.map((cell, colIndex) => <GridItem key={`${rowIndex}-${colIndex}`} className={`border p-2 w-16 h-16`}>
{cell}
</GridItem>)}
</div>)}
</Grid>
<div className="mt-4 flex space-x-4">
<Button onClick={() => handleMove('up', setBoard, setGameStarted)}>上</Button>
<Button onClick={() => handleMove('left', setBoard, setGameStarted)}>左</Button>
<Button onClick={() => handleMove('right', setBoard, setGameStarted)}>右</Button>
<Button onClick={() => handleMove('down', setBoard, setGameStarted)}>下</Button>
</div>
{!gameStarted && <Button onClick={() => startGame(setBoard, setGameStarted)}>开始游戏</Button>}
</CardContent>
</Card>;
}
看不爽,这个开始游戏在下面,我们再给Copilot
一条指令,让这个按钮放到上面去;
重新生成的结果如下
到了现在,比较重要的是handleMove()
这个函数,我们给与下面这样的提示词
请帮忙生成2048小游戏核心逻辑,只需要生成handleMove函数,不考虑游戏结构
生成代码如下
// @ts-ignore;
import React from 'react';
const handleMove = (direction, setBoard, setGameStarted) => {
let newBoard = [...board];
let moved = false;
switch (direction) {
case 'up':
for (let col = 0; col < 4; col++) {
let lastMergedRow = -1;
for (let row = 1; row < 4; row++) {
if (newBoard[row][col] !== '') {
let current = newBoard[row][col];
newBoard[row][col] = '';
let targetRow = row;
while (targetRow > 0 && newBoard[targetRow - 1][col] === '') {
targetRow--;
}
if (targetRow === lastMergedRow) {
newBoard[targetRow - 1][col] += current;
lastMergedRow = -1;
} else if (targetRow !== row) {
newBoard[targetRow][col] = current;
lastMergedRow = targetRow;
} else {
lastMergedRow = row;
}
}
}
}
break;
case 'down':
for (let col = 0; col < 4; col++) {
let lastMergedRow = 4;
for (let row = 2; row >= 0; row--) {
if (newBoard[row][col] !== '') {
let current = newBoard[row][col];
newBoard[row][col] = '';
let targetRow = row;
while (targetRow < lastMergedRow - 1 && newBoard[targetRow + 1][col] === '') {
targetRow++;
}
if (targetRow === lastMergedRow - 1) {
newBoard[targetRow + 1][col] += current;
lastMergedRow = targetRow;
} else if (targetRow !== row) {
newBoard[targetRow][col] = current;
lastMergedRow = targetRow + 1;
} else {
lastMergedRow = row;
}
}
}
}
break;
case 'left':
for (let row = 0; row < 4; row++) {
let lastMergedCol = -1;
for (let col = 1; col < 4; col++) {
if (newBoard[row][col] !== '') {
let current = newBoard[row][col];
newBoard[row][col] = '';
let targetCol = col;
while (targetCol > 0 && newBoard[row][targetCol - 1] === '') {
targetCol--;
}
if (targetCol === lastMergedCol) {
newBoard[row][targetCol - 1] += current;
lastMergedCol = -1;
} else if (targetCol !== col) {
newBoard[row][targetCol] = current;
lastMergedCol = targetCol;
} else {
lastMergedCol = col;
}
}
}
}
break;
case 'right':
for (let row = 0; row < 4; row++) {
let lastMergedCol = 4;
for (let col = 2; col >= 0; col--) {
if (newBoard[row][col] !== '') {
let current = newBoard[row][col];
newBoard[row][col] = '';
let targetCol = col;
while (targetCol < lastMergedCol - 1 && newBoard[row][targetCol + 1] === '') {
targetCol++;
}
if (targetCol === lastMergedCol - 1) {
newBoard[row][targetCol + 1] += current;
lastMergedCol = targetCol;
} else if (targetCol !== col) {
newBoard[row][targetCol] = current;
lastMergedCol = targetCol + 1;
} else {
lastMergedCol = col;
}
}
}
}
break;
}
if (moved) {
setBoard(newBoard);
if (!newBoard.flat().some(cell => cell === '')) {
// No empty cells, game over or win condition
} else {
setBoard(generateNewNumber(newBoard));
}
}
};
处理结束后,点击预览查看效果
格子没有颜色,不友好,我们添加背景颜色,直接和Copliot
说,这一块就不展示了
运行查看效果,如下
发现点击按钮后,数字在格子中是一格一格移动的,这不是我们想要的
但我这一块给指令,生成的效果实在不怎么理想,试过了好多次
最终还是回到了第一版,自己改改用数组当队列来用,好吧我们来看看全部代码
// @ts-ignore;
import React, { useState } from 'react';
// @ts-ignore;
import { Button, Card, CardContent, CardHeader, CardTitle, Grid, GridItem } from '@/components/ui';
const getColorByValue = value => {
switch (value) {
case 2:
return 'bg-gray-200';
case 4:
return 'bg-gray-300';
case 8:
return 'bg-gray-400';
case 16:
return 'bg-gray-500';
case 32:
return 'bg-gray-600';
case 64:
return 'bg-gray-700';
case 128:
return 'bg-gray-800';
case 256:
return 'bg-gray-900';
case 512:
return 'bg-gray-950';
case 1024:
return 'bg-gray-970';
case 2048:
return 'bg-gray-990';
default:
return '';
}
};
const getRandomInt = (min, max) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
const startGame = (setBoard, setGameStarted) => {
let newBoard = Array.from({
length: 4
}, () => Array(4).fill(''));
let positions = [];
while (positions.length < 2) {
let row = getRandomInt(0, 3);
let col = getRandomInt(0, 3);
if (!positions.some(pos => pos[0] === row && pos[1] === col)) {
positions.push([row, col]);
newBoard[row][col] = 2;
}
}
setBoard(newBoard);
setGameStarted(true);
};
export default function Game2024(props) {
const [board, setBoard] = useState(Array.from({
length: 4
}, () => Array(4).fill('')));
const [gameStarted, setGameStarted] = useState(false);
const handleMove = direction => {
let newBoard = [...board];
let didMove = false;
switch (direction) {
case 'up':
for (let col = 0; col < 4; col++) {
let lastMergedRow = [];
let finalMergedRow = [];
for (let row = 0; row < 4; row++) {
if (newBoard[row][col] !== '') {
lastMergedRow.push(newBoard[row][col]);
}
}
for (let row = 0; row < lastMergedRow.length - 1; row++) {
if (lastMergedRow[row] === lastMergedRow[row + 1]) {
lastMergedRow[row] *= 2;
lastMergedRow[row + 1] = '';
didMove = true;
}
}
for (let num of lastMergedRow) {
if (num !== '') {
finalMergedRow.push(num);
}
}
while (finalMergedRow.length < 4) {
finalMergedRow.push('');
}
for (let row = 0; row < 4; row++) {
let oldNum = newBoard[row][col];
let newNum = finalMergedRow[row];
newBoard[row][col] = newNum;
if (!didMove) {
didMove = oldNum!=newNum;
}
}
}
break;
case 'down':
for (let col = 0; col < 4; col++) {
let lastMergedRow = [];
let finalMergedRow = [];
for (let row = 3; row >= 0; row--) {
if (newBoard[row][col] !== '') {
lastMergedRow.push(newBoard[row][col]);
}
}
for (let row = 0; row < lastMergedRow.length - 1; row++) {
if (lastMergedRow[row] === lastMergedRow[row + 1]) {
lastMergedRow[row] *= 2;
lastMergedRow[row + 1] = '';
didMove = true;
}
}
for (let num of lastMergedRow) {
if (num !== '') {
finalMergedRow.push(num);
}
}
while (finalMergedRow.length < 4) {
finalMergedRow.push('');
}
for (let row = 0; row < 4; row++) {
let oldNum = newBoard[3 - row][col];
let newNum = finalMergedRow[row];
newBoard[3 - row][col] = newNum;
if (!didMove) {
didMove = oldNum!=newNum;
}
}
}
break;
case 'left':
for (let row = 0; row < 4; row++) {
let lastMergedCol = [];
let finalMergedCol = [];
for (let col = 0; col < 4; col++) {
if (newBoard[row][col] !== '') {
lastMergedCol.push(newBoard[row][col]);
}
}
for (let col = 0; col < lastMergedCol.length - 1; col++) {
if (lastMergedCol[col] === lastMergedCol[col + 1]) {
lastMergedCol[col] *= 2;
lastMergedCol[col + 1] = '';
didMove = true;
}
}
for (let num of lastMergedCol) {
if (num !== '') {
finalMergedCol.push(num);
}
}
while (finalMergedCol.length < 4) {
finalMergedCol.push('');
}
for (let col = 0; col < 4; col++) {
let oldNum = newBoard[row][col];
let newNum = finalMergedCol[col];
newBoard[row][col] = newNum;
if (!didMove) {
didMove = oldNum!=newNum;
}
}
}
break;
case 'right':
for (let row = 0; row < 4; row++) {
let lastMergedCol = [];
let finalMergedCol = [];
for (let col = 3; col >= 0; col--) {
if (newBoard[row][col] !== '') {
lastMergedCol.push(newBoard[row][col]);
}
}
for (let col = 0; col < lastMergedCol.length - 1; col++) {
if (lastMergedCol[col] === lastMergedCol[col + 1]) {
lastMergedCol[col] *= 2;
lastMergedCol[col + 1] = '';
didMove = true;
}
}
for (let num of lastMergedCol) {
if (num !== '') {
finalMergedCol.push(num);
}
}
while (finalMergedCol.length < 4) {
finalMergedCol.push('');
}
for (let col = 0; col < 4; col++) {
let oldNum = newBoard[row][3 - col];
let newNum = finalMergedCol[col];
newBoard[row][3 - col] = newNum;
if (!didMove) {
didMove = oldNum!=newNum;
}
}
}
break;
}
console.log(didMove);
console.log(newBoard);
if (didMove) {
generateNewNumber(newBoard);
}
console.log(newBoard);
setBoard(newBoard);
};
const generateNewNumber = newBoard => {
let emptyCells = [];
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
if (newBoard[row][col] === '') {
emptyCells.push({row: row, col: col});
}
}
}
if (emptyCells.length > 0) {
let randomIndex = Math.floor(Math.random() * emptyCells.length);
let selectedCell = emptyCells[randomIndex];
newBoard[selectedCell.row][selectedCell.col] = Math.random() < 0.9 ? 2 : 4;
}
return newBoard;
};
return <Card className="p-4">
<CardHeader>
<div className="flex items-center space-x-4">
<CardTitle>2024 Game</CardTitle>
<Button onClick={() => startGame(setBoard, setGameStarted)}>开始游戏</Button>
</div>
</CardHeader>
<CardContent>
<Grid className="grid-cols-4 gap-2">
{board.map((row, rowIndex) => <div key={rowIndex} className="flex flex-row">
{row.map((cell, colIndex) => <GridItem key={`${rowIndex}-${colIndex}`} className={`border p-2 w-16 h-16 ${getColorByValue(cell)}`}>
{cell}
</GridItem>)}
</div>)}
</Grid>
<div className="mt-4 flex space-x-4">
<Button onClick={() => handleMove('up')}>上</Button>
<Button onClick={() => handleMove('left')}>左</Button>
<Button onClick={() => handleMove('right')}>右</Button>
<Button onClick={() => handleMove('down')}>下</Button>
</div>
</CardContent>
</Card>;
}
运行查看效果
四、最后
谈一下总体使用下来的感受
AI
结合云开发低代码的使用体验,接触下来感觉非常棒
对于我这种后端来说,前端的设计开发,往往占据了大部分时间,更别提样式的调优了
而结合AI
生成,给予提示词就能生成一个前端页面,然后结合微搭低代码平台,很快就能完成一个页面,非常地便捷;
再来说说,生成代码这一块,就体验生成2048
小游戏来说,关键部分的逻辑我是反反复复一直给提示词生成
- 上下文之间的关联不是很强(导致了关键逻辑生成受限,不能很好的运行)
- 完整输出会超时(所以在上面给了不需要关注结构的提示词)
- 提示词必须要准确(最好是一个简单的提示词,生成一个简陋,但能运行的代码;后面根据提示词进行优化细节)
总结一下,体验下来还是非常不错的
我以前不会写react
代码,看了Copilot
生成的react
,结构清晰,也非常容易修改;
我很期待后面的优化,未来可期!!!