Hello! I am having struggles with understanding how I would be able to store my Weapon data properly in my Arrays.
- I’m having issues having the Array and CurrentWeapon pointer figure out how to add my ammo properly
- Issues with when spawning from the array to show the correct CurrentAmmo and CurrentClip
Any Idea what’s happening or what I am doing wrong?
character.cpp
void ATesterCharacter::BeginPlay()
{
GiveDefaultWeapons();
}
void ATesterCharacter::GiveDefaultWeapons()
{
WeapInventory[0] = Knife_D;
AWeapon *Spawner = GetWorld()->SpawnActor<AWeapon>(WeapInventory[0]);
if (Spawner)
{
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Spawned");
CurrentWeapon = Spawner;
Spawner->AttachRootComponentTo(GetMesh1P(), "WeapSocket", EAttachLocation::SnapToTarget);
}
}
//////////////////////////////////////////////////////////////////////////
// Input
void ATesterCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
// set up gameplay key bindings
check(InputComponent);
InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
InputComponent->BindAction("PreviousWeapon", IE_Pressed, this, &ATesterCharacter::PrevWeapon);
InputComponent->BindAction("NextWeapon", IE_Pressed, this, &ATesterCharacter::NextWeapon);
InputComponent->BindAction("Fire", IE_Pressed, this, &ATesterCharacter::OnFire);
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ATesterCharacter::TouchStarted);
InputComponent->BindAxis("MoveForward", this, &ATesterCharacter::MoveForward);
InputComponent->BindAxis("MoveRight", this, &ATesterCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &ATesterCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &ATesterCharacter::LookUpAtRate);
}
void ATesterCharacter::OnFire()
{
if (CurrentWeapon != NULL)
{
CurrentWeapon->Fire();
}
}
void ATesterCharacter::OnCollision(AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
{
AWeapon *Weapon = Cast<AWeapon>(OtherActor);
if (Weapon)
{
ProcessWeaponPickup(Weapon);
}
}
void ATesterCharacter::ProcessWeaponPickup(AWeapon *Weapon)
{
if (Weapon != NULL)
{
//Check to see if Weapon is not currently in the priority slot in Array (basically checks to see if weapon is in the array)
if (WeapInventory[Weapon->WeapConfig.Priority] != Weapon->GetClass())
{
//Insert Weapon in correct slot by priority
WeapInventory.Insert(Weapon->GetClass(), Weapon->WeapConfig.Priority);
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "You Just picked up a " + WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.Name);
//Checks to see if the weapon is a higher priority number
if (Weapon->WeapConfig.Priority > WeapInventory[CurrentWeapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.Priority)
{
//Equip weapon with the higher priority
EquipWeapon(WeapInventory[Weapon->WeapConfig.Priority]);
}
//Destroy the picked up weapon on the scene
Weapon->Destroy();
}
else
{
if (!WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.bIsEquipped)
{
if (WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->CurrentAmmo >= 0 && Weapon->CurrentAmmo <= (WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.MaxAmmo - WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->CurrentAmmo))
{
WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->CurrentAmmo += Weapon->CurrentAmmo;
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Added " + Weapon->CurrentAmmo);
Weapon->Destroy();
}
else
{
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Full Ammo on " + WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.Name);
}
}
else
{
if (CurrentWeapon->CurrentAmmo >= 0 && Weapon->CurrentAmmo <= (CurrentWeapon->WeapConfig.MaxAmmo - CurrentWeapon->CurrentAmmo))
{
CurrentWeapon->CurrentAmmo += Weapon->CurrentAmmo;
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Added " + Weapon->CurrentAmmo);
}
if (CurrentWeapon->CurrentAmmo == CurrentWeapon->WeapConfig.MaxAmmo)
{
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Full");
}
else
{
CurrentWeapon->CurrentAmmo = CurrentWeapon->WeapConfig.MaxAmmo;
}
}
}
}
}
void ATesterCharacter::ProcessItemPickup(AItem *Item)
{
}
void ATesterCharacter::NextWeapon()
{
if (GetNextWeapon() != NULL && GetNextWeapon()->GetDefaultObject<AWeapon>() != CurrentWeapon)
{
EquipWeapon(GetNextWeapon());
WeapInventory[CurrentWeapon->WeapConfig.Priority] = CurrentWeapon->GetClass();
}
}
void ATesterCharacter::PrevWeapon()
{
if (GetPreviousWeapon() != NULL && CurrentWeapon != GetPreviousWeapon()->GetDefaultObject<AWeapon>())
{
EquipWeapon(GetPreviousWeapon());
WeapInventory[CurrentWeapon->WeapConfig.Priority] = CurrentWeapon->GetClass();
}
}
TSubclassOf<AWeapon> ATesterCharacter::GetPreviousWeapon()
{
if (WeapInventory[CurrentWeapon->WeapConfig.Priority - 1] == NULL)
{
return WeapInventory[CurrentWeapon->WeapConfig.Priority];
}
else
{
return WeapInventory[CurrentWeapon->WeapConfig.Priority - 1];
}
}
TSubclassOf<AWeapon> ATesterCharacter::GetNextWeapon()
{
if (WeapInventory[CurrentWeapon->WeapConfig.Priority + 1] == NULL)
{
return WeapInventory[CurrentWeapon->WeapConfig.Priority];
}
else
{
return WeapInventory[CurrentWeapon->WeapConfig.Priority + 1];
}
}
void ATesterCharacter::EquipWeapon(TSubclassOf<AWeapon> Weapon)
{
if (CurrentWeapon != NULL)
{
CurrentWeapon->Destroy();
Weapon->GetDefaultObject<AWeapon>()->WeapConfig.bIsEquipped = true;
AWeapon *Spawner = GetWorld()->SpawnActor<AWeapon>(Weapon);
if (Spawner)
{
Spawner->CollisionComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Spawned");
CurrentWeapon = Spawner;
Spawner->AttachRootComponentTo(GetMesh1P(), "WeapSocket", EAttachLocation::SnapToTarget);
}
}
}
character.h
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Character.h"
#include "Weapon.h"
#include "Item.h"
#include "Knife.h"
#include "TesterCharacter.generated.h"
UCLASS(config=Game)
class ATesterCharacter : public ACharacter
{
GENERATED_BODY()
/** Pawn mesh: 1st person view (arms; seen only by self) */
UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
class USkeletalMeshComponent* Mesh1P;
UPROPERTY(VisibleDefaultsOnly, Category = Collision)
class UBoxComponent *CollisionComp;
/** First person camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FirstPersonCameraComponent;
public:
ATesterCharacter(const FObjectInitializer& ObjectInitializer);
/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseTurnRate;
/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseLookUpRate;
/** Gun muzzle's offset from the characters location */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
FVector GunOffset;
/** Projectile class to spawn */
UPROPERTY(EditDefaultsOnly, Category=Projectile)
TSubclassOf<class ATesterProjectile> ProjectileClass;
/** Sound to play each time we fire */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
class USoundBase* FireSound;
/** AnimMontage to play each time we fire */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
class UAnimMontage* FireAnimation;
//Stores Currently Equiped Weapon Information(to access functions, variables, etc)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Weapon)
AWeapon *CurrentWeapon;
//Inventory for the Weapons
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Inventory)
TArray<TSubclassOf<AWeapon>> WeapInventory;
//Inventory for Key Items
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Inventory)
TArray<TSubclassOf<AItem>> ItemInventory;
TSubclassOf<AKnife> Knife_D;
void GiveDefaultWeapons();
//Tells Inventory if that weapon is already in inventory. If it is, the function will then add ammo. If ammo is full, it will then not process the pickup
void ProcessWeaponPickup(AWeapon *Weapon);
TSubclassOf<AWeapon> GetPreviousWeapon();
TSubclassOf<AWeapon> GetNextWeapon();
void NextWeapon();
void PrevWeapon();
void ProcessItemPickup(AItem *Item);
void EquipWeapon(TSubclassOf<AWeapon> Weapon);
virtual void BeginPlay() override;
protected:
/** Handler for a touch input beginning. */
void TouchStarted(const ETouchIndex::Type FingerIndex, const FVector Location);
/** Fires a projectile. */
void OnFire();
/** Handles moving forward/backward */
void MoveForward(float Val);
/** Handles stafing movement, left and right */
void MoveRight(float Val);
/**
* Called via input to turn at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void TurnAtRate(float Rate);
/**
* Called via input to turn look up/down at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void LookUpAtRate(float Rate);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
// End of APawn interface
UFUNCTION()
void OnCollision(AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult);
public:
/** Returns Mesh1P subobject **/
FORCEINLINE class USkeletalMeshComponent* GetMesh1P() const { return Mesh1P; }
/** Returns FirstPersonCameraComponent subobject **/
FORCEINLINE class UCameraComponent* GetFirstPersonCameraComponent() const { return FirstPersonCameraComponent; }
};
weapon.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "Weapon.generated.h"
#define TRACE_WEAPON ECC_GameTraceChannel1
UENUM(BlueprintType)
namespace EProjectile
{
enum Type
{
E_Bullet UMETA(DisplayName = "Bullet"),
E_Projectile UMETA(DisplayName = "Projectile"),
E_Melee UMETA(DisplayName = "Melee"),
};
}
USTRUCT(BlueprintType)
struct FWeaponConfig
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
int32 MaxAmmo;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
int32 MaxClip;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
float WeaponRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
float TimeBetweenShots;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Ammo)
int32 ShotCost;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
float WeaponSpread;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
FString Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
UTexture2D* SplashArt;
//Use of Priority Makes the picking up of weapon and equiping them automatically sort them in TesterCharacter
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
int32 Priority;
//Checks to see if weapon is equipped
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
bool bIsEquipped;
};
/**
*
*/
UCLASS()
class Tester_API AWeapon : public AActor
{
GENERATED_UCLASS_BODY()
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Mesh)
USkeletalMeshComponent *Mesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Collision)
UBoxComponent *CollisionComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
FWeaponConfig WeapConfig;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
TEnumAsByte<EProjectile::Type> ProjType;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
int32 CurrentAmmo;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
int32 CurrentClip;
void Fire();
void ReloadAmmo();
virtual void Instant_Fire();
virtual void ProjectileFire();
virtual void MeleeFire();
protected:
FHitResult WeaponTrace(const FVector &TraceFrom, const FVector &TraceTo) const;
void ProcessInstantHit(const FHitResult &Impact, const FVector &Origin, const FVector &ShootDir, int32 RandomSeed, float ReticleSpread);
};
weapon.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Tester.h"
#include "Weapon.h"
AWeapon::AWeapon(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Mesh = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, "Mesh");
RootComponent = Mesh;
CollisionComp = ObjectInitializer.CreateDefaultSubobject<UBoxComponent>(this, "CollisionComp");
CollisionComp->AttachParent = Mesh;
}
void AWeapon::Fire()
{
if (CurrentClip > 0)
{
CurrentClip -= WeapConfig.ShotCost;
switch (ProjType)
{
case EProjectile::E_Bullet:
Instant_Fire();
break;
case EProjectile::E_Projectile:
ProjectileFire();
break;
case EProjectile::E_Melee:
MeleeFire();
default:
break;
}
}
else
{
ReloadAmmo();
}
}
void AWeapon::ReloadAmmo()
{
if (CurrentAmmo > 0)
{
if (CurrentAmmo < WeapConfig.MaxClip)
{
CurrentClip = CurrentAmmo;
}
else
{
CurrentAmmo -= WeapConfig.MaxClip;
CurrentClip += WeapConfig.MaxClip;
}
}
else
{
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Blue, "No Ammo Left");
}
}
FHitResult AWeapon::WeaponTrace(const FVector &TraceFrom, const FVector &TraceTo) const
{
static FName WeaponFireTag = FName(TEXT("WeaponTrace"));
FCollisionQueryParams TraceParams(WeaponFireTag, true, Instigator);
TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = true;
TraceParams.AddIgnoredActor(this);
FHitResult Hit(ForceInit);
GetWorld()->LineTraceSingle(Hit, TraceFrom, TraceTo, TRACE_WEAPON, TraceParams);
return Hit;
}
void AWeapon::ProcessInstantHit(const FHitResult &Impact, const FVector &Origin, const FVector &ShootDir, int32 RandomSeed, float ReticleSpread)
{
const FVector EndTrace = Origin + ShootDir * WeapConfig.WeaponRange;
const FVector EndPoint = Impact.GetActor() ? Impact.ImpactPoint : EndTrace;
DrawDebugLine(this->GetWorld(), Origin, Impact.TraceEnd, FColor::Black, true, 10000, 10.f);
}
void AWeapon::Instant_Fire()
{
// Get the camera transform
FVector CameraLoc;
FRotator CameraRot;
GetActorEyesViewPoint(CameraLoc, CameraRot);
const int32 RandomSeed = FMath::Rand();
FRandomStream WeaponRandomStream(RandomSeed);
const float CurrentSpread = WeapConfig.WeaponSpread;
const float SpreadCone = FMath::DegreesToRadians(WeapConfig.WeaponSpread * 0.5);
const FVector AimDir = Mesh->GetSocketRotation("MF").Vector();
const FVector StartTrace = Mesh->GetSocketLocation("MF");
const FVector ShootDir = WeaponRandomStream.VRandCone(AimDir, SpreadCone, SpreadCone);
const FVector EndTrace = StartTrace + ShootDir * WeapConfig.WeaponRange;
const FHitResult Impact = WeaponTrace(StartTrace, EndTrace);
ProcessInstantHit(Impact, StartTrace, ShootDir, RandomSeed, CurrentSpread);
}
void AWeapon::ProjectileFire()
{
}
void AWeapon::MeleeFire()
{
}
In the constructor of the Pistol, I just make the WeapConfig.Priority = 1.and the knife is 0.
Another thing to keep in mind, On the level, there are a few pistols ready to pick up with preset currentclip and currentammo as well AND I’m using 4.6(but that portion shouldnt really matter except the different pointer stuff)