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 False
					if(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.mfbz.cn/a/797969.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

免费的ssh工具

1.Quickstart - kitty 2 Download Termius for Windows 3. MobaXterm Xserver with SSH, telnet, RDP, VNC and X11 - Download

Qt MV架构-视图类

一、基本概念 在MV架构中&#xff0c;视图包含了模型中的数据项&#xff0c;并将它们呈现给用户。数据项的表示方法&#xff0c;可能和数据项在存储时用的数据结构完全不同。 这种内容与表现分离之所以能够实现&#xff0c;是因为使用了 QAbstractItemModel提供的一个标准模…

EasyExcel批量读取Excel文件数据导入到MySQL表中

1、EasyExcel简介 官网&#xff1a;EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网 2、代码实战 首先引入jar包 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</v…

基于FPGA的千兆以太网设计(1)----大白话解释什么是以太网

1、什么是以太网? 还记得初学以太网的时候,我就被一大堆专业名词给整懵了:什么以太网,互联网,MAC,IP,局域网,万维网,网络分层模型等等等等。慢着!我学的不是以太网吗?怎么出来这么一大堆东西? 啊!以太网究竟是什么?别急,我接下来就尽量用通俗的大白话来给你解释…

Phpstudy 2018 之xhcms搭建

1、由于直接访问根目录无法进入网站 2、所以采用搭建网站&#xff0c;第一使用系统服务模式、选择php-5.4.45Apache模式 3、网站域名为本地ip地址或者127.0.0.1、端口8085 4、浏览器输入127.0.0.1:8085直接转到系统安装 5、返回输入127.0.0.1:8085&#xff0c;成功进入网站

前端JS特效第36波:jQ多种相册切换效果

jQ多种相册切换效果&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"h…

Mac安装stable diffusion 工具

文章目录 1.安装 Homebrew2.安装 stable diffusion webui 的依赖3.下载 stable diffusion webui 代码4.启动 stable diffusion webui 本体5.下载模型6.这里可能会遇到一个clip-vit-large-patch14报错 参考&#xff1a;https://brew.idayer.com/install/stable-diffusion-webui/…

[ruby on rails]部署时候产生ActiveRecord::PreparedStatementCacheExpired错误的原因及解决方法

一、问题&#xff1a; 有时在 Postgres 上部署 Rails 应用程序时&#xff0c;可能会看到 ActiveRecord::PreparedStatementCacheExpired 错误。仅当在部署中运行迁移时才会发生这种情况。发生这种情况是因为 Rails 利用 Postgres 的缓存准备语句(PreparedStatementCache)功能来…

数学建模·非线性规划

整型规划 适用于一个变量或多个变量的值只能是整型的情况 整形规划的分类 0-1背包问题 对于一个物品来说&#xff0c;只有选和不选两种情况 表现为单下标&#xff0c;单变量问题 例&#xff1a;建设学校问题 对于每个学校来说只有选和不选两种情况&#xff0c;在数学上我们用…

内网信息收集——MSF信息收集浏览器记录配置文件敏感信息

文章目录 一、配置文件敏感信息收集二、浏览器密码&记录三、MSF信息收集 域控&#xff1a;windows server 2008 域内机器&#xff1a;win7 攻击机&#xff1a;kali 就是红日靶场&#xff08;一&#xff09;的虚拟机。 一、配置文件敏感信息收集 使用searchall64.exe&#…

Windows11终端winget配置

一、工具安装 Windows11是自带该工具的&#xff0c;如果wind10&#xff0c;可以找应用商店和GitHub上进行下载。 安装地址使用 winget 工具安装和管理应用程序 | Microsoft Learn 发布地址 Releases microsoft/terminal GitHub 二、无法使用问题排错 在命令行界面出现以…

【python学习】多线程编程的背景、定义、特点、优缺点、使用场景和示例以及和单线程的区别

引言 随着计算机技术的发展&#xff0c;多核处理器已经成为了主流,为了充分利用多核处理器带来的并行计算能力&#xff0c;提高程序的执行效率和响应速度&#xff0c;多线程编程变得尤为重要 Python作为一种高级编程语言&#xff0c;提供了多线程编程的支持&#xff0c;允许开发…

电子期刊制作实战教程:从零开始制作

​随着互联网的普及&#xff0c;电子期刊已经成为了信息传递的重要载体。它以便捷、环保、互动性强等特点受到了越来越多人的青睐。那么&#xff0c;如何从零开始制作一份吸引人的电子期刊呢&#xff1f; 1.要制作电子杂志,首先需要选择一款适合自己的软件。比如FLBOOK在线制作…

链表(一)----单链表,链表的删除,链表的合并,链表的划分,头节点

官网地址&#xff1a;https://www.dhcode.cn/p/t_pc/goods_pc_detail/goods_detail/term_624bd804b3d39_Ac0g7V?fromH5true&type3&channel_id&pro_idterm_624bd804b3d39_Ac0g7V 本内容大部分从中截图 讲了三个力扣题&#xff1a;203&#xff0c;21&#xff0c;8…

【postgresql】锁

PostgreSQL 提供了多种锁模式来控制对表和行的并发访问&#xff0c;以确保数据的一致性和完整性。这些锁模式包括表级锁和行级锁&#xff0c;它们可以由应用程序显式控制&#xff0c;也可以在执行大多数 PostgreSQL 命令时自动获取。 锁类型 PostgreSQL类型的锁包括&#xff…

2024-07-14 Unity插件 Odin Inspector1 —— 插件介绍

文章目录 1 介绍2 模块3 学习目的 1 介绍 ​ Odin Inspector 是 Unity 的一个插件&#xff0c;拥有强大、自定义和用户友好的编辑器&#xff0c;而无需编写任何自定义编辑器代码&#xff0c;使得编程过程中的数据可视化更容易实现。 ​ 具体功能包括&#xff1a; 更舒适美观…

网络(二)——套接字编程

文章目录 理解源IP地址和目的IP地址认识端口号认识TCP/UDP协议网络字节序socket编程接口socket 常见APIsockaddr结构 理解源IP地址和目的IP地址 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址&#xff1b; 源IP即发送方的地址&#xff0c;目的IP即接受方的…

创建yaml文件并读取

yaml文件可以存放token数据&#xff0c;那怎么操作存放并读取呢&#xff1f; 1、创建yaml文件 1、导入类库 import yaml 注意要导入的类库&#xff0c;在解释器中叫pyaml 2、创建文件 #创建yaml文件 yamlfileopen("testdata.yaml",w,encodingutf-8) 3、设置数据…

PostgreSQL 中如何解决因频繁的小事务导致的性能下降?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中解决因频繁小事务导致性能下降的方法 PostgreSQL 中解决因频繁小事务导致性能下降的方法…

电气工程VR虚拟仿真实训平台以趣味化方式增强吸引力

在工业4.0时代和教育信息化的双重推动下&#xff0c;我们致力于推动实训课件的跨界合作与共创。VR实训课件不仅促进了不同领域、不同行业之间的紧密合作&#xff0c;更让学习变得生动直观。我们凭借3D技术生动、直观、形象的特点&#xff0c;开发了大量配套3D教材&#xff0c;让…