Loop in thread

thanks, found,read, understood

this allows you to flexibly separate the logic

but what’s the difference if I just
made separate methods for each logical piece

and if there were a lot of such objects, I would simply add them to the hashmap

it says not to mix oop and this approach


for example i have an entityData monster

how do i make a component with monster logic where there will be an update method and access to the player component?

for example i have monster control
inside:
player position
move logic
attack logic
animation
how to divide into components so that they interact with each other inside

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package hellopickingtest.game;

import com.jme3.anim.AnimComposer;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import java.time.Instant;

/**
 *
 * @author ivan
 */
public class EnemyControl extends AbstractControl{
    HelloPickingTest app;
    
    public EnemyControl(HelloPickingTest aThis) {
        this.app = aThis;
        
    }
    private float interp = 0.0f;
    private float mySpeed = 0.05f;
    private float agrDistance = 70f;
    public boolean agr = false;
    public long nextShoot = 0;
    public boolean live = true;
    public int hp = 100;
    AnimComposer animComposer;
    public boolean isRun = false;
    public boolean isRemove = false; 
    public long removeTime = 0;
    
    @Override
    protected void controlUpdate(float f) {
        CharacterControl control = spatial.getControl(CharacterControl.class);
        Vector3f monsterPosition = control.getPhysicsLocation();
        Vector3f playerPosition = app.charPosition.getLocalTranslation();

        float distance = playerPosition.distance(monsterPosition);
        interp += (mySpeed/distance) * f;
        
        if(live){
            spatial.lookAt(playerPosition, Vector3f.UNIT_Y);

            if(distance < agrDistance && agr == false){
                agr = true;
                app.ahtSound.play();
            }

            if ( agr && distance > 30f ) {
                control.setPhysicsLocation(new Vector3f().interpolateLocal(monsterPosition, playerPosition, interp));
                if(isRun == false){
                    animComposer.setCurrentAction("run");
                    isRun = true;
                }
            }

            if(distance <= 30f){
                if(nextShoot< Instant.now().getEpochSecond()){
                    app.shootSound.play();
                    animComposer.setCurrentAction("shoot");
                    isRun = false;
                    nextShoot = Instant.now().getEpochSecond() + 3;
                    app.hp = app.hp - 19;
                }
            }
        }else{
            if(isRemove == false){
                
                if(removeTime == 0){
                    removeTime = Instant.now().getEpochSecond() +7;

                }
                if(removeTime == Instant.now().getEpochSecond()){
                    app.shootables.detachChild(spatial);
                    isRemove = true;
                }
            }
        }
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    
    }
        
    @Override
    public void setSpatial(Spatial spatial) {
       super.setSpatial(spatial);
       Node npcNode = (Node)spatial;
       Node armature = (Node)npcNode.getChild("Armature");
       
       animComposer = armature.getControl(AnimComposer.class);
       
       animComposer.setCurrentAction("stand");
    }

    public void shuted() {
       if(live){
           agr=true;
           hp = hp - 50;
           if(hp <= 0){
               app.enemyCount = app.enemyCount - 1;
               app.dieSound.play();
               animComposer.setCurrentAction("die");
               animComposer.setCurrentAction("die2");
               live = false;
               
           }
       } 
    }
}

or should I pass entityData to the control and use it inside EnemyControl?

how do i set controll to spatial if the spatial is inside a component

and inside the control should I get access to other components?

Yes. If you look at OpenKeeper, this is exactly what I have done. That is the central place of querying all entities and components. I have also wrapped some entities (like what is essentially a character/creature) to controllers which do the queries hidden with API. But yes, that is it.

Separate the logic and the visuals. Even if it is simple like:

EntityVisual extends Component {
private string model = “derp.j3o”;
}

And then your visual side (AppState perhaps) just tries to find entities that have this component to visually represent them. Together maybe with Position component that tells the entity position.

1 Like

how to pass entityData to control?

EntityVisual extends Component {
private string model = “derp.j3o”;
 //load spatial

//setup control to spatial
}
public class HealthComponent extends Component {

    public float health;

    public HealthComponent(float health)
    {
        this.health = health;
    }

    public float getHealth()
    {
        return health;
    }

}
class MyControll extends AbstractControl{
//update loop
//getting components
}
EntityData entityData = new DefaultEntityData();

        EntityId entityId = entityData.createEntity();
        EntityVisual entityVisual = new EntityVisual();
        entityData.setComponent(entityId,entityVisual );

        entityData.setComponent(entityId,new HealthComponent ());
        entityVisual.setControl(new MyControl(entityData));

Right? will this work?

Looks to be ok.

Just pass EntityID (the control serves a single entity, right?) and instance of the EntityData to your AbstractControl like you would pass any Java object.

thanks i try this method

Remember that Components are just like database rows… just properties. No logic. Logic goes to Systems. A visual representation of your entity, whether it is based on AbstractControl or something else, is essentially one System. Although it probably doesn’t alter the logic state, just the link between a 3D object and your data.

and that’s not all?

Can I have an example system?

where to create it and how to apply it?

do you need to transfer components in the system?

Please look at the example links provided earlier. Those in the actual component repository are very simple and as such are excellent learning material. OpenKeeper is a massive working multiplayer ECS example of sorts. Primarily the Zay ES examples.

All this depends on what are your ultimate plans. Like multiplayer or not. Although, I would make all games so that they are designed as kinda multiplayer. Like a server - client type.

did not find classes with the system in the name in the examples

are they just component interaction logic classes?

it this example Damage Systems Example :: jMonkeyEngine Docs

system is DamageAppState ?

Yes, there the DamageAppState is a System.

1 Like

If I have many systems: appearance, health, damage, stats, items, inventory

how to save the game?

Like so:

i try create my own savable object with components
or it won’t work?

i need to save the player
items on it
and inventory

It is one way to do it. But ill-advised. If you read the thread I linked. I was about to do that. But I was talked out of it…

i add in dependencies
compile “com.simsilica:zay-es:1.2.1”
compile “com.simsilica:zay-es-net:1.2.1”
implementation(“com.google.guava:guava:31.1-jre”)
implementation group: ‘org.slf4j’, name: ‘slf4j-api’, version: ‘2.0.6’

add have next error

Could not find method compile() for arguments [com.simsilica:zay-es:1.2.1] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

I recon you are using newer version of Gradle that doesn’t have compile anymore. It is effectively replaced by implementation. Well, not so simple but read some docs if you are interested. I’ll guess implementation will work.