虚幻学习笔记

本文详细介绍了虚幻引擎项目开发中关键概念,包括属性和函数的说明符、元属性、组件操作(如Cast、AddToRoot、SpawnActor)、自定义功能(如SetWidget、SetMasterPoseComponent)以及一些实用工具(如FText转换、计时器管理等)。
摘要由CSDN通过智能技术生成

原型与实例

原型即项目文件中的类或蓝图,实例即将原型拖至编辑器中,也称实例化。

属性UPROPERTY说明符

属性说明符描述
BlueprintReadOnly蓝图可读
BlueprintWriteOnly蓝图可写
BlueprintReadWrite蓝图可读写
EditAnywhere编辑器任何地方可编辑
VisibleAnywhere编辑器任何地方可见
EditDefaultsOnly编辑器仅原型可编辑,实例不可编辑
Category可以设置属性在编辑器里显示的所属类别 例如: Category = “BaseCharacter”

函数UFUNCTION说明符

函数说明符描述
BlueprintCallable蓝图可调用函数
BlueprintNativeEvent蓝图可重写函数,若无重写默认调用C++版本
BlueprintImplementableEvent此函数在蓝图或关卡蓝图图表中实现,在C++中不提供函数体

元属性说明符

说明符前需加上meta = (说明符)

元属性说明符描述
ExposeOnSpawn在蓝图生成时暴露属性
AllowPrivateAccess = “true”允许私有成员被蓝图访问

Class属性声明

	UPROPERTY(BlueprintReadOnly, EditAnywhere)
	class UStaticMeshComponent* StaticMesh;

在声明前冠以class关键字,除了是变量声明外,还是各个类的前向声明,从而确保各自的类在头文件中能够识别,然后在CPP文件中需要包含类文件。

Cast函数指针类型转换

AAM_Hero* AM_Hero = Cast<AAM_Hero>(OtherActor);

Cast函数属于函数模板,将指针参数转换成模板类型的指针。

AddToRoot函数、SetupAttachment函数以及AttachToComponent函数

当我打算将组件绑定到根组件时,不小心错用了AddToRoot()函数。
AddToRoot()的作用:将object添加到根集合中,从而避免垃圾回收。
SetupAttachment()的作用和AttachToComponent()的作用相似:将组件附着在父组件的套接字上,AttachToComponent()函数会返回一个bool值,表示是否成功绑定。在构造函数里或者当组件未注册时,应使用SetupAttachment()

TSubclassOf< T >

此类型提供UClass安全性的模板类,在蓝图中可以选择所有派生自T类型的派生类,在C++可以接受派生类传递。

SpawnActor

Weapon = World->SpawnActor<AWeapon>(WeaponClass, GetTransform());

函数返回模板参数类型的指针(AWeapon*),接受UClass*类型参数(WeaponClass)以及生成物变换(GetTransform()),生成参数结构体(默认值)等。

SpawnActorDeferred

在Actor完成调用构造函数前创建Actor,推迟生成;使用UGameplayStatics::FinishSpawningActor生成Actor。此函数便于在生成时设置属性,类似于有参数的构造函数。

	/**
	 * Spawns given class and returns class T pointer, forcibly sets world transform (note this allows scale as well). WILL NOT run Construction Script of Blueprints 
	 * to give caller an opportunity to set parameters beforehand.  Caller is responsible for invoking construction
	 * manually by calling UGameplayStatics::FinishSpawningActor (see AActor::OnConstruction).
	 */
	AProjectile* Projectile = World->SpawnActorDeferred<AProjectile>((UClass*)ProjectileToSpawn, Arrow->GetComponentTransform());
		//设置子弹
		if (Projectile)
		{
			Projectile->ProjectileDamage = WeaponDamage;
			//完成生成
			UGameplayStatics::FinishSpawningActor(Projectile, Arrow->GetComponentTransform());
		}

SetWidget和SetWidgetClass

两个函数都是在WidgetComponent组件中声明定义的;SetWidgetClass的作用主要是设置控件组件需要生成的控件类型。而SetWidget的作用则是直接设置控件组件中的控件实例。
源码放上

void UWidgetComponent::SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass)
{
	if (WidgetClass != InWidgetClass)
	{
		WidgetClass = InWidgetClass;

		if (FSlateApplication::IsInitialized())
		{
			if (HasBegunPlay() && !GetWorld()->bIsTearingDown)
			{
				if (WidgetClass)
				{
					UUserWidget* NewWidget = CreateWidget(GetWorld(), WidgetClass);
					SetWidget(NewWidget);
				}
				else
				{
					SetWidget(nullptr);
				}
			}
		}
	}
}
void UWidgetComponent::SetWidget(UUserWidget* InWidget)
{
	if (InWidget != nullptr)
	{
		SetSlateWidget(nullptr);
	}

	if (Widget)
	{
		RemoveWidgetFromScreen();
	}
	Widget = InWidget;
	UpdateWidget();
}

meta = (BindWidget/BindWidgetOptional)

元说明符BindWidget以及BindWidgetOptional都提供绑定用户控件中元素的功能,前者比后者严格,后者针对于一些修饰性的即不是必要的元素,如文本等,若是绑定失败,编译器不会提示错误,而仅是在Log日志中显示提示信息。

	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "UserWidgetBase", meta = (BindWidget))
		class UProgressBar* HealthProgressBar;

使用此元说明符需要保证控件中存在于变量同名的元素并在蓝图中将元素设置为变量,若需要将元素显示在控件蓝图中,则将显示继承变量(Show Inherited Variables)设置打开。

通过BindWidget元说明符绑定的控件在Initialize中完成绑定,在C++中可通过Initialize函数、NativeConstruct函数或NativeOnInitialized函数为控件绑定事件或设置属性。

	virtual bool Initialize();
	virtual void NativeConstruct() override;
	virtual void NativeOnInitialized();

GetWidgetFromName

使用此函数可通过控件名在代码中获得控件,同一个控件中不得出现同名变量。

	TextBlock_Health = (UTextBlock*)GetWidgetFromName(FName("HealthTextBlock"));

FText与Float类型转换

1、调用UKismetTextLibrary::Conv_FloatToText()函数,需要传入正确的参数,如float值,舍入规则(向上取整、向下取整)等。

LevelTextBlock->SetText(UKismetTextLibrary::Conv_FloatToText(Level, ERoundingMode::FromZero));

2、通过FString间接转换

FText::FromString(FString::FromInt(Health))

换装功能-SetMasterPoseComponent

在角色由多个骨骼网格体组成的情况下,多个骨骼网格体如果都设置动画蓝图就会非常消耗资源。此时我们可以使用SetMasterPoseComponent设置最主要的骨骼,其他骨骼与主骨骼共用同一个动画蓝图,条件是各骨骼结构相同

蓝图中的使用

代码中的使用

void ASkeletonEnemy::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	if (SK_Quiver && SK_Helmet)
	{
		SK_Quiver->SetMasterPoseComponent(GetMesh());
		SK_Helmet->SetMasterPoseComponent(GetMesh());
	}
}

自定义处理通知——HandleNotify

源码实现

	/** Implementable custom function to handle notifies */
	virtual bool HandleNotify(const FAnimNotifyEvent& AnimNotifyEvent);
	
	/*在方法里可以对通知事件进行处理*/
	bool UAnimInstance::HandleNotify(const FAnimNotifyEvent& AnimNotifyEvent)
	{
		return false;
	}

源码调用

void UAnimInstance::TriggerSingleAnimNotify(const FAnimNotifyEvent* AnimNotifyEvent)
{
	// This is for non 'state' anim notifies.
	if (AnimNotifyEvent && (AnimNotifyEvent->NotifyStateClass == NULL))
	{
		/*HandleNotify返回一个bool值,若返回false,则不会执行下面的代码-调用通知转发事件或者执行动画实例中通知绑定的事件*/
		if (HandleNotify(*AnimNotifyEvent))
		{
			return;
		}

		if (AnimNotifyEvent->Notify != nullptr)
		{	
			// Implemented notify: just call Notify. UAnimNotify will forward this to the event which will do the work.
			``````省略实现代码
		}
		else if (AnimNotifyEvent->NotifyName != NAME_None)
		{
			``````省略实现代码
		}

SetHidden以及SetActorHiddenInGame

源码声明

	/**
	 * Sets the value of bHidden without causing other side effects to this instance.
	 *
	 * SetActorHiddenInGame is preferred preferred in most cases because it respects virtual behavior.
	 */
	void SetHidden(const bool bInHidden);
	/**
	 *	Sets the actor to be hidden in the game
	 *	@param	bNewHidden	Whether or not to hide the actor and all its components
	 */
	UFUNCTION(BlueprintCallable, Category="Rendering", meta=( DisplayName = "Set Actor Hidden In Game", Keywords = "Visible Hidden Show Hide" ))
	virtual void SetActorHiddenInGame(bool bNewHidden);

方法实现源码

void AActor::SetActorHiddenInGame( bool bNewHidden )
{
	if (IsHidden() != bNewHidden)
	{
		//SetActorHiddenInGame函数体内调用SetHidden
		SetHidden(bNewHidden);
		MarkComponentsRenderStateDirty();
	}
}

FTimerHandle-刷新时间句柄

GetWorldTimerManager()->SetTimer()函数的实现具有刷新时间句柄的功能,在源码实现中,引擎会检查是否存在此时间句柄,若存在,则清除并重新添加。
源码

	/**
	 * Sets a timer to call the given native function at a set interval.  If a timer 
	 * is already set for this handle, it will replace the current timer.
	 */
	template< class UserClass >
	FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, UserClass* InObj, typename FTimerDelegate::TUObjectMethodDelegate< UserClass >::FMethodPtr InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = -1.f)
	{
		InternalSetTimer(InOutHandle, FTimerUnifiedDelegate( FTimerDelegate::CreateUObject(InObj, InTimerMethod) ), InRate, InbLoop, InFirstDelay);
	}

故前后传入的时间句柄应该是共用同一个。

	//将计时器设置成静态,所有对象共用同一个类的成员
	static FTimerHandle StunTimerHandle;

	//初始化静态变量
	FTimerHandle AArrowProjectile::StunTimerHandle = FTimerHandle();

SetValueAsEnum

	Blackboard->SetValueAsEnum(FName("黑板键名"), (uint8)枚举值);

枚举值可理解为枚举值的索引,从0开始

碰撞预设CollisionPreset

在这里插入图片描述

碰撞预设CollisionPresets会根据不同类型自动设置相应的CollisionEnabled、ObjectType、CollisionResponses等,可使用SetCollisionProfileName()修改

Mesh->SetCollisionProfileName(FName("NoCollision"));

CollisionEnabled表示碰撞体的物理碰撞以及碰撞查询,使用SetCollisionEnabled()修改

Mesh->SetCollisionEnabled(ECollisionEnabled::QueryOnly);

Object Type表示碰撞体所属的Object类别,与其他Object Response相对应。

Mesh->SetCollisionObjectType(ECollisionChannel::ECC_PhysicsBody);

CollisionResponses表示Object对不同物体类型的碰撞响应,可使用SetCollisionResponseToAllChannels()和SetCollisionResponseToChannel()修改

Mesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);
Mesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);

自定义多播委托

/**
 * 声明一个多播委托,无返回值,一个函数参数
 */
DECLARE_MULTICAST_DELEGATE_OneParam( FOnNotifiedSignature, USkeletalMeshComponent* )

UCLASS()
class TWINSTICKSHOOTER_API UAnimNotify_AttackBegin : public UAnimNotify
{
	GENERATED_BODY()

public:
	virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;

	FOnNotifiedSignature OnNotifiedSignature;
};


void UAnimNotify_AttackBegin::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{	
	//将该委托广播给所有绑定的对象,除了过期的对象
	OnNotifiedSignature.Broadcast(MeshComp);
	Super::Notify(MeshComp, Animation);

}

多播委托允许您附加多个函数委托,然后通过调用多播委托的"Broadcast()"函数一次性同时执行它们。多播委托签名不得使用返回值。

GetDeltaSeconds()以及GetTimeSeconds()

GetWorld()函数取得世界指针UWorld*,通过世界指针调用GetDeltaSeconds()取得每帧耗费的时间。

//每帧造成伤害
GetWorldTimerManager().SetTimer(StormDamageTimer, this, &AStormProjectile::StormDamage, GetWorld()->GetDeltaSeconds(), true);

GetTimeSeconds()函数可以获取目前的游戏时间

UGameplayStatics::GetTimeSeconds(GetWorld())

接口Interface

UINTERFACE(MinimalAPI)
class UITalkable : public UInterface
{
	GENERATED_BODY()
};

/**
 * 	
 */
class TWINSTICKSHOOTER_API IITalkable
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ITalkable")
		bool Talk(AActor* TalkInstigator);
};

接口类有助于确保一组(可能)不相关的类实现一组通用函数,即相同函数在不同类对象可以实现着不同的行为。

UINTERFACE类不是实际的接口;它是一个空白类,它的存在只是为了向虚幻引擎反射系统确保可见性。

"前缀为U(U-prefixed)"的类不需要构造函数或任何其他函数,而"前缀为I(I-prefixed)"的类将包含所有接口函数,且此类实际上将被您的其他类继承。

虚幻文档参考

在接口函数声明中,可以使用不带UFUNCTION()宏的虚拟virtual函数,使得在其他C++类以及子类实现覆盖它,但此接口对蓝图不可见。
若使用了UFUNCTION()宏,例如BlueprintNativeEvent,则不能声明为virtual,可在蓝图中覆盖实现。

若既想在蓝图中以及C++子类中覆盖实现,可使用下面这种方法

public:		//接口函数一定要声明为 public
//对话函数
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ITalkable")
	bool Talk(AActor* TalkInstigator);		//这个函数不需要定义
	virtual bool Talk_Implementation(AActor* TalkInstigator);
	
bool IITalkable::Talk_Implementation(AActor* TalkInstigator)
{
	return false;
}

UKismetSystemLibrary::LineTraceSingle射线追踪

源码声明

	/**
	 * Does a collision trace along the given line and returns the first blocking hit encountered.
	 * This trace finds the objects that RESPONDS to the given TraceChannel
	 * 
	 * @param WorldContext	World context
	 * @param Start			Start of line segment.
	 * @param End			End of line segment.
	 * @param TraceChannel	
	 * @param bTraceComplex	True to test against complex collision, false to test against simplified collision.
	 * @param OutHit		Properties of the trace hit.
	 * @return				True if there was a hit, false otherwise.
	 */
	UFUNCTION(BlueprintCallable, Category="Collision", meta=(bIgnoreSelf="true", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore", DisplayName="LineTraceByChannel", AdvancedDisplay="TraceColor,TraceHitColor,DrawTime", Keywords="raycast"))
	static bool LineTraceSingle(const UObject* WorldContextObject, const FVector Start, const FVector End, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);

在函数参数中需要传入ETraceTypeQuery TraceChannel,检测通道,在引擎中默认有两种检测通道,一个是Visibility、一个是Camera,除此之外,我们可以在项目设置中添加自定义检测通道。

项目设置-Collision

添加后可在项目文件目录下的config文件夹中的DefaultEngine.ini找到对应的通道。
在这里插入图片描述
ECC_GameTraceChannel1为相应的ECollisionChannel类型通道名,使用UEngineTypes::ConvertToTraceType()函数可将ECollisionChannel类型转换成ETraceTypeQuery类型。

FTimerManager自定义计时器管理

在暂停游戏显示过渡UI时使用计时器恢复游戏,发现计时器并没有生效。当使用SetGamePaused暂停游戏时,计时器也相应地停止了,原理是因为GetWorldTimerManager()中的计时器管理使用的是世界的Tick中传入的DeltaTime。使用自定义计时器管理设置时间句柄,可以避免计时器收到游戏暂停的影响。

头文件
	//自定义计时器管理
	TUniquePtr<class FTimerManager> HUDTimerManager;

	//获得计时器管理
	inline FTimerManager& GetHUDTimerManager() const { return *HUDTimerManager.Get(); }	
	
CPP文件	
	//初始化TimerManager
	HUDTimerManager = MakeUnique<FTimerManager>();

	//设置当暂停时Tick执行
	SetTickableWhenPaused(true);

	bAllowTickBeforeBeginPlay = true;

	void AHUD_CM::Tick(float DeltaTime)
	{
		Super::Tick(DeltaTime);

		HUDTimerManager->Tick(DeltaTime);
	}
	
	void AHUD_CM::BeginPlay()
	{
		Super::BeginPlay();
		
		//设置自定义计时器管理,不受游戏暂停影响
		GetHUDTimerManager().SetTimer(RemoveUITimer, this, &AHUD_CM::RemoveLoadUI, 3.f);
	}

FString转换为FName

FName ConvertedFString = FName(*TheString);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值