STM32学习笔记十五:WS2812制作像素游戏屏-飞行射击游戏(5)探索动画之帧动画
本章又是个重要的章节——动画。
动画,本质上时一系列静态的画面连续播放,欺骗人眼产生动画效果。这个原理自打十九世纪电影诞生开始,就从来没变过。
我们的游戏中也需要一些动画效果,比如,被击中时的受伤效果,击毁效果,血包的动画效果等等。这些动画分为两类:连续线性动画、离散的帧动画。
离散动画,就是在指定的时间点,将目标变量设定为特定的值。
连续动画,就是除了两个特定时间之外,通过插值算法为中间帧设定中间值。
这两者的时间轴都应不受系统处理能力的影响,所以,我们又想到了tick。
我们先从简单的开始,先做个帧动画。设定飞机被击中时,变为红色,1秒后恢复,单次动画不重复。
1、先定义一个动画基类:
Animation.h
/** Animation.h** Created on: Dec 25, 2023* Author: YoungMay*/#ifndef SRC_ANICOMP_ANIMATION_H_
#define SRC_ANICOMP_ANIMATION_H_
#include "stdint.h"
#include "../drivers/DList.h"
#include "../drivers/tools.h"typedef struct {uint32_t time;int value;
} AnimationData;class Animation {
public:Animation() {dataList = ListCreate();}virtual ~Animation() {ListDestory(dataList);}void addItem(uint32_t time, int value);virtual int tick(uint32_t t)=0;void start();uint8_t isValid = 0;int *bindAddress = NULL;
protected:ListNode *dataList;uint32_t totalTick;
};#endif /* SRC_ANICOMP_ANIMATION_H_ */
其中
各时间点的数据,保存在链表dataList中。
bindAddress是绑定的数据地址,到了指定时刻,我们就修改它。
2、再定义一个离散动画类:
DispersedAnimation.h
/** DispersedAnimation.h** Created on: Dec 25, 2023* Author: YoungMay*/#ifndef SRC_ANICOMP_DISPERSEDANIMATION_H_
#define SRC_ANICOMP_DISPERSEDANIMATION_H_
#include "Animation.h"class DispersedAnimation: public Animation {
public:DispersedAnimation();~DispersedAnimation();int tick(uint32_t t);};#endif /* SRC_ANICOMP_DISPERSEDANIMATION_H_ */
DispersedAnimation.cpp
/** DispersedAnimation.cpp** Created on: Dec 25, 2023* Author: YoungMay*/#include "DispersedAnimation.h"DispersedAnimation::DispersedAnimation() {// TODO Auto-generated constructor stub}DispersedAnimation::~DispersedAnimation() {// TODO Auto-generated destructor stub
}int DispersedAnimation::tick(uint32_t t) {totalTick += t;if (((AnimationData*) dataList->prev->data)->time < totalTick) {isValid = 0;if (bindAddress != NULL)*bindAddress = ((AnimationData*) dataList->prev->data)->value;return ((AnimationData*) dataList->prev->data)->value;}if (((AnimationData*) dataList->next->data)->time > totalTick) {if (bindAddress != NULL)*bindAddress = ((AnimationData*) dataList->next->data)->value;return ((AnimationData*) dataList->next->data)->value;}ListNode *node = dataList->next;while (((AnimationData*) node->next->data)->time < totalTick) {node = node->next;}if (bindAddress != NULL)*bindAddress = ((AnimationData*) node->data)->value;return ((AnimationData*) node->data)->value;
}
动画类也有tick操作,我们把所有时间间隔都累加到了totalTick里面。
3、再看看怎么使用:
我们先在敌机的基类里面加上动画类 damageAnimation,让每个敌机都具备动画的能力。
class EnemyBase {
public:EnemyBase();virtual ~EnemyBase() {}virtual uint8_t tick(uint32_t t)=0;virtual void init()=0;virtual uint8_t show(void)=0;virtual uint8_t hitDetect(int x, int y)=0;ListNode *enemyBulletList;PlaneObject_t baseInfo;int HP;void hurt() {damageAnimation.start();}
protected:DispersedAnimation damageAnimation;ListNode *animationList;
};
animationList是用于保存所有动画的链表。动画damageAnimation 其实是可以在外层如enemyManager或者plane里面进行定义和注入的,但因为他与敌机强相关且其他类也不会用,所以直接在敌机类里面定义比较满足封装思想。
基类构造类里面完成链表初始化:
EnemyBase::EnemyBase() {baseInfo.x = ran_range(3 * PlaneXYScale, 29 * PlaneXYScale);baseInfo.y = 0;baseInfo.visiable = 1;animationList = ListCreate();ListPushBack(animationList, (LTDataType) &damageAnimation);
}
4、各种敌机本身颜色不一样,所以我们在各种敌机子类的初始化函数中,定义动画需要变得颜色:
void EnemyT1::init() {damageAnimation.addItem(0, 0xa02000);damageAnimation.addItem(1000, 0x208000);damageAnimation.bindAddress = &baseInfo.color;
}
5、最后在敌机的tick函数里面,遍历动画链表:
uint8_t EnemyT1::tick(uint32_t t) {baseInfo.y += t * baseInfo.speed;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;
}
TIPS:由于什么时候执行tick无法确定,可能非常接近的时间点不会执行,直接就跳过了。所以用于做显示的动画可以接受,毕竟跳过就跳过了,显示最终效果即可,但如果用来修改某些影响流程的状态值的话,需要小心一些,需要有足够的时间间隔,确保能tick进去。
同样的方法,我们再加上其他敌机类型的受伤效果,玩家被击中的效果等,不再累述。
飞机被击毁时,直接消失不见了,这不太合适,所以我们再给它加个击毁的动画。可以用类似前面焰火程序做个爆炸开来的样子。
还是用帧动画。
1、添加爆炸的动画explodeAnimation属性,添加爆炸阶段状态码explodeState。
class EnemyBase {
public:EnemyBase();virtual ~EnemyBase() {}virtual uint8_t tick(uint32_t t)=0;virtual void init()=0;virtual uint8_t show(void)=0;virtual uint8_t hitDetect(int x, int y, int damage)=0;ListNode *enemyBulletList;PlaneObject_t baseInfo;int HP;protected:DispersedAnimation damageAnimation;DispersedAnimation explodeAnimation;ListNode *animationList;int explodeState = 0;
};
2、给explodeAnimation灌入数据
void EnemyT1::init() {damageAnimation.addItem(0, 0xa02000);damageAnimation.addItem(1000, 0x208000);explodeAnimation.addItem(0, 1);explodeAnimation.addItem(200, 2);explodeAnimation.addItem(400, 3);explodeAnimation.addItem(600, 4);explodeAnimation.addItem(800, 100);}
3、根据状态码explodeState显示不同的爆炸形态
const int8_t Explode_X[] = { -1, 0, 1, 1, 1, 0, -1, -1 };
const int8_t Explode_Y[] = { -1, -1, -1, 0, 1, 1, 1, 0 };uint8_t EnemyT1::show(void) {if (explodeState) {for (uint8_t j = 0; j < 8; j++) {ws2812_pixel(baseInfo.x / PlaneXYScale + Explode_X[j] * explodeState,baseInfo.y / PlaneXYScale + Explode_Y[j] * explodeState,240, 20, 0);}if (explodeState == 100) {baseInfo.visiable = 0;}} else {for (uint8_t y = 0; y < baseInfo.height; y++) {for (uint8_t x = 0; x < baseInfo.width; x++) {if (sharp[y][x])ws2812_pixel(x + baseInfo.x / PlaneXYScale - baseInfo.width / 2,y + baseInfo.y / PlaneXYScale - baseInfo.height / 2,(baseInfo.color >> 16) & 0xff,(baseInfo.color >> 8) & 0xff,baseInfo.color & 0xff);}}}return 0;
}
4、原来血量为0时就直接消失了,现在还要再保留一下显示爆炸,而且这段时间也不能再动了。所以,对原来消失的和tick的逻辑做点小改动。
uint8_t EnemyT1::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();}for (ListNode *node = animationList->next; node != animationList; node =node->next) {if (((Animation*) node->data)->isValid) {((Animation*) node->data)->tick(t);}}return 0;
}
uint8_t EnemyT1::hitDetect(int x, int y, int damage) {if (explodeState)return 0;int a = (x - baseInfo.x) / 100;int b = (y - baseInfo.y) / 100;int c = 180; // 1.5 * 10000 / 100 // 碰撞圈子略大一点,uint8_t res = (a * a + b * b < c * c) ? 1 : 0;if (res) {HP -= damage;if (HP < 0) {explodeState = 1;explodeAnimation.start();} elsedamageAnimation.start();}return res;
}
嗯,还有补充T2和T3的爆炸效果。T2炸的范围更大一点,而T3可以爆出两朵大花。
好了,我们看看效果:
STM32学习笔记十五:WS2812制作像素游戏屏-飞行射击
STM32学习笔记十六:WS2812制作像素游戏屏-飞行射击游戏(6)探索动画之插值动画
相关文章:
STM32学习笔记十五:WS2812制作像素游戏屏-飞行射击游戏(5)探索动画之帧动画
本章又是个重要的章节——动画。 动画,本质上时一系列静态的画面连续播放,欺骗人眼产生动画效果。这个原理自打十九世纪电影诞生开始,就从来没变过。 我们的游戏中也需要一些动画效果,比如,被击中时的受伤效果&#…...
期末复习(程序设计)
根据字符出现频率排序 【问题描述】 给定一个字符串 s ,根据字符出现的 频率 对其进行降序排序。一个字符出现的频率是它出现在字符串中的次数。 返回已排序的字符串。 频率相同的的字符按ascii值降序排序。 s不包含空格、制表符、换行符等特殊字符。 【输入格…...
html-css-js移动端导航栏底部固定+i18n国际化全局
需求:要做一个移动端的仿照小程序的导航栏页面操作,但是这边加上了i18n国家化,由于页面切换的时候会导致国际化失效,所以写了这篇文章 1.效果 切换页面的时候中英文也会跟着改变,不会导致切换后回到默认的语言 2.实现…...
Ubuntu Linux 入门指南:面向初学者
目录 1. Ubuntu Linux 简介 Ubuntu 的由来 Ubuntu 与其他 Linux 发行版的比较 Debian: Fedora: openSUSE: Arch Linux: Linux Mint: 第二部分:安装 Ubuntu 1. 准备安装 系统需求 创建 Ubuntu 启…...
常见算法面试题目
前言 总结一些常见的算法题目,每一个题目写一行思路,方便大家复习。具体题目的来源是下面的网站。 剑指offer 剑指offe2 leetcode200题 leetcode 100题 leetcode150题 leetcode 75题 文章目录 前言二叉树非递归遍历牛客JZ31 栈的压入、弹出序列 (…...
PiflowX组件-JDBCWrite
JDBCWrite组件 组件说明 使用JDBC驱动向任意类型的关系型数据库写入数据。 计算引擎 flink 有界性 Sink: Batch Sink: Streaming Append & Upsert Mode 组件分组 Jdbc 端口 Inport:默认端口 outport:默认端口 组件属性 名称展示名称默…...
算法导论复习题目
这题需要考虑什么呢? 一换元,二要使用主方法猜出结果,三是证明的时候添加一个低阶项来消除 LC检索 C(x)是从上帝视角来看的成本 对C(x)的一个估计: 由两个部分组成,就相当于由以往的经验对未来…...
HTTPS协议详解
目录 前言 一、HTTPS协议 1、加密是什么 2、为什么要加密 二、常见加密方式 1、对称加密 2、非对称加密 三、数据摘要与数据指纹 1、数据摘要 2、数据指纹 四、HTTPS加密策略探究 1、只使用对称加密 2、只使用非对称加密 3、双方都使用非对称加密 4、对称加密非…...
菜鸟学习vue3笔记-vue3 router回顾
1、路由router pnpm i vue-router2、创建使用环境 1.src下创建 router文件夹、里面创建index.ts文件 //创建一个路由暴露出去//1.引入createRouter import { createRouter, createWebHistory } from "vue-router";// import Home from ../components/Home.vue//…...
Mybatis枚举类型处理和类型处理器
专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…...
2023 NCTF writeup
CRYPTO Sign 直接给了fx,gx,等于私钥给了,直接套代码,具体可以参考: https://0xffff.one/d/1424 fx [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…...
golang的大杀器协程goroutine
在Golang中,协程(Goroutine)是轻量级的执行单元,用于实现并发编程。它是Golang语言的重要组成部分,提供了简洁、高效的方式来处理并发任务。 特点: 1)轻量级:Go语言的协程是轻量级…...
[Angular] 笔记 9:list/detail 页面以及@Output
1. Output input 好比重力,向下传递数据,list 传给 detail,smart 组件传给 dumb 组件,父组件传给子组件。input 顾名思义,输入数据给组件。 output 与之相反,好比火箭,向上传递数据或事件。ou…...
Linux学习笔记(一)
如果有自己的物理服务器请先查看这篇文章 文章目录 网卡配置Linux基础指令ls:列出目录内容cd(mkdir.rmkdir): 切换文件夹(创建,删除操作)cp:复制文件或目录mv:文件/文件夹移动cat:查看文件vi:文件查看编辑man:查看命令手册more: 查看文件内容less : 查看文件内容 ps: 显示当前进…...
Python 爬虫 教程
python爬虫框架:Scrapyd,Feapder,Gerapy 参考文章: python爬虫工程师,如何从零开始部署ScrapydFeapderGerapy? - 知乎 神器!五分钟完成大型爬虫项目 - 知乎 爬虫框架-feapder - 知乎 scrap…...
uniapp原生插件 - android原生插件打包流程 ( 避坑指南一)
【彩带- 避坑知识点】: 当时开发中安卓插件打包成功后,uniapp引用插件aar,用云打包 ,总是提示不包含插件。原因是因为module的androidManifest.xml文件没有注册activity。 这一步 很重要,一定要注册。 --------------------------…...
搭建maven私服
maven maven简介 什么是maven? Maven这个单词来自于意第绪语(犹太语),意为知识的积累。 Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具软件。 Maven 除了以…...
EST-100身份证社保卡签批屏按捺终端PC版web版本http协议接口文档,支持web网页开发对接使用
<!DOCTYPE html><html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width,initial-scale1.0"><title>演示DEMO</title><script type"text/…...
基于SpringBoot的毕业论文管理系统
文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的毕业论文管理系统,java…...
iToF人脸识别
iToF(间接飞行时间)是一种测量光飞行时间的技术,主要应用于人脸识别。 iToF人脸识别技术在哪些场景下会用到 iToF人脸识别技术可以应用于许多场景,以下是一些常见的应用场景: 平安城市:在城市监控系统中,iToF人脸识别技术可以用于实时监控、目标检测和识别,以及异常行为…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
