Your first component

Gameplay code lives in components (C++ classes you attach to objects in a scene). This page shows how to make one.

Create the component

  1. In the menu bar, select Scripts ▸ New C++ Component... to open the form for a new component.

    The Scripts menu, with New C++ Component... highlighted
  2. Enter Spinner as the Class name, leave the folder empty, and select Create.

    The New C++ Component form, with Spinner as the class name (1) and Create (2)

The editor writes Source/Spinner.h and Source/Spinner.cpp, opens the header in your code editor, and compiles the project. The header:

Source/Spinner.h
#pragma once

// the engine's umbrella header. It includes everything a game normally needs
// (the packages turned on in the Package Manager among them) + brings
// `using namespace Shalltear` with it. Every engine header can still be
// included on its own
#include "Shalltear.h"

// starter component: press Play and the object it is attached to rotates... impressive?
// Rename/delete it once you have components of your own.
//
// fields listed via SLT_REFLECT_FIELD appear in the Inspector, saved with
// the scene, and keep their values across hot reloads. Add one -> save the file ->
// switch back to the editor to see it
class Spinner : public Behavior {
public:
	SLT_REFLECT_COMPONENT(Spinner, Behavior)
	SLT_REFLECT_FIELD(degreesPerSecond)
	SLT_REFLECT_FIELD(target)
	SLT_END_REFLECT()

	// editing it in the Inspector during play takes effect on the next frame
	f32 degreesPerSecond = 60.0f;

	// which object to rotate.
	//
	// NOTE: store a Handle, never a SceneObject*! Objects can be destroyed,
	// the pools holding them can move. A resolved pointer is valid for the
	// current frame only! Resolve when you need it (like OnUpdate does)
	Handle<SceneObject> target;
protected:
	void OnEnable() override;
	void OnUpdate() override;
};

The source file:

Source/Spinner.cpp
#include "Spinner.h"

// registers class with the engine. Spinner appears in Add Component,
// its reflected fields appear in the Inspector once the module compiles
SLT_REGISTER_TYPE(Spinner)

void Spinner::OnEnable() {
	SLT_LOG_INFO("MyProject", "Spinner enabled");
}

void Spinner::OnUpdate() {
	// rotate the target, or this object, when nothing is assigned (or the target
	// is gone). Both resolve to nullptr
	SceneObject* object = target.Get();
	if (!object) {
		object = GetOwner().Get();
	}
	// GetDeltaTime() is seconds since the last frame, so the speed is the
	// same at any frame rate. It is scaled game time -> slows -> stops with
	// the game (GetUnscaledDeltaTime() is real seconds)
	const Quat step = Quat::AngleAxis(degreesPerSecond * Time::GetDeltaTime(), Vec3::Up());
	// normalized because a rotation accumulated frame->frame drifts off unit
	// length over time
	object->SetLocalRotation((object->GetLocalRotation() * step).Normalized());
}

The ExampleComponent your project started with is the same code under another name. Delete its two files whenever you like.

Put it on an object

  1. In the Hierarchy, select the + button, or right-click an empty area, and choose 3D Object ▸ Cube. The editor creates the cube and selects it.

    The Hierarchy's + button (1) and its 3D Object menu, with Cube highlighted (2)
  2. In the Inspector, select Add Component. Your own components come first, under your project's name. Select Spinner. If the list says the project is still compiling, wait for the compile to finish.

    The Add Component list, with Spinner under the project's name
  3. Press Ctrl+P or the Play button in the title bar. The cube spins.

    The editor playing: the Runtime View shows the turning cube, and the Play button has become Stop
  4. While the game plays, change degreesPerSecond in the Inspector. The cube changes speed on the next frame.

    The Spinner's degreesPerSecond field set to 180 during play
  5. Press Ctrl+P again to stop. Whatever you changed during play is undone when play stops.

Save the scene with Ctrl+S.

Change the code while the editor runs

  1. In Spinner.h, add SLT_REFLECT_FIELD(label) below the other two SLT_REFLECT_FIELD lines, and declare the field beside the others: str label = "spinner";.
  2. Save the file.

The editor sees the change, compiles, and reloads your code on its own. Select the cube: label is in the Inspector, and degreesPerSecond still has the value you gave it.

The Spinner after the reload: the new label field, and degreesPerSecond keeping the value it had before

If you save while the game is playing, the editor compiles straight away and loads the new code when you stop. To load it without stopping, select Reload Now in the title bar.

The title bar's Reload Now button, amber while new code waits to load

When the code does not compile

The Compile button shows the number of errors, and each error is a line in the Console. Double-click it to open the file at that line in your code editor. Until the next compile succeeds, the editor keeps running the last version of your code that compiled.

The Console with a compile error highlighted, and the error count in its toolbar

Next, build the game so it runs without the editor.