Here is some code to add impulse (acceleration) to an object . Note, the longer you hold down the key, the longer the force is applied to the object. Therefore, the acceleration will be greater. Its like turning rockets on. The object will not stop until acted on by an equal but opposite impulse. In other words, if you turn on the forward rockets for 10 seconds you will need to turn on the back rockets with one burst for 10 seconds or two bursts at 5 seconds / burst to cancel out the impulses.
Impulse = Force X time = mass X change in velocity
As long as the rockets are on, the velocity with increase. Turn the rockets off and the velocity with remain the same, but not decrease. Note: To get the position, I had to use
self.cubeBoy.getY(self.plnp)
which is the position of the cube relative to a point light I attached to the render node at 0,0,0.
Using
self.cubeBoy.getPos()
always returned (0,0,0)
from direct.directbase.DirectStart import *
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.task.Task import Task
from direct.gui.OnscreenText import OnscreenText
import sys, math, random
# Function to put title on the screen.
def addTitle(text):
return OnscreenText(text=text, style=1, fg=(1,1,1,1),
pos=(1.0,0.8), align=TextNode.ARight, scale = .08)
# Function to put instructions on the screen.
def addInstructions(pos, msg):
return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
pos=(-1.23, pos), align=TextNode.ALeft, scale = .07)
# Function to put output on the screen.
def addOutput(pos, msg):
return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
pos=(-1.1, pos), align=TextNode.ALeft, scale = .06)
# Function to put information to the screen.
def addInfo (msg):
return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
pos=(0, -0.8), align=TextNode.ACenter, scale = .06)
class World (DirectObject):
def __init__(self):
base.win.setClearColor(Vec4(0,0,0,1))
base.setBackgroundColor(0,0,0,1)
# Post the instructions
self.title = addTitle("Impulse")
self.inst1 = addInstructions(0.85, "[ESC]: Quit")
self.inst2 = addInstructions(0.75, "[W]: Applies +Y force")
self.inst3 = addInstructions(0.65, "[E]: Applies -Y force")
self.inst4 = addInfo ("The force is applied as long as the key is held down, i.e., the rockets are on and the impulse is increasing")
self.output1 = addOutput(0.30, "")
self.output2 = addOutput(0.20, "")
self.output3 = addOutput(0.10, "")
self.output4 = addOutput(0, "")
#ambient light
self.alight = AmbientLight('alight')
self.alight.setColor(VBase4(0.5,0.5, 0.5, 1))
self.alnp = render.attachNewNode(self.alight)
render.setLight(self.alnp)
#light used as a reference point for 0,0,0
self.plight = PointLight('plight')
self.plight.setColor(VBase4(1, 1, 1, 1))
self.plight.setAttenuation(Point3(0,0.2, 0))
self.plnp = render.attachNewNode(self.plight)
render.setLight(self.plnp)
#object to apply a force to
self.cubeBoy = loader.loadModel('1cube')
#'camera'
base.oobe()
base.disableMouse()
self.camDist =20
self.camHeight = 10
base.camera.setPos(0,-self.camDist,self.camHeight)
base.camera.lookAt(self.cubeBoy)
base.camera.reparentTo(self.cubeBoy)
#initialize variables
self.Yforce = 40
self.thisPImpulse = 0
self.totalPImpulse = 0
self.thisNImpulse = 0
self.totalNImpulse = 0
self.totalImpulse = 0
self.currentImpulse = 0
self.timeForceApplied = 0
#setup a force along Y axis
self.setupForce()
#keys to add positive and negative impulse
#
#as long as the key is held down, the force is applied
#i.e., the rockets are on
#impulse = force X time
#if you increase the amount of time the force is applied
#you increase the impulse, which is an increase in acceleration
self.accept("w", self.PYimpulse, [1])
self.accept("w-up", self.PYimpulse, [-1])
self.accept("e", self.NYimpulse, [1])
self.accept("e-up", self.NYimpulse, [-1])
self.accept("escape", sys.exit)
#task to update output
taskMgr.add(self.updateT, "updateT")
def updateT(self,task):
self.output1 ['text'] = "Cube Position: %0.2f" % self.cubeBoy.getY(self.plnp)
self.output2 ['text'] = "Last applied impulse: %0.2f" % self.currentImpulse
self.output3 ['text'] = "Force of 40 applied for: %0.4f seconds " % self.timeForceApplied
self.output4 ['text'] = "Total impulse: %0.2f" % self.totalImpulse
return Task.cont
def PYimpulse(self, arg):
if arg ==1:
self.pull = LinearVectorForce(0, self.Yforce, 0)
self.fn.addForce(self.pull)
self.an.getPhysical(0).addLinearForce(self.pull)
self.startTime = globalClock.getFrameTime()
else:
self.fn.removeForce(self.pull)
self.an.getPhysical(0).removeLinearForce(self.pull)
self.endTime = globalClock.getFrameTime()
self.timeForceApplied = self.endTime - self.startTime
self.thisPImpulse = self.timeForceApplied * self.Yforce
self.currentImpulse = self.thisPImpulse
self.totalPImpulse+=self.thisPImpulse #ever applied
self.totalImpulse = self.totalPImpulse + self.totalNImpulse #current net impulse
def NYimpulse(self, arg):
if arg ==1:
self.pull = LinearVectorForce(0, -self.Yforce, 0)
self.fn.addForce(self.pull)
self.an.getPhysical(0).addLinearForce(self.pull)
self.startTime = globalClock.getFrameTime()
else:
self.fn.removeForce(self.pull)
self.an.getPhysical(0).removeLinearForce(self.pull)
self.endTime = globalClock.getFrameTime()
self.timeForceApplied = self.endTime - self.startTime
self.startTime =0
self.thisNImpulse = self.timeForceApplied * -self.Yforce
self.currentImpulse = self.thisNImpulse
self.totalNImpulse+=self.thisNImpulse
self.totalImpulse = self.totalPImpulse + self.totalNImpulse
def setupForce(self):
# The physical object, that is, the object which is acted upon by
# the physics system, needs an ActorNode to define the coordinate
# system in which it is moving, and to manifest the object's
# actions.
self.an = ActorNode('actor node')
self.anp = render.attachNewNode(self.an)
# A model is parented to the ActorNode just so we can see what
# it's doing.
self.cubeBoy.reparentTo(self.anp)
#light to highlight object
self.plight1 = PointLight('plight1')
self.plight1.setColor(VBase4(1, 1, 1, 1))
self.plight1.setAttenuation(Point3(0,0.2, 0))
self.plnp1 = self.cubeBoy.attachNewNode(self.plight1)
self.plnp1.setPos(0,0,1)
render.setLight(self.plnp1)
# We need to attach it to a PhysicsManager to do the actual work
# of moving it around.
base.physicsMgr.attachPhysicalNode(self.an)
# This method must be called to enable the task that computes the
# PhysicsManager every frame.
base.enableParticles()
# Now apply a force to the ActorNode. The Force needs a
# ForceNode, to define the coordinate system in which it is
# applied.
self.fn = ForceNode('pull')
self.fnp = render.attachNewNode(self.fn)
self.pull = LinearVectorForce(0, 0, 0)
self.fn.addForce(self.pull)
# Here we make it a local force that affects only this one
# ActorNode. We could make it a global force instead by adding it
# to base.physicsMgr.
self.an.getPhysical(0).addLinearForce(self.pull)
w=World()
run()