Entities and Components - Krystian-L-Lis/Stage GitHub Wiki

#Guide #EntitiesAndComponents

Overview

The model realizes composition pattern and provide global and iterable access to groups of function blocks implementing I_RawComponent interface. Each such group is called entity. Each entity is declared as a scope(EntityStart, EntityEnd), and components should be declared within this scope. This allows for convenient declaration patterns, where components automagically join entities regardless on how deep they are declared within other structures or function blocks.

Entities are part of global linked list accessed using GetNextEntity and GetNextEntity functions. Likewise components are also part of linked list within said entity. This means that both entities and components can be statically extended without providing fixed number of entities and components completely eliminating array overflow errors.

This architecture solves the pain of dependency propagation by offering global access to entities and components, but with access strictly defined through interfaces. Function blocks can discover and use each other without complex wiring or tight coupling. It is a middle ground between passing through layers of encapsulation and exposing to much functionality.

Warning: The code examples provided are intended to illustrate typical use cases for this framework. They focus on demonstrating concepts and usage patterns and may not be syntactically correct.

Entity Scope

Entity scopes define where and when components can be registered within an entity. In this framework, components must always be registered inside an active entity scope. This allows for automatic component registration with minimal boilerplate.

An entity scope is a bounded region in which components are associated with an entity. The scope starts with EntityStart and ends with EntityEnd. Any components declared within this region are automatically registered to the enclosing entity.

  • EntityStart: Function block that marks the beginning of an entity scope.
  • EntityEnd: Function block that marks the closure of the entity scope.
  • EntityExtend: Function block that reopens a previously declared entity for modifications(e.g. due to inheritance).

An entity scope starts with EntityStart and ends with EntityEnd. Any components declared in between are automatically registered.

_entityStart: EntityStart;
	_componentA: SomeComponentA;
	_componentB: SomeComponentB;
_entityEnd: EntityEnd;
  • _entityStart declares a new entity.
  • _componentA and _componentB are automatically registered to _entityStart.
  • _entityEnd closes the entity scope, preventing further modifications.

Extending an Entity Scope

In some cases, you may need to add more components to an existing entity. Instead of redeclaring the entity, use EntityExtend to reopen its scope. This is especially useful when a Function Block containing given entity needs to be extended and with it, its entity.

_entityExtend: EntityExtend(_entityStart);
	_newComponentA: SomeComponentA;
	_newComponentB: SomeComponentB;
_entityEnd: EntityEnd;
  • EntityExtend reopens the _entityStart scope, allowing additional components to be registered.
  • _newComponentA and _newComponentB are now part of _entityStart.

Nested scopes

If one EntityStart is declared inside another entity’s scope, both entities remain separate and equal, even though they appear nested in the code structure.

_entityA: EntityStart;
    _componentA: SomeComponentA;

    _entityB: EntityStart;
        _componentB: SomeComponentB;
    _entityBEnd: EntityEnd;

_entityAEnd: EntityEnd;

  • _entityA and _entityB are completely independent entities.
  • _componentA belongs to _entityA, and _componentB belongs to _entityB.
  • The fact that _entityB is declared inside _entityA does not mean _entityB is a child of _entityA.
  • EntityEnd closes the scope of the most recently opened entity.

The EntityStart function block serves as a unique identifier within the EC model, grouping and referencing a collection of components. It offers a flexible structure where the encapsulating function block doesn’t need to implement or extend any specific functionality. Instead, it can act as a raw container for one or more entity instances and their components.

The Component function block registers a component within the system and associates it with a specific entity during initialization, ensuring that the program structure is fully established upon the completion of the FB_Init cycle. It implements I_Component that allows its functionality and also stores some metadata regarding underlying raw I_RawComponent.

Example Usage:

FUNCTION_BLOCK CustomComponent IMPLEMENTS I_Component
VAR
	_component: Component(THIS^);
END_VAR

FUNCTION_BLOCK CustomEntity
VAR
	_entityStart: EntityStart;
		_myComponent: CustomComponent;
	_entityEnd: EntityEnd;
END_VAR

To create new component types they need to extend I_RawComponent, which extends __SYSTEM.IQueryInterface. These components can be then casted to various custom interfaces through __QUERYINTERFACE function.

Example Usage:

INTERFACE I_CustomComponent EXTENDS I_Component

FUNCTION_BLOCK CustomComponent IMPLEMENTS I_CustomComponent

FUNCTION_BLOCK CustomEntity
METHOD GetEntity : I_Entity
	GetEntity := self;
END_METHOD


PROGRAM MAIN
VAR
	customEntity: CustomEntity;
	iEntity: I_Entity := customEntity.GetEntity();
	iComp: I_Component;
	iCustom: I_CustomComponent;
END_VAR

// Retrieve a component associated with an entity
IF IsOk(iEntity.GetNextEntity(iComp)) THEN
	// Cast the I_Component to I_CustomComponent
	IF __QUERYINTERFACE(iComp.Raw, iCustom) THEN
		// This returns TRUE
	END_IF
END_IF

These functions allow you to browse through entities within the system. They accept a reference to the I_Entity interface. If the passed reference is 0, then GetNextEntity will return the first entity, and GetPrevEntity will return the last entity. Otherwise, they return the next or previous entity in relation to the passed entity.

Example Usage:

FUNCTION_BLOCK CustomEntity
METHOD Init
	VAR
		iEntity: I_Entity;
		iComp: I_Component;
	END_VAR
	
	WHILE IsOk(GetNextEntity(iEntity)) DO
		WHILE IsOk(iEntity.GetNextComp(iComp)) DO
			// Operate on entity components
		END_WHILE
	END_WHILE
END_METHOD

< Previous | Home | Next >

⚠️ **GitHub.com Fallback** ⚠️