This page shows a small player controller that reads input, shoots a physics raycast, and spawns an actor from a key press.
The engine does not currently expose a desktop input manager. Use GLFW for desktop keys and Input::XrInputHandler for OpenXR controller values.
APIs Used
Controller Actor
Pass the mirror window into the controller when the scene spawns it. The actor polls keys in tick() and edge-detects key presses so actions run once per press.
#pragma once
#include <GLFW/glfw3.h>
{
public:
SmallPlayerController(
const std::shared_ptr<Engine::Entities::SceneNode>& node,
GLFWwindow* window
);
void tick(
double deltaTimeSeconds)
override;
private:
void handleInput();
void shootRaycastAndSpawn();
void spawnCubeAt(const glm::vec3& position);
GLFWwindow* window_ = nullptr;
bool fireWasDown_ = false;
};
An Actor is similar to an Engine::Entities::Entity. An actor is an Entity with a transform.
virtual void beginPlay()
Gets executed when the actor is spawned into the world.
void tick(double deltaTime) override
Executed every frame.
A scene is the overarching structure which can spawn, contain and destroy actors or entities.
Asset::Ref< Asset::Path, Mesh > MeshAssetRef
#include "SmallPlayerController.h"
#include <glm/gtc/matrix_transform.hpp>
SmallPlayerController::SmallPlayerController(
const std::shared_ptr<Engine::Entities::SceneNode>& node,
GLFWwindow* window
) : Actor(node, owningScene), window_(window)
{
);
cubeMeshAssetPath,
"Cube"
);
allowTicking_ = true;
enableTick(true);
}
void SmallPlayerController::beginPlay()
{
Actor::beginPlay();
auto* scene = getOwningScene();
auto* assets = scene ? scene->getAssetManager() : nullptr;
if (!assets) {
return;
}
}
void SmallPlayerController::tick(double deltaTimeSeconds)
{
Actor::tick(deltaTimeSeconds);
handleInput();
}
void SmallPlayerController::handleInput()
{
if (!window_) {
return;
}
const bool fireIsDown = glfwGetKey(window_, GLFW_KEY_F) == GLFW_PRESS;
if (fireIsDown && !fireWasDown_) {
shootRaycastAndSpawn();
}
fireWasDown_ = fireIsDown;
}
void SmallPlayerController::shootRaycastAndSpawn()
{
if (!physics) {
return;
}
const glm::vec3 origin = getActorLocation();
const glm::vec3 direction = glm::normalize(
glm::vec3(getWorldTransform() * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f))
);
if (physics->raycastClosest(origin, direction, 100.0f, hit)) {
return;
}
spawnCubeAt(origin + direction * 3.0f);
}
void SmallPlayerController::spawnCubeAt(const glm::vec3& position)
{
auto* scene = getOwningScene();
auto* assets = scene ? scene->getAssetManager() : nullptr;
if (!assets) {
return;
}
if (!cubeMeshRef_.isLoaded()) {
return;
}
Engine::Ecs::LocalTransform transform;
transform.setPosition(position);
}
#define CREATE_ASSET_PATH(assetPath)
The mesh asset stores geometry data and.
static constexpr auto engineGeometry()
Path to engine geometry assets.
Core::PhysicsEngine * getPhysicsEngine() const
Getter for the pointer to the physics engine.
static EngineManager & getInstance()
gets a reference to the engine manager
EngineKern * getEngineModule()
gets the pointer to the engine object
Wrapper for entt component creation which ensures types safety and attaches the correct component wit...
Engine::Assets::MeshAssetRef keeps a stable handle to the mesh record while the model loads asynchronously. getAsset<Engine::Assets::Mesh>() returns that ref and requests the backing model when needed. Use isLoaded() on the returned ref before spawning when the actor needs the mesh immediately. StaticMeshActor still takes Asset::Path, so the ref is used for readiness and the path is passed to the actor constructor.
Scene Setup
GameModule owns the GLFW window pointer. Pass it to the scene, then pass it to the player controller when spawning the actor.
{
public:
explicit DemoScene(GLFWwindow* window) : window_(window) {}
{
Engine::Ecs::LocalTransform playerTransform;
playerTransform.setPosition({ 0.0f, 1.6f, 4.0f });
}
private:
GLFWwindow* window_ = nullptr;
};
{
return new DemoScene(getWindow());
}
virtual void loadContent()
T * spawnActor(const Ecs::Transform &spawnTransform, Args &&... args)
Spawns an actor from a specific class in somewhere into the scene.
VR Trigger Variant
For VR input, read the trigger value from Input::XrInputHandler. Use the same edge-detection pattern.
const bool triggerIsDown =
if (triggerIsDown && !triggerWasDown_) {
shootRaycastAndSpawn();
}
triggerWasDown_ = triggerIsDown;
Input::XrInputHandler * getXrInputHandler() const
Getter for the XR input handler.
Use Input::XrInputHandler::getHandPoseInfo(Input::Hand::RIGHT) when the ray should come from the controller aim pose instead of the player actor transform.
Notes
- spawnActor<T>() calls beginPlay() before returning the actor pointer.
- Set allowTicking_ = true before enableTick(true) for actors that need tick().
- Request reusable assets in beginPlay() and store an Asset::Ref for readiness checks.
- StaticMeshActor creates a CollidableMesh component for the mesh path.
- The ray only hits objects registered in Bullet's dynamics world.
- Use key edge detection for spawning. Polling GLFW_PRESS directly spawns one actor every frame while the key is held.