本文以贪吃蛇为例, 一步一步地分析如何实现一个命令行游戏.

实现原理

命令行输入

  • 通过 process.stdin 监听命令行输入的按键, 改变小蛇的前进的方向

画面渲染

  • 通过 ANSI 转义序列 擦除之前的输出
  • 通过 process.stdout 每隔一段时间将画面帧输出到命令行

源码解析

监听按键事件

使用过 yarn upgrade-interactive 命令更新 npm 依赖, 或者使用过 vue-cli 等脚手架创建过新项目的同学应该都见过: 这些工具会在命令行输出很多选项, 通过上下按键可以移动焦点, 通过空格键可以选择

那么这些操作是如何实现的呢? 下面通过 readline 和 process.stdin 来实现命令行监听按键事件:

process.stdin 是一个可读流, 通过 readline.emitKeypressEvents 可以给可读流注册 keypress 事件, 通过 keypress 事件就能获取到按键的值

readline.emitKeypressEvents(process.stdin) // 注册 keypress 事件

process.stdin.setRawMode(true) // 开启原始模式, 使输入的每个字符带上各种详细属性

process.stdin.on('keypress', (...args) => {
 console.log(args)
 // 按下方向键会输出
 // [
 //  undefined,
 //  {
 //   sequence: '\u001b[A',
 //   name: 'up',
 //   ctrl: false,
 //   meta: false,
 //   shift: false,
 //   code: '[A'
 //  }
 // ]
})

注意: setRawMode 会使命令行按下 ctrl c 不再发送终止信号, 可能需要自行处理退出逻辑

绘制帧画面

输出到命令行的游戏画面默认为 30 行 x 50 列, 将其划分为一个二维数组, 每隔一段时间将二维数组的值打印出来并擦除之前打印的值, 即完成一次帧画面的渲染

process.stdout 是一个可写流, 调用 process.stdout.write 可以向命令行写入数据, nodejs 中 console.log 其实就是将数据写入到 process.stdout 并换行

通过向命令行写入开头为 ANSI 转义序列 的字符串可以 光标移动/滚动屏幕/擦除显示/颜色文本 等等功能, 想要深入了解可以自行搜索关键字学习, 本文使用 ansi-escapes npm 包实现擦除功能

const ansiEscapes = require('ansi-escapes')

function clear(lines) {
 process.stdout.write(ansiEscapes.eraseLines(lines)) // 可以擦除指定行数的输出
}

根据游戏画面的宽高定义一个二维数组, 小蛇的头和身体视为画面中的点, 值为非空值, 空白画面则为空字符串

let dots = []
for (let col = 0; col < wall.height; col  ) {
 dots[col] = new Array(wall.width).fill(' ')
}

在每一帧中, 小蛇的头会向前进的方向前进一个, 头接着的第一节身体则会移动到上一帧头所在的位置, 以此类推每一节身体都会移动到前一节身体的位置上, 所以需要定义一个数据记录之前的头和身体的位置

const SNAKE_HEAD = '@' // 头的符号
const SNAKE_BODY = '○' // 身体的符号

function drawFrame() {
 let dots = []
 for (let col = 0; col < wall.height; col  ) {
  dots[col] = new Array(wall.width).fill(' ')
 }

 let nextBody = []
 let head = next(snake.body[0]) // next 方法传入当前点的 x, y 坐标, 返回向前进方向前进一个的 x, y 坐标
 nextBody.push(head)
 dots[head.y][head.x] = SNAKE_HEAD
 for (let i = 1; i < snake.length; i  ) {
  let body = snake.body[i - 1]
  dots[body.y][body.x] = SNAKE_BODY
  nextBody.push(body)
 }
 
 screen.draw(dots) // 将二维数组中的点输出到命令行中
 
 // 更新蛇的状态
 snake.body = nextBody
 snake.head = snake.body[0]
}

蛇吃鸟蛋逻辑

小蛇每吃到一个鸟蛋, 身体会长一节, 并在画面中随机生成另一个鸟蛋. 到了这一步其实就很简单了, 随机生成一个点作为鸟蛋的位置, 插入到之前的二维数组中.

function layAEgg() {
 let x = ~~(wall.width * Math.random())
 let y = ~~(wall.height * Math.random())
 return { x, y }
}

当小蛇的头的位置与鸟蛋的位置相同时, 则视为蛇吃到鸟蛋, 蛇的长度加一, 并在尾部增加一节上一帧蛇尾的节点位置

const SNAKE_HEAD = '@'
const SNAKE_BODY = '○'
const BIRD_EGG = '●'

function drawFrame() {
 let dots = []
 for (let col = 0; col < wall.height; col  ) {
  dots[col] = new Array(wall.width).fill(' ')
 }

 let nextBody = []
 let head = next(snake.body[0])
 nextBody.push(head)
 dots[head.y][head.x] = SNAKE_HEAD
 for (let i = 1; i < snake.length; i  ) {
  let body = snake.body[i - 1]
  dots[body.y][body.x] = SNAKE_BODY
  nextBody.push(body)
 }

 // 判断蛇头位置在上一帧中是否为鸟蛋位置, 为真视为蛇吃到鸟蛋
 if (prevDots && prevDots[head.y][head.x] === BIRD_EGG) {
  let body = snake.body[snake.length - 1]
  dots[body.y][body.x] = SNAKE_BODY
  nextBody.push(body)
  snake.length  = 1
  egg = null
  prevDots = null
 }

 if (!egg) {
  egg = layAEgg()
  while (dots[egg.y][egg.x] !== ' ') {
   egg = layAEgg()
  }
 }
 dots[egg.y][egg.x] = BIRD_EGG

 prevDots = dots // 保存上一帧的数据, 用于下次绘制时判断逻辑

 screen.draw(dots)
 snake.body = nextBody
 snake.head = snake.body[0]
}

总结

至此, 命令行贪吃蛇游戏基本逻辑都已实现, 剩下的就是使用定时器每隔一段时间绘制一次帧画面. 其实几乎任何像素游戏(如俄罗斯方块/吃豆人等)都可以按照这个流程实现, 不同的只是帧画面的处理逻辑而已. 如果感兴趣的话, 可以去我的 github 查看该 贪吃蛇游戏源码

到此这篇关于如何利用nodejs实现命令行游戏的文章就介绍到这了,更多相关nodejs实现命令行游戏内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

如何利用nodejs实现命令行游戏的更多相关文章

  1. xcode – 如何通过LLDB命令行添加断点操作?

    如果你从Xcode编辑一个断点,有一个超级有用的选项,可以添加一个“Action”,以便在每次遇到断点时自动执行.如何从LLDB命令行添加此类操作?

  2. 在Sierra上,Brew安装错误单独使用Xcode是不够的

    我正在尝试使用HomeBrewv1.3.8在运行xCodev9.1的MacOSXSerrav10.12.6上安装软件包.安装和错误是然后我运行命令表示软件更新服务器无法使用命令行工具包.我进入xCode,它表明安装了命令行工具.任何帮助将非常感激.西奥解决方法我去了AppleDeveloper网站并直接下载了命令行工具dmg.首先需要设置Apple帐户.命令行工具可在以下位置找到–https://

  3. ios – 通过真实设备上的命令行进行UIAutomation

    提前谢谢解决方法您需要为其提供.ipa文件的路径.它实际上只需要应用程序名称,因此您只需传入应用程序名称即可.非常重要:应用程序需要安装在设备上,Instruments不会在设备上安装应用程序,它只会在设备上运行已安装的应用程序

  4. ios7 – Xcode 5命令行代码签名错误

    有没有人遇到过Xcode5(Build版本5A11344j)命令行工具无法在Xcode4.6.3(Build版本4H1503)中构建以前成功构建的项目的情况.错误消息如下:“检查依赖项代码签名错误:未找到代码签名标识:未找到与构建设置(“”)中指定的配置文件匹配的代码签名标识(即证书和私钥对).CodeSign错误:SDK“iOS7.0”中的产品类型“应用”需要代码签名“非常感谢任何见解.谢谢!解

  5. 安装命令行工具Xcode 5

    我已经尝试过并尝试过但我仍然无法解决问题.我正在尝试安装PebbleSDK1.12并安装Xcode5的命令行工具.每次我在终端尝试xcode-select–install时,我都会收到“无法安装软件,因为它目前在软件更新服务器中不可用”我试图重新安装Xcode,但是没有用.当我进入Xcode-preferences-downloads时,没有命令行工具的部分.我也试过从开发者网站安装它们但无济于事.下载看起来正确,然后我去Xcode看到首选项,并没有列出命令行工具.你们都能提出什么建议吗?

  6. xcode – 命令行工具的静态分析器问题

    我们通过使用TeamCity/命令行工具自动化我们当前项目的构建.为了确保尽可能多地捕获潜在的问题,我们已经将项目设置为对每个构建使用静态分析器.几个第三方课程被分析仪标记,所以我们排除了可疑类,标记为:一切都按照预期的方式编译在Xcode中(用4.6.3和5.0.1测试).但是,当在TeamCity服务器上编译时,我们会收到每个排除的第三方文件的以下错误:如果我们删除了-Xanalyzer-an

  7. 从命令行确定Xcode派生的数据位置

    有没有办法从命令行确定您的派生数据文件夹的位置是用xcodebuild构建后的?

  8. Xcode 7:无法使用单元测试目标创建Swift命令行工具

    我受阻了如果我创建一个MacOSCocoa应用程序,我可以免费获得一个单元测试目标.但是我想编写一个命令行工具,并写一些单元测试.如果我能弄清楚,我会被伤害我已经足够远,我有一个命令行目标和一个测试目标.我可以“@testableimport”我的commnd行模块,并使用测试代码中的代码,Xcode中没有错误.但是,一旦我尝试运行我的测试,我得到一个链接错误.主模块中的任何类都不能链接.什么?

  9. 如何从命令行中删除iOS 8 Simulator中的应用程序?

    我有一个在iOS模拟器中运行的自动化,我必须在另一次运行之前删除.如何从命令行从iOS模拟器中删除该应用程序?

  10. macos – KeyChain安全命令行工具使用find-internet-passwords但不能使用find-generic-passwords

    研究如何使用KeyChain的命令行工具.我能够通过安全做很多事情;列出我的多个钥匙串,转储它们并设置默认值.阅读教程和其他帖子我希望找到我的密码安全查找–通用–密码测试但我明白了security:SecKeychainSearchcopyNext:ThespecifieditemCouldnotbefoundinthekeychain.这不适用于我的默认钥匙串或login.keychain.但是

随机推荐

  1. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  2. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  3. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  4. nodejs模块学习之connect解析

    这篇文章主要介绍了nodejs模块学习之connect解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  6. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Node.js编写爬虫的基本思路及抓取百度图片的实例分享

    这篇文章主要介绍了Node.js编写爬虫的基本思路及抓取百度图片的实例分享,其中作者提到了需要特别注意GBK转码的转码问题,需要的朋友可以参考下

  8. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  10. node下使用UglifyJS压缩合并JS文件的方法

    下面小编就为大家分享一篇node下使用UglifyJS压缩合并JS文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

返回
顶部