İlk yürütme
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "ShooterWeapon.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
#include "Engine/World.h"
|
||||
#include "ShooterProjectile.h"
|
||||
#include "ShooterWeaponHolder.h"
|
||||
#include "Components/SceneComponent.h"
|
||||
#include "TimerManager.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
|
||||
AShooterWeapon::AShooterWeapon()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// create the root
|
||||
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
|
||||
|
||||
// create the first person mesh
|
||||
FirstPersonMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("First Person Mesh"));
|
||||
FirstPersonMesh->SetupAttachment(RootComponent);
|
||||
|
||||
FirstPersonMesh->SetCollisionProfileName(FName("NoCollision"));
|
||||
FirstPersonMesh->SetFirstPersonPrimitiveType(EFirstPersonPrimitiveType::FirstPerson);
|
||||
FirstPersonMesh->bOnlyOwnerSee = true;
|
||||
|
||||
// create the third person mesh
|
||||
ThirdPersonMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Third Person Mesh"));
|
||||
ThirdPersonMesh->SetupAttachment(RootComponent);
|
||||
|
||||
ThirdPersonMesh->SetCollisionProfileName(FName("NoCollision"));
|
||||
ThirdPersonMesh->SetFirstPersonPrimitiveType(EFirstPersonPrimitiveType::WorldSpaceRepresentation);
|
||||
ThirdPersonMesh->bOwnerNoSee = true;
|
||||
}
|
||||
|
||||
void AShooterWeapon::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// subscribe to the owner's destroyed delegate
|
||||
GetOwner()->OnDestroyed.AddDynamic(this, &AShooterWeapon::OnOwnerDestroyed);
|
||||
|
||||
// cast the weapon owner
|
||||
WeaponOwner = Cast<IShooterWeaponHolder>(GetOwner());
|
||||
PawnOwner = Cast<APawn>(GetOwner());
|
||||
|
||||
// fill the first ammo clip
|
||||
CurrentBullets = MagazineSize;
|
||||
|
||||
// attach the meshes to the owner
|
||||
WeaponOwner->AttachWeaponMeshes(this);
|
||||
}
|
||||
|
||||
void AShooterWeapon::EndPlay(EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
// clear the refire timer
|
||||
GetWorld()->GetTimerManager().ClearTimer(RefireTimer);
|
||||
}
|
||||
|
||||
void AShooterWeapon::OnOwnerDestroyed(AActor* DestroyedActor)
|
||||
{
|
||||
// ensure this weapon is destroyed when the owner is destroyed
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void AShooterWeapon::ActivateWeapon()
|
||||
{
|
||||
// unhide this weapon
|
||||
SetActorHiddenInGame(false);
|
||||
|
||||
// notify the owner
|
||||
WeaponOwner->OnWeaponActivated(this);
|
||||
}
|
||||
|
||||
void AShooterWeapon::DeactivateWeapon()
|
||||
{
|
||||
// ensure we're no longer firing this weapon while deactivated
|
||||
StopFiring();
|
||||
|
||||
// hide the weapon
|
||||
SetActorHiddenInGame(true);
|
||||
|
||||
// notify the owner
|
||||
WeaponOwner->OnWeaponDeactivated(this);
|
||||
}
|
||||
|
||||
void AShooterWeapon::StartFiring()
|
||||
{
|
||||
// raise the firing flag
|
||||
bIsFiring = true;
|
||||
|
||||
// check how much time has passed since we last shot
|
||||
// this may be under the refire rate if the weapon shoots slow enough and the player is spamming the trigger
|
||||
const float TimeSinceLastShot = GetWorld()->GetTimeSeconds() - TimeOfLastShot;
|
||||
|
||||
if (TimeSinceLastShot > RefireRate)
|
||||
{
|
||||
// fire the weapon right away
|
||||
Fire();
|
||||
|
||||
} else {
|
||||
|
||||
// if we're full auto, schedule the next shot
|
||||
if (bFullAuto)
|
||||
{
|
||||
GetWorld()->GetTimerManager().SetTimer(RefireTimer, this, &AShooterWeapon::Fire, TimeSinceLastShot, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AShooterWeapon::StopFiring()
|
||||
{
|
||||
// lower the firing flag
|
||||
bIsFiring = false;
|
||||
|
||||
// clear the refire timer
|
||||
GetWorld()->GetTimerManager().ClearTimer(RefireTimer);
|
||||
}
|
||||
|
||||
void AShooterWeapon::Fire()
|
||||
{
|
||||
// ensure the player still wants to fire. They may have let go of the trigger
|
||||
if (!bIsFiring)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// fire a projectile at the target
|
||||
FireProjectile(WeaponOwner->GetWeaponTargetLocation());
|
||||
|
||||
// update the time of our last shot
|
||||
TimeOfLastShot = GetWorld()->GetTimeSeconds();
|
||||
|
||||
// make noise so the AI perception system can hear us
|
||||
MakeNoise(ShotLoudness, PawnOwner, PawnOwner->GetActorLocation(), ShotNoiseRange, ShotNoiseTag);
|
||||
|
||||
// are we full auto?
|
||||
if (bFullAuto)
|
||||
{
|
||||
// schedule the next shot
|
||||
GetWorld()->GetTimerManager().SetTimer(RefireTimer, this, &AShooterWeapon::Fire, RefireRate, false);
|
||||
} else {
|
||||
|
||||
// for semi-auto weapons, schedule the cooldown notification
|
||||
GetWorld()->GetTimerManager().SetTimer(RefireTimer, this, &AShooterWeapon::FireCooldownExpired, RefireRate, false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AShooterWeapon::FireCooldownExpired()
|
||||
{
|
||||
// notify the owner
|
||||
WeaponOwner->OnSemiWeaponRefire();
|
||||
}
|
||||
|
||||
void AShooterWeapon::FireProjectile(const FVector& TargetLocation)
|
||||
{
|
||||
// get the projectile transform
|
||||
FTransform ProjectileTransform = CalculateProjectileSpawnTransform(TargetLocation);
|
||||
|
||||
// spawn the projectile
|
||||
FActorSpawnParameters SpawnParams;
|
||||
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
SpawnParams.TransformScaleMethod = ESpawnActorScaleMethod::OverrideRootScale;
|
||||
SpawnParams.Owner = GetOwner();
|
||||
SpawnParams.Instigator = PawnOwner;
|
||||
|
||||
AShooterProjectile* Projectile = GetWorld()->SpawnActor<AShooterProjectile>(ProjectileClass, ProjectileTransform, SpawnParams);
|
||||
|
||||
// play the firing montage
|
||||
WeaponOwner->PlayFiringMontage(FiringMontage);
|
||||
|
||||
// add recoil
|
||||
WeaponOwner->AddWeaponRecoil(FiringRecoil);
|
||||
|
||||
// consume bullets
|
||||
--CurrentBullets;
|
||||
|
||||
// if the clip is depleted, reload it
|
||||
if (CurrentBullets <= 0)
|
||||
{
|
||||
CurrentBullets = MagazineSize;
|
||||
}
|
||||
|
||||
// update the weapon HUD
|
||||
WeaponOwner->UpdateWeaponHUD(CurrentBullets, MagazineSize);
|
||||
}
|
||||
|
||||
FTransform AShooterWeapon::CalculateProjectileSpawnTransform(const FVector& TargetLocation) const
|
||||
{
|
||||
// find the muzzle location
|
||||
const FVector MuzzleLoc = FirstPersonMesh->GetSocketLocation(MuzzleSocketName);
|
||||
|
||||
// calculate the spawn location ahead of the muzzle
|
||||
const FVector SpawnLoc = MuzzleLoc + ((TargetLocation - MuzzleLoc).GetSafeNormal() * MuzzleOffset);
|
||||
|
||||
// find the aim rotation vector while applying some variance to the target
|
||||
const FRotator AimRot = UKismetMathLibrary::FindLookAtRotation(SpawnLoc, TargetLocation + (UKismetMathLibrary::RandomUnitVector() * AimVariance));
|
||||
|
||||
// return the built transform
|
||||
return FTransform(AimRot, SpawnLoc, FVector::OneVector);
|
||||
}
|
||||
|
||||
const TSubclassOf<UAnimInstance>& AShooterWeapon::GetFirstPersonAnimInstanceClass() const
|
||||
{
|
||||
return FirstPersonAnimInstanceClass;
|
||||
}
|
||||
|
||||
const TSubclassOf<UAnimInstance>& AShooterWeapon::GetThirdPersonAnimInstanceClass() const
|
||||
{
|
||||
return ThirdPersonAnimInstanceClass;
|
||||
}
|
||||
Reference in New Issue
Block a user