I’m working on an inventory system that uses a Data Table populated by Data Assets in order to handle item management. The ItemData asset can hold a reference to a weapon component that will be given to the player, however currently the engine will crash if I ever populate this field in the data asset, returning the following error:
Typed element was requested for '/Game/FirstPerson/Data/ItemDatas/ItemData_Generic.ItemData_Generic:BP_WeaponRifle_C_1' before the registry was available! This usually means that NewObject was used instead of CreateDefaultSubobject during CDO construction.
The weapon object I’m using is a blueprint created from the weapon component class and assigned to the Item Data in the editor, so I’m struggling to take much from the error message since I don’t create the object in code at all. Below are some screenshots of the relevant classes.
WeaponData is another DataAsset that I set in editor, do I need to also initialise it in the CPP somewhere?
The Weapon Component CPP is mostly the default one from the FPS template:
UTP_WeaponComponent::UTP_WeaponComponent()
{
// Default offset from the character location for projectiles to spawn
MuzzleOffset = FVector(100.0f, 0.0f, 10.0f);
}
void UTP_WeaponComponent::Reload()
{
if (WeaponData != nullptr)
{
WeaponData->OnReload();
}
}
void UTP_WeaponComponent::Fire()
{
if (Character == nullptr || Character->GetController() == nullptr || WeaponData == nullptr)
{
return;
}
if (WeaponData->GetCurrentAmmo() == 0)
{
Reload();
return;
}
float time = UGameplayStatics::GetRealTimeSeconds(GetWorld());
if (time - WeaponData->GetLastFireTime() > WeaponData->GetFireRate())
{
// Try and fire a projectile
if (WeaponData->UseProjectile && WeaponData->ProjectileClass != nullptr)
{
UWorld* const World = GetWorld();
if (World != nullptr)
{
APlayerController* PlayerController = Cast<APlayerController>(Character->GetController());
const FRotator SpawnRotation = PlayerController->PlayerCameraManager->GetCameraRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
const FVector SpawnLocation = GetOwner()->GetActorLocation() + SpawnRotation.RotateVector(MuzzleOffset);
//Set Spawn Collision Handling Override
FActorSpawnParameters ActorSpawnParams;
ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
// Spawn the projectile at the muzzle
World->SpawnActor<AFPS_ProjectProjectile>(WeaponData->ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams);
}
}
else
{
//TODO: implement hitscan here
}
WeaponData->AdjustAmmo(-1);
WeaponData->SetLastFireTime(time);
// Try and play the sound if specified
if (WeaponData->FireSound != nullptr)
{
//TODO: add back in later - just too annoying to work with now
//UGameplayStatics::PlaySoundAtLocation(this, FireSound, Character->GetActorLocation());
}
// Try and play a firing animation if specified
if (WeaponData->FireAnimation != nullptr)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Character->GetMesh1P()->GetAnimInstance();
if (AnimInstance != nullptr)
{
AnimInstance->Montage_Play(WeaponData->FireAnimation, 1.f);
}
}
}
}
int UTP_WeaponComponent::GetMaxAmmo()
{
return WeaponData->GetMaxAmmo();
}
int UTP_WeaponComponent::GetCurrentAmmo()
{
return WeaponData->GetCurrentAmmo();
}
void UTP_WeaponComponent::AttachWeapon(AFPS_ProjectCharacter* TargetCharacter)
{
Character = TargetCharacter;
// Check that the character is valid, and has no rifle yet
if (Character == nullptr || Character->GetHasRifle())
{
return;
}
// Attach the weapon to the First Person Character
FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, true);
AttachToComponent(Character->GetMesh1P(), AttachmentRules, FName(TEXT("GripPoint")));
// switch bHasRifle so the animation blueprint can switch to another animation set
Character->SetHasRifle(true);
// Set up action bindings
if (APlayerController* PlayerController = Cast<APlayerController>(Character->GetController()))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
// Set the priority of the mapping to 1, so that it overrides the Jump action with the Fire action when using touch input
Subsystem->AddMappingContext(FireMappingContext, 1);
}
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerController->InputComponent))
{
// Fire
EnhancedInputComponent->BindAction(FireAction, ETriggerEvent::Triggered, this, &UTP_WeaponComponent::Fire);
EnhancedInputComponent->BindAction(ReloadAction, ETriggerEvent::Triggered, this, &UTP_WeaponComponent::Reload);
}
}
}
void UTP_WeaponComponent::BeginPlay()
{
if (WeaponData)
{
WeaponData->Init();
}
}
void UTP_WeaponComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (Character == nullptr)
{
return;
}
if (APlayerController* PlayerController = Cast<APlayerController>(Character->GetController()))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->RemoveMappingContext(FireMappingContext);
}
}
}
My character doesn’t start with the weapon component as a part of them, it’s added during runtime from the inventory. The crash only happens as a result of the weapon component object being set in the ItemData DataAsset in the editor.