STM32学习笔记十七:WS2812制作像素游戏屏-飞行射击游戏(7)探索动画之故事板,复杂动画

news2024/9/22 18:32:05

要让物体沿着路径移动,必须同时修改X/Y两个值,用两个连续插值动画行不行?

在单片机这种单线程设备,两个TICK会前后脚进行修改,具有相同的时间跨度,所以似乎也是可以的。但是在支持多线程的设备,各动画对象的tick就不一定了。显示时可能会偏离路径。另外我们可能还会有更复杂的动画,比如,我们的任务要奔跑,那需要同时修改XY,同时人物奔跑时还有手脚动作的帧动画,可能显示血条的单次动画,等等等等,这些东西要是分开做的话,其同步可能会令人崩溃。

所以,我们需要一个播放组合型动画的能力。这里,我们借用 C# 的故事板概念,实现类似功能:

AnimationGroup 之间串行处理

AnimationGroup 内部的Animation 并行处理

当然,我们不可能完整实现C# 的故事板,只是最核心的最精简的能力。尽可能封装好,将来可移植复用即可。

1、定义动画组 AnimationGroup

AnimationGroup.h

/*
 * AnimationGroup.h
 *
 *  Created on: Dec 26, 2023
 *      Author: YoungMay
 */

#ifndef SRC_ANICOMP_ANIMATIONGROUP_H_
#define SRC_ANICOMP_ANIMATIONGROUP_H_
#include "Animation.h"

class AnimationGroup {
public:
	AnimationGroup();
	virtual ~AnimationGroup();

	void addItem(Animation *animation);
	void tick(uint32_t t);
	void start();
	uint8_t isValid = 0;
	uint32_t repeat = 1;
	uint8_t id;
private:
	ListNode *animationList;
};

#endif /* SRC_ANICOMP_ANIMATIONGROUP_H_ */

AnimationGroup.cpp

/*
 * AnimationGroup.cpp
 *
 *  Created on: Dec 26, 2023
 *      Author: YoungMay
 */

#include "AnimationGroup.h"

AnimationGroup::AnimationGroup() {
	animationList = ListCreate();

}

AnimationGroup::~AnimationGroup() {
	for (ListNode *node = animationList->next; node != animationList; node =
			node->next) {
		delete (Animation*) node->data;
	}
	ListDestory(animationList);
}

void AnimationGroup::addItem(Animation *animation) {
	ListPushBack(animationList, (LTDataType) animation);
}
void AnimationGroup::tick(uint32_t t) {
	isValid = 0;
	for (ListNode *node = animationList->next; node != animationList; node =
			node->next) {
		if (((Animation*) node->data)->isValid) {
			((Animation*) node->data)->tick(t);
		}
		isValid = isValid | ((Animation*) node->data)->isValid;
	}
}
void AnimationGroup::start() {
	isValid = 1;
	for (ListNode *node = animationList->next; node != animationList; node =
			node->next) {
		((Animation*) node->data)->start();
	}
}

注意tick里面的处理,每个animation都能执行到。所有animation结束后,该group结束。

2、定义故事板 AnimationStoryBoard

AnimationStoryBoard.h

/*
 * AnimationStoryBoard.h
 *
 *  Created on: Dec 26, 2023
 *      Author: YoungMay
 */

#ifndef SRC_ANICOMP_ANIMATIONSTORYBOARD_H_
#define SRC_ANICOMP_ANIMATIONSTORYBOARD_H_
#include "AnimationGroup.h"
class AnimationStoryBoard {
public:
	AnimationStoryBoard();
	virtual ~AnimationStoryBoard();

	void addItem(AnimationGroup *group);
	void tick(uint32_t t);
	void start();
	uint8_t isValid = 0;
private:
	ListNode *animationGroupList;
};

#endif /* SRC_ANICOMP_ANIMATIONSTORYBOARD_H_ */

AnimationStoryBoard.cpp

/*
 * AnimationStoryBoard.cpp
 *
 *  Created on: Dec 26, 2023
 *      Author: YoungMay
 */

#include "AnimationStoryBoard.h"

AnimationStoryBoard::AnimationStoryBoard() {
	animationGroupList = ListCreate();
}

AnimationStoryBoard::~AnimationStoryBoard() {
	for (ListNode *node = animationGroupList->next; node != animationGroupList;
			node = node->next) {
		delete (AnimationGroup*) node->data;
	}
	ListDestory(animationGroupList);
}

void AnimationStoryBoard::addItem(AnimationGroup *group) {
	ListPushBack(animationGroupList, (LTDataType) group);
}
void AnimationStoryBoard::tick(uint32_t t) {
	if (!isValid)
		return;
	isValid = 0;
	for (ListNode *node = animationGroupList->next; node != animationGroupList;
			node = node->next) {
		AnimationGroup *group = (AnimationGroup*) node->data;
		if (group->isValid) {
			group->tick(t);
			isValid = 1;
			if ((!group->isValid) && group->repeat > 1) {
				group->repeat--;
				group->start();
			}
			return;
		}
	}
}
void AnimationStoryBoard::start() {
	isValid = 1;
	for (ListNode *node = animationGroupList->next; node != animationGroupList;
			node = node->next) {
		((AnimationGroup*) node->data)->start();
	}
}

注意tick里面的处理,只处理第一个未结束的animationGroup。

好了,现在可以设置BOSS的飞行路径。

 分为两个group.先从屏幕外面飞下来,然后绕8字形循环飞行。

1、修改BOSS,添加一个故事板 animationStoryBoard

class EnemyT3: public EnemyBase {
public:
	EnemyT3();
	~EnemyT3();
	uint8_t tick(uint32_t t);
	void init();
	uint8_t show(void);
	uint8_t hitDetect(int x, int y, int damage);
	bool sharp[5][9] = { { 0, 0, 1, 1, 1, 1, 1, 0, 0, }, { 0, 0, 0, 0, 1, 0, 0,
			0, 0, }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, },
			{ 0, 0, 1, 0, 1, 0, 1, 0, 0, }, { 0, 0, 0, 0, 1, 0, 0, 0, 0, } };
private:
	AnimationStoryBoard *animationStoryBoard;
	IntervalAniTimer_t fireTimer = { 2000, 6000 };
	void createBulletObject();
};

 2、在BOSS初始化 init 函数中灌入数据:


void EnemyT3::init() {
	damageAnimation.addItem(0, 0xa02000);
	damageAnimation.addItem(1000, 0x604000);

	explodeAnimation.addItem(0, 1);
	explodeAnimation.addItem(200, 2);
	explodeAnimation.addItem(400, 3);
	explodeAnimation.addItem(600, 4);
	explodeAnimation.addItem(800, 5);
	explodeAnimation.addItem(1000, 6);
	explodeAnimation.addItem(1200, 7);
	explodeAnimation.addItem(1400, 100);

	animationStoryBoard = new AnimationStoryBoard();
	AnimationGroup *group1 = new AnimationGroup();

	ContinuousAnimation *ani1X = new ContinuousAnimation();
	ContinuousAnimation *ani1Y = new ContinuousAnimation();
	ani1X->bindAddress = &baseInfo.x;
	ani1Y->bindAddress = &baseInfo.y;
	group1->addItem(ani1X);
	group1->addItem(ani1Y);
	ani1X->addItem(0, 20 * PlaneXYScale);
	ani1Y->addItem(0, 0 * PlaneXYScale);
	ani1X->addItem(2000, 20 * PlaneXYScale);
	ani1Y->addItem(2000, 10 * PlaneXYScale);
	animationStoryBoard->addItem(group1);

	AnimationGroup *group2 = new AnimationGroup();
	ContinuousAnimation *ani2X = new ContinuousAnimation();
	ContinuousAnimation *ani2Y = new ContinuousAnimation();
	ani2X->bindAddress = &baseInfo.x;
	ani2Y->bindAddress = &baseInfo.y;
	group2->addItem(ani2X);
	group2->addItem(ani2Y);
	ani2X->addItem(0, 20 * PlaneXYScale);
	ani2Y->addItem(0, 10 * PlaneXYScale);
	ani2X->addItem(5658, 12 * PlaneXYScale);
	ani2Y->addItem(5658, 15 * PlaneXYScale);
	ani2X->addItem(8658, 7 * PlaneXYScale);
	ani2Y->addItem(8658, 15 * PlaneXYScale);
	ani2X->addItem(10356, 5 * PlaneXYScale);
	ani2Y->addItem(10356, 16 * PlaneXYScale);
	ani2X->addItem(10956, 5 * PlaneXYScale);
	ani2Y->addItem(10956, 14 * PlaneXYScale);
	ani2X->addItem(12654, 7 * PlaneXYScale);
	ani2Y->addItem(12654, 10 * PlaneXYScale);
	ani2X->addItem(15654, 12 * PlaneXYScale);
	ani2Y->addItem(15654, 10 * PlaneXYScale);
	ani2X->addItem(21312, 20 * PlaneXYScale);
	ani2Y->addItem(21312, 15 * PlaneXYScale);
	ani2X->addItem(24312, 25 * PlaneXYScale);
	ani2Y->addItem(24312, 15 * PlaneXYScale);
	ani2X->addItem(26010, 27 * PlaneXYScale);
	ani2Y->addItem(26010, 16 * PlaneXYScale);
	ani2X->addItem(26610, 27 * PlaneXYScale);
	ani2Y->addItem(26610, 14 * PlaneXYScale);
	ani2X->addItem(28308, 25 * PlaneXYScale);
	ani2Y->addItem(28308, 10 * PlaneXYScale);
	ani2X->addItem(31308, 20 * PlaneXYScale);
	ani2Y->addItem(31308, 10 * PlaneXYScale);
	group2->repeat = 1000000;
	animationStoryBoard->addItem(group2);
	animationStoryBoard->start();
}

3、最后在BOSS的tick里面加上故事板的调用。

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

好了。看看效果:

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

故事板功能可以做得非常强大,通过各种animation和group的组合,可以实现复杂的动画。还可以自行扩展各种group的触发条件,如前置后置条件,同步异步等。

大致思路如此,要做成什么样就看需求了。

STM32学习笔记十八:WS2812制作像素游戏屏-飞行射击游戏(8)探索游戏多样性,范围伤害模式

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CVPR2023】使用轻量 ToF 传感器的单目密集SLAM的多模态神经辐射场

目录 导读 本文贡献 本文方法 轻量级ToF传感器的感知原理 多模态隐式场景表示 时间滤波技术 实验 实验结果 消融实验 结论 未来工作 论文标题:Multi-Modal Neural Radiance Field for Monocular Dense SLAM with a Light-Weight ToF Sensor 论文链接&am…

Java程序设计阶段测试1

一、单选题(共15题; 共30.0分) 2.0分 1、以下哪个是Java应用程序main方法的有效定义? A.public static void main(); B.public static void main( String args ); C.public static void main( String args[] ); D.public static boolea…

交换机02_共享式交换式

1、共享式网络 早期的以太网是共享式网络,它是由集线器(HUB)相连,由一个HUB相连了两台主机,形成一个冲突域也称广播域。 (1)相关名词解释 集线器 HUB中心的意思,集线器就是对接收…

VINS-MONO拓展1----手写后端求解器,LM3种阻尼因子策略,DogLeg,构建Hessian矩阵

文章目录 0. 目标及思路1. 非线性优化求解器2. 基于VINS-MONO的Marginalization框架构建Hessian矩阵2.1 estimator.cpp移植2.2 solve.cpp/preMakeHessian()2.3 solve.cpp/makeHessian() 3. solve.cpp/solveLinearSystem()求解正规方程4. 更新状态5. 迭代求解6. EVO评估结果7. 待…

drf知识--10

接口文档 # 后端把接口写好后: 登录接口:/api/v1/login ---> post---name pwd 注册接口 查询所有图书带过滤接口 # 前后端需要做对接,对接第一个东西就是这个接口文档,前端照着接口文档开发 公司3个人&#xff…

LaTeX语法、工具及模板大全(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 我之前把Markdown和LaTeX的语法写在一个博文里了,但是现在感觉还是应该拆开来比较合适,因为LaTeX太复杂了…… LaTex核心其实是套模板,但是为了套好模版,也需要学习一些具体的语法。 文章目录 1.…

14 简约登录页

效果演示 实现了一个简单的登录表单的样式,包括背景颜色、边框、字体颜色、字体大小、字体粗细、输入框样式、提交按钮样式等。当用户在输入框中输入内容时,输入框下方的提示文字会动态地变化,以提示用户输入正确的信息。当用户点击提交按钮时…

广播及代码实现

广播(Broadcast)是一种网络通信方式,它允许一台设备向网络中的所有其他设备发送消息。广播通常用于在网络上传递一些信息,让所有设备都能接收并处理。在广播中,通信的目标是整个网络而不是特定的单个设备。 向子网中…

电风扇目标检测数据集VOC格式1100张

电风扇的全方位介绍 一、功能特性 电风扇作为一种晋及化的家用电器,其主要功能是利用电机驱动扇叶旋转,从而产生风力,用干调节室内空气流通,达至降温、通风和改善室内环境的目的。此外,现代电风扇还具备定时、遥控、…

踩坑记录-安装nuxt3报错:Error: Failed to download template from registry: fetch failed;

报错复现 安装nuxt3报错:Error: Failed to download template from registry: fetch failednpx nuxi init nuxt-demo 初始化nuxt 项目 报错 Error: Failed to download template from registry: fetch faile 解决方法 配置hosts Mac电脑:/etc/hostswin电…

vue本地打包预览

1、项目打包 npm run build2、安装serve npm install -g serve3、在项目的 dist 文件运行命令行 serve 4、运行如下在浏览器打开即可

游戏用代理IP怎么检查是否有效?哪些因素会影响代理IP的质量?

随着网络游戏的普及,越来越多的玩家选择使用代理IP来提升游戏体验。然而,在使用代理IP的过程中,玩家们可能会遇到一些问题,其中最关键的就是如何检查代理IP是否有效以及哪些因素会影响代理IP的质量。本文将详细介绍这些问题&#…

R306指纹识别模块功能实现示例

1 基本通信流程 1.1 UART 命令包的处理过程 1.2 UART 数据包的发送过程 UART 传输数据包前,首先要接收到传输数据包的指令包,做好传输准备后发送成功应答包,最后才开始传输数据包。数据包主要包括:包头、设备地址、包标识、包长…

2024年【浙江省安全员-C证】模拟考试及浙江省安全员-C证证考试

题库来源:安全生产模拟考试一点通公众号小程序 浙江省安全员-C证模拟考试是安全生产模拟考试一点通生成的,浙江省安全员-C证证模拟考试题库是根据浙江省安全员-C证最新版教材汇编出浙江省安全员-C证仿真模拟考试。2024年【浙江省安全员-C证】模拟考试及…

Jenkins持续集成(上篇)

(一)持续集成与 Jenkins 介绍 持续集成 持续集成(Continuous integration,简称 CI),随着近几年的发展,持续集成在项目中得到了广泛的推广和应用。本章将带领读者一起了解持续集成工具 Jenkins…

jdk和IDEA教育版下载和安装详解

前言 研究生专业是通信系统,为了寻找实习于是在研二时期学习java。但是在学习java的过程中没有进行系统总结,很多知识点或者一些细节已经忘记。由于工作找的是某行软件中心的软件开发。准备在毕业前对java知识进行系统性学习。本专栏将从零基础开始,从最简单的jdk和IDEA下载…

阿赵UE学习笔记——6、免费资源获取

阿赵UE学习笔记目录 大家好,我是阿赵。   接下来准备要往UE引擎里面放美术资源了。美术资源可以自己做,不过也有一些免费的资源可以供我们使用的,这里介绍一些获得免费美术资源的方法。 一、Quixel 1、Quixel网站下载 Quixel资源库&#…

高效管理版本控制,Cornerstone 4 for Mac助您成为SVN专家

在软件开发和团队合作中,版本控制是一个至关重要的环节。为了帮助开发者更加高效地管理和控制代码版本,Cornerstone 4 for Mac应运而生。作为一款功能强大的SVN(Subversion)管理工具,Cornerstone 4 for Mac为Mac用户提…

动手学深度学习一:环境安装与数据学习

2024,重新开始深度学习。 第一步:李沐动手学深度学习 课程网址:https://courses.d2l.ai/zh-v2/ 包含教材和视频网址链接 Jupyter notebook安装 目前在本地先使用cpu版本pytorch,我的本地已经安装好conda,跟着教材创建…

什么是自动化测试?为啥要学自动化测试?

什么是自动化测试,接着对常用的自动化测试框架进行了对比分析,最后,介绍了如果将自动化测试框架Cypress运用在项目中。 一、自动化测试概述 为了保障软件质量,并减少重复性的测试工作,自动化测试已经被广泛运用。在开…