[教你做小游戏] 用177行代码写个体验超好的五子棋
Posted HullQin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[教你做小游戏] 用177行代码写个体验超好的五子棋相关的知识,希望对你有一定的参考价值。
1. 需求描述
- 支持本地双人对战的五子棋游戏。
- 对于刚下的一步棋,要有标记。
- 要有提示:五联珠后提示谁赢了。
- 支持重新开局。
- 适配多种分辨率的屏幕。
面对这样一个五子棋游戏的需求,你会怎么做呢?
2. 技术选型
参考掘金文章《H5小游戏技术选型分析,低代码?小游戏框架?canvas或SVG?还能用React?》,我们利用文章的决策树进行技术选型:
- 我们不需要借助现有的游戏模板。
- 我们不需要素材管理、不涉及物理引擎。
- 我们不需要动画、不需要每帧渲染。
结论:手撸五子棋。
此外,因为要适配不同的分辨率,所以我们采用SVG绘制棋盘和棋子,不用canvas。
3. 绘制棋盘
背景
棋盘背景选个木头的棕色。
body
height: 100vh;
margin: 0;
background: bisque;
15*15的线条
我们先通过path
绘制15条横线、15条竖线,每个格子设置为10的宽度,那么就是从-70绘制到70。
<svg id="svg" viewBox="-76,-76,152,152" xmlns="http://www.w3.org/2000/svg">
<path stroke="brown" stroke- fill="none"
d="m-70,-70h140v140h-140zm10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140" />
</svg>
此外,我们还需要适配多种分辨率,参考《2行代码,让你的UI适配移动端、PC端,快来收藏》,只需2行代码!
5个标记点
五子棋棋盘通常有5个标记点,我们再通过rect
画5个黑色的矩形。因为可以复用,所以我们采用defs
定义,这样以后可以通过use
来复用。使用defs
就好比我们封装了个可复用的标记组件。
<svg id="svg" viewBox="-76,-76,152,152" xmlns="http://www.w3.org/2000/svg">
<defs>
<rect id="mark" x="-1" y="-1" />
</defs>
<path stroke="brown" stroke- fill="none"
d="m-70,-70h140v140h-140zm10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,0v-140m10,0v140m10,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140m0,-10h140m0,-10h-140" />
<use xlink:href="#mark" />
<use xlink:href="#mark" x="40" y="40" />
<use xlink:href="#mark" x="40" y="-40" />
<use xlink:href="#mark" x="-40" y="40" />
<use xlink:href="#mark" x="-40" y="-40" />
</svg>
目前,效果如图:
4. 提示功能
为了实现提示,我们参考antd的Message
,可以写出这个功能:
<div id="message"></div>
#message
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0,0,0,.85);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5715;
list-style: none;
-webkit-font-feature-settings: "tnum";
font-feature-settings: "tnum";
position: fixed;
top: 8px;
left: 0;
z-index: 1010;
width: 100%;
pointer-events: none;
text-align: center;
#message > div
padding: 6px;
#message > div > div
display: inline-block;
padding: 12px 18px;
background: #fff;
border-radius: 2px;
-webkit-box-shadow: 0 3px 6px -4px rgba(0,0,0,.12), 0 6px 16px 0 rgba(0,0,0,.08), 0 9px 28px 8px rgba(0,0,0,.05);
box-shadow: 0 3px 6px -4px rgba(0,0,0,.12), 0 6px 16px 0 rgba(0,0,0,.08), 0 9px 28px 8px rgba(0,0,0,.05);
pointer-events: all;
const messageDiv = document.getElementById(message);
const Message = (content) =>
const div = document.createElement(div);
div.innerhtml = `<div>$content</div>`;
messageDiv.appendChild(div);
setTimeout(() => messageDiv.removeChild(div), 3000);
;
调用Message(游戏结束,黑棋胜利!)
的效果如下:
5. 绘制棋子
封装棋子组件
棋子也是组件,这里给出defs
定义。
定义了2种渐变色,分别用于填充棋子。棋子就是简单的circle
圆形,半径4.2,直径就是8.4。
<defs>
<radialGradient id="black">
<stop offset="0%" style="stop-color:#808080" />
<stop offset="100%" style="stop-color:#111" />
</radialGradient>
<radialGradient id="white">
<stop offset="0%" style="stop-color:#fff" />
<stop offset="100%" style="stop-color:#ddd" />
</radialGradient>
<circle id="piece" r="4.2" />
</defs>
绘制新下棋子的标记
const mark = document.createElementNS("http://www.w3.org/2000/svg", rect);
mark.setAttribute(fill, red);
mark.setAttribute(width, 2);
mark.setAttribute(height, 2);
mark.setAttribute(opacity, 0);
svg.appendChild(mark);
渲染所有棋子
未下的棋子,设置为透明。一次性把15*15个棋子都提前渲染好。
顺便添加了hover事件:
- 鼠标进入时,如果位置当前可以下棋,变成半透明。
- 鼠标离开时,如果位置当前可以下棋,变成全透明。
顺便添加了点击事件:
- 如果位置当前可以下棋,那么就把这个棋子变成黑色或白色。
- 下棋后,如果游戏没结束,当前就该另一方下棋。否则,提示游戏结束。
这里需要给出game
定义:
const game =
black: true, // 该黑下棋了吗?
winner: null, // 游戏有胜利者了吗?是black或white或null
;
const svg = document.getElementById(svg);
const pieces = [];
for (let x = 0; x < 15; x++)
pieces.push([]);
for (let y = 0; y < 15; y++)
const piece = document.createElementNS(http://www.w3.org/2000/svg, use);
pieces[x].push(piece);
piece.setAttribute(x, (x * 10 - 70).toString());
piece.setAttribute(y, (y * 10 - 70).toString());
piece.setAttribute(fill-opacity, 0);
piece.setAttributeNS(http://www.w3.org/1999/xlink, xlink:href, #piece);
piece.addEventListener(mouseenter, () =>
if (game.winner || piece.getAttribute(fill-opacity) === 1) return;
piece.setAttribute(fill, game.black ? url(#black) : url(#white));
piece.setAttribute(fill-opacity, 0.5);
);
piece.addEventListener(mouseleave, () =>
if (game.winner || piece.getAttribute(fill-opacity) === 1) return;
piece.setAttribute(fill-opacity, 0);
);
piece.addEventListener(click, () =>
if (game.winner || piece.getAttribute(fill-opacity) === 1) return;
piece.setAttribute(fill, game.black ? url(#black) : url(#white));
piece.setAttribute(fill-opacity, 1);
dropPiece(x, y);
game.black = !game.black;
);
svg.appendChild(piece);
下棋函数
设置了最新下棋点的标记;并把对应位置的棋子设置为不透明。
const dropPiece = (x, y) =>
mark.setAttribute(x, (x * 10 - 71).toString());
mark.setAttribute(y, (y * 10 - 71).toString());
mark.setAttribute(opacity, 0.7);
game.winner = checkWinner(x, y);
if (game.winner)
Message(`游戏结束,$game.winner === black ? 黑 : 白棋胜利!`);
;
6. 判断是否胜利
只需要判断最新下的棋子,是否有五联珠,就知道是否胜利了。
参考文章《《五子棋》怎么判断输赢?你能5分钟交出代码吗?》,这里不罗列代码啦。
7. 重新开局功能
弄一个按钮,点它后重置游戏数据即可:
- 所有棋子变透明。
- 隐藏最新棋子的标记。
- 重置
game
变量。
<button id="restart" onclick="initializeGame()">重新开局</button>
const game =
black: true,
winner: null,
;
window.initializeGame = () =>
game.black = true;
game.winner = null;
mark.setAttribute(opacity, 0);
for (let x = 0; x < 15; x++)
for (let y = 0; y < 15; y++)
pieces[x][y].setAttribute(fill-opacity, 0);
;
window.initializeGame();
8. 码上掘金
9. 写在最后
我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。
以上是关于[教你做小游戏] 用177行代码写个体验超好的五子棋的主要内容,如果未能解决你的问题,请参考以下文章
[教你做小游戏] 《五子棋》怎么判断输赢?你能5分钟交出代码吗?