[SOLVED] NPC motion direction is not accurate

Hi there !
So , since there’s no way to build NPC natively , i build an Abstract Control class to auto hover(on y-axis) NPC on the water surface & towards the user (on z-axis) & added the control to the NPC spatial

Code:

package Mars_Map;

import com.jme3.bullet.control.CharacterControl;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;

/**
 *
 * @author twisted
 */
public class NPCAutoControl extends AbstractControl {

    private CharacterControl player;
    private CharacterControl NPC_control;
    private Camera cam;
    private Vector3f NPCmotion=new Vector3f();
    private float time=0.0f;
    private float zDir=0f;
    private float xDir=0.0f;
    
    public NPCAutoControl(CharacterControl player, CharacterControl NPC_control,Camera cam) {
        this.player=player;
        this.NPC_control=NPC_control;
        this.cam=cam;
        /**
         * getting the start position of the NPC's
         */
        NPCmotion=NPC_control.getPhysicsLocation();
    }

    @Override
    protected void controlUpdate(float tpf) {
        /**
         * Create CoolDown motion of around 0.3f frames
         */
        time+=tpf;
        if(time >=0.25f){
        if(player.getPhysicsLocation().distance(NPC_control.getPhysicsLocation()) <= 200 ){
            hoverNPC_Up();
              moveNPC();
            attackNPC();
        }else{

            NPC_control.setWalkDirection(new Vector3f(0,0,0));
            NPC_control.setPhysicsLocation(new Vector3f(72.81782f,-60f,760.87f));


        }
                                System.out.println(NPC_control.getPhysicsLocation());
                 
        /**
         * Reset CoolDown motion
         */
         time=0.0f;
        }
    
    
    
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {

    }

    private void moveNPC() {
        /**
         * Move the enemy Rover towards the player Rover
         */
        if(player.getPhysicsLocation().distance(NPC_control.getPhysicsLocation()) >= (200f/2f) ){
            /**
             * steppping NPC's motion
             */
            NPCmotion.set(0,0,0);
            /**
             * adding values on Z axis towards player camera direction
             * Note : negation on z-axis is done to ensure the NPC moves against the camera Direction
             */
                zDir=-cam.getDirection().getZ();
            NPCmotion.addLocal(new Vector3f(0f,0f,zDir));
            /**
             * setting the driection & the vaules of NPC motion
             */
            NPC_control.setWalkDirection(NPCmotion);
                 
          }else{
            /**
             * Reset the NPC motion & freezes it in place ..if the distance is less than 100f 
             */
            NPCmotion.set(0,0,0);
            NPC_control.setWalkDirection(NPCmotion);
        }
    }


    private void hoverNPC_Up() {
        /**
         * Rise the Enemy Rover up on the water Surface
         */
        if(NPC_control.getPhysicsLocation().getY() <= -10f){
             /**
              * Jump the NPC on the y-axis by 30f world units
              */
            NPC_control.jump();
             /**
              * Change the looking view of NPC camera to lookAtThePlayer
              */
            NPC_control.setViewDirection(cam.getDirection());
            NPC_control.setEnabled(true);

            
            
         }
    }

    private void attackNPC() {
        
    }
    
}

Now the problem lies in this snippet :

 private void moveNPC() {
        /**
         * Move the enemy Rover towards the player Rover
         */
        if(player.getPhysicsLocation().distance(NPC_control.getPhysicsLocation()) >= (200f/2f) ){
            /**
             * steppping NPC's motion
             */
            NPCmotion.set(0,0,0);
            /**
             * adding values on Z axis towards player camera direction
             * Note : negation on z-axis is done to ensure the NPC moves against the camera Direction
             */
                zDir=-cam.getDirection().getZ();
            NPCmotion.addLocal(new Vector3f(0f,0f,zDir));
            /**
             * setting the driection & the vaules of NPC motion
             */
            NPC_control.setWalkDirection(NPCmotion);
                 
          }else{
            /**
             * Reset the NPC motion & freezes it in place ..if the distance is less than 100f 
             */
            NPCmotion.set(0,0,0);
            NPC_control.setWalkDirection(NPCmotion);
        }
    }

these lines specifically:

 NPCmotion.set(0,0,0);
            /**
             * adding values on Z axis towards player camera direction
             * Note : negation on z-axis is done to ensure the NPC moves against the camera Direction
             */
                zDir=-cam.getDirection().getZ();
            NPCmotion.addLocal(new Vector3f(0f,0f,zDir));
            /**
             * setting the driection & the vaules of NPC motion
             */
            NPC_control.setWalkDirection(NPCmotion);

I cannot accurately move the NPC towards the player by some physical units …it sometimes works when i am pointing the camera to the +ve z-axis direction otherwise it moves around in-accurately !

Why are you moving the NPC in the direction the camera is facing instead of just moving it toward the player position? That’s going to be really strange because if you look left, the NPC will move to the right and not at all towards the player.

1 Like

@pspeed i tried but i cannot do it , so this was my alternative solution

If you want to move towards the player then build a direction vector to the player.

Vector3f dir = playerPosition.subtract(npcPosition).normalize();

…not whatever random direction the camera happens to be facing.

Edit: when in doubt, draw a picture. Vectors are not hard to deal with on paper.

image

P = player position
NPC = npc position
blue = playerPosition.subtract(npcPosition);
yellow = playerPosition.subtract(npcPosition).normalize();

1 Like

@pspeed Yeh ! it works , thank you !

i have a question :

I have studied that normalize() gives unit vectors ,so when i do this at the beginning of controlUpdate() :

System.out.println(player.getPhysicsLocation().subtract(NPC_control.getPhysicsLocation()).normalize());

i get these values : (-0.094627544, 0.062817514, -0.9935289) so why the value of y & x isnot normalized … Am i misUnderstanding something ?

Yes. normalize() makes the length of the vector 1.

I think you may want to brush up on 3D math concepts. This is a good place to start:
https://wiki.jmonkeyengine.org/tutorials/math/assets/fallback/index.html

1 Like

i found this in Vector3f docs :


so yeh in java normalizing vectors isnot perfectly 1 … its between (-/+)0.0f ~ (-/+)1f

No, it is approximately equal to 1, as close as one can get within the limits of floating point math.

1 Like

but x & y vaules here arenot actually approximated to one !

And just in case you want to learn what a floating point number means:

1 Like

The length of the vector, not the sum.

1 Like

I guess you didn’t even read the document I linked.

A unit vector is of length one. By Pythagorean theorem, this necessarily means that the only time x, y, or z will be 1.0 is when the vector is axis aligned… otherwise they will always be BETWEEN -1.0 and 1.0.

From the doc, it mentions how the length of a vector is determined:

Now… imagine what you’d have to do to x and y if you wanted that vector to be length 1.

1 Like

Great ! i got the point

No , i have actually read it , since starting JME i read math for dummies , the scene graph ,scene graph for dummies & 3d graphics & Terminology but you know i am a little bit confused :smiley: :smile: anyway thanks for support ! :slight_smile: :slight_smile:

1 Like

Being wrong doesn’t make you stupid. Being wrong and refusing to accept you’re wrong does. Being wrong and not learning what is right does. Being wrong, accepting you’re wrong, and learning what is right? Well that makes you an educated person. The best kind.

1 Like

@jayfella Great Video ,so floating points are dangerous concerning currencies & accuracy due to cutting off the recurring numbers , but in games that doesnot matter because its not a real world , if it works then donot ask why :sweat_smile: :sweat_smile: :joy: ,nah i am kidding

yes , actually its impressive the resultant vector length is actually one :smile:
image

so , for 3d world Pythagorean theorem formula is :

 Vector3f vectorA=new Vector3f(-0.094627544f, 0.062817514f, -0.9935289f);
        System.out.println(Math.sqrt((Math.pow(vectorA.getX(), 2) + Math.pow(vectorA.getY(), 2) + Math.pow(vectorA.getZ(), 2))));

its the longest diagonal
& it gives me 1.0000000461794674 & i observed that without using normalize() some vaules get into bounced large numbers & the NPC got disappeared from the viewPort so Great !

Yes, the pythagorean theorem works in any dimension.

Your version of the formula works in double precision (64 bit floating point). So the Vector3f floats (32 bit floating point) when cast to double are not accurate anymore… which is why you get something ‘very close to one but not 1’.

When in doubt, use the methods already provided since they have been tested a billion times already.

1 Like

@pspeed again i have frogotten FastMath :joy:

Vector3f vectorA=new Vector3f(-0.094627544f, 0.062817514f, -0.9935289f);
        System.out.println(FastMath.sqrt((FastMath.pow(vectorA.getX(), 2) + FastMath.pow(vectorA.getY(), 2) + FastMath.pow(vectorA.getZ(), 2))));

& it approximates numbers to one ! :slightly_smiling_face: :slightly_smiling_face: :grinning:

Thank you !

Still not correct, vectorA.length() (Hint: for other cases, use lengthSquared where possible, e.g. for simple comparisons)

1 Like