feat: 完成PVP,新增投射物OverLap判断,关闭子弹引力
This commit is contained in:
@@ -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<IDamageableInterface>(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<IDamageableInterface>(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) {
|
||||
}
|
||||
|
||||
@@ -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: 服务器调用,服务器+所有客户端执行
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user