STM32学习笔记二十一:WS2812制作像素游戏屏-飞行射击游戏(11)探索游戏脚本

 还记得上次在第十七章中为BOSS创建的路径动画吧。我们写了一大坨的代码来描述BOSS的运动路径,但凡是写过几年代码的人都不会干出这样的事情。-_-!

没办法,谁叫那时候还没有脚本呢。这章就来补齐这块短板。

脚本属于配置化的一种,你可以把脚本当成配置文件来看待。要做脚本,优先要确定脚本的格式。常用的格式有:

1、二进制格式。优势是最节约空间,非常适合单片机这种嵌入式设备。劣势是无法直接阅读,不需要另外写个编解码工具来生成配置文件。

2、文本行格式。优势是可阅读,空间也可接受。劣势是修改容易出错,尽量不要有对齐要求,否则用起来可能要崩溃。

3、JSON或XML。优势是方便阅读方便编辑,不需要专用工具。劣势是需要空间较大。

本项目中,采用2+3方式,即按行保存压缩的JSON串。

脚本主要起两个作用,一是描述物体运动的轨迹,如前面BOSS的运动动画。另一个是制作关卡剧本。

先来搞第一个:

1、现有弄个JSON解析工具。以前做MQTT时用过一个:《【嵌入式项目应用】__cJSON在单片机的使用》

下载cJSON.h 和cJSON.c,并放到项目中。

 2、统一所有敌机的TICK,先尝试有没有故事板可以运行。如果故事板运行结束,则按原有固定向下运行。

uint8_t EnemyT1::tick(uint32_t t) {if (animationStoryBoard->isValid) {animationStoryBoard->tick(t);} else {if (explodeState == 0)baseInfo.y += t * baseInfo.speedY;if (baseInfo.y > 64 * PlaneXYScale)baseInfo.visiable = 0;}if (fireTimer.tick(t))createBulletObject();for (ListNode *node = animationList->next; node != animationList; node =node->next) {if (((Animation*) node->data)->isValid) {((Animation*) node->data)->tick(t);}}return 0;
}

3、把原来BOSS的运行路径转为JSON串:

[{"r": 1,"i": [{				"c": 0,						"x": 200000,				"y": 0			},{				"c": 2000,				"x": 200000,				"y": 100000			}]},{"r": 2,"i": [{				"c": 0,						"x": 200000,				"y": 100000		},{				"c": 3772,				"x": 120000,				"y": 150000		},{				"c": 5772,				"x":  70000,				"y": 150000		},{				"c": 6904,				"x":  50000,				"y": 160000		},{				"c": 7304,				"x":  50000,				"y": 140000		},{				"c": 8436,				"x":  70000,				"y": 100000		},{				"c": 10436,				"x": 120000,				"y": 100000		},{				"c": 14208,				"x": 200000,				"y": 150000		},{				"c": 16208,				"x": 250000,				"y": 150000		},{				"c": 17340,				"x": 270000,				"y": 160000		},{				"c": 17740,				"x": 270000,				"y": 140000		},{				"c": 18872,				"x": 250000,				"y": 100000		},{				"c": 20872,				"x": 200000,				"y": 100000		}]}
]

r 为 group的重复次数

c为时间点

x,y为坐标值。应提前 * PlaneXYScale

4、对JSON进行压缩转义,然后用字符串来保存。

/** PlaneScript.h**  Created on: Dec 28, 2023*      Author: YoungMay*/#ifndef SRC_PLANE_PLANESCRIPT_H_
#define SRC_PLANE_PLANESCRIPT_H_const char *PlaneLevelScript[] ={"[{\"r\":1,\"i\":[{\"c\":0,\"x\":200000,\"y\":0},{\"c\":2000,\"x\":200000,\"y\":100000}]},{\"r\":2,\"i\":[{\"c\":0,\"x\":200000,\"y\":100000},{\"c\":3772,\"x\":120000,\"y\":150000},{\"c\":5772,\"x\":70000,\"y\":150000},{\"c\":6904,\"x\":50000,\"y\":160000},{\"c\":7304,\"x\":50000,\"y\":140000},{\"c\":8436,\"x\":70000,\"y\":100000},{\"c\":10436,\"x\":120000,\"y\":100000},{\"c\":14208,\"x\":200000,\"y\":150000},{\"c\":16208,\"x\":250000,\"y\":150000},{\"c\":17340,\"x\":270000,\"y\":160000},{\"c\":17740,\"x\":270000,\"y\":140000},{\"c\":18872,\"x\":250000,\"y\":100000},{\"c\":20872,\"x\":200000,\"y\":100000}]}]"};#endif /* SRC_PLANE_PLANESCRIPT_H_ */

5、在故事板中添加脚本解析方法。

void AnimationStoryBoard::loadScript(const char *value, int *bindAddX,int *bindAddY) {cJSON *root = cJSON_Parse(value);int gcount = cJSON_GetArraySize(root);for (int i = 0; i < gcount; i++) {cJSON *jGroup = cJSON_GetArrayItem(root, i);AnimationGroup *group = new AnimationGroup();ContinuousAnimation *aniX = new ContinuousAnimation();ContinuousAnimation *aniY = new ContinuousAnimation();aniX->bindAddress = bindAddX;aniY->bindAddress = bindAddY;group->addItem(aniX);group->addItem(aniY);group->repeat = cJSON_GetObjectItem(jGroup, "r")->valueint;cJSON *iGroup = cJSON_GetObjectItem(jGroup, "i");int icount = cJSON_GetArraySize(iGroup);for (int j = 0; j < icount; j++) {cJSON *items = cJSON_GetArrayItem(iGroup, j);int time = cJSON_GetObjectItem(items, "c")->valueint;int x = cJSON_GetObjectItem(items, "x")->valueint;int y = cJSON_GetObjectItem(items, "y")->valueint;aniX->addItem(time, x);aniY->addItem(time, y);}ListPushBack(animationGroupList, (LTDataType) group);}
}

这就ok了。

脚本的另一个作用是制作关卡。

我们通过脚本定义不同时间或者不同位置(滚轴游戏),出现不同的敌人,也可以定义出现一些地形物体,加上碰撞,可以做到类似地图的效果。

我们来试着做个排队行进的小飞机。方式是几个飞机绑定相同的线路,错开相同间隔出现。

1、先定义飞机的飞行线路

对应的JSON:

[{"r": 1,"i": [{				"c": 0,						"x": 200000,				"y": 0			},{				"c": 4000,				"x": 200000,				"y": 400000	},{				"c": 5000,				"x": 100000,				"y": 400000	},{				"c": 7000,				"x": 100000,				"y": 200000	},{				"c": 7500,				"x": 150000,				"y": 200000	},]}
]

再加一个与之对称的。

2、加入数组

/** PlaneScript.h**  Created on: Dec 28, 2023*      Author: YoungMay*/#ifndef SRC_PLANE_PLANESCRIPT_H_
#define SRC_PLANE_PLANESCRIPT_H_static const char *PlaneLevelScript[] ={"[{\"r\":1,\"i\":[{\"c\":0,\"x\":200000,\"y\":0},{\"c\":2000,\"x\":200000,\"y\":100000}]},{\"r\":2,\"i\":[{\"c\":0,\"x\":200000,\"y\":100000},{\"c\":3772,\"x\":120000,\"y\":150000},{\"c\":5772,\"x\":70000,\"y\":150000},{\"c\":6904,\"x\":50000,\"y\":160000},{\"c\":7304,\"x\":50000,\"y\":140000},{\"c\":8436,\"x\":70000,\"y\":100000},{\"c\":10436,\"x\":120000,\"y\":100000},{\"c\":14208,\"x\":200000,\"y\":150000},{\"c\":16208,\"x\":250000,\"y\":150000},{\"c\":17340,\"x\":270000,\"y\":160000},{\"c\":17740,\"x\":270000,\"y\":140000},{\"c\":18872,\"x\":250000,\"y\":100000},{\"c\":20872,\"x\":200000,\"y\":100000}]}]","[{\"r\":1,\"i\":[{\"c\":0,\"x\":210000,\"y\":0},{\"c\":4000,\"x\":210000,\"y\":400000},{\"c\":5000,\"x\":100000,\"y\":400000},{\"c\":7000,\"x\":100000,\"y\":200000},{\"c\":7500,\"x\":160000,\"y\":200000}]}]","[{\"r\":1,\"i\":[{\"c\":0,\"x\":100000,\"y\":0},{\"c\":4000,\"x\":100000,\"y\":400000},{\"c\":5000,\"x\":210000,\"y\":400000},{\"c\":7000,\"x\":210000,\"y\":200000},{\"c\":7500,\"x\":160000,\"y\":200000}]}]",
};#endif /* SRC_PLANE_PLANESCRIPT_H_ */

3、设置关卡剧本,关卡剧本我们换一种方法,不是因为这样更好,仅仅是为了多测试几种方式。如果将来要做更复杂,则应该用文件方式保存在flash里面。

/** PlaneScript.h**  Created on: Dec 28, 2023*      Author: YoungMay*/#ifndef SRC_PLANE_PLANESCRIPT_H_
#define SRC_PLANE_PLANESCRIPT_H_static const char *PlaneLevelScript[] ={"[{\"r\":1,\"i\":[{\"c\":0,\"x\":200000,\"y\":0},{\"c\":2000,\"x\":200000,\"y\":100000}]},{\"r\":2,\"i\":[{\"c\":0,\"x\":200000,\"y\":100000},{\"c\":3772,\"x\":120000,\"y\":150000},{\"c\":5772,\"x\":70000,\"y\":150000},{\"c\":6904,\"x\":50000,\"y\":160000},{\"c\":7304,\"x\":50000,\"y\":140000},{\"c\":8436,\"x\":70000,\"y\":100000},{\"c\":10436,\"x\":120000,\"y\":100000},{\"c\":14208,\"x\":200000,\"y\":150000},{\"c\":16208,\"x\":250000,\"y\":150000},{\"c\":17340,\"x\":270000,\"y\":160000},{\"c\":17740,\"x\":270000,\"y\":140000},{\"c\":18872,\"x\":250000,\"y\":100000},{\"c\":20872,\"x\":200000,\"y\":100000}]}]","[{\"r\":1,\"i\":[{\"c\":0,\"x\":210000,\"y\":0},{\"c\":4000,\"x\":210000,\"y\":400000},{\"c\":5000,\"x\":100000,\"y\":400000},{\"c\":7000,\"x\":100000,\"y\":200000},{\"c\":7500,\"x\":160000,\"y\":200000}]}]","[{\"r\":1,\"i\":[{\"c\":0,\"x\":100000,\"y\":0},{\"c\":4000,\"x\":100000,\"y\":400000},{\"c\":5000,\"x\":210000,\"y\":400000},{\"c\":7000,\"x\":210000,\"y\":200000},{\"c\":7500,\"x\":160000,\"y\":200000}]}]", };typedef struct {uint32_t createTime;uint8_t objType;uint8_t bindScript;
} StageScript_t;#define StageScriptsCount 13
const StageScript_t StageScripts[] = {{1000,		0,		1},{1600,		0,		1},{2200,		0,		1},{2800,		0,		1},{3400,		0,		1},{4000,		0,		1},{11000,		0,		2},{11600,		0,		2},{12200,		0,		2},{12800,		0,		2},{13400,		0,		2},{14000,		0,		2},{20000,		0,		1},};#endif /* SRC_PLANE_PLANESCRIPT_H_ */

4、修改EnemyManager管理类敌机创造的逻辑,加上脚本。

uint8_t EnemyManager::tick(uint32_t t) {if (stageScriptPoint < StageScriptsCount) {stageTime += t;while (stageScriptPoint < StageScriptsCount&& StageScripts[stageScriptPoint].createTime < stageTime) {EnemyBase *enemy = createEnemyObject(StageScripts[stageScriptPoint].objType);enemy->init();enemy->animationStoryBoard->loadScript(PlaneLevelScript[StageScripts[stageScriptPoint].bindScript],&enemy->baseInfo.x, &enemy->baseInfo.y);enemy->animationStoryBoard->start();ListPushBack(enemyList, (LTDataType) enemy);stageScriptPoint++;}} else {if (createTimer.tick(t)) {EnemyBase *enemy = createEnemyObject(ran_seq(2, enemyTypeProportion));enemy->init();ListPushBack(enemyList, (LTDataType) enemy);}if (createBossTimer.tick(t)) {createTimer.defaultSpan -= 100;EnemyBase *enemy = new EnemyT3();enemy->init();enemy->animationStoryBoard->loadScript(PlaneLevelScript[0],&enemy->baseInfo.x, &enemy->baseInfo.y);enemy->animationStoryBoard->start();ListPushBack(enemyList, (LTDataType) enemy);}}for (ListNode *cur = enemyList->next; cur != enemyList; cur = cur->next) {EnemyBase *enemy = ((EnemyBase*) (cur->data));enemy->tick(t);}return 0;
}

TIPS:花点时间设计脚本,就可以做出沙罗曼蛇那样的闯关游戏了。

 看看效果吧

STM32学习笔记二十一:WS2812制作像素游戏屏-飞行射

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/325720.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

罗德与施瓦茨FSVA40信号和频谱分析仪

罗德与施瓦茨FSVA40是一款功能信号和频谱分析仪&#xff0c;适用于从事射频系统的开发、生产、安装和服务的用户。FSVA40信号和频谱分析仪系列始终提供最佳的价格和性能组合&#xff0c;无论是根据最新通信标准测试生产中的无线设备&#xff0c;还是测量低相位噪声、高灵敏度和…

QT翻金币

QT翻金币 在B站跟着视频进行QT学习&#xff0c;现把代码全部贴上来&#xff0c;备忘 整体解决方案文件结构如下&#xff1a; chooselevelscene.h #ifndef CHOOSELEVELSCENE_H #define CHOOSELEVELSCENE_H#include <QMainWindow> #include"playscene.h"class…

Hive实战:分科汇总求月考平均分

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、启动Hive Metastore服务2、启动Hive客户端3、创建分区的学生成绩表4、按分区加载数据5、查看分区…

域名流量被劫持怎么办?如何避免域名流量劫持?

随着互联网不断发展&#xff0c;流量成为线上世界的巨大财富。然而一种叫做域名流量劫持的网络攻击&#xff0c;将会在不经授权的情况下控制或重定向一个域名的DNS记录&#xff0c;导致用户在访问一个网站时&#xff0c;被引导到另一个不相关的网站&#xff0c;从而劫持走原网站…

Git 命令大全:解锁代码版本控制的神奇秘密!

Git 命令大全&#xff1a;解锁代码版本控制的神奇秘密&#xff01; 以下是备忘单中的所有命令&#xff1a; 设置 设置将附加到提交和标记的名称和电子邮件 $ git config --global user.name "Danny Adams"$ git config --global user.email "myemailgmail.c…

武汉灰京文化:技术先锋辐射游戏行业,带来全新体验乐趣无穷!

科技的持续演进&#xff0c;给游戏产业打了强心剂&#xff0c;让这个领域变得前所未有的越来越好玩儿。今天我们将深入探讨如何利用虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;技术&#xff0c;让你玩得开心&#xff0c;玩得尽兴。 想象一下&…

.NET Standard 支持的 .NET Framework 和 .NET Core

.NET Standard 是针对多个 .NET 实现推出的一套正式的 .NET API 规范。 推出 .NET Standard 的背后动机是要提高 .NET 生态系统中的一致性。 .NET 5 及更高版本采用不同的方法来建立一致性&#xff0c;这种方法在大多数情况下都不需要 .NET Standard。 但如果要在 .NET Framewo…

Linux vi/vim 教程

文章目录 【 1. vi/vim 的三种模式 】1.1 命令模式1.2 输入模式1.3 底线命令模式 【 2. 实例 】【 3. vim 的其他命令 】 所有的 Unix Like 系统都会内建 vi 文本编辑器&#xff0c;其他的文本编辑器则不一定会存在。目前我们使用比较多的是 vim 编辑器。vim 从 vi 发展出来&am…

2024年初级会计资格考试报名照片要求,千万不要直接上传哦

2024年初级会计资格考试照片要求&#xff0c;千万不要直接上传哦。 第一步&#xff1a;支付宝搜索"亿鸣证件照"或者微信搜索”随时照“&#xff0c;然后进入小程序的搜索列表搜索"初级会计"&#xff0c;然后上传一张生活照或者自拍一张&#xff0c;就能制…

DBeaver配置达梦数据库连接

随着信创逐渐推广&#xff0c;达梦数据库也成为流行。下面展示如何使用dbeaver配置达梦数据库连接 1 驱动新建 菜单&#xff0c;数据库->驱动管理器 2 驱动信息填写 选择新建之后&#xff0c;弹出一个填写页面 需要填写的几个关键信息&#xff1a; 驱动名称&#xff1a;…

数据结构第六弹---带头双向循环链表

双向循环链表 1、带头双向循环链表概念2、带头双向循环链表的优势3、带头双向循环链表的实现3.1、头文件包含和结构定义3.2、创建新结点3.3、打印3.4、初始化3.5、销毁3.6、尾插3.7、头插3.8、头删3.9、尾删3.10、查找3.11、在pos之前插入3.12、删除pos位置3.13、判断是否为空3…

lotus-farcaster 搭建

lotus 开源监控仪表盘 安装修改配置文件监听钱包地址指定安装目录可以修改脚本指定miner配置文件路径执行安装脚本 修改修改端口为9200修改安装脚本 9100改为9200&#xff0c;重新安装修改检查脚本 端口 9200&#xff0c;重新安装 安装成功prometheus 安装创建存储目录 配置文件…