Implement enemy shooting and scale down for more arcade game feel
This commit is contained in:
156
Source/MyProject3/EnemyProjectile.cpp
Normal file
156
Source/MyProject3/EnemyProjectile.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "EnemyProjectile.h"
|
||||
#include "GameFramework/ProjectileMovementComponent.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "SpaceshipPawn.h" // Include the player ship header
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
AEnemyProjectile::AEnemyProjectile()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// Create and setup the projectile mesh
|
||||
ProjectileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMesh"));
|
||||
RootComponent = ProjectileMesh;
|
||||
ProjectileMesh->SetCollisionProfileName(TEXT("EnemyProjectile"));
|
||||
|
||||
// Set up collision
|
||||
ProjectileMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||||
ProjectileMesh->SetCollisionObjectType(ECC_WorldDynamic);
|
||||
ProjectileMesh->SetCollisionResponseToAllChannels(ECR_Ignore);
|
||||
|
||||
// Block GameTraceChannel1 (player ship)
|
||||
ProjectileMesh->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Block);
|
||||
|
||||
// Block world static for environment collisions
|
||||
ProjectileMesh->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
|
||||
|
||||
// Block pawns for player ship collision
|
||||
ProjectileMesh->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
|
||||
|
||||
// Enable overlap events
|
||||
ProjectileMesh->SetGenerateOverlapEvents(true);
|
||||
|
||||
// Bind hit event
|
||||
ProjectileMesh->OnComponentHit.AddDynamic(this, &AEnemyProjectile::OnHit);
|
||||
|
||||
// Bind overlap event for redundant collision detection
|
||||
ProjectileMesh->OnComponentBeginOverlap.AddDynamic(this, &AEnemyProjectile::OnOverlapBegin);
|
||||
|
||||
// Create and setup projectile movement
|
||||
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
|
||||
ProjectileMovement->UpdatedComponent = ProjectileMesh;
|
||||
ProjectileMovement->InitialSpeed = ProjectileSpeed;
|
||||
ProjectileMovement->MaxSpeed = ProjectileSpeed;
|
||||
ProjectileMovement->bRotationFollowsVelocity = true;
|
||||
ProjectileMovement->ProjectileGravityScale = 0.0f;
|
||||
|
||||
// Important: Enable sweep to prevent tunneling at high speeds
|
||||
ProjectileMovement->bSweepCollision = true;
|
||||
|
||||
// Set lifetime
|
||||
InitialLifeSpan = 3.0f;
|
||||
}
|
||||
|
||||
void AEnemyProjectile::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Debug message to verify projectile spawned
|
||||
if (GEngine)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Blue, TEXT("Enemy Projectile Spawned"));
|
||||
}
|
||||
}
|
||||
|
||||
void AEnemyProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp,
|
||||
FVector NormalImpulse, const FHitResult& Hit)
|
||||
{
|
||||
// Debug message to verify hit detection
|
||||
if (GEngine && OtherActor)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Orange,
|
||||
FString::Printf(TEXT("Enemy Projectile hit: %s"), *OtherActor->GetName()));
|
||||
}
|
||||
|
||||
if (OtherActor && OtherActor != this)
|
||||
{
|
||||
// Check if we hit a player ship
|
||||
ASpaceshipPawn* Player = Cast<ASpaceshipPawn>(OtherActor);
|
||||
if (Player)
|
||||
{
|
||||
// Apply damage to player
|
||||
FDamageEvent DamageEvent;
|
||||
float ActualDamage = Player->TakeDamage(DamageAmount, DamageEvent, nullptr, this);
|
||||
|
||||
// Debug message for damage
|
||||
if (GEngine)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red,
|
||||
FString::Printf(TEXT("Dealt %.1f damage to player"), ActualDamage));
|
||||
}
|
||||
|
||||
// Spawn impact effect if set
|
||||
if (ImpactEffect)
|
||||
{
|
||||
UGameplayStatics::SpawnEmitterAtLocation(
|
||||
GetWorld(),
|
||||
ImpactEffect,
|
||||
Hit.Location,
|
||||
Hit.Normal.Rotation()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the projectile
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void AEnemyProjectile::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
|
||||
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
|
||||
bool bFromSweep, const FHitResult& SweepResult)
|
||||
{
|
||||
// This is a redundant collision detection method
|
||||
// Some fast-moving projectiles might miss hit events but catch overlap events
|
||||
|
||||
// Debug message to verify overlap detection
|
||||
if (GEngine && OtherActor)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow,
|
||||
FString::Printf(TEXT("Enemy Projectile overlap with: %s"), *OtherActor->GetName()));
|
||||
}
|
||||
|
||||
if (OtherActor && OtherActor != this)
|
||||
{
|
||||
// Check if we overlap with a player ship
|
||||
ASpaceshipPawn* Player = Cast<ASpaceshipPawn>(OtherActor);
|
||||
if (Player)
|
||||
{
|
||||
// Apply damage to player
|
||||
FDamageEvent DamageEvent;
|
||||
float ActualDamage = Player->TakeDamage(DamageAmount, DamageEvent, nullptr, this);
|
||||
|
||||
// Debug message for damage
|
||||
if (GEngine)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red,
|
||||
FString::Printf(TEXT("Overlap - Dealt %.1f damage to player"), ActualDamage));
|
||||
}
|
||||
|
||||
// Spawn impact effect if set
|
||||
if (ImpactEffect)
|
||||
{
|
||||
UGameplayStatics::SpawnEmitterAtLocation(
|
||||
GetWorld(),
|
||||
ImpactEffect,
|
||||
GetActorLocation(),
|
||||
GetActorRotation()
|
||||
);
|
||||
}
|
||||
|
||||
// Destroy the projectile
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user