@skewedaspect/sage 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/Readme.md +53 -0
  3. package/dist/classes/bindings/toggle.d.ts +122 -0
  4. package/dist/classes/bindings/trigger.d.ts +79 -0
  5. package/dist/classes/bindings/value.d.ts +104 -0
  6. package/dist/classes/entity.d.ts +83 -0
  7. package/dist/classes/eventBus.d.ts +94 -0
  8. package/dist/classes/gameEngine.d.ts +57 -0
  9. package/dist/classes/input/gamepad.d.ts +94 -0
  10. package/dist/classes/input/keyboard.d.ts +66 -0
  11. package/dist/classes/input/mouse.d.ts +80 -0
  12. package/dist/classes/input/readers/gamepad.d.ts +77 -0
  13. package/dist/classes/input/readers/keyboard.d.ts +60 -0
  14. package/dist/classes/input/readers/mouse.d.ts +45 -0
  15. package/dist/classes/loggers/consoleBackend.d.ts +29 -0
  16. package/dist/classes/loggers/nullBackend.d.ts +14 -0
  17. package/dist/engines/scene.d.ts +11 -0
  18. package/dist/interfaces/action.d.ts +20 -0
  19. package/dist/interfaces/binding.d.ts +144 -0
  20. package/dist/interfaces/entity.d.ts +9 -0
  21. package/dist/interfaces/game.d.ts +26 -0
  22. package/dist/interfaces/input.d.ts +181 -0
  23. package/dist/interfaces/logger.d.ts +88 -0
  24. package/dist/managers/binding.d.ts +185 -0
  25. package/dist/managers/entity.d.ts +70 -0
  26. package/dist/managers/game.d.ts +20 -0
  27. package/dist/managers/input.d.ts +56 -0
  28. package/dist/managers/level.d.ts +55 -0
  29. package/dist/sage.d.ts +20 -0
  30. package/dist/sage.es.js +2208 -0
  31. package/dist/sage.es.js.map +1 -0
  32. package/dist/sage.umd.js +2 -0
  33. package/dist/sage.umd.js.map +1 -0
  34. package/dist/utils/capabilities.d.ts +2 -0
  35. package/dist/utils/graphics.d.ts +10 -0
  36. package/dist/utils/logger.d.ts +66 -0
  37. package/dist/utils/physics.d.ts +2 -0
  38. package/dist/utils/version.d.ts +5 -0
  39. package/docs/architecture.md +129 -0
  40. package/docs/behaviors.md +706 -0
  41. package/docs/binding_system.md +820 -0
  42. package/docs/design/input.md +86 -0
  43. package/docs/entity_system.md +538 -0
  44. package/docs/eventbus.md +225 -0
  45. package/docs/getting_started.md +264 -0
  46. package/docs/images/sage_logo.png +0 -0
  47. package/docs/images/sage_logo_shape.png +0 -0
  48. package/docs/overview.md +38 -0
  49. package/docs/physics_system.md +686 -0
  50. package/docs/scene_system.md +513 -0
  51. package/package.json +69 -0
  52. package/src/classes/bindings/toggle.ts +261 -0
  53. package/src/classes/bindings/trigger.ts +211 -0
  54. package/src/classes/bindings/value.ts +227 -0
  55. package/src/classes/entity.ts +256 -0
  56. package/src/classes/eventBus.ts +259 -0
  57. package/src/classes/gameEngine.ts +125 -0
  58. package/src/classes/input/gamepad.ts +388 -0
  59. package/src/classes/input/keyboard.ts +189 -0
  60. package/src/classes/input/mouse.ts +276 -0
  61. package/src/classes/input/readers/gamepad.ts +179 -0
  62. package/src/classes/input/readers/keyboard.ts +123 -0
  63. package/src/classes/input/readers/mouse.ts +133 -0
  64. package/src/classes/loggers/consoleBackend.ts +135 -0
  65. package/src/classes/loggers/nullBackend.ts +51 -0
  66. package/src/engines/scene.ts +112 -0
  67. package/src/images/sage_logo.svg +172 -0
  68. package/src/images/sage_logo_shape.svg +146 -0
  69. package/src/interfaces/action.ts +30 -0
  70. package/src/interfaces/binding.ts +191 -0
  71. package/src/interfaces/entity.ts +21 -0
  72. package/src/interfaces/game.ts +44 -0
  73. package/src/interfaces/input.ts +221 -0
  74. package/src/interfaces/logger.ts +118 -0
  75. package/src/managers/binding.ts +729 -0
  76. package/src/managers/entity.ts +252 -0
  77. package/src/managers/game.ts +111 -0
  78. package/src/managers/input.ts +233 -0
  79. package/src/managers/level.ts +261 -0
  80. package/src/sage.ts +119 -0
  81. package/src/types/global.d.ts +11 -0
  82. package/src/utils/capabilities.ts +16 -0
  83. package/src/utils/graphics.ts +148 -0
  84. package/src/utils/logger.ts +225 -0
  85. package/src/utils/physics.ts +16 -0
  86. package/src/utils/version.ts +11 -0
@@ -0,0 +1,86 @@
1
+ # Input System Design Document
2
+
3
+ ## 1. Overview
4
+
5
+ The input system decouples raw device events (from keyboards, mice, controllers, etc.) from high-level game actions.
6
+ Low-level inputs are mapped to configurable actions (e.g., `action:jump` or `action:pitch`), and the system supports
7
+ both digital and analog inputs—including modifier key combinations. Entire sets of bindings are defined in
8
+ **Action Contexts**, which can be swapped dynamically when switching game contexts.
9
+
10
+ ## 2. Requirements
11
+
12
+ - **High-Level Actions:**
13
+ - Support digital actions (e.g., toggle, press, momentary)
14
+ - Support analog actions (e.g., continuous values)
15
+
16
+ - **Action Contexts:**
17
+ - Facilitate switching game contexts at runtime by swapping entire input configurations
18
+ - Allow the developer to define which actions are active in each context (a global context is implied)
19
+
20
+ - **Flexible Binding Configuration:**
21
+ - Bindings can be loaded from external sources (batch definitions) or modified in real time (via a UI)
22
+ - In either case, loading bindings is considered an external concern to this library; it will simply consume the
23
+ provided objects/lists or offer endpoints to configure them on the fly
24
+
25
+ ## 3. Binding Types
26
+
27
+ Bindings are categorized based on the type of input they accept and the type of action they output.
28
+
29
+ ### Digital-to-Digital Bindings
30
+
31
+ - **Trigger**
32
+ • **Input:** Digital (key/button press)
33
+ • **Action:** Digital (instantaneous event, e.g., jump)
34
+
35
+ - **Momentary**
36
+ • **Input:** Digital (key/button hold)
37
+ • **Action:** Digital (active while held, e.g., sustained movement)
38
+
39
+ - **Toggle**
40
+ • **Input:** Digital (key/button press)
41
+ • **Action:** Digital (toggles state; remains set until triggered again, e.g., turning a flashlight on/off)
42
+
43
+ ### Analog-to-Analog Bindings
44
+
45
+ - **Value**
46
+ • **Input:** Analog (continuous axis, such as a joystick)
47
+ • **Action:** Analog (emits continuous values, e.g., camera pitch)
48
+
49
+ ### Digital-to-Analog Bindings
50
+
51
+ - **TriggerValue**
52
+ • **Input:** Digital (key/button press)
53
+ • **Action:** Analog (emits a fixed value when triggered, e.g., a key press produces a “1.0” value)
54
+
55
+ - **ToggleValue**
56
+ • **Input:** Digital (key/button press)
57
+ • **Action:** Analog (toggles between off and a defined analog value, similar to a digital toggle but emitting an
58
+ analog output)
59
+
60
+ - **MomentaryValue**
61
+ • **Input:** Digital (key/button hold)
62
+ • **Action:** Analog (continually emits a specified analog value while the input is held)
63
+
64
+ ### Analog-to-Digital Bindings
65
+ - **ValueTrigger**
66
+ • **Input:** Analog (continuous axis, such as a joystick)
67
+ • **Action:** Digital (emits a digital event when the analog value exceeds a threshold, e.g., joystick tilt triggers
68
+ a jump)
69
+
70
+ ## 4. Binding Modifiers
71
+
72
+ *Binding Modifiers* are additional properties that can be applied to any binding to enhance or alter its behavior
73
+ without affecting its fundamental digital or analog nature. Examples include:
74
+
75
+ - **Combination Modifier:**
76
+ • Requires additional inputs (e.g., modifier keys) to be active for the binding to fire
77
+ • Differentiates between a bare key press and one combined with a key modifier
78
+
79
+ - **Repeat Modifier:**
80
+ • Enables auto-repeat functionality for digital inputs with configurable delay and repeat rate
81
+
82
+ - **Analog Modifier (Deadzone/Sensitivity):**
83
+ • Adjusts the processing of analog inputs by applying deadzone filtering and sensitivity scaling
84
+
85
+ *Note:* Although analog modifiers naturally align with analog bindings, binding modifiers offer a unified mechanism
86
+ for behavior tweaking across all binding types.
@@ -0,0 +1,538 @@
1
+ # Entity System Guide
2
+
3
+ SAGE uses a flexible building-block approach to create game objects. This guide explains how this system works and how you can use it to build your game.
4
+
5
+ ## Building Games with Building Blocks
6
+
7
+ Think of creating game objects like building with LEGO bricks. Instead of creating one large, specialized piece for each object in your game, you use smaller, reusable pieces that snap together in different combinations.
8
+
9
+ For example, rather than creating a complete "Player" object from scratch, you might:
10
+ - Start with a basic object
11
+ - Add a piece that handles movement
12
+ - Add another piece that manages health
13
+ - Add a piece that handles player input
14
+
15
+ Then, for an enemy character, you might:
16
+ - Start with that same basic object
17
+ - Use that same movement piece
18
+ - Use that same health piece
19
+ - But instead of player input, add a piece that controls AI behavior
20
+
21
+ This approach lets you build many different game objects by mixing and matching these smaller pieces.
22
+
23
+ ## Why Build Games This Way?
24
+
25
+ 1. **Reuse and Efficiency**:
26
+ - Once you create a piece (like "movement"), you can use it in many different objects
27
+ - You don't have to rewrite the same code over and over
28
+
29
+ 2. **Easier Changes**:
30
+ - Need to modify how movement works? Change it in one place
31
+ - All objects using that piece automatically get the update
32
+
33
+ 3. **Flexibility**:
34
+ - Want to give a character new abilities? Just add the right pieces
35
+ - Want to temporarily modify something? Just swap pieces in or out
36
+
37
+ 4. **Better Organization**:
38
+ - Each piece does one thing well
39
+ - Easier to understand and fix problems when things are separated
40
+
41
+ 5. **Dynamic Changes**:
42
+ - Objects can change their capabilities during gameplay
43
+ - For example, a character could gain flying abilities by adding a "flying" piece when they get a power-up
44
+
45
+ ## Our Building Block System: Entities and Behaviors
46
+
47
+ In SAGE, we call our system the "Entity System," and it has two main concepts:
48
+
49
+ ### Entities
50
+
51
+ An entity is a game object - anything that exists in your game world. It could be:
52
+ - A character (player, enemy, NPC)
53
+ - An item (weapon, health pack)
54
+ - An obstacle (door, crate)
55
+ - An invisible manager (spawn point, trigger area)
56
+
57
+ Each entity has:
58
+ - A unique ID (so we can find it)
59
+ - A type (what kind of entity it is)
60
+ - State data (information about the entity, like position or health)
61
+ - A collection of behaviors (the pieces that define what it can do)
62
+
63
+ ### Behaviors
64
+
65
+ Behaviors are the building blocks that define what an entity can do. Each behavior:
66
+ - Handles a specific aspect of functionality (movement, health, combat)
67
+ - Responds to events (like "take damage" or "player pressed jump")
68
+ - Can update each frame (for continuous actions like movement)
69
+ - Can interact with the entity's state data
70
+
71
+ Examples of behaviors:
72
+ - A movement behavior that updates position based on speed
73
+ - A health behavior that tracks damage and healing
74
+ - An input behavior that responds to player controls
75
+ - An AI behavior that makes decisions for enemies
76
+
77
+ For a comprehensive guide on creating and using behaviors, check out the [Behaviors Guide](behaviors.md). It provides detailed information about implementing, testing, and combining behaviors to create complex entity functionality.
78
+
79
+ ## Technical Details
80
+
81
+ In code, these concepts are implemented as:
82
+
83
+ - `GameEntity`: The class that represents any object in your game
84
+ - `GameEntityBehavior`: The base class for all the building-block behaviors
85
+
86
+ When you want to create objects for your game, you'll define what type of entity it is, what state data it has, and which behaviors it should use. The Entity System takes care of connecting everything together and making sure it all works smoothly.
87
+
88
+ The following sections will go deeper into how to work with entities and behaviors, including code examples and advanced usage patterns.
89
+
90
+ ## Entity Communication Architecture
91
+
92
+ In our game engine, we use an event-driven architecture that allows game objects (entities) to communicate without direct dependencies. This creates a more modular and maintainable system.
93
+
94
+ ### Event Broadcasting System
95
+
96
+ Events function as a communication channel in our game. When an entity needs to communicate:
97
+ - It can "broadcast" events (like "I just took damage" or "Player collected an item")
98
+ - Other entities can "listen" for specific events they care about
99
+ - Listeners can respond when they receive relevant events
100
+
101
+ This pattern enables game objects to interact without being directly coupled, improving flexibility and maintainability. See the [Event Bus Guide](eventbus.md) for more information on how events work in SAGE.
102
+
103
+ ## Core System Components
104
+
105
+ ### Entities: Game Objects
106
+
107
+ Each entity in our system has:
108
+ - A unique ID
109
+ - A type (such as "player," "enemy," or "item")
110
+ - State data (properties like health, position, or inventory)
111
+ - Behaviors (component pieces that define functionality)
112
+
113
+ ### The Entity Manager
114
+
115
+ The Entity Manager serves as the central controller for all entities. It:
116
+ - Creates new entities as needed
117
+ - Maintains the collection of all active entities
118
+ - Ensures all entities are updated each frame
119
+ - Handles entity removal when appropriate
120
+
121
+ ## Entity Implementation Details
122
+
123
+ ### The GameEntity Class
124
+
125
+ Every game object is based on the `GameEntity` class, which provides:
126
+ - The ability to attach different behaviors
127
+ - Event listening and response mechanisms
128
+ - State management
129
+ - Update logic for game loop integration
130
+
131
+ ### Behavior Components
132
+
133
+ Behaviors are modular components that provide specific functionality. Each behavior:
134
+ - Has a unique identifier
135
+ - Specifies which events it responds to
136
+ - Contains event response logic
137
+ - May include per-frame update logic
138
+ - Can emit its own events
139
+
140
+ While behaviors are covered in more detail in the [Behaviors Guide](behaviors.md), here's a quick example:
141
+
142
+ #### Example: Implementing a Behavior
143
+
144
+ ```typescript
145
+ // Define a drinking behavior for a character
146
+ class DrinkingBehavior extends GameEntityBehavior {
147
+ name = 'DrinkingBehavior';
148
+ eventSubscriptions = ['beer:nearby', 'alcohol:consumed'];
149
+
150
+ // Handle when beer is nearby
151
+ handleBeerNearby(event: GameEvent, state: any): boolean {
152
+ // Character notices beverage and moves toward it
153
+ this.$emit({
154
+ type: 'character:speak',
155
+ data: { message: "I should check that out!" }
156
+ });
157
+ return true;
158
+ }
159
+
160
+ // Handle when alcohol is consumed
161
+ handleAlcoholConsumed(event: GameEvent, state: any): boolean {
162
+ // Character gains energy when drinking
163
+ state.energyLevel += 25;
164
+ this.$emit({
165
+ type: 'character:speak',
166
+ data: { message: "That's much better!" }
167
+ });
168
+ return true;
169
+ }
170
+
171
+ // Main event processor that routes to the right handler
172
+ processEvent(event: GameEvent, state: any): boolean {
173
+ // Use the event type to determine which handler to call
174
+ if (event.type === 'beer:nearby') {
175
+ return this.handleBeerNearby(event, state);
176
+ }
177
+
178
+ if (event.type === 'alcohol:consumed') {
179
+ return this.handleAlcoholConsumed(event, state);
180
+ }
181
+
182
+ return false;
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### Organizing Event Handling with the Strategy Pattern
188
+
189
+ When behaviors need to handle multiple types of events, the code can quickly become cluttered and hard to read. A helpful approach is to use what programmers call the "Strategy Pattern" - though you can think of it as the "Right Tool for the Right Job" approach.
190
+
191
+ #### How It Works (In Simple Terms)
192
+
193
+ Imagine you're a handyperson with different tools in your toolbox:
194
+ - You have a hammer for nails
195
+ - You have a screwdriver for screws
196
+ - You have pliers for bending wire
197
+
198
+ When you get a task, you first identify what you're working with, then grab the right tool for the job. You don't try to hammer in a screw!
199
+
200
+ In our code, we do something similar:
201
+ 1. We create separate methods (tools) for handling each type of event
202
+ 2. When an event arrives, we first check what type it is
203
+ 3. Then we call the specific method designed to handle that event type
204
+
205
+ #### The Benefits
206
+
207
+ This approach has several advantages:
208
+ - **Cleaner Code**: Each event handler does one job and does it well
209
+ - **Easier to Read**: You can quickly see all the events a behavior handles
210
+ - **Simpler to Maintain**: Need to change how one event is handled? Just update that one method
211
+ - **Better Organization**: Related code stays together, making it easier to understand
212
+
213
+ #### Example: The Strategy Pattern in Action
214
+
215
+ Here's how our previous examples look using this pattern:
216
+
217
+ ```typescript
218
+ class WeaponBehavior extends GameEntityBehavior {
219
+ name = 'WeaponBehavior';
220
+ eventSubscriptions = ['command:fire', 'command:reload', 'command:inspect'];
221
+
222
+ // Handle firing the weapon
223
+ handleFire(event: GameEvent, state: any): boolean {
224
+ // Broadcast firing event
225
+ this.$emit({
226
+ type: 'weapon:fired',
227
+ data: {
228
+ target: event.data.targetId,
229
+ damage: state.damage
230
+ }
231
+ });
232
+ return true;
233
+ }
234
+
235
+ // Handle reloading the weapon
236
+ handleReload(event: GameEvent, state: any): boolean {
237
+ state.ammo = state.maxAmmo;
238
+ this.$emit({
239
+ type: 'weapon:reloaded',
240
+ data: { ammo: state.ammo }
241
+ });
242
+ return true;
243
+ }
244
+
245
+ // Handle inspecting the weapon
246
+ handleInspect(event: GameEvent, state: any): boolean {
247
+ this.$emit({
248
+ type: 'character:speak',
249
+ data: { message: `This ${state.weaponName} looks well-maintained.` }
250
+ });
251
+ return true;
252
+ }
253
+
254
+ // Main event processor - routes to the right handler
255
+ processEvent(event: GameEvent, state: any): boolean {
256
+ // Map event types to their handlers
257
+ const handlers = {
258
+ 'command:fire': this.handleFire,
259
+ 'command:reload': this.handleReload,
260
+ 'command:inspect': this.handleInspect
261
+ };
262
+
263
+ // If we have a handler for this event type, call it
264
+ if (handlers[event.type]) {
265
+ return handlers[event.type].call(this, event, state);
266
+ }
267
+
268
+ // No handler found
269
+ return false;
270
+ }
271
+ }
272
+ ```
273
+
274
+ ### Entity Creation Process
275
+
276
+ To create an entity for your game:
277
+
278
+ 1. Define an entity blueprint - specifying type, initial state, and behaviors
279
+ 2. Register this definition with the Entity Manager
280
+ 3. Request the Entity Manager to create instances as needed
281
+
282
+ Here's an example for creating a weapon:
283
+
284
+ ```typescript
285
+ // Define a weapon
286
+ const energySwordDefinition = {
287
+ type: 'weapon:energySword',
288
+ defaultState: {
289
+ color: 'blue',
290
+ isActive: false,
291
+ damage: 50,
292
+ owner: null
293
+ },
294
+ behaviors: [
295
+ GlowingBehavior,
296
+ SoundEffectBehavior,
297
+ DamageBehavior
298
+ ]
299
+ };
300
+
301
+ // Register the definition
302
+ entityManager.registerEntityDefinition(energySwordDefinition);
303
+
304
+ // Later, create the weapon with additional initial state
305
+ const playerSword = entityManager.createEntity('weapon:energySword', { color: 'green' });
306
+ playerSword.state.color = 'green'; // Customize the instance
307
+ ```
308
+
309
+ ## Communication Between Entities
310
+
311
+ Since our entities are designed to be independent, they need reliable communication methods:
312
+
313
+ ### 1. Event Broadcasting
314
+
315
+ The primary communication method uses the event system:
316
+
317
+ ```typescript
318
+ class WeaponBehavior extends GameEntityBehavior {
319
+ name = 'WeaponBehavior';
320
+ eventSubscriptions = ['command:fire'];
321
+
322
+ processEvent(event: GameEvent, state: any): boolean {
323
+ if (event.type === 'command:fire') {
324
+ // Broadcast firing event
325
+ this.$emit({
326
+ type: 'weapon:fired',
327
+ data: {
328
+ target: event.data.targetId,
329
+ damage: state.damage
330
+ }
331
+ });
332
+ return true;
333
+ }
334
+ return false;
335
+ }
336
+ }
337
+
338
+ // In another entity's behavior
339
+ class DamageReceiverBehavior extends GameEntityBehavior {
340
+ name = 'DamageReceiverBehavior';
341
+ eventSubscriptions = ['weapon:fired', 'healing:applied'];
342
+
343
+ // Handle taking damage
344
+ handleWeaponFired(event: GameEvent, state: any): boolean {
345
+ // Only process if we're the target
346
+ if (event.data.target === this.entity.id) {
347
+ // Take damage
348
+ state.health -= event.data.damage;
349
+
350
+ // Emit damaged event
351
+ this.$emit({
352
+ type: 'character:damaged',
353
+ data: { amount: event.data.damage }
354
+ });
355
+
356
+ // Check if health is depleted
357
+ if (state.health <= 0) {
358
+ this.$emit({
359
+ type: 'character:died',
360
+ data: { cause: 'weapon' }
361
+ });
362
+ }
363
+
364
+ return true;
365
+ }
366
+ return false;
367
+ }
368
+
369
+ // Handle receiving healing
370
+ handleHealingApplied(event: GameEvent, state: any): boolean {
371
+ // Only process if we're the target
372
+ if (event.data.target === this.entity.id) {
373
+ // Apply healing
374
+ const oldHealth = state.health;
375
+ state.health = Math.min(state.maxHealth, state.health + event.data.amount);
376
+
377
+ // Emit healed event
378
+ this.$emit({
379
+ type: 'character:healed',
380
+ data: { amount: state.health - oldHealth }
381
+ });
382
+
383
+ return true;
384
+ }
385
+ return false;
386
+ }
387
+
388
+ // Dispatch to the correct handler based on event type
389
+ processEvent(event: GameEvent, state: any): boolean {
390
+ if (event.type === 'weapon:fired') {
391
+ return this.handleWeaponFired(event, state);
392
+ }
393
+
394
+ if (event.type === 'healing:applied') {
395
+ return this.handleHealingApplied(event, state);
396
+ }
397
+
398
+ return false;
399
+ }
400
+ }
401
+ ```
402
+
403
+ ### 2. Entity Queries
404
+
405
+ Sometimes an entity needs to find specific other entities:
406
+
407
+ ```typescript
408
+ class ReproductionBehavior extends GameEntityBehavior {
409
+ name = 'ReproductionBehavior';
410
+
411
+ update(dt: number, state: any): void {
412
+ // Check if there are resources nearby
413
+ const entityManager = sage.managers.entityManager;
414
+
415
+ // Look for food in the area
416
+ for (const entity of entityManager.entities.values()) {
417
+ if (entity.type === 'item:food' && this.isNearby(entity, state)) {
418
+ // Found food! Time to reproduce
419
+ state.reproductionTimer = 0;
420
+
421
+ // Create a new entity
422
+ if (state.canReproduce) {
423
+ entityManager.createEntity(this.entity.type, {
424
+ position: { ...state.position }
425
+ });
426
+ }
427
+ break;
428
+ }
429
+ }
430
+ }
431
+
432
+ isNearby(entity, state) {
433
+ // Check proximity logic
434
+ // Implementation details...
435
+ return true;
436
+ }
437
+ }
438
+ ```
439
+
440
+ ### 3. Direct Entity References
441
+
442
+ For established relationships between entities:
443
+
444
+ ```typescript
445
+ class ControllerBehavior extends GameEntityBehavior {
446
+ name = 'ControllerBehavior';
447
+ eventSubscriptions = ['controller:command'];
448
+
449
+ processEvent(event: GameEvent, state: any): boolean {
450
+ if (event.type === 'controller:command') {
451
+ const entityManager = sage.managers.entityManager;
452
+
453
+ // Manage controlled entities
454
+ if (!state.controlledEntities) {
455
+ state.controlledEntities = [];
456
+ }
457
+
458
+ // Find entities to control
459
+ for (const entity of entityManager.entities.values()) {
460
+ if (entity.type === 'unit' && this.canControl(entity)) {
461
+ // Add to controlled collection
462
+ state.controlledEntities.push(entity.id);
463
+
464
+ // Set entity to controlled state
465
+ entity.state.controlled = true;
466
+ entity.state.controllerId = this.entity.id;
467
+ }
468
+ }
469
+ return true;
470
+ }
471
+ return false;
472
+ }
473
+
474
+ update(dt: number, state: any): void {
475
+ // Manage controlled entities
476
+ if (state.controlledEntities && state.controlledEntities.length > 0) {
477
+ const entityManager = sage.managers.entityManager;
478
+
479
+ // Issue commands to controlled units
480
+ for (const unitId of state.controlledEntities) {
481
+ const unit = entityManager.getEntity(unitId);
482
+ if (unit) {
483
+ // Command implementation
484
+ // Details omitted...
485
+ }
486
+ }
487
+ }
488
+ }
489
+
490
+ canControl(entity) {
491
+ // Control logic
492
+ // Implementation details...
493
+ return true;
494
+ }
495
+ }
496
+ ```
497
+
498
+ ## Best Practices for Entity Design
499
+
500
+ 1. **Single-Responsibility Behaviors**: Each behavior should handle one aspect of functionality well.
501
+
502
+ 2. **Minimize Behavior Interdependencies**: Design behaviors that can operate independently of other specific behaviors when possible.
503
+
504
+ 3. **Use Events for Communication**: Instead of direct references between entities, use the event system to maintain loose coupling.
505
+
506
+ 4. **Modular Construction**: Build complex entities by combining simpler, reusable behaviors.
507
+
508
+ 5. **Test Behaviors Individually**: Validate behavior functionality in isolation before integration.
509
+
510
+ 6. **Document Behavior Needs**: Clearly document what state properties each behavior expects.
511
+
512
+ 7. **Consider Behavior Order**: Remember that the order behaviors are attached affects event processing priority.
513
+
514
+ 8. **Use the Strategy Pattern**: For behaviors that handle multiple event types, use separate methods for each type to keep your code organized.
515
+
516
+ For detailed guidance on behavior implementation, best practices, and patterns, see the [Behaviors Guide](behaviors.md).
517
+
518
+ ## Performance Considerations
519
+
520
+ - Monitor entities subscribing to high-frequency events
521
+ - Batch similar updates when possible
522
+ - Implement entity pooling for frequently created/destroyed objects
523
+ - Use efficient entity queries and filtering
524
+
525
+ ## Debugging Tools
526
+
527
+ - Create visualization behaviors for development
528
+ - Implement event logging for tracking communication
529
+ - Add runtime inspection tools for entities
530
+
531
+ ## Summary
532
+
533
+ Our entity system provides a modular approach to game object creation. By focusing on small, reusable behaviors and communication through events, you can create games that are easier to build, modify, and maintain. This architecture supports projects of varying complexity while providing the flexibility needed to implement diverse gameplay mechanics.
534
+
535
+ For the next steps in your learning journey:
536
+ - Dive deeper into [Behaviors Guide](behaviors.md) to master behavior creation
537
+ - Learn about communication with the [Event Bus Guide](eventbus.md)
538
+ - Explore the [Scene System Guide](scene_system.md) for managing game worlds