(February 2024) Monthly WIP Screenshot Thread

Looks like I’m starting the Feb. WIP thread… :slight_smile:
I consider state management to be one of the most important (and challenging) aspects of game design and implementation so I’m thinking a lot about ways to improve it.
I program my games in my own interpreted language so in this milestone I have added a new switch to command. It goes like this:

switch to "level2"

where “level2” is a folder name where you put all your level code files. This one line of code, cleans the current engine state (JME & Interpreter) and switches (runs) to whatever code there is in “level2” folder.
It’s totally dynamic. For example, You can switch to a random level out of 10:

switch to "level"+rnd(10)

Or you can just-in-time download your level’s code from the web and switch to it. Everything becomes so easy…

In the above demo clip, I put 3 different games, each in its dedicated folder and let the engine to switch between them after a few seconds of game play.
The biggest challenge developing this was to avoid memory leaks when switching between states.
Next, I’m going to develop a shared memory feature so that some variables / game entities will be shared between states. It’s essential for scoring / life / time / main character etc.

Coding is so fun! :ok_hand:

8 Likes

I started working on some jme3-examples and convert them to use Articular-ES before moving further in the API.

Here is the JaimeBuilder, a system designed to build Jaimes (components) and attach them to the scene:

EDIT:
In this example, Jaime is a component with some data, and all Jaimes are added under a system-module object which is registered to the JaimeBuilder system. The JaimeBuilder iterates over all the registered components in the modules present under the entities (id), and add them to the scene.

I still working on the CinematicBuilder that should wrap cinematics for jaimes, and the InputSystem that wraps user input for the input manager.

Here is the code:

[Jaime.java]:

/**
 * A composite component providing high-level object fields
 * for the scene environment, this corresponds to the setupScene().
 *
 * @author pavl_g
 */
public final class Jaime implements Component {

    private final Node jaime;
    private final Vector3f worldPosition;
    private final AmbientLight ambientLight = new AmbientLight();
    private final PointLight pointLight = new PointLight();

    public Jaime(final AssetManager assetManager, final Vector3f worldPosition) {
        jaime = (Node) assetManager.loadModel("Models/Jaime/Jaime.j3o");
        this.worldPosition = worldPosition;
    }

    @Override
    public Id getId() {
        return new Component.Id((hashCode() >>> 16) ^
                GameComponents.CHARACTERS.getEntity().getId().intValue());
    }
}

[JaimeBuilder.java]:

/**
 * A system used for building Jaime characters at runtime.
 *
 * @author pavl_g
 */
public class JaimeBuilder implements SystemEntitiesUpdater<SimpleApplication> {

    @Override
    public ArticularSystem getAssociatedSystem() {
        return Systems.ENV_SYSTEM;
    }

    @Override
    public void update(MemoryMap.EntityComponentMap entityMap, EntityComponentManager<SimpleApplication> entityComponentManager, SimpleApplication input) {
        final Module env = (Module)
                entityComponentManager.getComponent(GameComponents.CHARACTERS.getEntity(), this);
        env.getComponents().forEach((number, component) -> {
            try {
                setup(input, component);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        });
    }

    private void setup(SimpleApplication app, Component env) throws NoSuchFieldException, IllegalAccessException {
        // Get involved components
        final Vector3f worldPosition = env.getData("worldPosition");
        final Node jaime = env.getData("jaime");
        final AmbientLight al = env.getData("ambientLight");
        final PointLight pl = env.getData("pointLight");
        // attach jaime and its data
    }
}

    private final ArticularManager<SimpleApplication> ecsManager = new ArticularManager<>();
    private final JaimeBuilder jaimeBuilder = new JaimeBuilder();
    private final ComponentCollection characters =
            new ComponentCollection(GameComponents.CHARACTERS.getEntity().getId());
    ...
    @Override
    public void simpleInitApp() {

        // environment setup and update
        final Component jaime0 = new Jaime(getAssetManager(), new Vector3f(0, 0f, 0)); // initialize components
        final Component jaime1 = new Jaime(getAssetManager(), new Vector3f(2f, 0f, 0f)); // initialize components
        final Component jaime2 = new Jaime(getAssetManager(), new Vector3f(0f, 0f, 2f)); // initialize components
        final Component jaime3 = new Jaime(getAssetManager(), new Vector3f(2f, 0f, 2f)); // initialize components

        ecsManager.allocateMemoryMap(jaimeBuilder); // allocate a system and register it
        characters.register(jaime0);
        characters.register(jaime1);
        characters.register(jaime2);
        characters.register(jaime3);
        ecsManager.register(GameComponents.CHARACTERS.getEntity(), characters, jaimeBuilder);
        ecsManager.updateSystemComponents(jaimeBuilder, this); // update environment components
    }
4 Likes

Why do you need a concrete Jaime class and a dedicated builder? are you planning to create such classes for every character in your game/s ?

1 Like

Not really, I am just trying to convert the TestJaime example to use an ECS architecture, so this is a part of conversion. In real games, it really depends on the reusability-modifiability range you want to support. You will always want to build a generic API as far as you can. For example, a spaceship game can have a SpaceshipBuilder, RocksGenerator, and a UIBuilder systems for example.

In the example of Jaime, if I plan to add another character that has a model different from Jaime, I will try to dissect it even to the grade in which the model is an actual Component too, so I will have Jaime and Ninja components for instance, and Sword component, …etc, so composing them will be much easier. And then the JaimeBuilder will be CharacterBuilder.

1 Like

Specifically, one very particular ECS architecture.

A Zay-ES example would be done differently (and arguably with a lot less ECS-specific code).

2 Likes

Is this considered a bad approach?

I don’t use ECS in my game, but I do define each unique NPC as its own class extending an NPCAgent class and include aspects of componentization for the things that would not be suitable to put in a parent class. (I don’t like committing entirely to one approach, but I also have never coded outside of my own personal projects so I am not well versed on different project-architecture approaches, and I sort of just wing it and do things anyway that works, as long as it feels organized and compiles)

I had figured the alternative would be writing some simpler scripting for defining each NPC type in a non-java class, but I’ve always just liked keeping everything written in java classes to keep my mind in java-mode, even if its just a short class that references the model and animations for that NPC.

I’m curious to hear more on this if others do think this is a bad approach.

2 Likes

There are lots of ways to do it… and lots of ways to spread the logic around.

The topic is maybe too deep for a screen shot thread.

I do tend to “hard code” a lot of AI behavior but most of my actual mobs/game object creation is just setComponents(component1, component2, component3) calls. Maybe in a utility method.

Thinking too heavily in “my mob is a class” means you may miss opportunities to mix and match things. It can lead to ‘inheritance over composition’ style mistakes when one should prefer composition… and that’s what an ECS really excels at.

For Mythruna, some object behaviors are based on scripted types… but the scripted types support multiple inheritance so in the end is really just a way of collecting a bag of actions together.

4 Likes

I am not qualified to answer this question specifically as I am new to ECS, but let me give you an example from the embedded systems world that I have already studied a lot of time…

Imagine, you want to build a driver API for mice, a standard mouse will have 3 buttons, 1 scrollball, and a laser sensor. Well, you can just bump a StandardMouse class with all these stuff, but here is what happens when the market releases another mouse with a forward/backward option, you will have to extend the StandardMouse to add the new features, let’s say OfficeMouse extends StandardMouse. Then, the market is back on your shoulders with the DPI feature, so by following this inheritance pattern, you decided to extend the OfficeMouse to create the DPIMouse, but here is the burden; a mouse with DPI, but doesn’t support forward/backward options, this will lead to an overwhelming internal complexity which is an anti-pattern; for example: creating boolean flags (a flag hasDPI()), adding empty bits to the data report …and so on.

To refactor this mess to ECS, it’s far easier, you just create a Component Button, a Component ScrollBall, and a Component LaserSensor; then you compose them as you wish under a mouse entity. The detailed-design can be anything ranging from simple generic objects added to a collection to more advanced bizarre features.

One bizarre feature I thought of is the system Modules, a module is just an interface that provides a colllection of Components, so back to the mouse example, I have a hope that CircuitModules can play a role in reusability behavior; where a mouse circuit module is reusable in other entities without rebuilding the whole logic when in need to the mouse circuit for other bizarre reasons.

If you plan to be as generic as possible in your tool/game/API or whatever, the ecs architectural pattern will make it possible for maintenance and modifiability reasons.

There are a lot of ways to implement an ecs architecture, as Paul said, the end that this is only an architectural pattern, it cannot describe the detailed-design clearly. There are game engines that support ecs internally nowadays, but this doesn’t add that much, data is much flexible to compose than anything ever.

EDIT:
If you are keen to know more, you can read the thread [An ECS API], also I have a software specification catalogue for articular-es, it displays the problem in general, then the one specifc solutions from the articular-es perspective (but it inherits the solution from the architecture itself).

1 Like

Btw, if you want to deeply study this specific problem, there is already an API, that I erroneously built on top of an inheritance model, then realized the mess and continued to adapt the articular-es to fix this mess.

1 Like

The mouse example is an interesting one, and a better one than most others I’ve seen. I’ve tried watching some YouTube videos comparing OOP to ECS that always end up trying paint any usage of inheritance in a terrible light by assuming that you can’t apply aspects of composition to avoid the downsides of a full-on inheritance approach. And usually those videos/articles leave me feeling more confused since they will try very hard to code themselves into a corner to prove inheritance is awful and will say that inheritance should never ever ever be used for any reason, whereas I’ve come to find inheritance should be sparsely and carefully used in combination with composition to negate the downsides.

Regarding the mouse example, if I were to apply the hybrid approach I take with my game, I would still end up with a big StandardMouse class and would declare all of the common mouse component classes in the parent class as public vars, but leave them null. So then any child classes or new instances of StandardMouse could initiate whichever components they need. This also allows the components in StandardMouse to be used by other non-related classes that don’t extend StandardMouse if necessary. So I rarely hard-code complex functionality into a parent or child class, and just use them as a staging ground for initiating components.

So I guess I probably do lean more towards composition than inheritance, but a full on ECS is still fairly confusing to me. Although I also haven’t tried making an app that way, and am already so far committed with my own approach, so I honestly haven’t had the time or motivation to start making a new app to learn ECS. And I tend to be a hands-on learner so I usually struggle to understand things until I try it first hand.

But I don’t want to go too far off the rails on this thread, so I’ll stop there. I appreciate the discussion and information though.

2 Likes

And every new feature adds yet another field to StandardMouse… many of which will not be used by most of the mouse implementations.

One important aspect of ECS architectures is the ability to add new features with minimal adjustment to existing code. If some new “mouse” requires new feature then you write a new component type and some new system code to deal with it… without touching anything but where system code is instantiated. Well tested and solid code gets to stay well tested and solid.

Using a more realistic game example, I could probably add shields to my ECS asteroids example without touching any of the existing game code except where the new state is added. Or flaming trails that can set asteroids on fire.

Sometimes an existing system will need to be modified to support some feature (for example adding gravity wells to the simple physics) but then opens up a ton of new potential for how objects can be composed.

I find that almost 100% of the time when I think of “classes” it’s when instantiating an entity or when writing some specific AI. Everything else is dealing with a set of components.

4 Likes

After adding the cinematic effects, note the idle Jaime is not added to the JumpKickCinematicBuilder:

The problem with this example is that it plays animation from the model itself (and not from scratch), so if a JumpKickCinematic will be applied to other models, that should be very identical to the way Jaime animation was created, anyway this is a very bad example to work on, I will try to look at Zay-es examples, let’s see if I can rework them!

EDIT:
This is the architecture of this implementation example, the system-component interactions are modeled as requires-provides relationship; where as for “op-code” stands for operational code (which is the action associated wtih the system), if modules are used; as in this case, the component identifiers are re-used inside the modules to provide direct associations between components among systems; if no system modules are involved, then everything stays classic and every Jaime will create its own entity, that will have a Jaime component under the JaimeBuilder system, and a Cinematic component under the CinematicBuilder system (if only we want to animate it anyway at some point):

3 Likes

Zay-ES is my all-time favorite part of programming games, meanwhile, I have a hard time to even thinking about doing it without that :smiley:

3 Likes

Ahh! Nothing like the smell of a fresh new build. Get it while it’s hot. Available Now!

Game: Luna’s Adventure. Leap series.

@PixelappOfficial

7 Likes

Rigged a gun to the character.

8 Likes

I made a short video showing the current state of my game’s combat system. It still has a lot of bugs and work to be done, but there’s at least a few new things and minor improvements since I last made a video showing combat.

This video mostly shows fighting against a rat enemy that has been given a new combat behavior called Scatter. This behavior makes them run away at low health and scatter around in different directions for a few seconds before turning around and charging at the player for a final surprise attack.

12 Likes

I just discovered I enjoy making animations. :slight_smile:

If you enjoy my posts please like the video.

9 Likes

Added the model to JMonkey. Notice that the pistol is in a separate node!

Things to do:

  • Break down the model into top and bottom part. I’m not sure if jmonkey supports animations to be applied to arms and legs separately. But just in case I’ll break down the model as a possible workaround. Edit: I’ll use AnimComposer’s makeLayer() to solve this. DONE!

  • Fix the mesh. I bought the mesh from a store so I have to clean it up a bit. DONE!!

  • Fix the material. DONE!!!

4 Likes

It can be great if you also update the documentation and teach your workflow of steps you do, or at least general steps needed to how to make online 3d asset both bought or free can actually be used in jmonkeyengine.

For what it’s worth, for my own experience, so far the steps for me with downloaded assets are:

  1. download the gltf and original files (if available)
  2. use JmeConvert to convert to j3o
  3. load the model in JME to see what it looks like
  4. if necessary, write a convert script to clean some things up for JmeConvert to do a better job.
  5. done.

Sometimes there is a step 1.5 where I have to load it into Blender, clean some things up, and re-export the GLTF (which is why sometimes it’s nice to have the original files when available).

1 Like