diff --git a/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll b/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll index be81efe..1ce6a27 100644 --- a/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll +++ b/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8de36789f7f4e40e0c92691907fc5b2aea3f78cc72e08149c55e57817e36ce7f -size 719360 +oid sha256:c0618d893b4d5c643cc183fff63e8e3d769dae20165353a1bde7d0fb979078be +size 729600 diff --git a/Content/Surviver/Blueprints/BP_ProjectileBase.uasset b/Content/Surviver/Blueprints/BP_ProjectileBase.uasset index d0c5fbf..517186a 100644 --- a/Content/Surviver/Blueprints/BP_ProjectileBase.uasset +++ b/Content/Surviver/Blueprints/BP_ProjectileBase.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f9593438f0731172851e47ca7bd54498556c19bcefc61b9129b7af83bd7f94c -size 33338 +oid sha256:c6a108520166e9025e9b9a3a8347be9bfa35984f7ddc8a11eb759e30bc47a36e +size 34036 diff --git a/Content/Surviver/Blueprints/BP_SurviverPlayerCharacter.uasset b/Content/Surviver/Blueprints/BP_SurviverPlayerCharacter.uasset index d561d9e..10c671a 100644 --- a/Content/Surviver/Blueprints/BP_SurviverPlayerCharacter.uasset +++ b/Content/Surviver/Blueprints/BP_SurviverPlayerCharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f500f3f49b58cf5c9cb15aafac14e198eb7140f23d76bdf5d31e96c90ad306c -size 38716 +oid sha256:bcf3e141c606db23078724277c210233010b4343be4838a289678dec6fdc8929 +size 39212 diff --git a/Content/Surviver/UI/WBP_BattleHUD.uasset b/Content/Surviver/UI/WBP_BattleHUD.uasset index e1a03b6..b012c28 100644 --- a/Content/Surviver/UI/WBP_BattleHUD.uasset +++ b/Content/Surviver/UI/WBP_BattleHUD.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e45c7a325e8baac0f7234789ec58e4d2271460aa682d1a924d7ff139ea0a104d -size 25782 +oid sha256:aa7f6d8cb39bad2dde694e53f1c353ab890139433107ad53240f96f1fdf50290 +size 30010 diff --git a/Content/__ExternalActors__/Surviver/Surviver/7/5K/9C6MU3HE74FP84Y52PFT33.uasset b/Content/__ExternalActors__/Surviver/Surviver/7/5K/9C6MU3HE74FP84Y52PFT33.uasset deleted file mode 100644 index 8386a6c..0000000 --- a/Content/__ExternalActors__/Surviver/Surviver/7/5K/9C6MU3HE74FP84Y52PFT33.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ee94199c99ff549e09f27f4a689e51184cda8e3b2780d95ba4f0bcb92bfa025 -size 4286 diff --git a/FirstPersonDemo.uproject.DotSettings.user b/FirstPersonDemo.uproject.DotSettings.user new file mode 100644 index 0000000..17eac92 --- /dev/null +++ b/FirstPersonDemo.uproject.DotSettings.user @@ -0,0 +1,3 @@ + + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.cpp b/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.cpp index 280791b..ef3a78b 100644 --- a/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.cpp +++ b/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.cpp @@ -42,39 +42,121 @@ AProjectileBase::AProjectileBase() { ProjectileMovement->MaxSpeed = 2000.f; ProjectileMovement->bRotationFollowsVelocity = true; // 子弹朝向跟随速度方向 ProjectileMovement->bShouldBounce = false; + // 关掉重力 + ProjectileMovement->ProjectileGravityScale = 0.0f; // 3秒后自动销毁(服务器销毁后,客户端也会自动销毁) InitialLifeSpan = BulletLifeSpan; } void AProjectileBase::BeginPlay() { Super::BeginPlay(); - // 仅在服务器绑定碰撞事件 - if (HasAuthority()) { - CollisionComp->OnComponentHit.AddDynamic(this, &AProjectileBase::OnHit); - // 忽略发射者(防止子弹生成时直接炸到自己) - if (GetInstigator()) { - CollisionComp->IgnoreActorWhenMoving(GetInstigator(), true); - } + + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] BeginPlay - HasAuthority: %d, Name: %s"), + HasAuthority(), *GetName()); + + // 绑定碰撞事件(服务器和客户端都绑定,但只在服务器处理伤害) + CollisionComp->OnComponentHit.AddDynamic(this, &AProjectileBase::OnHit); + CollisionComp->OnComponentBeginOverlap.AddDynamic(this, &AProjectileBase::OnBeginOverlap); + UE_LOG(LogTemp, Log, TEXT("[ProjectileBase] OnComponentHit event bound")); + + // 忽略发射者 + if (GetInstigator()) { + CollisionComp->IgnoreActorWhenMoving(GetInstigator(), true); + UE_LOG(LogTemp, Log, TEXT("[ProjectileBase] Ignoring instigator: %s"), *GetInstigator()->GetName()); + } else { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] No instigator found")); } + + // 日志碰撞设置 + if (HasAuthority()) { + UE_LOG(LogTemp, Log, TEXT("[ProjectileBase] Collision Profile: %s, Enabled: %d"), + *CollisionComp->GetCollisionProfileName().ToString(), + (int32)CollisionComp->GetCollisionEnabled()); + } + // 测试子弹轨迹 + DrawDebugLine( + GetWorld(), + GetActorLocation(), + GetActorLocation() + GetActorForwardVector() * 1000.0f, + FColor::Red, + false, + 5.0f, // 持续2秒 + 0, + 1.0f + ); } void AProjectileBase::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] OnHit called - HasAuthority: %d, OtherActor: %s, OtherComp: %s, Instigator: %s"), + HasAuthority(), + OtherActor ? *OtherActor->GetName() : TEXT("None"), + OtherComp ? *OtherComp->GetName() : TEXT("None"), + GetInstigator() ? *GetInstigator()->GetName() : TEXT("None")); + if (!HasAuthority()) return; + if (OtherActor != nullptr && OtherActor != this && OtherActor != GetInstigator()) { + UE_LOG(LogTemp, Log, TEXT("[ProjectileBase] Processing hit on: %s (Class: %s)"), + *OtherActor->GetName(), *OtherActor->GetClass()->GetName()); + // 1. 基类默认行为:单体伤害 IDamageableInterface* DamageableActor = Cast(OtherActor); if (DamageableActor) { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] ✓ DamageableActor found, applying damage: %f"), BaseDamage); DamageableActor->ReceiveDamage(BaseDamage, GetInstigator()); + } else { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] ✗ %s does not implement IDamageableInterface") + , *OtherActor->GetName()); } + // 2. 播放特效 Multicast_OnImpact(Hit.Location, Hit.ImpactNormal.Rotation()); + // 3. 根据配置决定是否销毁 if (bDestoryOnHit) { Destroy(); } + } else { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] Hit ignored - OtherActor is null, self, or instigator")); } } + +void AProjectileBase::OnBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, + int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] OnBeginOverlap called - HasAuthority: %d, OtherActor: %s, Instigator: %s"), + HasAuthority(), + OtherActor ? *OtherActor->GetName() : TEXT("None"), + GetInstigator() ? *GetInstigator()->GetName() : TEXT("None")); + + if (!HasAuthority()) return; + + if (OtherActor != nullptr && OtherActor != this && OtherActor != GetInstigator()) { + UE_LOG(LogTemp, Log, TEXT("[ProjectileBase] Processing overlap with: %s (Class: %s)"), + *OtherActor->GetName(), *OtherActor->GetClass()->GetName()); + + // 1. 基类默认行为:单体伤害 + IDamageableInterface* DamageableActor = Cast(OtherActor); + if (DamageableActor) { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] ✓ DamageableActor found, applying damage: %f"), BaseDamage); + DamageableActor->ReceiveDamage(BaseDamage, GetInstigator()); + } else { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] ✗ %s does not implement IDamageableInterface"), *OtherActor->GetName()); + } + + // 2. 播放特效 + Multicast_OnImpact(SweepResult.Location, SweepResult.ImpactNormal.Rotation()); + + // 3. 根据配置决定是否销毁 + if (bDestoryOnHit) { + Destroy(); + } + } else { + UE_LOG(LogTemp, Warning, TEXT("[ProjectileBase] Overlap ignored - OtherActor is null, self, or instigator")); + } +} + + void AProjectileBase::Multicast_OnImpact_Implementation(FVector HitLocation, FRotator HitRotation) { } diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.h b/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.h index 5a3dea7..d1027ba 100644 --- a/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.h +++ b/Source/FirstPersonDemo/Surviver_FPS/Battle/ProjectileBase.h @@ -59,6 +59,13 @@ protected: virtual void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit); + /** + * @brief 仅在服务器触发的重叠回调 + */ + UFUNCTION() + virtual void OnBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, + int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); + /** * @brief 多播通知所有客户端播放击中特效(粒子、声音) * NetMulticast: 服务器调用,服务器+所有客户端执行 diff --git a/Source/FirstPersonDemo/Surviver_FPS/SurviverPlayer.cpp b/Source/FirstPersonDemo/Surviver_FPS/SurviverPlayer.cpp index 6b77561..c379d24 100644 --- a/Source/FirstPersonDemo/Surviver_FPS/SurviverPlayer.cpp +++ b/Source/FirstPersonDemo/Surviver_FPS/SurviverPlayer.cpp @@ -21,6 +21,7 @@ ASurviverPlayer::ASurviverPlayer() { // 网络同步设置 bReplicates = true; + AActor::SetReplicateMovement(true); GetCharacterMovement()->SetIsReplicated(true); // 允许该角色每帧调用Tick() @@ -46,8 +47,14 @@ ASurviverPlayer::ASurviverPlayer() { // configure the character comps GetMesh()->SetOwnerNoSee(true); GetMesh()->FirstPersonPrimitiveType = EFirstPersonPrimitiveType::WorldSpaceRepresentation; + // 确保Mesh可以被子弹击中(网络同步) + GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + GetMesh()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); GetCapsuleComponent()->SetCapsuleSize(34.0f, 96.0f); + // 确保胶囊体可以被子弹击中 + GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); // Configure character movement GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f; @@ -152,8 +159,12 @@ void ASurviverPlayer::DoJumpEnd() { */ void ASurviverPlayer::ReceiveDamage(float DamageAmount, AActor* InstigatorActor) { if (HealthComponent) { + UE_LOG(LogTemp, Log, TEXT("[SurviverPlayer] ReceiveDamage called - DamageAmount: %f, Instigator: %s"), + DamageAmount, InstigatorActor ? *InstigatorActor->GetName() : TEXT("None")); // 通过生命组件处理伤害 HealthComponent->HandleDamage(DamageAmount); + } else { + UE_LOG(LogTemp, Warning, TEXT("[SurviverPlayer] HealthComponent is null, cannot process damage")); } }