78. UE5 RPG 创建技能数据并初始化技能ui

在上一篇文章里,我们创建了技能的UI,接下来,我们要考虑如何实现对技能UI的填充,肯定不能直接写死,需要有一些方法去实现技能的更新。我们期望能够创建一个技能数据,然后根据数据通过回调的方式实现数据的更新。
为了实现这个功能,我们会先创建一个结构体,用于存储技能的相关数据(Tag,使用的图片等),然后创建一个DataAsset,然后创建回调函数,在注册技能的时候,将技能相关的数据广播出去,在UI里接受,更新UI显示。

创建DataAsset

首先,我们基于DataAsset创建一个新的类,用于设置技能需要的相关配置
在这里插入图片描述
创建命名 AbilityInfo,技能数据
在这里插入图片描述
在类里面,我们首先创建一个结构体,用于设置技能所需哪些配置,如果需要,我们后续还可以继续添加,这里添加了四项数据

USTRUCT(BlueprintType)
struct FRPGAbilityInfo
{GENERATED_BODY()//技能标签UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)FGameplayTag AbilityTag = FGameplayTag();//技能输入映射标签UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)FGameplayTag InputTag = FGameplayTag();//技能图标UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)TObjectPtr<const UTexture2D> Icon = nullptr;//背景材质UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)TObjectPtr<UMaterialInterface> BackgroundMaterial = nullptr;
};

接着,我们在数据类里面增加一个参数,用于在蓝图中使用此类后,可以设置一个技能数据数组,并增加一个通过技能标签获取对应数据的方法

/*** */
UCLASS()
class RPG_API UAbilityInfo : public UDataAsset
{GENERATED_BODY()public:UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="AbilityInformation")TArray<FRPGAbilityInfo> AbilityInformation;//通过技能标签获取到技能相关数据FRPGAbilityInfo FindAbilityInfoForTag(const FGameplayTag& AbilityTag, bool bLogNotFound = false) const;
};

在获取技能数据的函数实现这里,我们直接遍历数组,查找到相同的技能标签返回,并增加一个参数,如果无法查询到,打印一个错误信息,方便后续调试

FRPGAbilityInfo UAbilityInfo::FindAbilityInfoForTag(const FGameplayTag& AbilityTag, const bool bLogNotFound) const
{for(const FRPGAbilityInfo& Info : AbilityInformation){if(Info.AbilityTag == AbilityTag){return Info;}}if(bLogNotFound){//如果获取不到数据,打印消息}return FRPGAbilityInfo();
}

实现日志分类

在打印这里,我们想实现对于技能设置不同的打印通道,和其它默认的区分开来,这样调试起来会更加的方便。为了实现这个功能,我们需要额外的创建一个.h 和 .cpp文件
直接项目文件夹上面,右键选择添加,文件
在这里插入图片描述
在弹出窗口这里写入需要创建的文件名称
在这里插入图片描述
我们将两个文件都创建出来
在这里插入图片描述
在.h文件中,我们设置#pragma once 可以实现一次编译,多次复用。然后引入基础头文件和打印相关的头文件,并通过宏定义了一个我们自定义的打印通道。

#pragma once#include "CoreMinimal.h"
#include "Logging/LogMacros.h"DECLARE_LOG_CATEGORY_EXTERN(LogRPG, Log, All);

然后在cpp文件中,使用DEFINE_LOG_CATEGORY对一个打印通道进行实例化,这个宏与DECLARE_LOG_CATEGORY_EXTERN宏一起使用来实现一个新的打印通道。

#include "RPGLogChannels.h"DEFINE_LOG_CATEGORY(LogRPG);

接着,我们可以在获取技能相关数据的函数中,引入此文件

#include "RPG/RPGLogChannels.h"

并在查询不到对应数据时,在我们自定义的日志分类中打印

	if(bLogNotFound){//如果获取不到数据,打印消息UE_LOG(LogRPG, Error, TEXT("无法通过技能标签[%s]在技能数据[%s]查找到对应的技能数据"), *AbilityTag.ToString(), *GetNameSafe(this));}

应用技能数据

我们将技能数据的DataAsset创建完成,接下来,要实现对其的应用,我们在蓝图中创建了技能数据,需要有一个地方去设置,并可以应用。这些数据是在在UI上使用的,我们将其设置在OverlayWidgetController里面,增加一个对齐配置的配置项。OverlayWidgetController是配置在HUD类上面的,在项目运行时,就会初始化OverlayWidgetController,并应用对OverlayWidget,覆盖屏幕的OverlayWidget就会从OverlayWidgetController中获取数据。

	//技能的表格数据UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Widget Data")TObjectPtr<UAbilityInfo> AbilityInfo;

我们现实现对数据的设置,编译代码,打开UE,创建一个数据资产
在这里插入图片描述
类就选择我们创建的数据资产的类
在这里插入图片描述
命名为DA_AbilityInfo
在这里插入图片描述
接着打开基于OverlayWidgetController创建的蓝图,将创建的数据资产设置上去,这样,我们就可以在后续UI更新中,使用此数据,并能够实现对数据的获取。
在这里插入图片描述

给数据资产添加第一条数据

有了数据资产,我们首先将玩家角色的第一个技能添加进去,就是普通攻击火球术,我们还没有其相关的技能标签
我们首先创建一个火球术的技能标签

FGameplayTag Abilities_Fire_FireBolt; //火球术技能标签

然后在cpp里面对其注册

	GameplayTags.Abilities_Fire_FireBolt = UGameplayTagsManager::Get().AddNativeGameplayTag(FName("Abilities.Fire.FireBolt"),FString("火球术技能标签"));

然后编译,进行设置
在这里插入图片描述
这里考虑到有可能后续玩家会修改触发技能的按键,对于技能的输入tag设置,我们后续将修改为在程序中动态设置它,并且,后续将其在蓝图中设置的功能关闭。

	//技能输入映射标签UPROPERTY(BlueprintReadOnly)FGameplayTag InputTag = FGameplayTag();

重新编译
在这里插入图片描述

广播技能数据

有了技能数据,我们需要实现在ASC应用角色技能时,UI上也能够获取到应用通知,跟随更新数据。
为了实现这点,我们需要在ASC中增加委托,并在应用技能后,进行广播触发回调。
我们在自定义ASC中增加一个委托宏,这个宏用于在技能初始化应用完成后广播回调

DECLARE_MULTICAST_DELEGATE_OneParam(FAbilityGiven, URPGAbilitySystemComponent*) //技能初始化应用后的回调委托

使用宏创建一个委托

FAbilityGiven AbilityGivenDelegate; //技能初始化应用后的回调委托

由于我们无法确定运行起来后,是技能的初始化完成,还是UI的初始化完成,所以,我们通过一个变量来记录,在技能初始化应用完成后,将其设置为true

bool bStartupAbilitiesGiven = false; //初始化应用技能后,此值将被设置为true,用于记录当前是否被初始化完成

接着在初始化应用技能的函数里,将变量设置为true,并将委托广播出去

void URPGAbilitySystemComponent::AddCharacterAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupAbilities)
{for(const TSubclassOf<UGameplayAbility> AbilityClass : StartupAbilities){FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass, 1);if(const URPGGameplayAbility* AbilityBase = Cast<URPGGameplayAbility>(AbilitySpec.Ability)){AbilitySpec.DynamicAbilityTags.AddTag(AbilityBase->StartupInputTag); //设置技能激活输入标签GiveAbility(AbilitySpec); //只应用不激活// GiveAbilityAndActivateOnce(AbilitySpec); //应用技能并激活一次}}bStartupAbilitiesGiven = true;AbilityGivenDelegate.Broadcast(this);
}

这样,我们就实现了技能初始化应用的委托,然后,我们在Overlay的Controller的类里面,绑定此委托的回调,完成和ASC的交互,我们在OverlayWidgetController里面创建一个回调函数

void OnInitializeStartupAbilities(URPGAbilitySystemComponent* RPGAbilitySystemComponent) const; //技能初始化应用后的回调

刚好里面有我们之前书写的绑定委托的函数,我们在里面对此委托进行绑定。
这里逻辑是,我们获取到使用的ASC,将其转换为自定义ASC,通过判断变量,如果变量值为true,代表当前技能初始化应用已经完成,我们可以直接调用回调。如果变量为false,初始化还未完成状态,我们就需要去绑定委托,在技能初始化应用完成后,也可以触发委托的回调。
通过这两步,不管谁先谁后,都可以成功触发我们写在OverlayWidgetController里面的回调。并且我们也成功的获取到了ASC,并进行下一步处理。

void UOverlayWidgetController::BindCallbacksToDependencies()
{...if(URPGAbilitySystemComponent* RPGASC = Cast<URPGAbilitySystemComponent>(AbilitySystemComponent)){if(RPGASC->bStartupAbilitiesGiven){//如果执行到此处时,技能的初始化工作已经完成,则直接调用初始化回调OnInitializeStartupAbilities(RPGASC);}else{//如果执行到此处,技能初始化还未完成,将通过绑定委托,监听广播的形式触发初始化完成回调RPGASC->AbilityGivenDelegate.AddUObject(this, &ThisClass::OnInitializeStartupAbilities);}//AddLambda 绑定匿名函数RPGASC->EffectAssetTags.AddLambda([this](const FGameplayTagContainer& AssetTags) //中括号添加this是为了保证内部能够获取类的对象{for(const FGameplayTag& Tag : AssetTags){//对标签进行检测,如果不是信息标签,将无法进行广播FGameplayTag MessageTag = FGameplayTag::RequestGameplayTag(FName("Message"));// "A.1".MatchesTag("A") will return True, "A".MatchesTag("A.1") will return Falseif(Tag.MatchesTag(MessageTag)){const FUIWidgetRow* Row = GetDataTableRowByTag<FUIWidgetRow>(MessageWidgetDataTable, Tag);MessageWidgetRowDelegate.Broadcast(*Row); //前面加*取消指针引用}//将tag广播给Widget Controller 测试代码// const FString Msg = FString::Printf(TEXT("GE Tag in Widget Controller: %s"), *Tag.ToString()); //获取Asset Tag// GEngine->AddOnScreenDebugMessage(-1, 8.f, FColor::Cyan, Msg); //打印到屏幕上 -1 不会被覆盖}});}
}

接下来,我们将实现技能的初始化回调的内容逻辑,它将实现对所有的应用的技能进行类型判断,并选出需要手动触发的技能,然后获取对应的技能UI数据,并通过Controller广播给用户控件的UI。虽然逻辑稍微复杂点,但是这种方式能够将逻辑拆分开来,不会造成代码之间的耦合度过高,造成报错问题。

实现UI技能委托

现在,当角色的技能初始化应用后,会触发UI的Controller里面的初始化回调。在回调里面,我接下来将实现的是,从里面获取到主动技能,然后获取其是否是需要按键激活的技能,然后通过技能Tag去获取数据,将数据广播出去。
接下来,我们实现技能初始化应用后的回调,在控制器里,初始化后,我们进行一次判断,当前技能是否初始化成功

if(!RPGAbilitySystemComponent->bStartupAbilitiesGiven) return; //判断当前技能初始化是否完成,触发回调时都已经完成

接下来,我们要遍历调用技能的实例,对技能进行处理,这里我们创建一个新的单播委托,它只能绑定一个回调函数

DECLARE_DELEGATE_OneParam(FForEachAbility, const FGameplayAbilitySpec&); //单播委托,只能绑定一个回调

在ASC中增加一个新的函数,用于遍历技能,并通过委托回调的形式广播出去,通过这种方式,降低了和OverlayWidgetController之间的耦合,即使你换一个其它的类,也可以调用。参数我们传入单播委托

void ForEachAbility(const FForEachAbility& Delegate); //遍历技能,并将技能广播出去

在函数实现这里,我们首先使用一次域锁,在执行下面的逻辑时,传入的内容内部的数据是无法被更改变动的。然后我们遍历所有可激活的技能,通过委托广播的形式调用。

void URPGAbilitySystemComponent::ForEachAbility(const FForEachAbility& Delegate)
{FScopedAbilityListLock ActiveScopeLock(*this); //使用域锁将此作用域this的内容锁定(无法修改),在遍历结束时解锁,保证线程安全for(const FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()){if(!Delegate.ExecuteIfBound(AbilitySpec)) //运行绑定在技能实例上的委托,如果失败返回false{UE_LOG(LogRPG, Error, TEXT("在函数[%hs]运行委托失败"), __FUNCTION__);}}
}

有了这个函数,我们在OverlayWidgetController里面就可以实现对技能的遍历,而且还不需要类型转换等操作。上面的函数可以针对每个技能实例触发一次委托回调,所以,我们创建一个委托,并绑定回调函数,就可以实现对所有技能的处理。
具体实现如下,创建委托,绑定回调,然后通过函数调用,即可将技能实例进行遍历。

	//创建单播委托FForEachAbility BroadcastDelegate;//委托绑定回调匿名函数,委托广播时将会触发函数内部逻辑BroadcastDelegate.BindLambda([this](const FGameplayAbilitySpec& AbilitySpec){...});//遍历技能并触发委托回调RPGAbilitySystemComponent->ForEachAbility(BroadcastDelegate);

我们在回调函数中,将针对于每个技能实例进行处理,获取技能的Tag标签,判断它是否属于技能,并获取技能的输入标签设置给技能数据,通过委托,广播出去。我们在技能的用户控件实例里面,就可以通过监听相关委托来实现修改技能图标。
我们在OverlayWidgetController里面新创建一个委托,用于在蓝图中对其进行监听,委托返回一个参数,对应的技能的数据

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAbilityInfoSignature, const FRPGAbilityInfo, Info);

根据委托的类型创建一个参数,用于在蓝图中绑定监听

	UPROPERTY(BlueprintAssignable, Category="GAS|Messages")FAbilityInfoSignature AbilityInfoDelegate;

我们还需要实现两个函数,它们可以通过传入的技能实例,从技能实例里面获取到对应的技能标签和输入标签。这里我们直接创建两个静态函数

	static FGameplayTag GetAbilityTagFromSpec(const FGameplayAbilitySpec& AbilitySpec);static FGameplayTag GetInputTagFromSpec(const FGameplayAbilitySpec& AbilitySpec);

它们的实现是,我们技能标签直接从AbilityTags里面获取,由于它不只可以设置一个标签,我们需要遍历。

FGameplayTag URPGAbilitySystemComponent::GetAbilityTagFromSpec(const FGameplayAbilitySpec& AbilitySpec)
{if(AbilitySpec.Ability){for(FGameplayTag Tag : AbilitySpec.Ability.Get()->AbilityTags) //获取设置的所有的技能标签并遍历{if(Tag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("Abilities")))) //判断当前标签是否包含"Abilities"名称{return Tag;}}}return FGameplayTag();
}FGameplayTag URPGAbilitySystemComponent::GetInputTagFromSpec(const FGameplayAbilitySpec& AbilitySpec)
{for(FGameplayTag Tag : AbilitySpec.DynamicAbilityTags) //从技能实例的动态标签容器中遍历所有标签{if(Tag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("InputTag")))) //查找标签中是否设置以输入标签开头的标签{return Tag;}}return FGameplayTag();
}

MatchesTag的解释是A.1如果MatchesTag的A那么将返回true,相当于判断的它的标签下面的子级,我们的技能都是在"Abilities"下面
在这里插入图片描述
输入标签也是同理
在这里插入图片描述

输入标签的设置是我们在初始的时候设置在技能上的,我们可以通过蓝图设置它的输入
在这里插入图片描述
并在应用技能时,设置在技能实例的动态技能标签中,所以,我们要去技能标签中去获取判断,这种还可以实现如果玩家修改键位了,可以实现不同的键位,前提是你需要把之前默认的删除掉。
在这里插入图片描述
可以获取技能标签和输入标签,还有了蓝图可以绑定的回调,那么,我们就可以去实现回到的函数,首先获取技能标签,然后通过技能标签获取到技能对应的技能数据,并设置技能数据广播出去,完成整个逻辑。

//委托绑定回调匿名函数,委托广播时将会触发函数内部逻辑BroadcastDelegate.BindLambda([this](const FGameplayAbilitySpec& AbilitySpec){//通过静态函数获取到技能实例的技能标签,并通过标签获取到技能数据FRPGAbilityInfo Info = AbilityInfo->FindAbilityInfoForTag(URPGAbilitySystemComponent::GetAbilityTagFromSpec(AbilitySpec));//获取到技能的输入标签Info.InputTag = URPGAbilitySystemComponent::GetInputTagFromSpec(AbilitySpec);//广播技能数据AbilityInfoDelegate.Broadcast(Info); });

以下是初始化技能应用后的完整回调代码

void UOverlayWidgetController::OnInitializeStartupAbilities(URPGAbilitySystemComponent* RPGAbilitySystemComponent) const
{if(!RPGAbilitySystemComponent->bStartupAbilitiesGiven) return; //判断当前技能初始化是否完成,触发回调时都已经完成//创建单播委托FForEachAbility BroadcastDelegate;//委托绑定回调匿名函数,委托广播时将会触发函数内部逻辑BroadcastDelegate.BindLambda([this](const FGameplayAbilitySpec& AbilitySpec){//通过静态函数获取到技能实例的技能标签,并通过标签获取到技能数据FRPGAbilityInfo Info = AbilityInfo->FindAbilityInfoForTag(URPGAbilitySystemComponent::GetAbilityTagFromSpec(AbilitySpec));//获取到技能的输入标签Info.InputTag = URPGAbilitySystemComponent::GetInputTagFromSpec(AbilitySpec);//广播技能数据AbilityInfoDelegate.Broadcast(Info); });//遍历技能并触发委托回调RPGAbilitySystemComponent->ForEachAbility(BroadcastDelegate);
}

实现在技能UI上绑定委托回调

完成上面内容,我们可以编译代码打开UE,在UE里面对我们上一篇文章中制作的技能UI进行修改。
打开我们之前创建的WBP_SpellGlobe用户控件,我们需要添加一个标签,用于记录当前的UI需要显示哪个技能,因为我们输入的键位是固定的,需要在实例上面标识这个技能的出入标签。
在这里插入图片描述
接着我们添加逻辑,在控制器设置回调里面,去将控制器实例转换为目标类型,方便后续使用
在这里插入图片描述
然后绑定监听上面的委托回调,在目标委托广播后,将触发后续逻辑,返回一个技能相关数据,我们可以通过判断当前的输入标签和技能数据的输入标签是否一致,如果一致,使用技能数据的技能图标和背景材质更新当前的技能UI。
在这里插入图片描述
接着打开WBP_HealthManaSpells这个用户控件
在这里插入图片描述
我们首先需要在技能ui上面设置它的输入标签,按照对应的输入,设置对应的输入标签。
在这里插入图片描述
还需要一步,就是设置控件的控制器,这样就可以成功触发控制器设置回调
在这里插入图片描述
最后,我们在技能身上设置好对应的技能输入标签和技能标签
在这里插入图片描述
接着运行,查看是否能够在ui上面显示出来。
在这里插入图片描述
查看修改输入标签后是否也能够切换
在这里插入图片描述

处理多人玩法中的bug

如果我们开启两个玩家运行游戏,会发现第二个玩家的ui没有跟着更新。这个原因是因为技能初始化应用完成调用的广播是在服务器执行的,在客户端无法执行
在这里插入图片描述
我们查看源代码,可以看到在ASC里面,存储着当前激活的技能的容器,被修改后,会调用同步函数OnRep_ActivateAbilities
在这里插入图片描述
这个函数是一个虚函数,我们可以复写它,然后在里面调用,去初始化客户端的技能
在这里插入图片描述
我们在自定义的ASC中覆写它

virtual void OnRep_ActivateAbilities() override;

然后在函数内,判断当前是否已经初始化广播,如果没有,则调用广播

void URPGAbilitySystemComponent::OnRep_ActivateAbilities()
{Super::OnRep_ActivateAbilities();if(!bStartupAbilitiesGiven){bStartupAbilitiesGiven = true;AbilityGivenDelegate.Broadcast(this);}
}

然后发现在客户端也能够顺利初始化技能ui
在这里插入图片描述

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

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

相关文章

【经典面试题】是否形成有环链表

1.环形链表oj 2. oj解法 利用快慢指针&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode; bool hasCycle(struct ListNode *head) {ListNode* slow head, *fast…

UNIAPP_ReferenceError: TextEncoder is not defined 解决

错误信息 1、安装text-decoding npm install text-decoding2、main.js import { TextEncoder, TextDecoder } from text-decoding global.TextEncoder TextEncoder global.TextDecoder TextDecoder

【网络安全】Oracle:SSRF获取元数据

未经许可&#xff0c;不得转载。 文章目录 前言正文漏洞利用 前言 Acme 是一家广受欢迎的播客托管公司&#xff0c;拥有庞大的客户群体。与许多大型运营公司一样&#xff0c;Acme 采用了Apiary的服务&#xff0c;使用户能够安全高效地管理他们的播客。 Apiary 于2017年初被Or…

Java SpringBoot 若依 后端实现评论“盖楼“,“楼中楼“功能 递归查询递归组装评论结构

效果图 数据库设计 还可以使用路径模块 一级评论id,二级评论id, 用like最左匹配原则查询子评论 因为接手遗留代码&#xff0c;需要添加字段&#xff0c;改动数据库&#xff0c;我就不改动了&#xff0c;导致我下面递归查询子评论不是很好。 业务代码 Overridepublic List<S…

OpenCV漫水填充函数floodFill函数的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 功能描述 ffloodFill函数是OpenCV库中用于图像处理的一个功能&#xff0c;它用于填充与种子点颜色相近的连通区域。这个函数在很多场景下都非常有用&#x…

电子电气架构 --- 关于DoIP的一些闲思 上

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

上传图片,base64改为文件流,并转给后端

需求&#xff1a; html代码&#xff1a; <el-dialog v-model"dialogPicVisible" title"新增图片" width"500"><el-form :model"picForm"><el-form-item label"图片名称&#xff1a;" :label-width"10…

【数组、特殊矩阵的压缩存储】

目录 一、数组1.1、一维数组1.1.1 、一维数组的定义方式1.1.2、一维数组的数组名 1.2、二维数组1.2.1、二维数组的定义方式1.2.2、二维数组的数组名 二、对称矩阵的压缩存储三、三角矩阵的压缩存储四、三对角矩阵的压缩存储五、稀疏矩阵的压缩存储 一、数组 概述&#xff1a;数…

基于Vue和UCharts的前端组件化开发:实现高效、可维护的词云图与进度条组件

基于Vue和UCharts的前端组件化开发&#xff1a;实现高效、可维护的词云图与进度条组件 摘要 随着前端技术的迅速发展和业务场景的日益复杂&#xff0c;传统的整块应用开发方式已无法满足现代开发的需求。组件化开发作为一种有效的解决方案&#xff0c;能够将系统拆分为独立、…

SpringCoud组件

一、使用SpringCloudAlibaba <dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2023.0.1.0</version><…

纯净IP的重要性解析与测评分析

作为连接互联网世界的桥梁&#xff0c;IP地址的纯净度不仅关乎网络访问的速度与稳定性&#xff0c;更是影响着数据安全与隐私保护。今天&#xff0c;我们将带您深入探索纯净IP的重要性&#xff0c;并分享我们对芝麻HTTP与巨量IP这两家提供纯净SOCKS5代理服务的深度测评分析。 一…

ESP32的I2S引脚及支持的音频标准使用说明

ESP32 I2S 接口 ESP32 有 2 个标准 I2S 接口。这 2 个接口可以以主机或从机模式&#xff0c;在全双工或半双工模式下工作&#xff0c;并且可被配置为 8/16/32/48/64-bit 的输入输出通道&#xff0c;支持频率从 10 kHz 到 40 MHz 的 BCK 时钟。当 1 个或 2 个 被配置为主机模式…

AWS-WAF-Log S3存放,通过Athena查看

1.创建好waf-cdn 并且设置好规则和log存储方式为s3 2. Amazon Athena 服务 使用 &#xff08;注意s3桶位置相同得区域&#xff09; https://docs.aws.amazon.com/zh_cn/athena/latest/ug/waf-logs.html#waf-example-count-matched-ip-addresses 官方文档参考,建一个分区查询表…

自定义波形图View,LayoutInflater动态加载控件保存为本地图片

效果图&#xff1a; 页面布局&#xff1a; <?xml version"1.0" encoding"utf-8"?><LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:la…

硬盘模式vmd怎么改ahci_电脑vmd改ahci模式详细步骤

最近有很多网友问&#xff0c;我新买的电脑安装原版win10或win11找不到驱动器呀&#xff0c;进入第三方pe又找不到硬盘&#xff0c;找到硬盘安装后又出现安装蓝屏的情况&#xff0c;新机器怎么回事呀&#xff1f;这位网友内心有点崩溃&#xff0c;不知道啥原因。其实这些都是由…

MySQL实战45讲学习笔记(持续更新ing……)

文章目录 一、基础架构&#xff1a;一条SQL查询语句是如何执行的&#xff1f;概览连接器查询缓存分析器优化器执行器 二、日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f;redo logbinlog两阶段提交 一、基础架构&#xff1a;一条SQL查询语句是如何执行的&#xf…

力扣每日一题:3011. 判断一个数组是否可以变为有序

力扣官网&#xff1a;前往作答&#xff01;&#xff01;&#xff01;&#xff01; 今日份每日一题&#xff1a; 题目要求&#xff1a; 给你一个下标从 0 开始且全是 正 整数的数组 nums 。 一次 操作 中&#xff0c;如果两个 相邻 元素在二进制下数位为 1 的数目 相同 &…

STM32G4 DMA的使用(寄存器开发)

下面以STM32G474为例&#xff0c;使用DMA来存储USART1的接收数据。 1. 查看硬件支持 首先查看要使用的DMA支持的通道数&#xff0c;在手册中有如下说明。 根据上图可以看到&#xff0c;对于不同的设备类型有不同的DMA通道数量。设备类型分类如下图所示。 我使用的是STM32G474…

【Three.js基础学习】16.Physice

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 课程回顾 物理库 3D Ammo.js Cannon.js Oimo.js 2D Matter.js P2.js Planck.js Box2D.js 补充:一些看似3D的效果实际使用2D库来实现的 物理 和 three.js的结合 概念补充…

雷达视频采集卡 HPx-410

产品简介 雷达视频采集卡 HPx-410&#xff0c;应用于接入导航雷达数据&#xff0c;导航雷达视频&#xff0c;适用于JRC雷达、古野furuon雷达、Sperry雷达等多种型号的雷达。 HPx-410 可以接入导航雷达数据&#xff0c;引入导航雷达原始回波&#xff0c;然后将雷达视频采集到计…