My solution…
We choose a blueprint class string found by the menu on the COntent Browser item, Copy Reference.
The copied string looks like…
Blueprint’/Game/Blueprints/Loot/Weapons/Ranged/Handguns/weapon_JmacPistol.weapon_JmacPistol’
Then we have C++ code to take that string and spawn…
AActor * UtdlBlueprintHelpers::SpawnActorFromReferenceString(AActor * sourceActor, FTransform trans, FString blueprintRefString, FName nameToAssign, bool CalculateLevelSubBlock)
{
//UE_LOG(TDLLog, Log, TEXT("SpawnActorFromReferenceString"));
if (sourceActor == NULL)
{
UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString MUST have an Actor."));
return NULL;
}
if (blueprintRefString.IsEmpty())
{
UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString MUST have an Blueprint Ref String."));
return NULL;
}
//UE_LOG(TDLLog, Log, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString %s"), *blueprintRefString);
AActor * a = NULL;
FStringAssetReference itemRef = blueprintRefString;
if (itemRef.TryLoad() != NULL)
{
UObject* itemObj = itemRef.ResolveObject();
if (itemObj == NULL)
{
UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString invalid blueprint reference (resolve falied): %s"), *itemRef.AssetLongPathname);
return NULL;
}
UBlueprint* gen = Cast<UBlueprint>(itemObj);
if (gen == NULL)
{
UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString invalid blueprint reference (cast failed): %s"), *itemRef.AssetLongPathname);
return NULL;
}
FActorSpawnParameters SpawnInfo;
SpawnInfo.bNoFail = true;
SpawnInfo.bNoCollisionFail = true;
SpawnInfo.bRemoteOwned = false;
if (CalculateLevelSubBlock)
{
ULevel * foundLvl = CalcSubBlockFromWorldPosition(sourceActor, trans.GetLocation());
if (foundLvl == NULL)
{
UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString Error. No matching block/"));
return NULL;
}
SpawnInfo.OverrideLevel = foundLvl;
}
else
{
SpawnInfo.OverrideLevel = GetActorLevel(sourceActor);
}
if (nameToAssign.IsValid() && nameToAssign.ToString().Len() > 0)
{
SpawnInfo.Name = nameToAssign;
}
a = sourceActor->GetWorld()->SpawnActor<AActor>(gen->GeneratedClass, SpawnInfo);
if (a == NULL)
{
UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString invalid blueprint reference (spawn failed): %s"), *itemRef.AssetLongPathname);
return NULL;
}
// Is it an NPC ?
if (a->IsA(APawn::StaticClass()))
{
APawn * pwn = (APawn *)a;
if (pwn->Controller == NULL)
{ // NOTE: SpawnDefaultController ALSO calls Possess() to possess the pawn (if a controller is successfully spawned).
pwn->SpawnDefaultController();
}
}
a->SetActorTransform(trans);
}
return a;
}
But in a tiled world you need the CalcSubBlockFromWorldPosition which is…
** WARNING - This assumes you name your tiles like MyTile_x12_y3 **
If you don’t use tiles then the above code is a little simpler.
ULevel * UtdlBlueprintHelpers::CalcSubBlockFromWorldPosition(AActor * sourceActor, FVector wp)
{
//UE_LOG(TDLLog, Log, TEXT("CalcSubBlockFromWorldPosition"));
if (sourceActor == NULL)
{
UE_LOG(TDLLog, Error, TEXT("UtdlBlueprintHelpers::CalcSubBlockFromWorldPosition sourceActor is NULL."));
}
UWorld * w = sourceActor->GetWorld();
if (w == NULL)
{
UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::CalcSubBlockFromWorldPosition sourceActor has no world?"));
return NULL;
}
TArray<ULevel *> a = w->GetLevels();
for (int i = 0; i < a.Num(); i++)
{
ULevel * v = a[i];
int x = GetLevelTileX(v);
if (x != -666)
{
int y = GetLevelTileY(v);
if (y != -666)
{
//UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::CalcSubBlockFromWorldPosition level is: %s : %s world: %s [%d,%d]"),
// *(v->GetPathName()), *(v->GetName()), *(w->GetName()), x, y);
// WARNING - Tiles are One-Based, not Zero-Based. And Tile 1,1 lower corner is at -8k, -8k
// ANd tiles are 2014 x 20148.
float lowX = (x - 1) * 204800.0f - 819200.0f;
float hiX = x * 204800.0f - 819200.0f;
float lowY = (y - 1) * 204800.0f - 819200.0f;
float hiY = y * 204800.0f - 819200.0f;
if (wp.X >= lowX && wp.X < hiX &&
wp.Y >= lowY && wp.Y < hiY)
{
return v;
}
}
}
}
//UE_LOG(TDLLog, Warning, TEXT("UtdlBlueprintHelpers::CalcSubBlockFromWorldPosition No matching sub-block? "));
return NULL;
}
And you need the X and Y calc methods…
int32 UtdlBlueprintHelpers::GetActorLevelTileX(AActor * actor)
{
//UE_LOG(TDLLog, Log, TEXT("GetActorLevelTileX"));
if (actor == NULL)
{
return -666;
}
ULevel * lev = actor->GetLevel();
return GetLevelTileX(lev);
}
int32 UtdlBlueprintHelpers::GetActorLevelTileY(AActor * actor)
{
//UE_LOG(TDLLog, Log, TEXT("GetActorLevelTileY"));
if (actor == NULL)
{
return -666;
}
ULevel * lev = actor->GetLevel();
return GetLevelTileY(lev);
}
int32 UtdlBlueprintHelpers::GetLevelTileX(ULevel * lev)
{
//UE_LOG(TDLLog, Log, TEXT("GetLevelTileX"));
if (lev == NULL)
{
return -666;
}
UObject * world = lev->GetOuter();
if (world == NULL)
{
return -666;
}
int32 x = 0;
FString nm = world->GetName();
// 0 1
// 012345678901234567
// nm is like "PepperValley_x4_y3"
int32 ix = nm.Find(FString("_x"));
int32 iy = nm.Find(FString("_y"));
int32 xsz = iy - ix - 2;
int32 ysz = nm.Len() + 1 - iy - 2;
if (ix >= 0 && iy >= 0 && xsz > 0 && ysz > 0)
{
char * s = TCHAR_TO_ANSI(*(nm.Mid(ix + 2, xsz)));
int n = sscanf_s(s, "%d", &x);
if (n != 1)
{
return -666;
}
return x;
}
return -666;
}
int32 UtdlBlueprintHelpers::GetLevelTileY(ULevel * lev)
{
//UE_LOG(TDLLog, Log, TEXT("GetLevelTileY"));
if (lev == NULL)
{
return -666;
}
UObject * world = lev->GetOuter();
if (world == NULL)
{
return -666;
}
int32 y = 0;
FString nm = world->GetName();
// 0 1
// 012345678901234567
// nm is like "PepperValley_x4_y3"
int32 ix = nm.Find(FString("_x"));
int32 iy = nm.Find(FString("_y"));
int32 xsz = iy - ix - 2;
int32 ysz = nm.Len() + 1 - iy - 2;
if (ix >= 0 && iy >= 0 && xsz > 0 && ysz > 0)
{
char * s = TCHAR_TO_ANSI(*(nm.Mid(iy + 2, ysz)));
int n = sscanf_s(s, "%d", &y);
if (n != 1)
{
return -666;
}
return y;
}
return -666;
}