From e0e7880b9d41c7f0ff2f1c04d0a840a5ada7ac1a Mon Sep 17 00:00:00 2001 From: Alexei Gladkikh Date: Mon, 29 Apr 2024 17:23:43 +0300 Subject: [PATCH] #248 Fixed dynamic materials behaviour --- Source/Cloud9/Character/Cloud9Character.cpp | 32 +++++++++-------- Source/Cloud9/Character/Cloud9Character.h | 8 +++++ .../Cloud9SkeletalMeshComponent.cpp | 34 +++++++++++++++++++ .../Components/Cloud9SkeletalMeshComponent.h | 21 ++++++++++++ 4 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.cpp create mode 100644 Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.h diff --git a/Source/Cloud9/Character/Cloud9Character.cpp b/Source/Cloud9/Character/Cloud9Character.cpp index 4e1ae164b..e3c2862ef 100644 --- a/Source/Cloud9/Character/Cloud9Character.cpp +++ b/Source/Cloud9/Character/Cloud9Character.cpp @@ -47,6 +47,7 @@ #include "Components/Cloud9HealthComponent.h" #include "Components/Cloud9AnimationComponent.h" #include "Components/Cloud9EffectsComponent.h" +#include "Components/Cloud9SkeletalMeshComponent.h" const FName ACloud9Character::SpringArmComponentName = TEXT("CameraBoom"); const FName ACloud9Character::CameraComponentName = TEXT("TopDownCamera"); @@ -58,7 +59,9 @@ const FName ACloud9Character::AnimationComponentName = TEXT("AnimationComponent" const FName ACloud9Character::WidgetInteractionComponentName = TEXT("WidgetInteractionComponent"); ACloud9Character::ACloud9Character(const FObjectInitializer& ObjectInitializer) : Super( - ObjectInitializer.SetDefaultSubobjectClass(CharacterMovementComponentName)) + ObjectInitializer + .SetDefaultSubobjectClass(CharacterMovementComponentName) + .SetDefaultSubobjectClass(MeshComponentName)) { bUseControllerRotationPitch = false; bUseControllerRotationYaw = false; @@ -102,9 +105,9 @@ ACloud9Character::ACloud9Character(const FObjectInitializer& ObjectInitializer) ); CursorToWorld->SetRelativeRotation({CrosshairRotationPitch, 0.0, 0.0f}); - EffectsComponent = CreateDefaultSubobject(EffectsComponentName); InventoryComponent = CreateDefaultSubobject(InventoryComponentName); HealthComponent = CreateDefaultSubobject(HealthComponentName); + EffectsComponent = CreateDefaultSubobject(EffectsComponentName); AnimationComponent = CreateDefaultSubobject(AnimationComponentName); WidgetInteractionComponent = CreateDefaultSubobject(WidgetInteractionComponentName); WidgetInteractionComponent->SetupAttachment(RootComponent); @@ -114,6 +117,7 @@ ACloud9Character::ACloud9Character(const FObjectInitializer& ObjectInitializer) HealthComponent->OnCharacterDie.AddDynamic(this, &ACloud9Character::OnCharacterDie); Score = 0; + bIsPlayer = false; // Activate ticking to update the cursor every frame. PrimaryActorTick.bCanEverTick = true; @@ -281,7 +285,7 @@ UCloud9AnimationComponent* ACloud9Character::GetAnimationComponent() const { ret // ReSharper disable once CppMemberFunctionMayBeConst UCloud9CharacterEffectTrait* ACloud9Character::AddCharacterEffect(TSubclassOf EffectClass) { - return EffectsComponent->AddEffect(EffectClass); + return IsValid(EffectClass) ? EffectsComponent->AddEffect(EffectClass) : nullptr; } // ReSharper disable once CppMemberFunctionMayBeConst @@ -426,17 +430,6 @@ void ACloud9Character::OnConstruction(const FTransform& Transform) MyMesh->bAffectDynamicIndirectLighting = true; MyMesh->SetCollisionProfileName(COLLISION_PROFILE_HITBOX); - MyMesh->GetMaterials() - | ETContainer::WithIndex{} - | ETContainer::Filter{[](let It) { return not Cast(It.Value); }} - | ETContainer::ForEach{ - [&MyMesh](let It) - { - let Dynamic = UMaterialInstanceDynamic::Create(It.Value, MyMesh); - MyMesh->SetMaterial(It.Key, Dynamic); - } - }; - #ifdef USE_PHYSICAL_ASSET_HITBOX // TODO: Make same hit boxes for all character's type - currently disabled let PhysicsAsset = MyMesh->GetPhysicsAsset(); @@ -473,10 +466,21 @@ void ACloud9Character::BeginPlay() if (not IsPlayerControlled()) { + bIsPlayer = false; CursorToWorld->Deactivate(); TopDownCameraComponent->Deactivate(); CameraBoom->Deactivate(); } + else + { + bIsPlayer = true; + } +} + +void ACloud9Character::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + Super::EndPlay(EndPlayReason); + log(Error, "[Character='%s'] EndPlay IsPlayer=%d", *GetName(), bIsPlayer); } void ACloud9Character::Tick(float DeltaSeconds) diff --git a/Source/Cloud9/Character/Cloud9Character.h b/Source/Cloud9/Character/Cloud9Character.h index e5e7edda5..da9a640f0 100644 --- a/Source/Cloud9/Character/Cloud9Character.h +++ b/Source/Cloud9/Character/Cloud9Character.h @@ -74,6 +74,7 @@ class ACloud9Character : public ACharacter virtual void OnConstruction(const FTransform& Transform) override; virtual void BeginPlay() override; + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; virtual void Tick(float DeltaSeconds) override; /** Returns TopDownCameraComponent subobject **/ @@ -208,6 +209,13 @@ class ACloud9Character : public ACharacter UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Effects, meta=(AllowPrivateAccess)) UCloud9EffectsComponent* EffectsComponent; + /** + * Cache variable is this Character a player or a bot + * TODO: Looks like on EndPlay method IsPlayerControlled() returns wrong value + */ + UPROPERTY() + bool bIsPlayer; + /** Current number of frags made by character */ UPROPERTY(BlueprintReadOnly, Category=State, meta=(AllowPrivateAccess)) int Score; diff --git a/Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.cpp b/Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.cpp new file mode 100644 index 000000000..3ff96396e --- /dev/null +++ b/Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2024 Alexei Gladkikh + +#include "Cloud9SkeletalMeshComponent.h" + +#include "Cloud9/Tools/Extensions/TContainer.h" + +void UCloud9SkeletalMeshComponent::BeginPlay() +{ + Super::BeginPlay(); + MakeDynamicMaterials(GetMaterials()); +} + +void UCloud9SkeletalMeshComponent::SetSkeletalMesh(USkeletalMesh* NewMesh, bool bReinitPose) +{ + Super::SetSkeletalMesh(NewMesh, bReinitPose); + let Materials = NewMesh->GetMaterials() + | ETContainer::Transform{[](let It) { return It.MaterialInterface; }} + | ETContainer::ToArray{}; + MakeDynamicMaterials(Materials); +} + +void UCloud9SkeletalMeshComponent::MakeDynamicMaterials(const TArray& Materials) +{ + Materials + | ETContainer::WithIndex{} + | ETContainer::Filter{[](let It) { return not Cast(It.Value); }} + | ETContainer::ForEach{ + [this](let It) + { + let DynamicMaterial = UMaterialInstanceDynamic::Create(It.Value, this); + SetMaterial(It.Key, DynamicMaterial); + } + }; +} diff --git a/Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.h b/Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.h new file mode 100644 index 000000000..8c41a32e9 --- /dev/null +++ b/Source/Cloud9/Character/Components/Cloud9SkeletalMeshComponent.h @@ -0,0 +1,21 @@ +// Copyright (c) 2024 Alexei Gladkikh + +#pragma once + +#include "CoreMinimal.h" +#include "Components/SkeletalMeshComponent.h" +#include "Cloud9SkeletalMeshComponent.generated.h" + +UCLASS() +class CLOUD9_API UCloud9SkeletalMeshComponent : public USkeletalMeshComponent +{ + GENERATED_BODY() + +public: + virtual void BeginPlay() override; + + virtual void SetSkeletalMesh(USkeletalMesh* NewMesh, bool bReinitPose) override; + +protected: + void MakeDynamicMaterials(const TArray& Materials); +};