Splatoon 2 自带了一个简单的像素涂鸦工具,大触们可以一个一个像素点绘制出类似于下面这样的作品,然后把它顶在广场自己的小乌贼头上。
然而对于我这种手残而言,真要自己一个一个像素点画幅画是不可能的,这辈子都不可能的。但是又想要好看的画怎么办,Switch-Fightstick 这个项目就是来拯救你我这种手残的。
Switch-Fightstick 主要有两个功能,一个是将你的图片转换为像素图(和对应的数组),另一个是将像素数组通过单片机模拟 Pokken Tournament Pro Pad 手柄,把一个一个像素给点进去。
比如一张图片(320 x 120):
转换为像素图:
编译,烧入单片机。给 Switch 插上单片机,然后它就一点一点开始画了,速度很慢:
最终成果:
Switch-Fightstick 在 macOS 的使用过程相对简单,按照 GitHub 上的 Guide 一步一步走,安装 AVR CrossPack,下载 LUFA,编辑 makefile,pip 安装 pillow,执行 Python 脚本,make,Tensy Loader 写入 hex 二进制文件即可。
虽然 Switch-Fightstick 文档写的支持 Tensy 2.0++,但买错板子的 我实测 Tensy 2.0 也是完全没问题的,不过在执行 make 之前,需要编辑一下 makefile,把
MCU = at90usb1286
修改成
MCU = atmega32u4
这样才能保证 Teensy Loader 能够正确地将 hex 二进制文件写入到单片机中。
浅读了一下 Switch-Fightstick 的源码,大概理解了一下几个源代码的作用。
png2c.py 的作用是生成 image.c 用于后期编译,输入为一张 340 * 120 的 png 文件,转换为黑白图,逐像素读取得到一个二进制数组(1 表示涂色,0 表示不涂),再把这个二进制数每八个构造一个 Byte,写到 image.c 中。
JoyStick.c 比较重要,从源码来看,与主机交互的过程放到一个无限 Loop 中,每次循环读取一次主机传递过来的输入,再把控制指令传给主机,而每次具体的指令内容则由一个状态机来维护,有四个状态用于控制手柄左摇杆的运动和打点(按 A),控制运动的顺序是这样的:
emmm…蛇形走位虽然很简单但是…效率似乎并不是特别高,尤其是对于留白比较多的图片而言,实际上很多步骤移动都是没有必要的。一种比较简单的思路是酱紫的:
思路很简单,当前行移动的最远点,是根据当前行最右(或最左,根据方向决定)的涂色点(值为 1),和下一行最右(最左)的涂色点共同决定,每次都走到两者之间最远的点,然后立即走到下一行,而不是必须走到最末的点。对于留白多的图,时间会远远优于原始的算法。
稍微修改了一下 png2c.py,除了生成图形点的数据之外,还额外生成一个数组存储每行最左和最右的黑点。
for i in range(0, 120):
sublist = data[i * 320:(i + 1) * 320 ]
indices = [j for j,x in enumerate(sublist) if x == 1]
if (len(indices) == 0):
edges.append(0)
edges.append(319)
elif (len(indices) == 1):
edges.append(indices[0])
edges.append(indices[0])
else:
edges.append(indices[0])
edges.append(indices[-1])
JoyStick.c 这边主要是每次走下一行之间,获取下一行走的最远的点,然后左右移动时候控制在最远的点之内的范围。
if ((ypos % 2 && xpos > minLeftMost) || (!(ypos % 2) && xpos < maxRightMost)) {
state = STOP_X;
} else {
state = STOP_Y;
}
新的源码放在了我的 GitHub 上。
好吧折腾到此为止,原理上可以用 Tensy 模拟所有的手柄操作,比如看到 一个项目 基于 Switch-Fightstick 修改来实现异度神剑 2 自动购买食物和喂食异刃。以后有啥好的思路再继续折腾吧。