vkmEngine 1.0.0
A C++ game engine · vkmengine.com
Loading...
Searching...
No Matches
behavior.h
1#pragma once
2
3#include <exception>
4#include <functional>
5#include <memory>
6#include <string>
7#include <string_view>
8#include <type_traits>
9#include <utility>
10#include <vector>
11
12#include "logger.h"
13
14#include "core/reflect.h"
15#include "debug/engine_error_log.h"
16
17#include "ecs/entity.h"
18#include "ecs/scene.h"
19#include "ecs/component/core/name.h"
20#include "ecs/hierarchy_operations.h"
21#include "platform/window/window_manager.h"
22#include "system/render/render_settings.h"
23#include "core/event/event_bus.h"
24#include "net/net_session.h"
25#include "system/physics/physics_events.h"
26#include "platform/input/input_map.h"
27
28namespace Vkm::Engine {
29
30class Behavior;
31class Clock;
32class ResourceManager;
33class BehaviorSystem;
35
43 Scene* scene = nullptr;
44 ResourceManager* resources = nullptr;
45
48
49 EventBus* events = nullptr;
50 InputMap* input = nullptr;
51 NetSession* net = nullptr;
52 Clock* clock = nullptr;
53 RenderSettings* render = nullptr;
54 std::vector<EntityId>* pendingDestroy = nullptr;
55 std::string* pendingSceneLoad = nullptr;
56};
57
58namespace detail {
59
65template<typename Fn>
66struct ListenerEvent : ListenerEvent<decltype(&Fn::operator())> {};
67
68template<typename C, typename R, typename A>
69struct ListenerEvent<R (C::*)(A) const> {
70 using type = std::decay_t<A>;
71};
72
73template<typename C, typename R, typename A>
74struct ListenerEvent<R (C::*)(A)> {
75 using type = std::decay_t<A>;
76};
77
78template<typename R, typename A>
79struct ListenerEvent<R (*)(A)> {
80 using type = std::decay_t<A>;
81};
82
91template<typename EventT, typename Fn>
92struct SubscribedEvent {
93 using type = EventT;
94};
95
96template<typename Fn>
97struct SubscribedEvent<void, Fn> {
98 using type = typename ListenerEvent<Fn>::type;
99};
100
101} // namespace detail
102
111Behavior* findBehaviorNamed(Scene& scene, EntityId entity, std::string_view typeName);
112
114const Behavior* findBehaviorNamed(const Scene& scene, EntityId entity, std::string_view typeName);
115
126template<typename T>
127T* findBehavior(Scene& scene, EntityId entity) {
128 return static_cast<T*>(findBehaviorNamed(scene, entity, Reflect::Traits<T>::NAME));
129}
130
132template<typename T>
133const T* findBehavior(const Scene& scene, EntityId entity) {
134 return static_cast<const T*>(findBehaviorNamed(scene, entity, Reflect::Traits<T>::NAME));
135}
136
144class Behavior {
145 public:
146 Behavior() = default;
147 virtual ~Behavior() { clearSubscriptions(); }
148
149 Behavior(const Behavior& other) = delete;
150 Behavior& operator=(const Behavior& other) = delete;
151
152 Behavior(Behavior && other) = delete;
153 Behavior& operator=(Behavior && other) = delete;
154
155 public:
161 virtual void onStart() {}
162
171 virtual void onUpdate(float dt) {}
172
181 virtual void onRealtimeUpdate(float dt) {}
182
188 virtual void onFixedUpdate(float dt) {}
189
201 virtual void onCollisionEnter(const Collision& hit) {}
202
210 virtual void onCollisionStay(const Collision& hit) {}
211
220 virtual void onCollisionExit(const Collision& hit) {}
221
227 virtual void onTriggerEnter(EntityId other) {}
228
234 virtual void onTriggerStay(EntityId other) {}
235
241 virtual void onTriggerExit(EntityId other) {}
242
246 virtual void onDestroy() {}
247
253 virtual const char* typeName() const = 0;
254
260 virtual void visitFields(BehaviorFieldVisitor& visitor) {}
261
267 virtual std::unique_ptr<Behavior> clone() const = 0;
268
269 protected:
275 EntityId entity() const { return m_entity; }
276
287 template<typename T>
288 T* tryGet() { return m_ctx->scene->tryGet<T>(m_entity); }
289
291 template<typename T>
292 const T* tryGet() const { return m_ctx->scene->tryGet<T>(m_entity); }
293
300 template<typename T>
301 T& get() { return m_ctx->scene->get<T>(m_entity); }
302
304 template<typename T>
305 const T& get() const { return m_ctx->scene->get<T>(m_entity); }
306
313 template<typename T>
314 bool has() const { return m_ctx->scene->has<T>(m_entity); }
315
323 template<typename T>
324 auto& add(T && component) {
325 return m_ctx->scene->add(m_entity, std::forward<T>(component));
326 }
327
334 template<typename T>
335 T* findBehavior() { return findBehavior<T>(m_entity); }
336
350 template<typename T>
351 T* findBehavior(EntityId other) { return Vkm::Engine::findBehavior<T>(*m_ctx->scene, other); }
352
361 template<typename T>
362 static T* findBehavior(Scene& world, EntityId entity) {
363 return Vkm::Engine::findBehavior<T>(world, entity);
364 }
365
367 template<typename T>
368 static const T* findBehavior(const Scene& world, EntityId entity) {
369 return Vkm::Engine::findBehavior<T>(world, entity);
370 }
371
377 Scene& scene() { return *m_ctx->scene; }
378
384 ResourceManager& resources() { return *m_ctx->resources; }
385
393 EventSender events() { return EventSender(*m_ctx->events); }
394
400 RenderSettings& render() { return *m_ctx->render; }
401
409 InputMap& input() { return *m_ctx->input; }
410
416 NetSession& net() { return *m_ctx->net; }
417
426 Clock& clock() { return *m_ctx->clock; }
427 const Clock& clock() const { return *m_ctx->clock; }
428
434 WindowManager* window() { return m_ctx->window; }
435
445 bool isSimulated() const {
446 return m_ctx->net->simulates(m_entity);
447 }
448
457 bool isMine() const {
458 return m_ctx->net->isMine(m_entity);
459 }
460
469 bool isReplaying() const { return m_ctx->net->replaying(); }
470
480 const InputCommand& command() const {
481 return m_ctx->net->commandFor(m_entity);
482 }
483
493 if (m_ctx->net->role() == NetRole::Client) warnSpawnOnClient();
494 return m_ctx->scene->createEntity();
495 }
496
503 EntityId spawn(const char* name) {
504 const EntityId entity = spawn();
505 m_ctx->scene->add(entity, makeName(name));
506 return entity;
507 }
508
518 EntityId spawn(const char* name, EntityId parent) {
519 const EntityId entity = spawn(name);
520 HierarchyOperations::setParent(*m_ctx->scene, entity, parent);
521 return entity;
522 }
523
532 void destroy(EntityId target) { m_ctx->pendingDestroy->push_back(target); }
533
537 void destroy() { destroy(m_entity); }
538
548 void loadScene(const std::string& scenePath) { *m_ctx->pendingSceneLoad = scenePath; }
549
563 template<typename EventT = void, typename Fn>
564 void subscribe(Fn&& callback) {
565 using Event = typename detail::SubscribedEvent<EventT, std::decay_t<Fn>>::type;
566 std::function<void(const Event&)> listener = std::forward<Fn>(callback);
567 // Null only before binding, so this is a subscribe from a constructor.
568 if (!m_ctx) {
569 LOG_ERROR(
570 "A behavior subscribed before it started; "
571 "subscribe() belongs in onStart(), not the constructor"
572 );
573 return;
574 }
575 // Guarded, or a throw would unwind through the bus and out of the frame.
576 EventBus* events = m_ctx->events;
577 const ListenerId id = events->subscribe<Event>(
578 [this, listener = std::move(listener)](const Event& event) {
579 runGuarded("event listener", [&] { listener(event); });
580 }
581 );
582 m_subscriptions.push_back([events, id]() { events->unsubscribe<Event>(id); });
583 }
584
585 private:
586 friend class BehaviorSystem;
587
595 template<typename Fn>
596 bool runGuarded(const char* hookName, Fn&& fn) {
597 try {
598 fn();
599 return false;
600 } catch (const std::exception& e) {
601 reportError("Behavior", hookLabel(hookName), e.what());
602 } catch (...) {
603 reportError("Behavior", hookLabel(hookName), "non-std exception");
604 }
605 return true;
606 }
607
614 std::string hookLabel(const char* hookName) const {
615 return std::string(typeName()) + " / " + hookName;
616 }
617
624 void bindContext(EntityId entity, BehaviorContext& context) {
625 m_entity = entity;
626 m_ctx = &context;
627 }
628
635 void clearSubscriptions() {
636 for (auto& unsubscribe : m_subscriptions) unsubscribe();
637 m_subscriptions.clear();
638 }
639
645 static void warnSpawnOnClient();
646
647 private:
648 EntityId m_entity{};
649 BehaviorContext* m_ctx = nullptr;
650
651 std::vector<std::function<void()>> m_subscriptions;
652 bool m_started = false;
653 bool m_disabled = false;
654};
655
656} // namespace Vkm::Engine
Type-erased visitor over a behavior's reflected authoring fields.
Definition behavior_field_visitor.h:23
Drives the lifecycle of every entity's ScriptComponent behaviors.
Definition behavior_system.h:27
Base class for native C++ gameplay behaviors.
Definition behavior.h:144
const T & get() const
This entity's T, which it must have.
Definition behavior.h:305
InputMap & input()
Named input actions, for the frame queries and to define bindings.
Definition behavior.h:409
WindowManager * window()
The window, or null on a host that draws nothing, such as a dedicated server.
Definition behavior.h:434
virtual void onTriggerStay(EntityId other)
Called on each later tick other is still inside, on onCollisionStay's terms.
Definition behavior.h:234
const InputCommand & command() const
The input driving this entity on the tick now running.
Definition behavior.h:480
T * findBehavior(EntityId other)
other's behavior of type T, or null when it has none.
Definition behavior.h:351
T * findBehavior()
This entity's behavior of type T, or null when it has none.
Definition behavior.h:335
virtual void onUpdate(float dt)
Called every variable-step frame on which simulation time advanced.
Definition behavior.h:171
const T * tryGet() const
This entity's T, or null when it has none.
Definition behavior.h:292
NetSession & net()
The wire, or an offline session that answers as though local.
Definition behavior.h:416
virtual const char * typeName() const =0
Stable type name, identical to this type's BehaviorRegistry key; serialization uses it.
void destroy()
Destroy this behavior's own entity and its subtree, deferred likewise.
Definition behavior.h:537
virtual std::unique_ptr< Behavior > clone() const =0
Deep copy for entity duplication; authored fields only, the context is rebound.
Clock & clock()
Real and simulation time, and the play state behind them.
Definition behavior.h:426
bool isMine() const
Whether this entity belongs to the player at this end.
Definition behavior.h:457
static T * findBehavior(Scene &world, EntityId entity)
The free findBehavior, which the member overloads would otherwise hide.
Definition behavior.h:362
static const T * findBehavior(const Scene &world, EntityId entity)
The free findBehavior, which the member overloads would otherwise hide.
Definition behavior.h:368
virtual void onCollisionEnter(const Collision &hit)
Called on the first tick this entity touches another, in a resolved (non-trigger) contact.
Definition behavior.h:201
virtual void onDestroy()
Called on entity removal, and when the session ends (see BehaviorSystem::endSession).
Definition behavior.h:246
virtual void onFixedUpdate(float dt)
Called on each fixed-step tick; fed from simulation time, so pause and time-scale apply.
Definition behavior.h:188
bool isReplaying() const
Whether this tick already happened and is being run again.
Definition behavior.h:469
EntityId spawn(const char *name, EntityId parent)
Create an entity carrying name, parented under parent.
Definition behavior.h:518
virtual void onRealtimeUpdate(float dt)
Called every frame on real time, paused or not.
Definition behavior.h:181
virtual void onTriggerExit(EntityId other)
Called on the first tick other no longer overlaps, on onCollisionExit's terms.
Definition behavior.h:241
void loadScene(const std::string &scenePath)
Load scenePath, replacing everything currently in the scene.
Definition behavior.h:548
bool isSimulated() const
Whether this end decides what happens to this entity.
Definition behavior.h:445
virtual void onCollisionStay(const Collision &hit)
Called on each later tick the contact lasts.
Definition behavior.h:210
ResourceManager & resources()
The assets this session holds, to look one up or add one.
Definition behavior.h:384
virtual void onCollisionExit(const Collision &hit)
Called on the first tick the contact is over.
Definition behavior.h:220
void subscribe(Fn &&callback)
Listen for an event until the behavior is destroyed or the session ends.
Definition behavior.h:564
bool has() const
Whether this entity carries a T.
Definition behavior.h:314
virtual void onStart()
Called on the first simulation tick this instance runs in play mode.
Definition behavior.h:161
EventSender events()
The event bus's sending half, to emit or enqueue on.
Definition behavior.h:393
T & get()
This entity's T, which it must have.
Definition behavior.h:301
virtual void onTriggerEnter(EntityId other)
Called on the first tick other overlaps this entity's trigger.
Definition behavior.h:227
EntityId entity() const
The entity this behavior is attached to.
Definition behavior.h:275
T * tryGet()
This entity's T, or null when it has none.
Definition behavior.h:288
auto & add(T &&component)
Give this entity a T.
Definition behavior.h:324
EntityId spawn(const char *name)
Create an entity carrying name.
Definition behavior.h:503
RenderSettings & render()
The quality settings the frame is drawn at; a write lands on the next frame.
Definition behavior.h:400
void destroy(EntityId target)
Destroy entity and its subtree, firing onDestroy.
Definition behavior.h:532
Scene & scene()
The scene this behavior's entity lives in.
Definition behavior.h:377
virtual void visitFields(BehaviorFieldVisitor &visitor)
Visit the behavior's reflected authoring fields; ReflectedBehavior generates it.
Definition behavior.h:260
EntityId spawn()
Create a new, empty entity; add components to it through scene().
Definition behavior.h:492
The engine's frame clock: real + simulation time and the fixed-step accumulator.
Definition clock.h:18
Typed pub/sub event dispatcher.
Definition event_bus.h:22
The sending half of an EventBus: emit and enqueue, and no subscribe.
Definition event_bus.h:148
Named actions resolved from physical bindings, sampled once per frame.
Definition input_map.h:64
One end of a game, whether it is the authority or a guest.
Definition net_session.h:116
Open type-erased resource registry with typed handles and generational lifetimes.
Definition resource_manager.h:46
Central registry managing entities and an open set of component types.
Definition scene.h:29
Owns the application's window, GL context, input, and frame limiting.
Definition window_manager.h:61
Everything gameplay code may reach, bundled behind one pointer.
Definition behavior.h:42
WindowManager * window
The only nullable field: a host that draws nothing has none.
Definition behavior.h:47
One entity's view of a CollisionEvent, as its behaviors' collision hooks receive it.
Definition physics_events.h:45
Names one entity of one Scene, for as long as that slot holds it.
Definition entity.h:23
Everything one simulation tick was told to do, as a value.
Definition input_command.h:25
The reflection trait, specialised per type by VKM_REFLECT_BEGIN / VKM_F / VKM_REFLECT_END.
Definition reflect.h:41
Editable render tuning: pass toggles + per-effect parameters.
Definition render_settings.h:86