Hi there,
I’m basing my script off of the default FirstPersonCharacter that appears when you start a new FPS project. I have a class called MasterWeapon that has a SkeletalMeshComponent “MasterWeapon_Skel”, a StaticMeshComponent “MasterWeapon_Static”, and a bunch of int/float values for firerate, spread, etc. These are all editable in the editor per object. In FPSDemoCharacter, I have declared an array of MasterWeapon*s called inventory. Whenever the Character hits an Actor of class MasterWeapon, it adds that object to the array w/ its values. That works fine. The trouble I have is when I call the EquipWeapon(inventory[slotnumberhere]) function, it crashes the editor immediately. Does anyone know why this is? I want to set FP_Gun to equal the MasterWeapon_Skel of the MasterWeapon stored in that particular slot. Code below:
MasterWeapon.h
#pragma once
#include "GameFramework/Actor.h"
#include "MasterWeapon.generated.h"
UCLASS()
class FPSDEMO_API AMasterWeapon : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMasterWeapon();
//NOT IN USE
//AMasterWeapon(AMasterWeapon* myWeapon);
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
//virtual void Tick( float DeltaSeconds ) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Name)
FName WeaponName;
//UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Mesh)
//class USkeletalMesh* Gun_SkelMesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Mesh)
class UStaticMeshComponent* Gun_StaticMeshComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Mesh)
class USkeletalMeshComponent* Gun_SkelMeshComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
int spread;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
float weaponFireRate;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
int damage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
int currentAmmo;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
int maxAmmo;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
bool isAutoWeapon;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Sound)
class USoundBase* FireSound;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Animation)
class UAnimMontage* FireAnimation;
};
MasterWeapon.cpp
#include “FPSDemo.h”
#include “MasterWeapon.h”
// Sets default values
AMasterWeapon::AMasterWeapon()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
//FIGURE OUT HOW TO CREATE MESH AND CHOOSE IN EDITOR
Gun_SkelMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MasterWeapon_Skel"));
Gun_SkelMeshComp->SetVisibility(false);
Gun_StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MasterWeapon_Static"));
Gun_StaticMeshComp->BodyInstance.SetCollisionProfileName("MasterWeapon_Collision");
}
//NOT IN USE
/*AMasterWeapon::AMasterWeapon(AMasterWeapon* myWeapon)
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
//FIGURE OUT HOW TO CREATE MESH AND CHOOSE IN EDITOR
Gun_SkelMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MasterWeapon_Skel"));
Gun_SkelMeshComp->SetVisibility(false);
Gun_StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MasterWeapon_Static"));
Gun_StaticMeshComp->BodyInstance.SetCollisionProfileName("MasterWeapon_Collision");
int mySpread = myWeapon->spread;
float myWeaponFireRate = myWeapon->weaponFireRate;
int myDamage = myWeapon->damage;
int myCurrentAmmo = myWeapon->currentAmmo;
int myMaxAmmo = myWeapon->maxAmmo;
bool myIsAutoWeapon = myWeapon->isAutoWeapon;
}
*/
// Called when the game starts or when spawned
void AMasterWeapon::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
/*void AMasterWeapon::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
}*/
FPSDemoCharacter.h
#pragma once
#include "GameFramework/Character.h"
#include "FPSDemoCharacter.generated.h"
class UInputComponent;
UCLASS(config=Game)
class AFPSDemoCharacter : public ACharacter
{
GENERATED_BODY()
UPROPERTY(VisibleDefaultsOnly, Category = Collision)
class UCapsuleComponent* CharCollisionComp;
/** Pawn mesh: 1st person view (arms; seen only by self) */
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
class USkeletalMeshComponent* Mesh1P;
/** Gun mesh: 1st person view (seen only by self) */
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
class USkeletalMeshComponent* FP_Gun;
/** Location on gun mesh where projectiles should spawn. */
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
class USceneComponent* FP_MuzzleLocation;
/** First person camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FirstPersonCameraComponent;
UFUNCTION()
void OnHit(UPrimitiveComponent* MyComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
void EquipWeapon(int weaponNum);
virtual void Tick(float DeltaSeconds) override;
//This ■■■■ is REALLY ■■■■■■■ ANNOYING EPIC!
void Switch1();
void Switch2();
void Switch3();
void Switch4();
void Switch5();
void Switch6();
void Switch7();
void Switch8();
//============================================
public:
AFPSDemoCharacter();
virtual void BeginPlay();
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Gameplay)
int occupiedSlots = 1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
int maxSlots;
/** 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 AFPSDemoProjectile> ProjectileClass;
/** Sound to play each time we fire */
//Moving to Weapon class
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
class USoundBase* FireSound;
/** AnimMontage to play each time we fire */
//Moving to Weapon class
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
class UAnimMontage* FireAnimation;
//Moving to Weapon class
UPROPERTY(EditAnywhere, Category = Gameplay)
float fireRate = 0.125f;
UFUNCTION()
void PickupWeapon(class AMasterWeapon* pWeapon);
UFUNCTION(Category = Gameplay)
void PlayerTakeDamage(float damageTaken);
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<AMasterWeapon*> inventory;
protected:
//Switches between full auto and semi-auto.
void ToggleAuto();
void RespawnPlayer();
void ToggleHUD();
//Initial function called when fire button is pressed, checks whether or not auto-fire is enabled and calls the appropriate function.
void DecideFire();
//Fires full auto assuming firing mode is set to auto and fire button is held.
void AutoFire();
//Ceases firing upon release.
void StopFire();
/** Fires a projectile. */
void OnFire();
void KillPlayer();
/** 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);
struct TouchData
{
TouchData() { bIsPressed = false; Location = FVector::ZeroVector; }
bool bIsPressed;
ETouchIndex::Type FingerIndex;
FVector Location;
bool bMoved;
};
void BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location);
void EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location);
void TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location);
TouchData TouchItem;
protected:
// APawn interface
virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;
// End of APawn interface
/*
* Configures input for touchscreen devices if there is a valid touch interface for doing so
*
* @param InputComponent The input component pointer to bind controls to
* @returns true if touch controls were enabled.
*/
bool EnableTouchscreenMovement(UInputComponent* InputComponent);
public:
/** Returns Mesh1P subobject **/
FORCEINLINE class USkeletalMeshComponent* GetMesh1P() const { return Mesh1P; }
/** Returns FirstPersonCameraComponent subobject **/
FORCEINLINE class UCameraComponent* GetFirstPersonCameraComponent() const { return FirstPersonCameraComponent; }
};
FPSDemoCharacter.cpp
#include "FPSDemo.h"
#include "FPSDemoCharacter.h"
#include "FPSDemoProjectile.h"
#include "FPSDemoHUD.h"
#include "FPSDemoGameMode.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"
#include "Engine.h"
#include "MasterWeapon.h"
DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);
//////////////////////////////////////////////////////////////////////////
// AFPSDemoCharacter
bool isAuto = false;
bool firing = false;
float delay = 0.0f;
float ticker = 1.0f;
bool isDead = false;
bool HUDOn = true;
bool isWeaponAuto = false;
int playerHealth = 100;
float respawnTimer = 3.0f;
AFPSDemoCharacter::AFPSDemoCharacter()
{
inventory.SetNum(maxSlots);
PrimaryActorTick.bCanEverTick = true;
//CharCollisionComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleComp"));
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);
GetCapsuleComponent()->OnComponentHit.AddDynamic(this, &AFPSDemoCharacter::OnHit);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->SetupAttachment(GetCapsuleComponent());
FirstPersonCameraComponent->RelativeLocation = FVector(-39.56f, 1.75f, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;
// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOwnerNoSee(false);
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->SetupAttachment(FirstPersonCameraComponent);
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
Mesh1P->RelativeRotation = FRotator(1.9f, -19.19f, 5.2f);
Mesh1P->RelativeLocation = FVector(-0.5f, -4.4f, -155.7f);
// Create a gun mesh component
//CALL VIA EquipWeapon LATER
FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOwnerNoSee(false);
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
//
FP_Gun->SetVisibility(false);
// FP_Gun->SetupAttachment(Mesh1P, TEXT("GripPoint"));
FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
FP_MuzzleLocation->SetupAttachment(FP_Gun);
FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));
// Default offset from the character location for projectiles to spawn
GunOffset = FVector(100.0f, 30.0f, 10.0f);
if (HUDOn == false)
ToggleHUD();
// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}
void AFPSDemoCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
FP_Gun->AttachToComponent(Mesh1P, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("GripPoint")); //Attach gun mesh component to Skeleton, doing it here because the skelton is not yet created in the constructor
}
//////////////////////////////////////////////////////////////////////////
// Input
void AFPSDemoCharacter::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("Toggle", IE_Pressed, this, &AFPSDemoCharacter::ToggleAuto);
//THIS IS SUCH ■■■■■■■■
InputComponent->BindAction("Switch1", IE_Pressed, this, &AFPSDemoCharacter::Switch1);
InputComponent->BindAction("Switch2", IE_Pressed, this, &AFPSDemoCharacter::Switch2);
InputComponent->BindAction("Switch3", IE_Pressed, this, &AFPSDemoCharacter::Switch3);
InputComponent->BindAction("Switch4", IE_Pressed, this, &AFPSDemoCharacter::Switch4);
InputComponent->BindAction("Switch5", IE_Pressed, this, &AFPSDemoCharacter::Switch5);
InputComponent->BindAction("Switch6", IE_Pressed, this, &AFPSDemoCharacter::Switch6);
InputComponent->BindAction("Switch7", IE_Pressed, this, &AFPSDemoCharacter::Switch7);
InputComponent->BindAction("Switch8", IE_Pressed, this, &AFPSDemoCharacter::Switch8);
//=========================================================================================
//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AFPSDemoCharacter::TouchStarted);
if (EnableTouchscreenMovement(InputComponent) == false)
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AFPSDemoCharacter::DecideFire);
InputComponent->BindAction("Fire", IE_Released, this, &AFPSDemoCharacter::StopFire);
}
InputComponent->BindAxis("MoveForward", this, &AFPSDemoCharacter::MoveForward);
InputComponent->BindAxis("MoveRight", this, &AFPSDemoCharacter::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, &AFPSDemoCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AFPSDemoCharacter::LookUpAtRate);
}
//VVV EDITOR CRASHES HERE VVV
void AFPSDemoCharacter::EquipWeapon(int weaponNum)
{
if (inventory[weaponNum] != NULL)
{
//inventory[weaponNum];
UE_LOG(LogTemp, Error, TEXT("BREAK?"));
// Create a gun mesh component
//FP_Gun->SetSkeletalMesh(inventory[weaponNum].Gun_SkelMesh);
AMasterWeapon* activeWeapon = inventory[weaponNum];
FP_Gun = activeWeapon->Gun_SkelMeshComp;
FP_Gun->SetOwnerNoSee(false);
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->SetVisibility(true);
// FP_Gun->SetupAttachment(Mesh1P, TEXT("GripPoint"));
FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
FP_MuzzleLocation->SetupAttachment(FP_Gun);
FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));
FP_Gun->AttachToComponent(Mesh1P, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("GripPoint"));
UE_LOG(LogTemp, Warning, TEXT("Weapon %i equipped."), weaponNum);
}
}
void AFPSDemoCharacter::Switch1()
{
EquipWeapon(1);
}
void AFPSDemoCharacter::Switch2()
{
EquipWeapon(2);
}
void AFPSDemoCharacter::Switch3()
{
EquipWeapon(3);
}
void AFPSDemoCharacter::Switch4()
{
EquipWeapon(4);
}
void AFPSDemoCharacter::Switch5()
{
EquipWeapon(5);
}
void AFPSDemoCharacter::Switch6()
{
EquipWeapon(6);
}
void AFPSDemoCharacter::Switch7()
{
EquipWeapon(7);
}
void AFPSDemoCharacter::Switch8()
{
EquipWeapon(8);
}
void AFPSDemoCharacter::OnHit(UPrimitiveComponent* MyComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
AMasterWeapon* MyWeapon = Cast<AMasterWeapon>(OtherActor);
UE_LOG(LogTemp, Warning, TEXT("Hit"));
// Only add impulse and destroy projectile if we hit a physics
if ((OtherActor != NULL) && (OtherActor != this) && (OtherComp != NULL) && (!MyWeapon) && OtherComp->IsSimulatingPhysics())
{
OtherComp->AddImpulseAtLocation(GetVelocity() * 0.0f, GetActorLocation());
}
if (MyWeapon)
{
UE_LOG(LogTemp, Warning, TEXT("WeaponHit"));
PickupWeapon(MyWeapon);
}
}
void AFPSDemoCharacter::PickupWeapon(class AMasterWeapon* pWeapon)
{
if (occupiedSlots < maxSlots)
{
UE_LOG(LogTemp, Warning, TEXT("Weapon picked up."));
inventory.Add(pWeapon);
occupiedSlots++;
}
}