AttributeError in pygame - pygame

Here is where my error jumps onto the screen. With this message:
self.rect = self.image.get_rect()
AttributeError: 'list' object has no attribute 'get_rect'
Because im trying to make a sprite
rigto = [pygame.image.load("img/Iconos/guy1.png"), pygame.image.load("img/Iconos/guy2.png"), pygame.image.load("img/Iconos/guy3.png"), pygame.image.load("img/Iconos/guy4.png")]
class Player:
def __init__(self, x, y, image):
self.image = image
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
jugador1 = Player(500, 500, rigto)

rigto is a list of images (pygame.Surface objects). You have to choose one image:
e.g.:
jugador1 = Player(500, 500, rigto[1])
Alternatively, you can manage the images in the class (e.g. for animations):
class Player:
def __init__(self, x, y, image_list):
self.image_list = image_list
self.count = 0
self.image = self.image_list[self.count]
self.rect = self.image.get_rect(topleft = (x, y))
def update(self)
self.count += 1
frame = self.count // 10
if frame >= len(self.image_list):
self.count = 0
frame = 0
self.image = self.image_list[frame]
jugador1 = Player(500, 500, rigto)

Related

IndexError that i Can solve, list index out of range [duplicate]

Here is where my error jumps onto the screen. With this message:
self.rect = self.image.get_rect()
AttributeError: 'list' object has no attribute 'get_rect'
Because im trying to make a sprite
rigto = [pygame.image.load("img/Iconos/guy1.png"), pygame.image.load("img/Iconos/guy2.png"), pygame.image.load("img/Iconos/guy3.png"), pygame.image.load("img/Iconos/guy4.png")]
class Player:
def __init__(self, x, y, image):
self.image = image
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
jugador1 = Player(500, 500, rigto)
rigto is a list of images (pygame.Surface objects). You have to choose one image:
e.g.:
jugador1 = Player(500, 500, rigto[1])
Alternatively, you can manage the images in the class (e.g. for animations):
class Player:
def __init__(self, x, y, image_list):
self.image_list = image_list
self.count = 0
self.image = self.image_list[self.count]
self.rect = self.image.get_rect(topleft = (x, y))
def update(self)
self.count += 1
frame = self.count // 10
if frame >= len(self.image_list):
self.count = 0
frame = 0
self.image = self.image_list[frame]
jugador1 = Player(500, 500, rigto)

How do i make interactive NPCs?

Hello i am making a graphic adventure/rpg with pygame.
Is there a way to make NPCs with pygame, and be able to interact with them, like having a dialog ?
I've been searching on the internet but i didn't have useful results. It would be great if someone could help me.
Here is the main code.
import pygame as pg
import sys
from os import path
from settings import *
from sprites import *
from tiledmap import *
from pgu import gui
from pygame.draw import circle
import pygame_ai as pai
import time
class Game:
def __init__(self):
pg.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.load_data()
def load_data(self):
game_folder = path.dirname(__file__)
img_folder = path.join(game_folder, 'img')
map_folder = path.join(game_folder, 'maps')
self.map = TiledMap(path.join(map_folder, 'mapa_inici.tmx'))
self.map_img = self.map.make_map()
self.map_rect = self.map_img.get_rect()
self.player_img = pg.image.load(path.join(img_folder, PLAYER_IMG)).convert_alpha()
def new(self):
# iniciar totes les variables i fer tota la preparació per a una nova partida
self.all_sprites = pg.sprite.Group()
self.walls = pg.sprite.Group()
#for row, tiles in enumerate(self.map.data):
#for col, tile in enumerate(tiles):
#if tile == '1':
#Wall(self, col, row)
#if tile == 's':
#self.player = Player(self, col, row)
for tile_object in self.map.tmxdata.objects:
if tile_object.name == 'Jugador':
self.player = Player(self, tile_object.x, tile_object.y)
if tile_object.name == 'Muro':
Obstacle(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
self.camera = Camera(self.map.width, self.map.height)
def run(self):
# bucle del joc - s'iguala self.playing = False per finalitzar el joc
self.playing = True
while self.playing:
self.dt = self.clock.tick(FPS) / 1000
self.events()
self.update()
self.draw()
def quit(self):
pg.quit()
sys.exit()
def update(self):
# update portion of the game loop
self.all_sprites.update()
self.camera.update(self.player)
def draw_grid(self):
for x in range(0, WIDTH, TILESIZE):
pg.draw.line(self.screen, LIGHTGREY, (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, TILESIZE):
pg.draw.line(self.screen, LIGHTGREY, (0, y), (WIDTH, y))
def draw(self):
self.screen.blit(self.map_img, self.camera.apply_rect(self.map_rect))
for sprite in self.all_sprites:
self.screen.blit(sprite.image, self.camera.apply(sprite))
pg.display.flip()
def events(self):
# tots els events
for event in pg.event.get():
if event.type == pg.QUIT:
self.quit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.quit()
g = Game()
while True:
g.new()
g.run()
The sprites code
import pygame as pg
from os import path
import sys
from settings import *
import pygame_ai as pai
from tiledmap import TiledMap
vec = pg.math.Vector2
class Player(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = game.player_img
self.rect = self.image.get_rect()
self.vel = vec(0,0)
self.pos = vec(x, y)
self.rot = 0
def get_keys(self):
self.vel = vec(0,0)
keys = pg.key.get_pressed()
if keys[pg.K_LEFT] or keys[pg.K_a]:
self.vel.x = -PLAYER_SPEED
if keys[pg.K_RIGHT] or keys[pg.K_d]:
self.vel.x = PLAYER_SPEED
if keys[pg.K_UP] or keys[pg.K_w]:
self.vel.y = -PLAYER_SPEED
if keys[pg.K_DOWN] or keys[pg.K_s]:
self.vel.y = PLAYER_SPEED
if self.vel.x != 0 and self.vel.y != 0:
self.vel *= 0.7071
def collide_walls(self,dir):
if dir == 'x':
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vel.x > 0:
self.pos.x = hits[0].rect.left - self.rect.width
if self.vel.x < 0:
self.pos.x = hits[0].rect.right
self.vel.x = 0
self.rect.x = self.pos.x
if dir == 'y':
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vel.y > 0:
self.pos.y = hits[0].rect.top - self.rect.height
if self.vel.y < 0:
self.pos.y = hits[0].rect.bottom
self.vel.y = 0
self.rect.y = self.pos.y
def update(self):
self.get_keys()
self.pos += self.vel * self.game.dt
self.rect.x = self.pos.x
self.collide_walls('x')
self.rect.y = self.pos.y
self.collide_walls('y')
class Obstacle(pg.sprite.Sprite):
def __init__(self, game, x, y, w, h):
self.groups = game.walls
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pg.Rect(x, y, w, h)
self.x = x
self.y = y
self.rect.x = x
self.rect.y = y
The tile map code
import pygame as pg
import pytmx
from settings import *
class Map:
def __init__(self, filename):
self.data = []
with open(filename, 'rt') as f:
for line in f:
self.data.append(line.strip())
self.tilewidth = len(self.data[0])
self.tileheight = len(self.data)
self.width = self.tilewidth * TILESIZE
self.height = self.tileheight * TILESIZE
class TiledMap:
def __init__(self, filename):
tm = pytmx.load_pygame(filename, pixelalpha=True)
self.width = tm.width * tm.tilewidth
self.height = tm.height * tm.tileheight
self.tmxdata = tm
def render(self, surface):
ti = self.tmxdata.get_tile_image_by_gid
for layer in self.tmxdata.visible_layers:
if isinstance(layer, pytmx.TiledTileLayer):
for x, y, gid, in layer:
tile = ti(gid)
if tile:
surface.blit(tile, (x * self.tmxdata.tilewidth, y * self.tmxdata.tileheight))
def make_map(self):
temp_surface = pg.Surface((self.width, self.height))
self.render(temp_surface)
return temp_surface
class Camera:
def __init__(self, width, height):
self.camera = pg.Rect(0,0,width,height)
self.width = width
self.height = height
def apply(self, entity):
return entity.rect.move(self.camera.topleft)
def apply_rect(self, rect):
return rect.move(self.camera.topleft)
def update(self, target):
x = -target.rect.centerx + int(WIDTH / 2)
y = -target.rect.centery + int(HEIGHT / 2)
# limit al seguiment del personatge
x = min(0,x) # esquerra
y = min(0,y) # part de dalt
x = max(-(self.width - WIDTH), x) # dreta
y = max(-(self.height - HEIGHT), y) # part de baix
self.camera = pg.Rect(x, y, self.width, self.height)
In the 1960's there was a simple dialogue-like handler named Eliza. Since then there has been many changes and variations, and they have become known as "chat bots". Perhaps your NPCs could have an Eliza-like conversation with the player? It might be worth doing some research on these sort of natural text processors.
However if you mean simply choosing conversation topics from, say an [(a), (b), (c)] type list, these dialogues would typically be mapped out in a graph of choices. Have you read stories where you make choices to change the narrative? These are a simple graph of choices. You could draw them out on a piece of paper then encode them into a data-structure, maybe a python dictionary. Each option moves to another node in the graph. Perhaps they also loop around back upon themselves.

pygame collissions between two groups

I still have a problem with my software to check collisions between spaceship and asteroids. I have got no idea why I get a collision only in the top left corner of the screen.
any ideas ? any help please ?
import pygame, sys
import random
import math
from pygame.locals import KEYDOWN, K_SPACE
pygame.init()
pygame.display.set_caption("ASTROCRASH version 0.1 >>> DKR103 <<<")
clock = pygame.time.Clock()
SCREENH = 600
SCREENW = 800
SCREEN = pygame.display.set_mode((SCREENW, SCREENH))
sGRAD = math.pi/180
BLACK = (0,0,0)
WHITE = (255,255,255)
BBB = (0, 75, 230)
ASTEROIDS = []
MISSILES = []
SPACESHIPS = []
class AsteroidSprite(pygame.sprite.Sprite):
def __init__(self,posX,posY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/asteroid3.png").convert()
self.rect = self.image.get_rect()
self.x = posX
self.y = posY
self.speed = 2
self.dx = random.choice([1,-1]) * self.speed * random.random()
self.dy = random.choice([1,-1]) * self.speed * random.random()
def update(self):
if self.y > SCREENH:
self.y = (0 - self.rect[3])
if self.y < (0 - self.rect[3]):
self.y = SCREENH
if self.x > SCREENW:
self.x = (0 - self.rect[2])
if self.x < (0 - self.rect[2]):
self.x = SCREENW
self.x += self.dx
self.y += self.dy
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#load original image
self.imageMaster = pygame.image.load("img/spaceship.png")
self.imageMaster = self.imageMaster.convert()
###self.imageMaster.set_colorkey(WHITE)
#set Sprite attribute
self.image = self.imageMaster
#get Sprite rect
self.rect = self.image.get_rect()
self.rect.center = ((SCREEN.get_width()/2),(SCREEN.get_height()/2))
#initial rotation value
self.dir = 0
#ship movement speed
self.speed = 5
def rotation(self):
#set Sprite center before rotation
self.oldCenter = self.rect.center
#rotate Sprite
self.image = pygame.transform.rotate(self.imageMaster,self.dir)
self.rect= self.image.get_rect()
#set new Sprite center equal to old Center so it stays in place
self.rect.center = self.oldCenter
self.value = self.dir * math.pi / 180
def update(self):
#move
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
self.rect[0] -= self.speed * math.sin(self.value)
self.rect[1] -= self.speed * math.cos(self.value)
#rotate
if key[pygame.K_LEFT]:
self.dir += 5
if self.dir > 360:
self.dir = 15
if key[pygame.K_RIGHT]:
self.dir -= 5
if self.dir < 0:
self.dir = 355
#outside SCREEN conditions
if self.rect[1] > SCREENH:
self.rect[1] = (0 - self.rect[3])
if self.rect[1] < (0 - self.rect[3]):
self.rect[1] = SCREENH
if self.rect[0] > SCREENW:
self.rect[0] = (0 - self.rect[2])
if self.rect[0] < (0 - self.rect[2]):
self.rect[0] = SCREENW
def draw(self):
SCREEN.blit(self.image,(self.rect[0],self.rect[1]))
def main():
#spaceship
spaceship = Ship()
SPACESHIPS.append(spaceship)
for i in range(8):
ASTEROIDS.append(AsteroidSprite(300,300))
runGame = True
while runGame:
clock.tick(60)
SCREEN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runGame = False
#update asteroids
for i in range(8):
ASTEROIDS[i].update()
SCREEN.blit(ASTEROIDS[i].image,(ASTEROIDS[i].x,ASTEROIDS[i].y))
for a in ASTEROIDS:
if pygame.sprite.spritecollide(a,SPACESHIPS,0):
SCREEN.fill(BBB)
spaceship.rotation()
spaceship.update()
spaceship.draw()
print spaceship.rect[0]
pygame.display.update()
main()
pygame.quit()
The pygame.sprite.groupcollide() function finds collisions between all sprites of the two passed sprite-groups and returns a dictionary containing the collision information, as the documentation states.
Because the collision is determined by comparing the sprite.rect attribute of each sprite, every time you call an update() method of an sprite instance you need to update the position of self.rect object, instead of changing its self.x and self.y attributes.
Your Ship class is ok, because you change the self.rect object and its own .x or .y attributes. (e.g. self.rect[1] = (0 - self.rect[3])).But in your AsteroidSprite class you create a rect object in the __init__() method and only change the self.x and self.y attributes of an instance when you call the .update() method.
What you need to change:
The .update() method of the AsteroidSprite class, because you need to change the self.rect objects position, which is used for collision detection.
The if statement where you check for a collision, because pygame.sprite.groupcollide() returns a dict object, not a Boolean value.
I hope this helps you a little bit :)
Many thanks. Your answer helped me to solve the problem.
updated code:
import pygame, sys
import random
import math
from pygame.locals import KEYDOWN, K_SPACE
pygame.init()
pygame.display.set_caption("ASTROCRASH version 0.1 >>> DKR103 <<<")
clock = pygame.time.Clock()
SCREENH = 600
SCREENW = 800
SCREEN = pygame.display.set_mode((SCREENW, SCREENH))
sGRAD = math.pi/180
BLACK = (0,0,0)
WHITE = (255,255,255)
BBB = (0, 75, 230)
ASTEROIDS = []
MISSILES = []
SPACESHIPS = []
class AsteroidSprite(pygame.sprite.Sprite):
def __init__(self,posX,posY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/asteroid3.png").convert()
self.rect = self.image.get_rect()
self.rect.x = posX
self.rect.y = posY
self.speed = 2
self.dx = random.choice([1,-1]) * self.speed * random.random()
self.dy = random.choice([1,-1]) * self.speed * random.random()
def update(self):
if self.rect.y > SCREENH:
self.rect.y = (0 - self.rect[3])
if self.rect.y < (0 - self.rect[3]):
self.rect.y = SCREENH
if self.rect.x > SCREENW:
self.rect.x = (0 - self.rect[2])
if self.rect.x < (0 - self.rect[2]):
self.rect.x = SCREENW
self.rect.x += self.dx
self.rect.y += self.dy
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#load original image
self.imageMaster = pygame.image.load("img/spaceship.png")
self.imageMaster = self.imageMaster.convert()
###self.imageMaster.set_colorkey(WHITE)
#set Sprite attribute
self.image = self.imageMaster
#get Sprite rect
self.rect = self.image.get_rect()
self.rect.center = ((SCREEN.get_width()/2),(SCREEN.get_height()/2))
#initial rotation value
self.dir = 0
#ship movement speed
self.speed = 5
def rotation(self):
#set Sprite center before rotation
self.oldCenter = self.rect.center
#rotate Sprite
self.image = pygame.transform.rotate(self.imageMaster,self.dir)
self.rect= self.image.get_rect()
#set new Sprite center equal to old Center so it stays in place
self.rect.center = self.oldCenter
self.value = self.dir * math.pi / 180
def update(self):
#move
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
self.rect[0] -= self.speed * math.sin(self.value)
self.rect[1] -= self.speed * math.cos(self.value)
#rotate
if key[pygame.K_LEFT]:
self.dir += 5
if self.dir > 360:
self.dir = 15
if key[pygame.K_RIGHT]:
self.dir -= 5
if self.dir < 0:
self.dir = 355
#outside SCREEN conditions
if self.rect[1] > SCREENH:
self.rect[1] = (0 - self.rect[3])
if self.rect[1] < (0 - self.rect[3]):
self.rect[1] = SCREENH
if self.rect[0] > SCREENW:
self.rect[0] = (0 - self.rect[2])
if self.rect[0] < (0 - self.rect[2]):
self.rect[0] = SCREENW
def draw(self):
SCREEN.blit(self.image,(self.rect[0],self.rect[1]))
def main():
#spaceship
spaceship = Ship()
SPACESHIPS.append(spaceship)
for i in range(8):
ASTEROIDS.append(AsteroidSprite(300,300))
runGame = True
while runGame:
clock.tick(60)
SCREEN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runGame = False
#update asteroids
for i in range(8):
ASTEROIDS[i].update()
SCREEN.blit(ASTEROIDS[i].image,(ASTEROIDS[i].rect.x,ASTEROIDS[i].rect.y))
for a in ASTEROIDS:
if pygame.sprite.spritecollide(a,SPACESHIPS,0):
SCREEN.fill(BBB)
spaceship.rotation()
spaceship.update()
spaceship.draw()
print spaceship.rect[0]
pygame.display.update()
main()
pygame.quit()

AtrributeError with basic sprite collision

Just started with sprite collision in pygame. When this code is run, an AttributeError pops up that says ''Group' object has no attribute 'rect''. I can't figure out why this error occurs. Suggestions?
from random import randint
import pygame
pygame.init()
white = [255,255,255]
blue = [0,0,255]
red = [255,0,0]
size = (400,400)
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Simple character')
clock = pygame.time.Clock()
class Ball(pygame.sprite.Sprite):
def __init__(self, xDelta, yDelta, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20,20])
self.image.fill(white)
self.image.set_colorkey(white)
self.xDelta = xDelta
self.yDelta = yDelta
self.color = color
self.rect = self.image.get_rect()
pygame.draw.circle(self.image, self.color,
[(self.rect.x + 10), (self.rect.y + 10)], 10)
def update(self):
self.rect.x += self.xDelta
self.rect.y += self.yDelta
if self.rect.y <= 0 or self.rect.y >= 380:
self.yDelta *= -1
if self.rect.x >= 380 or self.rect.x <= 0:
self.xDelta *= -1
allSprites = pygame.sprite.Group()
blues = pygame.sprite.Group()
reds = pygame.sprite.Group()
for i in range(5):
circle = Ball(randint(1, 10), randint(1, 10), blue)
circle.rect.x = randint(0,380)
circle.rect.y = randint(0,380)
blues.add(circle)
allSprites.add(circle)
circle2 = Ball(randint(1, 10), randint(1, 10), red)
circle2.rect.x = randint(0,380)
circle2.rect.y = randint(0,380)
reds.add(circle2)
allSprites.add(circle2)
play = True
while play:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
play = False
allSprites.update()
collision = pygame.sprite.spritecollide(blues, reds, True)
for circle in collision:
circle.xDelta = circle.yDelta = 0
screen.fill(white)
allSprites.draw(screen)
pygame.display.flip()
pygame.quit()
You are calling spritecollide on two objects of class Group. The function takes a Sprite and a Group.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
What you could do is loop through all the blues sprites, and call spritecollide with one blue ball.
for blueball in blues.sprites:
collision = pygame.sprite.spritecollide(blues, reds, True)
for circle in collision:
circle.xDelta = circle.yDelta = 0

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
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)