UE5--015--DodgeballProjectile--EnemyCharacter


1. DodgeballProjectile

1.1 DodgeballProjectile.h


#pragma once

#include "CoreMinimal.h"

#include "GameFramework/Actor.h"

#include "DodgeballProjectile.generated.h"

UCLASS()
class C005DODGEBALL_API ADodgeballProjectile : public AActor
{
    GENERATED_BODY()
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Dodgeball, meta = (AllowPrivateAccess = "true"))
    class USphereComponent* SphereComponent;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Dodgeball, meta = (AllowPrivateAccess = "true"))
    class UProjectileMovementComponent* ProjectileMovement;

public:    
    // Sets default values for this actor's properties
    ADodgeballProjectile();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    // The damage the dodgeball will deal to the player's character
    UPROPERTY(EditAnywhere, Category = Damage)
    float Damage = 34.f;

    // The sound the dodgeball will make when it bounces off of a surface
    UPROPERTY(EditAnywhere, Category = Sound)
    class USoundBase* BounceSound;

    // The sound attenuation of the previous sound
    UPROPERTY(EditAnywhere, Category = Sound)
    class USoundAttenuation* BounceSoundAttenuation;

    // The particle system the dodgeball will spawn when it hits the player
    UPROPERTY(EditAnywhere, Category = Particles)
    class UParticleSystem* HitParticles;

    // The sound the dodgeball will make when it hits the player
    UPROPERTY(EditAnywhere, Category = Sound)
    class USoundBase* DamageSound;



public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    //
    UFUNCTION()
    void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);

    FORCEINLINE class UProjectileMovementComponent* GetProjectileMovementComponent() const
    {
        return ProjectileMovement;
    }

};


1.2 DodgeballProjectile.cpp


#include "DodgeballProjectile.h"
#include "Components/SphereComponent.h"
#include "../C005DodgeballCharacter.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "HealthComponent.h"
#include "Kismet/GameplayStatics.h"

// Sets default values
ADodgeballProjectile::ADodgeballProjectile()
{
     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    //
    SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereCollision"));
    SphereComponent->SetSphereRadius(35.f);
    SphereComponent->SetCollisionProfileName(FName("Dodgeball"));
    SphereComponent->SetSimulatePhysics(true);
    //Simulation generates Hit events
    SphereComponent->SetNotifyRigidBodyCollision(true);

    //
    // Listen to the OnComponentHit event by binding it to our function
    SphereComponent->OnComponentHit.AddDynamic(this, &ADodgeballProjectile::OnHit);
    // Set this Sphere Component as the root component, otherwise collision won't behave properly
    RootComponent = SphereComponent;

    //
    ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("Projectile Movement"));
    ProjectileMovement->InitialSpeed = 1500.f;

}

// Called when the game starts or when spawned
void ADodgeballProjectile::BeginPlay()
{
    Super::BeginPlay();

    //
    SetLifeSpan(5.f);
}

// Called every frame
void ADodgeballProjectile::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    
}

void ADodgeballProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult&    Hit)
{
    if (BounceSound != nullptr && NormalImpulse.Size() > 600.0f)
    {
        //UGameplayStatics::PlaySoundAtLocation(this, BounceSound, GetActorLocation(), 1.0f, FMath::RandRange(0.7f, 1.3f));

        //UGameplayStatics::PlaySoundAtLocation(this, BounceSound, GetActorLocation(), 1.0f, 1.0f, 0.0f, BounceSoundAttenuation);

        UGameplayStatics::PlaySoundAtLocation(this, BounceSound, GetActorLocation(), 1.0f, FMath::RandRange(0.7f, 1.3f), 0.0f, BounceSoundAttenuation);
    }

    //
    AC005DodgeballCharacter* Player = Cast<AC005DodgeballCharacter>(OtherActor);
    if (Player != nullptr)
    {
        UHealthComponent* HealthComponent = Player->FindComponentByClass<UHealthComponent>();
        if (HealthComponent != nullptr)
        {
            HealthComponent->LoseHealth(Damage);
        }
        //
        if (DamageSound != nullptr)
        {
            UGameplayStatics::PlaySound2D(this, DamageSound);
        }
        //
        if (HitParticles != nullptr)
        {
            UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), HitParticles, GetActorTransform());
        }
        //
        Destroy();
    }

}


1.3 BP_DodgeballProjectile

image


image


image


1. EnemyCharacter

1.1 EnemyCharacter.h


#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "EnemyCharacter.generated.h"

class AActor;

UCLASS()
class C005DODGEBALL_API AEnemyCharacter : public ACharacter
{
    GENERATED_BODY()

    /** Camera boom positioning the camera behind the character */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = LookAt, meta = (AllowPrivateAccess = "true"))
    class USceneComponent* SightSourceDeprecated;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = LookAt, meta = (AllowPrivateAccess = "true"))
    class ULookAtActorComponent* LookAtActorComponent;

    
public:
    // Sets default values for this character's properties
    AEnemyCharacter();

    //The class used to spawn a dodgeball object
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Dodgeball)
    TSubclassOf<class ADodgeballProjectile> DodgeballClass;


protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    //Whether the enemy can see the player this frame
    bool bCanSeePlayer = false;
    //Whether the enemy could see the player last frame
    bool bPreviousCanSeePlayer = false;

    FTimerHandle ThrowTimerHandle;

    float ThrowingInterval = 2.f;

    float ThrowingDelay = 0.5f;

    void ThrowDodgeball();

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

public:
    // Change the rotation of the character to face the given actor
    bool LookAtActorDeprecated(AActor* TargetActor);

    // Can we see the given actor
    bool CanSeeActorDeprecated(const AActor* TargetActor) const;

};


1.2 EnemyCharacter.cpp


#include "EnemyCharacter.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Components/SceneComponent.h"
#include "TimerManager.h"
#include "DodgeballProjectile.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "DodgeballFunctionLibrary.h"
#include "LookAtActorComponent.h"


// Sets default values
AEnemyCharacter::AEnemyCharacter()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    //
    if (1 == 0) {
        SightSourceDeprecated = CreateDefaultSubobject<USceneComponent>(TEXT("SightSource"));
        SightSourceDeprecated->SetupAttachment(RootComponent);
    }
    else {
        LookAtActorComponent = CreateDefaultSubobject<ULookAtActorComponent>(TEXT("LookAtActor Component"));
        LookAtActorComponent->SetupAttachment(RootComponent);
    }
}

// Called when the game starts or when spawned
void AEnemyCharacter::BeginPlay()
{
    Super::BeginPlay();

    // Fetch the character currently being controlled by the player
    ACharacter* PlayerCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);

    LookAtActorComponent->SetTarget(PlayerCharacter);

}

// Called every frame
void AEnemyCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    // Fetch the character currently being controlled by the player
    ACharacter* PlayerCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);


    if (1 == 0) {
        // Look at the player character every frame
        bCanSeePlayer = LookAtActorDeprecated(PlayerCharacter);
    }
    else {
        // Look at the player character every frame
        bCanSeePlayer = LookAtActorComponent->CanSeeTarget();
    }

    if (bCanSeePlayer != bPreviousCanSeePlayer)
    {
        if (bCanSeePlayer)
        {
            //Start throwing dodgeballs
            GetWorldTimerManager().SetTimer(ThrowTimerHandle, this, &AEnemyCharacter::ThrowDodgeball, ThrowingInterval, true, ThrowingDelay);
        }
        else
        {
            //Stop throwing dodgeballs
            GetWorldTimerManager().ClearTimer(ThrowTimerHandle);
        }
    }

    bPreviousCanSeePlayer = bCanSeePlayer;
}

// Called to bind functionality to input
void AEnemyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

}

bool  AEnemyCharacter::LookAtActorDeprecated(AActor* TargetActor) {
    if (TargetActor == nullptr)
    {
        return false;
    }
    if (1 == 0) {
        if (CanSeeActorDeprecated(TargetActor))
        {
            FVector Start = GetActorLocation();
            // 
            // Where the Line Trace starts and ends
            //FVector Start = SightSource->GetComponentLocation();
            FVector End = TargetActor->GetActorLocation();
            // Calculate the necessary rotation for the Start point to face the End point
            FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(Start, End);
            //Set the enemy's rotation to that rotation
            SetActorRotation(LookAtRotation);
            return true;
        }
    }
    else {
        const TArray<const AActor*> IgnoreActors = { this, TargetActor };
        if (UDodgeballFunctionLibrary::CanSeeActor(GetWorld(), SightSourceDeprecated->GetComponentLocation(), TargetActor, IgnoreActors)) {
            FVector Start = GetActorLocation();
            // 
            // Where the Line Trace starts and ends
            //FVector Start = SightSource->GetComponentLocation();
            FVector End = TargetActor->GetActorLocation();
            // Calculate the necessary rotation for the Start point to face the End point
            FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(Start, End);
            //Set the enemy's rotation to that rotation
            SetActorRotation(LookAtRotation);
            return true;
        }
    }
    return false;
}

bool  AEnemyCharacter::CanSeeActorDeprecated(const AActor* TargetActor) const {
    if (TargetActor == nullptr)
    {
        return false;
    }
    // Store the results of the Line Trace
    FHitResult Hit;
    // Where the Line Trace starts and ends
    //FVector Start = GetActorLocation();
    FVector Start = SightSourceDeprecated->GetComponentLocation();
    FVector End = TargetActor->GetActorLocation();
    if (0 == 1) {
        // The trace channel we want to compare against
        ECollisionChannel Channel = ECollisionChannel::ECC_Visibility;
        // Execute the Line Trace
        //GetWorld()->LineTraceSingleByChannel(Hit, Start, End, Channel);
        FCollisionQueryParams QueryParams;
        // Ignore the actor that's executing this Line Trace
        QueryParams.AddIgnoredActor(this);
        // Ignore the target we're checking for
        QueryParams.AddIgnoredActor(TargetActor);
        // Execute the Line Trace
        GetWorld()->LineTraceSingleByChannel(Hit, Start, End, Channel, QueryParams);
        // Show the Line Trace inside the game
        DrawDebugLine(GetWorld(), Start, End, FColor::Red);
    }
    else {
        // Rotation of the shape used in the Sweep Trace
        FQuat Rotation = FQuat::Identity;
        // The trace channel we want to compare against
        ECollisionChannel Channel = ECollisionChannel::ECC_Visibility;
        // Shape of the object used in the Sweep Trace
        FCollisionShape Shape = FCollisionShape::MakeBox(FVector(20.f, 20.f, 20.f));
        GetWorld()->SweepSingleByChannel(Hit, Start, End, Rotation, Channel, Shape);
        DrawDebugLine(GetWorld(), Start, End, FColor::Red);
    }
    return !Hit.bBlockingHit;

}

void AEnemyCharacter::ThrowDodgeball() {
    if (DodgeballClass == nullptr)
    {
        return;
    }
    FVector ForwardVector = GetActorForwardVector();
    float SpawnDistance = 40.f;
    FVector SpawnLocation = GetActorLocation() + (ForwardVector * SpawnDistance);
    //Spawn new dodgeball
    if (0 == 1) {
        //GetWorld()->SpawnActor<ADodgeballProjectile>(DodgeballClass, SpawnLocation, GetActorRotation());    
    }
    else {
        FTransform SpawnTransform(GetActorRotation(), SpawnLocation);
        ADodgeballProjectile* Projectile = GetWorld()->SpawnActorDeferred<ADodgeballProjectile>(DodgeballClass, SpawnTransform);
        Projectile->GetProjectileMovementComponent()->InitialSpeed = 2200;
        Projectile->FinishSpawning(SpawnTransform);
    }
}


1.3 BP_EnemyCharacter

image


image


image

posted @ 2025-04-05 21:47  ParamousGIS  阅读(25)  评论(0)    收藏  举报