I’ve asked many of questions about garbage collection, since I am not a programmer and I don’t really understand how it works in Panda. I want to change that with one big and hopefully final question.
Below is the entire code block for the vehicle class in my game. When the race is over, I will need to remove this class and all it’s subclasses from memory, completely and totally. For the subclasses like the guns, I can take what I learn from this question and write clean up functions for them on my own.
What I need to know is this: What do I need to do to remove every scrap and trace of an instance of this class from memory?
[code]class Cycle(DirectObject):
def init(self, dataFile, assetLoader, track, boomManager, cycleManager, team, polePos, color, joy = None, ai = False):
self.setupVarsNodes(assetLoader, dataFile, track, boomManager, cycleManager, team, polePos, color)
self.setupCollisions()
# Calls the setup functions common to all cycles
if(ai == False):
self.setupPS(joy)
# Sets up the player specific components.
#self.setupCamera()
self.hud = HUD(self)
# Sets the camera to follow this cycle and creates a HUD.
else:
self.setupAIS(polePos)
# Sets up the player specific components.
#if(self.name == "Victor"):
# self.hud = HUD(self)
# self.setupCamera()
# Sets the camera to follow this cycle and creates a HUD.
return
def setupVarsNodes(self, assetLoader, dataFile, track, boomManager, cycleManager, team, polePos, color):
self.assetLoader = assetLoader # Stores a reference to the assetLoader.
self.track = track # Stores a reference to the track.
self.boomManager = boomManager # Stores a reference to the boom manager.
self.team = team # Stores the team number for the team the cycle is on.
self.cycleManager = cycleManager
dataFile = self.assetLoader.loadFile("Cycles/" + dataFile, "r")
cycleData = formatData(dataFile)
dataFile.close()
pwrFile = self.assetLoader.loadFile("Parts/" + cycleData["Power Plant"], "r")
pwrData = formatPwrData(pwrFile)
pwrFile.close()
discFile = self.assetLoader.loadFile("Parts/" + cycleData["Discs"], "r")
discData = formatDiscData(discFile)
discFile.close()
shieldFile = self.assetLoader.loadFile("Parts/" + cycleData["Shield Projector"], "r")
shieldData = formatShieldData(shieldFile)
shieldFile.close()
pilotFile = self.assetLoader.loadFile("Pilots/" + cycleData["Pilot"], "r")
pilotData = formatPilotData(pilotFile)
pilotFile.close()
self.name = pilotData["Name"]
self.topSpeed = (pwrData["Top Speed"] + discData["Top Speed"]) * pilotData["Top Speed"] # The cycle's top speed in kph
self.handling = discData["Handling"] * pilotData["Handling"] # The cycle's turning speed in degrees/second. This number should be half of the desired degrees/second.
self.acceleration = (pwrData["Acceleration"] + discData["Acceleration"]) * pilotData["Acceleration"] # The cycle's rate of acceleration in kph/second
self.maxShield = shieldData["Shield Strength"] * pilotData["Shield Strength"] # The maximum strength of the cycle's shield
self.shieldRecharge = shieldData["Shield Recharge"] * pilotData["Shield Recharge"] # The recharge rate of the shield, in units per second.
self.stability = shieldData["Stability"] * pilotData["Stability"] # The amount of impact the shield can absorb before it affects the cycle's control.
self.maxEnergy = pwrData["Energy"] # The maximum energy pool the power plant has for weapons fire.
self.dirVec = Vec3(0,0,0)
self.cycleVec = Vec3(0,0,0)
self.refVec = Vec3(0,0,1)
# creates three vectors to be used in aligning the cycle's facing and it's movement.
self.lanes = self.track.lanes # store a reference to the track markers
self.uc1 = self.lanes[polePos[0]][0] # Preps the first up coming marker variable with the first marker in the lane.
self.uc2 = self.lanes[polePos[0]][1] # Preps the second up coming marker variable with the second marker in the lane.
self.uc3 = self.lanes[polePos[0]][2] # Preps the third up coming marker variable with the third marker in the lane.
self.speed = 0 # The cycle's current speed in kph
self.throttle = 0 # The setting for the throttle in percent. Ranges from -1 - 1.
self.currentLap = 0 # A lap counter for keeping track of completed laps.
self.markerCount = 0 # Counts the markers that the cycle has passed to track it's race progress.
self.freeFall = False # Indicates if the cycle is in free fall or not.
self.fallSpeed = 0 # The current speed of falling for the cycle
self.momentum = 0 # indicates the speed of forward/upward motion during a free fall.
self.turretSpeed = 30 # The turret's turning speed in degrees/second.
self.currentShield = self.maxShield # The current strength of the cycle's shield
self.currentEnergy = self.maxEnergy # The current energy of the cycle.
self.weapons = [[],[],[],[]] # Creates a list to store all the weapons on the cycle.
self.root = render.attachNewNode(self.name + "_CycleRoot")
self.root.setPos(self.uc1.getPos().getX(), polePos[1], 0)
# creates a root node in the scene graph and repositions it.
self.refNode = self.root.attachNewNode(self.name + "_CycleRefNode")
# creates a reference node as a child of the root. This node is used to gather vector info.
self.dirNode = self.root.attachNewNode(self.name + "_DirectionNode")
# create a node that will face in the direction the cycle is moving.
self.cycle = self.assetLoader.loadActor("Parts/Frame.bam")
self.cycle.reparentTo(self.root)
self.coffinMount = self.cycle.exposeJoint(None, "modelRoot", "Coffin")
self.fuelMount = self.cycle.exposeJoint(None, "modelRoot", "FuelTank")
self.turretMount = self.cycle.exposeJoint(None, "modelRoot", "TurretMount")
self.shieldVisMount = self.cycle.exposeJoint(None, "modelRoot", "ShieldVis")
self.frontDiscMount = self.cycle.exposeJoint(None, "modelRoot", "FrontDisc")
self.rearDiscMount = self.cycle.exposeJoint(None, "modelRoot", "RearDisc")
self.shieldProjMount = self.cycle.exposeJoint(None, "modelRoot", "ShieldProjector")
self.powerPlantMount = self.cycle.exposeJoint(None, "modelRoot", "PowerPlant")
self.rightWingMount = self.cycle.exposeJoint(None, "modelRoot", "RightWing")
self.leftWingMount = self.cycle.exposeJoint(None, "modelRoot", "LeftWing")
self.frontMoldingMount = self.cycle.exposeJoint(None, "modelRoot", "FrontMolding")
self.rearMoldingMount = self.cycle.exposeJoint(None, "modelRoot", "RearMolding")
self.coffin = self.assetLoader.loadModel("Parts/Coffin.bam")
self.coffin.reparentTo(self.coffinMount)
self.fuelTank = self.assetLoader.loadModel("Parts/FuelTank.bam")
self.fuelTank.reparentTo(self.fuelMount)
self.shieldVis = self.assetLoader.loadModel("Parts/ShieldVis.bam")
self.shieldVis.reparentTo(self.shieldVisMount)
self.frontMolding = self.assetLoader.loadModel("Parts/" + pilotData["Team"] + "/FrontMolding.bam")
self.frontMolding.reparentTo(self.frontMoldingMount)
self.rearMolding = self.assetLoader.loadActor("Parts/" + pilotData["Team"] + "/RearMolding.bam")
self.rearMolding.reparentTo(self.rearMoldingMount)
self.rightWing = self.assetLoader.loadActor("Parts/" + pilotData["Team"] + "/RightWing.bam")
self.rightWing.reparentTo(self.rightWingMount)
self.leftWing = self.assetLoader.loadActor("Parts/" + pilotData["Team"] + "/LeftWing.bam")
self.leftWing.reparentTo(self.leftWingMount)
self.turret = self.assetLoader.loadActor("Parts/" + pilotData["Team"] + "/Turret.bam")
self.turret.reparentTo(self.turretMount)
self.powerPlant = self.assetLoader.loadModel("Parts/" + pwrData["Model"])
self.powerPlant.reparentTo(self.powerPlantMount)
self.shieldProj = self.assetLoader.loadModel("Parts/" + shieldData["Model"])
self.shieldProj.reparentTo(self.shieldProjMount)
self.frontDisc = self.assetLoader.loadModel("Parts/" + discData["Model"])
self.frontDisc.reparentTo(self.frontDiscMount)
self.rearDisc = self.assetLoader.loadModel("Parts/" + discData["Model"])
self.rearDisc.reparentTo(self.rearDiscMount)
if(cycleData["Turret Type"] == "Standard"):
self.targeterMount = self.turret.exposeJoint(None, "modelRoot", "TargeterMount")
self.heavyMount = self.turret.exposeJoint(None, "modelRoot", "HeavyMount")
self.rightLightMount = self.turret.exposeJoint(None, "modelRoot", "RightLightMount")
self.leftLightMount = self.turret.exposeJoint(None, "modelRoot", "LeftLightMount")
# loads the turret model, reparents it to the turretMount, and exposes the gun mount joints.
self.hvyGun = HeavyGun(self, cycleData["Heavy Gun"], self.heavyMount)
self.weapons[0].append(self.hvyGun)
self.leftLightGun = LightGun(self, cycleData["Light Gun L"], self.leftLightMount)
self.weapons[0].append(self.leftLightGun)
self.rightLightGun = LightGun(self, cycleData["Light Gun R"], self.rightLightMount)
self.weapons[0].append(self.rightLightGun)
# Loads the gun models, reparents them to the mount joints, and adds them to the list of weapons.
self.rearWpnMount = self.rearMolding.exposeJoint(None, "modelRoot", "WeaponMount")
if("Rear Layer" in cycleData):
self.rearLayer = MineLayer(self, cycleData["Rear Layer"], self.rearWpnMount)
self.weapons[3].append(self.rearLayer)
elif("Rear Pod" in cycleData):
self.rearPod = RearHunterPod(self, cycleData["Rear Pod"], self.rearWpnMount)
self.weapons[3].append(self.rearPod)
self.leftWpnMount = self.leftWing.exposeJoint(None, "modelRoot", "WeaponMount")
if("Left Side Rack" in cycleData):
self.leftRack = MissileRack(self, cycleData["Left Side Rack"], self.leftWpnMount, "L")
self.weapons[1].append(self.leftRack)
elif("Left Side Pod" in cycleData):
self.leftPod = SideHunterPod(self, cycleData["Left Side Pod"], self.leftWpnMount, "L")
self.weapons[2].append(self.leftPod)
self.rightWpnMount = self.rightWing.exposeJoint(None, "modelRoot", "WeaponMount")
if("Right Side Rack" in cycleData):
self.rightRack = MissileRack(self, cycleData["Right Side Rack"], self.rightWpnMount, "R")
self.weapons[1].append(self.rightRack)
elif("Right Side Pod" in cycleData):
self.rightPod = SideHunterPod(self, cycleData["Right Side Pod"], self.rightWpnMount, "R")
self.weapons[2].append(self.rightPod)
for L in self.weapons:
for W in L:
W.gunStats["Reload Time"] = W.gunStats["Reload Time"] * pilotData["Reload Time"]
if("Accuracy" in W.gunStats):
W.gunStats["Accuracy"] = W.gunStats["Accuracy"] * pilotData["Accuracy"]
#self.shieldVis.setTransparency(TransparencyAttrib.MAlpha)
#self.shieldVis.setColor(1,1,1,0)
# Loads the visual for the cycle's shield, parents it to the cycle, turns on transparency, and sets the alpha to 0.
self.trackNode = render.attachNewNode(self.name + "_TrackNode")
# Create a node that will always maintain perfect "height" on the track and serve as the target position when "falling"
return
Sets up most of the cycle’s arbitrary variables and all the non-collision nodes used by the cycle.
def setupPS(self, joy):
self.ai = False
self.activeWpnGroup = 0
self.joy = joy # Get a reference to the joystick.
self.keyMap = { "f" : False,
"a" : False,
"s" : False,
"d" : False,
"q" : False,
"w" : False,
"e" : False,
"r" : False,
"g" : False} # Create a dictionary of keyboard keys and boolean values.
self.accept("f", self.setKey, ["f", True])
self.accept("a", self.setKey, ["a", True])
self.accept("s", self.setKey, ["s", True])
self.accept("d", self.setKey, ["d", True])
self.accept("q", self.setKey, ["q", True])
self.accept("w", self.setKey, ["w", True])
self.accept("e", self.setKey, ["e", True])
self.accept("r", self.setKey, ["r", True])
self.accept("g", self.setKey, ["g", True]) # Register key down events and tie them to the setKey function.
self.accept("f-up", self.setKey, ["f", False])
self.accept("a-up", self.setKey, ["a", False])
self.accept("s-up", self.setKey, ["s", False])
self.accept("d-up", self.setKey, ["d", False])
self.accept("q-up", self.setKey, ["q", False])
self.accept("w-up", self.setKey, ["w", False])
self.accept("e-up", self.setKey, ["e", False])
self.accept("r-up", self.setKey, ["r", False])
self.accept("g-up", self.setKey, ["g", False]) # Register key up events and tie them to the setKey function.
self.accept("h", self.nextWpnGroup)
return
Sets up the player specific components.
def setupAIS(self, polePos):
self.ai = True
self.targetThrottle = 0 # The thottle setting the AI wants to be at.
self.target = None # The enemy the AI has chosen to fire on.
self.blocker = None # Indicates if there is a cycle blocking the way for this cycle.
self.currentLane = polePos[0]
self.detectorCN = CollisionNode(self.name + "_DetectorCN")
self.detectorCN.setPythonTag("owner", self)
self.detectorCS = CollisionSphere(0,0,0, 50)
self.detectorCN.addSolid(self.detectorCS)
self.detectorCN.setIntoCollideMask(BitMask32.bit(5))
self.detectorCN.setFromCollideMask(BitMask32.allOff())
self.detectorCNP = self.root.attachNewNode(self.detectorCN)
self.dCTrav = CollisionTraverser()
self.dCHan = CollisionHandlerQueue()
self.dCTrav.addCollider(self.detectorCNP, self.dCHan)
return
Sets up the AI specific components.
def setupCamera(self):
base.disableMouse() # Disable mouse camera control
base.camera.reparentTo(self.dirNode) # Reparent the camera to the cycle so it will follow the cycle and turn with it.
base.camera.setPos(0, -20, 6) # Move the camera back and up to look over the cycle
base.camera.setHpr(0, -10, 0) # Angle the camera down slightly
#base.camera.setPos(0,0, 40)
#base.camera.lookAt(self.root)
#base.camera.setPos(0, -100, 200)
#base.camera.setHpr(0, -60, 0)
return
Sets up the camera to follow this cycle.
def setupCollisions(self):
self.shieldCS = []
self.shieldCN = CollisionNode(self.name + "_ShieldCN")
self.shieldCN.setPythonTag("owner", self)
self.shieldCS.append(CollisionSphere(0, -.025, .75, .785))
self.shieldCS.append(CollisionSphere(0, -1.075, .85, .835))
self.shieldCS.append(CollisionSphere(0, 1.125, .6, .61))
for CS in self.shieldCS:
self.shieldCN.addSolid(CS)
self.shieldCN.setIntoCollideMask(BitMask32.range(3,2))
self.shieldCN.setFromCollideMask(BitMask32.bit(2))
self.shieldCNP = self.cycle.attachNewNode(self.shieldCN)
#self.shieldCNP.show()
# Creates a collision node to contain the shield and adds all the shield spheres to it. The Into masks for this node
# is set to bits 2, 3 and 4, and the From mask is set to 2.
self.bumpCTrav = CollisionTraverser()
#self.bumpCTrav.showCollisions(render)
self.bumpHan = CollisionHandlerPusher()
self.bumpHan.addCollider(self.shieldCNP, self.root)
self.bumpHan.addAgainPattern("%fn-again")
self.bumpCTrav.addCollider(self.shieldCNP, self.bumpHan)
# Creates a collision traverser and a pusher handler to deal with the cycle bumping into walls and other cycles.
# The pusher is set to create a uniquely named event when a collision occurs multiple times.
self.accept(self.name + "_ShieldCN-again", self.bump)
# Registers the uniquely named event of the pusher so that we can reduce speed when the cycle runs into things.
self.targeterCN = CollisionNode(self.name + "_TargeterCN")
self.targeterRay = CollisionRay(0,0,0,0,1,0)
self.targeterCN.addSolid(self.targeterRay)
self.targeterCN.setFromCollideMask(BitMask32.bit(3))
self.targeterCN.setIntoCollideMask(BitMask32.allOff())
self.targeterCNP = self.targeterMount.attachNewNode(self.targeterCN)
#self.targeterCNP.show()
# creates 1 collision ray to represent the line of fire of the cycle for the purpose shooting and locking on with missiles.
# The ray is added to a collision node which is parented to the targeter mount of the turret.
self.targeterCTrav = CollisionTraverser()
self.targeterCHan = CollisionHandlerQueue()
self.targeterCTrav.addCollider(self.targeterCNP, self.targeterCHan)
# Creates a collision traverser and a queue handler to deal with collisions involving the targeter ray.
self.gRayCN = CollisionNode(self.name + "_GRayCN")
self.fRay = CollisionRay(0, .5, 10, 0, 0, -1)
self.bRay = CollisionRay(0, -.5, 10, 0, 0, -1)
self.gRayCN.addSolid(self.fRay)
self.gRayCN.addSolid(self.bRay)
self.gRayCN.setFromCollideMask(BitMask32.bit(1))
self.gRayCN.setIntoCollideMask(BitMask32.allOff())
self.gRayCNP = self.cycle.attachNewNode(self.gRayCN)
#self.gRayNP.show()
# Creates a collision ray called gRay and points it down. This ray will be used to monitor the track beneath the cycle
# to keep the cycle a certain distance above the track and to ensure it's pitch and roll are correct.
self.gCTrav = CollisionTraverser()
self.gHan = CollisionHandlerQueue()
self.gCTrav.addCollider(self.gRayCNP, self.gHan)
# Creates a traverser and a handler for the ground ray and adds the gRay and it's handler to the traverser.
return
Sets up all the from collision objects, their handlers, and their traversers as well as registering events for collision response.
def start(self):
if(self.ai == True):
taskMgr.add(self.runAI, self.name + "_runAI") # Create a task to run the AI
taskMgr.doMethodLater(.5, self.checkTarget, self.name + "_checkTarget")
else:
taskMgr.add(self.keyCheck, self.name + "_Key_Check") # Create a task to moniter the key map and respond to key presses.
def throttleUp(self, dt):
self.throttle = self.throttle + (.25 * dt)
if(self.throttle > 1): self.throttle = 1
return
increase the throttle setting by 10 %/second and constrain the throttle to a maximum of 100 percent.
def throttleDown(self, dt):
self.throttle = self.throttle - (.25 * dt)
if(self.throttle < -1): self.throttle = -1
return
decrease the throttle setting by 10 %/second and constrain the throttle to a minimum of -100 percent.
def turn(self, dt, hardness = None, dir = None):
if(self.speed == 0): turnRate = self.handling
else: turnRate = self.handling * (2 - (self.speed / self.topSpeed))
# Determine turn rate based on speed.
if(dir == "R"): turnRate = -turnRate
if(hardness != None): turnRate *= -hardness
self.cycle.setH(self.cycle, turnRate * dt)
Turn the cycle.
def rotateTurret(self, dt, hardness = None, dir = None):
turnRate = self.turretSpeed
if(dir == "R"): turnRate = -turnRate
if(hardness != None):
turnRate *= -hardness
potentialH = self.turret.getH() + turnRate * dt
if(potentialH > 60): potentialH = 60
elif(potentialH < -60): potentialH = -60
self.turret.setH(potentialH)
Turn the turret.
def tiltGuns(self, dt, hardness = None, dir = None):
tiltRate = self.turretSpeed
if(dir == "D"): tiltRate = -tiltRate
if(hardness != None): tiltRate *= -hardness
potentialTilt = self.turret.getP() + tiltRate * dt
if(potentialTilt > 30): potentialTilt = 30
elif(potentialTilt < -10): potentialTilt = -10
self.turret.setP(potentialTilt)
Tilt the guns up or down.
def fire(self, group):
if(self.currentLap > 0):
for W in self.weapons[group]:
W.fire()
return
Fires all the guns.
def nextWpnGroup(self):
self.activeWpnGroup += 1
if(self.activeWpnGroup > 3):
self.activeWpnGroup = 0
def hit(self, damage):
self.currentShield -= damage
self.shieldVis.setColor(1,1,1,.8)
instability = damage - self.stability
if(instability > 0):
if(self.speed - instability > 0):
self.speed -= instability
else: self.speed = 0
return
Registers a hit on the cycle by damaging the shield and turning up the shield’s alpha.
def updateWeapons(self, dt):
for L in self.weapons:
for W in L:
W.update(dt)
return
Updates all the guns reload progress and ready states
def updateShield(self, dt):
if(self.currentShield <= 0):
return(True)
elif(self.currentShield < self.maxShield):
newShieldStr = self.currentShield + (self.shieldRecharge * dt)
if(newShieldStr <= self.maxShield): self.currentShield = newShieldStr
else: self.currentShield = self.maxShield
# Checks if the shield is down and sets off the destroyed function if so. Otherwise, recharges the shield if it needs it.
alpha = self.shieldVis.getColor().getW()
if(alpha > 0):
if(alpha - dt > 0): self.shieldVis.setColor(1,1,1, alpha - dt)
else: self.shieldVis.setColor(1,1,1,0)
# Checks if the shield is visible, and if so, reduces it's alpha down to zero over time.
return(False)
Monitors the shield’s status and updates as necessary.
def orientToTrack(self, dt):
self.gCTrav.traverse(render)
# Intitiate the ground ray traverser.
points = [None, None]
if(self.gHan.getNumEntries() > 1):
self.gHan.sortEntries()
for E in range(self.gHan.getNumEntries()):
entry = self.gHan.getEntry(E)
if(entry.getFrom() == self.fRay and points[0] == None): points[0] = entry.getSurfacePoint(render)
elif(entry.getFrom() == self.bRay and points[1] == None): points[1] = entry.getSurfacePoint(render)
# Get the surface contact points for the nearest 2 collisions, which will correspond to the 2 rays.
# Put them into the points list in a particular order so they are easy to make sense of.
if(points[0] == None or points[1] == None):
self.teleport(dt)
return
else:
# If either ray didn't collide with the ground, teleport back onto the center of the track.
if(self.freeFall == False):
self.refNode.setPos(points[1])
self.refNode.lookAt(points[0])
pDiff = self.refNode.getP()- self.cycle.getP()
if(pDiff < .1 and pDiff > -.1):
self.cycle.setP(self.refNode.getP())
else:
self.cycle.setP(self.cycle, pDiff * dt * 5)
# When not in free fall, find the pitch between the two collision points and smoothly match the cycle to it.
elif((self.cycle.getP() - (dt * 10)) > -15): self.cycle.setP(self.cycle, -(dt * 10))
else: self.cycle.setP(-15)
# In free fall, let the pitch of the cycle slowly drop.
if(self.speed >= 0): self.trackNode.setPos(points[0].getX(), points[0].getY(), points[0].getZ())
else: self.trackNode.setPos(points[1].getX(), points[1].getY(), points[1].getZ())
# Set the track node at the collision point on the leading end of the cycle.
height = self.root.getZ(self.trackNode)
# Get the height of the root node as seen from the track node.
if(height > 2 and self.freeFall == False):
self.freeFall = True
self.fallSpeed = 0
self.momentum = self.speed
# If the height is greater than 2, enter a free fall state and prep free fall variables.
if(self.freeFall == True):
self.fallSpeed += self.track.gravity * dt
newHeight = height - (self.fallSpeed * dt)
# In a free fall state, begin accelerating the fall speed and calculate a new height based on that fall speed.
else:
hDiff = 1 - height
if(hDiff > .01 or hDiff < -.01): newHeight = height + (hDiff * dt * 5)
else: newHeight = 1
# If not in a freefall state, calculate a new height that gravitates towards the desired height,
# or if you're close to the desired height, just set it as the new height.
if(newHeight >= 0): self.root.setZ(self.trackNode, newHeight)
# If the new height is greater than or equal to zero, set it as root node's height.
else:
self.root.setZ(self.trackNode, 0)
self.freeFall = False
# Otherwise, set the root node to a height of 0, and turn off the free fall state.
self.cycle.setR(0)
# Constrain the cycle's roll to 0.
return
Reads the ground below the cycle and sets the pitch, and Z based on it. Also contrains the cycle’s roll to 0.
def dirNodeCatchUp(self, dt):
if(self.freeFall == False):
self.refNode.setPos(self.dirNode, 0, 1, 0)
self.dirVec.set(self.refNode.getPos().getX(), self.refNode.getPos().getY(), 0)
# gets the direction vector of the dirNode's facing.
self.refNode.setPos(self.cycle, 0, 1, 0)
self.cycleVec.set(self.refNode.getPos().getX(), self.refNode.getPos().getY(), 0)
# gets the direction vector of the cycle's facing.
self.refVec.set(0,0,1)
vecDiff = self.dirVec.signedAngleDeg(self.cycleVec, self.refVec)
# gets the signed angle difference between the dirNode and cycle vectors.
if(vecDiff < .1 and vecDiff > -.1):
# check to see how far off the two vectors are.
self.dirNode.setHpr(self.cycle.getHpr().getX(), 0, 0)
# If they're really close, set the dirNode's Hpr to equal the cycle's Hpr, thus perfectly aligning them.
else: self.dirNode.setHpr(self.dirNode, vecDiff * dt * 2.5, 0, 0)
# If they aren't really close, start closing the gap by rotating the dirNode.
self.dirNode.setP(self.cycle.getP())
self.dirNode.setR(0)
return
Causes the dirNode to slowly alter it’s heading to the cycle’s, and forces the dirNode to maintain the same pitch and roll as the cycle.
def speedCheck(self, dt):
if(self.freeFall == False):
if(self.speed < self.topSpeed * self.throttle):
# Checks if the cycle speed is lower than the throttle setting.
self.speed = self.speed + (self.acceleration * dt)
# If so, Increases the speed by acceleration * time
if(self.speed > self.topSpeed * self.throttle):
self.speed = self.topSpeed * self.throttle
# If the speed has exceeded the throttle setting, set it to the throttle setting.
elif(self.speed > self.topSpeed * self.throttle):
# Checks if the cycle speed is higher than the throttle setting.
self.speed = self.speed - ((self.acceleration * 1.1) * dt)
# If so, decreases the speed by acceleration * time. The only method of braking hover cycles have is inverse thrust,
# so to slow down you have to accelerate backwards.
if(self.speed < self.topSpeed * self.throttle):
self.speed = self.topSpeed * self.throttle
# If the speed has dropped below the throttle setting, set it to the throttle setting.
else:
self.momentum -= (self.momentum * .125) * dt
# In a free fall, drop momentum by 25%/second
return
Controls the rate of the cycle’s forward motion.
def move(self, dt):
if(self.freeFall == False): mps = self.speed * 1000 / 3600 # Convert kph to meters per second
# When not in free fall, use speed to determine distance moved along dirNode's facing.
else: mps = self.momentum * 1000 / 3600 # Convert kph to meters per second
# In a free fall, use momentum.
self.refNode.setPos(self.dirNode, 0, 1, 0)
# Set the ref node 1 meter unit forward in the dirNode's facing.
self.dirVec.set(self.refNode.getX(), self.refNode.getY(), self.refNode.getZ())
# Get the direction vector that corresponds to the dirNode's facing.
self.root.setPos(self.root, self.dirVec.getX() * mps * dt, self.dirVec.getY() * mps * dt, self.dirVec.getZ() * mps * dt)
# Advances the cycle in the direction dirNode is facing based on time passed and speed.
self.bumpCTrav.traverse(render)
# Checks collisions for use in the next frame.
return
Manages the facing of the dirNode, accelerates and deaccelerates to match throttle, and moves the cycle
according to it’s current speed.
def teleport(self, dt):
marker = self.track.getNearestMarker(self)
markerPos = marker.getPos()
self.root.setPos(markerPos.getX(), markerPos.getY(), self.root.getZ())
# Put the cycle back on the track.
self.gCTrav.traverse(render)
# Intitiate the ground ray traverser.
points = [None, None]
if(self.gHan.getNumEntries() > 1):
self.gHan.sortEntries()
for E in range(self.gHan.getNumEntries()):
entry = self.gHan.getEntry(E)
if(entry.getFrom() == self.fRay and points[0] == None): points[0] = entry.getSurfacePoint(render)
elif(entry.getFrom() == self.bRay and points[1] == None): points[1] = entry.getSurfacePoint(render)
# Get the surface contact points for the nearest 2 collisions, which will correspond to the 2 rays.
# Put them into the points list in a particular order so they are easy to make sense of.
if(self.speed >= 0): self.trackNode.setPos(points[0].getX(), points[0].getY(), points[0].getZ())
else: self.trackNode.setPos(points[1].getX(), points[1].getY(), points[1].getZ())
# Set the track node at the collision point on the leading end of the cycle.
self.root.setZ(self.trackNode, 1)
self.dirNode.setHpr(marker.getHpr())
self.cycle.setHpr(marker.getHpr())
self.speed /= 2
return
Puts the cycle back on the track and recalls orientToTrack.
def bump(self, entry):
if(self.speed * .95 > self.topSpeed * .1): self.speed = self.speed * .95
else: self.speed = self.topSpeed * .1
self.hit(0)
if(entry.getIntoNodePath().hasPythonTag("owner")):
otherCycle = entry.getIntoNodePath().getPythonTag("owner")
if(otherCycle.speed * .975 > otherCycle.topSpeed * .1): otherCycle.speed = otherCycle.speed * .975
else: otherCycle.speed = otherCycle.topSpeed * .1
if(self.checkInFront(otherCycle) == True):
self.changeLanes()
return
If the cycle hit an obstacle of some kind, this aaevent response will slow the cycle down, to minimum of 10% top speed.
def setKey(self, key, val):
self.keyMap[key] = val
return
Sets the provided key in the keyMap dictionary to the provided value.
def keyCheck(self, task):
dt = globalClock.getDt()
if (dt > .20):
return task.cont
# Find the amount of time that has passed since the last frame. If this amount is too large,
# there's been a hiccup and this frame should be skipped.
if(self.keyMap["f"] == True):
self.throttleUp(dt)
# checks if the f key is pressed. If so, increase throttle.
if(self.keyMap["a"] == True):
self.throttleDown(dt)
# checks if the a key is pressed. If so, decrease throttle.
if(self.keyMap["s"] == True):
self.turn(dt, dir = "L")
# checks if the s key is pressed. If so, turn left.
if(self.keyMap["d"] == True):
self.turn(dt, dir = "R")
# checks if the d key is pressed. If so, turn right.
if(self.keyMap["w"] == True):
self.rotateTurret(dt, dir = "L")
# checks if the w key is pressed. If so, rotate the turret left.
if(self.keyMap["e"] == True):
self.rotateTurret(dt, dir = "R")
# checks if the w key is pressed. If so, rotate the turret left.
if(self.keyMap["q"] == True):
self.tiltGuns(dt, dir = "D")
# checks if the q key is pressed. If so, tilt the guns down.
if(self.keyMap["r"] == True):
self.tiltGuns(dt, dir = "U")
# checks if the r key is pressed. If so, tilt the guns up.
if(self.keyMap["g"] == True):
self.fire(self.activeWpnGroup)
if(self.joy != None):
for e in pygame.event.get(): pass
# allows access to the joystick. must be called every frame.
lAxislr = self.joy.get_axis(0)
if(lAxislr > .1 or lAxislr < -.1):
self.turn(dt, hardness = lAxislr)
# Turn the cycle based on the left and right axis of the left stick.
rAxislr = self.joy.get_axis(2)
if(rAxislr > .1 or rAxislr < -.1):
self.rotateTurret(dt, hardness = rAxislr)
# Rotate the turret based on the left and right axis of the right stick.
rAxisud = self.joy.get_axis(3)
if(rAxisud > .1 or rAxisud < -.1):
self.tiltGuns(dt, hardness = rAxisud)
# Rotate the turret based on the left and right axis of the right stick.
if(self.joy.get_button(10) == 1):
self.throttleDown(dt)
elif(self.joy.get_button(8) == 1):
self.throttleUp(dt)
# Throttle up and down with the left trigger buttons. Throttle down takes
# precedence.
if(self.joy.get_button(9) == 1):
self.fire(2)
destroyed = self.updateShield(dt)
if(destroyed == True): return task.done
self.updateWeapons(dt)
self.orientToTrack(dt)
# Read the track to better orient the cycle.
self.dirNodeCatchUp(dt)
# Let dirNode try to catch up to the cycle's orientation.
self.checkMarkers()
# Updates the up coming track markers if necessary.
self.speedCheck(dt)
# updates the cycle's speed, if necessary.
self.move(dt)
# moves the cycle.
return task.cont
Monitors the keyMap for changes caused by key presses and responds to them.
Also calls all of the functions that need to be called every frame.
def findAngle(self, marker, node):
markPos = marker.getPos(node)
self.dirVec.set(markPos.getX(), markPos.getY(), 0)
self.dirVec.normalize()
# Gets the directional vector to the marker and normalizes it.
self.cycleVec.set(0,1,0)
# Uses cycleVec for the reference vector.
self.refVec.set(0,0,1)
return(self.cycleVec.signedAngleDeg(self.dirVec, self.refVec))
Returns the angle between the node’s facing and the direction to the marker.
def checkMarkers(self):
if(self.uc1.checkInFront(self) == True):
#self.unMark()
self.uc1 = self.uc2
self.uc2 = self.uc3
self.uc3 = self.uc2.nextMarker
self.markerCount += 1
if(self.uc1 == self.lanes[0][1] or self.uc1 == self.lanes[1][1]):
self.currentLap += 1
# If the cycle just passed the first marker, which is at the finish line, increment the lap count.
#self.mark()
return
Checks if uc1 has been passed, and if so, updates all the markers.
def checkThrottle(self, dt):
if(self.throttle < self.targetThrottle): self.throttleUp(dt)
elif(self.throttle > self.targetThrottle): self.throttleDown(dt)
# Set the throttle to match the target throttle
return
Monitors the track ahead and sets the target throttle accordingly. Also adjusts the throttle when it doesn’t
match the target throttle.
def checkTurn(self, dt):
turnAngle = self.findAngle(self.uc3, self.cycle)
if(turnAngle < 1 and turnAngle > -1):
turnAngle = 0
if(self.targetThrottle < 1):
self.targetThrottle = 1
# Get the perceived turn angle.
if(self.speed == 0): turnRate = self.handling
else: turnRate = self.handling * (2 - (self.speed / self.topSpeed))
# Determine turn rate based on speed.
if(turnAngle < 0):
turnRate *= -1
# If it's a right turn, set the turn rate to a negative
if(turnAngle < turnRate * dt):
self.cycle.setH(self.cycle, turnRate * dt)
self.targetThrottle = 1 + (turnRate * dt / self.handling)
else:
hardness = (turnRate * dt)/ turnAngle
turnRate *= hardness
self.cycle.setH(self.cycle, turnRate * dt * hardness)
if(turnAngle > 0):
if(turnAngle > turnRate * dt):
self.cycle.setH(self.cycle, turnRate * dt)
self.targetThrottle = 1 - (turnRate * dt / self.handling)
else:
hardness = (turnRate * dt)/ turnAngle
turnRate *= hardness
self.cycle.setH(self.cycle, turnRate * dt * hardness)
return
Monitors the track ahead and determines if turning is necessary, how much, and which direction.
def checkTarget(self, task):
targIR = []
self.target = None
self.dCTrav.traverse(render)
if(self.dCHan.getNumEntries() > 0):
self.dCHan.sortEntries()
for E in range(self.dCHan.getNumEntries()):
target = self.dCHan.getEntry(E).getIntoNodePath().getPythonTag("owner")
if(target.team != self.team and self.checkInFront(target) == True):
targIR.append(target)
break
if(len(targIR) > 0): self.target = targIR[0]
else: self.target = self.cycleManager.getLowerPole(self)
if(len(self.weapons[2]) > 0):
higherPole = self.cycleManager.getHigherPole(self)
if(higherPole != None and self.markerCount - higherPole.markerCount > 5):
for W in self.weapons[2]:
W.setTrackDir("F")
self.fire(2)
if(len(self.weapons[3]) > 0 or len(self.weapons[2]) > 0):
lowerPole = self.cycleManager.getLowerPole(self)
if(lowerPole != None and lowerPole.markerCount - self.markerCount > 5):
self.fire(3)
for W in self.weapons[2]:
W.setTrackDir("B")
self.fire(2)
return task.again
def changeLanes(self):
self.blocker = None
if(self.currentLane == 0):
self.uc1 = self.lanes[1][self.lanes[0].index(self.uc1)]
self.uc2 = self.lanes[1][self.lanes[0].index(self.uc2)]
self.uc3 = self.lanes[1][self.lanes[0].index(self.uc3)]
self.currentLane = 1
else:
self.uc1 = self.lanes[0][self.lanes[1].index(self.uc1)]
self.uc2 = self.lanes[0][self.lanes[1].index(self.uc2)]
self.uc3 = self.lanes[0][self.lanes[1].index(self.uc3)]
self.currentLane = 0
def checkAim(self, dt):
if(self.target != None):
self.findHAim(dt, self.target)
self.findPAim(dt, self.target)
self.targeterCTrav.traverse(render)
if(self.targeterCHan.getNumEntries() > 0):
self.targeterCHan.sortEntries()
if(self.targeterCHan.getEntry(0).getIntoNodePath().hasPythonTag("owner")):
self.fire(0)
else:
self.refNode.setPos(self.cycle,0,1,0)
self.findHAim(dt, self.refNode)
def findHAim(self, dt, target):
targetPos = target.getPos(self.targeterCNP)
self.dirVec.set(targetPos.getX(), targetPos.getY(), targetPos.getZ())
self.dirVec.normalize()
# Gets the directional vector to the target and normalizes it.
self.cycleVec.set(0,1,0)
# Set the cycle vec to straight forward.
self.refVec.set(0,0,1)
# Sets the reference vector.
aimAngle = self.cycleVec.signedAngleDeg(self.dirVec, self.refVec)
if(aimAngle < .1 and aimAngle > -.1):
aimAngle = 0
aimRate = self.turretSpeed
# Determine angle and rate.
if(aimAngle < 0):
aimRate *= -1
# If it's a right turn, set the aim rate to a negative
if(aimAngle < aimRate * dt):
self.rotateTurret(dt, hardness = 1)
else:
hardness = (aimRate * dt)/ aimAngle
self.rotateTurret(dt, hardness = -hardness)
if(aimAngle > 0):
if(aimAngle > aimRate * dt):
self.rotateTurret(dt, hardness = -1)
else:
hardness = (aimRate * dt)/ aimAngle
self.rotateTurret(dt, hardness = -hardness)
return
Monitors the target and determines if turning is necessary, how much, and which direction.
def findPAim(self, dt, target):
targetPos = target.getPos(self.targeterCNP)
self.dirVec.set(targetPos.getX(), targetPos.getY(), targetPos.getZ())
self.dirVec.normalize()
# Gets the directional vector to the target and normalizes it.
self.cycleVec.set(0,1,0)
# Set the cycle vec to straight forward.
self.refVec.set(1,0,0)
# Sets the reference vector.
aimAngle = self.cycleVec.signedAngleDeg(self.dirVec, self.refVec)
if(aimAngle < 1 and aimAngle > -1):
aimAngle = 0
aimRate = self.turretSpeed
# Determine angle and rate.
if(aimAngle < 0):
aimRate *= -1
# If it's a right turn, set the aim rate to a negative
if(aimAngle < aimRate * dt):
self.tiltGuns(dt, hardness = 1)
else:
hardness = (aimRate * dt)/ aimAngle
self.tiltGuns(dt, hardness = -hardness)
if(aimAngle > 0):
if(aimAngle > aimRate * dt):
self.tiltGuns(dt, hardness = -1)
else:
hardness = (aimRate * dt)/ aimAngle
self.tiltGuns(dt, hardness = -hardness)
return
def checkInFront(self, cycle):
cyclePos = cycle.root.getPos(self.cycle)
self.dirVec.set(cyclePos.getX(), cyclePos.getY(), self.cycle.getZ())
self.dirVec.normalize()
# Gets the directional vector to the cycle and normalizes it.
self.cycleVec.set(0,1,0)
cycleAngle = self.cycleVec.angleDeg(self.dirVec)
# Gets the angle between the cycle's facing and the direction to the other cycle.
if(cycleAngle > 90): return(False)
else: return(True)
# Returns True if the cycle is in front of this cycle or False if it is behind it.
def runAI(self, task):
dt = globalClock.getDt()
if (dt > .20):
return task.cont
# Find the amount of time that has passed since the last frame. If this amount is too large,
# there's been a hiccup and this frame should be skipped.
destroyed = self.updateShield(dt)
if(destroyed == True): return task.done
self.updateWeapons(dt)
self.orientToTrack(dt)
# Read the track to better orient the cycle.
self.dirNodeCatchUp(dt)
# Let dirNode try to catch up to the cycle's orientation.
self.checkMarkers()
self.checkThrottle(dt)
self.checkTurn(dt)
self.speedCheck(dt)
# updates the cycle's speed, if necessary.
self.move(dt)
# moves the cycle.
#self.checkAim(dt)
return task.cont
Performs all the AI functions each frame.
def unMark(self):
self.uc1.sphere.setColor(1,1,1)
self.uc2.sphere.setColor(1,1,1)
self.uc3.sphere.setColor(1,1,1)
def mark(self):
self.uc1.sphere.setColor(1,0,0)
self.uc2.sphere.setColor(1,1,0)
self.uc3.sphere.setColor(0,1,0)
def getPos(self, ref = None):
if(ref == None): return(self.root.getPos())
else: return(self.root.getPos(ref))
def getH(self):
return(self.cycle.getH())[/code]