Aikman007
Aikman007

Reputation: 49

Trying to rotate a guided missile in pygame

Trig is not my strong suit which is why I am having probs trying to get my guided missile to rotate properly. Looks like I have the direction fine although Im still not 100% happy with the movement, so Im hoping by fixing the rotation it will fix that also. Problem is my missiles dont point towards the target (player), and they also flip at a certain point past the target. Heres is the code...the rotations just dont work correctly.

def update(self):
    self.calcVect()
    self.calcPos()
    self.rotate()
    self.checkBounds()
    self.rect.center = (self.x, self.y)

def calcVect(self):
    self.vectX = self.targetX - self.x
    self.vectY = self.targetY - self.y
    self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY)) 
    self.normX = self.vectX/self.length
    self.normY = self.vectY/self.length
    self.dx += self.normX*self.power
    self.dy += self.normY*self.power

def calcPos(self):
    self.x += self.dx*0.2
    self.y += self.dy*0.2

def rotate(self):
    radians = math.atan2(-self.vectY,self.vectX)
    radians %= 2*math.pi
    self.dir = math.degrees(radians)
    print self.dir
    oldCenter = self.rect.center
    self.image = pygame.transform.rotate(self.imageMissile, self.dir)
    self.rect = self.image.get_rect()
    self.rect.center = oldCenter

Working Code Here is the new working code for the entire class. The expression at the end of this line enabled correct rotations "radians = math.atan2(self.vectX, self.vectY)- math.pi/2" even though the image was horizontal to start with.

class GuidedMissile(pygame.sprite.Sprite):
def __init__(self, source):
    pygame.sprite.Sprite.__init__(self)
    self.screen = source.screen

    self.imageMissile = pygame.image.load("Images/missile.tga").convert()
    self.imageMissile.set_colorkey((0,0,255))
    self.image = self.imageMissile
    self.rect = self.image.get_rect()
    self.rect.center = source.rect.midbottom

    self.rect.inflate_ip(-15, -5)        

    self.x, self.y = self.rect.center
    self.X, self.Y = self.rect.center
    self.dx = 0
    self.dy = 0
    self.power = 3
    self.dir = 0

def update(self):
    self.player = goodSpritesOneGRP.sprite
    self.targetX, self.targetY = self.player.rect.center
    NX, NY = self.calcVect()
    self.calcVel(NX, NY)
    self.calcPos()
    self.rotate()
    self.checkBounds()
    self.rect.center = (self.x, self.y)

def calcVect(self):
    self.vectX = self.targetX - self.x
    self.vectY = self.targetY - self.y
    self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY)) 
    if self.length == 0:
        self.normX = 0
        self.normY = 1
    else:
        self.normX = self.vectX/self.length
        self.normY = self.vectY/self.length
    return (self.normX, self.normY)

def calcVel(self,NX,NY ):
    self.dx += NX*self.power
    self.dy += NY*self.power            

def calcPos(self):
    self.x += self.dx*0.05
    self.y += self.dy*0.05

def rotate(self):
    radians = math.atan2(self.vectX, self.vectY)- math.pi/2
    radians %= 2*math.pi
    self.dir = math.degrees(radians)
    oldCenter = self.rect.center
    self.image = pygame.transform.rotate(self.imageMissile, self.dir)
    self.rect = self.image.get_rect()
    self.rect.center = oldCenter

def checkBounds(self):
    global guidedMissile
    screen = self.screen
    if self.x > screen.get_width():
        self.kill()
        guidedMissile -= 1
    if self.x < -100:
        self.kill()            
        guidedMissile -= 1
    if self.y > screen.get_height() + 100:
        self.kill()
        guidedMissile -= 1
    if self.y < -100:
        self.kill()            
        guidedMissile -= 1

Upvotes: 2

Views: 324

Answers (1)

Federico
Federico

Reputation: 2133

When you call calcPos() you have to recalculate self.vectX and self.vectY, otherwise the missile will not point to the target. And don't forget to check if self.length == 0

You can fix splitting calcVect():

def calcVect(self):
    self.vectX = self.targetX - self.x
    self.vectY = self.targetY - self.y
    self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY)) 
    if self.length != 0:
        normX = vectX/self.length
        normY = vectY/self.length

# Update velocity
def calcVel(self):
    self.dx += self.normX*self.power
    self.dy += self.normY*self.power

Then

def update(self):
    self.calcVect()
    self.calcVel()
    self.calcPos()
    self.calcVect()
    self.rotate()
    self.checkBounds()
    self.rect.center = (self.x, self.y)

This solution works, but I suggest you to write a generic method calcVectTo(self, target) that returns the unitary vector pointing to (target.x, target.y):

def calcVect(self, target):
    vectX = target.x - self.x
    vectY = target.y - self.y
    length = math.sqrt((vectX*vectX)+(vectY*vectY)) 
    if length == 0:
        normX = 0
        normY = 1
    else
        normX = vectX/length
        normY = vectY/length
    return (normX, normY)

Edit

I did not understand that the target is fixed, now it is much easier: you only need to calculate vect at the construction of the class, moving calcVect from update to __construct (ignore my second implementation of calcVect):

class GuidedMissile(pygame.sprite.Sprite):
def __init__(self, source, target):
    ...
    self.calcVect()

def update(self):
    self.calcVel()
    self.calcPos()
    self.rotate()
    self.checkBounds()
    self.rect.center = (self.x, self.y)

Upvotes: 1

Related Questions