How do I detach BetterCharacterControl navigation from cam?

Hi,

I am trying to figure out how to navigate my character independently from the camera’s location. (I’m designing a strategy game where character navigation depends upon their own AI plus strategic commands rather than direct user input.) In every tutorial and forum post involving either BetterCharacterControl or CharacterControl, navigation either involves a first-person perspective or a third-person perspective where the character’s navigation is directly bound to the camera. I’ve figured out that I can obtain direction vectors (for the left and right directions) that are relative to my character’s orientation by calculating the cross product between the character’s forward and up or down vectors like this:

[java]private AnalogListener analogListener = new AnalogListener(){
public void onAnalog(String name, float value, float tpf){
if (isRunning) {
if (name.equals(“Right”)) {
Vector3f forward = player.getLocalTranslation();
// right direction = cross product between forward and up (forward cross up)
Vector3f upVector = new Vector3f(0,1,0);
Vector3f rightVector = forward.cross(upVector).normalize();
playerControl.setWalkDirection(rightVector);
//player.setLocalTranslation(v.x + valuespeed, v.y, v.z);
}
if (name.equals(“Left”)) {
Vector3f forward = player.getLocalTranslation();
// right direction = cross product between forward and up (forward cross up)
Vector3f downVector = new Vector3f(0,-1,0);
Vector3f leftVector = forward.cross(downVector).normalize();
playerControl.setWalkDirection(leftVector);
//Vector3f v = player.getLocalTranslation();
//player.setLocalTranslation(v.x - value
speed, v.y, v.z);
}
// . . .
[/java]

There are a few problems with this code, however.
First, this code allows me to strafe, which is useful, but I was really intending to rotate the character (about y) instead.
Second, my character continues to drift after the key is no longer depressed. In other words, I need a way to reset the standing position when I am done depressing the key. I would guess that this needs to be placed in the simpleUpdate method, but if I do that, I’m not sure how to correctly invoke the AnalogListener for the rotation methods (to get my character to turn left or right). I suppose the solution might be to use the boolean left and right bindings (such as hello_collision suggests), but when I do that, I can’t seem to get my character to move unless the flyCam is attached.

How do I actually get my character to rotate without being bound to the flyCam? This should not be that hard, but I just don’t understand how things are done in the jBullet world.

I also am not sure that I understand the difference between defining onAction from inside ActionListener and defining onAction without ActionListener (like the following)

[java]public void onAction(String binding, boolean isPressed, float tpf) {
if (binding.equals(“Left”)) {
left = isPressed;
} else if (binding.equals(“Right”)) {
right= isPressed;
} else if (binding.equals(“Up”)) {
up = isPressed;
} else if (binding.equals(“Down”)) {
down = isPressed;
} else if (binding.equals(“Jump”)) {
if (isPressed) { player.jump(); }
}
}[/java]

I realize that we are implementing the ActionListener interface to customize this method, but can someone please explain to me why we are not just using the default ActionListener for onAction? According to the link above, “Remember that this class implements the ActionListener interface, so you can customize the flyCam inputs. The ActionListener interface requires you to implement the onAction() method: You re-define the actions triggered by navigation key presses to work with physics.” Does this statement suggest that the custom onAction() method is used only to override the flyCam inputs? Am I still supposed to override this method in the case that I don’t want to override the flyCam inputs?

what about using actionlistener and not analog ? and inside you could have something like this:
[java]
if(name.equals(“Right”)) {
playerControl.setWalkDirection(isPressed ? rightVector : Vector3f.zero);
}
[/java]

or i do sometimes something like this:
in input handling i do

  1. applyWalkDirection(walkingVector)
  2. in charactercontrol update method (or physics update method, whatever you want) i grab this vector and then actually use setwalkdirection and then resets walkingVector. so it is reseted in charactercontrol in each step after it is used
1 Like

main difference between action and analog listeners are:
action is executed on key down and also on key up so you can set and reset something
analog is continuously executed while user keeps key pressed down and then does nothing when user releases key

1 Like

you can create more action/analog listeners at once, sou you can keep default and create another class with another listener and define othey keys there

@Ascaria, thank you for solving my drift problem! My character now stops sliding as soon as I stop pressing the button.

That solves 1/2 of the issue. The last bit relates to the character rotation. How do I cause my character to rotate rather than strafe?

Here’s the updated code, including a method for moving forward:

[java]
private void initKeys() {
inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_J));
inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_K));
inputManager.addMapping(“Run”, new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, “Left”, “Right”, “Run”);
}

// . . .

private ActionListener actionListener = new ActionListener(){
public void onAction(String name, boolean keyPressed, float tpf){
if (name.equals(“Run”)) {
Vector3f v = player.getLocalTranslation();
Vector3f forward = new Vector3f(0,0,.05f);
Vector3f actualForward = new Vector3f();
player.localToWorld(forward, actualForward);
if (!channel.getAnimationName().equals(“Walk”)) {
channel.setAnim(“Walk”, 0.50f);
channel.setLoopMode(LoopMode.Loop);
}
//player.setLocalTranslation(actualForward);
playerControl.setWalkDirection(keyPressed ? actualForward : Vector3f.ZERO);
}
if (name.equals(“Left”)) {
Vector3f forward = player.getLocalTranslation();
// right direction = cross product between forward and up (forward cross up)
Vector3f downVector = new Vector3f(0,-1,0);
Vector3f leftVector = forward.cross(downVector).normalize();
playerControl.setWalkDirection(keyPressed ? leftVector : Vector3f.ZERO);
//playerControl.setWalkDirection(leftVector);
//Vector3f v = player.getLocalTranslation();
//player.setLocalTranslation(v.x - valuespeed, v.y, v.z);
}
if (name.equals(“Right”)) {
Vector3f forward = player.getLocalTranslation();
// right direction = cross product between forward and up (forward cross up)
Vector3f upVector = new Vector3f(0,1,0);
Vector3f rightVector = forward.cross(upVector).normalize();
playerControl.setWalkDirection(keyPressed ? rightVector : Vector3f.ZERO);
//playerControl.setWalkDirection(rightVector);
//player.setLocalTranslation(v.x + value
speed, v.y, v.z);
}[/java]
Here are the remaining tasks:

  1. Figure out how to rotate the character.
  2. Figure out how to strafe or rotate while running. (At the current moment, the character can run or strafe but not perform both at the same time.

I figured out how to solve this problem. I will update the wiki documentation to make this clearer.