I’m trying to write a game where player picks where they are going by just mouse-clicking on that position and it goes there (just like RuneScape’s point-and-click mouse interface).
I’m trying to get it working on the jme3test.bullet.TestWalkingChar example.
I’ve tried:
[java]character.setLocalTranslation(
cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f));[/java]
Also tried:
[java]Vector3f worldCoordinates = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
Vector3f localCoordinates = null;
character.worldToLocal(worldCoordinates, localCoordinates);
character.setLocalTranslation(localCoordinates);[/java]
and:
[java]Vector3f worldCoordinates = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
Vector3f localCoordinates = null;
character.setLocalTranslation(character.worldToLocal(worldCoordinates, localCoordinates));[/java]
But it’s just teleporting the player to some unrelated coordinates.
Any ideas?
BTW: If there’s a complete example on how to do that, it’d be great. I don’t care about game engine - I prefer (the legendary) jME3 of course but it doesn’t really matter for me. Plain OpenGL is also great.
Thanks!
you need to trace whre you click/look, not where te center of the cam (object) is
Dude you’re using Oto as a model…
Oto is a highly sophisticated robot…he doesn’t walk like mere humans…he teleports because it’s way cooler!!
Try to use another model because Oto is just too awesome for a walk in the park…
…
…
Nha ok in fact the setLocalTranslation sets the position of a spatial.
If you want it to walk to a point you’ll have to use the setWalkDirection method of the character node toward the point’s direction.
play the walking animation …and stop once the model reached the point.
You’ll notice in TestWalkingChar the player.walkDirection(Vec3f) is being used in the update. That is what determines the direction(and speed) that the player is going to walk. What you’re doing with setLocalTranslation is exactly as you described, it just plops the player right where you tell it to.
I’d actually given this some thought for a project I’m working on. Basically you’re going to want to do this (skipping some steps and some refinement):
Player clicks on ground - get the point on the terrain/object where they clicked. I wouldn’t worry too much about the Y coordinate just yet as you’re probably more interested in the x,z coordinates at the moment.
Calculate a vector between the player’s current position and the position from above.
Normalize this result, and multiply it by the speed you want the player to travel. (Arbitrary, don’t do this if you don’t want to)
Set the walkDirection of the character to this vector.
Now, the tricky part (IMO) is knowing when to stop. That’s something I haven’t thought about just yet, but I’m guessing you just need to figure out when you’ve passed the point and then set the walkDirection to zero and setWorldTranslation/setLocalTranslation to the intended point.
In a much cleaner world, you’d accelerate and decelerate as you leave and stop, but that’s the refinement I was skipping =p
Here’s hoping that I’m right about at least part of this… I’ll stop blabbing now, heh.
Cheers,
~FlaH
[Edit] I’m slow. Slow like a sloth. waggles around on a tree
Guys, I know that I should use setWalkDirection, I’ll implement this later. The problem is when I’m using setLocalTranslation, it teleports them to completely unrelated coordinates, instead of the coordinates I was clicking on. .
@tehflah - Is there any way you upload your project (or at least the mouse movement interface code)?
Thanks!
Try this?
[java]
Vector3f origin = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);
direction.subtractLocal(origin).normalizeLocal();
Ray ray = new Ray(origin, direction);
CollisionResults results = new CollisionResults();
rootNode.collideWith(ray, results);
if (results.size() > 0) CollisionResult target = results.getClosestCollision();
[/java]
Thanks, working great!
Now, how can I implement something like this:
[java]character.walk(CollisionResult)[/java]
@tehflah’s idea was pretty good, I thought. Look at how walking is implemented in the example. Instead of waiting for WASD keypresses, you construct the walkdirection and walkrotation variables yourself, by adding a value corresponding to the direction (vector) to where the user clicked. But I haven’t tried it myself, so I don’t know…
Just get the contact point using CollisionResult.getContactPoint().
This tells you where in the world the mouse clicked, now just plot a course from the character to that point.
Why this doesn’t work?
[java]
boolean isWalking = false;
Vector3f walkDirection;
public void onAction(String binding, boolean value, float tpf) {
…
if (binding.equals(“Move”)) {
if (!value) {
Vector3f origin = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);
direction.subtractLocal(origin).normalizeLocal();
CollisionResults results = new CollisionResults();
rootNode.collideWith(new Ray(origin, direction), results);
if (results.size() > 0) {
isWalking = true;
walkDirection = results.getClosestCollision().getContactPoint();
}
}
}
}
@Override
public void update() {
if (isWalking) {
character.setWalkDirection(walkDirection);
isWalking = false;
}
}
[/java]
(didn’t think about the rotation calculation yet…)
setWalkDirection is continuous, once it’s set the character will go on walking for ever; so you don’t need to re use it in the update method.
To stop the character use setWalkDirection(Vector3F.ZERO)
@tehflah – Is there any way you upload your project (or at least the mouse movement interface code)?
heh. Sorry, as I said before, I'd only given this some thought. I haven't actually implemented it though, as I started realizing some annoying drawbacks to using this in my project - both in implementing it, and in it's actual usage getting in the way of my specific game's flow. As long as you're in a pretty open world setting, it should work fine though. I was pretty worried about pathfinding issues, but they'll surely crop up again to haunt me as soon as I start working some AI... But that's a long way down the road. ;p
But don't let any of that dissuade you. This is something that's been done before, and done many times well, so I'm sure that it's possible. I guess it would be a good idea to look at how movement responds in Runescape (the game you're emulating I'm assuming?), the Diablo games, maybe even RTS games with the way units move to points.
And you know, the more and more I think about it, I don't think I've ever seen acceleration/deceleration from point to point movement like this so you might not even need to worry about that. It'd be hella more responsive if you didn't even bother methinks, so you may want to ignore my blabbering about that altogether.
Cheers,
~FlaH
Changed to:
[java]Vector3f walkTarget;
public void onAction(String binding, boolean value, float tpf) {
…
if (binding.equals(“Move”)) {
if (!value) {
Vector3f origin = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);
direction.subtractLocal(origin).normalizeLocal();
CollisionResults results = new CollisionResults();
rootNode.collideWith(new Ray(origin, direction), results);
if (results.size() > 0) {
walkTarget = results.getClosestCollision().getContactPoint();
character.setWalkDirection(walkTarget);
}
}
}
}
@Override
public void update() {
if (walkTarget != null) {
if (character.getLocalTranslation().equals(walkTarget)) {
character.setWalkDirection(Vector3f.ZERO);
walkTarget = null;
}
}
}[/java]
still not working - it’s just starts going really fast and never ends up. If I change line 26 to if(true), it’s not going anywhere - means that this condition isn’t true. I also tried getWorldTranslation instead of getlocalTranslation, still not working. Also tried:
[java]Vector3f tmp = character.getLocalTranslation();
if (tmp.x == walkTarget.x && tmp.z == walkTarget.z) { … }[/java]
still not working.
UPDATE: also tried to normalize both vectors, not working either.
The contact point is a position, not a direction.
Could you be more specific please? Which methods should I use then?
Vector3f direction=targetLocation.subtract(myLocation);
Okay, changed it to:
[java]
isWalking = true;
walkTarget = results.getClosestCollision().getContactPoint();
character.setWalkDirection(walkTarget
.subtract(character.getLocalTranslation())
.mult(0.03f));[/java]
The last vector multiple is for slowing down the movement.
For stopping the character, in the update method, I do this:
[java]if (isWalking) {
Vector3f currentPosition = character.getLocalTranslation();
if ((int)walkTarget.getX() == (int)currentPosition.getX()
&& (int)walkTarget.getZ() == (int)currentPosition.getZ()) {
character.setWalkDirection(Vector3f.ZERO);
character.setLocalTranslation(currentPosition); // also tried without this line
isWalking = false;
walkTarget = null;
}
}[/java]
It works partially. There are few problems with this -
- Sometimes the character just doesn't stop.
- When I click on some really close position, it goes really slow. When I click on a position that is more far than the previous position, it goes really fast. It shouldn't be like that - speed should be constant.
- As you can see, for checking if the character should stop now, I compare his position and the target position, and if it equals, then stop. Now, I did this really stupidly: I'm converting the current X and the target X from float to int, and then comparing them. Same for Z. When I tried to just compare the floats, without converting them to int, it didn't work. Also, when I tried to compare the Y either, or just to compare the two vectors instead of comparing each components - it didn't work.
Any ideas how to fix this?
Hopefully others might have an idea for stopping, but this is why I thought stopping would be the hard part. -
Sometimes the character just doesn’t stop.
Is probably because there's no guarantee that your character is going to hit the exact point that you planned for it to stop, on that update tick. When it does stop = you got lucky and it was close enough to register as equal. When it doesn't stop = you were unlucky and the character's update didn't tick when the X, Z was approximately equal, so it just keeps going.
A way I could see doing it would be checking the distance from the walkTo location, and if the distance ever increases, stop. Because that would mean you're moving away from it = you passed it. This is sorta crude, but I can't think of another way off the top of my head.
Cheers,
~FlaH
Pretty simple for stop check if it si less than x away from the target (aka if it is nearer than 1/4 metere its good enough)
tehflah said:
Hopefully others might have an idea for stopping, but this is why I thought stopping would be the hard part. -
Is probably because there's no guarantee that your character is going to hit the exact point that you planned for it to stop, on that update tick. When it does stop = you got lucky and it was close enough to register as equal. When it doesn't stop = you were unlucky and the character's update didn't tick when the X, Z was approximately equal, so it just keeps going.
A way I could see doing it would be checking the distance from the walkTo location, and if the distance ever increases, stop. Because that would mean you're moving away from it = you passed it. This is sorta crude, but I can't think of another way off the top of my head.
Cheers,
~FlaH
How do I calculate it?
I guess something like:
[java]if ((int)walkTarget.getX() <= (int)currentPosition.getX()
&& (int)walkTarget.getZ() <= (int)currentPosition.getZ()) { ... } [/java]
won't work.
Thanks!!!