Here’s mine, without fitDestAngle2Src, but by limiting the lookAt resulting hpr to the target with clampScalar, to all 3 axis.
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from direct.showbase.DirectObject import DirectObject
from random import choice
from copy import copy
import direct.directbase.DirectStart
import os
base.cTrav = CollisionTraverser('')
base.cTrav.setRespectPrevTransform(1)
CHPusher = CollisionHandlerFluidPusher()
CHEvent = CollisionHandlerEvent()
CHEvent.addInPattern('%fn-hit-%in')
GHOST = BitMask32.allOff()
SHIP2SHIP = BitMask32.bit(0)
PROJ2SHIP = BitMask32.bit(1)
class Proj(DirectObject):
lastID = -1
def __init__(self,target):
self.target = target
self.targetExist = True
self.maxSpeed = self.speed = 70.
self.turn = self.speed*3
self.p = loader.loadModel('misc/Spotlight')
self.p.reparentTo(render)
self.p.setP(180)
self.p.setY(2)
self.p.setScale(.1,.25,.1)
self.p.flattenLight()
maxb = self.p.getTightBounds()[1]
Proj.lastID+=1
collName = 'proj%s'%Proj.lastID
self.collNP = self.p.attachCollisionSphere(collName, 0,maxb[1]*.5,0, maxb[1]*.2, PROJ2SHIP, GHOST)
base.cTrav.addCollider(self.collNP,CHEvent)
self.p.setPos(player,0,player.getChild(0).getTightBounds()[1][1],0)
self.p.setHpr(player,0,0,0)
self.p.setColor(Vec4(1,1,0,1))
self.p.setTextureOff(1)
self.acceptOnce('%s-hit-ship'%collName,self.hit)
self.updateTask = taskMgr.add(self.update,'update-'+collName)
self.probe = self.p.attachNewNode('')
def update(self,task):
dt = globalClock.getDt()
if self.targetExist and self.target.getErrorType()==NodePath.ETRemoved:
self.targetExist = False
taskMgr.doMethodLater(3,self.cleanup,'cleanup',extraArgs=[])
if self.targetExist:
self.probe.lookAt(self.target)
degThreshold = self.turn*dt
origHpr = self.probe.getHpr()
d = copy(origHpr)
for i in range(3):
d[i] = clampScalar(-degThreshold,degThreshold,d[i])
self.p.setHpr(self.p,d)
dist = self.p.getDistance(self.target)
# within this range, and not immediately after launched, and not head on,
# slow down smoothly, otherwise go full speed
if dist<30 and task.time>.5 and (d-origHpr).lengthSquared()>500:
self.speed = max(10,self.maxSpeed * dist/30.)
elif self.speed<self.maxSpeed:
self.speed+=15*dt
# after the target is destroyed, accelerate to maxSpeed
elif self.speed<self.maxSpeed:
self.speed+=15*dt
speed = self.speed*dt
self.p.setFluidY(self.p,speed)
return task.cont
def cleanup(self):
taskMgr.remove(self.updateTask)
base.cTrav.removeCollider(self.collNP)
print 'CLEANUP',self.collNP.getName()
self.p.removeNode()
self.ignoreAll()
def hit(self,ce):
into = ce.getIntoNodePath()
if into.hasNetPythonTag('hit'):
into.getNetPythonTag('hit')()
self.cleanup()
class Ship:
def __init__(self):
global s
if choice([0,1]):
self.s = loader.loadModel('smiley')
self.s.reparentTo(shipsParent)
self.s.getChild(0).setScale(3)
csNP = self.s.attachCollisionSphere('ship', 0,0,0, 3, SHIP2SHIP,PROJ2SHIP)
else:
self.s = loader.loadModel('misc/rgbCube')
self.s.reparentTo(shipsParent)
self.s.getChild(0).setScale(7,3,3)
b = self.s.getTightBounds()
dim = b[1]-b[0]
coll = CollisionNode('ship')
box = CollisionBox(*b)
coll.addSolid(box)
coll.setFromCollideMask(SHIP2SHIP)
coll.setIntoCollideMask(PROJ2SHIP)
boxNP = self.s.attachNewNode(coll)
csNP = self.s.attachCollisionSphere('bounds', 0,0,0, dim.length()*.5, SHIP2SHIP,GHOST)
base.cTrav.addCollider(csNP,CHPusher)
CHPusher.addCollider(csNP,self.s)
self.s.setY(25)
self.s.setPythonTag('hit',self.hit)
self.health = 1
s=self
def hit(self):
self.health -= .25
self.s.setColor(1,self.health,self.health,1)
if self.health<=0:
self.destroyed()
def destroyed(self):
global s
if self.s.hasPythonTag('hit'):
self.s.clearPythonTag('hit')
base.cTrav.removeCollider(self.s.find('+CollisionNode'))
self.s.setTransparency(1)
Sequence(
Parallel(
self.s.scaleInterval(.2,Vec3(2)),
self.s.colorScaleInterval(.2,Vec4(1,1,1,0))
),
Func(self.s.removeNode),
Wait(1),
Func(spawnShip)
).start()
s=None
def spawnShip():
global s
s = Ship()
def fire():
if s is None: return
Proj(s.s)
def updatePlayer(task):
dt = globalClock.getDt()
move = playerMove[0]-playerMove[1]
rotate = playerMove[2]-playerMove[3]
if move:
player.setY(player,move*20*dt)
if move<0:
rotate*=-1
if rotate:
player.setH(player,rotate*120*dt)
return task.cont
def toggleShipMovement():
if shipsParentIval.isPlaying():
shipsParentIval.pause()
else:
shipsParentIval.resume()
shipsParent = render.attachNewNode('')
shipsParentIval = shipsParent.hprInterval(4,Vec3(360,0,0))
shipsParentIval.loop()
spawnShip()
player = loader.loadModel('misc/Dirlight')
player.reparentTo(render)
player.setTextureOff(1)
coll = CollisionNode('player')
box = CollisionBox(*player.getTightBounds())
coll.addSolid(box)
coll.setFromCollideMask(SHIP2SHIP)
coll.setIntoCollideMask(SHIP2SHIP)
playerColl = player.attachNewNode(coll)
CHPusher.addCollider(playerColl,player)
base.cTrav.addCollider(playerColl,CHPusher)
playerMove = Vec4(0)
base.accept('f',fire)
base.accept('arrow_up',playerMove.setX,[1])
base.accept('arrow_up-up',playerMove.setX,[0])
base.accept('arrow_down',playerMove.setY,[1])
base.accept('arrow_down-up',playerMove.setY,[0])
base.accept('arrow_left',playerMove.setZ,[1])
base.accept('arrow_left-up',playerMove.setZ,[0])
base.accept('arrow_right',playerMove.setW,[1])
base.accept('arrow_right-up',playerMove.setW,[0])
base.accept('space',toggleShipMovement)
base.accept('escape',os._exit,[0])
taskMgr.add(updatePlayer,'updatePlayer')
camera.setPos(-1.09, -52.53, 115.76)
camera.setHpr(0.53, -65.61, 3.09)
mat4=Mat4(camera.getMat())
mat4.invertInPlace()
base.mouseInterfaceNode.setMat(mat4)
run()
ARROWS to move, F to shoot.
EDIT:
added speed damping when close to the target and not head on.