Saturday, April 4, 2020

Tutorial 2.3: Let's make "Things": Entities

One essential “core part” of your engine & game, are entities. It’s a root class used for many different “things” in your game. Dynamic things I should add. Stuff that can be spawned, eventually in big numbers, stuff that can be removed/killed. Things that can move or think, and have their own status (ie. “dead”, “very angry”, “watching TV”, …). You, my friend, are an Entity. And so is your mother.
All the junk in this Tower22 kitchen, but also the lightsources, dirt decals, floor, walls, and invisible (A.I. nodes, sound emitters) are entities. 3D entities mostly, but the idea is the same.

In this LibGDX case, the Entity is also basically a wrapper around the “Sprite” class, though keep in mind that not every Entity is necessarily a visual thing. A sound emitter could also be considered as an entity. So the very abstract base class, doesn’t even have a Sprite. Yet most of them are drawable. The very base entity class has the following properties:
public class EnEntityBase {
    int     entityId;       // Unique number. Can be used for searching
    String  name;           // Name like "Player". Can be used for searching as well, though may not be unique
    Vector2 position;       // Meters away from center
    Vector2 size;           // In meters
    float   rotationAngle;  // In degrees

} // EnEntityBase

Note the position and size are (LibGDX) 2D vectors. They are just tuples (X and Y coordinate), but also include all kinds of handy math functions we may need later on. Oh, and witness here how we made a firm decision for our engine here: it’s a 2D engine. Otherwise we would have to use Vector3 types, to begin with (amongst some other differences).

Size is used for setting Sprite dimensions, in case of drawable entities. But it could also be an ellipsoid, in case of a trigger or sound effect that starts buzzing once the camera is within range.

Most basic functions we are about to add now, are either “virtual” (doing nothing at this abstract level), or involve the coordinate system. Setting positions and such. And then there are a few “virtual” methods, like render(). The base class doesn’t do anything with it, but other classes that inherit, can override this method. For starters, the class would look like this (function implementations missing):
public class EnEntityBase {
    int     entityId;       // Unique number. Can be used for searching
    String  name;           // Name like "Player". Can be used for searching as well, though may not be unique
    Vector2 position;       // Meters away from center
    Vector2 size;           // In meters
    float   rotationAngle;  // In degrees
    boolean flagKill;       // If true, next update shall return false, so it gets removed from the entitySystem


    public void kill()
    {
        this.flagKill   = true; // Return false next update cycle
    } // kill

    public boolean update( float deltaSecs ) {
       return (this.flagKill == false);
    }

    public void render( Batch batch )

    public int getId()
    public void setId( int id )

    public String getName()
    public void setName( String name )

    public Vector2 getSize()
    public void setSize( float radius )
    public void setSize( float width, float height )

    public Vector2 getPosition()
    public void setPosition( Vector2 pos )
    public void setPosition( float x, float y )
    public void move( Vector2 direction )
    public void move( float deltaX, float deltaY )

    public float getRotation()
    public void setRotation( float angleDegrees )
    public void rotate( float deltaDegrees )

} // EnEntityBase

Note the functions “render” and “update”. Render is about drawing, but may not be called if the entity is invisible / off the screen. Update on the other hand is always called, regardless. So, where render is just for drawing, update is for A.I., updating animation frames, doing physics, et cetera. Update returns TRUE or FALSE. FALSE means the entity wants to get killed, removed from the system, self-destruct.

Another thing to explain: the difference between “setPosition” and “move”, where the latter adds a delta up to the existing position, while “set” changes it. Same thing for rotate.
 




Next file would be “EnEntitySprite”. Another kinda global, high-level class. It overrules the Base class, and adds a LibGDX sprite:
public class EnEntitySprite extends EnEntityBase {

    Sprite sprite;

    public EnEntitySprite( )
    {
        this.sprite             = new Sprite( );
        this.setSize( 2 );
        this.setPosition( 0,0 );
    } // create
    
    @Override
    public void render( Batch batch )
    {
        // Update Size / origin / position
        float width = this.size.x * this.renderScale;
        float height= this.size.y * this.renderScale;
        this.sprite.setSize( width, height );
        this.sprite.setOrigin( width * 0.5f, height * 0.5f );

        this.sprite.setPosition( this.position.x - width * 0.5f,  this.position.y - height * 0.5f  );

        // Render
        this.sprite.draw( batch );
    } // render

    ...

} // EnEntitySprite
The plan for today is to replace the original (LibGDX sprite) in the Game code with this class above – just to see if it works. We need to add one more thing first though, the sprite image itself. Grab your pencils and move on to the next chapter gentlemen.

No comments:

Post a Comment