Issue with Bullet's applyCentralForce from a member variable

I’m not sure how to ask this in a tl;dr version, so I will do a short walkthough with what I am doing and my issue

For my spaceship game I’m using an entity-component design, one of the components is a regular python object:

class BulletPhysicsComponent(object):
    '''Contains reference to bullet shape and node'''
    bulletShape = None
    node = None
    nodePath = None
    currentThrust = 0

It is a client-server architecture. The client request what percent of thrust to use and the server calculates the actual force:

def setThrottle(self, shipid, data):
        if abs(data) > 100:
            print "Invalid"
            return
        ship = sandbox.entities[shipid]
        shipPhysics = ship.getComponent(shipComponents.BulletPhysicsComponent)
        shipThrust = ship.getComponent(shipComponents.MaxThrustComponent)
        shipPhysics.currentThrust = shipThrust.forward / 100.0 * data

I then have a process function which applies the force every tick:

def process(self, ship):
        shipPhysics = ship.getComponent(shipComponents.BulletPhysicsComponent)
        #shipPhysics.currentThrust = 3.9e+14
        print shipPhysics.currentThrust 
        shipPhysics.node.applyCentralForce(( shipPhysics.currentThrust, 0, 0))

The print statement prints the correct value, however the ship does not move. The ship does move when I manually set currentThrust in the process function.

Thanks for any help educating this poor sap.

Well, first let me mention that our Bullet integration is not (yet?) client/server capable. The Bullet nodes do not implement any serialisation stuff, so only the PandaNode part would be serialized. I would have expected random crashes if you try to transfer nodes between client and server.

Second, please ensure that you are really applying the exactly same argument as force.
Also try using Vec3(x,y,z) and not let the Python wrapper auto-convert tuples:

def process(self, ship):
  shipPhysics = ship.getComponent(shipComponents.BulletPhysicsComponent)
  shipPhysics.currentThrust = 3.9e+14
  print (shipPhysics.currentThrust,)
  print (shipPhysics.node.is_active())
  shipPhysics.node.applyCentralForce(Vec3(shipPhysics.currentThrust, 0, 0))
def process(self, ship):
  shipPhysics = ship.getComponent(shipComponents.BulletPhysicsComponent)
  print (shipPhysics.currentThrust,)
  print (shipPhysics.node.is_active())
  shipPhysics.node.applyCentralForce(Vec3(shipPhysics.currentThrust, 0, 0))

I’m doing the serialization myself (bless protobufs!).

What I found is that the node remains active with

shipPhysics.currentThrust = 3.9e+14
shipPhysics.node.applyCentralForce(Vec3(shipPhysics.currentThrust, 0, 0))

but is_active() returns false after about 800-900 frames. I’ve confirmed that I really am applying the exact argument as force.

Edit: It sounds like bullet will put object to sleep if they are inactive for a small time. I just added a simple check to manually node.setActive(True) if it returns false.

Everything works fine now! Thanks for your help.

Ok, so the node has been put to sleep. Applying a force won’t wake it up. You can call setActive(True), like you already mentioned. But if you have to wake up this particular node each frame then you should disable deactivation for this node completely:

np.node().setDeactivationEnabled(False)

Putting objects to sleep (de-activate them) is a feature found in most physics engines. There are two goal aimed for with sleeping:

  1. save some CPU time
  2. avoiding jitter if e.g. an object rests on some other object.
    So disabling sleeping for all objects is not recommended. Only disable it for those objects which get manipulated each frame anyway.