使用 Kontra.js 为 JS13K 构建一个小游戏

2025-06-08

使用 Kontra.js 为 JS13K 构建一个小游戏

你觉得你能在 30 天内用少于 13kB 的 JavaScript、CSS 和/或 HTML 代码开发一款游戏吗?我有个挑战给

由GitHub Star @end3r组织的2021 年 JS13K 竞赛刚刚拉开帷幕,主题为SPACE

你可以随心所欲地诠释这个主题——重现经典的《太空侵略者》或《小行星》风格的游戏,制作一款只能用控制SPACE杆控制的游戏,构建一款探索两个物体之间空间的游戏,或者任何你能想到的其他方式。但要注意空间——你只有 13kB 可用😉

如果你从未做过类似的事情,甚至从未写过太多 JavaScript 代码,这可能会有点吓人。这里有一个简短的教程,教你如何使用Kontra.js (一个专为 JS13K 开发的小型游戏库)以及几行代码来构建这个超棒的游戏:

玩游戏查看源代码,或按照以下步骤和相应的差异进行操作。

1. 生成 HTML 模板

如果您是 DEV 的常客读者,那么您可能不需要太多帮助,但让我们从一个超级简单的 HTML 模板开始:



<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <canvas width="250" height="250" id="game" style="background-color: black;"></canvas>
  </body>
</html>


Enter fullscreen mode Exit fullscreen mode

在您的浏览器中查看它,您应该会看到一个⬛ - 我们的游乐区。

💾步骤 1 结束时的源代码+差异

2. 包含 Kontra.js 库

为了简单起见,我们只需从 CDN 中提取最新版本的 Kontra,并在标签后包含我们知道将要使用的函数/帮助程序</canvas>



<script src="https://cdn.jsdelivr.net/npm/kontra@7.1.3/kontra.min.js"></script>
<script>
  let { GameLoop, Sprite, bindKeys, collides, init, initKeys, keyPressed, randInt } = kontra;

  let { canvas } = init();
</script>


Enter fullscreen mode Exit fullscreen mode

💾步骤 2 结束时的源代码+差异

3. 玩家一号准备好了!

首先,让我们为玩家 1 定义一个图像let { canvas } = init();。为了快速显示/提升自我,我们将使用我的 GitHub 头像:



let image1 = new Image();
image1.src = 'https://avatars.githubusercontent.com/u/121322?v=4'
image1.width = 40;
image1.height = 40;


Enter fullscreen mode Exit fullscreen mode

接下来,我们将创建精灵并将其放置在屏幕的左上角:



let sprite1 = Sprite({
  x: 40,
  y: 40,
  anchor: {
    x: 0.5,
    y: 0.5
  },
  image: image1
});


Enter fullscreen mode Exit fullscreen mode

现在我们将定义我们的游戏循环并开始运行!



let loop = GameLoop({
  render: function() {
    sprite1.render();
  }
});


Enter fullscreen mode Exit fullscreen mode

如果你现在在浏览器中查看游戏,应该会看到我的头像在一个大大的黑色方块里。哇哦——进步了!

💾步骤 3 结束时的源代码+差异

等等!那个图片 URL 是从哪里来的?我怎么用我自己的?你可以从 GitHub API 轻松获取头像 URL,例如



$ curl -s https://api.github.com/users/leereilly | jq -r '.avatar_url' 
https://avatars.githubusercontent.com/u/121322?v=4


Enter fullscreen mode Exit fullscreen mode

或者



$ curl -s https://api.github.com/users/leereilly | grep -i avatar_url
  "avatar_url": "https://avatars.githubusercontent.com/u/121322?v=4",


Enter fullscreen mode Exit fullscreen mode

不知道您怎么想,但每次我在终端中针对 GitHub API 运行命令curl时,我的感受是这样的:jq

好了,跑题了。看着黑色方块上的静态精灵可不是什么好玩的事,所以,我们动起来吧!

4. 让玩家 1 移动

让我们在游戏循环中引入一个update()函数,它可以响应 并适当移动我们的精灵:



update: function() {
  if (keyPressed('left')) {
    sprite1.x = sprite1.x - 1;
  }

  if (keyPressed('right')) {
    sprite1.x = sprite1.x + 1;
  }

  if (keyPressed('up')) {
    sprite1.y = sprite1.y - 1;
  }

  if (keyPressed('down')) {
    sprite1.y = sprite1.y + 1;
  }
},


Enter fullscreen mode Exit fullscreen mode

initKeys();我们还需要在之前添加一个调用loop.start();



initKeys();

loop.start();


Enter fullscreen mode Exit fullscreen mode

您现在应该能够在屏幕上移动玩家 1 了🕹️

💾步骤 4 结束时的源代码+差异

5. 介绍敌人

我们绝对可以让这个游戏更有趣。让我们把我们的敌方玩家——我的好朋友@mishmanners *——添加到随机位置,但不要超出屏幕范围。

* 这与 Michelle 在 Fornite、Magic The Gathering 和蛇形建造/战斗等游戏中击败我无关。

我们首先定义精灵的最大 X 和 Y 值(基本上是画布尺寸),然后利用 Kontra 的randInt()助手来设置精灵的位置:



let maxX = 250;
let maxY = 250;

let image2 = new Image();
image2.src = 'https://avatars.githubusercontent.com/u/36594527?v=4'
image2.width = 40;
image2.height = 40;

let sprite2 = Sprite({
  x: randInt(0, maxX),
  y: randInt(0, maxY),
  anchor: {
    x: 0.5,
    y: 0.5
  },
  image: image2
});


Enter fullscreen mode Exit fullscreen mode

💾步骤 5 结束时的源代码+差异

6.添加一些碰撞检测

这时你的大学水平的数学知识就会派上用场。

collides()开玩笑的。这听起来很吓人,但值得庆幸的是,Kontra 通过助手为我们完成了所有艰苦的工作。一旦发生碰撞,我们就通过在函数末尾添加以下内容将玩家 2 精灵移动到随机位置update()



if (collides(sprite1, sprite2)) {
  sprite2.x = randInt(41, maxX - 40);
  sprite2.y = randInt(41, maxY - 40);
}


Enter fullscreen mode Exit fullscreen mode

💾步骤 6 结束时的源代码+差异

7. 使用这个巧妙的技巧使其像素化/8 位!

提示让你的精灵看起来像素化的方法很简单。因为我们使用的是 GitHub Avatar URL,所以我们可以将查询参数从 更改为 ,v=4s=10请求 10x10 像素的版本。



- https://avatars.githubusercontent.com/u/121322?v=4
+ https://avatars.githubusercontent.com/u/121322?s=10


Enter fullscreen mode Exit fullscreen mode

由于我们在代码中将图像设置为 4 倍,因此浏览器将尝试调整其大小,使其看起来像素化。

之前和之后

魔法

注意:肯定有更复杂的技术,而且使用这么大的图像对 JS13K 来说是个糟糕的主意。最好使用AsepritePiskel之类的工具来创作你自己的像素艺术。

💾第 7 步结束时的源代码+差异

8.添加一些音效

JS13K 里没有太多空间容纳 OGG 和 MP3。还好,比我聪明的人开发了一些简洁的库和编辑器,只需几行代码就能创建音效和背景音乐。

@xemMiniSoundEditor为例,我可以从一些预定义的声音中进行选择,然后复制并粘贴 JavaScript。

我将这样做并将其复制并粘贴到块的末尾if (collides(sprite1, sprite2))



f = function(i){
  var n=2e4;
  if (i > n) return null;
  var q = t(i,n);
  i=i*0.7;
  return (Math.pow(i*50,0.8)&66)?q:-q;
}

t=(i,n)=>(n-i)/n;
A=new AudioContext()
m=A.createBuffer(1,96e3,48e3)
b=m.getChannelData(0)
for(i=96e3;i--;)b[i]=f(i)
s=A.createBufferSource()
s.buffer=m
s.connect(A.destination)
s.start()


Enter fullscreen mode Exit fullscreen mode

我完全不知道这段代码是干什么的,但复制粘贴之后感觉自己更聪明了。你也会的。试着把它(或者你自己的声音)复制粘贴到碰撞检测代码的末尾。

⚠️ 显然,如果你不知道代码的作用,就不要盲目地从网上复制粘贴和使用。值得庆幸的是,这样做是无害的。

现在,你的代码应该看起来有点像这样:



<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <canvas width="250" height="250" id="game" style="background-color: black;"></canvas>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/kontra@7.1.3/kontra.min.js"></script>
  <script>
    let { GameLoop, Sprite, bindKeys, collides, init, initKeys, keyPressed, randInt } = kontra;

    let { canvas } = init();

    let maxX = 250;
    let maxY = 250;

    let image1 = new Image();
    image1.src = 'https://avatars.githubusercontent.com/u/121322?s=10'
    image1.width = 40;
    image1.height = 40;

    let sprite1 = Sprite({
      x: 40,
      y: 40,
      anchor: {
        x: 0.5,
        y: 0.5
      },
      image: image1
    });

    let image2 = new Image();
    image2.src = 'https://avatars.githubusercontent.com/u/36594527?s=10'
    image2.width = 40;
    image2.height = 40;

    let sprite2 = Sprite({
      x: randInt(0, maxX),
      y: randInt(0, maxY),
      anchor: {
        x: 0.5,
        y: 0.5
      },
      image: image2
    });

    let loop = GameLoop({
      update: function() {
        if (keyPressed('left')) {
          sprite1.x = sprite1.x - 1;
        }

        if (keyPressed('right')) {
          sprite1.x = sprite1.x + 1;
        }

        if (keyPressed('up')) {
          sprite1.y = sprite1.y - 1;
        }

        if (keyPressed('down')) {
          sprite1.y = sprite1.y + 1;
        }

        if (collides(sprite1, sprite2)) {
          sprite2.x = randInt(41, maxX - 40);
          sprite2.y = randInt(41, maxY - 40);

          f = function(i) {
            var n = 1e4;
            var c = n / 3;
            if (i > n) return null;
            var q = Math.pow(t(i, n), 2.1);
            return (Math.pow(i, 3) & (i < c ? 16 : 99)) ? q : -q;
          }

          t = (i, n) => (n - i) / n;
          A = new AudioContext()
          m = A.createBuffer(1, 96e3, 48e3)
          b = m.getChannelData(0)
          for (i = 96e3; i--;) b[i] = f(i)
          s = A.createBufferSource()
          s.buffer = m
          s.connect(A.destination)
          s.start()
        }
      },
      render: function() {
        sprite1.render();
        sprite2.render();
      }
    });

    initKeys();

    loop.start();
  </script>
</html>


Enter fullscreen mode Exit fullscreen mode

它在您的浏览器中看起来应该有点像这样:

此 GIF 中的声音似乎不起作用,但每次精灵接触时你都应该听到哔声。

就是这样。这款游戏将提供 小时尽享欢乐时光。敬请关注 Steam 的完整版本。

💾步骤 8 结束时的源代码+差异

更进一步

如果你查看文件大小,你会发现它比 13kB 稍大一点:



$ ls -lth
total 88
-rw-r--r--@ 1 leereilly  staff    28K Aug 13 09:50 kontra.min.js
-rw-r--r--@ 1 leereilly  staff   674B Aug 13 09:49 mishmanners.jpeg
-rw-r--r--@ 1 leereilly  staff   679B Aug 13 09:48 leereilly.jpeg
-rw-r--r--@ 1 leereilly  staff   2.2K Aug 13 08:07 index.html


Enter fullscreen mode Exit fullscreen mode

我们使用的是 Kontra 的精简版,但其中仍然包含一些我们不需要的内容。有关进一步缩减文件大小的详细信息,请参阅 Kontra 网站。

加入 JS13K!!!

欢迎随意 fork 并扩展此内容,用于您自己的 JS13K 条目。还有很多地方可以改进……

  • 使其成为双人游戏(玩家 2 可以回应W A S D)?
  • 添加对高分的支持?
  • 再介绍一些音效吗?
  • 添加一些实际的游戏玩法 LOL

更好的是,从零开始,享受乐趣。以下是一些可能有用的资源:

祝你好运,玩得开心!期待看到你在下方评论区分享你的作品。<3

故障排除

你在学习本教程的过程中遇到过一些 bug 吗?如果你以前从未使用过Chrome 开发者控制台,那么它就是你的好帮手。

+ Option+ J(macOS) 或Control+ Shift+ J(Windows、Linux、Chrome OS) 直接进入控制台面板。从那里你可以看到哪些功能运行不正常……

如果您感觉自己像 L337 H4X0R 正在运行curljq发出命令,您会感觉自己现在就在矩阵中,可以在其中做各种事情。

您还可以查看此repo 的完整源代码。查看提交历史记录,您将看到上述每个步骤的差异/代码。

鏂囩珷鏉ユ簮锛�https://dev.to/github/build-a-tiny-game-for-js13k-with-kontra-js-8pb
PREV
无需了解如何编码即可为开源做出贡献
NEXT
备份代码的 3 种方法(即使你不了解 Git)