Best way to create moving objects

Hello jmonkeyrs,

I’m having one question to you.
I have an simple multiplayer project with server and client. Server started -> client connected -> next client connected without any problems - one player can sees other players… Everything is working nice, but I want add object that will be able to move by player. And other players can see that object is moving right now. Object I’m talking about will be vehicles.
So, on the scene will be players (BetterCharacterControl class) and cars (VehicleControl class).
Cars will stay on the scene always - not depending on actual online players. Something like Grand Theft Auto online.

Do I have to create cars same way as players: through PlayerAppState an then write distinguish code for server or I can make them any other way?
If I missed something ask for it, I will try to explain.
Thank for any replies

1 Like

We don’t know what your “PlayerAppState” is doing actually…

Physics normally is controlled by the server. So yeah, you have to create the physics controls on server side.

Clients could send input like “Moving_Forward” to the server which in turn refreshes the game state and informs all clients about the change. That would be the totally normal approach.

When you feel that your inputs should be processed instantly you would have to implmenent some kind of client side prediction.

Nevertheless, we have to less information about how you handle your client-server-connections to really help you.

1 Like

Thank you. I was thinking about this way. But I wasn’t sure that it will be good way. I know I told less information about my handling. I’m going to explain how it is working in next two or three weeks (no time for jME)

1 Like

My PlayerAppState is creating player node and attaching spatials to control. Exactly it is doing code:

public class PlayerAppState extends AbstractAppState implements ActionListener {

private PlayerData playerData;
private BulletAppState bulletAppState;
private Node rootNode;
private InputManager inputManager;
private AssetManager assetManager;

private boolean w, a, s, d;
private Node playerNode;
private Spatial player;
private BetterCharacterControl playerControl;
private Vector3f walkDirection = new Vector3f(0, 0, 0);
private Vector3f viewDirection = new Vector3f(0, 0, 0);
private Camera cam;
private CameraNode camNode;
public static final Quaternion PITCH045 = new Quaternion().fromAngleAxis(FastMath.PI/3/4,   new Vector3f(1,0,0));

public PlayerAppState(BulletAppState bulletAppState, Node rootNode, PlayerData data) {
    this.bulletAppState = bulletAppState;
    this.rootNode = rootNode;
    this.playerData = data;
}


@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app); //To change body of generated methods, choose Tools | Templates.
    this.inputManager = app.getInputManager();
    this.assetManager = app.getAssetManager();
    this.cam = app.getCamera();
    
    initPlayer();
    initInput();
}

private void initInput() {
    inputManager.addMapping("UP", new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping("DOWN", new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping("LEFT", new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping("RIGHT", new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping("JUMP", new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addMapping("RESET", new KeyTrigger(KeyInput.KEY_RETURN));
    
    inputManager.addListener(this, new String[] {"UP", "DOWN", "LEFT", "RIGHT", "JUMP", "RESET"});
}

private void initPlayer() {
    playerNode = new Node();
    player = assetManager.loadModel("Models/player/player.j3o");
    player.setLocalTranslation(0, 0, 0);
    playerControl = new BetterCharacterControl(.45f, 1.8f, 80);
    playerNode.addControl(playerControl);
    playerControl.setGravity(new Vector3f(0, 20, 0));
    playerControl.setJumpForce(new Vector3f(0, 320, 0));
    playerControl.warp(new Vector3f(0, 2, 0));
    playerNode.attachChild(player);
    
    camNode = new CameraNode("CamNode", cam);
    playerNode.attachChild(camNode);
    camNode.setLocalTranslation(new Vector3f(0, 3f, -5));
    camNode.setLocalRotation(PITCH045);
    
    bulletAppState.getPhysicsSpace().addAll(playerNode);
    rootNode.attachChild(playerNode);
}

@Override
public void update(float tpf) {
    Vector3f camDir = cam.getDirection().mult(0.2f);
    Vector3f camLeft = cam.getLeft().mult(0.003f);
    camDir.y = 0;
    camLeft.y = 0;
    viewDirection.set(camDir);
    walkDirection.set(Vector3f.ZERO);
    if (w) {
        walkDirection.addLocal(cam.getDirection());
    }
    if (a) {
        viewDirection.addLocal(camLeft);
    }
    if (s) {
        walkDirection.addLocal(cam.getDirection().negate());
    }
    if (d) {
        viewDirection.addLocal(camLeft.negate());
    }
    walkDirection.setY(0);
    playerControl.setWalkDirection(walkDirection.multLocal(5));
    playerControl.setViewDirection(viewDirection.multLocal(0.5f));
    
    refreshPlayerData();
}

private void refreshPlayerData() {
    playerData.setLocation(playerNode.getLocalTranslation());
    playerData.setRotation(playerNode.getLocalRotation());
    playerData.setWalkDirection(playerControl.getWalkDirection());
    playerData.setViewDirection(playerControl.getViewDirection());
}  



public void onAction(String name, boolean isPressed, float tpf) {
    if (name.equals("UP")) {
        w = isPressed;
    } 
    if (name.equals("DOWN")) {
        s = isPressed;
    }
    if (name.equals("LEFT")) {
        a = isPressed;
    }
    if (name.equals("RIGHT")) {
        d = isPressed;
    }
    if (name.equals("JUMP")) {
        playerControl.jump();
    }
    if (name.equals("RESET")) {
        playerControl.warp(new Vector3f(0, 0, 0));
    }
}    
}

but ClientMain is calling PlayerAppState after creating client with id for server. And creating scene too. For precision posting ClientMain code:

public class ClientMain extends SimpleApplication implements ClientStateListener {

private Client client;
private PlayerAppState playerAppState;
private BulletAppState bulletAppState;
private PlayerData playerData;
private SceneManager sceneManager;

public static void main(String[] args) {

    Utils.initSerializers();

    ClientMain app = new ClientMain();
    app.setPauseOnLostFocus(false);
    app.start();
}


@Override
public void simpleInitApp() {

    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);

    bulletAppState.setDebugEnabled(true);
    
    flyCam.setMoveSpeed(30);
    flyCam.setDragToRotate(true);

    sceneManager = new SceneManager(this, assetManager, bulletAppState);
    
    initPlayerData();
    initConnection();        
    initAppStates();
    initScene();
    
    
}

@Override
public void simpleUpdate(float tpf) {
    
    if (client.isConnected()) {
        client.send(new PlayerDataMessage(playerData));
    }
    
}

private void initConnection() {
    try {
        client = Network.connectToServer(Utils.IP_ADRESS, Utils.PORT);
        client.addMessageListener(new ClientListener(client, sceneManager));
        client.addClientStateListener(this);
        client.start();

        System.out.println("Client erfolgreich connected!");
    } catch (IOException ex) {
        Logger.getLogger(ClientMain.class.getName()).log(Level.SEVERE, null, ex);
    }
}

public void clientConnected(Client c) {
    playerData.setId(c.getId());
    sceneManager.setMyID(c.getId());
    client.send(new TextMessage("Hello Server! I'm ID: " + c.getId()));
    System.out.println("C: " + c);
    System.out.println("PD: " + playerData);
}

public void clientDisconnected(Client c, DisconnectInfo info) {
}



private void initAppStates() {
    playerAppState = new PlayerAppState(bulletAppState, rootNode, playerData);
    stateManager.attach(playerAppState);
   
}

private void initScene() {
    Material cityMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    cityMat.setTexture("DiffuseMap", assetManager.loadTexture("Materials/cityTex1.png"));
    Box floor = new Box(50, 0, 50);
    Geometry floorGeom = new Geometry("City", floor);
    floorGeom.setMaterial(cityMat);
    floorGeom.setLocalTranslation(new Vector3f(0, 0, 0));
    floorGeom.addControl(new RigidBodyControl(new MeshCollisionShape(floorGeom.getMesh()), 0));
    rootNode.attachChild(floorGeom);
    bulletAppState.getPhysicsSpace().add(floorGeom);

    /**
     * A white ambient light source.
     */
    AmbientLight ambient = new AmbientLight();
    ambient.setColor(ColorRGBA.White);
    rootNode.addLight(ambient);

    /**
     * A white, directional light source
     */
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun);
}

private void initPlayerData() {
    playerData = new PlayerData();
}

@Override
public void destroy() {
    
    bulletAppState.cleanup();
    // ...
    client.send(new CloseConnectionMessage(playerData.getId()));
    
    
    super.destroy(); 
}

But at first, I want only to make car that will be visible for every player on the scene and players be able to push the car and see how it moves.
I have searched for some tutorials but each is so different from other and I do not know which I should use. If anyone found a good tutorial, I will try code it by that tutor.
Thank you so much.

1 Like