3
\$\begingroup\$

I'm trying to replicate a version of "Paper.io" using Unreal Engine and C++ as an exercise.

My idea is to have an instance of an Actor (that is my "Grid class" in this case), and another Actor acting like "tiles", so i made another class called "Tile". This is the constructor code of the class "Tile":

ATile::ATile()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    if (!RootComponent)
    {
        RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    }

    TileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Tile Mesh"));

    static ConstructorHelpers::FObjectFinder<UStaticMesh> PlaneMesh(TEXT("/Engine/BasicShapes/Plane.Plane"));

    if (PlaneMesh.Succeeded())
    {
        TileMesh->SetupAttachment(RootComponent);
        TileMesh->SetStaticMesh(PlaneMesh.Object);
    }
}

Each time i drag and drop this file into the scene, i can easily see the Plane Mesh in the World, so i believe this is working so far.

Into my Grid class i defined my array in this way:

TArray<ATile*> Grid;

And what i'd like to do now, is to populate this array of Tile's instances. I've been looking around for a solution for tons of time but i still can't figure out how to do it. I would do something like this:

ATile Tile = ATile();

But of course it doesn't work. And i also found this documentation: Spawning Actors

I tried to do a similar thing, but since i'm a beginner, everything exploded. I don't really get an error, UE4 just crash, but Visual Studio still compile it.

void AGrid::CreateGrid()
{
    for (int x = 0; x <= 5; x++)
    {
        for (int y = 0; y <= 5; y++)
        {
            ATile* Tile = GetWorld()->SpawnActor<ATile>(ATile::StaticClass());
            Grid.Add(Tile);
        }
    }
}

How could i make this work? Am i doing something wrong?

EDIT 1: I added the suggested line of code, but it still keep crashing.

Access violation - code c0000005 (first/second chance not available)

UE4Editor_Engine!UWorld::SpawnActor() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\engine\private\levelactor.cpp:303] UE4Editor_Engine!UWorld::SpawnActor() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\engine\private\levelactor.cpp:293] UE4Editor_Paper!AGrid::CreateGrid() [c:\users\paradisee\desktop\c++\paper\source\paper\grid.cpp:30] UE4Editor_Paper!AGrid::AGrid() [c:\users\paradisee\desktop\c++\paper\source\paper\grid.cpp:20] UE4Editor_CoreUObject!UClass::CreateDefaultObject() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\coreuobject\private\uobject\class.cpp:2728] UE4Editor_CoreUObject!UObjectLoadAllCompiledInDefaultProperties() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\coreuobject\private\uobject\uobjectbase.cpp:795] UE4Editor_CoreUObject!ProcessNewlyLoadedUObjects() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\coreuobject\private\uobject\uobjectbase.cpp:869] UE4Editor_CoreUObject!TBaseStaticDelegateInstance::ExecuteIfSafe() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\core\public\delegates\delegateinstancesimpl.h:1027] UE4Editor_Core!TBaseMulticastDelegate::Broadcast() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\core\public\delegates\delegatesignatureimpl.inl:937] UE4Editor_Core!FModuleManager::LoadModuleWithFailureReason() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\core\private\modules\modulemanager.cpp:487] UE4Editor_Projects!FModuleDescriptor::LoadModulesForPhase() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\projects\private\moduledescriptor.cpp:476] UE4Editor_Projects!FProjectManager::LoadModulesForProject() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\projects\private\projectmanager.cpp:69] UE4Editor!FEngineLoop::LoadStartupModules() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\launch\private\launchengineloop.cpp:2577] UE4Editor!FEngineLoop::PreInit() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\launch\private\launchengineloop.cpp:1992] UE4Editor!GuardedMain() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\launch\private\launch.cpp:127] UE4Editor!GuardedMainWrapper() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\launch\private\windows\launchwindows.cpp:134] UE4Editor!WinMain() [d:\build++ue4+release-4.18+compile\sync\engine\source\runtime\launch\private\windows\launchwindows.cpp:210] UE4Editor!__scrt_common_main_seh() [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:253] kernel32 ntdll

\$\endgroup\$
3
\$\begingroup\$

In your original code:

ATile* Tile = GetWorld()->SpawnActor<ATile>(GetClass());

The parameter to this overload of SpawnActor is the type of the actor to spawn. You're calling GetClass() on the AGrid, but trying to cast the resulting AGrid actor to an ATile. This is legal C++, which is why it compiles; it will fail at runtime.

You want:

ATile* Tile = GetWorld()->SpawnActor<ATile>(ATile::StaticClass());

(e.g., the type of the thing you want to create, not the type of the thing creating it)

Based on your edit, it's also apparent that you're calling CreateGrid() inside the constructor for AGrid, where GetWorld() may (and does, in the case of the class default object that you're constructing -- the UClass::CreateDefaultObject call on the stack) return a null pointer.

You should probably call CreateGrid during the BeginPlay overload of the AGrid actor, or a similarly later call (such as OnConstruction), depending on your specific needs.

You could also just check the result of GetWorld(), and if it is null, skip the spawn attempt.

\$\endgroup\$
  • \$\begingroup\$ That solves the crash, but my Grid's array is still empty in the editor. Also, i wanna create my grid before the BeginPlay(). \$\endgroup\$ – user3287178 Dec 6 '17 at 20:29
  • \$\begingroup\$ Empty in the editor where? UE's object lifetime is complicated. You can consider OnConstruction or PostLoad functions if BeginPlay doesn't work for you. But you can't do anything that requires a World pointer in your constructor unless you guard against the CDO case. \$\endgroup\$ – Josh Dec 6 '17 at 20:32
  • \$\begingroup\$ I'm using an IF condition inside the constructor to check GetWorld(), and seems to work actually. if (GetWorld()) CreateGrid(); \$\endgroup\$ – user3287178 Dec 6 '17 at 20:36
  • \$\begingroup\$ Yes, that can work, if you're careful. The nature of UE's object system is that a lot of assumptions you might normally have don't always hold in constructors with respect to the state of the object graph, so you need to be careful in there. Places like PostLoad or OnConstruction are generally safer or at least easier to work with, and BeginPlay is where you'd want to do pre-runtime sort of work. \$\endgroup\$ – Josh Dec 6 '17 at 20:40
  • \$\begingroup\$ Ok, thank you so much for your help and tips. I appreciate it. \$\endgroup\$ – user3287178 Dec 6 '17 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.