认识Mars Code
俗话说得好:“磨刀不误砍柴工”。一款优秀的工具能够显著提升我们的开发效率和代码质量。在编程的世界里,MarsCode就是这样一款值得推荐的智能开发工具。 MarsCode是由字节跳动公司推出的基于“豆包大模型”打造的智能开发工具。它主要包括两个核心产品:编程助手和云端集成开发环境(IDE)。这款工具旨在通过AI技术,帮助开发者减少重复劳动,提高代码质量和可维护性。
MarsCode编程助手提供了一系列强大的功能,如智能补全、智能预测、智能问答等。这些功能能够在编码过程中提供单行或整个函数的建议,同时支持在用户编码过程中提供代码解释、单测生成、问题修复、技术问答等辅助功能。目前,MarsCode编程助手支持超过100种主流编程语言,包括Python、Go、JavaScript、TypeScript、C++、Java、Kotlin、C、Rust等,并且兼容VSCode和JetBrains等主流代码编辑器。
在VSCode中使用MarsCode编程助手非常简单。用户只需进入VSCode插件商店,安装并登录MarsCode插件,即可开始使用。MarsCode编程助手支持在编码过程中提供单行或多行的代码推荐,并且支持通过注释生成代码片段,从而大幅提升代码编写速度。此外,MarsCode编程助手还能够精确解释项目代码,帮助用户快速上手开发。它还可以为整个函数或每行代码生成注释,提升代码可读性,方便协同开发。
认识canvas
canvas翻译为中文为“画布”的意思,在HTML5中为一个标签,它是一个容器,可以使用js在里面绘制图形和文字内容。
一个基本的 <canvas> 元素看起来像这样:
<canvas id="myCanvas" width="500" height="500"></canvas>
id属性用于在脚本中引用这个特定的 canvas。width和height定义了 canvas 的大小。默认情况下,宽度为 300px,高度为 150px,但可以通过属性或 CSS 来改变。
要开始在 canvas 上绘图,首先需要获取到 canvas 元素,并创建一个渲染上下文。最常用的是 2D 渲染上下文,例如我们将要实现的五子棋游戏就是建立在二维的基础上。
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
一旦有了上下文对象 ctx,就可以调用各种方法来绘制图形了。可以绘制各种图形,例如最基础的圆、矩形、直线等等,例如绘制一个基础的矩形。
// 设置填充颜色
ctx.fillStyle = 'blue';
// 开始路径
ctx.beginPath();
// 移动到点 (50, 50)
ctx.moveTo(50, 50);
// 绘制线条至点 (150, 150),形成矩形
ctx.lineTo(150, 50);
ctx.lineTo(150, 150);
ctx.lineTo(50, 150);
ctx.closePath(); // 关闭路径
// 填充路径
ctx.fill();
静Yu 正在“豆包MarsCode AI 红人创造营”中借助 AI 工具创作精彩内容,请帮TA投上一票吧! www.marscode.cn/events/2024…
每日投票还能参与抽奖哦,快来领取你的专属福利。
五子棋
正式开始之前,先捋一下思路如何才能更好地实现五子棋小游戏。如果犹豫不决,不知从何下手,千万不要慌,我们有【豆包MarsCode】。先让他帮我们梳理一下思路:
哈哈,这下舒服多了,思路清晰之后,我们就可以进入正题了。参考MarsCode的思路以及结合我们自己想要实现的效果,我也将完整的开发流程整理在下面。
明确需求
在开发一款五子棋小游戏时,首要任务是清晰界定需求及预期实现的效果。具体而言,该游戏应达到以下标准:
- 游戏界面:设计一个简洁直观的30x30棋盘。
- 游戏规则:遵循经典五子棋规则,即玩家轮流在棋盘上放置棋子,率先在横向、纵向或斜向上连成五个连续棋子的一方即为胜者。
- 交互方式:通过鼠标点击棋盘上的空位来放置棋子,实现用户与游戏的直接互动。
- 状态显示:实时展示当前轮到的玩家以及游戏胜利信息等关键状态,确保玩家能随时掌握游戏进展。
设计界面
- 棋盘背景:利用
<canvas>元素来创建棋盘背景,提供绘图的基础。 - 网格线绘制:在
<canvas>上精心绘制网格线,以清晰呈现棋盘格。 - 当前玩家信息显示:设计专门的区域来实时展示当前轮到的玩家信息。
- 胜利信息展示区:预备好显示胜利信息的区域,以便在游戏结束时准确传达胜利结果。
初始化棋盘
- 棋盘规格设定:明确棋盘的大小,如标准的30x30格。
- 棋子样式配置:设定棋子的半径大小及颜色,确保棋子在棋盘上清晰可见。
- 棋盘背景与网格绘制:在画布上绘制棋盘背景,并仔细勾勒网格线,形成完整的棋盘布局。
用户输入处理
- 鼠标点击监听:为棋盘区域添加鼠标点击事件监听器。
- 坐标转换:根据鼠标点击的位置,精确计算出对应的棋盘坐标。
- 占位检查:验证该坐标位置是否已被棋子占据。
- 棋子绘制与状态更新:若位置空闲,则在该位置绘制新棋子,并同步更新游戏内部状态。
玩家切换机制
- 玩家轮替:每次成功放置棋子后,自动切换至下一位玩家。
- 当前玩家追踪:通过修改全局变量来追踪并记录当前落子的玩家信息。
胜负判定逻辑
- 五子连线检查:每次落子后,全面检查棋盘,判断是否有任意方向上形成了连续的五个棋子。
- 胜利信息显示:若发现五子连线,立即显示胜利消息,并终止游戏进程。
- 平局判定:若棋盘被完全填满且未出现胜利者,则判定为平局,并相应显示平局信息。
开发步骤
1.定义canvas画布,并创建一个渲染上下文ctx.
<canvas id="chessBoard" width="800" height="800"></canvas>
//渲染上下文ctx
let canvas = document.getElementById('chessBoard');
let ctx = canvas.getContext('2d');
2.绘制棋盘:定义一个drawBoard函数,首次进入页面,或者玩家获胜之后都要调一次这个方法,重新绘制棋盘。
每次绘制棋盘之前都要清除掉之前的棋盘,因为玩家获胜之后需要将之前的落子清空,重新开始。
ctx.clearRect(0, 0, 800, 800);
ctx.fillStyle = '#F7F7F7'; // 设置棋盘背景颜色
ctx.fillRect(0, 0, 800, 800); // 填充背景
定义boardSize变量初始化值为30,表示绘制的棋盘是30 * 30大小,横竖都有30个小格子。定义cellSize变量初始化为800/boardSize,表示每个格子的宽高是多少,因为canvas画布我这里设定的是800 * 800,可以根据自己的需求进行更改。这样,我们就能够确保整个棋盘能够恰好适应在800x800的画布上。
哎呀,突然忘记了如何绘制一条线段,不慌我们有MarsCode。不仅完整的生成实现代码,而且贴心的附着中文解释。
for (let i = 0; i < thisboardSize; i++) {
ctx.beginPath();
ctx.moveTo(i * this.cellSize + this.cellSize / 2, this.cellSize / 2);
ctx.lineTo(i * this.cellSize + this.cellSize / 2, this.boardSize * this.cellSize - this.cellSize / 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(this.cellSize / 2, i * this.cellSize + this.cellSize / 2);
ctx.lineTo(this.boardSize * this.cellSize - this.cellSize / 2, i * this.cellSize + this.cellSize / 2);
ctx.stroke();
}
| 方法 | 描述 | 示例 |
|---|---|---|
| moveTo(x, y) | 将路径移动到指定的坐标点 | ctx.moveTo(100, 100) |
| lineTo(x, y) | 添加一条从当前位置到指定坐标点的直线 | ctx.lineTo(200, 200) |
循环boardSize次代表着一共有多少条线,是从最上方和最左边开始绘制,从(0,0)点开始绘制到(边长,0)一条直线,因为起始点y坐标没有改变所以绘制的都是竖线;那么相反的X坐标不变绘制的就是横线。不过从上面代码可以看出,我们不是从(0,0)点开始的,目的是不让棋盘完全占满整个canvas画布,留有一定的边距。
上图为循环第一次绘制的线段
3.实现落子 棋盘绘制完成之后就开始实现“下棋”的功能了。其实实现原理就是给canvas画布绑定一个点击函数,每点击一次就绘制一个棋子,但需要注意的是要判断当前位置是否已经有棋子,而且要确保棋子的位置正好能落在棋盘线段的交汇处,不能是任意位置都能落子。
实现落子的功能的时候,如果我们单纯的从canvas画图角度考虑可能就走了弯路了。我们应该更好的转化数据的处理。一个完整的棋盘共30行,每一行包含了30个交汇处,我们就可以将这个棋盘转换成一个二维数组,数组中的每一个元素就相当于一个位置,只需要判断落子的那个元素是否有值就可以了。
this.board = Array.from({ length: this.boardSize }, () => Array(this.boardSize).fill(null));
如果猛地看不懂这段代码,问一下MarsCode,详细的解释了这段代码的含义。其实就是根据刚才的思路分析,初始化了一个二维数组,每个元素的初始值为null。
const rect = event.target.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const row = Math.floor(y / this.cellSize);
const col = Math.floor(x / this.cellSize);
获取了触发事件的目标元素(canvas 元素)的边界矩形,getBoundingClientRect() 方法返回一个 DOMRect 对象,包含了元素的位置和大小信息。
const x = event.clientX - rect.left;:这行代码计算了鼠标点击位置相对于 canvas 元素左边缘的水平距离。
const y = event.clientY - rect.top;:这行代码计算了鼠标点击位置相对于 canvas 元素上边缘的垂直距离。
Math.floor(x / this.cellSize) 方法计算鼠标位置在第几个完整的线段交汇处,确保棋子位置落在交汇中心。
currentPlayer: 'black', // 当前玩家
placeStone(row, col, ctx) {
this.board[row][col] = this.currentPlayer;
ctx.beginPath();
ctx.arc(col * this.cellSize + this.cellSize / 2, row * this.cellSize + this.cellSize / 2, this.cellSize / 3, 0, 2 * Math.PI);
ctx.fillStyle = this.currentPlayer;
ctx.fill();
},
如果落子成功就将二维数组对应位置的数值改变,currentPlayer代表当前玩家黑色方或白色方。棋子就是绘制一个圆圈,填充黑色或白色即可。 落子之前我们还要进行一些必要的判断,判断当前位置是否有棋子,判断是否有玩家已经获胜,不满足上述的两个条件才能调用这个绘制棋子的方法。
if (this.board[row][col] === null) {
this.placeStone(row, col, ctx);
if (this.checkWin(row, col)) {
alert(`${this.currentPlayer} wins!`);
this.initBoard();
this.drawBoard(ctx);
} else {
this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
}
}
checkWin方法就是用来判断是否有玩家获胜。
判断是否胜利的标准是在水平、垂直、两条对角线方向是否有连续的并且为同色的五颗棋子。
- 水平方向:判断落子的位置坐标左侧四颗棋子或右侧四颗棋子是否为同色,实现就是x坐标一直++四次或一直--四次,y坐标不变
- 竖直方向:判断落子的位置坐标上方四颗棋子或下方四颗棋子是否为同色,实现就是y坐标一直++四次或一直--四次,x坐标不变
- 对角线\: 判断落子的位置坐标左上方四颗棋子或右下方四颗棋子是否为同色,实现就是x坐标一直++四次,y坐标一直++四次,x坐标一直--四次,y坐标一直--四次
- 对角线/: 判断落子的位置坐标左下方四颗棋子或右上方四颗棋子是否为同色,实现就是x坐标一直++四次,y坐标一直--四次,x坐标一直--四次,y坐标一直++四次
checkWin(row, col) {
const directions = [
{ dx: 1, dy: 0 }, // 水平
{ dx: 0, dy: 1 }, // 垂直
{ dx: 1, dy: 1 }, // 对角线 \
{ dx: 1, dy: -1 } // 对角线 /
];
for (const { dx, dy } of directions) {
let count = 1;
for (let i = 1; i < 5; i++) {
const r = row + dy * i;
const c = col + dx * i;
if (r >= 0 && r < this.boardSize && c >= 0 && c < this.boardSize && this.board[r][c] === this.currentPlayer) {
count++;
} else {
break;
}
}
for (let i = 1; i < 5; i++) {
const r = row - dy * i;
const c = col - dx * i;
if (r >= 0 && r < this.boardSize && c >= 0 && c < this.boardSize && this.board[r][c] === this.currentPlayer) {
count++;
} else {
break;
}
}
if (count >= 5) {
return true;
}
}
return false;
}
count初始值为1,每遇到一个同色的便++,如果最终count大于等于5就证明有五颗连续的棋子,返回true即为胜利。
如果胜利之后需要重置一下棋盘,清除掉之前绘制的棋子。
- 最后加一些提示性的东西,一个简易的五子棋小游戏就制作完成了。
<template>
<div class="game">
<h1>Welcome to Go Game!</h1>
<div><span class="playerTips">{{currentPlayer==='black'? '请黑方玩家落子' : '请白方玩家落子'}}</span></div>
<canvas id="chessBoard" width="800" height="800"></canvas>
</div>
</template>
结语
在本文中,我们详细探讨了如何使用Canvas元素来创建一个简单而富有挑战性的五子棋小游戏。从基础的Canvas绘图操作,到五子棋的逻辑实现,再到用户交互的设计,我们一步步构建了这个小游戏。希望本文能够帮助读者更好地理解Canvas在小游戏开发中的应用,同时也能够激发大家对游戏编程的兴趣和热情。
在整个小游戏的开发过程中,MarsCode AI也是为我提供了极大地便利。MarsCode AI提供了智能补全功能,在编写代码时,它能够根据上下文自动补全代码片段,减少了重复劳动和错误输入的可能性。在五子棋游戏的开发中,这一功能使得开发者能够更专注于游戏逻辑的实现,而无需花费过多时间在代码编写上。除此之外,代码生成、优化到补全、预测,它都能够胜任。这使得开发者在编程时能够更加得心应手,提高开发效率。
静Yu 正在“豆包MarsCode AI 红人创造营”中借助 AI 工具创作精彩内容,请帮TA投上一票吧! www.marscode.cn/events/2024…
每日投票还能参与抽奖哦,快来领取你的专属福利。