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制作像素游戏屏-飞行射

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB数据类型详解

BSON 协议与数据类型 MongoDB 为什么会使用 BSON&#xff1f; JSON 是当今非常通用的一种跨语言 Web 数据交互格式&#xff0c;属 ECMAScript 标准规范的一个子集。JSON &#xff08;JavaScript Object Notation&#xff0c;JS 对象简谱&#xff09;即 JavaScript 对象表示法…

Docker网络相关操作

文章目录 网络相关操作1 网络模式1.1 bridge模式1.2 host模式1.3 Container网络模式1.4 none模式1.5 overlay网络模式1.6 macvlan网络模式 2 bridge网络2.1 通过link的方式2.2 新建bridge网络 3 none网络4 host网络5 网络命令汇总5.1 查看网络5.2 创建网络5.3 删除网络5.4 查看…

Python 中的==操作符 和 is关键字

Python是一种功能强大的通用编程语言&#xff0c;提供了各种比较值和对象的方法。其中包括操作符和is关键字&#xff0c;它们的用途不同&#xff0c;但由于它们有时可以达到相同的目的&#xff0c;所以经常会被混淆。在本文中&#xff0c;我们将深入研究和is之间的区别&#xf…

过滤器亚马逊审核UL900报告标准

过滤器亚马逊审核UL900防火等级检测标准,要符合ISO17025资质实验室出具的报告才能成功的上架亚马逊平台。 过滤器&#xff08;filter&#xff09;是输送介质管道上不可缺少的一种装置&#xff0c;通常安装在减压阀、泄压阀、定水位阀 ,方工过滤器其它设备的进口端设备。过滤器…

wsl(ubuntu)创建用户

我们打卡ubuntu窗口&#xff0c;如果没有创建用户&#xff0c;那么默认是root用户 用户的增删改查 查 查询所有的用户列表 cat /etc/passwd | cut -d: -f1cat /etc/passwd: 这个命令用于显示 /etc/passwd 文件的内容。/etc/passwd 文件包含了系统上所有用户的基本信息。每一…

Java字符串:构建和操作字符序列的动态工具

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、常用方法1、字符串构造2、String对象的比较Ⅰ、比较是否引用同一个对象Ⅱ、 按照字典序比较 3、转换Ⅰ、数值和字符串的转换…

探索 OceanBase 中图数据的实现

在数据管理和处理的现代环境中&#xff0c;对能够处理复杂数据结构的复杂数据模型和方法的需求从未如此迫切。图数据的出现以其自然直观地表示复杂关系的独特能力&#xff0c;开辟了数据分析的新领域。 虽然 Neo4j 等成熟的图形数据库为处理图形数据提供了强大的解决方案&…

HTML 使用 ruby 给汉字加拼音

使用 ruby 给汉字加拼音 兼容性 使用 ruby 给汉字加拼音 大家有没有遇到过要给汉字头顶上加拼音的需求? 如果有的话, 你是怎么解决的呢? 如果费尽心思, 那么你可能走了很多弯路, 因为 HTML 原生就有这样的标签来帮我们实现类似的需求. <ruby> ruby 本身是「红宝石」…

K8S陈述式资源管理(1)

命令行: kubectl命令行工具 优点: 90%以上的场景都可以满足对资源的增&#xff0c;删&#xff0c;查比较方便&#xff0c;对改不是很友好 缺点:命令比较冗长&#xff0c;复杂&#xff0c;难记声明式 声明式&#xff1a;K8S当中的yaml文件来实现资源管理 GUI&#xff1a;图形…

C#,入门教程(08)——基本数据类型及使用的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(07)——软件项目的源文件与目录结构https://blog.csdn.net/beijinghorn/article/details/124139947 数据类型用于指定数据体&#xff08;DataEntity&#xff0c;包括但不限于类或结构体的属性、变量、常量、函数返回值&#xff09;…

3_并发编程可见性(volatile)之缓存锁内存屏障过程

并发编程可见性volatile 1.背景原来 从下面的程序可以知道main线程把stop修改成false&#xff0c;而在t1线程没有中没有读取到stop值为false&#xff0c;所以导致了t1线程不能够停止。 从而说明stop值在线程t1不可见&#xff0c;解决这个问题在stop变量上添加volatile即可(p…

java spring boot 获取resource目录下的文档

主要代码 String filePath"templates/test.xls" ClassPathResource classPathResource new ClassPathResource(filePath); InputStream inputStream classPathResource.getInputStream();目录 主要目录存放再这 代码案例 public void downloadTemplate( HttpS…

计算机毕业设计 基于SpringBoot的公司资产网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

rhino犀牛怎么导入和调整背景图?

rhino犀牛怎么导入和调整背景图&#xff1f;Rhino建模过程中经常要用到背景图&#xff0c;为了更加方便快捷&#xff0c;我们会直接导入一些图片来当做背景&#xff0c;那么Rhino犀牛如何导入和调整背景图呢&#xff0c;让我们一起来看看吧 打开犀牛软件&#xff0c;进入操作界…

玩转Mysql 二(MySQL的目录结构与表结构)

一路走来,所有遇到的人,帮助过我的、伤害过我的都是朋友,没有一个是敌人。 一、MYSQL目录结构及命令存放路径 1、查看MYSQL数据文件存放路径 mysql> show variables like datadir; 注意:生成环境要提前规划好数据存放目录,存储一般以T为单位闪盘。 2、MYSQL命令存放…

Android两个APP之间跳转+手机图标修改

APP之间跳转 两个APP之间跳转同样使用Intent进行跳转&#xff0c;将需要跳转的APP都下载到手机中&#xff0c;通过主APP调用需要跳转的APP包名进行跳转。 最好在其中加上try-catch语句&#xff0c;当需要跳转的APP不存在时进行错误抓取。 代码如下&#xff1a; Intent mInten…

iview inputNumber有一个默认值1,来看解决方案

iview inputNumber为什么总有一个默认值1&#xff0c;怎么让它为空。 修改编辑没问题&#xff0c;赋值都没问题&#xff0c;但是新增的时候会有默认值1&#xff0c;也没赋值 这种情况你要手动解决&#xff0c;看看当前值有没有被覆盖 我这个问题就是出现覆盖导致的 看代码似乎…

生信技能33 - gnomAD数据库hg19/hg38 VCF文件批量下载脚本

gnomAD数据库下载地址 gnomAD downloads gnomAD v2.1.1数据集包含来自125,748个外显子组和15,708个全基因组的数据,所有这些数据都映射到GRCh 37/hg 19和GRCh 38/hg 38 两个版本的参考序列。 gnomAD数据库hg19与hg39 VCF文件批量下载脚本 download.sh # 获取当前目录路径…

STM32 IAP学习

STM32三种烧录方式 ISP&#xff1a;In System Programming&#xff08;在系统编程&#xff09; 执行芯片厂商的BootLoader程序进入ISP模式&#xff0c;进入ISP模式后&#xff0c;用户可选择官方提供的烧录通信接口&#xff08;如&#xff1a;串口&#xff09;&#xff0c;并配…

机械配件移动商城课程概述

项目介绍 开发准备 任务 开源库介绍 框架搭建 工具类