Learn what Felgo offers to help your business succeed. Start your free evaluation today! Felgo for Your Business

EntityBase

The base class for custom game entities. More...

Import Statement: import Felgo 4.0
Inherits:

Item

Inherited By:

RubeBody

Properties

Signals

Methods

Detailed Description

The EntityBase component is the base class for all game entities in a Felgo game. It is a container of other components for the game logic, visual representation, physics or audio components. Entities are used together with the EntityManager, which handles creation, removal and receiving of entities.

The EntityBase has 2 major properties: entityType and entityId.

Entity Type

The entityType property is the same for all entities of the same EntityBase, i.e. for all EntityBase instances. In contrast, the entityId is unique for each entity. The entityType property is useful e.g. for collision detection, when you would like to know with which kind of entity you collided with.

If you never need to make any decisions in your game logic based on the entityType, you do not need to set it. However, it is considered good practice to always define an entityType, because the type shows up in the debug outputs and thus makes the application flow easier to follow.

Variation Type

In some cases, the entityType might not be sufficient to distinguish between entities. One such case is when you define an EntityBase and provide properties for the outside that define what kind of entity it is. E.g. a stronger version with more health than the default entity version, but with the exact same code except this property modification. To distinguish between these "variations" of the same entityType without having to copy existing QML code, the variationType property is available.

It serves as the "extended type" of an entity. Consider the following example:

Enemy.qml:

 EntityBase {
     entityType: "enemy"
     variationType: "defaultEnemy"

     property int health: 100
 }

VariationTypeMain.qml:

 Item {
   Enemy {
       x:100
       y: 100
       // the variationType will be defaultEnemy, health is 100
   }

   Enemy {
       x: 200
       y: 100
       // make this enemy stronger - to be able to distinguish them at a collision
       health: 200
       variationType: "strongEnemy"
   }
 }

So with the variationType set to strongEnemy, a call of isOfType("strongEnemy") will only return true for the second entity. Whereas isOfType("enemy") returns true for both, the defaultEnemy and strongEnemy variations.

Entity Id

Every entity in a game has a unique entityId, which helps in distinguishing them and finding them from the EntityManager. If you do not set an entityId explicitly, it will automatically be generated from the EntityManager. The auto-generated entityId string is the entityType concatenated with the variationType and the current number of created entities in EntityManager.

The entityId is different than the QML id property in several ways. First, the QML id is only available within the same QML file where it was assigned and in their children components. Thus it does not allow you to access dynamically created entities which is possible with the entityId property. The type of the entityId is a String, compared to the typeless id property.

You can access other entities by their id property like in this example:

 Item {
   EntityBase {
       id: player1 // for access within the same QML file
       entityId: "player1" // for dynamic access with entityManager.getEntityId()
   }

   EntityBase {
       id: player2
       entityId: "player2"
   }
 }

However, this approach is not sufficient when you create entities dynamically at runtime, because you can only set id for entities declared in QML files. For that purpose, you can use the EntityManager function EntityManager::getEntityById().

The entityId is also useful for collision detection. E.g. if you want to handle collisions between 2 entity types, but only for a specific entityId.

Note: : If you do not need collision detections or game logic based on the entityId, you do not need to set this property and leave the auto-generated version.

Entity Creation And Removal with EntityManager

You can create and destroy entities at runtime with the EntityManager. Here is an example of a simple entity that consists of a simple (non-animated) Image.

 import Felgo
 import QtQuick

 GameWindow {

     EntityManager {
         id: entityManager
         entityContainer: scene
     }

     Scene {
         id: scene

         EntityBase {
             id: boxEntity
             entityId: "box1"
             entityType: "box"

             Image {
                 source: "../assets/img/box.png"
             }
         }

         MouseArea {
             anchors.fill: parent
             onClicked: {
                 // if you click the scene, a new entity is created
                 var newEntityProperties = {
                     x: Math.random()*scene.width,
                     y: Math.random()*scene.height,
                     rotation: Math.random()*360
                 }

                 entityManager.createEntityFromComponentWithProperties(
                             boxEntity,
                             newEntityProperties);
             }
         }
     }
 }

In this example, the entity was defined within the same QML file like the GameWindow and Scene. It is good practice, to write entity definitions in an own QML file. That has the advantage that it is logically separated from other code. So instead of having a single file EntityTestMain.qml, we split the previous example into 2 files:

Box.qml:

 import Felgo
 import QtQuick

 EntityBase {
     id: boxEntity
     // the entityId will be set from outer components!
     entityType: "box"

     Image {
         source: "../assets/img/box.png"
     }
 }

and EntityTestMain.qml:

 import Felgo
 import QtQuick

 GameWindow {

     EntityManager {
         id: entityManager
         entityContainer: scene
     }

     Scene {
         id: scene

         Box {
             id: boxEntity
             entityId: "box1"
         }

         MouseArea {
             anchors.fill: parent
             onClicked: {
                 // if you click the scene, a new entity is created
                 var newEntityProperties = {
                     x: Math.random()*scene.width,
                     y: Math.random()*scene.height,
                     rotation: Math.random()*360
                 }

                 entityManager.createEntityFromUrlWithProperties(
                             Qt.resolvedUrl("entities/Box.qml"),
                             newEntityProperties);
             }
         }
     }
 }

Note: the entity creation from the entityManager is done differently now, as we are creating a new entity from a qml file. Thus we are using EntityManager::createEntityFromUrlWithProperties.

Entity Pooling

You can use entity pooling to increase the performance of your game. Usually, a call of removeEntity() or one of the removal functions in EntityManager like EntityManager::removeEntityById() immediately destroy the entity from memory. This has the advantage that memory consumption is at a minimum. However, creating entities during the game may be a very slow operation, depending how complex your entity is. That means, the more components you have as children of the entity, the longer the creation takes, which can build up to tens of milliseconds. This creation process would then stall the game and produce a perceived slowlyness of your game.

To prevent this performance drain at creating new entities, you can use entity pooling. Entity pooling does not immediately destroy the entity after a call to removeEntity(), but instead just marks it as invisible. When an entity of the same entityType should be created later with EntityManger::createEntityFromUrl() or similar creation functions, the invisible entity is reused. Thus there is no negative performance impact of creation. So with pooling, you trade higher memory consumption for better runtime performance.

To enable entity pooling, both the EntityManager::poolingEnabled and the poolingEnabled property of the entity must be enabled. If you want to prevent some entities from being pooled, you can leave the default setting of poolingEnabled, which is false.

The best strategy to gain maximum performance is to enable entity pooling for all entity types that get removed and created frequently. To make sure there already are pooled entities available when the user first starts the game, you can pre-create pooled entities with EntityManager::createPooledEntitiesFromUrl() or EntityManager::createPooledEntitiesFromComponent().

Entity Pooling Example

See this example how to pre-create 10 entities at game startup, which are then used from the entit pool later in the game:

 import Felgo
 import QtQuick

 GameWindow {

   EntityManager {
     id: entityManager
     // creates the entities in the game scene
     entityContainer: scene

     // enables pooling, also set in the Entity that shall be pooled to true
     poolingEnabled: true

     Component.onCompleted: {
       // creates 10 pooled entities at startup, which are then made invisible
       createPooledEntitiesFromComponent(entityComponent, 10)
     }
   }

   Scene {
     id: scene

     Row {
       spacing: 5

       SimpleButton {
         text: "Create Entitiy"
         onClicked: {
           entityManager.createEntityFromComponentWithProperties(entityComponent, {x: utils.generateRandomValueBetween(0, scene.width), y: utils.generateRandomValueBetween(0, scene.height) })
         }
       }
       SimpleButton {
         text: "Remove Last Added Entity"
         onClicked: {
           entityManager.removeLastAddedEntity()
         }
       }
       // toggle pooling - when disabled, a call of Entity.removeEntity() leads to destruction of the entity
       SimpleButton {
         text: "PoolingEnabled: " + entityManager.poolingEnabled
         onClicked: {
           entityManager.poolingEnabled = !entityManager.poolingEnabled
         }
       }
     }
   }

   // in a real game, make an own qml file out of this and use createEntityFromUrl() instead of createEntityFromComponent()
   Component {
     id: entityComponent
     EntityBase {
       id: poolingTestEntity
       entityType: "poolingTest"

       // enable this entity type for pooling (also set EntityManager::poolingEnabled to true)
       // other entity types might not be set for pooling, thus this needs to be enabled per-entity-type
       poolingEnabled: true

       width: 20
       height: 30

       Rectangle {
         color: "green"
         anchors.fill: parent
       }

       MouseArea {
         anchors.fill: parent
         // click on the rectangle to remove it
         onClicked: poolingTestEntity.removeEntity()
       }

       onEntityCreated: console.debug("entity created with id", entityId)
       onEntityDestroyed: console.debug("entity destroyed with id", entityId)

       Component.onCompleted: console.debug("entity constructor with id", entityId)
       // onDestruction is NOT called when removeEntity is called, because poolingEnabled is set to true
       // this speeds up multiple creation/removal of entities
       Component.onDestruction: console.debug("entity destructor with id", entityId)
     }
   }

 }

Property Documentation

collidersActive : bool

This property indicates if the entity's colliders are active. The active property of BoxCollider and CircleCollider are connected with the colliderActive property, so you can deactivate an entity from the physics system. Usually you do not need to set this property explicitly, because it is connected with the visible property. So when the entity is set to invisible, the colliders also are inactive. If you want to enable the physics colliders of this entity when it is invisible, you can set it to true.

You can also connect this property with other components, if you do not want to make an entity invisible but just deactivate some of the components.

By default, collidersActive is set to the visible property.


entityId : string

This string property holds the unique identifier of an entity. It can be used for collision detection and distinguishing between single EntityBase instances especially when created dynamically. If the entityId is not set explicitly, the EntityManager generates an entityId for it. The auto-generated name starts with the entityType followed by the variationType if one is specified and ends with an increasing counter value for the current number of available entities. Each of these are separated by a "_".

An example entityId for an entity would be obstacle_toyblock_3. This is the entityId of an entity with entityType obstacle and variationType toyblock, which is the third entity created in the entityManager.

With the entityId you can retrieve a reference to other entities with EntityManager::getEntityById(). You can also remove entities based on it with EntityManager::removeEntityById().

The entityId will automatically be adjusted if you accidentally set the same id twice. In such a case, another "_" is appended at the end of your initially desired entityId. Thus always fetch the entityId of a dynamically created entity, even if you set the entityId as a creation parameter. Consider this example code as best practice to retrieve the entityId from a dynamically created component:

 var entityId = entityManager.createEntityFromUrlWithProperties( Qt.resolvedUrl("entities/MyEntity.qml"), { entityId: "myEntityId"})

 // do NOT query the entityManager with the id "myEntityId" directly
 // because it might have changed the entityId if an entity with the same id already exists to "myEntityId_"
 var entity = entityManager.getEntityById(entityId)

See also EntityBase description about entityId and EntityManager.


entityType : string

This property holds the type of the entity which is the same for all instances (i.e. objects) of this type. It can be used e.g. for checking the type of the collided entity at collision detection.

If multiple variations of the same type should be used, consider additionally setting a variationType.

The function isOfType compares if the argument is equal to this defined entityType, or the variationType.

See also variationType, EntityBase description about entityType, and isOfType.


poolingEnabled : bool

Set this property to use this entity for pooling in the EntityManager. By default, it is set to false.

For pooling to work, you also need to set EntityManager::poolingEnabled to true.

If you enable pooling, the entity does not get destroyed from memory when you call removeEntity(), but is set to invisible until the entity type can be reused again. This has the benefit of a better performance when creating entities at runtime.

If you enable pooling for an entity, make sure all Timers, Animations and Sprites are stopped in onMovedToPool and get reset to their intended initial values in onUsedFromPool. You need to do this manually, because auto-storing all properties of an entity and its children proved not being efficient enough.

See also Entity Pooling, EntityManager::poolingEnabled, movedToPool, and usedFromPool.


preventFromRemovalFromEntityManager : bool

If this property is set, a call of EntityManager::removeAllEntities() wont delete this entity. This property is set to true from BuildEntityButton for the entity created for level dragging, to prevent them getting destroyed. You can also set it to true, if you do not want to remove an entity with EntityManager::removeAllEntities(). Alternatively, if you only want to remove specific entities with defined entityType properties, you could also use EntityManager::removeEntitiesByFilter().

Note: if you set this property, the entity also does not get stored with EntityManager::storeEntitiesAsJson().

The default is false.

See also EntityManager::removeEntitiesByFilter() and EntityManager::removeAllEntities().


toStoreProperties : variant

This property is used in conjunction with the serialization feature and EntityManager::storeEntitiesAsJson().

All properties in this list get stored in that function. By default, the entityId, entityType, variationType, x, y, and rotation are stored.

If you also want to store for example myCustomProperty and myOtherProperty, set it to the following:

 EntityBase {
   // these properties are modified at runtime, and should be stored when the level is stored
   property int myCustomProperty
   property string myOtherProperty

   toStoreProperties: ["myCustomProperty", "myOtherProperty"]
 }

variationType : string

A variationType is useful for entities that share the same properties and logic (so basically are the same entityType), but have different initial settings of these properties. E.g. two versions of an entity may exist, one variationType "defaultEnemy" and one "strongerEnemy" with a higher initial value of the health property.

The function isOfType compares if the argument is equal to this defined entityType, or the variationType.

See also entityType, EntityBase description about entityType, and isOfType.


Signal Documentation

entityCreated()

This handler is called when the entity got created.

If pooling is disabled, this only happens once and is the same as Component.onCompleted(). However, when pooling is enabled it may happen multiple times, in fact every time the entity gets reused for pooling. The order of pooling calls is the following:

  1. onUsedFromPool
  2. onEntityCreated
  3. onEntityDestroyed
  4. onMovedToPool

Note: The corresponding handler is onEntityCreated.

See also poolingEnabled and EntityManager::poolingEnabled.


entityDestroyed()

This handler is called when the entity got destroyed.

If you connected a property to this entity, you should reset it to null in this handler.

Note: The corresponding handler is onEntityDestroyed.

See also poolingEnabled and EntityManager::poolingEnabled.


movedToPool()

This handler is called when poolingEnabled is set to true and the entity got removed from EntityManager.

Internally, the visible property is set to false when this signal occurs, so you do not need to set it invisible explicitly if pooling is enabled.

You should stop all timers, animations and running SpriteSequences when this handler occurs, so you are not wasting resources while the entity is pooled.

Note: The corresponding handler is onMovedToPool.

See also usedFromPool and removeEntity().


usedFromPool()

This handler is called when the entity was pooled before and gets reused now by calling EntityManager::createEntityFromUrl() or other creation functions.

Internally, the visible property is set to true when this signal occurs, so you do not need to set it visible explicitly if pooling is enabled.

You should restart all timers, animations and SpriteSequences when this handler occurs, and set all properties to the initial values. You could also do that in onEntityCreated, but it is better in this function because onEntityCreated gets also called when the entity is created the first time, i.e. when Component.onCompleted() is called. The onUsedFromPool handler is only called the second time when an entity is created, when it already removed before.

Note: The corresponding handler is onUsedFromPool.

See also movedToPool and entityCreated.


Method Documentation

getComponent(objectName)

Returns the component with the given objectName or undefined if none is found.

Use this method if you want to access a component of this entity from the outside, without using an alias. To reference it within an entity, the QML id property should be used as it is faster than calling this function. Also an alias to this id is faster, because in getComponent() all children must be iterated, whereas an alias is just a direct reference to the inner component.

Note: Only direct children of EntityBase are searched, so that is the difference to Qt's findChild() method.


isOfType(type)

Returns true if the entityType or variationType matches the type.

Use this function e.g. for collision detection, when you want to know with which entity type you collided.


removeEntity()

Removes the entity from the EntityManager.

Internally this is just a wrapper function for a call of EntityManager::removeEntityById().

If pooling is enabled, the entity does not get removed but set to invisible.

See also poolingEnabled.


Qt_Technology_Partner_RGB_475 Qt_Service_Partner_RGB_475_padded