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"));
}
}