diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index df8e667..eb4c743 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -148,3 +148,61 @@ ConnectionType=USBOnly bUseManualIPAddress=False ManualIPAddress= +[/Script/Engine.CollisionProfile] +-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False) +-Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False) +-Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False) +-Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic",Response=ECR_Block),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False) +-Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False) +-Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False) +-Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False) +-Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False) +-Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Block),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) ++Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision") ++Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.") ++Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ") ++Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ") ++Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic"),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.") ++Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.") ++Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors") ++Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors") ++Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.") ++Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.") ++Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.") ++Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.") ++Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.") ++Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="Projectile",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Player",Response=ECR_Ignore)),HelpMessage="Needs description") ++DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="Player") +-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") +-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") +-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") +-ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor") +-ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic") ++ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") ++ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") ++ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") ++ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor") ++ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic") +-CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic") +-CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic") +-CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") +-CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") ++CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic") ++CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic") ++CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") ++CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") + diff --git a/Content/Blueprints/BP_EnemySpaceship.uasset b/Content/Blueprints/BP_EnemySpaceship.uasset index f9f8d8a..29a4df5 100644 --- a/Content/Blueprints/BP_EnemySpaceship.uasset +++ b/Content/Blueprints/BP_EnemySpaceship.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32a1079a3aa4dbba7634949a8b98f467c7959a23fdfaffccc94556673146d940 -size 30566 +oid sha256:d4e26c26896c7225386a0b925bab12069393cc61765f9a32422cd0bac048769c +size 31068 diff --git a/Content/Blueprints/BP_SpaceShooterGameMode.uasset b/Content/Blueprints/BP_SpaceShooterGameMode.uasset index cb02d8e..ec25c37 100644 --- a/Content/Blueprints/BP_SpaceShooterGameMode.uasset +++ b/Content/Blueprints/BP_SpaceShooterGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3206d77acf0d9f5558ccc71a1ff9a6980c162881ed1a8ea39f195d558584965 -size 19699 +oid sha256:d1ee6edc9f63cf69d3f0449ed485fbe473f0165ef00498861d19387a831f08fb +size 20146 diff --git a/Content/Blueprints/BP_SpaceshipPawn.uasset b/Content/Blueprints/BP_SpaceshipPawn.uasset index 9c01f45..f8298f2 100644 --- a/Content/Blueprints/BP_SpaceshipPawn.uasset +++ b/Content/Blueprints/BP_SpaceshipPawn.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:383453441cf057084b8f8af9f79580d7a51aaad1dae90912ccccd5ca61a6dfef -size 33608 +oid sha256:6e6f3deee01a0b19b9c895fbe7016d28aece167815e6a162920bda72d78b13ce +size 34050 diff --git a/Content/Blueprints/BP_SpaceshipProjectile.uasset b/Content/Blueprints/BP_SpaceshipProjectile.uasset index 80e9c59..7602d82 100644 --- a/Content/Blueprints/BP_SpaceshipProjectile.uasset +++ b/Content/Blueprints/BP_SpaceshipProjectile.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6c797116730acdf88d7fe12289d51ee034603e44a09ca78fc8bd3419e55d65e -size 30412 +oid sha256:1ffdd0587cf11ff2ee82abbd7ded2e54afa6a639fa8732183a8562fbd2cba0c4 +size 32781 diff --git a/Content/Input/IA_Shoot.uasset b/Content/Input/IA_Shoot.uasset new file mode 100644 index 0000000..703fe82 --- /dev/null +++ b/Content/Input/IA_Shoot.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23289d35353d03dd39608305d87389985238559667a3adc88c8f0b2e6ea07201 +size 1893 diff --git a/Content/Input/IMC_Spaceship.uasset b/Content/Input/IMC_Spaceship.uasset index 56e5e94..6c487b2 100644 --- a/Content/Input/IMC_Spaceship.uasset +++ b/Content/Input/IMC_Spaceship.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fd69807528795d2fce8f5aa3033cc8a407fe8754d768ecd1a6cd850f87bf88d -size 5519 +oid sha256:7835df7e64bc032143d479af01dc03b4d39585eec741c69310ccd7a0e6da5678 +size 5521 diff --git a/Source/MyProject3/EnemySpaceship.cpp b/Source/MyProject3/EnemySpaceship.cpp index f8885bf..9e1056c 100644 --- a/Source/MyProject3/EnemySpaceship.cpp +++ b/Source/MyProject3/EnemySpaceship.cpp @@ -1,24 +1,33 @@ #include "EnemySpaceship.h" -#include "SpaceshipPawn.h" -#include "SpaceshipProjectile.h" #include "Kismet/GameplayStatics.h" AEnemySpaceship::AEnemySpaceship() { PrimaryActorTick.bCanEverTick = true; - ShipMesh = CreateDefaultSubobject(TEXT("ShipMesh")); - RootComponent = ShipMesh; + // Create and setup the enemy mesh + EnemyMesh = CreateDefaultSubobject(TEXT("EnemyMesh")); + RootComponent = EnemyMesh; - CurrentHealth = MaxHealth; + // Disable gravity and physics simulation + EnemyMesh->SetSimulatePhysics(false); + EnemyMesh->SetEnableGravity(false); + + // Set up collision for the enemy mesh + EnemyMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + EnemyMesh->SetCollisionObjectType(ECC_Pawn); + EnemyMesh->SetCollisionResponseToAllChannels(ECR_Block); + EnemyMesh->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore); // Ignore other pawns + EnemyMesh->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Ignore); // Ignore player + EnemyMesh->SetGenerateOverlapEvents(true); } void AEnemySpaceship::BeginPlay() { Super::BeginPlay(); - PlayerPawn = Cast(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)); - GetWorldTimerManager().SetTimer(FireTimerHandle, this, &AEnemySpaceship::FireAtPlayer, FireRate, true); + // Find the player pawn + PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0); } void AEnemySpaceship::Tick(float DeltaTime) @@ -27,66 +36,43 @@ void AEnemySpaceship::Tick(float DeltaTime) if (PlayerPawn) { - // Calculate distance to player - float DistanceToPlayer = FVector::Dist(GetActorLocation(), PlayerPawn->GetActorLocation()); + // Move towards the player + FVector Direction = (PlayerPawn->GetActorLocation() - GetActorLocation()).GetSafeNormal(); + FVector NewLocation = GetActorLocation() + Direction * MovementSpeed * DeltaTime; + SetActorLocation(NewLocation); - if (DistanceToPlayer <= DetectionRange) - { - // Look at player - FVector Direction = PlayerPawn->GetActorLocation() - GetActorLocation(); - FRotator NewRotation = Direction.Rotation(); - SetActorRotation(FMath::RInterpTo(GetActorRotation(), NewRotation, DeltaTime, 2.0f)); - - // Move towards player while maintaining some distance - float TargetDistance = 1000.0f; - if (DistanceToPlayer > TargetDistance) - { - FVector NewLocation = GetActorLocation() + (Direction.GetSafeNormal() * MovementSpeed * DeltaTime); - SetActorLocation(NewLocation); - } - else if (DistanceToPlayer < TargetDistance - 100.0f) - { - FVector NewLocation = GetActorLocation() - (Direction.GetSafeNormal() * MovementSpeed * DeltaTime); - SetActorLocation(NewLocation); - } - } + // Face towards the player + FRotator NewRotation = Direction.Rotation(); + SetActorRotation(NewRotation); } } -void AEnemySpaceship::FireAtPlayer() -{ - if (PlayerPawn && FVector::Dist(GetActorLocation(), PlayerPawn->GetActorLocation()) <= DetectionRange) - { - UWorld* World = GetWorld(); - if (World) - { - FVector SpawnLocation = GetActorLocation() + (GetActorForwardVector() * 100.0f); - FRotator SpawnRotation = GetActorRotation(); - FActorSpawnParameters SpawnParams; - SpawnParams.Owner = this; - SpawnParams.Instigator = GetInstigator(); - - ASpaceshipProjectile* Projectile = World->SpawnActor( - ASpaceshipProjectile::StaticClass(), - SpawnLocation, - SpawnRotation, - SpawnParams - ); - } - } -} - -float AEnemySpaceship::TakeDamage(float Damage, FDamageEvent const& DamageEvent, +float AEnemySpaceship::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) { - float ActualDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser); + float DamageToApply = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser); - CurrentHealth -= ActualDamage; - if (CurrentHealth <= 0) + CurrentHealth -= DamageToApply; + + // Debug message + if (GEngine) { - // Destroy the enemy ship - Destroy(); + GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, + FString::Printf(TEXT("Enemy Health: %f"), CurrentHealth)); } - return ActualDamage; + if (CurrentHealth <= 0) + { + Die(); + } + + return DamageToApply; +} + +void AEnemySpaceship::Die() +{ + // Add any death effects here + + // Destroy the enemy + Destroy(); } \ No newline at end of file diff --git a/Source/MyProject3/EnemySpaceship.h b/Source/MyProject3/EnemySpaceship.h index b7ef931..758e518 100644 --- a/Source/MyProject3/EnemySpaceship.h +++ b/Source/MyProject3/EnemySpaceship.h @@ -1,41 +1,40 @@ #pragma once #include "CoreMinimal.h" -#include "GameFramework/Pawn.h" +#include "GameFramework/Actor.h" #include "EnemySpaceship.generated.h" UCLASS() -class MYPROJECT3_API AEnemySpaceship : public APawn +class MYPROJECT3_API AEnemySpaceship : public AActor { GENERATED_BODY() public: AEnemySpaceship(); - virtual void Tick(float DeltaTime) override; protected: virtual void BeginPlay() override; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components") - class UStaticMeshComponent* ShipMesh; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI") - float DetectionRange = 1500.0f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI") - float FireRate = 2.0f; + UStaticMeshComponent* EnemyMesh; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement") - float MovementSpeed = 500.0f; + float MovementSpeed = 300.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") float MaxHealth = 100.0f; - float CurrentHealth; - FTimerHandle FireTimerHandle; - class ASpaceshipPawn* PlayerPawn; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + float CurrentHealth = 100.0f; - void FireAtPlayer(); - virtual float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, - AController* EventInstigator, AActor* DamageCauser) override; +public: + virtual void Tick(float DeltaTime) override; + + // Override TakeDamage to handle damage taken + virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, + class AController* EventInstigator, AActor* DamageCauser) override; + +private: + AActor* PlayerPawn; + void Die(); }; \ No newline at end of file diff --git a/Source/MyProject3/SpaceShooterGameMode.cpp b/Source/MyProject3/SpaceShooterGameMode.cpp index e7153f7..2a5a1ab 100644 --- a/Source/MyProject3/SpaceShooterGameMode.cpp +++ b/Source/MyProject3/SpaceShooterGameMode.cpp @@ -12,6 +12,13 @@ ASpaceShooterGameMode::ASpaceShooterGameMode() DefaultPawnClass = PlayerPawnBPClass.Class; } + // Find enemy blueprint class + static ConstructorHelpers::FClassFinder EnemyBPClass(TEXT("/Game/Blueprints/BP_EnemySpaceship")); + if (EnemyBPClass.Class != nullptr) + { + EnemyClass = EnemyBPClass.Class; + } + // Enable Tick() PrimaryActorTick.bCanEverTick = true; @@ -52,14 +59,15 @@ void ASpaceShooterGameMode::SpawnEnemy() if (FoundEnemies.Num() < MaxEnemies) { UWorld* World = GetWorld(); - if (World) + if (World && EnemyClass) { FVector SpawnLocation = GetRandomSpawnLocation(); FRotator SpawnRotation = FRotator::ZeroRotator; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; - World->SpawnActor(AEnemySpaceship::StaticClass(), SpawnLocation, + // Spawn using the Blueprint class instead of the C++ class directly + AEnemySpaceship* NewEnemy = World->SpawnActor(EnemyClass, SpawnLocation, SpawnRotation, SpawnParams); } } @@ -75,10 +83,13 @@ FVector ASpaceShooterGameMode::GetRandomSpawnLocation() // Generate random angle float Angle = FMath::RandRange(0.0f, 2.0f * PI); + // Generate random radius between min and max + float Radius = FMath::RandRange(MinSpawnRadius, MaxSpawnRadius); + // Calculate spawn position in a circle around the player FVector SpawnLocation; - SpawnLocation.X = PlayerLocation.X + SpawnRadius * FMath::Cos(Angle); - SpawnLocation.Y = PlayerLocation.Y + SpawnRadius * FMath::Sin(Angle); + SpawnLocation.X = PlayerLocation.X + Radius * FMath::Cos(Angle); + SpawnLocation.Y = PlayerLocation.Y + Radius * FMath::Sin(Angle); SpawnLocation.Z = PlayerLocation.Z; return SpawnLocation; diff --git a/Source/MyProject3/SpaceShooterGameMode.h b/Source/MyProject3/SpaceShooterGameMode.h index 7cbc4cc..12f3f05 100644 --- a/Source/MyProject3/SpaceShooterGameMode.h +++ b/Source/MyProject3/SpaceShooterGameMode.h @@ -17,13 +17,19 @@ public: protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") - float EnemySpawnInterval = 3.0f; + TSubclassOf EnemyClass; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") - float SpawnRadius = 2000.0f; + float EnemySpawnInterval = 2.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") - int32 MaxEnemies = 5; + int32 MaxEnemies = 10; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") + float MinSpawnRadius = 1000.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") + float MaxSpawnRadius = 2000.0f; private: FTimerHandle EnemySpawnTimer; diff --git a/Source/MyProject3/SpaceshipPawn.cpp b/Source/MyProject3/SpaceshipPawn.cpp index 55cc655..8221a24 100644 --- a/Source/MyProject3/SpaceshipPawn.cpp +++ b/Source/MyProject3/SpaceshipPawn.cpp @@ -19,6 +19,10 @@ ASpaceshipPawn::ASpaceshipPawn() ShipMesh->SetSimulatePhysics(false); ShipMesh->SetEnableGravity(false); + // Create projectile spawn point + ProjectileSpawnPoint = CreateDefaultSubobject(TEXT("ProjectileSpawnPoint")); + ProjectileSpawnPoint->SetupAttachment(ShipMesh); + // Create camera spring arm CameraSpringArm = CreateDefaultSubobject(TEXT("CameraSpringArm")); CameraSpringArm->SetupAttachment(RootComponent); @@ -46,12 +50,40 @@ ASpaceshipPawn::ASpaceshipPawn() CurrentYaw = 0.0f; TargetRotation = FRotator::ZeroRotator; LastMouseDelta = FVector2D::ZeroVector; + + if (ShipMesh) + { + ShipMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + ShipMesh->SetCollisionObjectType(ECC_GameTraceChannel1); // This is the "Player" channel + ShipMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block); + ShipMesh->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECollisionResponse::ECR_Ignore); + ShipMesh->SetCollisionResponseToChannel(ECC_Pawn, ECollisionResponse::ECR_Ignore); + } } void ASpaceshipPawn::BeginPlay() { Super::BeginPlay(); + // Debug messages for setup verification + if (GEngine) + { + if (ProjectileClass) + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("ProjectileClass is set")); + else + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("ProjectileClass is NOT set")); + + if (ShootAction) + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("ShootAction is set")); + else + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("ShootAction is NOT set")); + + if (ProjectileSpawnPoint) + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("ProjectileSpawnPoint is set")); + else + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("ProjectileSpawnPoint is NOT set")); + } + // Store player controller reference PlayerControllerRef = Cast(Controller); @@ -217,8 +249,8 @@ void ASpaceshipPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputCompo // Bind mouse control EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &ASpaceshipPawn::HandleMouseLook); - // Bind fire action - EnhancedInputComponent->BindAction(FireAction, ETriggerEvent::Triggered, this, &ASpaceshipPawn::HandleFire); + // Add shooting binding + EnhancedInputComponent->BindAction(ShootAction, ETriggerEvent::Triggered, this, &ASpaceshipPawn::HandleShoot); } } @@ -243,27 +275,64 @@ void ASpaceshipPawn::HandleMouseLook(const FInputActionValue& Value) LastMouseDelta = MouseDelta; } -void ASpaceshipPawn::HandleFire(const FInputActionValue& Value) +void ASpaceshipPawn::HandleShoot(const FInputActionValue& Value) { + // Debug message + if (GEngine) + GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Yellow, TEXT("HandleShoot Called")); + + if (bCanFire) + { + Fire(); + } +} + +void ASpaceshipPawn::Fire() +{ + // Debug messages + if (!ProjectileClass) + { + if (GEngine) + GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Red, TEXT("ProjectileClass not set!")); + return; + } + UWorld* World = GetWorld(); if (World) { - FVector SpawnLocation = ShipMesh->GetSocketLocation(TEXT("ProjectileSpawn")); - if (SpawnLocation == FVector::ZeroVector) - { - SpawnLocation = GetActorLocation() + (GetActorForwardVector() * 100.0f); - } - + FVector SpawnLocation = ProjectileSpawnPoint->GetComponentLocation(); FRotator SpawnRotation = GetActorRotation(); + FActorSpawnParameters SpawnParams; SpawnParams.Owner = this; SpawnParams.Instigator = GetInstigator(); - World->SpawnActor( - ASpaceshipProjectile::StaticClass(), + // Spawn the projectile + ASpaceshipProjectile* Projectile = World->SpawnActor( + ProjectileClass, SpawnLocation, SpawnRotation, SpawnParams ); + + if (Projectile) + { + if (GEngine) + GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Green, TEXT("Projectile Spawned")); + } + else + { + if (GEngine) + GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Red, TEXT("Failed to spawn projectile")); + } + + // Start fire rate timer + bCanFire = false; + GetWorldTimerManager().SetTimer(FireTimerHandle, this, &ASpaceshipPawn::ResetFire, FireRate, false); } +} + +void ASpaceshipPawn::ResetFire() +{ + bCanFire = true; } \ No newline at end of file diff --git a/Source/MyProject3/SpaceshipPawn.h b/Source/MyProject3/SpaceshipPawn.h index 876de86..a6adaf2 100644 --- a/Source/MyProject3/SpaceshipPawn.h +++ b/Source/MyProject3/SpaceshipPawn.h @@ -38,9 +38,6 @@ protected: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") class UInputAction* MouseLookAction; - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") - class UInputAction* FireAction; - // Movement Parameters UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement") float MaxThrust = 2000.0f; @@ -67,11 +64,24 @@ protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement", meta = (ClampMin = "0.0", ClampMax = "1.0", ToolTip = "How much of the current velocity is maintained when changing direction (0 = none, 1 = full)")) float DirectionalInertia = 0.3f; + // Shooting properties + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + TSubclassOf ProjectileClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + float FireRate = 0.2f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + USceneComponent* ProjectileSpawnPoint; + + // Input action for shooting + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") + class UInputAction* ShootAction; + // Input functions void HandleThrottleStarted(const FInputActionValue& Value); void HandleThrottleReleased(const FInputActionValue& Value); void HandleMouseLook(const FInputActionValue& Value); - void HandleFire(const FInputActionValue& Value); private: // Movement state @@ -91,4 +101,11 @@ private: bool bWasThrottlePressed; FVector DesiredVelocityDirection; + + FTimerHandle FireTimerHandle; + bool bCanFire = true; + + void HandleShoot(const FInputActionValue& Value); + void Fire(); + void ResetFire(); }; \ No newline at end of file diff --git a/Source/MyProject3/SpaceshipProjectile.cpp b/Source/MyProject3/SpaceshipProjectile.cpp index 14dc22a..3a000c0 100644 --- a/Source/MyProject3/SpaceshipProjectile.cpp +++ b/Source/MyProject3/SpaceshipProjectile.cpp @@ -1,6 +1,7 @@ #include "SpaceshipProjectile.h" #include "GameFramework/ProjectileMovementComponent.h" #include "Components/StaticMeshComponent.h" +#include "EnemySpaceship.h" ASpaceshipProjectile::ASpaceshipProjectile() { @@ -12,6 +13,17 @@ ASpaceshipProjectile::ASpaceshipProjectile() ProjectileMesh->SetCollisionProfileName(TEXT("Projectile")); ProjectileMesh->OnComponentHit.AddDynamic(this, &ASpaceshipProjectile::OnHit); + // Set up collision + ProjectileMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + ProjectileMesh->SetCollisionObjectType(ECC_WorldDynamic); + ProjectileMesh->SetCollisionResponseToAllChannels(ECR_Ignore); + ProjectileMesh->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block); // Block pawns (enemies) + ProjectileMesh->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block); // Block world static + ProjectileMesh->SetGenerateOverlapEvents(true); + + // Bind hit event + ProjectileMesh->OnComponentHit.AddDynamic(this, &ASpaceshipProjectile::OnHit); + // Create and setup projectile movement ProjectileMovement = CreateDefaultSubobject(TEXT("ProjectileMovement")); ProjectileMovement->UpdatedComponent = ProjectileMesh; @@ -32,12 +44,32 @@ void ASpaceshipProjectile::BeginPlay() void ASpaceshipProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { - if (OtherActor && OtherActor != this) + // Debug message to verify hit detection + if (GEngine) { - // Apply damage to the hit actor (if it implements damage interface) - FDamageEvent DamageEvent; - OtherActor->TakeDamage(DamageAmount, DamageEvent, nullptr, this); + GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, + FString::Printf(TEXT("Projectile hit: %s"), *OtherActor->GetName())); } + if (OtherActor && OtherActor != this) + { + // Check if we hit an enemy + AEnemySpaceship* Enemy = Cast(OtherActor); + if (Enemy) + { + // Apply damage + FDamageEvent DamageEvent; + Enemy->TakeDamage(DamageAmount, DamageEvent, nullptr, this); + + // Debug message for damage + if (GEngine) + { + GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, + FString::Printf(TEXT("Dealt %f damage to enemy"), DamageAmount)); + } + } + } + + // Destroy the projectile Destroy(); } \ No newline at end of file