# Train moving along path

Hey!

I want a vehicle with at least two axes and a hull based an that axes to move along a certain path. I thought about a few possibilities to do this, but none of them seemed smartly to me so i wanted to ask if somebody has any suggestions to implement this.

I guess the problem is not so much placing the hull right between the 2 axes but to move them with a constant distance along the path. I thought about storing the positions of the first axes in a stack-like way and use them for the following axes but this brings up the problem that I have to calculate some time offset out of spatial offset.

Another idea was to follow the predecessor with the same constant (initial) distance. This should work great on straight paths but I donâ€™t really see this as a solution for curves.

http://i.imgur.com/3J2Zp.png

I donâ€™t really want to use a physics engine on that because it seems to be quite an overkill to me. Also Iâ€™m perfectly happy with my train just heading for the next way point (Vector3f)- no need for bezier curves and splines.

Thanks for any reply!

You are correct, the length will change.

The solution is going to be to position one point on the track (Iâ€™d probably go with the front one but either works) and then work from there to the other one.

Length between center points is r, points are front Pf and Pr rear.

That gives you have a circle of radius r centered on Pf. Formula is (x-Pfx)^2+(z-Pfz)^2=r^2

Pr is the point where that circle intersects the line of the track.

Line of the track is linear you just said (you will need to handle cases where it passes joins in the track by switching the line).

Formula of the line is : z = a+bx

So you are looking for the intersection of:

(x-Pfx)^2+(z-Pfz)^2=r^2

z = a+bx

Iâ€™ll let you take it from thereâ€¦you can either do the algebra and solve for the values or you could use an iterative approach using the position from the previous frame and just moving along accordingly.

1 Like

check out cinematics, they may have what you need.

Silly ideaâ€¦ but why not take an abstract approach!

Use the Billboard class and the picture in the original postâ€¦ with Bitmap text above it that says: IMAGE THIS.

Will save you a TON of time

Orâ€¦ if you donâ€™t care about curves, etc (like stated in the OP)â€¦ LookAt() + movement + distance checks against next waypoint should do it for ya. If you implement simple collision detection using ray casting + constant gravity, your ground doesnâ€™t have to be flat. Something like this for the way-point checksâ€¦

[java]

private void evalWaypoint() {

elapsedTime += current_zone.zone.TPF;

if (elapsedTime > 10) {

cCommandMoveFF move = new cCommandMoveFF();

move.setSubCommand(â€śMOVE_Fâ€ť);

move.setObjId(NPC.getId());

move.setObjType(â€śNPCSâ€ť);

move.setIsMovingF(true);

move.setCurrentDirMove(cObjNPC.DIR_RESET);

move.setAngles(NPC.getAngles());

move.setCurrentXYZ(new float[] { warpPoint.getX(), NPC.getCurrentXYZ()[1], warpPoint.getZ() }); //warpPoint.getY()

current_zone.notifyRoom(move);

elapsedTime = 0;

setNPCBusyStatus(false);

setNPCAction(ACTION.PASSIVE);

setNPCPathingStatus(PATHING.POI_GET);

// this.doFindTargetScript();

} else {

// update current coords //

float[] xyz = NPC.getCurrentXYZ().clone();

Vector3f cVector = new Vector3f(xyz[0],0,xyz[2]);

Vector3f wpVector = new Vector3f(nextNPCWaypoint[0],0,nextNPCWaypoint[2]);

if (cVector.distance(wpVector) <= 1f) {

elapsedTime = 0;

setNPCBusyStatus(false);

}

}

}

private float checkDistance() {

Vector3f nV = n.getLocalTranslation().clone();

nV.setY(0);

float[] tXYZ;

if (target.getClass() == cGameServerClient.class)

tXYZ = ((cGameServerClient)target).PC.getCurrentXYZ().clone();

else

tXYZ = ((cNPC)target).NPC.getCurrentXYZ().clone();

Vector3f tV = new Vector3f(tXYZ[0],0,tXYZ[2]);

return nV.distance(tV);

}

private void checkLOS() {

class losNode implements Callable {

Vector3f origin;

Vector3f dir;

Node node;

Node target;

public boolean executed = false;

boolean LOS = false;

public losNode(Node node, Node target, Vector3f origin, Vector3f dir) {

this.origin = origin;

this.dir = dir;

this.node = node;

this.target = target;

}

public Object call() {

Ray ray = new Ray();

ray.setOrigin(origin);

ray.setDirection(dir);

CollisionResults rayResults = new CollisionResults();

current_zone.zone.sceneGraph.get(cGameServerZone.NODE_COL).collideWith(ray, rayResults);

CollisionResult result = null;

for (int i = 0; i < rayResults.size(); i++) {

if (node.getChild(rayResults.getCollision(i).getGeometry().getName()) == null) {

// boolean isNPC = false;

// for (int n = 0; n < current_zone.zone.vNpcs.size(); n++) {

// Node xNPC = (Node)((ArrayList)current_zone.zone.vNpcs.get(n)).get(1);

// if (xNPC.getChild(rayResults.getCollision(i).getGeometry().getName()) == null) {

// isNPC = true;

// n = current_zone.zone.vNpcs.size();

// }

// }

// if (!isNPC) {

result = rayResults.getCollision(i);

i = rayResults.size();

// }

}

}

if (result != null) {

// System.out.println(result.getGeometry().getName());

if (target.getChild(result.getGeometry().getName()) != null)

this.LOS = true;

}

executed = true;

return null;

}

public boolean getHasLOS() {

return this.LOS;

}

}

this.hasLOS = false;

Vector3f xyz = n.getLocalTranslation().clone();

float[] tXYZ;

if (target.getClass() == cGameServerClient.class)

tXYZ = ((cGameServerClient)target).PC.getCurrentXYZ().clone();

else

tXYZ = ((cNPC)target).NPC.getCurrentXYZ().clone();

Vector3f nV = new Vector3f(xyz.getX(),xyz.getY(),xyz.getZ());

Vector3f tV = new Vector3f(tXYZ[0],tXYZ[1],tXYZ[2]);

float x = (tV.getX()-nV.getX())/FastMath.PI;

float y = (tV.getY()-nV.getY())/FastMath.PI;

float z = (tV.getZ()-nV.getZ())/FastMath.PI;

Vector3f dir = new Vector3f(x,y,z);

Node t = null;

if (target.getClass() == cGameServerClient.class) {

int tIndex = current_zone.zone.findObjIndexById(((cGameServerClient)target).PC.getId(),â€śPLAYERSâ€ť);

if (tIndex != -1)

t = current_zone.zone.players.get(tIndex).getCharNode().getCharRootNode();

} else {

int tIndex = current_zone.zone.findObjIndexById(((cNPC)target).NPC.getId(),â€śNPCSâ€ť);

if (tIndex != -1)

t = current_zone.zone.npcs.get(tIndex).getCharNode().getCharRootNode();

}

if (t != null) {

losNode task = new losNode(n, t, nV, dir);

current_zone.zone.enqueue(task);

while (!task.executed) { }

this.hasLOS = task.getHasLOS();

} else {

this.hasLOS = false;

}

}

private void setWayPoint(float X, float Y, float Z) {

// Set start coords //

nextNPCWaypoint = new float[] { X, Y, Z };

float[] xyz = NPC.getCurrentXYZ();

warpPoint = new Vector3f(xyz[0],xyz[1],xyz[2]);

}

private cZonePOI getTargetClosestPOI() {

float[] targetXYZ;

if (target.getClass() == cGameServerClient.class)

targetXYZ = ((cGameServerClient)target).PC.getCurrentXYZ().clone();

else

targetXYZ = ((cNPC)target).NPC.getCurrentXYZ().clone();

Vector3f tVector = new Vector3f(targetXYZ[0], targetXYZ[1], targetXYZ[2]);

cZonePOI tClosest = null;

float distance = 0;

for (int i = 0; i < current_zone.poiList.POIs.size(); i++) {

cZonePOI nPOI = (cZonePOI)current_zone.poiList.POIs.get(i);

float[] poiXYZ = nPOI.getPOIXYZ();

Vector3f poiVector = new Vector3f(poiXYZ[0], poiXYZ[1], poiXYZ[2]);

if (tClosest == null) {

tClosest = nPOI;

distance = tVector.distance(poiVector);

}

if (nPOI != tClosest) {

float checkDistance = tVector.distance(poiVector);

if (checkDistance < distance) {

tClosest = nPOI;

distance = checkDistance;

}

}

}

return tClosest;

}

private cZonePOI getNPCClosestPOI() {

float[] npcXYZ = this.NPC.getCurrentXYZ().clone();

Vector3f npcVector = new Vector3f(npcXYZ[0], npcXYZ[1], npcXYZ[2]);

cZonePOI npcClosest = null;

float distance = 0;

for (int i = 0; i < current_zone.poiList.POIs.size(); i++) {

cZonePOI nPOI = (cZonePOI)current_zone.poiList.POIs.get(i);

float[] poiXYZ = nPOI.getPOIXYZ();

Vector3f poiVector = new Vector3f(poiXYZ[0], poiXYZ[1], poiXYZ[2]);

if (npcClosest == null) {

npcClosest = nPOI;

distance = npcVector.distance(poiVector);

}

if (nPOI != npcClosest) {

float checkDistance = npcVector.distance(poiVector);

if (checkDistance < distance) {

npcClosest = nPOI;

distance = checkDistance;

}

}

}

return npcClosest;

}

private boolean validateNPCClosestPOI() {

boolean ret = true;

cZonePOI check = getNPCClosestPOI();

if (check != npcClosestPOI)

ret = false;

return ret;

}

private boolean validateTargetClosestPOI() {

boolean ret = true;

cZonePOI check = getTargetClosestPOI();

if (check != targetClosestPOI)

ret = false;

return ret;

}

// Default pathing //

public void getNextPathStep() {

float[] xyz = NPC.getCurrentXYZ().clone();

current_path_step++;

if (current_path_step == path.length)

current_path_step = 0;

setWayPoint(path[current_path_step][0],xyz[1],path[current_path_step][1]);

sendNPCMoveCommand();

setNPCBusyStatus(true);

}[/java]

This is the tidbit from the AI pathing I use for just following a default path. It is not complete, by any stretch of the imagination. It is not optimizedâ€¦ actuallyâ€¦ it is rather ugly, butâ€¦ it has everything you need to do what you want.

Thanks for all the replies, and sorry that it took me three days to answer but I wanted to figure out the posted solutions at first. Especially thanks to zarch whoâ€™s approach is what weâ€™ve finally done. We got our train driving along the specified path with all axes aligned to the path and all hulls aligned to their corresponding axes. Looks pretty cool.

I can post a short video if this produced interest.

Thanks a million!

@mduck said:
Thanks for all the replies, and sorry that it took me three days to answer but I wanted to figure out the posted solutions at first. Especially thanks to zarch who's approach is what we've finally done. We got our train driving along the specified path with all axes aligned to the path and all hulls aligned to their corresponding axes. Looks pretty cool. :D

I can post a short video if this produced interest.

Thanks a million!

Sorry about that... the "posted solution" wasn't a solution... it was all the needed info for doing linear waypoint evals. I probably should have clarified that.

Oh never mind, I guessed that right.

this is a old topic but i want to do make a train moving along a path, like in this topic.

but anyone know what is Billboard class?

Is it Billboard Control?

Thanks for your help.