Unreal View Model结合GAS使用

这个东西真的难用,各种问题,记录下
官方文档
bilibili教学

开启插件

插件开启 Viewmodel:

在这里插入图片描述
build.cs内PublicDependencyModuleNames加上ModelViewViewModel

创建ViewModel类

#pragma once

#include "CoreMinimal.h"
#include "MVVMViewModelBase.h"
#include "AttributeViewModel.generated.h"

UCLASS(BlueprintType)
class PROJECT2D_API UAttributeViewModel : public UMVVMViewModelBase
{
	GENERATED_BODY()
public:
	UPROPERTY(BlueprintReadWrite, FieldNotify, Setter, Getter)
	float Health;
	UPROPERTY(BlueprintReadWrite, FieldNotify, Setter, Getter)
	float MaxHealth;

public:
	float GetHealth() const;
	void SetHealth(float NewHealth);
	float GetMaxHealth() const;
	void SetMaxHealth(float NewMaxHealth);

	UFUNCTION(BlueprintCallable, FieldNotify)
	float GetHealthPercent() const;
};

#include "UI/AttributeViewModel.h"

float UAttributeViewModel::GetHealth() const
{
	return Health;
}

void UAttributeViewModel::SetHealth(float NewHealth)
{
	if (UE_MVVM_SET_PROPERTY_VALUE(Health, NewHealth))
	{
		UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent);
	}
}

float UAttributeViewModel::GetMaxHealth() const
{
	return MaxHealth;
}

void UAttributeViewModel::SetMaxHealth(float NewMaxHealth)
{
	if (UE_MVVM_SET_PROPERTY_VALUE(MaxHealth, NewMaxHealth))
	{
		UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent);
	}
}

float UAttributeViewModel::GetHealthPercent() const
{
	return Health / FMath::Max(1, MaxHealth);
}
	

用角色类管理 ViewModel 实例:(这样子各个UI都可以用这个数据)

UPROPERTY()
class UAttributeViewModel* AttributeViewModel;
UFUNCTION(BlueprintCallable)
class UAttributeViewModel* GetAttributeViewModel();
class UAttributeViewModel* AXXXCharacter::GetAttributeViewModel()
{
	if (!AttributeViewModel)
	{
		AttributeViewModel = NewObject<UAttributeViewModel>(this);
	}
	return AttributeViewModel;
}

AttributeSet内部存一份ViewModel(记得多端):

void UXXXAttributeSet::Reset()
{
	if (UAbilitySystemComponent* ASC = GetOwningAbilitySystemComponent())
	{
		if (AXXXCharacter* Char = Cast<AXXXCharacter>(ASC->GetOwnerActor()))
		{
			ViewModel = Char->GetAttributeViewModel();
		}
	}
}

在属性集的PostAttributeChange和属性的OnRep函数调用ViewModel的Set函数:

#define XXX_GAMEPLAYATTRIBUTE_REPNOTIFY(ClassName, PropertyName, OldValue) \
	static FProperty* ThisProperty = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
	GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(ThisProperty), PropertyName, OldValue); \
	SyncInfoFromAttribute(Get##PropertyName##Attribute(), Get##PropertyName());

#define SYNC_VIEW_MODEL_BEGIN(PropertyName) \
	if (Attribute == Get##PropertyName##Attribute()) \
	{ \
		ViewModel->Set##PropertyName(Value); \
	}
#define SYNC_VIEW_MODEL(PropertyName) \
	else if (Attribute == Get##PropertyName##Attribute()) \
	{ \
		ViewModel->Set##PropertyName(Value); \
	}

void UXXXAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
	...
	SyncInfoFromAttribute(Attribute, NewValue);
}
void UXXXAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
	XXX_GAMEPLAYATTRIBUTE_REPNOTIFY(UXXXAttributeSet, Health, OldHealth);
}

void UXXXAttributeSetPrimary::SyncInfoFromAttribute(const FGameplayAttribute& Attribute, float Value)
{
	if (ViewModel)
	{
		SYNC_VIEW_MODEL_BEGIN(Health)
		SYNC_VIEW_MODEL(MaxHealth)
	}
}

这样子在属性修改后,各个端都会调用到ViewModel的Set函数

Widget使用

UserWidget来到Designer界面:
在这里插入图片描述
为当前的Widget创建一个ViewModel:
在这里插入图片描述

在这里插入图片描述
进行数据绑定:
在这里插入图片描述
然后就是根据UProperty进行绑定,这里可以直接绑定到ViewModel的某个带有FieldNotify关键字的UFunction:
在这里插入图片描述
需要在ViewModel的Set函数内部,主动触发回调:

UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent);

Widget寻找ViewModel

Widget的ViewModel有4种方式:

UENUM()
enum class EMVVMBlueprintViewModelContextCreationType : uint8
{
	Manual,	// The viewmodel will be assigned later.
	CreateInstance, // A new instance of the viewmodel will be created when the widget is created.
	GlobalViewModelCollection, // The viewmodel exists and is added to the MVVMSubsystem. It will be fetched there.
	PropertyPath, // The viewmodel will be fetched by evaluating a function or a property path.
};

在这里插入图片描述

PropertyPath

以Self作为开始点,调用各种UFunction和UProperty链,获取到目标ViewModel,但是在初始化的时候就需要有。

蓝图定义一个获取ViewModel的函数:(注意需要改为const,并且这个只能拿到当前PlayerController的Pawn)
在这里插入图片描述
然后修改ViewModel的View Model Property Path为刚才的函数:
在这里插入图片描述

Manual

人为设置,代码内在Widget的类上存一个可以拿ViewModel的Object,例如Character,然后蓝图内去调用SetAttributeViewModel,坑的是这个函数是蓝图这边生成的,没法在Cpp那边设置

在这里插入图片描述

CreateInstance

每个Widget都会创建一个,然后别的地方用这个变量去操作

GlobalViewModelCollection

在某个地方创建全局的ViewModel,但是有个麻烦的地方是以字符串作为Key去检索,所以重复性的数据没法弄(例如100个怪的生命值)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值