大概是大二的时候,
我在TwitchTV上看到了一个极其精彩的Idea。

背景

TwitchTV是外国的一个主要做游戏直播网站,
观众可以打开网页观看游戏直播,
每个人都能随时发弹幕表达自己的想法。
弹幕是啥?

依赖于发弹幕万人同屏的这个设定,
TwitchTV做了一个很好玩的功能,
就是“几千人玩操纵同一个角色,玩同一个游戏。”
详情可以参见Twitch Plays Pokemon这个维基词条

当时看到这个消息,
我也去玩了一下,
感受是:特别好玩!!!

几千个人同屏玩游戏的话,
最大的感受就是混乱
这也是最棒的体验。
大部分玩家是冲着通关的目标去玩的,
所以总体看来,角色的行为是有目标性的。
但是也会有一部分玩家以捣乱为乐,
在一些精细操作的时候(比如收服Pokemon的时候)
就会额外混乱。

最终整个游戏(或者说社会实验)在混乱中前行,
经过了16个日夜,
最终打过了四大天王,
完成了通关壮举。

OK,前面都是背景介绍,
那么看到这么好玩的一个东西,
我的心痒了很久:
我也想实现一个类似的功能!

实现

就像把大象塞冰箱里需要拆解步骤,
为了实现“用弹幕玩同屏GBA游戏”这一点,
我们主要要做的事情有如下几点:

  1. 申请一个直播间
  2. 获取直播间的弹幕
  3. 实现从弹幕到键位的映射
  4. 用程序操纵GBA模拟器

当然,我们这个拆分非常的粗略,
而且会有很多具体的问题,
我们一个一个地来看。

申请一个直播间

我要没记错的话(_懒得去查资料验证了_)
TwitchTV的同屏玩游戏功能应该是官方实现的,
不是某一个主播或者是我这样的第三方程序员实现的。
平台自己实现的话会有非常多的自主权,
而且可以给到游戏本身的推广,
一波活动推出去不论是效果还是效果都会更好。

但是毕竟人微言轻,
我们普通人类还是要从头开始,
从申请直播间开始。

申请直播间的话主要是涉及到直播平台的选择,
因为我一直都是A/B站用户,
所以直播平台基本上就是斗鱼(A站生放送)和B站直播之间选一个。

最终我两年前注册了一个斗鱼直播间
(打个广告,从来不播的直播间:douyu.com/lisp

第一步算是做完了。

获取直播间的弹幕

这个是个非常interesting的问题。
首先他受前置问题的影响,
带来的现实问题是每个直播平台获取弹幕的难度是不一样的

斗鱼和B站直播相比,
看斗鱼的人肯定是更多的,
但是受氛围和观众群体的影响,
会为B站写插件的程序员更多_CITATION NEEDED_。

但毕竟我们在上一个步骤选择了斗鱼,
还是得从一而终。
于是两年前我就在知乎关注了《如何获取斗鱼直播间的弹幕信息?》这个问题,
顺便学习了一波socket相关知识(对,这句话反映了我的真实水平…)

当时没有一套好用的库,
我又懒得自己钻研,
所以就卡在这里了。

后来我在GitHub闲逛的时候
发现itchat的作者写了一个包,
支持获取各大直播平台的弹幕,
感觉就是我要的轮子!
于是我点了一个star先马克着

今天是2018年1月,
这个库最近的更新是2017年5月,
有大半年没更新代码了。
正式采用之前我试了一下,
斗鱼的弹幕是没问题的,
不过B站的弹幕因为是直接解析网页原文拿ROOMID的,
已经失效了。
又因为这个库(或者说littlecoder这个人)不是很Pythonic,
于是我心中又暗立一个flag,
fork了这个项目,
想着啥时候摸鱼摸够了就去改进一波。

于是最终我采用了danmu这个库,
几行代码就成功地获取了斗鱼弹幕,
大概代码(伪)如下:

import danmu

client = danmu.DanMuClient('http://www.douyu.com/lisp')

@client.danmu
def receive(message):
    print('[{}] {}'.format(message['NickName'], message['Content']))

client.start()

实现从弹幕到键位的映射

这个没什么可以说的,
就是业务逻辑/苦力活。
简单。跳过。

用程序操纵GBA模拟器

好,这里首先有个坑。
我们回忆一下,
最开始我们的目标其实是“实现用弹幕玩GBA游戏”,
现在的这个小步骤被回归成了“用程序操纵GBA模拟器”。
这模拟器的需求是哪冒出来的?

嗨呀,这个是有苦衷的。

首先,我对GBA的回忆除了实体机,
有一半都是VisualBoyAdvance.exe这个模拟器给的。
还有就是理论上我们也可以实现Web版的,
或者自己撸一个GBA模拟器,
但那样又相当于额外的工作量了。
所以基于“把我不会的目标拆成我能做到的小步骤”
和“鲁迅说过,不要重复造轮子”这两个设定,
我就把“用程序玩GBA游戏”拆分成了“用程序操纵GBA模拟器”+“用GBA模拟器玩GBA游戏”(现成的)这两个任务了。

OK,我们来现实地看第一点:
“怎么用程序操纵GBA模拟器”。

在我的脑海中,
假如用Python来实现,
基本上这个就是Python调用windows库 + windows库给程序传递信号 + 程序接收信号达成效果,
这样一套combo下来就行了。

虽然我平常开发环境是windows,
但其实我对windows的接口是一窍不通(暴露水平 x2)
不过不懂可以问啊!
于是前几天我问了一下身边的windows大拿hulucc,
他表示windows有个叫sendkey的API可以做这个,
不过只能操纵active window。
后来他又查了一下,
跟我说Python里有个pywin32的库可以调用windows接口,
又发了一篇文章 How to Build a Python Bot That Can Play Web Games
给我参考。

学习了这么一大段以后,
我十分感动,
然后自己找了个另外的库keyboard(雾)

事实是后来我回来研究了一下,
用windows接口肯定能达到我的目的,
但是问题是pywin32或者是类似的pypiwin32这俩包没有合适的pip release要手动安装
这让我很不舒服。
刚好google的同时,
我搜到了另外一个python库keyboard
它的主页长得还蛮好看的,
也能达到我的需求,
不错,就这个了。

于是实现了接收弹幕+发送按键的代码(伪)就大概长这样子:

import danmu
import keyboard
import constants

client = danmu.DanMuClient('http://www.douyu.com/lisp')

@client.danmu
def receive(message):
    print('[{}] {}'.format(message['NickName'], message['Content']))
    content = message['Content']  # 主要是新增了这行和下面的if
    if content in constants.valid_keystrokes:
        keyboard.send(content)

client.start()

一个小坑

这里还遇到了一个小坑,
简单来说就是VisualBoyAdvance这款GBA模拟器(以下简称VBA)
它应该是通过监控键盘事件来转化键位的。
(此句描述不够专业)

转化成代码语言就是keyboard.send(content)这行代码对VBA不起作用。
经过思考加尝试以后,
最终使用了keyboard.press(content) + time.sleep(0.02) + keyboard.release(content)三个combo达成了效果。

最终不禁要感慨一下:
上面这段描述里的经过思考加尝试
就是写程序这件事情最痛苦也是最美好的所在了。

总结

上面讲的比较零碎,
总结下来:

为了做到 “用弹幕玩GBA游戏” 这个事情,
被拆分细化完成的任务有这些:

  • 注册直播间,选了斗鱼。
  • 获取直播间弹幕,平常关注一波信息,最终用了danmu这个库。
  • 实现业务逻辑。
  • 用程序操纵GBA模拟器来玩GBA游戏,使用了keyboard库发送键位信息。

最终的成品在我的GitHub上:LKI/danmaboy这个项目,
有效代码就100行,就在danmaboy/__init__.py一个文件内。

一下午就写完了,
不过中途摸鱼思考的时间用了几年(惭愧)

下午写代码的时候我还做了一个尝试,
就是开着直播写代码,
不过因为没人看(现实的原因),
所以总体感受跟小黄鸭编程差不多,
写一会儿,就紧张地想一下思路,
效果意外的不错。

最终成品出来以后,
试着在直播间里跑了一下,
创业失败遇到了一个基石上的失误:

因为是游戏,所以普通的弹幕延迟在这个项目上会放大到极度影响游戏体验

基本上就是发一条弹幕,
15秒以后才会有对应的游戏变动,
别说TwitchTV那样的效果了,
就是基本的自己玩玩都玩不动…

叹气。

不过好歹是我心里很多idea之一了,
这篇文章也算是给这个项目画个句号吧。

我的心中还有很多未竟的事业,
有兴趣的话,
欢迎和我交流噢~

下次见。

screen
直播时的画面截图