完成道具基本逻辑

This commit is contained in:
dd
2026-05-31 21:15:04 +08:00
parent 6b6c0dcf95
commit 6e82792e16
41 changed files with 655 additions and 26 deletions
+17
View File
@@ -0,0 +1,17 @@
;METADATA=(Diff=true, UseCommands=true)
[/Script/GameplayTags.GameplayTagsSettings]
ImportTagsFromConfig=True
WarnOnInvalidTags=True
ClearInvalidTags=False
AllowEditorTagUnloading=True
AllowGameTagUnloading=False
FastReplication=False
bDynamicReplication=False
InvalidTagCharacters="\"\',"
NumBitsForContainerSize=6
NetIndexFirstBitSegment=16
+GameplayTagList=(Tag="Attribures.Vital.Health",DevComment="")
+GameplayTagList=(Tag="Attributes.Item.FireBall",DevComment="")
+GameplayTagList=(Tag="Attributes.Vital.Health",DevComment="")
+GameplayTagList=(Tag="Item.Held.Fireball",DevComment="")
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6 -1
View File
@@ -12,7 +12,8 @@
"Engine",
"AIModule",
"UMG",
"GameplayAbilities"
"GameplayAbilities",
"ModelViewViewModel"
]
}
],
@@ -35,6 +36,10 @@
{
"Name": "GameplayAbilities",
"Enabled": true
},
{
"Name": "ModelViewViewModel",
"Enabled": true
}
]
}
+66
View File
@@ -0,0 +1,66 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Item/ItemActor.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
// Sets default values
AItemActor::AItemActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AItemActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AItemActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AItemActor::ApplyEffectToTarget(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffectClass)
{
// 1) 从目标 Actor 身上拿到 AbilitySystemComponentASC
// 只有带 ASC 的对象才能应用 GameplayEffect(比如角色/PlayerState)。
// 这个函数会去找 IAbilitySystemInterface 或组件里挂的 ASC。
UAbilitySystemComponent* TargetASC =
UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
if (TargetASC == nullptr) return;
// 2) 创建一个 EffectContext(效果上下文)
// Context 用来携带“这次效果的来源信息”,比如 Instigator、Causer、SourceObject、HitResult 等
// 以后你想在伤害公式里拿“是谁打的、用什么打的、命中点在哪”都靠它。
FGameplayEffectContextHandle EffectContextHandle = TargetASC->MakeEffectContext();
// 3) 给 Context 塞一个 SourceObject(源对象)
// 这里你传的是 thisAEffectActor),表示“这次效果是由这个 EffectActor 产生的”
// 之后在 ExecutionCalculation / GameplayCue / 逻辑里可以从 Context 拿到这个 SourceObject。
EffectContextHandle.AddSourceObject(this);
// 4) 用 GE 类 + 等级 + Context 生成一个 Spec(效果规格)
// Spec 是“这一次应用的实例参数”,包含:
// - 具体哪个 GE
// - Level(影响 Modifier 计算、曲线、MMC 等)
// - Context(来源信息)
// - 以及 SetByCaller 等动态参数(如果你后面设置的话)
FGameplayEffectSpecHandle EffectSpecHandle =
TargetASC->MakeOutgoingSpec(GameplayEffectClass, 1.f, EffectContextHandle);
// 5) 把这个 Spec 应用到“自己身上”
// 注意:这里是 ToSelf,不是 ToTarget。
// 因为你现在的 TargetASC 就是目标自己的 ASC,所以相当于“目标对自己应用一个 GE”。
// 结果可能是改属性、加 Tag、触发周期、触发 GameplayCue 等,取决于 GE 的配置。
TargetASC->ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());
}
@@ -3,6 +3,27 @@
#include "MainAttributeSet.h"
#include "Net/UnrealNetwork.h"
UMainAttributeSet::UMainAttributeSet()
{
InitHealth(100.f);
InitMaxHealth(100.f);
}
void UMainAttributeSet::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(UMainAttributeSet, Health, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMainAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
}
void UMainAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UMainAttributeSet, Health, OldHealth);
}
void UMainAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UMainAttributeSet, MaxHealth, OldMaxHealth);
}
@@ -0,0 +1,83 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MainMVVMViewModelBase.h"
#include "MainAttributeSet.h"
void UMainMVVMViewModelBase::InitializeWithASC(UAbilitySystemComponent* InASC)
{
UnbindASC();
ASC = InASC;
if (!ASC.IsValid()) return;
SetHealth(ASC->GetNumericAttribute(UMainAttributeSet::GetHealthAttribute()));
SetMaxHealth(ASC->GetNumericAttribute(UMainAttributeSet::GetMaxHealthAttribute()));
BindASC();
}
void UMainMVVMViewModelBase::Uninitialize()
{
UnbindASC();
ASC.Reset();
}
void UMainMVVMViewModelBase::SetHealth(float NewHealth)
{
// 判断值是否有改变
if (UE_MVVM_SET_PROPERTY_VALUE(Health, NewHealth))
{
// 执行广播
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent);
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetCurrentHealthText);
}
}
void UMainMVVMViewModelBase::SetMaxHealth(float NewMaxHealth)
{
if (UE_MVVM_SET_PROPERTY_VALUE(MaxHealth, NewMaxHealth))
{
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent);
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetMaxHealthText);
}
}
void UMainMVVMViewModelBase::BindASC()
{
if (!ASC.IsValid()) return;
// 绑定 Attribute Set 属性变化通知
HealthChangedHandle = ASC->GetGameplayAttributeValueChangeDelegate(UMainAttributeSet::GetHealthAttribute()).AddUObject(this, &ThisClass::OnHealthChanged);
MaxHealthChangedHandle = ASC->GetGameplayAttributeValueChangeDelegate(UMainAttributeSet::GetMaxHealthAttribute()).AddUObject(this, &ThisClass::OnMaxHealthChanged);
}
void UMainMVVMViewModelBase::UnbindASC()
{
if (!ASC.IsValid()) return;
// 解绑
if (HealthChangedHandle.IsValid())
{
ASC->GetGameplayAttributeValueChangeDelegate(UMainAttributeSet::GetHealthAttribute()).Remove(HealthChangedHandle);
}
if (MaxHealthChangedHandle.IsValid())
{
ASC->GetGameplayAttributeValueChangeDelegate(UMainAttributeSet::GetMaxHealthAttribute()).Remove(MaxHealthChangedHandle);
}
HealthChangedHandle.Reset();
MaxHealthChangedHandle.Reset();
}
void UMainMVVMViewModelBase::OnHealthChanged(const struct FOnAttributeChangeData& Data)
{
SetHealth(Data.NewValue);
}
void UMainMVVMViewModelBase::OnMaxHealthChanged(const struct FOnAttributeChangeData& Data)
{
SetMaxHealth(Data.NewValue);
}
@@ -28,3 +28,15 @@ UAttributeSet* AMainPlayerState::GetAttributeSet() const
{
return AttributeSet;
}
float AMainPlayerState::GetHealth() const
{
const UMainAttributeSet* MainAS = Cast<UMainAttributeSet>(AttributeSet);
return MainAS ? MainAS->GetHealth() : 1.f;
}
float AMainPlayerState::GetMaxHealth() const
{
const UMainAttributeSet* MainAS = Cast<UMainAttributeSet>(AttributeSet);
return MainAS ? MainAS->GetMaxHealth() : 1.f;
}
+67
View File
@@ -0,0 +1,67 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestFireBall.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
// Sets default values
ATestFireBall::ATestFireBall()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void ATestFireBall::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ATestFireBall::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATestFireBall::ApplyEffectToTarget(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffectClass)
{
// 1) 从目标 Actor 身上拿到 AbilitySystemComponentASC
// 只有带 ASC 的对象才能应用 GameplayEffect(比如角色/PlayerState)。
// 这个函数会去找 IAbilitySystemInterface 或组件里挂的 ASC。
UAbilitySystemComponent* TargetASC =
UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
if (TargetASC == nullptr) return;
// 2) 创建一个 EffectContext(效果上下文)
// Context 用来携带“这次效果的来源信息”,比如 Instigator、Causer、SourceObject、HitResult 等
// 以后你想在伤害公式里拿“是谁打的、用什么打的、命中点在哪”都靠它。
FGameplayEffectContextHandle EffectContextHandle = TargetASC->MakeEffectContext();
// 3) 给 Context 塞一个 SourceObject(源对象)
// 这里你传的是 thisAEffectActor),表示“这次效果是由这个 EffectActor 产生的”
// 之后在 ExecutionCalculation / GameplayCue / 逻辑里可以从 Context 拿到这个 SourceObject。
EffectContextHandle.AddSourceObject(this);
// 4) 用 GE 类 + 等级 + Context 生成一个 Spec(效果规格)
// Spec 是“这一次应用的实例参数”,包含:
// - 具体哪个 GE
// - Level(影响 Modifier 计算、曲线、MMC 等)
// - Context(来源信息)
// - 以及 SetByCaller 等动态参数(如果你后面设置的话)
FGameplayEffectSpecHandle EffectSpecHandle =
TargetASC->MakeOutgoingSpec(GameplayEffectClass, 1.f, EffectContextHandle);
// 5) 把这个 Spec 应用到“自己身上”
// 注意:这里是 ToSelf,不是 ToTarget。
// 因为你现在的 TargetASC 就是目标自己的 ASC,所以相当于“目标对自己应用一个 GE”。
// 结果可能是改属性、加 Tag、触发周期、触发 GameplayCue 等,取决于 GE 的配置。
TargetASC->ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());
}
@@ -0,0 +1,5 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestGameplayEffect.h"
+33
View File
@@ -0,0 +1,33 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemActor.generated.h"
UCLASS()
class PVPDEMO_API AItemActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AItemActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable)
void ApplyEffectToTarget(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffectClass);
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<UGameplayEffect> InstantGameplayEffectClass;
};
+20
View File
@@ -3,6 +3,7 @@
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "AttributeSet.h"
#include "MainAttributeSet.generated.h"
@@ -15,4 +16,23 @@ class PVPDEMO_API UMainAttributeSet : public UAttributeSet
GENERATED_BODY()
public:
UMainAttributeSet();
virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health)
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS_BASIC(UMainAttributeSet, Health) // 生成一组 Attribute 的访问器函数
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth)
FGameplayAttributeData MaxHealth;
ATTRIBUTE_ACCESSORS_BASIC(UMainAttributeSet, MaxHealth)
public:
// 2. 定义复制的函数
UFUNCTION()
void OnRep_Health(const FGameplayAttributeData& OldHealth) const;
UFUNCTION()
void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const;
};
@@ -0,0 +1,76 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "MVVMViewModelBase.h"
#include "MainMVVMViewModelBase.generated.h"
class UAbilitySystemComponent;
/**
*
*/
UCLASS(BlueprintType)
class PVPDEMO_API UMainMVVMViewModelBase : public UMVVMViewModelBase
{
GENERATED_BODY()
public:
// 通过Attribute Set初始化
UFUNCTION(BlueprintCallable, Category = "GASM VVM View Model")
void InitializeWithASC(UAbilitySystemComponent* InASC);
UFUNCTION(BlueprintCallable, Category = "GASM VVM View Model")
void Uninitialize();
// 变量的 Get/Set 函数
float GetHealth() const { return Health; }
void SetHealth(float NewHealth);
float GetMaxHealth() const { return MaxHealth; }
void SetMaxHealth(float NewMaxHealth);
// 带通知的函数
UFUNCTION(BlueprintPure, FieldNotify)
float GetHealthPercent() const
{
return (MaxHealth > 0.f) ? (Health / MaxHealth) : 0.f;
}
UFUNCTION(BlueprintPure, FieldNotify)
FText GetCurrentHealthText() const
{
return FText::AsNumber(FMath::RoundToInt(Health));
}
UFUNCTION(BlueprintPure, FieldNotify)
FText GetMaxHealthText() const
{
return FText::AsNumber(FMath::RoundToInt(MaxHealth));
}
private:
// 来自Attribute Set的属性
UPROPERTY(BlueprintReadWrite, FieldNotify, Getter, Setter, meta=(AllowPrivateAccess="true"))
float Health;
UPROPERTY(BlueprintReadWrite, FieldNotify, Getter, Setter, meta=(AllowPrivateAccess="true"))
float MaxHealth;
private:
// ASC相关
TWeakObjectPtr<UAbilitySystemComponent> ASC;
FDelegateHandle HealthChangedHandle;
FDelegateHandle MaxHealthChangedHandle;
// FGameplayTag StunTag;
void BindASC();
void UnbindASC();
void OnHealthChanged(const struct FOnAttributeChangeData& Data);
void OnMaxHealthChanged(const struct FOnAttributeChangeData& Data);
};
+6
View File
@@ -26,6 +26,12 @@ public:
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
UAttributeSet* GetAttributeSet() const;
UFUNCTION(BlueprintPure, Category="GAS|Attributes")
float GetHealth() const;
UFUNCTION(BlueprintPure, Category="GAS|Attributes")
float GetMaxHealth() const;
protected:
// ASC
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
+34
View File
@@ -0,0 +1,34 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestFireBall.generated.h"
UCLASS()
class PVPDEMO_API ATestFireBall : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATestFireBall();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
protected:
UFUNCTION(BlueprintCallable)
void ApplyEffectToTarget(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffectClass);
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<UGameplayEffect> InstantGameplayEffectClass;
};
@@ -0,0 +1,17 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameplayEffect.h"
#include "TestGameplayEffect.generated.h"
/**
*
*/
UCLASS()
class PVPDEMO_API UTestGameplayEffect : public UGameplayEffect
{
GENERATED_BODY()
};
+3 -1
View File
@@ -21,7 +21,9 @@ public class PvPDemo : ModuleRules
"Slate",
"GameplayAbilities",
"GameplayTags",
"GameplayTasks"
"GameplayTasks",
"ModelViewViewModel",
"FieldNotification"
});
PrivateDependencyModuleNames.AddRange(new string[] { });
+55
View File
@@ -14,6 +14,7 @@
#include "PvPDemo.h"
#include "AbilitySystemComponent.h"
#include "AttributeSet.h"
#include "Kismet/KismetSystemLibrary.h"
APvPDemoCharacter::APvPDemoCharacter()
@@ -85,6 +86,9 @@ void APvPDemoCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo
// Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &APvPDemoCharacter::Look);
// Attack
// EnhancedInputComponent->BindAction(AttackAction, ETriggerEvent::Started, this, &APvPDemoCharacter::DoAttack);
}
else
{
@@ -152,6 +156,14 @@ void APvPDemoCharacter::DoJumpEnd()
StopJumping();
}
// void APvPDemoCharacter::DoAttack()
// {
// if (AbilitySystemComponent)
// {
// AbilitySystemComponent->TryActivateAbilityByClass(HeldItem);
// }
// }
UAbilitySystemComponent* APvPDemoCharacter::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
@@ -166,4 +178,47 @@ void APvPDemoCharacter::InitAbilityActorInfo()
MainPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(MainPlayerState, this);
AbilitySystemComponent = MainPlayerState->GetAbilitySystemComponent();
AttributeSet = MainPlayerState->GetAttributeSet();
if (HasAuthority() && AbilitySystemComponent && HeldItem)
{
AbilitySystemComponent->GiveAbility(
FGameplayAbilitySpec(HeldItem, 1, 0)
);
}
}
bool APvPDemoCharacter::AddHeldItemTag(FGameplayTag Tag)
{
if (!HasAuthority())
{
return false;
}
UAbilitySystemComponent* ASC = GetAbilitySystemComponent();
if (!ASC)
{
return false;
}
ASC->AddLooseGameplayTag(Tag);
return true;
}
bool APvPDemoCharacter::RemoveHeldItemTag(FGameplayTag Tag)
{
if (!HasAuthority())
{
return false;
}
UAbilitySystemComponent* ASC = GetAbilitySystemComponent();
if (!ASC)
{
return false;
}
ASC->RemoveLooseGameplayTag(Tag);
return true;
}
+17
View File
@@ -14,6 +14,7 @@ class UInputAction;
class UAbilitySystemComponent;
class UAttributeSet;
struct FInputActionValue;
class UGameplayAbility;
DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);
@@ -52,6 +53,9 @@ protected:
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* MouseLookAction;
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* AttackAction;
// ASC
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
@@ -65,6 +69,7 @@ public:
/** Constructor */
APvPDemoCharacter();
UFUNCTION(BlueprintCallable)
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
virtual void PossessedBy(AController* NewController) override;
@@ -100,6 +105,9 @@ public:
/** Handles jump pressed inputs from either controls or UI interfaces */
UFUNCTION(BlueprintCallable, Category="Input")
virtual void DoJumpEnd();
// UFUNCTION(BlueprintCallable, Category="Input")
// virtual void DoAttack();
public:
@@ -109,7 +117,16 @@ public:
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Items")
TSubclassOf<UGameplayAbility> HeldItem;
private:
void InitAbilityActorInfo();
UFUNCTION(BlueprintCallable)
bool AddHeldItemTag(FGameplayTag Tag);
UFUNCTION(BlueprintCallable)
bool RemoveHeldItemTag(FGameplayTag Tag);
};
+105 -24
View File
@@ -2,41 +2,25 @@
#include "PvPDemoPlayerController.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystemInterface.h"
#include "EnhancedInputSubsystems.h"
#include "Engine/LocalPlayer.h"
#include "InputMappingContext.h"
#include "Blueprint/UserWidget.h"
#include "INotifyFieldValueChanged.h"
#include "MainMVVMViewModelBase.h"
#include "MainPlayerState.h"
#include "MVVMSubsystem.h"
#include "PvPDemo.h"
#include "View/MVVMView.h"
#include "View/MVVMViewClass.h"
#include "Widgets/Input/SVirtualJoystick.h"
void APvPDemoPlayerController::BeginPlay()
{
Super::BeginPlay();
if (IsLocalPlayerController())
{
TSubclassOf<UUserWidget> MainWidgetClass = LoadClass<UUserWidget>(nullptr, TEXT("/Game/PvPDemo/Blueprints/UMG/WBP_MainUI.WBP_MainUI_C"));
if (MainWidgetClass)
{
MainWidget = CreateWidget<UUserWidget>(this, MainWidgetClass);
if (MainWidget)
{
MainWidget->AddToPlayerScreen(0);
} else {
UE_LOG(LogPvPDemo, Error, TEXT("Could not spawn main widget."));
}
} else {
UE_LOG(LogPvPDemo, Error, TEXT("Could not load main widget class."));
}
}
// only spawn touch controls on local player controllers
if (ShouldUseTouchControls() && IsLocalPlayerController())
{
@@ -57,6 +41,11 @@ void APvPDemoPlayerController::BeginPlay()
}
}
void APvPDemoPlayerController::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
}
void APvPDemoPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
@@ -89,3 +78,95 @@ bool APvPDemoPlayerController::ShouldUseTouchControls() const
// are we on a mobile platform? Should we force touch?
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
}
UAbilitySystemComponent* APvPDemoPlayerController::GetMainWidgetAbilitySystemComponent() const
{
if (const AMainPlayerState* MainPlayerState = GetPlayerState<AMainPlayerState>())
{
return MainPlayerState->GetAbilitySystemComponent();
}
if (const APawn* ControlledPawn = GetPawn())
{
if (const IAbilitySystemInterface* AbilitySystemInterface = Cast<IAbilitySystemInterface>(ControlledPawn))
{
return AbilitySystemInterface->GetAbilitySystemComponent();
}
}
return nullptr;
}
UMainMVVMViewModelBase* APvPDemoPlayerController::GetOrCreateMainWidgetViewModel()
{
if (!MainWidget)
{
return nullptr;
}
UMVVMView* MVVMView = UMVVMSubsystem::GetViewFromUserWidget(MainWidget.Get());
if (!MVVMView)
{
UE_LOG(LogPvPDemo, Warning, TEXT("Main widget does not have an MVVM view."));
return nullptr;
}
MVVMView->InitializeSources();
for (const FMVVMView_Source& Source : MVVMView->GetSources())
{
if (UMainMVVMViewModelBase* ExistingViewModel = Cast<UMainMVVMViewModelBase>(Source.Source))
{
MainWidgetViewModel = ExistingViewModel;
return MainWidgetViewModel;
}
}
const UMVVMViewClass* ViewClass = MVVMView->GetViewClass();
if (!ViewClass)
{
return nullptr;
}
const FMVVMViewClass_Source* MatchingSource = nullptr;
for (const FMVVMViewClass_Source& ClassSource : ViewClass->GetSources())
{
UClass* SourceClass = ClassSource.GetSourceClass();
if (ClassSource.IsViewModel() && SourceClass && SourceClass->IsChildOf(UMainMVVMViewModelBase::StaticClass()))
{
if (MatchingSource)
{
UE_LOG(LogPvPDemo, Warning, TEXT("Main widget has more than one MainMVVMViewModelBase-compatible MVVM source."));
return nullptr;
}
MatchingSource = &ClassSource;
}
}
if (!MatchingSource)
{
UE_LOG(LogPvPDemo, Warning, TEXT("Main widget does not declare a MainMVVMViewModelBase MVVM source."));
return nullptr;
}
if (!MatchingSource->CanBeSet())
{
UE_LOG(LogPvPDemo, Warning, TEXT("Main widget MainMVVMViewModelBase MVVM source is not settable and no instance was created."));
return nullptr;
}
MainWidgetViewModel = NewObject<UMainMVVMViewModelBase>(this, MatchingSource->GetSourceClass());
TScriptInterface<INotifyFieldValueChanged> ViewModelInterface;
ViewModelInterface.SetObject(MainWidgetViewModel);
ViewModelInterface.SetInterface(Cast<INotifyFieldValueChanged>(MainWidgetViewModel));
if (!MVVMView->SetViewModel(MatchingSource->GetName(), ViewModelInterface))
{
MainWidgetViewModel = nullptr;
return nullptr;
}
return MainWidgetViewModel;
}
+12
View File
@@ -4,10 +4,13 @@
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "TimerManager.h"
#include "PvPDemoPlayerController.generated.h"
class UInputMappingContext;
class UUserWidget;
class UAbilitySystemComponent;
class UMainMVVMViewModelBase;
/**
* Basic PlayerController class for a third person game
@@ -40,6 +43,10 @@ protected:
UPROPERTY()
TObjectPtr<UUserWidget> MainWidget;
/** ViewModel used by the main widget when the widget declares it as a manual MVVM source. */
UPROPERTY()
TObjectPtr<UMainMVVMViewModelBase> MainWidgetViewModel;
/** If true, the player will use UMG touch controls even if not playing on mobile platforms */
UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
bool bForceTouchControls = false;
@@ -47,10 +54,15 @@ protected:
/** Gameplay initialization */
virtual void BeginPlay() override;
virtual void OnRep_PlayerState() override;
/** Input mapping context setup */
virtual void SetupInputComponent() override;
/** Returns true if the player should use UMG touch controls */
bool ShouldUseTouchControls() const;
UAbilitySystemComponent* GetMainWidgetAbilitySystemComponent() const;
UMainMVVMViewModelBase* GetOrCreateMainWidgetViewModel();
};