home

Tutorial index

Unreal Engine 4: Tutorial 2 - Adding a Mesh

Since our class doesn't have any particular behavior, and frankly we don't really have much on which it could behave, we have no means of perceiving its existence in our world. It is thus reasonable that the first thing we will do is to add a mesh to it. Meshes, as well as anything else, including particle effects, skeletal meshes and animations, etc, are components of the object. Components are generic things that attach to objects to give them certain attributes, while being reusable so they don't have to be reimplemented every time we want to add them to a class. These components can be declared in the header, in which case we always have a reference to them and they are availlable in the code, or they can be created through the code, which means they can be created on-demand during gameplay, and are not predefined or even limited, as you can add any amount necessary. This however requires that you keep track of them if you want to reference them in the future, otherwise they only interract through the engine. So if you want to add some particle effects of smoke and fire where the object is hit, you will have to keep track of them if you want to extinguish them (disable or remove the corresponding component) later on.

If a component is definitely gonna be there, and especially if there's no chance of adding more of it later on, it's probably more versatile to just declare its existence in the header. Essentially you only move the declaration to the header, so that is a change you can do later on if you don't want to. For now we will only work with the .cpp file, so go to your constructor and add some code:

USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
RootComponent = SphereComponent;
SphereComponent->InitSphereRadius(40.0f);
SphereComponent->SetCollisionProfileName("Pawn");

UStaticMeshComponent* SphereMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
SphereMesh->AttachTo(RootComponent);
static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMeshAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));

if(SphereMeshAsset.Succeeded())
{
SphereMesh->SetStaticMesh(SphereMeshAsset.Object);
SphereMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
SphereMesh->SetWorldScale3D(FVector(0.8f)) ;
}

Now to take a look at the code:

USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));. From the beggining to the end: We declare a sphere component, that is exactly what it says it is, a point and a radius, a sphere, not a mesh. As we are using objects it is a pointer to an object of this type. We give it an object with CreateDefaultSubobject and giving it the right type. The <> symbols pass a type to a template, the function then is duplicated on compilation to accomodate that type as well, thus that version of the funcion is availlable on runtime. The parameter passed is the name of this object in the editor, it does not affect whatsoever further coding. Thus, this line declares a spherical existence with no further information as to its properties.

RootComponent = SphereComponent; The RootComponent mentioned here, is the root component of the object, and only now do we say that our sphere is that component. It is entirely unrelated to the text we passed to the constructor just before, this component exists in all classes with substance. The RootComponent is the component which holds the actual position of your object, and all other components are defined relative to that. This helps us by keeping track of only one location, and computing the others on the fly, the cost of the computation is cheap compared to the cost of updating all positions, keeping track of them, and the programmatical effort to actually get them right every time we add or modify something. The cost of this practice is effectively negligible, and it quickly becomes obvious to anyone who discards it, that it is nigh impossible to work otherwise.

SphereComponent->InitSphereRadius(40.0f);. As you have probably imagined, this sets the radius of the sphere to 40. Attention: since the radius is 40, the diameter is 80! Also note, we give it a floating point value. This is more for convention over here, as a conversion is no problem for the engine, but the final result will definitely be stored in a floating point variable.

SphereComponent->SetCollisionProfileName("Pawn");. Over here we define that the sphere collides with just about everything that a pawn would collide with, i.e. it can go through anything the player and AI can go through, it is blocked by anything that blocks them. If you don't have anything in mind that could possibly not block the object in question, you can set it to "BlockAll". For a full list of collision profiles, you can view the reference.

Ok, so now you could place an instance of this class in the world, and it would not allow you to go through it. Not that it would also drop to the ground or move by any force, but it's a good start. The problem is that you unfortunately still cannot see it (don't worry, I am refering to the code as we explore it, it's in the code right up there). Let's see how we made it visible.

UStaticMeshComponent* SphereMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));. Familliar? Indeed it is just as before, you merely create a new mesh.

SphereMesh->AttachTo(RootComponent); Now this is something somewhat useful, you attach the mesh to the root component, i.e. it follows the root component anywhere it goes.

static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMeshAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere")); ConstructorHelpers are objects that are used, well, to help with constructors, but essentially they are an interface between your code and the assets. As is obvious, an FObjectFinder will find an object within the assets, we specify that the object is a static mesh (the template parameter), and we give it the text associated with that object as a parameter. Now, why the parameters come right after the name of the newly declared object you may ask, well, it's just the syntax for this one, you will not be called to implement any such classes any time soon, if ever actually.

if(SphereMeshAsset.Succeeded()). Self consciousness and self awareness are important. Unreal knows when its objects have found what they are looking for, and thus we can querry it as to that, and if they did find it, we proceed as planned.

SphereMesh->SetStaticMesh(SphereMeshAsset.Object); Pretty self-explanatory, I am sure. We set the static mesh to what the object finder managed to retrieve for us.

SphereMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f)); Remember how I said the component binds to the root object? You can set its relative position to that, or its offset if you prefer it that way. I set it all to zero, as I want my mesh and my collision to occupy the same space. If you give it an offset, some stuff will pass through the mesh (visually) without being stopped (thus not physically), and others will collide with the mesh when it doesn't look like it's there. Vectors are sets of values, corresponding to XYZ coordinates, you will probably get used to them pretty soon while in game development.

SphereMesh->SetWorldScale3D(FVector(0.8f)) ; Now, the radius of the mesh is 50, but of the collision sphere is 40, so we have to scale the mesh down. This is what we just did. We set its world scale because we want it to consistently be 80% of what it would be towards the world, but you may resort to local scale in the future if you want a component to hold its proportions relative to its parent as its parent changes.

 

Now, build, make sure no syntax errors or typos are present (otherwise it just won't build), go into the editor, navigate to the folder you created that houses your classes, and drag and drop it into your world, you should be able to see it right away! Note that you can see it even before the leve starts up because anything that lives in the editor is created by constructor before the game starts, and its BeginPlay() function is called afterwards. If you move the code to the BeginPlay() funcion, you will see the mesh appear once you start the game.

In the next tutorial we will add gravity and physics to our class.