Moving my nodes

Right - my previous thread was a complete failure - maybe this one will help me find a solution to my problem.



I want to use some form of way pointing for my enemy units to run along a selected path according to my coordinates. Currently my enemys are being moved by this :



private void setEnemyMovement(Buster enemy){
      
      Vector3f[] enemyPoints = new Vector3f[]{
               new Vector3f(enemy.getLocalTranslation()),
               new Vector3f(enemy.getLocalTranslation().x+1000f,enemy.getLocalTranslation().y,(enemy.getLocalTranslation().z+500f)),
           };
           // Create a path for the camera.
           BezierCurve bc = new BezierCurve("camera path", enemyPoints);
           // Create a camera node to move along that path.
        
           // Create a curve controller to move the CameraNode along the path
           CurveController cc = new CurveController(bc, enemy);
           // Cycle the animation.
           cc.setRepeatType(Controller.RT_CYCLE);
         
           // Slow down the curve controller a bit
           cc.setSpeed(.15f);
           enemy.addController(cc);
   }



And I think this is why my previous problems were occuring i.e the ones with the movement of the node not following the terrain height according to this snippet in the main update loop :


Vector3f update = enemy.getWorldTranslation();   
   update.y = terrain.getHeightFromWorld(enemy.getWorldTranslation());
   update.y -= 25;
   enemy.getLocalTranslation().set(update);
  terrain.getSurfaceNormal(enemy.getLocalTranslation(), normal);
        if(normal != null) {
           enemy.rotateUpTo(normal);
       }



Can anybody advise a better method of movement for my enemy nodes and explain with an example how to use it!

Thanks so much in advance, andy

groan I have waited over 24 hours for a single reply here, I cannot understand what is so hard about explaining to somebody how to do what I am trying to do. jME is a fairly advanced game engine, however doing something like this seems to have stumped the community… Nothing from the flag rush tutorial helps - the bad guys section has not been written yet so I have no starting point in how to learn how to move my enemy nodes along a certain path, and make sure they move in accordance with the terrain! :expressionless: :expressionless: :expressionless:

jme is not a commercial game engine where you can demand any support.



The more info you provide and the better you describe the problem, the better are the chances for someone to take his time and try to help you.

Chances of getting help are quadrupled, if a easy copy/paste testcase (SimpleGame) is provided.



This is a confusing comment, this controller moves your enemy, not the camera.


           // Create a curve controller to move the CameraNode along the path
           CurveController cc = new CurveController(bc, enemy);



I don't think its good to have a Controller move your enemy and then alter the enemy's location again in the main update method.
I'd propably try to create my own CurveController, which also alters the objects height depending on the terrain.

So, create your own 'TerrainCurveController', where you can pass the terrain as a reference to it.
Then you can update the position depending on the curve and the height depending on the terrain.

Oh, ok Ill try writing one - if I have a problem ill post back.



so would this new class extend controller? Wouldnt it also need to be passed the spatial , or because it will be attached to one you dont need to - if you dont need to how do you say move the spatial which WILL be attached to this location?



Thanks



Andy

Ok, so I have started to right my controller class for enemies movements, however I am having some trouble understanding how this class is able to determine information about the spatial I will be attaching it do. Since this controller is responsible for moving the spatial, it needs access to the spatials members, however as it stands it doesnt have them… how in practice and normally does this work??



The code below is the class as it stands - with the commented question in updateMove() being where i need advice!



package controllers;

import java.util.ArrayList;
import java.util.LinkedList;

import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jmex.terrain.TerrainBlock;

public class EnemyMovementController extends Controller{

   
   private LinkedList<Vector3f> movements;
   private TerrainBlock terrain;
   private Vector3f currentPosition;
   private Vector3f currentPoint;
   public EnemyMovementController(LinkedList<Vector3f> movements, TerrainBlock terrain){
      
      this.movements = movements;
      this.terrain = terrain;
      currentPosition = new Vector3f();
   }

   
   
   @Override
   public void update(float arg0) {
      // TODO Auto-generated method stub
      
      boolean moveComplete = checkIfReachedPoint();
      
      if(moveComplete == true){
         
         currentPoint = movements.remove();
         
      }
      
      updateMove();
      
   }
   
   public boolean checkIfReachedPoint(){
      
      if(currentPoint.subtract(currentPosition).equals(new Vector3f(0,0,0)))return true;
      else return false;
      
   }
   
   public void updateMove(){
      
      //IN HERE HOW DO I FIND OUT INFO ABOUT MY SPATIAL I ATTACHED THE CONTROLLER TO?
      
   }
   
}

Please don't create too many different threads for the same problem, its hard to follow the problems 'storyline'.



terrain.getHeightFromWorld() returns the height of the terrain. but it seems does not take any terrains Y translation into account.

So when you set the enemies height, you also need to add the terrains Y translation.


terrain.getHeightFromWorld(node.getLocalTranslation())+terrainYTranslation,



here is a complete test.
i also altered your controller so it moves with linear interpolation towards the next target point.


package renegade;

import java.util.LinkedList;

import javax.swing.ImageIcon;

import jmetest.terrain.TestTerrain;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.FaultFractalHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;

public class TestTerrainMovement  extends SimpleGame{
    private LinkedList<Vector3f> movements = new LinkedList<Vector3f>();
   private TerrainPage page;

   @Override
   protected void simpleInitGame() {
      //Add terrain to the rootNode
      buildTerrain();
      
      buildEnemy();
      
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(45, 0, 45));
      movements.add(new Vector3f(5, 0, 5));
      movements.add(new Vector3f(-45, 0, -15));
      movements.add(new Vector3f(0, 0, 45));
      movements.add(new Vector3f(22, 0, -22));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(-15, 0, 30));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(45, 0, 45));
      movements.add(new Vector3f(5, 0, 5));
      movements.add(new Vector3f(-45, 0, -15));
      movements.add(new Vector3f(0, 0, 45));
      movements.add(new Vector3f(22, 0, -22));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(-15, 0, 30));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(45, 0, 45));
      movements.add(new Vector3f(5, 0, 5));
      movements.add(new Vector3f(-45, 0, -15));
      movements.add(new Vector3f(0, 0, 45));
      movements.add(new Vector3f(22, 0, -22));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(-15, 0, 30));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(45, 0, 45));
      movements.add(new Vector3f(5, 0, 5));
      movements.add(new Vector3f(-45, 0, -15));
      movements.add(new Vector3f(0, 0, 45));
      movements.add(new Vector3f(22, 0, -22));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(-15, 0, 30));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(45, 0, 45));
      movements.add(new Vector3f(5, 0, 5));
      movements.add(new Vector3f(-45, 0, -15));
      movements.add(new Vector3f(0, 0, 45));
      movements.add(new Vector3f(22, 0, -22));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(-15, 0, 30));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(45, 0, 45));
      movements.add(new Vector3f(5, 0, 5));
      movements.add(new Vector3f(-45, 0, -15));
      movements.add(new Vector3f(0, 0, 45));
      movements.add(new Vector3f(22, 0, -22));
      movements.add(new Vector3f(0, 0, 0));
      movements.add(new Vector3f(-15, 0, 30));
      
   }
   
    private void buildEnemy() {
      Box b = new Box("enemy", new Vector3f(), 4, 2.5f, 10);
      b.setModelBound(new BoundingBox());
      b.updateModelBound();
      rootNode.attachChild(b);
      b.addController(new EnemyMovementController(b, movements, page ,-200 ));
      b.getLocalTranslation().y = page.getHeightFromWorld(b.getWorldTranslation());
   }


   /**
     * build the height map and terrain block.
     */
    private void buildTerrain() {
       FaultFractalHeightMap heightMap = new FaultFractalHeightMap(257, 32, 0, 255,
            0.75f);
        Vector3f terrainScale = new Vector3f(10,2,10);
        heightMap.setHeightScale( 0.001f);
        page = new TerrainPage("Terrain", 10, heightMap.getSize(), terrainScale,
                                         heightMap.getHeightMap());
        page.setModelBound(new BoundingBox());
        page.updateModelBound();

        page.setDetailTexture(1, 16);
        rootNode.attachChild(page);

        // generate a terrain texture with 2 textures
        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                heightMap);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/highest.jpg")), 128, 255,
                384);
        pt.createTexture(32);
       
        // assign the texture to the terrain
        TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
        ts.setTexture(t1, 0);
       
        //load a detail texture and set the combine modes for the two terrain textures.
        Texture t2 = TextureManager.loadTexture(
                TestTerrain.class.getClassLoader().getResource(
                "jmetest/data/texture/Detail.jpg"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear);

        ts.setTexture(t2, 1);
        t2.setWrap(Texture.WrapMode.Repeat);

        t1.setApply(Texture.ApplyMode.Combine);
        t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate);
        t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor);
        t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

        t2.setApply(Texture.ApplyMode.Combine);
        t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned);
        t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t2.setCombineSrc1RGB(Texture.CombinerSource.Previous);
        t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

        page.setRenderState(ts);
        //set the detail parameters.
        page.setDetailTexture(1, 16);
        page.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        rootNode.attachChild(page);
       
        page.getLocalTranslation().x -= 50;
        page.getLocalTranslation().y -= 200;
        page.getLocalTranslation().z -= 50;
       
    }
   
   public static void main(String[] args) {
      TestTerrainMovement game = new  TestTerrainMovement();
      game.setConfigShowMode(ConfigShowMode.AlwaysShow);
      game.start();
   }
   
}

class EnemyMovementController extends Controller{
   private LinkedList<Vector3f> movements;
   private TerrainPage terrain;
   private Vector3f currentPoint;
   private Spatial node;
   private float terrainYTranslation;
   
   public EnemyMovementController(Spatial node, LinkedList<Vector3f> movements, TerrainPage terrain, float terrainYTranslation){
      this.movements = movements;
      this.terrain = terrain;
      this.node = node;
      this.terrainYTranslation = terrainYTranslation;
   }

   @Override
   public void update(float tpf) {
      if(movements.isEmpty() == false){
         currentPoint = movements.getFirst();
         boolean moveComplete = checkIfReachedPoint();
         if (moveComplete)
            movements.remove();
         updateMove(tpf);
      }
   }
   
   public boolean checkIfReachedPoint(){
      if((currentPoint.x <= node.getLocalTranslation().x+0.01f && currentPoint.x >= node.getLocalTranslation().x-0.01f) &&
         currentPoint.z <= node.getLocalTranslation().z+0.01f && currentPoint.z >= node.getLocalTranslation().z-0.01f) {
         return true;
      } else {
         return false;
      }
   }
   
   public void updateMove(float tpf){
      System.out.println("spatial location: " +node.getLocalTranslation());
      System.out.println("current target: " +currentPoint +"; heigth: " +terrain.getHeightFromWorld(node.getLocalTranslation()));
      node.getLocalTranslation().interpolate(new Vector3f(currentPoint.x,
            terrain.getHeightFromWorld(node.getLocalTranslation())+terrainYTranslation,
            currentPoint.z), 1f*tpf);
      
      Vector3f normal = new Vector3f();
        terrain.getSurfaceNormal(node.getLocalTranslation(), normal);
        if(normal != null) {
            node.rotateUpTo(normal);
        }
   }
}

OK I developed a further solution which correctly moves my model along the height of the terrain and it looks as follows:



Any comments on it please shout!



package controllers;
 
import java.util.LinkedList;
 
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Spatial;
import com.jmex.terrain.TerrainBlock;
import com.jmex.terrain.TerrainPage;
 
public class EnemyMovementController extends Controller
{
   private static final float SPEED = 3;

        private LinkedList<Vector3f> movements;
        private TerrainPage terrain;
        private Vector3f currentPosition;
        private Vector3f currentPoint;
        private Spatial node;
        private Vector3f startingPosition;
   private Vector3f direction;

        public EnemyMovementController(Spatial node, LinkedList<Vector3f> movements, TerrainPage terrain)
   {      
                this.movements = movements;
                this.terrain = terrain;
                currentPosition = node.getLocalTranslation();
                startingPosition = currentPosition.clone();
                currentPoint = movements.remove();
                direction = currentPoint.normalize();//take one step in direction of where we wana go!
                this.node = node;
        }
 
        @Override
        public void update(float delta)
        {
                if(!checkIfReachedPoint())
                {
                        updateMove(delta);
                }
                else if(!movements.isEmpty())
                {
                        startingPosition = currentPosition;
                        currentPoint = movements.remove();
                        direction = currentPoint.normalize();
                        System.out.println("I got there");
                }      
        }
       
        public boolean checkIfReachedPoint()
        {
                return (currentPosition.subtract(startingPosition).lengthSquared() >= currentPoint.lengthSquared());
        }
       
        public void updateMove(float delta)
        {
                currentPosition = node.getLocalTranslation();

      currentPosition.x += (SPEED * direction.x * delta);//multuiply speed by our direction and our delta
      currentPosition.y = terrain.getHeight(currentPosition.x, currentPosition.z) + 25;
      currentPosition.z += (SPEED * direction.z * delta);
               
                node.setLocalTranslation(currentPosition);
               
               
        }      
}

Hello jME community,



Thanks for make this free for everyone



-> I just used few lines of the "TestTerrainMovement" class to manage the intelligent agents which follows a chain of movements already given in a txt file, I would like to know: how to make my box to wait / stand for a few seconds and then keep going with the other movements, the time per frame says a bit, but how can I make my box to dont reach any movement until 4 secs have gone in the tpf update method.



Thanks for your help,



Av.

avril said:

I would like to know: how to make my box to wait / stand for a few seconds and then keep going with the other movements, the time per frame says a bit, but how can I make my box to dont reach any movement until 4 secs have gone in the tpf update method.

Thanks for your help,

Av.


The flow could look similar to below.  I'm not sure if the time parameter is measured in seconds, but this is a way to delay movement for your box.  When you stop, reset isMoving and timePassed.  :D  Oh yeah, another way is to start counting and do the stand anim first in one method.  And then use another method for the moving anim and actual translation.


boolean isMoving = false;
float timePassed = 0;

public void update(float time){
        move(time);
}

public void move(float time){
  if(timePassed > 4f) { // 4 seconds have passed
     if(!isMoving){  // start moving
        isMoving = true;
        // code: unload standing up animation (depends on animation type)
        // code: start moving animation, wrap
     }

     // code: move box
  }

   else { // zero to less than 4 seconds have passed
      if (timePassed == 0){
         // code: standing up animation, clamp
      } 

      timePassed += time;  // don't need to update timePassed after 4 seconds
   }
}