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

要让物体沿着路径移动,必须同时修改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.hqwc.cn/news/318170.html

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

相关文章

AI大模型引领未来智慧科研暨ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮,可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

FairGuard游戏加固产品常见问题解答

针对日常对接中,各位用户对FairGuard游戏加固方案在安全性、稳定性、易用性、接入流程等方面的关注,我们梳理了相关问题与解答,希望可以让您对产品有一个初步的认知与认可。 Q1:FairGuard游戏加固产品都有哪些功能? A:FairGuar…

pytorch04:网络模型创建

目录 一、模型创建过程1.1 以LeNet网络为例1.2 LeNet结构1.3 nn.Module 二、网络层容器(Containers)2.1 nn.Sequential2.1.1 常规方法实现2.1.2 OrderedDict方法实现 2.2 nn.ModuleList2.3 nn.ModuleDict2.4 三种容器构建总结 三、AlexNet网络构建 一、模型创建过程 1.1 以LeNe…

【力扣100】39.组合总和

添加链接描述 class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:def backtrack(path,target,res,index):if target0:res.append(path[:])returnif target<0:return for i in range(index,len(candidates)):if target&g…

Java虚拟机介绍

JVM是一种用于计算设备的规范&#xff0c;它是一个虚拟出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟计算机的各个功能来实现的。Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。每个Java虚拟机都着一个清晰的任务&#x…

轻量级性能测试工具 wrk 如何使用?

项目设计之初或者是项目快要结束的时候&#xff0c;大佬就会问我们&#xff0c;这个服务性能测试的结果是什么&#xff0c;QPS 可以达到多少&#xff0c;RPS 又能达到多少&#xff1f;接口性能可以满足未来生产环境的实际情况吗&#xff1f;有没有自己测试过自己接口的吞吐量&a…

阿里云服务器云盘ESSD Entry、SSD、高效云盘性能测评

阿里云服务器系统盘或数据盘支持多种云盘类型&#xff0c;如高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘、ESSD PL-X云盘及ESSD AutoPL云盘等&#xff0c;阿里云百科aliyunbaike.com详细介绍不同云盘说明及单盘容量、最大/最小IOPS、最大/最小吞吐量、单路随机写平均时延等性…

三菱plc的点动控制循环(小灯闪烁,单控气缸循环)

以为前一段时间小编做了一个气缸定时循环的程序&#xff0c;根据程序有不足之处&#xff0c;所以小编写下这篇文章&#xff0c;将网络上的plc小灯控制进行总结&#xff01;如果对你有帮助&#xff0c;不要忘了点赞收藏&#xff01;如果有更加好的梯形图&#xff0c;欢迎评论&am…

Redis(一)

1、redis Redis是一个完全开源免费的高性能&#xff08;NOSQL&#xff09;的key-value数据库。它遵守BSD协议&#xff0c;使用ANSI C语言编写&#xff0c;并支持网络和持久化。Redis拥有极高的性能&#xff0c;每秒可以进行11万次的读取操作和8.1万次的写入操作。它支持丰富的数…

WEB:探索开源OFD.js技术应用

1、简述 OFD.js 是一个由开源社区维护的 JavaScript 库&#xff0c;专注于在浏览器中渲染和处理 OFD 文件。OFD 作为一种开放式的文档格式&#xff0c;被广泛应用于电子政务、电子合同等领域。OFD.js 的出现为开发者提供了一个强大的工具&#xff0c;使得在前端实现 OFD 文件的…

阿里云系统盘测评ESSD、SSD和高效云盘IOPS、吞吐量性能参数表

阿里云服务器系统盘或数据盘支持多种云盘类型&#xff0c;如高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘、ESSD PL-X云盘及ESSD AutoPL云盘等&#xff0c;阿里云百科aliyunbaike.com详细介绍不同云盘说明及单盘容量、最大/最小IOPS、最大/最小吞吐量、单路随机写平均时延等性…

用Audio2Face驱动UE - MetaHuman

新的一年咯&#xff0c;很久没发博客了&#xff0c;就发两篇最近的研究吧。 开始之前说句话&#xff0c;别轻易保存任何内容&#xff0c;尤其是程序员不要轻易Ctrl S 在UE中配置Audio2Face 先检查自身电脑配置看是否满足&#xff0c;按最小配置再带个UE可能会随时崩&#x…