Creating a Weapons System C++

Hello everyone, first post here in the forums. Been learning Unreal on and off for a few months now and trying to tackle more sophisticated systems. I’m a solo developer so reaching out for an issue I’m having hooking up my weapons system.

The basic idea is to create a reusable component to easily standup new weapons in the editor.

Therefore, I have created 3 CPP implementations

  • SWeaponData (data Struct)
  • SWeaponAsset (of type UPrimaryDataAsset)
  • SWeaponComponent (of type UActorComponent)

The Weapon Component is attached to the player actor and references the data from SWeaponAsset (which references the SWeaponData). I would imagine this is a pretty familiar design pattern - the SWeaponComponent will also be references in a Weapon Pickup component later.

The SWeaponComponent will also hold the function for equipping the weapon and have an editor field for the Default Starting Weapon. This is where things are falling apart.

I can create my SWeaonAsset and it’s default properties come over - and holds all the basic weapon properties as well as it’s visual elements (meshs, anims etc). Seen below:

However, when I add the Weapon Component to my BP_Player, I can not input the Data Asset to the Weapon Component. I have messed with editor settings (for example, I allowed the field to accept any BPs derving from DataAsset (thus including UPrimaryDataAsset) and can see the BP and assign there - but I want to be sure that only the UPrimaryDataAsset BPs are listed. Anywho, here’s what my BP_Player with Weapon Component attach looks like:

As you can see these Data Assets exist but are not being populated.

I’m coding all this up on the C++ side but to reduce length here I’ll forego everything but the header file for SWeaponComponent

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SWeaponAsset.h"
#include "SWeaponComponent.generated.h"

UCLASS(Blueprintable, BlueprintType, ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))  
class SPYR_API USWeaponComponent : public UActorComponent
{
    GENERATED_BODY()

public:
    USWeaponComponent();

    // Use fully qualified class path for AllowedClasses
    UPROPERTY(EditAnywhere, Category = "Weapon", meta = (AllowedClasses = "USWeaponAsset"))
    USWeaponAsset* DefaultWeaponAsset;

    UFUNCTION(BlueprintCallable, Category = "Weapon")
    void EquipWeapon(USWeaponAsset* NewWeaponAsset);

    UFUNCTION(BlueprintCallable, Category = "Weapon")
    void UnequipWeapon();

protected:
    virtual void BeginPlay() override;
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

private:

    UPROPERTY(Transient)
    USWeaponAsset* CurrentWeaponAsset;

    UPROPERTY(Transient)
    USkeletalMeshComponent* CurrentWeaponMesh;

    UPROPERTY(Transient)
    ACharacter* OwnerCharacter;

    UPROPERTY(Transient)
    int32 CurrentAmmo;

public:
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

Finally, I have also registered these components in the Asset Manager and have pointed them to the correct folder under code.

Any ideas why this drop down isn’t functioning so I can reference the Data Assets? Am I off logically?

Thanks for any help and apologies for the long read but wanted to make sure I gave as much detail as possible! This has been driving me nuts!

can you try with

TSubclassOf<USWeaponAsset> 

should work or

TSoftObjectPtr<USWeaponAsset>

?

Holy crap, that works, or at least gets me in the direction I want to go! Thank you!

    UPROPERTY(EditAnywhere, Category = "Weapon")
    TSubclassOf<USWeaponAsset> DefaultWeapon;

Is the final property I used in case anyone needs this thread in the future.

@RedGrimnir is there any context as to why that works versus a direct pointer (I believe thats the right terminology haha, still learning :D) ?

1 Like

Well one is direct reference to an object instance, however subclass is a reference to class. So it lists all available classes in project.

So let’s say you made an actor and added this actor compoent with UPROPERTY(Transient)
USWeaponAsset* CurrentWeaponAsset;

If you place this actor in a level, and click on the details panel, you can select an instance of the USWeaponAsset if exist in world (like a loot object) otherwise it won’t work cause it can not find instances. I found it confusing and still its confusing , sometimes I forget putting it as class reference. So I learned by trial by fire :slight_smile:

1 Like

This is not how you create Data Assets.

You need to Right Click in the Content Browser and select Data Asset from Miscellaneous

Then select the class of Data Asset you want. In your case SWeaponAsset

This will Create a Data Asset Object that you can select from the dropdown.

Your initial code is correct, but instead of creating child classes of the Data Asset class, you need to add it like above.

2 Likes

Your initial code is correct, but instead of creating child classes of the Data Asset class, you need to add it like above.

Think this is more correct answer to it.

However I still reference them as soft object (just checking from my inventory system)

	UPROPERTY(EditAnywhere, Blueprintable, BlueprintReadWrite, Replicated, Category="MGInventory Component - Variables")
	TArray<TSoftObjectPtr<UMGInventoryItemAsset>> InventoryData;
2 Likes

For what is worth your suggestion got me there. But yes, I was creating the Data Assets wrong :slight_smile:

Ahah! That makes so much more sense now, thanks for clearing that up. I was definitely thinking the direct reference was the ref to the class itself. That’s a pretty easy tripwire haha.

1 Like

Ah, this is perfect and exactly what the issue is. Seems like everything is in order now and is working as I expected. Thank you so much again!

Just going off the conversation with Grimnir - would it be better to have the DefaultWeaponAsset as a soft reference or a direct reference? I implemented this with a direct ref but just curious if one is better than the other in this particular setup.

1 Like