写点什么

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

作者:HullQin
  • 2022 年 8 月 23 日
    广东
  • 本文字数:3770 字

    阅读完需:约 12 分钟

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

我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

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>
复制代码


目前,效果如图:


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 噢~我有空了会分享做游戏的相关技术。

发布于: 刚刚阅读数: 5
用户头像

HullQin

关注

公众号【线下聚会游戏】 2020.10.07 加入

game.hullqin.cn 我做了一些联机桌游网页:支持2-10人联机的UNO、2-4人联机的斗地主、2人联机的五子棋。无需下载,点开即玩!叫上朋友,即刻开局!不看广告,不做任务,享受「纯粹」的游戏!

评论

发布
暂无评论
[教你做小游戏] 用177行代码写个体验超好的五子棋_CSS_HullQin_InfoQ写作社区