STM32学习笔记十二:WS2812制作像素游戏屏-飞行射击游戏(2)探索时间间隔同步,双向链表

本文主要是介绍STM32学习笔记十二:WS2812制作像素游戏屏-飞行射击游戏(2)探索时间间隔同步,双向链表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上章我们做好了空间的比例尺,不至于物体定位出错。这次我们尝试一下时间间隔的同步。当然,游戏中需要同步时间的地方很多,这里仅仅涉及很小的一个点。

我们已经创造了玩家飞机,是时候让它能发射子弹了。

发射子弹,哪怕是密集如加特林,也需要有一个发射间隔。这个间隔如何做?显然是不可能用Hal_delay之类的等待函数。实际上,整个代码中都不会出现等待函数。假设我们需要保证每个玩家的每个子弹间隔都是400ms,同时还要考虑两个玩家并不是同时发射子弹,他们的间隔是独立的。另一方面,敌机也应该能发射子弹。

我们前面章节定义了tick入口函数,入参中携带了运行间隔时间。我们可以利用这个,为每种间隔定义自己的定时器。

typedef struct {uint32_t lastTick = 0;uint32_t defaultSpan = 100;uint8_t tick(uint32_t tick) {if (lastTick > tick) {lastTick -= tick;return 0;} else {lastTick = defaultSpan;return 1;}}
} IntervalAniTimer_t;

对象检查定时器是否到时间,如果没到时间,那该干嘛干嘛,如果已经到达时间了,那就干点其他啥。

现在可以在玩家属性了面加上这个间隔了。

private:IntervalAniTimer_t fireTimer = { 0, 400 };

插播:在实现发射子弹之前,我们要考虑子弹的数据结构。

子弹数据有几个特点:

1、数量不确定。

2、频繁的增删操作。

3、似乎没有随机访问的场景。

所以,使用链表比使用数组更合适。

没有现成的库,那就参考网上别人家的,手锤一个双向链表。

DList.h

#ifndef __SLIST_H__
#define __SLIST_H__#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "stdint.h"#ifdef __cplusplus
extern "C" {
#endiftypedef intptr_t LTDataType;
typedef struct ListNode {LTDataType data;struct ListNode *next;struct ListNode *prev;
} ListNode;//创造节点
ListNode* BuyLTNode(LTDataType x);
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode *pHead);
// 双向链表打印
void ListPrint(ListNode *pHead, void (*callback)(LTDataType x));
// 双向链表尾插
void ListPushBack(ListNode *pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode *pHead);
// 双向链表头插
void ListPushFront(ListNode *pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode *pHead);
// 双向链表查找
ListNode* ListFind(ListNode *pHead, LTDataType x);// 双向链表查找
ListNode* ListFindItem(ListNode *pHead, LTDataType y,uint8_t (*callback)(LTDataType x, LTDataType y));// 双向链表在pos的前面进行插入
void ListInsert(ListNode *pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode *pos);uint16_t ListCount(ListNode *pHead);ListNode* ListGetNodeAt(ListNode *pHead, LTDataType idx);#ifdef __cplusplus
}
#endif#endif

DList.c

#include "DList.h"
// 创建返回链表的头结点.
ListNode* ListCreate() {ListNode *head = BuyLTNode(0);head->next = head; //循环列表创建头时头的首尾都指向自己head->prev = head;return head;
}
//创造节点
ListNode* BuyLTNode(LTDataType x) {ListNode *cur = (ListNode*) malloc(sizeof(ListNode));if (cur == NULL) {perror("malloc");exit(-1);}cur->data = x;return cur;}
// 双向链表打印
void ListPrint(ListNode *pHead, void (*callback)(LTDataType x)) {assert(pHead);ListNode *cur = pHead->next;if (callback) {while (cur != pHead) {(*callback)(cur->data);cur = cur->next;}} else {while (cur != pHead) {printf("%d->", cur->data);cur = cur->next;}printf("head\n");}
}
// 双向链表尾插
void ListPushBack(ListNode *pHead, LTDataType x) {assert(pHead);ListNode *newnode = BuyLTNode(x);newnode->prev = pHead->prev; //要尾插的节点的prev指向原来的尾节点newnode->next = pHead; //要尾插的节点的next指向头pHead->prev->next = newnode; //原来的尾节点的next指向新尾pHead->prev = newnode; //头的prev指向新尾}
// 双向链表尾删
void ListPopBack(ListNode *pHead) {assert(pHead);assert(pHead->next != pHead);ListNode *tail = pHead->prev; //用一个指针保存尾巴tail->prev->next = pHead; //将倒数第二个节点的next指向头pHead->prev = tail->prev; //头节点的prev指向倒数第二节点free(tail);}
// 双向链表头插
void ListPushFront(ListNode *pHead, LTDataType x) {assert(pHead);ListNode *newnode = BuyLTNode(x);newnode->next = pHead->next; //新空间的next指向原来的第一个数据newnode->prev = pHead; //新空间的prev指向头pHead->next->prev = newnode; //原来的的一个数据的prev指向newnodepHead->next = newnode; //头的next指向newnode
}
// 双向链表头删
void ListPopFront(ListNode *pHead) {assert(pHead);assert(pHead->next != pHead); //先判断链表中除了头有无其他数据ListNode *oldnode = pHead->next; //将要删除的数据的位置保存起来,以防后面丢失pHead->next = oldnode->next; //头的next指向第二个数据oldnode->next->prev = pHead; //第二个数据的prev指向头free(oldnode); //释放数据空间即可
}
// 双向链表查找
ListNode* ListFind(ListNode *pHead, LTDataType x) {if (pHead == NULL)return NULL;ListNode *cur = pHead->next;while (cur != pHead) {if (cur->data == x) {return cur;}cur = cur->next;}return NULL;
}ListNode* ListFindItem(ListNode *pHead, LTDataType y,uint8_t (*callback)(LTDataType x, LTDataType y)) {if (pHead == NULL)return NULL;ListNode *cur = pHead->next;while (cur != pHead) {if ((*callback)(cur->data, y)) {return cur;}cur = cur->next;}return NULL;
}// 双向链表在pos的前面进行插入
void ListInsert(ListNode *pos, LTDataType x) {assert(pos);//调整pos newnode pos前面的数据这三个空间的prev和next即可ListNode *newnode = BuyLTNode(x);ListNode *prev = pos->prev;prev->next = newnode;newnode->prev = prev;newnode->next = pos;pos->prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode *pos) {assert(pos);ListNode *prev = pos->prev;ListNode *next = pos->next;free(pos);prev->next = next;next->prev = prev;}
// 双向链表销毁
void ListDestory(ListNode *pHead) {if (pHead == NULL)return;ListNode *cur = pHead->next;if (cur == NULL)return;ListNode *next = cur->next;while (cur != pHead) //先释放除头以外的所有节点,再释放头{free(cur);cur = next;next = next->next;}free(cur);
}uint16_t ListCount(ListNode *pHead) {if (pHead == NULL)return 0;uint16_t c = 0;ListNode *cur = pHead->next;while (cur != pHead) {c++;cur = cur->next;}return c;
}ListNode* ListGetNodeAt(ListNode *pHead, LTDataType idx) {ListNode *cur = pHead->next;for (uint16_t i = 0; i < idx; i++)cur = cur->next;return cur;
}

可以在玩家数据里面添加子弹的数据了:

class PlanePlayer {
public:PlanePlayer();~PlanePlayer();void init(uint8_t id);uint8_t tick(uint32_t t, uint8_t b1);uint8_t show(void);PlaneObject_t baseInfo;ListNode *bulletList;
private:IntervalAniTimer_t fireTimer = { 0, 400 };};

然后在玩家的tick里面加上发射子弹的判断:

uint8_t PlanePlayer::tick(uint32_t t, uint8_t b1) {if (b1 & KEY_DOWN) {baseInfo.y += baseInfo.speed * t;if (baseInfo.y > 62 * PlaneXYScale)baseInfo.y = 62 * PlaneXYScale;}if (b1 & KEY_UP) {baseInfo.y -= baseInfo.speed * t;if (baseInfo.y < 5 * PlaneXYScale)baseInfo.y = 5 * PlaneXYScale;}if (b1 & KEY_LEFT) {baseInfo.x -= baseInfo.speed * t;if (baseInfo.x < 1 * PlaneXYScale)baseInfo.x = 1 * PlaneXYScale;}if (b1 & KEY_RIGHT) {baseInfo.x += baseInfo.speed * t;if (baseInfo.x > 30 * PlaneXYScale)baseInfo.x = 30 * PlaneXYScale;}if (b1 & KEY_BUTTON_C && fireTimer.tick(t)) {BulletObject_t *but = new BulletObject_t();but->x = baseInfo.x;but->y = baseInfo.y - 2;but->speedX = 0;but->speedY = -400;but->visiable = 1;getRainbowColor(&but->color, 150);ListPushBack(bulletList, (LTDataType) but);}return 0;
}

看看最终效果: 

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

STM32学习笔记十三:WS2812制作像素游戏屏-飞行射击游戏(3)探索数据管理

这篇关于STM32学习笔记十二:WS2812制作像素游戏屏-飞行射击游戏(2)探索时间间隔同步,双向链表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/553098

相关文章

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

MySQL按时间维度对亿级数据表进行平滑分表

《MySQL按时间维度对亿级数据表进行平滑分表》本文将以一个真实的4亿数据表分表案例为基础,详细介绍如何在不影响线上业务的情况下,完成按时间维度分表的完整过程,感兴趣的小伙伴可以了解一下... 目录引言一、为什么我们需要分表1.1 单表数据量过大的问题1.2 分表方案选型二、分表前的准备工作2.1 数据评估

Java集合中的链表与结构详解

《Java集合中的链表与结构详解》链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序的通过链表中的引用链接次序实现,文章对比ArrayList与LinkedList的结构差异,详细讲解了链表... 目录一、链表概念与结构二、当向单链表的实现2.1 准备工作2.2 初始化链表2.3 打印数据、链表长

C#控制台程序同步调用WebApi实现方式

《C#控制台程序同步调用WebApi实现方式》控制台程序作为Job时,需同步调用WebApi以确保获取返回结果后执行后续操作,否则会引发TaskCanceledException异常,同步处理可避免异... 目录同步调用WebApi方法Cls001类里面的写法总结控制台程序一般当作Job使用,有时候需要控制

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.