RootComponent is ignored, sort of, in 4.7?

Hi,

I decided to upgrade to 4.7 now, before I get too far along with my project in 4.6. I created a “Basic Code” project and started bringing over code from my old 4.5 and 4.6 projects. I immediately ran into a strange problem.

I added a new C++ class based on “Pawn.” I ported over the old code while making the changes necessary for 4.7. Everything built and ran without error, but I didn’t see my pawn’s mesh. I could see the camera and the camera boom components in the editor, but not the actual mesh component of the pawn. It didn’t show up when I hit “Play,” either. So after hours (seriously, hours) of poking around in the editor trying to figure out why it was invisible, I found that if I:

  1. Select the pawn in the World Outliner (I can’t click on it in the editor because it’s invisible)
  2. Open the details
  3. Select the mesh component in the little tree view, which is apparently somehow generated based on the code (new in 4.7?)
  4. Go to the “Static Mesh” box
  5. Select my mesh in the drop-down list

Finally, the mesh appears as it should. This is a problem for several reasons:

  1. I shouldn’t have to do this because I already load a mesh in the code (in the constructor of my pawn class)
  2. I don’t want to have to do this for every mesh in the game – there are hundreds
  3. I can apparently select any mesh I want in the drop-down and it overrides what I’ve done in the code (so why did I bother to do anything in the code?)

Is this the intended behavior in 4.7? Is there any way to not have to do this for every actor that I create in code?

It’s not intended. If you post your class declaration and constructor implementation, maybe we can find the issue.

Thank you, kamrann.

PlayerPawn.h:


#pragma once

#include "GameFramework/Pawn.h"
#include "PlayerPawn.generated.h"


UCLASS()
class UE47_TEST_PROJECT_API APlayerPawn : public APawn
{
    GENERATED_BODY()

public:

    APlayerPawn(const FObjectInitializer&);

    // Called when the game starts or when spawned
    virtual void BeginPlay();
    
    // Called every frame
    virtual void Tick(float);

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent*);

protected:

    // I've experimented with both having and not having the following line, seems to make no difference:
    // UPROPERTY(Category = Mesh, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    class UStaticMeshComponent* ship_body_mesh_component;

    class UCameraComponent* camera_component;
    class USpringArmComponent* camera_boom_component;
};

PlayerPawn constructor:


APlayerPawn::APlayerPawn(const FObjectInitializer& init) :
    Super(init)
{
    // Get mesh and set RootComponent
    static ConstructorHelpers::FObjectFinder<UStaticMesh> ship_body_mesh(TEXT("/Game/Meshes/ship_body.ship_body"));
    ship_body_mesh_component = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ship_body_mesh_component"));
    RootComponent = ship_body_mesh_component;

    // Create camera boom
    camera_boom_component = CreateDefaultSubobject<USpringArmComponent>(TEXT("camera_boom_component"));
    camera_boom_component->AttachTo(ship_body_mesh_component);
    camera_boom_component->bAbsoluteRotation = true;
    camera_boom_component->TargetArmLength = 600.0f;
    camera_boom_component->RelativeRotation = FRotator(-90.0f, 0.0f, 0.0f);
    camera_boom_component->bDoCollisionTest = false;

    // Create camera
    camera_component = CreateDefaultSubobject<UCameraComponent>(TEXT("camera_component"));
    camera_component->AttachTo(camera_boom_component, USpringArmComponent::SocketName);
    camera_component->bUsePawnControlRotation = false;
}


I should probably also mention that I tried specifying the ship_body_mesh with the long syntax (“StaticMesh’/Game/Meshes/ship_body.ship_body’”), as well, and the behavior was the same.

Okay, so there are two different things here which it’s important to distinguish.

UStaticMeshComponent, a type of actor component which you have created in your constructor.
UStaticMesh, an actual mesh asset.

UStaticMeshComponent has a property of type UStaticMesh*, called StaticMesh. When you create the component, the StaticMesh property will be null. So after calling CreateDefaultSubobject, you need to assign the static mesh asset you loaded to the property.


ship_body_mesh_component->StaticMesh = ship_body_mesh.Object;

Edit: In hindsight I think you probably knew this but just forgot the assignment.

That said, I don’t think it’s generally good practice to hard code asset names into your C++ code. Much easier to just create a blueprint class based on your C++ class, edit your blueprint defaults and set the mesh property of your component to the asset you want. Then you can just drop/spawn the blueprint into your level as many times as you like. This way, any time you want to switch the mesh or relocate it, you can do so in the editor without needing to recompile.

Thanks a ton. I did know (at some point) that setting the “StaticMesh” property was required, because it’s right there in all the example code. No idea how that line disappeared in the port.