How to move target to a contact point from curser

I have already tried setWalkDirection to move it but it obviously doesn’t stop it at the click location, it just keeps moving. I know how to set a walking direction, I just dont know how to make it stop at the location.



For example, I want to left click an object (character.Control) and right click on a destination where i want it to walk.I already know how to move the target to the destination instantaneously, but i want to see the motion of the object moving towards the destination, and not just popping up there.



please dont just link me to the jmonkey tutorials because if I didn’t do a ton of research on my own why would I be posting here.

Well,



If you did read the tutorials you would know exactly what to do.


  • First, you need to find what will be the point the caracter is going to walk to
  • You can find that in https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:beginner:hello_picking
  • Then, you can setWalkDirection of your character to that point
  • Somewhere in the update proccess
  • Finally, on every iteration of the update, you can check if your character is, let’s say, within a chosen distance of the target (may be 0, but I recommend something like half or 1/4 of the character’s width.)
  • When this happens, simply don’t call setWalkDirection anymore, until you have another target



    You should look at the tutorials, my first question around here was somewhat related to the docs and the tutorials. Now I’ve learned my lesson =]
@derpherp said:
please dont just link me to the jmonkey tutorials because if I didn't do a ton of research on my own why would I be posting here.


Is that a double negative? Or triple negative? I'm lost.

As @shirkit mentions, you obviously didn't do the tutorials, or if you did, you missed important points.

You need to have a "walking speed" to your... object. I have no idea if it's a model, a pure geometry or whatnot. At each frame you have your whatever go "forward" by speed * tpf in the update loop until it finds itself at the destination. At that point it should stop there.

If you don't understand the last line then you need to (re)read and (re)do the tutorials. There's no other way.
1 Like
  • I have contact point.
  • I have looked at hello picking many times. I understand that tutorial well.
  • i have setWalkDirection to that point
  • this is where im failing. how do I check if my character is within that distance? lets say i just want to stop calling setWalkDirection when distance is 0. how would i do that?



    thank you for your reply
@madjack said:
Is that a double negative? Or triple negative? I'm lost.

As @shirkit mentions, you obviously didn't do the tutorials, or if you did, you missed important points.

You need to have a "walking speed" to your... object. I have no idea if it's a model, a pure geometry or whatnot. At each frame you have your whatever go "forward" by speed * tpf in the update loop until it finds itself at the destination. At that point it should stop there.

If you don't understand the last line then you need to (re)read and (re)do the tutorials. There's no other way.


Thank you for commenting on my grammar as it is extremely relevant to this post.

You obviously didn't read my post, or if you did, you missed important points. I obviously DID do the tutorials, and yes, I obviously DID miss important points. Which is why I am posting here.... after countless readings of the tutorial I am still stuck with this problem. So I need help from this kind community of developers who actually have a better understanding of jmonkey than I do. Unless you just don't know the answer either and are just pointing me towards tutorials because it's the easiest, least helpful thing you could possibly post.

If anyone would actually be kind enough to help, here is my source code.

[java]
} else if(name.equals("Move") && !isPressed) {
if(temp != null) {
temp = null;
playerController = (CharacterControl) player.getControl(0);
CollisionResults results = new CollisionResults();
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
Ray ray = new Ray(click3d, dir);
floorNode.collideWith(ray, results);
if (results.size() > 0) {
CollisionResult point = results.getCollision(0);
destination = point.getContactPoint();
destination.y = player.getWorldTranslation().y;
float distance = player.getWorldTranslation().distance(destination);
initGhostObject();
walkingDirection.addLocal(destination.subtractLocal(player.getLocalTranslation())).normalizeLocal();
playerController.setWalkDirection(walkingDirection);
}
}
}
}
};

@Override
public void simpleUpdate(float tpf) {
float distance = player.getLocalTranslation().distance(destination);
System.out.println(distance);
if(distance < 10) {
walkingDirection.set(0, 0, 0);
playerController.setWalkDirection(walkingDirection);
}
}
[/java]
@derpherp said:
Thank you for commenting on my grammar as it is extremely relevant to this post.

It is up to some extent. If I don't understand what you're saying then it makes it difficult for me to help. I do understand that some people are not native speakers (not saying it's you case), but it makes everybody's job easier when the text is clear.


because if I didn’t do a ton of research on my own why would I be posting here.

You probably wouldn't believe how many people say they have done the tutorials when they haven't. The above isn't totally clear to me either way. Anyway...


Unless you just don't know the answer either and are just pointing me towards tutorials because it's the easiest, least helpful thing you could possibly post.


For one thing, doing your own coding wouldn't help you. Doing all the tutorials, even when they look and seem boring, is the best way to learn. I'm not trying to be patronizing or condescending, but that's how things work, that you like it or not.

Now, with that said...

There are so many things wrong in there I don't know where to begin...

If you already have a Vector2f, you don't need to make a new one with the first's components... Thus:

[java]
cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f)
// becomes that
cam.getWorldCoordinates(click2d, 0f)
[/java]

Getting a headache here so here's a simple picking. See this as a freebie.
[java]
Vector3f direction = getCamera().getWorldCoordinates(gMgrs.getInputManager().getCursorPosition(), 0);
direction.subtractLocal(getCamera().getLocation()).normalizeLocal();
Ray ray = new Ray(getCamera().getLocation(), direction);
[/java]

To retrieve a distance, always use world coordinates. Both the starting point and the destination have to be world.
The "walkingDirection" I'm not sure exactly what you're trying to do here... But, it seems you're trying to use this as a direction vector? Passing it to the controller when the distance is < 10. If your goal is to stop the player from going forward then, as I said in my first post, you have to flag something (a variable or something) that tells your control update method (if that's how you use it) that the player has reached its destination and doesn't need to walk anymore. In short, you tell your controller that walking speed is either 0 or false.

Or...
[java]
public void controlUpdate(float tpf) {
// if destination NOT reached
if (!atDestination) {
// go forward
walkingMethod(...)
}
}
[/java]

Do more tutorials. If you want to become proficient, there isn't any other way.
1 Like
@madjack said:
It is up to some extent. If I don't understand what you're saying then it makes it difficult for me to help. I do understand that some people are not native speakers (not saying it's you case), but it makes everybody's job easier when the text is clear.


You probably wouldn't believe how many people say they have done the tutorials when they haven't. The above isn't totally clear to me either way. Anyway...



For one thing, doing your own coding wouldn't help you. Doing all the tutorials, even when they look and seem boring, is the best way to learn. I'm not trying to be patronizing or condescending, but that's how things work, that you like it or not.

Now, with that said...

There are so many things wrong in there I don't know where to begin...

If you already have a Vector2f, you don't need to make a new one with the first's components... Thus:

[java]
cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f)
// becomes that
cam.getWorldCoordinates(click2d, 0f)
[/java]

Getting a headache here so here's a simple picking. See this as a freebie.
[java]
Vector3f direction = getCamera().getWorldCoordinates(gMgrs.getInputManager().getCursorPosition(), 0);
direction.subtractLocal(getCamera().getLocation()).normalizeLocal();
Ray ray = new Ray(getCamera().getLocation(), direction);
[/java]

To retrieve a distance, always use world coordinates. Both the starting point and the destination have to be world.
The "walkingDirection" I'm not sure exactly what you're trying to do here... But, it seems you're trying to use this as a direction vector? Passing it to the controller when the distance is < 10. If your goal is to stop the player from going forward then, as I said in my first post, you have to flag something (a variable or something) that tells your control update method (if that's how you use it) that the player has reached its destination and doesn't need to walk anymore. In short, you tell your controller that walking speed is either 0 or false.

Or...
[java]
public void controlUpdate(float tpf) {
// if destination NOT reached
if (!atDestination) {
// go forward
walkingMethod(...)
}
}
[/java]

Do more tutorials. If you want to become proficient, there isn't any other way.


[java] CollisionResults results = new CollisionResults();
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
Ray ray = new Ray(click3d, dir);[/java]

1. i got that piece of code from the jme3 tutorial so just thought i would inform you of that. all it does if "shoot" a Ray from where i clicked my mouse so i can collect the collisions inbetween.
2. i have done all the tutorials up until the creation of terrain. read them multiple times and am actually reading some more right before i started writing this.
3. [java]walkingDirection.addLocal(destination.subtractLocal(player.getLocalTranslation())).normalizeLocal();[/java]i really have no idea what this does. i found it in another forum discussion when someone was talking about how just putting the destination as an argument caused them to fly off screen. this was there solution and sure enough it made my character walk at a constant speed so i just left it.
4. i also understand the concept that i need to check to see if my player has gotten to where he needs to go and if so stop him from walking anymore. my original question (which i obviously didn't make clear enough) is what should go into that method? i tried comparing destination vector with players and that failed for obvious reasons, i am currently trying to place a ghostControl at the destination location and cycling through a List for any objectOverLapping which contains a my player.
5. i have no idea what you mean by a walking speed. my understanding of how setWalkDirection(Vector3f v) works is that you have to supply it with a CHANGE in one or more of the axis and it just moves that far every tick, which i suppose is what i was doing with that bit a code form number 3.

Sort of inconsequential to your original question, but I’d change:

walkingDirection.addLocal(destination.subtractLocal(player.getLocalTranslation())).normalizeLocal();



to:

walkingDirection.addLocal(destination.subtractLocal(player.getWorldTranslation())).normalizeLocal();



It only ever makes a difference if player is the child of some other node but it doesn’t hurt if it’s not.



I’m not that familiar with CharacterControl so I don’t know if there is an easier way to do this, but if it were me, I’d probably add a custom Control to the player Spatial (in this case) that recalculates the distance to the destination on its update method. If the distance is less than some small threshold then find the CharacterControl and stop it.



Something along the lines of: (typing from memory here)

[java]

public class StopMeAt extends AbstractControl {

private Vector3f destination;



public StopMeAt(Vector3f destination) {

this.destination = destination;

}



…other boiler plate here…



public void controlUpdate(float tpf) {

if( destination.distanceSquared( spatial.getWorldTranslation() ) < 0.5 ) { // half a meter threshold, adjust as needed

spatial.getControl( CharacterControl.class ).setWalkDirection(null); //or whatever

spatial.removeControl(this); // job is done

}

}

}

[/java]



In theory, adding that control to your spatial will make it stop at destination. It self-removes so you can add a new one with a new destination later as needed.

It’s basic vector math to find the length of the vector (there is a method you can call on one to get it).



Given location dest and location position - to get the distance between them what do you think you do?



How about subtract position from dest (to give the movement vector needed) and then look at its length.



Normalising that vector then puts its length to 1 - that lets you set the direction of the movement at a constant speed rather than moving faster the further you are from the target.



tpf then comes into it to make sure movement doesn’t speed up/slow down as frame rate changes.







I know its tempting to just put code in and not touch it “because it works” but if you are using code without understanding how/why it works then if it doesn’t work in some cases or stops working because you change something else then you are stuck. Any code going into your program you need to understand. If need be pick it apart one statement at a time and use a bit of paper to trace through just what is happening in each statement.

@zarch said:
It's basic vector math to find the length of the vector (there is a method you can call on one to get it).

Given location dest and location position - to get the distance between them what do you think you do?

How about subtract position from dest (to give the movement vector needed) and then look at its length.

Normalising that vector then puts its length to 1 - that lets you set the direction of the movement at a constant speed rather than moving faster the further you are from the target.

tpf then comes into it to make sure movement doesn't speed up/slow down as frame rate changes.



I know its tempting to just put code in and not touch it "because it works" but if you are using code without understanding how/why it works then if it doesn't work in some cases or stops working because you change something else then you are stuck. Any code going into your program you need to understand. If need be pick it apart one statement at a time and use a bit of paper to trace through just what is happening in each statement.


To be fair, from the code he has posted, he already has normalized walking working. He may not understand why it works but he has 90% of your post already working.

And actually, I just noticed they even have a full solution that for some reason isn't working. Note this code:
[java]public void simpleUpdate(float tpf) {
float distance = player.getLocalTranslation().distance(destination);
System.out.println(distance);
if(distance < 10) {
walkingDirection.set(0, 0, 0);
playerController.setWalkDirection(walkingDirection);
}
}[/java]

So I'm giving him the benefit of the doubt on this one. Seems like the original poster actually does know some stuff.

To the original poster, I'm confused now. Can you explain what about your solution isn't working? Is distance never less than 10? What is distance?

Also, did you get this line from a tutorial:
playerController = (CharacterControl) player.getControl(0);

...if so, we need to make a pretty thorough effort to abolish that kind of abomination from all tutorials. Instead, this is better:
playerController = player.getControl(CharacterControl.class);

...since it won't break at the slightest provocation. It's not a cause of any issues in your case but I'd like to know where the badness comes from.

[To forestall the inevitable post from someone that says "getControl(0) is faster" I'd counter that in cases that it matters one shouldn't be looking it up every time at all anyway.]



To everyone else, take a deep breath, stop brow-beating and try to actually look at the posted code. I can point out stylistic issues but nothing jumps out at me as wrong. At this point, patronizing condescension would be more effective if it was wrapped in an actual solution. ;)

playerController = (CharacterControl) player.getControl(0);



i was just thinking at the time that i needed the first control attached to this player so that’s what came to mind at first and you are right this is better and i have changed it.

i learned this basic type of casting when i was learning java but i like the other way more. also its easier to read.



now onto why the distance was failing. when i would move my player the FIRST time the code sample works. the player would stop where i said. on any other time, however, the player doesn’t. i looked at console to see what the distance var was and in some cases it wouldn’t even drop below 50. the only thing i can come up with for my test not returning true was that it meant the player had to be going around the destination and that it wasn’t walking in a straight line. one thing someone on here posted was to do this:

walkingDirection.addLocal(destination.subtractLocal(player.getLocalTranslation())).normalizeLocal();

to:

walkingDirection.addLocal(destination.subtractLocal(player.getWorldTranslation())).normalizeLocal();



which after the test it started straightening out, but by that time i had already changed the way i detect for location by adding a sphere shaped GhostControl at the destination and in the update loop just cycle through the list of overlappingObjects.

[java] if (results.size() > 0) {

Vector3f point = results.getClosestCollision().getContactPoint();

initGhostObject();

rootNode.attachChild(ghostNode);

ghostNode.setLocalTranslation(point);

walkingDirection.addLocal(point.subtractLocal(player.getWorldTranslation())).normalizeLocal();

playerController.setWalkDirection(walkingDirection);

}

}

}

}

};



@Override

public void simpleUpdate(float tpf) {

if(ghostControl != null) {

List<PhysicsCollisionObject> list = ghostControl.getOverlappingObjects();

System.out.println(list);

if(list.contains(playerController)) {

walkingDirection.set(0, 0, 0);

playerController.setWalkDirection(walkingDirection);

rootNode.detachChild(ghostNode);

ghostControl.destroy();

}[/java]

the reason i destroy it after it has collided is because if i simply just remove it, it was still reporting that i was colliding with it. me destroying it is just a temporary fix until i get a cleaner way of handling this.

btw thank you for taking to time to read my post and realize that maybe i do know what im talking about. and i do have a pretty decent understanding of the walking code. all you are doing by calling it is giving it a deltaX, deltaY, deltaZ in order for it to get the slope which is actually a direction towards the target. (simply put)



EDIT: also can you explain why playerController = (CharacterControl) player.getControl(0);

is not as good as

playerCopntrol - player.getControl(CharacterControl.class);

? don’t they do the same thing but the latter is safer?



EDIT2: i switched the code back to the distance method and no luck… the lowest the distance becomes in 13 and thats only on the very fist move. any move after that is nowhere near 13 the lowest it will get on that so far that i have seen s about ~50

(CharacterControl) player.getControl(0)



Makes a lot of assumptions so the code is more fragile. It assumes that you or some other piece of code hasn’t already added some other control to that spatial pushing the character control to some other index.



getControl( CharacterControl.class ) on the other hand will give you the first CharacterControl on the spatial, no matter what it’s index is.

ok thanks. also have you any ideas as to why my distance keeps getting awkward numbers?

@derpherp said:
ok thanks. also have you any ideas as to why my distance keeps getting awkward numbers?


Don't just print the distance. Print the two points you are getting the distance for. I suspect one of them is not what you think.

float distance = player.getLocalTranslation().distance(point);

System.out.println(player.getLocalTranslation());

System.out.println(point); //point is the destination Vector3f from results.getContactPoint();

System.out.println(distance);



17.615414

(-96.94797, 7.062892, 100.315094)

(-107.99414, -6.0154986, 99.90838)

17.12389

(-97.681404, 7.055313, 100.99362)

(-107.99414, -6.0154986, 99.90838)

16.684616

(-98.41484, 7.059078, 101.67215)

(-107.99414, -6.0154986, 99.90838)

16.30394

(-99.14827, 7.069997, 102.35068)

(-107.99414, -6.0154986, 99.90838)

15.98263

(-99.8817, 7.072983, 103.029205)

(-107.99414, -6.0154986, 99.90838)

15.711767

(-100.615135, 7.0557904, 103.70773)

(-107.99414, -6.0154986, 99.90838)

15.483649

(-101.34857, 7.0730944, 104.38626)

(-107.99414, -6.0154986, 99.90838)

15.346866

(-102.082, 7.0635395, 105.06479)

(-107.99414, -6.0154986, 99.90838)

15.251335

(-102.815445, 7.0603952, 105.74332)

(-107.99414, -6.0154986, 99.90838)

15.226437

(-103.548874, 7.0666695, 106.421844)

(-107.99414, -6.0154986, 99.90838)

15.275103

(-104.2823, 7.051183, 107.10037)

(-107.99414, -6.0154986, 99.90838)

15.370123

(-105.01574, 7.05, 107.7789)

(-107.99414, -6.0154986, 99.90838)

15.541018

(-105.74918, 7.0634484, 108.45743)

(-107.99414, -6.0154986, 99.90838)

15.785593

(-106.48262, 7.072878, 109.135956)

(-107.99414, -6.0154986, 99.90838)

16.08535

(-107.21605, 7.0554323, 109.81448)

(-107.99414, -6.0154986, 99.90838)

16.419062

(-107.949486, 7.0760593, 110.49301)

(-107.99414, -6.0154986, 99.90838)

16.835241

(-108.68292, 7.060052, 111.17154)

(-107.99414, -6.0154986, 99.90838)

17.271458

(-109.41637, 7.053694, 111.85007)

(-107.99414, -6.0154986, 99.90838)

17.760363

(-110.149796, 7.0664086, 112.528595)

(-107.99414, -6.0154986, 99.90838)

18.304453

@derpherp said:
float distance = player.getLocalTranslation().distance(point);
System.out.println(player.getLocalTranslation());
System.out.println(point); //point is the destination Vector3f from results.getContactPoint();
System.out.println(distance);

17.615414
(-96.94797, 7.062892, 100.315094)
(-107.99414, -6.0154986, 99.90838)
17.12389
(-97.681404, 7.055313, 100.99362)
(-107.99414, -6.0154986, 99.90838)


I can tell just from the first two that your walk direction is wrong. All parts of the coordinate should be converging on the point's parts but they aren't. If you notice, z is getting farther away, not closer.

In this line:
walkingDirection.addLocal(point.subtractLocal(player.getWorldTranslation())).normalizeLocal();

I see you are adding a direction to some previous direction. Is there a reason you are doing this as opposed to just setting the new direction?
walkingDirection.set( point.subtractLocal(player.getWorldTranslation()) ).normalizeLocal();

And just another point on world versus local:
float distance = player.getLocalTranslation().distance(point);
System.out.println(player.getLocalTranslation());
System.out.println(point); //point is the destination Vector3f from results.getContactPoint();

If player is ever the child of some other node then you will get really bizarre results because you are mixing world space (the point) with parent-local space (getLocalTranslation()). For these sorts of things you should always be using world space... though you may have to reinterpret the direction for parent-local space if player is ever the child of something else with a transform.

I'm presuming in your case that getWorldTranslation() will be returning the same results but using it is more appropriate and will keep other people from pointing it out to you all the time.