On the steering of flying objects

Well I’ve been struggling for a while but I still can’t figure out how to get the wings to tilt when the flying object turns.

This video shows how the tank’s steering is currently meeting my expectations.

    @Override
    protected void controlUpdate(float tpf) {
        if (spatial != null) {
            if (i < move.size() - 1) {

                Vector3f currentPoint = spatial.getLocalTranslation();
                Vector3f nextPoint = move.get(i + 1);
                // Calculate the step of the node towards the next point
                Vector3f step = nextPoint.subtract(currentPoint).normalizeLocal().multLocal(tpf).multLocal(50f);

                Vector3f directionToTarget = nextPoint.subtract(currentPoint).normalizeLocal();
               
                Quaternion rotation=spatial.getLocalRotation();
                targetQua.lookAt(step, new Vector3f(0,1,0));
                rotation.nlerp(targetQua, 0.1f);
               float GoalAngle = Math.abs(spatial.getLocalRotation().dot(targetQua));
              // System.err.println("相似度:" + GoalAngle);

                 spatial.setLocalTranslation(currentPoint.add(step));
                // Check if you have reached the next point getLocalTranslation
                float distanceToNextPoint = spatial.getWorldTranslation().distance(nextPoint);
                // System.err.println(distanceToNextPoint);
                if (distanceToNextPoint < 0.2f ) {
                    //If the next point has been reached, update the current path index
                    i++;
                }

            }
        }
    }

I’m currently using this code to move.

Use this code to complete the turn

                Quaternion rotation=spatial.getLocalRotation();
                targetQua.lookAt(step, new Vector3f(0,1,0));
                rotation.nlerp(targetQua, 0.1f);

How do I tilt while steering and level when I’m done(Steer like an aeroplane.)?

//Please point out any confusion about my questions.

Up vector is 0,1,0 so of course up is always going to point exactly up. You could use a different up vector but probably you want to combine a roll rotation with this rotation.

For example:

// 45 degree roll
Quaternion roll = new Quaternion().fromAngles(0, 0, FastMath.QUARTER_PI);
rotation = rotation.mult(roll);
3 Likes

Thanks for the quick reply, I’m going to try it now

1 Like

Sorry for activating this thread again.


When I click on areas 1 and 4 the model doesn’t tilt and rotate like an aeroplane as I would

But if the destination is at 2 or 3 and the model is tilted (turned like an aeroplane) the way I want it to be

But I don’t know what happened, maybe the code itself is wrong knowledge coincidentally happened

If there is a better way to do these please let me know thanks a lot!

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package SelectUnit.Hominini;

import CameraAndMouse.RayMap;
import FindPath.FindPath;
import FindPath.Tools;
import static SelectUnit.Hominini.T100.i;
import SelectUnit.UnitControl;
import SelectUnit.UnitGlobalData;
import com.dongbat.walkable.FloatArray;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.collision.ContactListener;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.GhostControl;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mygame.Main;
import org.dyn4j.geometry.Vector2;
import physicsState.MinieBulletAppState;
import physicsState.dyn4j.BodyControl;
import scenario.NestedArrayList;
import scenario.scenarioState;
import static scenario.scenarioState.mapGrid3D;

/**
 *
 * @author icyboxs
 */
public class T100Control extends AbstractControl implements ContactListener, UnitControl,PhysicsCollisionListener {

    //停止距离
    private float STOPPING_DISTANCE = 2.5f;
    public SimpleApplication simpleApp;



    List<Vector3f> move = new ArrayList<Vector3f>();
    int imove = 0;
    private Vector3f moveTo = null;
    private float moveSpeed = 0.5f; // 设置移动速度
    Quaternion targetQua = new Quaternion();
    private Vector3f previousGridPosition=new Vector3f();
    private Vector3f spatialPreviousGridPosition = new Vector3f();
    private float length;
    private int Cellsize = (int) mapGrid3D.getCellsize();

    private int CurrentState = 0;
    private final int MoveState = 1;
    private final int WaitState = 2;
    private float AverageDistance=0;
    private int FieldOfView=128;//视野范围
    
    private ArrayList<Integer> VisionRange = new ArrayList<>();
    /**
     * @return the CurrentState
     */
    public int getCurrentState() {
        return CurrentState;
    }

    public T100Control(SimpleApplication app,Vector3f v3f,Spatial Role) {
        this.simpleApp = app;
        this.setSpatial(Role);
        this.spatial.setLocalScale(3f, 3f, 3f);
        this.spatial.setLocalTranslation(v3f);
        this.spatial.setUserData("CombatantsUnitNode", i);
        this.spatial.setUserData("Type", "CombatUnit");
        this.spatial.setShadowMode(RenderQueue.ShadowMode.Cast);
        this.spatial.setQueueBucket(RenderQueue.Bucket.Transparent);
        Node GhostPhysicsNode = new Node();
        GhostControl ghostControl = new GhostControl(new CapsuleCollisionShape(8f, 4f));
        MinieBulletAppState.bulletAppState.getPhysicsSpace().add(ghostControl);
        GhostPhysicsNode.addControl(ghostControl);
        GhostPhysicsNode.move(0, 0.9f, 0);
        ((Node)this.spatial).attachChild(GhostPhysicsNode);
    }

    public void setPath(FloatArray floatArray) {

        this.move.clear();
        time = 0;
        for (int i = 0; i < floatArray.size; i += 2) {
            float x = (float) floatArray.get(i);
            float y = 0.0f;
            float z = (float) floatArray.get(i + 1);
            Vector3f vector = new Vector3f(x, y, z);
            this.move.add(vector);
        }

    }

    public void setPath(FloatArray floatArray,float AverageDistance) {
        this.AverageDistance=AverageDistance;
        setPath(floatArray);
    }
    
    
    float FOWtime = 0;

    @Override
    protected void controlUpdate(float tpf) {
        if (spatial != null) {
           
            // if (imove < move.size() - 1) {
            if (!move.isEmpty()) {
                CurrentState = MoveState;
//                var s=spatial.getUserData("CombatantsUnitNode");
//                System.err.println(s);
                // 获取当前空间位置
                Vector3f currentWorldPosition = spatial.getLocalTranslation();
                // 将世界位置转换为网格坐标
                Vector3f currentGridPosition = new Vector3f((int) currentWorldPosition.x / Cellsize, 0, (int) currentWorldPosition.z / Cellsize);

                // 判断位置是否改变
               // this.FOWtime += tpf;

              //  if (this.FOWtime > 0.25) {
                    if (!currentGridPosition.equals(previousGridPosition)) {
                        // 位置已更改,执行相应的操作
                        // ...

                    getVisionRange().clear();
                    VisionRange =  scenarioState.setFowImag((int) spatial.getLocalTranslation().x, (int) spatial.getLocalTranslation().z, scenarioState.raster,FieldOfView);
//                simpleApp.enqueue(new Runnable() {
//                    @Override
//                    public void run() {
//                    getVisionRange().clear();
//                    //scenarioState.setFowImagAlphai((int)previousGridPosition.x*Cellsize, (int) previousGridPosition.z*Cellsize, scenarioState.raster);
//                    VisionRange =  scenarioState.setFowImag((int) spatial.getLocalTranslation().x, (int) spatial.getLocalTranslation().z, scenarioState.raster,FieldOfView);
//                    }
//                });
 
//                System.out.println((int)spatial.getLocalTranslation().x+","+(int) spatial.getLocalTranslation().z+"----spatial");
//                System.out.println(previousGridPosition.x*Cellsize+","+previousGridPosition.z*Cellsize+"----previousGridPosition");
                

                        // 更新前一网格坐标
                        previousGridPosition = currentGridPosition;

                    }
//                    this.FOWtime = 0;
//                }

                MoveWork(this.spatial, tpf);

            } else {
                CurrentState = WaitState;
            }

        }
    }

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

    @Override
    public void setSpatial(Spatial spatial) {
        super.setSpatial(spatial);
        MinieBulletAppState.bulletAppState.getPhysicsSpace().addContactListener(this);
        MinieBulletAppState.bulletAppState.getPhysicsSpace().addCollisionListener(this);

    }

    /**
     * 让物体按照寻路给出的坐标进行移动
     *
     * @param spatial
     * @param tpf
     */
    float time = 0;
    private float interp = 0.0f;
    
    protected void MoveWork(Spatial spatial, float tpf) {
        Vector3f currentPoint = spatial.getLocalTranslation();
        Vector3f nextPoint = this.move.get(0);
        // 计算节点朝下一个点移动的步进
        //float t = Math.min(tpf, maxT);
        Vector3f step = nextPoint.subtract(currentPoint).normalizeLocal().multLocal(tpf).multLocal(50f);
        
        //Vector3f step = currentPoint.interpolateLocal(nextPoint, 1.5f*tpf);
        
        step.setY(0);//设置高度
        //旋转
        Quaternion rotation = spatial.getLocalRotation();

//        Vector3f directionToTarget=nextPoint.subtract(currentPoint).normalize();
//        Vector3f up = Vector3f.UNIT_Z;
//        Vector3f crossProduct = Dir.cross(up);
        Vector3f directionToTarget=nextPoint.subtract(currentPoint).normalize();
        // 当前单位的朝向方向(假设朝向方向是Z轴方向)
        Vector3f forward = rotation.mult(Vector3f.UNIT_Z);
        // 计算叉积来判断目标在朝向的左侧还是右侧
        Vector3f crossProduct = forward.cross(directionToTarget);
        System.out.println(crossProduct);
        if (crossProduct.y < 0) {
            System.out.println("目标在单位的右边。");
            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(1, 0, 1));
            rotation.nlerp(targetQua, 10f*tpf);
        } else if (crossProduct.y > 0) {
            System.out.println("目标在单位的左边。");
            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(0, 0, -1));
//            rotation.nlerp(targetQua, 10f*tpf);
            rotation.slerp(targetQua, 1f*tpf);
        }
//        Vector3f Dir=nextPoint.subtract(currentPoint);
//        Vector3f up = Vector3f.UNIT_Z;
//        Vector3f crossProduct = Dir.cross(up);
//        // 判断象限
//        System.out.println(Dir);
//        if (Dir.x > 0  && Dir.z > 0) {
//            System.out.println("目标位于第一象限。");
//            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(1, 0, 0));
//            rotation.nlerp(targetQua, 1f*tpf);
//        } else if (Dir.x < 0 && Dir.z > 0) {
//            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(0, 0, 1));
//            rotation.nlerp(targetQua, 1f*tpf);
//            System.out.println("位于第二象限");
//        } else if (Dir.x < 0 &&  Dir.z < 0) {
//            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(-1, 0, 0));
//            rotation.nlerp(targetQua, 1f*tpf);
//            System.out.println("位于第三象限");
//        } else if (Dir.x > 0 &&  Dir.z < 0) {
//            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(0, 0, -1));
//            rotation.nlerp(targetQua, 1f*tpf);
//            System.out.println("位于第四象限");
//        } else {
//            System.out.println("目标 位于原点,或者太靠近原点而无法确定。");
//        }

//        targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(0, 1, 0));
//        rotation.nlerp(targetQua, 1f*tpf);
        //移动
        spatial.move(step);
    
        
        // 检查是否已经到达下一个点getLocalTranslation
        float distanceToNextPoint = spatial.getWorldTranslation().distance(nextPoint);
        

        
        //System.err.println(distanceToNextPoint);
        if (distanceToNextPoint < STOPPING_DISTANCE+(UnitGlobalData.getInstance().getGroup().size()*0.25f)) {
            // 如果已经到达下一个点,更新当前路径索引
            //imove++;
            this.move.remove(0);
  
        }

//        this.time += tpf;
//
//        if (this.time > 0.5) {
//            System.err.println(this.spatial.getLocalTranslation()+"1秒内");
//            
//            float distance = this.spatial.getWorldTranslation().distance(this.spatialPreviousGridPosition);
//            System.err.println(distance + ",distance123");
//            if (distance<3f && getCurrentState()==1) {
//                
//              System.err.println("停止移动");
//              this.move.clear();
//            } else {
//
//            }
//            
//            this.spatialPreviousGridPosition.set(this.spatial.getWorldTranslation()) ;
//            System.err.println(this.spatialPreviousGridPosition);
//            this.time = 0;
//        }
    }


    public void collision(T100Control controlA, T100Control controlB, Spatial objectAParent, Spatial objectBParent, Vector3f worldTranslationA, Vector3f worldTranslationB){
            if(controlB.getCurrentState() == 2 || controlA.getCurrentState() == 2){
        if (controlB.getCurrentState() == 2 && controlB == this) {
            Vector3f local = worldTranslationB.subtract(worldTranslationA).normalizeLocal();
            try {
                FloatArray path = FindPath.getInstance().PathFinding(worldTranslationB.x, worldTranslationB.z, worldTranslationB.add(local.mult(20f)).x, worldTranslationB.add(local.mult(20f)).z, 2);
                controlB.setPath(path);
            } catch (Exception e) {

            }
            if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                    controlA.move.clear();
                }
            }
        } else if (controlA.getCurrentState() == 2 && controlA == this) {

            Vector3f local = worldTranslationA.subtract(worldTranslationB).normalizeLocal();
            //((Spatial) pcoA.getUserObject()).getParent().setLocalTranslation(this.spatial.getWorldTranslation().add(local.setY(0)));
            try {
                FloatArray path = FindPath.getInstance().PathFinding(worldTranslationA.x, worldTranslationA.z, worldTranslationA.add(local.mult(20f)).x, worldTranslationA.add(local.mult(20f)).z, 2);
                controlA.setPath(path);
            } catch (Exception e) {

            }
            if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                    controlB.move.clear();
                }
            }
        }
}

//        if (controlA.getCurrentState() == 2 && controlA == this) {
//            Vector3f localB = worldTranslationA.subtract(worldTranslationB).normalizeLocal();
//            simpleApp.enqueue(new Runnable() {
//                @Override
//                public void run() {
//                    System.out.println(localB+",localB");
//                    objectAParent.move(localB.setY(0));
//                }
//            });
//        } else if (controlB.getCurrentState() == 2 && controlB == this) {
//            Vector3f localA = worldTranslationB.subtract(worldTranslationA).normalizeLocal();
//
//            simpleApp.enqueue(new Runnable() {
//                @Override
//                public void run() {
//                     System.out.println(localA+",localA");
//                    objectBParent.move(localA.setY(0));
//                }
//            });
//        }
 
            if (controlB.getCurrentState() == 1 && controlB == this) {
                Vector3f localB = worldTranslationA.subtract(worldTranslationB).normalizeLocal();
                simpleApp.enqueue(new Runnable() {
                    @Override
                    public void run() {
                        objectAParent.move(localB.setY(0));
                    }
                });
            } else if (controlA.getCurrentState() == 1 && controlA == this) {
                Vector3f localA = worldTranslationB.subtract(worldTranslationA).normalizeLocal();

                simpleApp.enqueue(new Runnable() {
                    @Override
                    public void run() {
                        objectBParent.move(localA.setY(0));
                    }
                });
            }

        }
    
    
    

    @Override
    public void onContactEnded(long manifoldId) {

    }

    @Override
    public void onContactProcessed(PhysicsCollisionObject pcoA, PhysicsCollisionObject pcoB, long manifoldPointId) {

        T100Control controlA = ((Spatial) pcoA.getUserObject()).getParent().getControl(T100Control.class);
        T100Control controlB = ((Spatial) pcoB.getUserObject()).getParent().getControl(T100Control.class);
        Spatial objectAParent = ((Spatial) pcoA.getUserObject()).getParent();
        Spatial objectBParent = ((Spatial) pcoB.getUserObject()).getParent();
        Vector3f worldTranslationA = objectAParent.getWorldTranslation();
        Vector3f worldTranslationB = objectBParent.getWorldTranslation();
        
        
        int PhysicsSpace = MinieBulletAppState.bulletAppState.getPhysicsSpace().pairTest(pcoA, pcoB, new PhysicsCollisionListener() {
            @Override
            public void collision(PhysicsCollisionEvent event) {
            }
        });
        
        if(PhysicsSpace>0){
        collision(controlA,controlB,objectAParent,objectBParent,worldTranslationA,worldTranslationB);
        }


    }

    @Override
    public void onContactStarted(long manifoldId) {

    }
   
    public void path(float AverageDistance,Vector3f average,RayMap rayMap, Vector3f RightClickMapLocation) {

        FloatArray path;
            try {

                UnitGlobalData.getInstance().addGroup(UnitGlobalData.getInstance().getSelectedUnit());
                Vector3f Nodeoffset = this.spatial.getWorldTranslation().subtract(average);
                float distance = average.distance(RightClickMapLocation);
                System.err.println(distance + ",distance");
                if (distance <= 30f) {
                    Nodeoffset.set(0, 0, 0);
                }

                if (average.distance(this.spatial.getWorldTranslation()) < AverageDistance * 1.8) {
                    path = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.add(Nodeoffset).x, RightClickMapLocation.add(Nodeoffset).z, 2);
                    this.setPath(path, AverageDistance);
                } else {
                    path = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.x, RightClickMapLocation.z, 2);
                    this.setPath(path, AverageDistance);
                }

                if (path.size <= 0) {
                    FloatArray path1 = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.x, RightClickMapLocation.z, 2);
                    this.setPath(path1, AverageDistance);
                    if (path1.size <= 0) {
                        Vector3f vectors = this.spatial.getWorldTranslation().subtract(RightClickMapLocation).normalizeLocal();
                        //findPath(i,vectors,RightClickMapLocation);
                        FloatArray path2 = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.add(vectors).x, RightClickMapLocation.add(vectors).z, 2);
                        int p = 0;

                        while (true) {
                            p++;
                            System.err.println(p);
                            if (path2.size <= 0) {
                                vectors.addLocal(vectors);
                                path2 = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.add(vectors).x, RightClickMapLocation.add(vectors).z, 2);
                                if (p > 500) {
                                    break; // 跳出循环
                                }

                            } else if (path2.size > 0) {
                                this.setPath(path2);
                                break; // 跳出循环
                            }

                        }

                    }
                }

            } catch (Exception e) {
                System.err.println(e);
            }

    }
    
    @Override
    public void sendRightClickCommand(float AverageDistances,Vector3f average,RayMap rayMap, Vector3f RightClickMapLocation) {
    path(AverageDistances,average,rayMap,RightClickMapLocation);
    }

    @Override
    public void collision(PhysicsCollisionEvent event) {

    }

    /**
     * @return the VisionRange
     */
    @Override
    public ArrayList getVisionRange() {
        return VisionRange;
    }



}

I use this part of the code to take care of the rotation and tilt

        //旋转
        Quaternion rotation = spatial.getLocalRotation();

//        Vector3f directionToTarget=nextPoint.subtract(currentPoint).normalize();
//        Vector3f up = Vector3f.UNIT_Z;
//        Vector3f crossProduct = Dir.cross(up);
        Vector3f directionToTarget=nextPoint.subtract(currentPoint).normalize();
        // Direction of orientation of the current unit (assuming that the direction of orientation is the Z-axis direction)
        Vector3f forward = rotation.mult(Vector3f.UNIT_Z);
        // Calculate the cross product to determine whether the target is on the left or right side of the orientation.
        Vector3f crossProduct = forward.cross(directionToTarget);
        System.out.println(crossProduct);
        if (crossProduct.y < 0) {
            System.out.println("The destination is to the right of the unit.");
            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(1, 0, 1));
            rotation.nlerp(targetQua, 10f*tpf);
        } else if (crossProduct.y > 0) {
            System.out.println("The destination is to the left of the unit.");
            targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(-1, 0, -1));
//            rotation.nlerp(targetQua, 10f*tpf);
            rotation.slerp(targetQua, 1f*tpf);
        }

This video is the effect I wanted to create

My English may not be very good, so if you're confused by the questions I'm asking, please point them out

dot product. Not cross product. For a cross product you would have to take the length instead of just randomly picking ‘y’.

dot product will be waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay easier.

And to decide to turn left or right you should dot the “direction to target” against the left vector (UNIT_X).

Vector3f left = rotation.mult(Vector3f.UNIT_X);
float howMuchLeft = left.dot(directionToTarget);

…positive numbers, turn left. negative numbers, turn right.

And I think your turning logic is odd, too.

For one thing, the “up vector” should be of length 1 and yours is not… which makes me think you don’t know what the up vector is for.

up vector: if you imagine you are looking in the direction the up vector would be out of the top of your head… or a close approximation. (Almost always UNIT_Y)

If you want it to tilt while rotating then add a roll to your final rotation. The video linked uses roll almost as an animation effect… it’s not really part of the object’s “rotation”.

For objects that are essentially 2D, it’s probably better to keep rotation as a ‘yaw’ value (y-axis rotation). From that you can derive your full rotation.

If your game objects keeps track of a yaw value then the code is easy with the dot product I provided above.

Vector3f left = rotation.mult(Vector3f.UNIT_X);
// Calculate how 'left' the target is -1 to 1, 1 is 90 degrees left, -1 is 90 degrees right, 0 is directly in front.
float howMuchLeft = left.dot(directionToTarget);
float rollAmount = 0;
float turnAmount = 0;
if( howMuchLeft !=  0 ) {
    turnAmount = howMuchLeft * turnSpeed;
    rollAmount = howMuchLeft;
    Vector3f forward = rotation.mult(Vector3f.UNIT_Z);
    // Calculate how much in front of us the object is -1 to 1, -1 is behind us, 1 is in front
    float howMuchForward = forward.dot(directionToTarget);
    if( howMuchForward < 0 ) {
        // object is behind us... so maximize turn
        if( turnAmount != 0 ) {
            turnAmount = FastMath.sign(turnAmount) * turnSpeed;
        } else {
            // Object is directly behind us so just pick a direction
            turnAmount = turnSpeed;
        }
    }
    yaw += turnAmount;
    // try to keep yaw between 0 and 2 PI
    if( yaw < 0 ) {
        yaw += FastMath.TWO_PI;
    } else if( yaw > FastMath.TWO_PI ) {
        yaw -= FastMath.TWO_PI;
    }
}
Quaternion newRotation = new Quaternion().fromAngles(0, yaw, rollAmount);

I may have some signs flipped somewhere because I wrote that from memory.

1 Like

Thank you for your reply

Thanks for the tip! :grinning:
I’ll give it a try.

Sorry for the slow reply.

Since I can’t figure out this part of the math anytime soon, I can’t understand the problem I’m having at the moment

I don’t know why the Tank model didn’t turn

As you can see from this video, the tank model, when moving in reverse, does exactly what I expect it to do, the tank tilts and rolls, and then heads towards the target point.
I need to have the front of the tank facing this target position.
The tank’s Z-axis seems to be facing the wrong way.


You can see on this video that sometimes the orientation is the opposite direction of the Z-axis and the blue arrow is the front of the model

targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(0, 1, 0));

Previously I used this code to rotate the model so that the Z-axis was always facing the target location
This code allows the tank to be oriented towards its destination

    protected void MoveWork(Spatial spatial, float tpf) {
        Vector3f currentPoint = spatial.getLocalTranslation();
        Vector3f nextPoint = this.move.get(0);
        // 计算节点朝下一个点移动的步进
        //float t = Math.min(tpf, maxT);
        Vector3f step = nextPoint.subtract(currentPoint).normalizeLocal().multLocal(tpf).multLocal(50f);
        
        //Vector3f step = currentPoint.interpolateLocal(nextPoint, 1.5f*tpf);
        
        step.setY(0);//设置高度
        //旋转
        Quaternion rotation = spatial.getLocalRotation();

        Vector3f directionToTarget = nextPoint.subtract(currentPoint).normalize();
        Vector3f left = rotation.mult(Vector3f.UNIT_X);
        float howMuchLeft = left.dot(directionToTarget);
        float rollAmount = 0;
        float turnAmount;
        float turnSpeed= 5f;
        float yaw=0;

        if (howMuchLeft > 0) {
            System.out.println("目标点的在左边");
        } else if (howMuchLeft < 0) {
            System.out.println("目标点的在右边");
        }
        
if( howMuchLeft !=  0 ) {
    turnAmount = howMuchLeft * turnSpeed;
    rollAmount = howMuchLeft;
    Vector3f forward = rotation.mult(Vector3f.UNIT_Z);
    // Calculate how much in front of us the object is -1 to 1, -1 is behind us, 1 is in front
    float howMuchForward = forward.dot(directionToTarget);
    if( howMuchForward < 0 ) {
        // object is behind us... so maximize turn
        if( turnAmount != 0 ) {
            turnAmount = FastMath.sign(turnAmount) * turnSpeed;
        } else {
            // Object is directly behind us so just pick a direction
            turnAmount = turnSpeed;
        }
    }
    yaw += turnAmount;
    // try to keep yaw between 0 and 2 PI
    if( yaw < 0 ) {
        yaw += FastMath.TWO_PI;
    } else if( yaw > FastMath.TWO_PI ) {
        yaw -= FastMath.TWO_PI;
    }
}

//// 平滑生成新的旋转四元数
Quaternion newRotation = new Quaternion().fromAngles(0, yaw, -rollAmount);
rotation.nlerp(newRotation, tpf * 5f);  // 平滑旋转过渡
//    targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(0, 1, 0));
//    rotation.nlerp(targetQua, 5f*tpf);

        
        
        //移动
        spatial.move(step);
    
        
        // 检查是否已经到达下一个点getLocalTranslation
        float distanceToNextPoint = spatial.getWorldTranslation().distance(nextPoint);
        

        
        //System.err.println(distanceToNextPoint);
        if (distanceToNextPoint < STOPPING_DISTANCE+(UnitGlobalData.getInstance().getGroup().size()*0.25f)) {
            // 如果已经到达下一个点,更新当前路径索引
            //imove++;
            this.move.remove(0);
  
        }

I’m not sure where the problem is, but the Z-axis is not facing the target position while moving. Ideally, every time the ship changes direction, it should tilt and rotate until it is directly facing the target position, and then level out its attitude.

Your entire life will be easier if you make sure that your tank looks down the Z-axis.

If you can’t edit the model itself to face the correct direction then add the tank to a node, rotate the tank by 180 degrees, then use the node instead of the tank.

Spatial tank = tank loaded from somewhere
Node node = new Node();
node.attachChild(tank);
tank.rotate(0, FastMath.PI, 0);
tank = node;
1 Like

I also don’t know why the tank model didn’t turn.

You don’t have to understand the math to debug this problem. Just print out the values you get for howMuchLeft and howMuchForward. One is the distance left the other is the distance forward.

1 Like