9月30日 更新
上传了程序附件
--------------------------------------------------------------------
作为《凡人修仙传》的粉丝,动漫中的各种手诀真的是很喜欢,幻想着有一天能够修仙飞升(当然那是不可能滴),不过捏捏剑诀什么的自我满足一下还是可以滴,刚好用手头的二哈2和K10做一个剑诀修炼模拟器。


思路如下图

关于人物:
(作品中人物均取自动漫《凡人修仙转》人物造型)
小师妹——宋玉饰
邪祟——玄骨饰
材料清单
- 行空板K10 X1
- 二哈识图2 X1
- 舵机 X7
- 纸盒 X1
- 垃圾袋 X1
- 热熔胶枪 X1
- 充电宝 X1
步骤1 确定主题,绘制流程图
主要一个是喜欢《凡人修仙传》,一个是新品二哈识图2要多用起来
其实很想研究二哈2的实例分割这个功能,但是贪多嚼不烂,先把手势识别多用用再说别的功能吧!
整个作品流程并不复杂,故事背景就是:
你扮演一个修仙的凡人,在一次跟师妹练习剑诀的时候不小心捏了一个禁忌剑诀,从而解除了某个邪祟的封印,然后你通过复杂的(其实就三个……)剑诀组合,以及坚持不懈(程序中大概不到5秒的时间)与邪祟抗衡,最终重新镇压邪祟的剧情。
步骤2 编写程序
程序写起来不难,主要是思路要清晰。分模块编写,测试,然后整合。
大致步骤如下:
1.寻找合适的图片,使用豆包帮助改图;

2.编写纯软件部分程序,实现语音识别、简单的手势识别功能;
3.加入图片,建立函数,实现较复杂程序内容(能够识别不同剑诀,并且进行解读);

4.进一步创建邪祟出现、灭杀等相关函数,借用串口调试等工具(也可以使用行空板屏幕调试);

5.单独编写舵机控制程序;

6.整合程序,修改bug
步骤3 测试舵机
1.这里我分成了八种情况

舵机0°认物卡片隐藏在盒子里面,舵机90°人物卡片出现在视野中。
2.根据表格,创建函数并测试

注意:每个函数都是7个舵机绑定在一起作为一个整体的系统进行程序编写(特殊情况除外)


(哈哈哈,简陋的测试)
测试视频见分享视频2P
步骤4 制作人物卡片、舞台、连接硬件
1.制作人物卡片
使用WPS演示抠图排版,用相纸打印,最后裁剪好轮廓

2.舞台
2.1 安置舵机
手边刚好有个纸盒,直接拿来做舞台,裁剪好舵机的位置,放置好舵机(热熔胶枪)



2.2 使用垃圾袋+卡片装饰舞台(没错,刚好看到了垃圾袋,就拿来用了……)

3.二哈2+K10
手头刚好有二哈一代的支架,拿来安装在K10上也勉强ok,就是螺丝不合适,上不紧,刚好手边有纸盒子,剪下来一点做一个应急垫片,妥妥的


步骤5 测试改bug,补细节
贴上部分程序截图(太长了,不好弄)
初始化

主循环

灭杀邪祟

/*!
* MindPlus
* esp32s3bit
*
*/
#include "asr.h"
#include "unihiker_k10.h"
#include <Microbit_Motor.h>
#include "arduino_image_cache.h"
#include "DFRobot_HuskylensV2.h"
// 动态变量
volatile float mind_n_BiaoJi, mind_n_MoShi, mind_n_ShiShen, mind_n_YuGui, mind_n_QingXin,
mind_n_MieShaJinDu, mind_n_XieSuiZhuangTai;
// 函数声明
void DF_DuoJiChuShiHua();
void DF_DuoJiShiShenJue();
void DF_DuoJiShiMeiZiXin();
void DF_ChuShiHua();
void DF_DuoJiYuGuiJue();
void DF_DuoJiQingXinJue();
void DF_ShiMeiJingKong();
void DF_DuoJiXieSuiChuXian();
void DF_DuoJiChengGongMieSha();
void DF_XieSuiChuXian();
void DF_JianJueShiBie();
void DF_MieShaXieSui();
void DF_JieDuJianJue();
// 创建对象
Microbit_Motor motorbit;
UNIHIKER_K10 k10;
uint8_t screen_dir=2;
ASR asr;
HuskylensV2 huskylens;
// 主程序开始
void setup() {
k10.begin();
Serial.begin(9600);
k10.initScreen(screen_dir);
k10.creatCanvas();
asr.setAsrSpeed(2);
asr.asrInit(CONTINUOUS, CN_MODE, 6000);
while(asr._asrState == 0){delay(100);}
Wire.begin();
while (!huskylens.begin(Wire)) {
delay(100);
}
DF_ChuShiHua();
}
void loop() {
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
// 特殊情况:一开始就是用禁忌剑诀
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==5)) {
DF_XieSuiChuXian();
DF_MieShaXieSui();
}
while (!(mind_n_MoShi==1)) {
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
DF_JianJueShiBie();
DF_JieDuJianJue();
}
while (!(mind_n_MoShi==0)) {
DF_MieShaXieSui();
}
}
// 自定义函数
void DF_DuoJiChuShiHua() {
motorbit.servo(S1, 0);
motorbit.servo(S2, 0);
motorbit.servo(S3, 0);
motorbit.servo(S4, 0);
motorbit.servo(S5, 0);
motorbit.servo(S6, 0);
motorbit.servo(S7, 0);
}
void DF_DuoJiShiShenJue() {
motorbit.servo(S1, 0);
motorbit.servo(S2, 90);
motorbit.servo(S3, 0);
motorbit.servo(S4, 0);
motorbit.servo(S5, 0);
motorbit.servo(S6, 0);
motorbit.servo(S7, 0);
}
void DF_DuoJiShiMeiZiXin() {
motorbit.servo(S1, 90);
motorbit.servo(S2, 0);
motorbit.servo(S3, 0);
motorbit.servo(S4, 0);
motorbit.servo(S5, 0);
motorbit.servo(S6, 0);
motorbit.servo(S7, 0);
}
void DF_ChuShiHua() {
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data1);
k10.canvas->updateCanvas();
Serial.println("标题画面载入完毕。");
asr.addASRCommand(0+1, "kai shi xiu lian");
while (!asr.isDetectCmdID(0+1)) {}
DF_DuoJiChuShiHua();
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data2);
k10.canvas->canvasText("师兄,又来练习剑诀啦!", 40, 260, 0xFFCCCC, k10.canvas->eCNAndENFont16, 20, false);
k10.canvas->updateCanvas();
asr.speak("师兄,又来练习剑诀啦!");
DF_DuoJiShiMeiZiXin();
delay(3000);
k10.canvas->canvasClear();
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data3);
k10.canvas->canvasText("待我准备一下!", 40, 260, 0xFFCCCC, k10.canvas->eCNAndENFont16, 20, false);
k10.canvas->updateCanvas();
asr.speak("待我准备一下!");
Serial.println("初始化二哈2……");
DF_DuoJiChuShiHua();
huskylens.switchAlgorithm(ALGORITHM_HAND_RECOGNITION);
Serial.println("二哈2初始化完毕,载入标题画面……");
Serial.println("重置标记变量与模式变量……");
mind_n_BiaoJi = 0;
mind_n_MoShi = 0;
Serial.println("标记变量与模式变量重置完毕。");
Serial.println((String("当前标记值:") + String(mind_n_BiaoJi)));
Serial.println((String("当前模式值:") + String(mind_n_MoShi)));
delay(3000);
DF_DuoJiShiMeiZiXin();
k10.canvas->canvasClear();
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data4);
k10.canvas->canvasText("准备好啦!按下A键开始吧!", 40, 260, 0xFFCCCC, k10.canvas->eCNAndENFont16, 20, false);
k10.canvas->updateCanvas();
asr.speak("准备好啦!按下A键开始吧!");
while (!(k10.buttonA->isPressed())) {}
k10.canvas->canvasClear();
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data5);
k10.canvas->updateCanvas();
asr.speak("请出剑诀!");
}
void DF_DuoJiYuGuiJue() {
motorbit.servo(S1, 0);
motorbit.servo(S2, 0);
motorbit.servo(S3, 90);
motorbit.servo(S4, 0);
motorbit.servo(S5, 0);
motorbit.servo(S6, 0);
motorbit.servo(S7, 0);
}
void DF_DuoJiQingXinJue() {
motorbit.servo(S1, 0);
motorbit.servo(S2, 0);
motorbit.servo(S3, 0);
motorbit.servo(S4, 90);
motorbit.servo(S5, 0);
motorbit.servo(S6, 0);
motorbit.servo(S7, 0);
}
void DF_ShiMeiJingKong() {
motorbit.servo(S1, 0);
motorbit.servo(S2, 0);
motorbit.servo(S3, 0);
motorbit.servo(S4, 0);
motorbit.servo(S5, 90);
motorbit.servo(S6, 0);
motorbit.servo(S7, 0);
}
void DF_DuoJiXieSuiChuXian() {
motorbit.servo(S1, 0);
motorbit.servo(S2, 0);
motorbit.servo(S3, 0);
motorbit.servo(S4, 0);
motorbit.servo(S5, 0);
motorbit.servo(S6, 90);
motorbit.servo(S7, 0);
}
void DF_DuoJiChengGongMieSha() {
motorbit.servo(S1, 0);
motorbit.servo(S2, 0);
motorbit.servo(S3, 0);
motorbit.servo(S4, 0);
motorbit.servo(S5, 0);
motorbit.servo(S6, 0);
motorbit.servo(S7, 90);
}
void DF_XieSuiChuXian() {
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data6);
DF_ShiMeiJingKong();
k10.canvas->canvasText("师兄,这个剑诀不可以捏啊!", 65, 260, 0xFF6600, k10.canvas->eCNAndENFont16, 20, false);
k10.canvas->updateCanvas();
asr.speak("师兄,这个剑诀不可以捏啊!");
delay(4000);
mind_n_MoShi = 1;
mind_n_ShiShen = 0;
mind_n_YuGui = 0;
mind_n_QingXin = 0;
k10.canvas->canvasClear();
DF_DuoJiXieSuiChuXian();
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data7);
k10.canvas->updateCanvas();
asr.speak("不好,有妖祟作怪,速速出剑诀!");
delay(3000);
k10.canvas->updateCanvas();
asr.speak("请按照如下顺序捏诀");
delay(3000);
k10.canvas->updateCanvas();
asr.speak("御鬼、清心、弑神");
delay(2000);
k10.canvas->updateCanvas();
asr.speak("速速灭杀了邪魔外道吧!");
delay(1000);
}
void DF_JianJueShiBie() {
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data8);
k10.canvas->updateCanvas();
if ((huskylens.available(ALGORITHM_HAND_RECOGNITION))) {
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==1)) {
delay(50);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==1)) {
mind_n_BiaoJi = 1;
}
}
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==2)) {
delay(50);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==2)) {
mind_n_BiaoJi = 2;
}
}
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==3)) {
delay(50);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==3)) {
mind_n_BiaoJi = 3;
}
}
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==5)) {
delay(50);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==5)) {
mind_n_BiaoJi = 5;
}
}
}
}
void DF_MieShaXieSui() {
k10.canvas->canvasClear();
k10.canvas->canvasDrawBitmap(0,0,240,319,image_data9);
k10.canvas->updateCanvas();
while (!(mind_n_YuGui==1)) {
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
// 不直接使用变量【标记】是因为,需要考虑一开始就是用禁忌剑诀的情况,那个时候还没有获取到标记变量
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==2)) {
delay(50);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==2)) {
DF_DuoJiYuGuiJue();
motorbit.servo(S6, 90);
mind_n_YuGui = 1;
}
}
}
asr.speak("御鬼");
delay(1000);
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data10);
k10.canvas->updateCanvas();
while (!(mind_n_QingXin==1)) {
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==3)) {
delay(50);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==3)) {
DF_DuoJiQingXinJue();
motorbit.servo(S6, 90);
mind_n_QingXin = 1;
}
}
}
asr.speak("清心");
delay(1000);
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data11);
k10.canvas->updateCanvas();
while (!(mind_n_ShiShen==1)) {
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==1)) {
delay(50);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==1)) {
DF_DuoJiShiShenJue();
motorbit.servo(S6, 90);
mind_n_ShiShen = 1;
}
}
}
asr.speak("弑神");
delay(1000);
asr.speak("速速使用灭杀剑诀!");
while (!((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==4)) {
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data12);
k10.canvas->updateCanvas();
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
delay(50);
}
k10.canvas->canvasClear();
k10.setScreenBackground(0xFF0000);
k10.canvas->canvasText("灭杀此獠,急急如律令!", 60, 120, 0xFFFF00, k10.canvas->eCNAndENFont16, 20, true);
k10.canvas->updateCanvas();
mind_n_MieShaJinDu = 0;
mind_n_XieSuiZhuangTai = 90;
while (!(((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==4) && (mind_n_MieShaJinDu==5))) {
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==4)) {
delay(500);
huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
if (((RET_ITEM_NUM(huskylens.getCachedCenterResult(ALGORITHM_HAND_RECOGNITION), Result, ID))==4)) {
mind_n_MieShaJinDu += 1;
mind_n_XieSuiZhuangTai -= 15;
motorbit.servo(S6, mind_n_XieSuiZhuangTai);
delay(100);
}
else {
motorbit.servo(S5, 90);
motorbit.servo(S6, 90);
mind_n_MieShaJinDu = 0;
mind_n_XieSuiZhuangTai = 90;
asr.speak("师兄,坚持住!");
delay(1000);
}
}
DF_DuoJiChuShiHua();
}
mind_n_MoShi = 0;
k10.canvas->canvasClear();
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data13);
DF_DuoJiChengGongMieSha();
k10.canvas->updateCanvas();
asr.speak("邪魔外道已经灭杀,师兄可以继续练习了!");
delay(2000);
DF_DuoJiChuShiHua();
k10.setScreenBackground(0xFFFFFF);
k10.canvas->canvasClear();
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data14);
k10.canvas->updateCanvas();
asr.speak("请出剑诀!");
}
void DF_JieDuJianJue() {
if ((mind_n_BiaoJi==1)) {
DF_DuoJiShiShenJue();
k10.canvas->canvasText("弑神诀,威力强大!", 65, 260, 0xFF6600, k10.canvas->eCNAndENFont16, 20, false);
k10.canvas->updateCanvas();
asr.speak("弑神诀,威力强大!");
delay(1500);
}
if ((mind_n_BiaoJi==2)) {
DF_DuoJiYuGuiJue();
k10.canvas->canvasText("御鬼诀,小心使用!", 65, 260, 0xFF6600, k10.canvas->eCNAndENFont16, 20, false);
k10.canvas->updateCanvas();
asr.speak("御鬼诀,小心使用!");
delay(1500);
}
if ((mind_n_BiaoJi==3)) {
DF_DuoJiQingXinJue();
k10.canvas->canvasText("清心诀,回复精力!", 65, 260, 0xFF6600, k10.canvas->eCNAndENFont16, 20, false);
k10.canvas->updateCanvas();
asr.speak("清心诀,回复精力!");
delay(1500);
}
if ((mind_n_BiaoJi==5)) {
mind_n_MoShi = 1;
DF_XieSuiChuXian();
DF_MieShaXieSui();
}
mind_n_BiaoJi = 0;
delay(1000);
}
写在最后:
程序虽然不难,但是中间弯弯绕绕的,改了好多次,还是有很多地方不尽人意,想修改好然后加上3D打印,做个2.0版本的,看什么时候有机会吧……

上图是我在创作过程中的小笔记,仅供参考
关于改进措施,我也想了好多

以上就是“剑诀修炼模拟器”的全部内容啦!欢迎讨论!!
附件
评论