0
点赞
收藏
分享

微信扫一扫

[教你做小游戏] 用177行代码写个体验超好的五子棋

持续创作,加速成长!

我是公众号「线下聚会游戏」的作者,开发了一些​​联机桌游网页​​​(UNO、斗地主、五子棋等),总结了一些小游戏开发经验,汇总在专栏​​《教你做小游戏》​​,分享给大家,欢迎关注。

1. 需求描述

  • 支持本地双人对战的五子棋游戏。
  • 对于刚下的一步棋,要有标记。
  • 要有提示:五联珠后提示谁赢了。
  • 支持重新开局。
  • 适配多种分辨率的屏幕。

面对这样一个五子棋游戏的需求,你会怎么做呢?

2. 技术选型

参考掘金文章​​《H5小游戏技术选型分析,低代码?小游戏框架?canvas或SVG?还能用React?》​​,我们利用文章的决策树进行技术选型:

  1. 我们不需要借助现有的游戏模板。
  2. 我们不需要素材管理、不涉及物理引擎。
  3. 我们不需要动画、不需要每帧渲染。

结论:手撸五子棋。

此外,因为要适配不同的分辨率,所以我们采用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-width="0.5" 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" width="2" height="2"
</defs>
<path stroke="brown" stroke-width="0.5" 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>

目前,效果如图:

[教你做小游戏] 用177行代码写个体验超好的五子棋_小游戏

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('游戏结束,黑棋胜利!')​​的效果如下:

[教你做小游戏] 用177行代码写个体验超好的五子棋_技术选型_02

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. 码上掘金

[教你做小游戏] 用177行代码写个体验超好的五子棋_技术选型_03

9. 写在最后

如果你像我一样,追求极致用户体验,非常推荐你关注专栏:​​《极致用户体验》​​,我会分享更多文章,绝对是全网独一无二的原创精华内容 😎 专栏里面的内容,看完一定会有收获!求赞,求关注,谢谢啦~

另外,如果你想学做小游戏,欢迎关注我的专栏:​​《教你做小游戏》​​,我会写些文章,介绍制作游戏过程中的难点及解决方案。

举报

相关推荐

0 条评论