basic_entity.js


/**
 * An entity is a game object. 
 * All the Objects in the game are entities. 
 * Entities can have components attached to them.
 * Entities can have children entities, which can be used to create a hierarchy of entities.
 * 
 * @class Entity
 * @example
 * // Create a new entity
 * let entity = new Entity("Box");
 * 
 */
export class Entity {
    constructor(name = "Entity") {
        this._name = name;
        this._children = [];
        this._parent = null;
        this._components = [];
    }

    /**
     * Name of the entity. Names are not processed by the engine. They are used for debugging purposes.
     * @type {string}
     * @protected
     */
    get name() {
        return this._name;
    }

    set name(name) {
        this._name = name;
    }

    /**
     * Parent Entity of this entity. If this entity is the root entity, this will be null.
     * @type {Entity}
     */
    get parent() {
        return this._parent;
    }

    set parent(parent) {
        this._parent = parent;
    }

    /**
     * Children of this entity.
     * @type {Entity[]}
     */
    get children() {
        return this._children;
    }

    /**
     * Component attached to this entity.
     * @type {Component[]}
     * @readonly
     * @protected
     */
    get components() {
        return this._components;
    }

    /**
     * Returns the component of the given type attached to this entity.
     * @param {Component} type The type of the component to be returned.
     * @returns {Component} The component of the given type attached to this entity.
     * @method
     * @example
     * // Get the transform component of the entity
     * let transform = entity.GetComponent(Transform);
     * 
     */
    GetComponent(type) {
        return this._components.find(component => component instanceof type);
    }

    /**
     * Returns true if the entity has a component of the given type.
     * @param {Component} type The type of the component to be checked.
     * @returns {boolean} True if the entity has a component of the given type.
     * @method
     * @example
     * // Check if the entity has a transform component
     * let hasTransform = entity.HasComponent(Transform);
     */
    HasComponent(type) {
        return this._components.some(component => component instanceof type);
    }

    /**
     * Get all children of this entity.
     * @returns {Entity[]} The children of this entity.
     */
    GetChildren() {
        return this._children;
    }


    /**
     * Returns the parent of this entity.
     * @returns {Entity} The parent of this entity.
     */
    GetParent() {
        return this._parent;
    }

    /**
     * Removes the component of the given type from the entity.
     * @param {Component} type The type of the component to be removed.
     * @method
     * @example
     * // Remove the transform component from the entity
     * entity.RemoveComponent(Transform);
     * 
     */
    RemoveComponent(type) {
        let index = this._components.findIndex(component => component instanceof type);
        if (index > -1) {
            this._components.splice(index, 1);
        }
        else console.warn("Component not found");
    }

    /**
     * Adds a component to the entity. This checks if the component is already attached to the entity. 
     * If it is, it will not be added again.
     * @param {Component} component The component to be added.
     * @method
     * @example
     * // Add a transform component to the entity
     * entity.AddComponent(new Transform());
     * 
     */
    AddComponent(component) {
        if (!this.HasComponent(component.constructor)) {
            component.entity = this;
            this._components.push(component);
        } else console.warn("Component already attached");
    }

    /**
     * Adds a child entity to this entity. This checks if the child is already added to the entity.
     * If it is, it will not be added again.
     * @param {Entity} child The child entity to be added.
     * @method
     * @example
     * // Create a new entity
     * let entity = new Entity("Box");
     * 
     * // Create a new child entity
     * let child = new Entity("Child");
     * 
     * // Add the child entity to the parent entity
     * entity.AddChild(child);
     */
    AddChild(child) {
        if (this._children == null) {
            this._children = [];
        }
        if (!this._children.includes(child)) {
            child.parent = this;
            this._children.push(child);
        } else console.warn("Child already attached");
    }

    /**
     * Adds multiple child entities to this entity.
     * @param {Entity[]} children The child entities to be added.
     * @method
     * @example
     * // Create a new entity
     * let entity = new Entity("Box");
     * let childEntity1 = new Entity("Child1");
     * let childEntity2 = new Entity("Child2");
     * let childEntity3 = new Entity("Child3");
     * 
     * // Add the child entities to the parent entity
     * entity.AddChildren(childEntity1, childEntity2, childEntity3);
     * 
     */
    AddChildren(...children) {
        children.forEach(child => {
            this.AddChild(child);
        });
    }

    /**
     * Get a component of the given type attached to the children.
     * Returns all the components of the given type attached to all the children.
     * @param {Component} type The type of the component to be returned.
     * @returns {Component[]} The components of the given type attached to the children.
     * @method
     *  
     * @example
     * // Get the transform components of all the children of the entity
     * let transforms = entity.GetComponentsInChildren(Transform);
     * 
     */
    GetComponentsInChildren(type) {
        const components = [];
        for (const child of this._children) {
            if (child.HasComponent(type)) {
                components.push(child.GetComponent(type));
            }
        }
        return components;
    }

    /**
     * Gets the child entity with the specified component type.
     * @param {Component} type The type of the component to be returned.
     * @returns {Entity} The child entity with the specified component type.
     * @method
     *  
     * @example
     * // Get the first child entity with a transform component 
     * let child = entity.GetChildWithComponent(Transform);
     * 
     * 
     */
    GetChildWithComponent(type) {
        return this._children.filter(child => child.HasComponent(type));
    }






    /**
     * Generate a tree with the children of this entity. 
     * Components are not included in the tree.
     * @returns {Object} The tree with the children of this entity.
     * @method
     * 
     * @example
     * // Generate a tree with the children of the entity
     * let tree = entity.GetTree();
     */
    GetTree() {
        let tree = {};
        tree.name = this.name;
        tree.children = [];
        this._children.forEach(child => {
            tree.children.push(child.GetTree());
        });
        return tree;
    }


    /**@private */
    destroy() {
        this._components.forEach(component => {
            component.destroy();
        });

        this._children.forEach(child => {
            child.destroy();
        });
        this._parent.RemoveChild(this);
        this._children = [];

    }

}