(SOLVED) How to handle it correctly orientation

Using setViewDirection can change direction
Using setWalkDirection can move

but On the move and swerve ,They’re all going on at the same time
The movement and swerve of the model is very not smoothness
How to correctly turn first in moving

This question is translated by translator
If you are willing to answer my question but don’t understand it, please point out the sentences you don’t understand
thanks:wink:

package net.jmecn;

import com.jme3.scene.debug.Arrow;
import com.jme3.input.ChaseCamera;
import com.jme3.anim.AnimComposer;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.collision.shapes.HullCollisionShape;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.scene.CameraNode;
/**
 * 按键发射小球轰击砖墙。
 * 
 * @author yanmaoyuan
 *
 */
public class HelloPhysics extends SimpleApplication implements ActionListener {

    /**
     * 开火,发射小球。鼠标左键触发。
     */
    public final static String FIRE = "fire";
    public final static String FORWARD = "forward";
    public final static String BACKWARD = "backward";
    public final static String LEFT = "left";
    public final static String RIGHT = "right";
    public final static String JUMP = "jump";
    //方向控制器
    private CharacterControl player;
    private Vector3f walkDirection = new Vector3f();
    private Vector3f walkDirectioni = new Vector3f();
     private boolean left = false, right = false, up = false, down = false;
    /**
     * 显示或隐藏BulletAppState的debug形状。按空格键触发。
     */
    public final static String DEBUG = "debug";
 // 临时变量,用于保存摄像机的方向。避免在simpleUpdate中重复创建对象。
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();
    /** 砖块的尺寸 */
    private static final float brickLength = 0.48f;
    private static final float brickWidth = 0.24f;
    private static final float brickHeight = 0.12f;
    private AnimComposer control;
    private BulletAppState bulletAppState;
    private Node character;
    private ChaseCamera chaseCam;
 
    @Override
    public void simpleInitApp() {

        createArrow(new Vector3f(5, 0, 0), ColorRGBA.Green);
        createArrow(new Vector3f(0, 5, 0), ColorRGBA.Red);
        createArrow(new Vector3f(0, 0, 5), ColorRGBA.Blue);
        
//        cam.setLocation(new Vector3f(0, 4f, 6f));
//        cam.lookAt(new Vector3f(2, 2, 0), Vector3f.UNIT_Y);

        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        bulletAppState.setDebugEnabled(true);
        // 初始化按键
        initKeys();

        // 初始化光照
        initLight();

        // 初始化场景
        initScene();
    }
 /**
     * 创建一个箭头
     * 
     * @param vec3  箭头向量
     * @param color 箭头颜色
     */
    private void createArrow(Vector3f vec3, ColorRGBA color) {
        // 创建材质,设定箭头的颜色
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", color);

        // 创建几何物体,应用箭头网格。
        Geometry geom = new Geometry("arrow", new Arrow(vec3));
        geom.setMaterial(mat);

        // 添加到场景中
        rootNode.attachChild(geom);
    }
    @Override
    public void onAction(String name, boolean isPressed, float tpf) {
//        if (isPressed) {
//            if (FIRE.equals(name)) {
//                shootBall();
//            } else if (DEBUG.equals(name)) {
//                boolean debugEnabled = bulletAppState.isDebugEnabled();
//                bulletAppState.setDebugEnabled(!debugEnabled);
//            }
//        }
 if (DEBUG.equals(name) && isPressed) {
            boolean debugEnabled = bulletAppState.isDebugEnabled();
            bulletAppState.setDebugEnabled(!debugEnabled);
        } else if (LEFT.equals(name)) {
            left = isPressed;
        } else if (RIGHT.equals(name)) {
            right = isPressed;
        } else if (FORWARD.equals(name)) {
            up = isPressed;
        } else if (BACKWARD.equals(name)) {
            down = isPressed;
        } else if (JUMP.equals(name) && isPressed) {
            player.jump();
        }
    }

    /**
     * 初始化按键输入
     */
    private void initKeys() {
        inputManager.addMapping(FIRE, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addMapping(DEBUG, new KeyTrigger(KeyInput.KEY_F1));
        inputManager.addMapping(LEFT, new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping(RIGHT, new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping(FORWARD, new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping(BACKWARD, new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping(JUMP, new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(this, FIRE, DEBUG,LEFT,RIGHT,FORWARD,BACKWARD,JUMP);
        
        
    }

    /**
     * 初始化光照
     */
    private void initLight() {
        // 环境光
        AmbientLight ambient = new AmbientLight();
        ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1f));

        // 阳光
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(-1, -2, -3).normalizeLocal());

        rootNode.addLight(ambient);
        rootNode.addLight(sun);
    }

    /**
     * 初始化场景
     */
    private void initScene() {
            model();
        makeFloor();
        //makeWall();
   
        
    }

    /**
     * 制作地板
     */
    private void makeFloor() {
        // 网格
        float radius = 100f;// 胶囊半径0.3米
        float height = 0.1f;// 胶囊身高1.8米
        float stepHeight = 100f;// 角色步高0.5米
        Box floor = new Box(radius, height, stepHeight);
        floor.scaleTextureCoordinates(new Vector2f(3, 6));

        // 材质
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture tex = assetManager.loadTexture("Interface/pic.png");
        tex.setWrap(WrapMode.Repeat);
        mat.setTexture("ColorMap", tex);

        // 几何体
        Geometry geom = new Geometry("floor", floor);
        geom.setMaterial(mat);
        geom.setLocalTranslation(0, -1.8f, 0);// 将地板下移一定距离,让表面和xoz平面重合。

        // 刚体
        RigidBodyControl rigidBody = new RigidBodyControl(0);
        geom.addControl(rigidBody);
        rigidBody.setCollisionShape(new BoxCollisionShape(new Vector3f(radius, height, stepHeight)));

        rootNode.attachChild(geom);
        bulletAppState.getPhysicsSpace().add(rigidBody);
    }

    /**
     * 建造一堵墙
     */
    private void makeWall() {
        // 利用for循环生成一堵由众多砖块组成的墙体。
        float startpt = brickLength / 4;
        float height = 0;
        for (int j = 0; j < 15; j++) {
            for (int i = 0; i < 6; i++) {
                Vector3f vt = new Vector3f(i * brickLength * 2 + startpt, brickHeight + height, 0);
                makeBrick(vt);
            }
            startpt = -startpt;
            height += 2 * brickHeight;
        }
    }
    
        private void model() {
        float radius = 0.5f;// 胶囊半径0.3米
        float height = 3.3f;// 胶囊身高1.8米
        float stepHeight = 0.5f;// 角色步高0.5米
            //模型
       character = new Node("Character");
            Node  model =  (Node)assetManager.loadModel("Textures/jisi/Test/hecheng.j3o");
            model.move(0, -(height/2+radius), 0);
            //model.scale(1.8f);
    
            character.attachChild(model);// 挂到角色根节点下
            rootNode.attachChild(character);
            control = model.getControl(AnimComposer.class);
            control.setCurrentAction("walk");
            control.setGlobalSpeed(2f);
            
//          Geometry submesh = (Geometry) model.getChild("ç½‘æ ¼.003_4");
//          HullCollisionShape hullCollisionShape = new HullCollisionShape(submesh.getMesh());
//          //刚体
//          RigidBodyControl rigidBody = new RigidBodyControl(hullCollisionShape,0f);
//          rigidBody.setCollisionShape(new BoxCollisionShape(new Vector3f(10f, 0.1f, 5f)));
//          bulletAppState.getPhysicsSpace().add(rigidBody);
        // 使用胶囊体作为玩家的碰撞形状
        CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(radius, height, 1);
        
        
        // 使用CharacterControl来控制玩家物体
        player = new CharacterControl(capsuleShape, stepHeight);
        player.setJumpSpeed(10);// 起跳速度
        player.setFallSpeed(55);// 坠落速度
        player.setGravity(9.8f * 3);// 重力加速度
        player.setPhysicsLocation(new Vector3f(10f, 10f, 0));// 位置
        character.addControl(player);
        
        bulletAppState.getPhysicsSpace().add(player);
        
                // Disable the default flyby cam
        // Disable the default flyby cam
        flyCam.setEnabled(false);
        // Enable a chase cam for this target (typically the player).
        chaseCam = new ChaseCamera(cam, model, inputManager);
          System.err.println(cam.getLocation());
        chaseCam.setSmoothMotion(false);
        chaseCam.setTrailingEnabled(true);

        
        // Disable the default flyby cam
//        flyCam.setEnabled(false);
//        //create the camera Node
//        CameraNode camNode = new CameraNode("CamNode", cam);  
//        //This mode means that camera copies the movements of the target:
//        camNode.setControlDir(ControlDirection.SpatialToCamera);
//        //Attach the camNode to the target:
//        character.attachChild(camNode);
//        //Move camNode, e.g. behind and above the target:
//        camNode.setLocalTranslation(new Vector3f(10, 5, -5));
//        //Rotate the camNode to look at the target:
//        camNode.lookAt(model.getLocalTranslation(), Vector3f.UNIT_Y);
        }
        
    @Override
    public void simpleUpdate(float tpf) {
        camDir.set(cam.getDirection()).multLocal(0.7f);
        camLeft.set(cam.getLeft()).multLocal(0.3f);
        walkDirection.set(0, 0, 0);

        // 计算运动方向
        boolean changed = false;
        if (left) {
            walkDirectioni.addLocal(camLeft);
         
            walkDirection.addLocal(camLeft);
            
            changed = true;
        }
        if (right) {
            walkDirectioni.addLocal(camLeft.negate());
           
            walkDirection.addLocal(camLeft.negate());
           
            changed = true;
        }
        if (up) {
            walkDirectioni.addLocal(camDir);
        
            walkDirection.addLocal(camDir);
      
            changed = true;
        }
        if (down) {
            walkDirectioni.addLocal(camDir.negate());
        
            walkDirection.addLocal(camDir.negate());
            
            changed = true;
        }
        if (changed) {
            walkDirection.y = 0;// 将行走速度的方向限制在水平面上。
            walkDirection.normalizeLocal();// 单位化
            walkDirection.multLocal(0.3f);// 改变速率
            walkDirectioni.y = 0;// 将行走速度的方向限制在水平面上。
            walkDirectioni.normalizeLocal();// 单位化
            walkDirectioni.multLocal(10f);// 改变速率
        }
      // System.err.println(walkDirectioni);
       player.setWalkDirection(walkDirection);
       player.setViewDirection(walkDirectioni);
       // cam.setLocation(player.getPhysicsLocation());
    }
    
    
    /**
     * 在指定位置放置一个物理砖块
     * 
     * @param loc
     *            砖块的位置
     */
    private void makeBrick(Vector3f loc) {
        // 网格
        Box box = new Box(brickLength, brickHeight, brickWidth);
        box.scaleTextureCoordinates(new Vector2f(1f, .5f));

        // 材质
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture tex = assetManager.loadTexture("Interface/pic.png");
        mat.setTexture("ColorMap", tex);

        // 几何体
        Geometry geom = new Geometry("brick", box);
        geom.setMaterial(mat);
        geom.setLocalTranslation(loc);// 把砖块放在指定位置

        // 刚体
        
        RigidBodyControl rigidBody = new RigidBodyControl(2f);
        geom.addControl(rigidBody);
        rigidBody.setCollisionShape(new BoxCollisionShape(new Vector3f(brickLength, brickHeight, brickWidth)));

        rootNode.attachChild(geom);
        bulletAppState.getPhysicsSpace().add(rigidBody);
    }

    /**
     * 从摄像机所在位置发射一个小球,初速度方向与摄像机方向一致。
     */
    private void shootBall() {
        // 网格
        Sphere sphere = new Sphere(32, 32, 0.4f, true, false);
        sphere.setTextureMode(TextureMode.Projected);

        // 材质
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture tex = assetManager.loadTexture("Interface/pic.png");
        mat.setTexture("ColorMap", tex);

        // 几何体
        Geometry geom = new Geometry("cannon ball", sphere);
        geom.setMaterial(mat);

        // 刚体
        RigidBodyControl rigidBody = new RigidBodyControl(1f);
        geom.addControl(rigidBody);
        rigidBody.setCollisionShape(new SphereCollisionShape(0.5f));
        rigidBody.setPhysicsLocation(cam.getLocation());// 位置
        rigidBody.setLinearVelocity(cam.getDirection().mult(50));// 初速度
      
        rootNode.attachChild(geom);
        bulletAppState.getPhysicsSpace().add(rigidBody);
    }

    public static void main(String[] args) {
        HelloPhysics app = new HelloPhysics();
        app.start();
    }
}

i dont understand this one, what is “they” here, and what is happening in same time?

I think i understand this one, but what exactly is not smooth? camera, or model movement? i mean do you have camera connected to model?

You can also check test-examples to compare here:

here example Video of issue would be best to understand what happends exactly.

If you are having trouble translating your question then one approach would be to provide images or videos of what you mean.

For example: video of what you have. example video online of someone else doing what you want.

1 Like

Thanks for your suggestion.

video
This is video

The vector of the model is opposite to the vector of the motion for a moment
I want to make the model turn and move smooth what’s the right thing to do first turn then move

I’m studing that how to create a simple game under bullet engine. I wroted a tiny demo about movement of game object, that controls an object to move forward, backward, turns left and turns right. And a problem occurred, sometime the game object that i try to control will turns to opposite direction. I had spent, many hours to fonding what is wrong with my codes. Any suggestions will appreciated.

Have you tried using Spatial#lookAt(Vector3f, Vector3f) method ?

Thanks for your reply. I’ll give it a try

1 Like

I wonder where I can find the testWalkingchar.java material file

感谢你的帮助问题已经解决了

1 Like

In the jme3-testdata module, you have to add it as a dependency.

1 Like

It sounds like you want to make the character rotate smoothly to match the walk direction.

If so, then you would also need to gradually slerp or interpolate from the current view dierection to the view direction matching the walk direction every frame in the update loop.

1 Like

Yeah, that’s exactly what I’m asking
According to your answer, is there any relevant literature?
Thank you for your answer

1 Like

I am not sure if there are any examples on how to do that with the CharacterControl you’re using, but here is how I do it for a standard spatial (I’m sure this code can still be improved, I don’t think I’m calculating the value to slerp with using tpf the best way possible, but it does work and makes the rotation smooth when the view direction is changed).

public Quaternion currentViewQuaternion;
public Quaternion finalViewQuaternion;

public void update(float tpf){

       currentViewQuaternion = chraracterNode.getWorldRotation();

       float slerpAmt = 1-(tpf*rotateSpeed));
       if(slerpAmt < 0){
            slerpAmt = 0.0001f;
        }else if(slerpAmt > 1.0f){
             slerpAmt = 1.0f;
        }
     
       currentViewQuaternion.slerp(finalViewQuaternion, slerpAmt);

       chraracterNode.setLocalRotation(currentViewQuaternion);
}

public void setViewDirection(Vector3f direction){
        finalViewQuaternion.lookAt(direction, Vector3f.UNIT_Y); 
}

I think this should work with the CharacterContol if you can adjust the code slightly and convert the slerped Quaternion back into a vector3f to input into the setViewDirection() method of the CharacterControl.

3 Likes

Thank you very much for your help

1 Like

Thank you for your reply. My problem is basically solved

3 Likes