Implement enemy shooting and scale down for more arcade game feel

This commit is contained in:
2025-04-15 22:21:28 +05:30
parent ca23fd129e
commit e2e709bffd
14 changed files with 886 additions and 41 deletions

View File

@@ -1,6 +1,7 @@
#include "SpaceshipPawn.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Camera/CameraShakeBase.h"
#include "Components/StaticMeshComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
@@ -59,6 +60,7 @@ ASpaceshipPawn::ASpaceshipPawn()
ShipMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);
ShipMesh->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECollisionResponse::ECR_Ignore);
ShipMesh->SetCollisionResponseToChannel(ECC_Pawn, ECollisionResponse::ECR_Ignore);
ShipMesh->SetGenerateOverlapEvents(true);
}
}
@@ -104,6 +106,11 @@ void ASpaceshipPawn::BeginPlay()
}
}
}
// Initialize health and shield values
CurrentHealth = MaxHealth;
CurrentShield = MaxShield;
LastDamageTime = 0.0f;
}
void ASpaceshipPawn::Tick(float DeltaTime)
@@ -201,7 +208,7 @@ void ASpaceshipPawn::UpdateArcadeMovement(float DeltaTime)
// Calculate desired velocity (forward/back + strafe)
FVector DesiredVelocity = (ForwardVector * CurrentThrottleInput * MaxSpeed) +
(RightVector * CurrentStrafeInput * (MaxSpeed * 0.7f)); // Strafe is slightly slower
(RightVector * CurrentStrafeInput * (MaxSpeed * 0.95f)); // Strafe is slightly slower
// Smoothly interpolate current velocity to desired velocity
if (!FMath::IsNearlyZero(CurrentThrottleInput) || !FMath::IsNearlyZero(CurrentStrafeInput))
@@ -394,4 +401,198 @@ void ASpaceshipPawn::Fire()
void ASpaceshipPawn::ResetFire()
{
bCanFire = true;
}
float ASpaceshipPawn::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent,
AController* EventInstigator, AActor* DamageCauser)
{
float DamageToApply = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
// Update last damage time
LastDamageTime = GetWorld()->GetTimeSeconds();
// Stop shield recharge and clear timer
GetWorldTimerManager().ClearTimer(ShieldRechargeTimerHandle);
// Apply damage to shield first
if (CurrentShield > 0.0f)
{
if (CurrentShield >= DamageToApply)
{
// Shield absorbs all damage
CurrentShield -= DamageToApply;
DamageToApply = 0.0f;
}
else
{
// Shield absorbs part of the damage
DamageToApply -= CurrentShield;
CurrentShield = 0.0f;
}
}
// Apply remaining damage to health
CurrentHealth -= DamageToApply;
// Debug message
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red,
FString::Printf(TEXT("Player Hit! Health: %.1f, Shield: %.1f"),
CurrentHealth, CurrentShield));
}
// Apply visual damage effects
ApplyDamageFlash();
// Play impact effect
if (ImpactEffect)
{
UGameplayStatics::SpawnEmitterAttached(
ImpactEffect,
ShipMesh,
NAME_None,
GetActorLocation(),
GetActorRotation(),
EAttachLocation::KeepWorldPosition
);
}
// Play impact sound
if (ImpactSound)
{
UGameplayStatics::PlaySoundAtLocation(
this,
ImpactSound,
GetActorLocation()
);
}
// Start shield recharge timer after delay
GetWorldTimerManager().SetTimer(
ShieldRechargeTimerHandle,
this,
&ASpaceshipPawn::StartShieldRecharge,
ShieldRechargeDelay,
false
);
// Check for death
if (CurrentHealth <= 0.0f)
{
Die();
}
return DamageToApply;
}
void ASpaceshipPawn::ApplyDamageFlash()
{
// Apply material flash effect to indicate damage
if (ShipMesh && ShipMesh->GetMaterial(0))
{
UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(ShipMesh->GetMaterial(0), this);
if (DynamicMaterial)
{
// Assuming the material has a parameter named "FlashColor"
DynamicMaterial->SetVectorParameterValue("FlashColor", DamageFlashColor);
ShipMesh->SetMaterial(0, DynamicMaterial);
// Set timer to reset the flash
GetWorldTimerManager().SetTimer(
DamageFlashTimerHandle,
this,
&ASpaceshipPawn::ResetDamageFlash,
DamageFlashDuration,
false
);
}
}
}
void ASpaceshipPawn::ResetDamageFlash()
{
// Reset material flash effect
if (ShipMesh && ShipMesh->GetMaterial(0))
{
UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(ShipMesh->GetMaterial(0));
if (DynamicMaterial)
{
// Reset the flash color (assuming transparent black means no flash)
DynamicMaterial->SetVectorParameterValue("FlashColor", FLinearColor(0.0f, 0.0f, 0.0f, 0.0f));
}
}
}
void ASpaceshipPawn::StartShieldRecharge()
{
// Start continuous shield recharge
GetWorldTimerManager().SetTimer(
ShieldRechargeTimerHandle,
this,
&ASpaceshipPawn::RechargeShield,
0.1f, // update every 0.1 seconds
true // looping
);
}
void ASpaceshipPawn::RechargeShield()
{
// Recharge shield gradually
if (CurrentShield < MaxShield)
{
CurrentShield = FMath::Min(CurrentShield + (ShieldRechargeRate * 0.1f), MaxShield);
}
else
{
// Shield is full, stop the timer
GetWorldTimerManager().ClearTimer(ShieldRechargeTimerHandle);
}
}
void ASpaceshipPawn::Die()
{
// Play death explosion effect
UGameplayStatics::SpawnEmitterAtLocation(
GetWorld(),
ImpactEffect,
GetActorLocation(),
GetActorRotation(),
FVector(3.0f) // Larger explosion for death
);
// Play death sound
if (ImpactSound)
{
UGameplayStatics::PlaySoundAtLocation(
this,
ImpactSound,
GetActorLocation(),
2.0f // Louder for death
);
}
// Hide the ship mesh
if (ShipMesh)
{
ShipMesh->SetVisibility(false);
ShipMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
// Disable input
APlayerController* PC = Cast<APlayerController>(GetController());
if (PC)
{
DisableInput(PC);
}
// You could either restart the level after a delay or show a game over screen here
// For example:
// GetWorldTimerManager().SetTimer(RestartTimerHandle, this, &ASpaceshipPawn::RestartLevel, 3.0f, false);
// For now, just log the death
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("PLAYER DIED!"));
}
}