[FIXED] Moving plattforms (elevators) - theories

Hi there again.



I searched the forum to get some answer for my problem, but I found nothing (what surprised me), so I decided to open this thread.



I am doing some kind of Jump'n Run and need to create moving plattforms like elevators. I realized that using StaticPhysicNodes which I translate would be a bad idea so I think I have to use DynamicPhysicNodes. Getting rid of the gravity is not the problem, but how do I ignore torques and forces that are applied to my plattform by a collision with another DynamicPhysicNode?



What I mean is a situation like this:

A plattform moves around, then a player (or any other object) jumps/falls on it. Now the plattform should move on, the things that fell onto it should bounce off or just do proper physical things, the plattform itself should not be moved by the impact of the collision.



I know the solution would be in using a ContactCallback. My first approach was to set the force of my plattform-node to zero, but this would cause the plattform to stop moving (I had the same problem with my player-character, which shall be hit by things but should itself not be affected by the collision). I think I could remember the forces and set them back after clearing them, but I think this would be some kind of dirty approach.



Is there any other clean or nice way to do this?

Dynamic node attached to the world using a slider joint (one translational axis). You can use a joint motor with high torque and low velocity to move it along the axis.



Joint joint = getPhysicsSpace().createJoint();

// Attach it to the world
joint.attach( dynamicNode );

// We want to allow it to slide up and down
JointAxis axis = joint.createTranslationalAxis();
axis.setDirection( new Vector3f( 0, 1, 0 ) );

// Set a high amount of force to be used to move the platform
axis.setAvailableAcceleration(10000.0f);

// Set the speed and direction of the platform movement
axis.setDesiredVelocity(0.1f);



Now I would assume the you want your platform to cycle up and down instead of moving up into infinity. This could be accomplished by writing a controller for your platform that checks the translation if it along its axis. If it reaches a certain extent, then reverse the desired velocity.

Pseudo code controller...


JointAxis axis;

update(float tpf) {

    if(axis.getPosition() > 5.0f) {
        axis.setDesiredVelocity(-0.1f);
    }
    else if(axis.getPosition() < -5.0f) {
        axis.setDesiredVelocity(0.1f);
    }
}

Hey! Thank you very much! I'll try that today.



Joints… sure there was something I didn't thought of…   :roll:



EDIT: Yeah! Works!

The controller-thing I will not use, though. I am going to put kind of waypoints into my world which set the velocities when a plattform collides with them. But thanks for the advice anyway!

The controller-thing I will not use, though. I am going to put kind of waypoints into my world which set the velocities when a plattform collides with them. But thanks for the advice anyway!


Yeah, this produces the next problem. A strange one...

This is how my moving-plattform-thing works:
I have waypoints that actually are boxes. If a dynamic node is in the same group of the waypoint, it reacts on the collision by being attached to the appropriate joint and start moving. So I am able to direct my plattforms through the level. This works quite fine and I am very happy about this, BUT right in the moment a plattform hits a waypoint, physics seem to stop working. Boxes I placed on StaticPhysicNodes fall through them, collision seems to be disabled at all!

Why???  :? I really would appreciate some help here because I do not have any clue.

EDIT: Well, I have one, I've written something about it at the end.

Here's the code which handles the whole thing. This is a ContactCallback I wrote for handling the collision between a plattform (called "dynamic") and a waypoint (called "control node"):

       //Callback for moving Plattforms
       ContactCallback plattformCallback = new ContactCallback() {
          @Override
          public boolean adjustContact(PendingContact contact) {
             
             Node conNod = null;
             Node platNod = null;             
                
                if (contact.getNode1().getName().equals("control node")) {
                    conNod = contact.getNode1();
                    contact.setIgnored(true);
                 }
                 else if (contact.getNode2().getName().equals("control node")) {
                    conNod = contact.getNode2();                    
                    contact.setIgnored(true);
                 }
                           
                 if (contact.getNode1().getName().equals("dynamic"))
                    platNod = contact.getNode1();
                 else if (contact.getNode2().getName().equals("dynamic"))
                    platNod = contact.getNode2();
                 
                 if (conNod != null && platNod != null) {

                    if (contact.getNode1().getParent() == contact.getNode2().getParent() &&
                        contact.getNode1().getParent().getName().equals("group") &&
                        contact.getNode2().getParent().getName().equals("group")) {

                 
                       if (platNod instanceof DynamicPhysicsNode && conNod instanceof DynamicPhysicsNode) {
                          DynamicPhysicsNode platNodDyn = (DynamicPhysicsNode) platNod;
                          DynamicPhysicsNode conNodDyn = (DynamicPhysicsNode) conNod;
                          PlattformCollisionData newData = new PlattformCollisionData(platNodDyn, conNodDyn);
                          if (!platColData.contains(newData)) {
                             platColData.add(newData);
                             return true;
                          }
                       }
                    }                
                 }
                 if (contact.isIgnored())
                    return true;
                 else
                    return false;
             }          
          };



As you see, I remember the two involved nodes, the waypoint and the plattform and save them into a class called PlattformCollisionData which only is a dataholder for these two things. I put this in an ArrayList and go through it in the update method. Here is the part that deals with that:


private void checkPlattforms() {
      //only do something interesting if there is data to work with
      if (platColData.size() > 0) {
         for (PlattformCollisionData platDat : platColData) {   
            platDat.getPlatNode().setAffectedByGravity(false);
            platDat.getConNode().clearDynamics();
            platDat.getPlatNode().clearDynamics();
            for (Joint joint : joints) {
               //this is for identifying the joint and if it fits to the DynamicPhysicsNode:
               if (joint.getName().equals(platDat.getConNode().getLocalRotation()
                     .getRotationColumn(1).toString())) {
                  joint.attach(platDat.getPlatNode());
                  joint.setActive(true);
                  for (Joint joint2 : joints) {
                     if (joint2 != joint) {
                        //detach the node from every other joint to prohibit strange things...
                        if (joint2.getNodes().contains(platDat.getPlatNode())) {
                           ArrayList<DynamicPhysicsNode> l = new ArrayList<DynamicPhysicsNode>();
                           for (DynamicPhysicsNode n : joint2.getNodes()) {
                              l.add(n);
                           }
                           joint2.detach();
                           l.remove(platDat.getPlatNode());
                           for (DynamicPhysicsNode n : l) {
                              joint2.attach(n);
                           }                           
                        }
                     }
                  }
               }
            }
         }
         //reset plattformcollisiondata array for fresh use in next cycle
         platColData = new ArrayList<PlattformCollisionData>();         
      }
      //set every joint inactive that has no nodes attached to it
      for (Joint joint : joints) {
         if (joint.getNodes().size() == 0) {
            joint.setActive(false);
         }
      }


   }



I would thank everyone who risk a look at this code. The problem probably is in the

contact.setIgnored(true)


part in the ContactCallback. I realized that the contact is fired and is ignored when a plattform touches a waypoint. This is how I wanted it to be. I wanted waypoints to be ignored by physics (nearly) at all. But it seems to also work for every other contact that occurs at the same time, too!

So, now more precisely: Can I reduce the contact-ignore(ation? -> english != mother language...) to only THIS SINGLE contact? Or is the problem a very different one?

GOT IT! Hooo-Raahhh!!!





Read Javadocs, Jens, read Javadocs! I returned trues and falses too simple minded. If I understood it right, a Callback should return a false when it shall go on with other callbacks (which are only for setting the details of contacts in this case). Otherwise, it stops with the callbacks and applies the details to the contacts (at all?)… Something like that.



Now I removed


if (contact.isIgnored())
    return true;
else



and it worked! So stupid I am, little JME-Padawan....