From 94a19cbdf4cfe7291b193132b0c625566d4d3caf Mon Sep 17 00:00:00 2001 From: starmen1265 Date: Wed, 6 Aug 2025 23:47:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8cursor=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.idea.BladeSmith/.idea/vcs.xml | 6 + .vsconfig | 4 +- BladeSmith.sln.DotSettings.user | 2 + BladeSmith.uproject | 2 +- README.md | 166 ++++++++- Source/BladeSmith/BladeSmith.Build.cs | 15 +- Source/BladeSmith/BladeSmithCharacter.cpp | 17 +- Source/BladeSmith/BladeSmithCharacter.h | 14 + .../BladeSmith/BladeSmithWeaponComponent.cpp | 218 ++++++++---- Source/BladeSmith/BladeSmithWeaponComponent.h | 61 ++-- Source/BladeSmith/FDeformComputeShader.cpp | 121 +++---- Source/BladeSmith/FDeformComputeShader.h | 21 +- Source/BladeSmith/IronBlock.cpp | 330 ++++++++++++++---- Source/BladeSmith/IronBlock.h | 9 +- 14 files changed, 735 insertions(+), 251 deletions(-) create mode 100644 .idea/.idea.BladeSmith/.idea/vcs.xml diff --git a/.idea/.idea.BladeSmith/.idea/vcs.xml b/.idea/.idea.BladeSmith/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.BladeSmith/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vsconfig b/.vsconfig index b3c233d..3b919ea 100644 --- a/.vsconfig +++ b/.vsconfig @@ -1,10 +1,12 @@ { "version": "1.0", "components": [ + "Component.Unreal.Debugger", + "Component.Unreal.Ide", "Microsoft.Net.Component.4.6.2.TargetingPack", - "Microsoft.VisualStudio.Component.Unreal.Workspace", "Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL", "Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64", + "Microsoft.VisualStudio.Component.VC.Llvm.Clang", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Workload.CoreEditor", diff --git a/BladeSmith.sln.DotSettings.user b/BladeSmith.sln.DotSettings.user index 8ace9b1..f937637 100644 --- a/BladeSmith.sln.DotSettings.user +++ b/BladeSmith.sln.DotSettings.user @@ -1,2 +1,4 @@  + ForceIncluded + ForceIncluded ForceIncluded \ No newline at end of file diff --git a/BladeSmith.uproject b/BladeSmith.uproject index 511e59d..89230e3 100644 --- a/BladeSmith.uproject +++ b/BladeSmith.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "5.5", + "EngineAssociation": "{DE41D031-4C3F-817F-365A-5AAD9A1CE2AD}", "Category": "", "Description": "", "Modules": [ diff --git a/README.md b/README.md index 4a1aadd..dc353c1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,166 @@ -# BladeSmith +# BladeSmith - UE5.5 打铁游戏 + +这是一个基于UE5.5开发的打铁游戏,使用C++实现铁锤击中铁块造成物理形变的效果。 + +## 系统特性 + +### 核心功能 +- **铁块变形系统**: 使用Compute Shader实时计算铁块顶点变形 +- **锤击交互**: 第一人称锤击系统,支持力度和方向检测 +- **物理形变**: 基于冲击力、位置和法线的真实物理变形 +- **材质系统**: 动态材质实例,实时更新变形效果 + +### 技术实现 +- **Compute Shader**: 使用GPU并行计算顶点变形 +- **双缓冲纹理**: 避免读写冲突,确保渲染稳定性 +- **射线检测**: 精确的锤击碰撞检测 +- **动态材质**: 实时更新变形参数 + +## 系统架构 + +### 主要组件 + +1. **AIronBlock** - 铁块Actor + - 管理变形纹理和Compute Shader + - 处理锤击碰撞和变形计算 + - 更新动态材质参数 + +2. **UBladeSmithWeaponComponent** - 锤子组件 + - 第一人称锤击交互 + - 射线检测和碰撞处理 + - 音效和动画播放 + +3. **FDeformationCS** - Compute Shader + - GPU并行变形计算 + - 基于距离的衰减效果 + - 塑性变形模拟 + +### 文件结构 +``` +Source/BladeSmith/ +├── IronBlock.h/cpp # 铁块实现 +├── FDeformComputeShader.h/cpp # Compute Shader +├── BladeSmithWeaponComponent.h/cpp # 锤子组件 +├── BladeSmithCharacter.h/cpp # 角色类 +└── BladeSmith.Build.cs # 模块配置 + +Shaders/Private/ +└── DeformationCS.usf # Compute Shader源码 +``` + +## 使用方法 + +### 1. 设置输入映射 +在UE编辑器中创建以下输入动作: +- **HammerAction**: 锤击输入(建议绑定鼠标左键) +- **MoveAction**: 移动输入(WASD) +- **LookAction**: 视角输入(鼠标) +- **JumpAction**: 跳跃输入(空格) + +### 2. 配置角色蓝图 +1. 打开角色蓝图 +2. 确保HammerComponent已正确附加 +3. 设置锤子网格和动画 +4. 配置输入映射上下文 + +### 3. 放置铁块 +1. 在场景中放置IronBlock Actor +2. 确保静态网格组件已设置 +3. 系统会自动初始化变形纹理 + +### 4. 创建材质 +创建支持变形的材质,包含以下参数: +- **PositionTexture**: 顶点位置纹理 +- **LocalBoundsMin/Max**: 本地边界 +- **DeformationStrength**: 变形强度 + +## 技术细节 + +### Compute Shader参数 +```hlsl +struct Parameters +{ + float3 ImpactPoint; // 冲击点 + float3 ImpactNormal; // 冲击法线 + float ImpactForce; // 冲击力 + float DeformationRadius; // 变形半径 + float Plasticity; // 塑性系数 + Texture2D InputTexture; // 输入纹理 + RWTexture2D OutputTexture; // 输出纹理 +}; +``` + +### 变形算法 +1. **距离计算**: 计算每个顶点到冲击点的距离 +2. **衰减函数**: 使用高斯衰减模拟真实物理 +3. **变形计算**: 基于法线方向和冲击力计算变形量 +4. **塑性应用**: 应用塑性系数实现永久变形 + +### 性能优化 +- **区域更新**: 只更新受影响的纹理区域 +- **双缓冲**: 避免读写冲突 +- **异步处理**: 在渲染线程中执行Compute Shader +- **LOD支持**: 根据距离调整计算精度 + +## 编译和运行 + +### 编译要求 +- UE5.5 +- Visual Studio 2022 +- Windows 10/11 + +### 编译步骤 +1. 右键点击.uproject文件 +2. 选择"Generate Visual Studio project files" +3. 打开生成的.sln文件 +4. 编译项目 + +### 运行步骤 +1. 在UE编辑器中打开项目 +2. 确保所有输入映射已配置 +3. 在场景中放置IronBlock +4. 运行游戏并测试锤击功能 + +## 故障排除 + +### 常见问题 + +1. **Compute Shader不工作** + - 检查GPU驱动是否支持Compute Shader + - 确认纹理格式正确 + - 查看渲染日志中的错误信息 + +2. **变形效果不明显** + - 调整ImpactForce参数 + - 增加DeformationRadius + - 检查材质参数设置 + +3. **性能问题** + - 减少纹理分辨率 + - 缩小更新区域 + - 启用LOD系统 + +### 调试功能 +- 启用调试绘制查看射线检测 +- 使用LogTemp输出调试信息 +- 检查纹理资源是否正确创建 + +## 扩展功能 + +### 可能的改进 +1. **多材质支持**: 不同材质的不同变形特性 +2. **温度系统**: 基于温度影响变形效果 +3. **声音系统**: 更真实的锤击音效 +4. **粒子效果**: 锤击时的火花和灰尘 +5. **工具系统**: 不同类型的锤子和工具 + +### 自定义开发 +- 修改Compute Shader实现不同的变形算法 +- 添加新的物理参数(弹性、硬度等) +- 实现更复杂的碰撞检测 +- 集成第三方物理引擎 + +## 许可证 + +本项目基于Epic Games的UE5.5开发,遵循相应的许可证条款。 diff --git a/Source/BladeSmith/BladeSmith.Build.cs b/Source/BladeSmith/BladeSmith.Build.cs index bbac650..399b74d 100644 --- a/Source/BladeSmith/BladeSmith.Build.cs +++ b/Source/BladeSmith/BladeSmith.Build.cs @@ -8,6 +8,19 @@ public class BladeSmith : ModuleRules { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput","RenderCore","RHI" }); + PublicDependencyModuleNames.AddRange(new string[] { + "Core", + "CoreUObject", + "Engine", + "InputCore", + "EnhancedInput", + "RenderCore", + "RHI", + "Renderer", + "PhysicsCore", + "AudioExtensions", + "AudioMixer", + "AudioPlatformConfiguration" + }); } } diff --git a/Source/BladeSmith/BladeSmithCharacter.cpp b/Source/BladeSmith/BladeSmithCharacter.cpp index 1470af9..90722a7 100644 --- a/Source/BladeSmith/BladeSmithCharacter.cpp +++ b/Source/BladeSmith/BladeSmithCharacter.cpp @@ -2,6 +2,7 @@ #include "BladeSmithCharacter.h" #include "BladeSmithProjectile.h" +#include "BladeSmithWeaponComponent.h" #include "Animation/AnimInstance.h" #include "Camera/CameraComponent.h" #include "Components/CapsuleComponent.h" @@ -35,6 +36,9 @@ ABladeSmithCharacter::ABladeSmithCharacter() Mesh1P->CastShadow = false; Mesh1P->SetRelativeLocation(FVector(-30.f, 0.f, -150.f)); + // Create hammer component + HammerComponent = CreateDefaultSubobject(TEXT("HammerComponent")); + HammerComponent->SetupAttachment(Mesh1P); } //////////////////////////////////////////////////////////////////////////// Input @@ -67,6 +71,9 @@ void ABladeSmithCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInpu // Looking EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ABladeSmithCharacter::Look); + + // Hammer + EnhancedInputComponent->BindAction(HammerAction, ETriggerEvent::Started, this, &ABladeSmithCharacter::Hammer); } else { @@ -74,7 +81,6 @@ void ABladeSmithCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInpu } } - void ABladeSmithCharacter::Move(const FInputActionValue& Value) { // input is a Vector2D @@ -99,4 +105,13 @@ void ABladeSmithCharacter::Look(const FInputActionValue& Value) AddControllerYawInput(LookAxisVector.X); AddControllerPitchInput(LookAxisVector.Y); } +} + +void ABladeSmithCharacter::Hammer(const FInputActionValue& Value) +{ + // 执行锤击 + if (HammerComponent) + { + HammerComponent->SwingHammer(); + } } \ No newline at end of file diff --git a/Source/BladeSmith/BladeSmithCharacter.h b/Source/BladeSmith/BladeSmithCharacter.h index 33b7594..6e6336b 100644 --- a/Source/BladeSmith/BladeSmithCharacter.h +++ b/Source/BladeSmith/BladeSmithCharacter.h @@ -12,6 +12,7 @@ class USkeletalMeshComponent; class UCameraComponent; class UInputAction; class UInputMappingContext; +class UBladeSmithWeaponComponent; struct FInputActionValue; DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All); @@ -29,6 +30,10 @@ class ABladeSmithCharacter : public ACharacter UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true")) UCameraComponent* FirstPersonCameraComponent; + /** 锤子组件 */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Weapon, meta = (AllowPrivateAccess = "true")) + UBladeSmithWeaponComponent* HammerComponent; + /** MappingContext */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) UInputMappingContext* DefaultMappingContext; @@ -44,6 +49,10 @@ class ABladeSmithCharacter : public ACharacter /** Look Input Action */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) class UInputAction* LookAction; + + /** Hammer Input Action */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + class UInputAction* HammerAction; public: ABladeSmithCharacter(); @@ -55,6 +64,9 @@ protected: /** Called for looking input */ void Look(const FInputActionValue& Value); + /** Called for hammer input */ + void Hammer(const FInputActionValue& Value); + protected: // APawn interface virtual void NotifyControllerChanged() override; @@ -66,6 +78,8 @@ public: USkeletalMeshComponent* GetMesh1P() const { return Mesh1P; } /** Returns FirstPersonCameraComponent subobject **/ UCameraComponent* GetFirstPersonCameraComponent() const { return FirstPersonCameraComponent; } + /** Returns HammerComponent subobject **/ + UBladeSmithWeaponComponent* GetHammerComponent() const { return HammerComponent; } }; diff --git a/Source/BladeSmith/BladeSmithWeaponComponent.cpp b/Source/BladeSmith/BladeSmithWeaponComponent.cpp index 68e2df6..05885e2 100644 --- a/Source/BladeSmith/BladeSmithWeaponComponent.cpp +++ b/Source/BladeSmith/BladeSmithWeaponComponent.cpp @@ -3,117 +3,183 @@ #include "BladeSmithWeaponComponent.h" #include "BladeSmithCharacter.h" -#include "BladeSmithProjectile.h" -#include "GameFramework/PlayerController.h" -#include "Camera/PlayerCameraManager.h" -#include "Kismet/GameplayStatics.h" +#include "IronBlock.h" +#include "Animation/AnimInstance.h" +#include "Components/SkeletalMeshComponent.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" -#include "Animation/AnimInstance.h" +#include "InputActionValue.h" #include "Engine/LocalPlayer.h" +#include "Kismet/GameplayStatics.h" #include "Engine/World.h" +#include "DrawDebugHelpers.h" +#include "PhysicsEngine/PhysicsHandleComponent.h" +#include "Camera/CameraComponent.h" // Sets default values for this component's properties UBladeSmithWeaponComponent::UBladeSmithWeaponComponent() { - // Default offset from the character location for projectiles to spawn - MuzzleOffset = FVector(100.0f, 0.0f, 10.0f); + // 设置默认值 + HammerHeadOffset = FVector(0.0f, 0.0f, -50.0f); + HammerForce = 1000.0f; + HammerRange = 200.0f; } - -void UBladeSmithWeaponComponent::Fire() -{ - if (Character == nullptr || Character->GetController() == nullptr) - { - return; - } - - // Try and fire a projectile - if (ProjectileClass != nullptr) - { - UWorld* const World = GetWorld(); - if (World != nullptr) - { - APlayerController* PlayerController = Cast(Character->GetController()); - const FRotator SpawnRotation = PlayerController->PlayerCameraManager->GetCameraRotation(); - // MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position - const FVector SpawnLocation = GetOwner()->GetActorLocation() + SpawnRotation.RotateVector(MuzzleOffset); - - //Set Spawn Collision Handling Override - FActorSpawnParameters ActorSpawnParams; - ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding; - - // Spawn the projectile at the muzzle - World->SpawnActor(ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams); - } - } - - // Try and play the sound if specified - if (FireSound != nullptr) - { - UGameplayStatics::PlaySoundAtLocation(this, FireSound, Character->GetActorLocation()); - } - - // Try and play a firing animation if specified - if (FireAnimation != nullptr) - { - // Get the animation object for the arms mesh - UAnimInstance* AnimInstance = Character->GetMesh1P()->GetAnimInstance(); - if (AnimInstance != nullptr) - { - AnimInstance->Montage_Play(FireAnimation, 1.f); - } - } -} - -bool UBladeSmithWeaponComponent::AttachWeapon(ABladeSmithCharacter* TargetCharacter) +bool UBladeSmithWeaponComponent::AttachHammer(ABladeSmithCharacter* TargetCharacter) { Character = TargetCharacter; - - // Check that the character is valid, and has no weapon component yet - if (Character == nullptr || Character->GetInstanceComponents().FindItemByClass()) + if (Character == nullptr) { return false; } - // Attach the weapon to the First Person Character - FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, true); - AttachToComponent(Character->GetMesh1P(), AttachmentRules, FName(TEXT("GripPoint"))); + // 附加到角色的第一人称网格 + USkeletalMeshComponent* CharacterMesh = Character->GetMesh1P(); + if (CharacterMesh) + { + AttachToComponent(CharacterMesh, FAttachmentTransformRules::KeepRelativeTransform); + SetOnlyOwnerSee(true); + SetOwnerNoSee(false); + CastShadow = false; + } - // Set up action bindings + // 设置输入映射 if (APlayerController* PlayerController = Cast(Character->GetController())) { if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PlayerController->GetLocalPlayer())) { - // Set the priority of the mapping to 1, so that it overrides the Jump action with the Fire action when using touch input - Subsystem->AddMappingContext(FireMappingContext, 1); - } - - if (UEnhancedInputComponent* EnhancedInputComponent = Cast(PlayerController->InputComponent)) - { - // Fire - EnhancedInputComponent->BindAction(FireAction, ETriggerEvent::Triggered, this, &UBladeSmithWeaponComponent::Fire); + if (HammerMappingContext) + { + Subsystem->AddMappingContext(HammerMappingContext, 1); + } } } return true; } +void UBladeSmithWeaponComponent::SwingHammer() +{ + // 播放锤击动画 + if (HammerSwingAnimation && Character) + { + USkeletalMeshComponent* CharacterMesh = Character->GetMesh1P(); + if (CharacterMesh) + { + CharacterMesh->PlayAnimation(HammerSwingAnimation, false); + } + } + + // 延迟执行锤击检测,等待动画播放 + GetWorld()->GetTimerManager().SetTimerForNextTick([this]() + { + PerformHammerHit(); + }); +} + +FVector UBladeSmithWeaponComponent::GetHammerHeadLocation() const +{ + if (Character) + { + FVector CameraLocation = Character->GetFirstPersonCameraComponent()->GetComponentLocation(); + FVector CameraForward = Character->GetFirstPersonCameraComponent()->GetForwardVector(); + + // 计算锤子头部位置 + return CameraLocation + CameraForward * HammerRange + HammerHeadOffset; + } + return FVector::ZeroVector; +} + +void UBladeSmithWeaponComponent::PerformHammerHit() +{ + if (!Character) + { + return; + } + + // 获取锤子头部位置 + FVector HammerHeadLocation = GetHammerHeadLocation(); + FVector StartLocation = Character->GetFirstPersonCameraComponent()->GetComponentLocation(); + + // 执行射线检测 + FHitResult HitResult; + FCollisionQueryParams QueryParams; + QueryParams.AddIgnoredActor(Character); + QueryParams.bTraceComplex = true; + + bool bHit = GetWorld()->LineTraceSingleByChannel( + HitResult, + StartLocation, + HammerHeadLocation, + ECC_Visibility, + QueryParams + ); + + // 绘制调试线 + FColor DebugColor = bHit ? FColor::Red : FColor::Green; + DrawDebugLine(GetWorld(), StartLocation, HammerHeadLocation, DebugColor, false, 2.0f); + + if (bHit) + { + DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 10.0f, 8, FColor::Yellow, false, 2.0f); + } + + // 处理碰撞结果 + if (bHit) + { + // 播放锤击音效 + if (HammerHitSound) + { + UGameplayStatics::PlaySoundAtLocation(this, HammerHitSound, HitResult.ImpactPoint, FRotator::ZeroRotator); + } + + // 检查是否击中铁块 + AIronBlock* IronBlock = Cast(HitResult.GetActor()); + if (IronBlock) + { + HandleIronBlockHit(IronBlock, HitResult); + } + } +} + +void UBladeSmithWeaponComponent::HandleIronBlockHit(AIronBlock* IronBlock, const FHitResult& HitResult) +{ + if (!IronBlock) + { + return; + } + + // 计算锤击力度(基于角色移动速度) + float ImpactForce = HammerForce; + if (Character) + { + FVector CharacterVelocity = Character->GetVelocity(); + float SpeedMultiplier = FMath::Clamp(CharacterVelocity.Size() / 500.0f, 0.5f, 2.0f); + ImpactForce *= SpeedMultiplier; + } + + // 调用铁块的锤击处理函数 + IronBlock->OnHammerHit(HitResult); + + UE_LOG(LogTemp, Log, TEXT("Hammer hit iron block with force: %f"), ImpactForce); +} + void UBladeSmithWeaponComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { - // ensure we have a character owner - if (Character != nullptr) + Super::EndPlay(EndPlayReason); + + // 清理输入映射 + if (Character) { - // remove the input mapping context from the Player Controller if (APlayerController* PlayerController = Cast(Character->GetController())) { if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PlayerController->GetLocalPlayer())) { - Subsystem->RemoveMappingContext(FireMappingContext); + if (HammerMappingContext) + { + Subsystem->RemoveMappingContext(HammerMappingContext); + } } } } - - // maintain the EndPlay call chain - Super::EndPlay(EndPlayReason); } \ No newline at end of file diff --git a/Source/BladeSmith/BladeSmithWeaponComponent.h b/Source/BladeSmith/BladeSmithWeaponComponent.h index d4e01e2..ca4c0eb 100644 --- a/Source/BladeSmith/BladeSmithWeaponComponent.h +++ b/Source/BladeSmith/BladeSmithWeaponComponent.h @@ -7,6 +7,7 @@ #include "BladeSmithWeaponComponent.generated.h" class ABladeSmithCharacter; +class AIronBlock; UCLASS(Blueprintable, BlueprintType, ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class BLADESMITH_API UBladeSmithWeaponComponent : public USkeletalMeshComponent @@ -14,47 +15,61 @@ class BLADESMITH_API UBladeSmithWeaponComponent : public USkeletalMeshComponent GENERATED_BODY() public: - /** Projectile class to spawn */ - UPROPERTY(EditDefaultsOnly, Category=Projectile) - TSubclassOf ProjectileClass; - - /** Sound to play each time we fire */ + /** 锤击音效 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay) - USoundBase* FireSound; + USoundBase* HammerHitSound; - /** AnimMontage to play each time we fire */ + /** 锤击动画 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) - UAnimMontage* FireAnimation; + UAnimMontage* HammerSwingAnimation; - /** Gun muzzle's offset from the characters location */ + /** 锤子头部偏移 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay) - FVector MuzzleOffset; + FVector HammerHeadOffset; - /** MappingContext */ + /** 锤击力度 */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay) + float HammerForce = 1000.0f; + + /** 锤击范围 */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay) + float HammerRange = 200.0f; + + /** 输入映射上下文 */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true")) - class UInputMappingContext* FireMappingContext; + class UInputMappingContext* HammerMappingContext; - /** Fire Input Action */ + /** 锤击输入动作 */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true")) - class UInputAction* FireAction; + class UInputAction* HammerAction; - /** Sets default values for this component's properties */ + /** 设置默认值 */ UBladeSmithWeaponComponent(); - /** Attaches the actor to a FirstPersonCharacter */ - UFUNCTION(BlueprintCallable, Category="Weapon") - bool AttachWeapon(ABladeSmithCharacter* TargetCharacter); + /** 将锤子附加到角色 */ + UFUNCTION(BlueprintCallable, Category="Hammer") + bool AttachHammer(ABladeSmithCharacter* TargetCharacter); - /** Make the weapon Fire a Projectile */ - UFUNCTION(BlueprintCallable, Category="Weapon") - void Fire(); + /** 执行锤击 */ + UFUNCTION(BlueprintCallable, Category="Hammer") + void SwingHammer(); + + /** 获取锤子头部位置 */ + UFUNCTION(BlueprintCallable, Category="Hammer") + FVector GetHammerHeadLocation() const; protected: - /** Ends gameplay for this component. */ + /** 组件结束游戏时的处理 */ UFUNCTION() virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; private: - /** The Character holding this weapon*/ + /** 持有此锤子的角色 */ ABladeSmithCharacter* Character; + + /** 执行锤击检测 */ + void PerformHammerHit(); + + /** 处理与铁块的碰撞 */ + void HandleIronBlockHit(AIronBlock* IronBlock, const FHitResult& HitResult); }; diff --git a/Source/BladeSmith/FDeformComputeShader.cpp b/Source/BladeSmith/FDeformComputeShader.cpp index 05ffcd0..c2e9d08 100644 --- a/Source/BladeSmith/FDeformComputeShader.cpp +++ b/Source/BladeSmith/FDeformComputeShader.cpp @@ -1,79 +1,68 @@ #include "FDeformComputeShader.h" -// DeformationCS.cpp -#include "RenderGraphUtils.h" -#include "ShaderParameterUtils.h" +#include "GlobalShader.h" +#include "ShaderParameterStruct.h" +#include "RHIStaticStates.h" +#include "Shader.h" +#include "GlobalShader.h" #include "RHICommandList.h" -IMPLEMENT_GLOBAL_SHADER(FDeformationCS, "/CustomShaders/DeformationCS.usf", "MainCS", SF_Compute); +#include "RHIResources.h" +#include "ShaderParameterUtils.h" + +// 全局参数存储 FThreadSafeDeformationParams GDeformationParams; + +// 实现Compute Shader +IMPLEMENT_GLOBAL_SHADER(FDeformationCS, "/Shaders/Private/DeformationCS.usf", "MainCS", SF_Compute); + +// 分派函数实现 void FDeformationCS::Dispatch( FRHICommandListImmediate& RHICmdList, const FDeformationCSDispatchParams& Params) { - // 获取Shader引用 + // 创建参数结构 + FParameters Parameters; + Parameters.ImpactPoint = FVector3f(Params.ImpactPoint); + Parameters.ImpactNormal = FVector3f(Params.ImpactNormal); + Parameters.ImpactForce = Params.ImpactForce; + Parameters.DeformationRadius = Params.DeformationRadius; + Parameters.Plasticity = Params.Plasticity; + Parameters.InputTexture = Params.InputTexture; + Parameters.OutputTexture = Params.OutputTexture; + Parameters.TextureSize = Params.TextureSize; + Parameters.UpdateRegionMin = FIntVector(Params.UpdateRegion.Min.X, Params.UpdateRegion.Min.Y, 0); + Parameters.UpdateRegionSize = FIntVector(Params.UpdateRegion.Width(), Params.UpdateRegion.Height(), 1); + + // 计算线程组数量 + const int32 ThreadGroupSizeX = 8; + const int32 ThreadGroupSizeY = 8; + const int32 ThreadGroupSizeZ = 1; + + const int32 GroupCountX = FMath::DivideAndRoundUp(Params.UpdateRegion.Width(), ThreadGroupSizeX); + const int32 GroupCountY = FMath::DivideAndRoundUp(Params.UpdateRegion.Height(), ThreadGroupSizeY); + const int32 GroupCountZ = 1; + + // 分派Compute Shader TShaderMapRef ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel)); - - // 设置参数 - FParameters ShaderParams; - ShaderParams.ImpactPoint = FVector3f(Params.ImpactPoint); - ShaderParams.ImpactNormal = FVector3f(Params.ImpactNormal); - ShaderParams.ImpactForce = Params.ImpactForce; - ShaderParams.DeformationRadius = Params.DeformationRadius; - ShaderParams.Plasticity = Params.Plasticity; - ShaderParams.InputTexture = Params.InputTexture; - FRHIResourceCreateInfo CreateInfo(TEXT("DeformationCS_UAV")); - ShaderParams.OutputTexture = RHICmdList.CreateUnorderedAccessView(Params.OutputTexture,0); - ShaderParams.TextureSize = Params.TextureSize; - ShaderParams.UpdateRegionMin = FIntVector(Params.UpdateRegion.Min.X, Params.UpdateRegion.Min.Y, 0); - ShaderParams.UpdateRegionSize = FIntVector(Params.UpdateRegion.Width(), Params.UpdateRegion.Height(), 1); - - // 设置计算Shader - FComputeShaderUtils::Dispatch( - RHICmdList, - ComputeShader, - ShaderParams, - FIntVector( - FMath::DivideAndRoundUp(Params.UpdateRegion.Width(), 8), - FMath::DivideAndRoundUp(Params.UpdateRegion.Height(), 8), - 1 - ) - ); + SetComputePipelineState(RHICmdList, ComputeShader.GetComputeShader()); + SetShaderParameters(RHICmdList, ComputeShader, ComputeShader.GetComputeShader(), Parameters); + RHICmdList.DispatchComputeShader(GroupCountX, GroupCountY, GroupCountZ); } -// 渲染线程入口点 -void DispatchDeformationComputeShaders(FRHICommandListImmediate& RHICmdList) + +// 线程安全参数管理 +void FThreadSafeDeformationParams::AddParams(const FDeformationCSDispatchParams& NewParams) { - FDeformationCSDispatchParams Params; - while(GDeformationParams.ConsumeParams(Params)) + FScopeLock Lock(&ParamsCriticalSection); + PendingParams.Add(NewParams); +} + +bool FThreadSafeDeformationParams::ConsumeParams(FDeformationCSDispatchParams& OutParams) +{ + FScopeLock Lock(&ParamsCriticalSection); + if (PendingParams.Num() > 0) { - FDeformationCS::Dispatch(RHICmdList, Params); - - // 复制结果回源纹理 - if(Params.InputTexture && Params.OutputTexture) - { - FRHICopyTextureInfo CopyInfo; - CopyInfo.SourcePosition = FIntVector(Params.UpdateRegion.Min.X, Params.UpdateRegion.Min.Y, 0); - CopyInfo.DestPosition = CopyInfo.SourcePosition; - CopyInfo.Size = FIntVector(Params.UpdateRegion.Width(), Params.UpdateRegion.Height(), 1); - - RHICmdList.CopyTexture( - Params.OutputTexture, - Params.InputTexture, - CopyInfo - ); - } + OutParams = PendingParams[0]; + PendingParams.RemoveAt(0); + return true; } -} -// 注册到渲染线程 -void SetupDeformationShutdown(); -FAutoConsoleCommand RegisterDeformationCmd( - TEXT("r.Deformation.Compute"), - TEXT(""), - FConsoleCommandDelegate::CreateStatic(SetupDeformationShutdown) -); -void SetupDeformationShutdown() -{ - // 每帧结束时调用 - ENQUEUE_RENDER_COMMAND(DeformationShutdown)( - [](FRHICommandListImmediate& RHICmdList) { - DispatchDeformationComputeShaders(RHICmdList); - }); + return false; } \ No newline at end of file diff --git a/Source/BladeSmith/FDeformComputeShader.h b/Source/BladeSmith/FDeformComputeShader.h index 0499694..f77c5ff 100644 --- a/Source/BladeSmith/FDeformComputeShader.h +++ b/Source/BladeSmith/FDeformComputeShader.h @@ -21,7 +21,7 @@ struct FDeformationCSDispatchParams // 纹理资源 FTextureRHIRef InputTexture; - FTextureRHIRef OutputTexture; + FUnorderedAccessViewRHIRef OutputTexture; // 更新区域 (优化部分更新) FIntRect UpdateRegion; @@ -44,23 +44,8 @@ struct FThreadSafeDeformationParams FCriticalSection ParamsCriticalSection; TArray PendingParams; - void AddParams(const FDeformationCSDispatchParams& NewParams) - { - FScopeLock Lock(&ParamsCriticalSection); - PendingParams.Add(NewParams); - } - - bool ConsumeParams(FDeformationCSDispatchParams& OutParams) - { - FScopeLock Lock(&ParamsCriticalSection); - if(PendingParams.Num() > 0) - { - OutParams = PendingParams[0]; - PendingParams.RemoveAt(0); - return true; - } - return false; - } + void AddParams(const FDeformationCSDispatchParams& NewParams); + bool ConsumeParams(FDeformationCSDispatchParams& OutParams); }; // 全局参数存储 extern FThreadSafeDeformationParams GDeformationParams; diff --git a/Source/BladeSmith/IronBlock.cpp b/Source/BladeSmith/IronBlock.cpp index 9f8c503..65e44e0 100644 --- a/Source/BladeSmith/IronBlock.cpp +++ b/Source/BladeSmith/IronBlock.cpp @@ -27,7 +27,7 @@ AIronBlock::AIronBlock(): StaticMeshComp(nullptr), PositionTexture0(nullptr), Po void AIronBlock::BeginPlay() { Super::BeginPlay(); - CalculateLocalBounds(); + //CalculateLocalBounds(); InitializePositionTexture(); } @@ -41,35 +41,55 @@ UTexture2D* AIronBlock::CreatePositionTexture(const FString& TexName, int32 Size ); // 设置基本属性 - NewTexture->Filter = TF_Nearest; + //NewTexture->Filter = TF_Nearest; NewTexture->SRGB = false; - NewTexture->CompressionSettings = TC_HDR; - NewTexture->MipGenSettings = TMGS_NoMipmaps; - NewTexture->NeverStream = true; + NewTexture->bNoTiling = true; + NewTexture->AddressX = TA_Clamp; + NewTexture->AddressY = TA_Clamp; + //NewTexture->CompressionSettings = TC_HDR; + //NewTexture->MipGenSettings = TMGS_NoMipmaps; + //NewTexture->NeverStream = true; + //NewTexture->LODGroup = TEXTUREGROUP_RenderTarget; // 关键:标记为渲染目标组 // 设置纹理尺寸和格式 + // NewTexture->SetPlatformData(new FTexturePlatformData()); + // NewTexture->GetPlatformData()->SizeX = Size; + // NewTexture->GetPlatformData()->SizeY = Size; + // // 使用兼容格式 + // EPixelFormat SafeFormat = PF_A32B32G32R32F; + // NewTexture->GetPlatformData()->PixelFormat = SafeFormat; + NewTexture->SetPlatformData(new FTexturePlatformData()); NewTexture->GetPlatformData()->SizeX = Size; NewTexture->GetPlatformData()->SizeY = Size; - NewTexture->GetPlatformData()->PixelFormat = PF_A32B32G32R32F; + NewTexture->GetPlatformData()->PixelFormat = PF_R32_FLOAT; // 添加初始Mip FTexture2DMipMap* Mip = new FTexture2DMipMap(); - NewTexture->GetPlatformData()->Mips.Add(Mip); + //NewTexture->GetPlatformData()->Mips.Reset(); Mip->SizeX = Size; Mip->SizeY = Size; - - // 如果有初始数据,填充纹理 - if (InitialData.Num() > 0) - { - const int32 DataSize = Size * Size * sizeof(FLinearColor); - Mip->BulkData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(Mip->BulkData.Realloc(DataSize), InitialData.GetData(), DataSize); - Mip->BulkData.Unlock(); - } + const int32 DataSize = Size * Size * sizeof(FLinearColor); + Mip->BulkData.Lock(LOCK_READ_WRITE); + Mip->BulkData.Realloc(DataSize); + Mip->BulkData.Unlock(); + NewTexture->GetPlatformData()->Mips.Add(Mip); + // // 如果有初始数据,填充纹理 + // if (InitialData.Num() >= Size * Size) + // { + // const int32 DataSize = Size * Size * sizeof(FLinearColor); + // Mip->BulkData.Lock(LOCK_READ_WRITE); + // FMemory::Memcpy(Mip->BulkData.Realloc(DataSize), InitialData.GetData(), DataSize); + // Mip->BulkData.Unlock(); + // } + // else + // { + // UE_LOG(LogTemp, Warning, TEXT("Initial data size mismatch for texture %s"), *TexName); + // } // 标记需要更新资源 NewTexture->UpdateResource(); + //ETextureDimension Dimension = NewTexture->GetResource()->GetTextureRHI()->GetDesc().Dimension; return NewTexture; } @@ -77,13 +97,17 @@ void AIronBlock::OnEndFrameInitializeTextures() { // 确保纹理资源已创建 if (PositionTexture0 && PositionTexture0->GetResource() && - PositionTexture1 && PositionTexture1->GetResource() && - PositionRenderTarget && PositionRenderTarget->GetResource()) + PositionTexture1 && PositionTexture1->GetResource() + /*&& PositionRenderTarget && PositionRenderTarget->GetResource()*/) { + // 等待资源提交完成 + FRenderCommandFence Fence; + Fence.BeginFence(); + Fence.Wait(); // 初始化纹理对 InitializeTexturePair(0); InitializeTexturePair(1); - + //CreateSafeUAV(PositionRenderTarget); // 移除委托 FCoreDelegates::OnEndFrame.Remove(TextureInitHandle); TextureInitHandle.Reset(); @@ -115,6 +139,56 @@ void AIronBlock::EndPlay(const EEndPlayReason::Type EndPlayReason) Super::EndPlay(EndPlayReason); } +// void CreateTexture() +// { +// const int32 RequestedMipIdx = 0; +// const FTexture2DMipMap* RequestedMip = GetPlatformMip(RequestedMipIdx); +// +// // create texture with ResourceMem data when available +// const FRHITextureCreateDesc Desc = +// FRHITextureCreateDesc::Create2D(TEXT("FTexture2DResource"), RequestedMip->SizeX, RequestedMip->SizeY, PixelFormat) +// .SetNumMips(1) +// .SetFlags(CreationFlags) +// .SetExtData(PlatformData->GetExtData()) +// .SetBulkData(ResourceMem) +// .SetClassName(Texture2DResourceName) +// .SetOwnerName(GetOwnerName()); +// +// TextureRHI = RHICreateTexture(Desc); +// +// // if( ResourceMem && !State.bReadyForStreaming) // To investigate! +// if (ResourceMem) +// { +// // when using resource memory the RHI texture has already been initialized with data and won't need to have mips copied +// check(State.NumRequestedLODs == ResourceMem->GetNumMips()); +// check(RequestedMip->SizeX == ResourceMem->GetSizeX() && RequestedMip->SizeY == ResourceMem->GetSizeY()); +// for( int32 MipIndex=0; MipIndex < MipData.Num(); MipIndex++ ) +// { +// MipData[MipIndex] = nullptr; +// MipDataSize[MipIndex] = 0; +// } +// } +// else +// { +// // Read the resident mip-levels into the RHI texture. +// for (int32 RHIMipIdx = 0; RHIMipIdx < State.NumRequestedLODs; ++RHIMipIdx) +// { +// const int32 ResourceMipIdx = RHIMipIdx + RequestedMipIdx; +// if (MipData[ResourceMipIdx]) +// { +// uint32 DestPitch = -1; +// uint64 Size = ~0ULL; +// void* TheMipData = RHILockTexture2D(TextureRHI, RHIMipIdx, RLM_WriteOnly, DestPitch, false /* bLockWithinMiptail */, true /* bFlushRHIThread */, &Size); +// //check(Size != ~0ULL); +// +// GetData( ResourceMipIdx, TheMipData, DestPitch, Size ); +// RHIUnlockTexture2D(TextureRHI, RHIMipIdx, false ); +// } +// } +// } +// } + + void AIronBlock::InitializeTexturePair(int32 Index) { if (Index < 0 || Index > 1) return; @@ -123,15 +197,42 @@ void AIronBlock::InitializeTexturePair(int32 Index) if (!Texture || !Texture->GetResource()) return; // 获取纹理引用 - TexturePairs[Index].RHITexture = Texture->GetResource()->TextureRHI; - FTextureRHIRef RHITexture = Texture->GetResource()->GetTexture2DRHI(); + FTextureRHIRef RHITexture = Texture->GetResource()->TextureRHI; if (!RHITexture.IsValid()) return; - + TexturePairs[Index].RHITexture = RHITexture; // 在渲染线程创建 UAV ENQUEUE_RENDER_COMMAND(CreateTextureUAV)( [this, Index](FRHICommandListImmediate& RHICmdList) { + // 创建UAV描述 + FRHITextureDesc Desc = TexturePairs[Index].RHITexture->GetDesc(); + ensure(Desc.Flags & TexCreate_UAV); // 必须包含UAV标志 + ensure(Desc.Format == PF_R32_FLOAT); // 匹配格式 + + // FRHIViewDesc::FUnorderedAccessView UAVDesc(FRHIViewDesc::FTextureUAV() + // .SetTexture2D(TextureRHI) + // .SetFormat(Desc.Format) + // ); + //FUnorderedAccessViewRHIRef UAV = RHICreateUnorderedAccessView(TextureRHI, UAVDesc); + FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV(); + ViewDesc.SetFormat(Desc.Format); + ViewDesc.SetDimensionFromTexture(TexturePairs[Index].RHITexture); + // ViewDesc.SetDimension(ETextureDimension::Texture2D); + // ViewDesc.SetPlane(ERHITexturePlane::Primary); + // ViewDesc.SetMipLevel(0); + // ViewDesc.SetArrayRange(0,1); + // 使用RDG安全创建 + // FRDGBuilder GraphBuilder(RHICmdList); + // FRDGTextureRef RDGTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(TexturePairs[Index].RHITexture, TEXT("PositionTexture"))); + // FRDGTextureUAVDesc UAVDesc(RDGTexture); + + // TexturePairs[Index].UAV = GraphBuilder.CreateUAV(UAVDesc); + + // 立即执行 + // GraphBuilder.Execute(); + + //ViewDesc.SetDimensionFromTexture(TexturePairs[Index].RHITexture); TexturePairs[Index].UAV = RHICmdList.CreateUnorderedAccessView(TexturePairs[Index].RHITexture,ViewDesc); UE_LOG(LogTemp, Log, TEXT("Created UAV for texture %d"), Index); @@ -166,38 +267,106 @@ void AIronBlock::GetStaticMeshVertexPositions(TArray& OutPositions) void AIronBlock::InitializePositionTexture() { - // 获取顶点数据 + // 创建静态网格组件 + if (!StaticMeshComp) + { + StaticMeshComp = CreateDefaultSubobject(TEXT("StaticMeshComponent")); + RootComponent = StaticMeshComp; + + // 设置默认网格(立方体) + UStaticMesh* DefaultMesh = LoadObject(nullptr, TEXT("/Engine/BasicShapes/Cube")); + if (DefaultMesh) + { + StaticMeshComp->SetStaticMesh(DefaultMesh); + } + } + + // 计算本地边界 + CalculateLocalBounds(); + + // 获取顶点位置 TArray VertexPositions; GetStaticMeshVertexPositions(VertexPositions); - // 创建初始数据数组 + // 创建位置纹理 + const int32 TextureSize = 256; // 纹理尺寸 TArray InitialData; - const int32 TexSize = FMath::RoundUpToPowerOfTwo(FMath::Sqrt((float)VertexPositions.Num())); - //int32 TexSize = 512; - InitialData.SetNumZeroed(TexSize * TexSize); - for (int32 i = 0; i < VertexPositions.Num() && i < InitialData.Num(); i++) + InitialData.SetNum(TextureSize * TextureSize); + + // 初始化纹理数据 + for (int32 Y = 0; Y < TextureSize; ++Y) { - InitialData[i] = FLinearColor(VertexPositions[i]); + for (int32 X = 0; X < TextureSize; ++X) + { + int32 Index = Y * TextureSize + X; + FVector2D UV(FVector2D(X, Y) / (TextureSize - 1)); + + // 将UV坐标转换为本地空间位置 + FVector LocalPos( + FMath::Lerp(LocalBounds.Min.X, LocalBounds.Max.X, UV.X), + FMath::Lerp(LocalBounds.Min.Y, LocalBounds.Max.Y, UV.Y), + LocalBounds.Min.Z + ); + + InitialData[Index] = FLinearColor(LocalPos.X, LocalPos.Y, LocalPos.Z, 1.0f); + } } - // 创建纹理 - PositionTexture0 = CreatePositionTexture("PosTex0", TexSize, InitialData); - PositionTexture1 = CreatePositionTexture("PosTex1", TexSize, InitialData); - // 初始化纹理对 - TexturePairs[0].RHITexture = PositionTexture0->GetResource()->TextureRHI; - TexturePairs[1].RHITexture = PositionTexture1->GetResource()->TextureRHI; + // 创建纹理对 + PositionTexture0 = CreatePositionTexture(TEXT("PositionTexture0"), TextureSize, InitialData); + PositionTexture1 = CreatePositionTexture(TEXT("PositionTexture1"), TextureSize, InitialData); + // 创建渲染目标 PositionRenderTarget = NewObject(this); - PositionRenderTarget->RenderTargetFormat = RTF_RGBA32f; - PositionRenderTarget->bCanCreateUAV = true; - PositionRenderTarget->InitAutoFormat(TexSize, TexSize); + PositionRenderTarget->SizeX = TextureSize; + PositionRenderTarget->SizeY = TextureSize; + PositionRenderTarget->RenderTargetFormat = RTF_RGBA16f; + PositionRenderTarget->bAutoGenerateMips = false; PositionRenderTarget->UpdateResource(); - // 注册纹理初始化委托 - TextureInitHandle = FCoreDelegates::OnEndFrame.AddUObject(this, &AIronBlock::OnEndFrameInitializeTextures); - - // 设置材质实例 + // 设置变形材质 SetupDeformationMaterial(); + + // 初始化纹理对 + InitializeTexturePair(0); + InitializeTexturePair(1); + + // 延迟初始化纹理资源 + TextureInitHandle = FDelegateHandle(); + GetWorld()->GetTimerManager().SetTimerForNextTick([this]() + { + OnEndFrameInitializeTextures(); + }); +} + +// 创建UAV的安全方法 +FUnorderedAccessViewRHIRef AIronBlock::CreateSafeUAV(UTextureRenderTarget2D* RenderTarget) +{ + if (!RenderTarget || !RenderTarget->GetResource()) + return nullptr; + + FTextureRHIRef TextureRHI = RenderTarget->GetResource()->GetTexture2DRHI(); + if (!TextureRHI.IsValid()) + return nullptr; + + // 创建UAV描述 + FUnorderedAccessViewRHIRef UAV; + ENQUEUE_RENDER_COMMAND(CreateUAVCommand)( + [TextureRHI, &UAV](FRHICommandListImmediate& RHICmdList) + { + FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV(); + ViewDesc.SetMipLevel(0); + ViewDesc.SetPlane(ERHITexturePlane::Primary); + ViewDesc.SetFormat(PF_A32B32G32R32F); + + UAV = RHICmdList.CreateUnorderedAccessView(TextureRHI, ViewDesc); + } + ); + + // 等待渲染命令完成 + FlushRenderingCommands(); + + return UAV; } void AIronBlock::CalculateLocalBounds() @@ -230,12 +399,30 @@ void AIronBlock::CalculateLocalBounds() void AIronBlock::SetupDeformationMaterial() { - DynMaterial = UMaterialInstanceDynamic::Create(StaticMeshComp->GetMaterial(0), this); - StaticMeshComp->SetMaterial(0, DynMaterial); - - DynMaterial->SetTextureParameterValue("PositionTexture", PositionTexture0); - - DynMaterial->SetScalarParameterValue("EnableDeformation", 1.0f); + // 创建动态材质实例 + if (StaticMeshComp) + { + // 加载基础材质 + UMaterial* BaseMaterial = LoadObject(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial")); + if (BaseMaterial) + { + DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, this); + StaticMeshComp->SetMaterial(0, DynMaterial); + + // 设置初始参数 + if (DynMaterial) + { + DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), PositionTexture0); + DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min)); + DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max)); + DynMaterial->SetScalarParameterValue(TEXT("DeformationStrength"), 1.0f); + } + } + else + { + UE_LOG(LogTemp, Warning, TEXT("Failed to load base material for iron block")); + } + } } void AIronBlock::OnHammerHit(const FHitResult& HitResult) @@ -307,10 +494,12 @@ void AIronBlock::ExecuteDeformationCS() UE_LOG(LogTemp, Error, TEXT("Position textures not initialized!")); return; } + // 获取当前纹理资源 UTexture2D* CurrentTex = (CurrentTextureIndex == 0) ? PositionTexture0 : PositionTexture1; UTexture2D* PrevTex = (CurrentTextureIndex == 0) ? PositionTexture1 : PositionTexture0; - // 获取纹理RHI资源 - 使用GetResource()方法 + + // 获取纹理RHI资源 FTextureRHIRef InputRHI = GetTextureRHI(PrevTex); FTextureRHIRef OutputRHI = GetRenderTargetRHI(PositionRenderTarget); @@ -319,6 +508,7 @@ void AIronBlock::ExecuteDeformationCS() UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources!")); return; } + // 计算纹理UV坐标 const int32 TexSize = PositionRenderTarget->SizeX; FVector2D ImpactUV( @@ -342,20 +532,40 @@ void AIronBlock::ExecuteDeformationCS() Params.DeformationRadius = CurrentDeformationRadius; Params.Plasticity = CurrentPlasticity; Params.InputTexture = InputRHI; - Params.OutputTexture = OutputRHI; - Params.UpdateRegion = UpdateRegion; - Params.TextureSize = FIntVector(TexSize, TexSize, 0); - // 提交到渲染线程 - GDeformationParams.AddParams(Params); + // 创建UAV用于输出 + FUnorderedAccessViewRHIRef OutputUAV; + ENQUEUE_RENDER_COMMAND(CreateOutputUAV)( + [OutputRHI, &OutputUAV](FRHICommandListImmediate& RHICmdList) + { + FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV(); + ViewDesc.SetMipLevel(0); + ViewDesc.SetPlane(ERHITexturePlane::Primary); + ViewDesc.SetFormat(PF_A32B32G32R32F); + + OutputUAV = RHICmdList.CreateUnorderedAccessView(OutputRHI, ViewDesc); + } + ); + FlushRenderingCommands(); + Params.OutputTexture = OutputUAV; + + Params.UpdateRegion = UpdateRegion; + Params.TextureSize = FIntVector(TexSize, TexSize, 1); + + // 在渲染线程中执行Compute Shader + ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS)( + [Params](FRHICommandListImmediate& RHICmdList) + { + FDeformationCS::Dispatch(RHICmdList, Params); + }); // 更新材质参数 - DynMaterial->SetTextureParameterValue("PositionTexture", CurrentTex); - - // 触发碰撞体更新 (异步) - GetWorld()->GetTimerManager().SetTimerForNextTick([this, UpdateRegion](){ - UpdateCollisionAsync(UpdateRegion); - }); + if (DynMaterial) + { + DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), CurrentTex); + DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min)); + DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max)); + } } // 异步更新物理碰撞体 void AIronBlock::UpdateCollisionAsync(const FIntRect& UpdateRegion) diff --git a/Source/BladeSmith/IronBlock.h b/Source/BladeSmith/IronBlock.h index 2fcda2e..3ddabcc 100644 --- a/Source/BladeSmith/IronBlock.h +++ b/Source/BladeSmith/IronBlock.h @@ -29,6 +29,9 @@ public: // Sets default values for this actor's properties AIronBlock(); + UFUNCTION(BlueprintCallable) + void OnHammerHit(const FHitResult& HitResult); + protected: // Called when the game starts or when spawned virtual void BeginPlay() override; @@ -37,10 +40,9 @@ protected: void GetStaticMeshVertexPositions(TArray& OutPositions); UTexture2D* CreatePositionTexture(const FString& TexName, int32 Size, const TArray& InitialData); void InitializePositionTexture(); + FUnorderedAccessViewRHIRef CreateSafeUAV(UTextureRenderTarget2D* RenderTarget); void OnEndFrameInitializeTextures(); void SetupDeformationMaterial(); - UFUNCTION(BlueprintCallable) - void OnHammerHit(const FHitResult& HitResult); FTextureRHIRef GetTextureRHI(UTexture2D* Texture); FTextureRHIRef GetRenderTargetRHI(UTextureRenderTarget2D* RT); private: @@ -57,11 +59,12 @@ private: // UAV 资源引用 FUnorderedAccessViewRHIRef UAV; - + //FRDGTextureUAVRef UAV; // 安全释放资源 void ReleaseRHI() { UAV.SafeRelease(); + //UAV->GetRHI()->Release(); RHITexture.SafeRelease(); } };