Have issues with collision detection in pygame - pygame

def movement(self):
prevX = self.x
prevY = self.y
key = pygame.key.get_pressed()
if key[pygame.K_w]:
self.y -= 32
elif key[pygame.K_s]:
self.y += 32
elif key[pygame.K_a]:
self.x -= 32
elif key[pygame.K_d]:
self.x += 32
x = int(self.x / 32)
y = int(self.y / 32)
if TEXT_LEVEL[x][y] != "=":
print("wall")
print(x,y)
print(TEXT_LEVEL[x][y])
self.rect.topleft = (self.x, self.y)
self.x = prevX
self.y = prevY
So I have an array that stores the map as a text file as such:
============.===============
=..........=...............=
=..........=...............=
=...........*..............=
=....===============.......=
=....=.....................=
=....=.............=.......=
.....===============........
=..................*.......=
=..........................=
=....===============.......=
=....*.............*.......=
=........=====.............=
= .........................=
============.===============
And what I'm doing is checking if the current position my sprite is in matches to the coordinates of a wall. If it does I reset the x and y and don't update its location. For some reason, this is not working and I'm stuck as to why it's failing.
The full project can be found on my github if anything needs to be referenced: https://github.com/rob-roibu/Pac-man

You need to update self.rect.topleft after changing self.x and self.y:
self.x = prevX
self.y = prevY
self.rect.topleft = (self.x, self.y)

Related

Pygame - Issues creating projectiles, "add() argument after * must be an iterable, not int"

I am creating an Asteroids type game using Pygame and am having trouble firing projectiles. I have a sprite group for the projectile object and some code to add the projectile.
if pressed[pygame.K_SPACE]:
projectiles.add(Projectile(player.rect.x, player.rect.y, player.direction))
Problem is when I press space in game to fire the projectile I get the error "add() argument after * must be an iterable, not int".
I have a very similar statement for adding asteroids when the game first starts without any issues so I'm not really sure what the problem is. I'll leave the rest of the code below. The add statement giving issues is in the main function near the bottom. Any help is appreciated.
#Import Modules
import pygame
import math
import random
#Movement Function
def calculate_new_xy(old_xy,speed,direction):
new_x = old_xy[0] + (speed*math.cos(direction))
new_y = old_xy[1] + (speed*math.sin(direction))
return new_x, new_y
#Collision Function
def isColliding(x, y, xTo, yTo, size):
if x > xTo - size and x < xTo + size and y > yTo - size and y < yTo + size:
return True
return False
#Draw Text Function
def drawText(msg, color, x, y, s, center=True):
screen_text = pygame.font.SysFont("Impact", s).render(msg, True, color)
if center:
rect = screen_text.get_rect()
rect.center = (x, y-50)
else:
rect = (x, y)
display.blit(screen_text, rect)
#Initialize Variables
#Colors
white = (255, 255, 255)
black = (0, 0, 0)
#Display Height/Width
display_width = 800
display_height = 600
#Asteroid Class
class Asteroid(pygame.sprite.Sprite):
#Initialize values
def __init__(self, pos=(0, 0)):
#Initialize sprite class
pygame.sprite.Sprite.__init__(self)
#Asteroid sprite
self.asteroid = pygame.image.load("asteroid.png").convert()
self.image = self.asteroid
#Rectangle
self.rect = self.image.get_rect()
self.rect.center = pos
#Initialize random starting angle
self.angle = random.randint(0, 360)
#Asteroid random Speed
self.speed = random.randint(2, 3)
#Asteroid random direction
self.direction = math.radians(random.randrange(0, 360, 3))
#Update asteroid object
def update(self):
#Constantly rotate asteroid
self.angle -= 3 % 360
#Get image angle and position
self.image = pygame.transform.rotate(self.asteroid, self.angle*-1)
#Use rectangle to get center of image
#Save ship's current center.
x, y = self.rect.center
#Replace old rect with new rect.
self.rect = self.image.get_rect()
#Put the new rect's center at old center.
self.rect.center = (x, y)
#Move Asteroid
self.rect.center = calculate_new_xy(self.rect.center,self.speed,self.direction)
#Screen Border
#Moves the asteroid to the opposite side of the screen if they go outside the border
if self.rect.x > display_width:
self.rect.x = -20
elif self.rect.x < -20:
self.rect.x = display_width
elif self.rect.y > display_height:
self.rect.y = -20
elif self.rect.y < -20:
self.rect.y = display_height
#Projectile Class
class Projectile(pygame.sprite.Sprite):
#Initialize values
def _init_(self,x,y,direction):
self.x = x
self.y = y
self.dir = direction
self.ttl = 30
#Update projectile object
def update(self):
#Changing direction
self.x += projectilespd * math.cos(self.direction)
self.y += projectilespd * math.sin(self.direction)
#Draw projectile
pygame.draw.circle(display, white, (self.x,self.y),1)
#Screen Border
if self.x > display_width:
self.x = 0
elif self.x < 0:
self.x = display_width
elif self.y > display_height:
self.y = 0
elif self.y < 0:
self.y = display_height
self.ttl -= 1
#Player Class
class Player(pygame.sprite.Sprite):
#Initialize ship sprite, angle lines, and rectangle
def __init__(self, pos=(0, 0), size=(200, 200)):
#Player sprite
self.ship = pygame.image.load("ship.png").convert()
self.image = self.ship
#Rectangle
self.rect = self.image.get_rect()
self.rect.center = pos
#Initialize angle
self.angle = 0
#Initialize direction
self.direction = 0
#Update player object
def update(self):
#Rotation
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: self.angle -= 3 % 360
if pressed[pygame.K_RIGHT]: self.angle += 3 % 360
#Get image angle and position
self.image = pygame.transform.rotate(self.ship, self.angle*-1)
#Use rectangle to get center of image
#Save ship's current center.
x, y = self.rect.center
#Replace old rect with new rect.
self.rect = self.image.get_rect()
#Put the new rect's center at old center.
self.rect.center = (x, y)
#Convert angle to radians
self.direction = math.radians(self.angle-90)
#Increase speed if Up is pressed
if pressed[pygame.K_UP]: self.speed = 5
else: self.speed = 0
#Move Ship
self.rect.center = calculate_new_xy(self.rect.center,self.speed,self.direction)
#Screen Border
#Moves the player to the opposite side of the screen if they go outside the border
if self.rect.x > display_width:
self.rect.x = -50
elif self.rect.x < -50:
self.rect.x = display_width
elif self.rect.y > display_height:
self.rect.y = -50
elif self.rect.y < -50:
self.rect.y = display_height
#Main Function
def main(gameState):
#Player starting position
player = Player(pos=(400, 300))
#Asteroid group
asteroids = pygame.sprite.Group()
#Projectile group
projectiles = pygame.sprite.Group()
#Create asteroids
for x in range(8):
asteroids.add(Asteroid(pos=(100 + (x*120), 100 + (x*20))))
while True:
for event in pygame.event.get():
#closes game
if event.type == pygame.QUIT:
done = True
pygame.quit()
exit()
#Game Menu
while gameState == "Menu":
#Fill background
display.fill((0,0,0))
#Display menu text
drawText("ASTEROIDS", white, display_width / 2, display_height / 2, 150)
drawText("Press any key to START", white, display_width / 2, display_height / 2 + 120, 40)
#Check game start or end
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
gameState = "Playing"
pygame.display.update()
#Low frame rate for menu
clock.tick(5)
#Get key inputs
pressed = pygame.key.get_pressed()
#Fill background
display.fill(black)
#Check for player collision with asteroid
for asteroid in asteroids:
if gameState != "Game Over":
if isColliding(player.rect.x, player.rect.y, asteroid.rect.x, asteroid.rect.y, 30):
gameState = "Game Over"
#Update and draw player if not game over
if gameState != "Game Over":
#Update player
player.update()
#Draw player
display.blit(player.image, player.rect)
#Update asteroids
asteroids.update()
#Draw asteroids
asteroids.draw(display)
#Fire Projectiles
if pressed[pygame.K_SPACE]:
projectiles.add(Projectile(player.rect.x, player.rect.y, player.direction))
#Update projectiles
projectiles.update()
#Draw projectiles
projectiles.draw(display)
#Display Game Over and restart option
if gameState == "Game Over":
drawText("GAME OVER", white, display_width / 2, display_height / 2, 150)
drawText("Press R to restart", white, display_width / 2, display_height / 2 + 120, 40)
if pressed[pygame.K_r]:
main(gameState = "Playing")
#Makes updates to the game screen
pygame.display.update()
#Frame rate
clock.tick(60)
#Initialize Game
if __name__ == '__main__':
#initialize pygame
pygame.init()
#initialize display settings
display = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Asteroids')
#initialize game clock
clock = pygame.time.Clock()
#start main function
main(gameState = "Menu")
The issue is not the pygame.sprite.Group.add operation, but the obejct you want to add is not a pygame.sprite.Sprite object, because the object is not constructed at all.
You missed to the super call in the constructor of Projectile. Furthermore the name of the constructor has to be __init__ rather _init_:
class Projectile(pygame.sprite.Sprite):
#Initialize values
def __init__(self,x,y,direction):
super.__init__()
self.x = x
self.y = y
self.dir = direction
self.ttl = 30

Pygame character face mouse

I am creating a basic 2d overhead view game, and am currently working to make the character "face" the mouse as I move it.
Here is my code so far:
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super(Player,self).__init__()
self.image=player_image
self.original_image=self.image
self.rect=self.image.get_rect(center=(x,y))
self.change_x=0
self.change_y=0
self.speed=7.5
self.inventory={'wood':0}
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self,rect):
player.rotate()
self.rect.x+=self.change_x
self.rect.y+=self.change_y
screen.blit(self.image,(rect.x,rect.y))
def check(self):
global tick
if pygame.mouse.get_pressed()[0]:
for tree in resources:
tree_rect=tree.rect
if self.rect.colliderect(tree_rect) and tick>=9:
self.inventory['wood']+=1
tick=0
def rotate(self):
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - self.rect.x, mouse_y -self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = pygame.transform.rotate(self.original_image, int(angle))
self.rect = self.image.get_rect(center=(self.rect.x,self.rect.y))
To do this, I followed this answer. It supposedly calculated the vector between the mouse and the player, and turned the character around toward the mouse each step, but for me the sprite wobbled around when the mouse moved, and turned invisible.
Full code:
import pygame
from pygame.locals import *
import sys
import math
import pygame.gfxdraw
import random
pygame.init()
black=(0,0,0)
white=(255,255,255)
forest=(34,139,34)
red=(255,0,0)
blue=(0,0,255)
green=(0,255,0)
light_green=(0,120,0)
dark_green=(0,90,0)
skin=(255,224,189)
yellow=(255,255,0)
wood_image=pygame.image.load('./assets/images/wood.png')
stone_image=pygame.image.load('./assets/images/stone.png')
player_image=pygame.image.load('./assets/images/player.png')
tick=0
def terminate():
pygame.quit()
sys.exit()
def drawTextcenter(text,font,screen,x,y,color):
textobj=font.render(text,True,color)
textrect=textobj.get_rect(center=(x,y))
screen.blit(textobj,textrect)
def drawText(text, font, surface, x, y,color):
textobj=font.render(text, 1, color)
textrect=textobj.get_rect()
textrect.topleft=(x, y)
surface.blit(textobj, textrect)
class Button(object):
global screen_width,screen_height,screen
def __init__(self,x,y,width,height,text_color,background_color,text):
self.rect=pygame.Rect(x,y,width,height)
self.x=x
self.y=y
self.width=width
self.height=height
self.text=text
self.text_color=text_color
self.background_color=background_color
self.angle=0
def check(self):
return self.rect.collidepoint(pygame.mouse.get_pos())
def draw(self):
pygame.draw.rect(screen, self.background_color,(self.rect),0)
drawTextcenter(self.text,font,screen,self.x+self.width/2,self.y+self.height/2,self.text_color)
pygame.draw.rect(screen,self.text_color,self.rect,3)
class Bar(object):
def __init__(self,x,y,length,color):
self.rect=pygame.Rect(x,y,length,17.5)
self.image=pygame.draw.rect(screen,white,(self.rect),)
self.x=x
self.y=y
self.width=100
self.height=17.5
self.color=color
self.multiplier=length/100
def draw(self,num):
rect=pygame.Rect(self.x,self.y,num*self.multiplier,self.height)
pygame.draw.rect(screen,self.color,self.rect,3)
pygame.draw.rect(screen,self.color,rect,0)
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super(Player,self).__init__()
self.image=player_image
self.original_image=self.image
self.rect=self.image.get_rect(center=(x,y))
self.change_x=0
self.change_y=0
self.speed=7.5
self.inventory={'wood':0}
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self,rect):
self.rotate(camera)
self.rect.x += self.change_x
self.rect.y += self.change_y
rect = camera.apply(self)
screen.blit(self.image, rect)
def check(self):
global tick
if pygame.mouse.get_pressed()[0]:
for tree in resources:
tree_rect=tree.rect
if self.rect.colliderect(tree_rect) and tick>=9:
self.inventory['wood']+=1
tick=0
def rotate(self,camera):
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_x -= camera.state.x
mouse_y -= camera.state.y
rel_x, rel_y = mouse_x - self.rect.centerx, mouse_y -self.rect.centery
angle = -math.degrees(math.atan2(rel_y, rel_x))
self.image = pygame.transform.rotate(self.original_image, angle)
self.rect = self.image.get_rect(center=self.rect.center)
class Tree(pygame.sprite.Sprite):
def __init__(self,x,y):
super(Tree,self).__init__()
self.rect=pygame.Rect(x,y,100,100)
self.rect.x=x
self.rect.y=y
def update(self,rect):
pygame.gfxdraw.filled_circle(screen,rect.x,rect.y,80,light_green)
pygame.gfxdraw.aacircle(screen,rect.x,rect.y,80,light_green)
class Rock(pygame.sprite.Sprite):
def __init__(self):
pass
class Camera(object):
def __init__(self,camera_func,width,height):
self.camera_func=camera_func
self.state=pygame.Rect(0,0,width,height)
def apply(self,target):
return target.rect.move(self.state.topleft)
def update(self,target):
self.state=self.camera_func(self.state,target.rect)
def complex_camera(camera, target_rect):
l, t = target_rect.center
_,_,w,h = camera
l,t,_,_ = -l+screen_width/2, -t+screen_height/2, w, h
l = min(0, l)
l = max(-(camera.width-screen_width), l)
t = max(-(camera.height-screen_height), t)
t = min(0, t)
return pygame.Rect(l, t, w, h)
clock=pygame.time.Clock()
font=pygame.font.SysFont(None,40)
screen_width=1440
screen_height=800
screen=pygame.display.set_mode([screen_width,screen_height])
pygame.display.set_caption('Survival')
total_level_width=screen_width*5
total_level_height=screen_height*5
camera = Camera(complex_camera, total_level_width, total_level_height)
player=Player(random.randint(100,7900),random.randint(100,5900))
friendlies=pygame.sprite.Group()
friendlies.add(player)
player_health_bar=Bar(125,15,200,green)
player_health=100
player_food_bar=Bar(437.5,15,200,red)
player_food=100
player_thirst_bar=Bar(750,15,200,blue)
player_thirst=100
player_energy_bar=Bar(1100,15,200,yellow)
player_energy=100
resources=pygame.sprite.Group()
wood=False
inventory={}
for i in range(1000):
tree=Tree(random.randint(100,7900),random.randint(100,5900))
resources.add(tree)
done1=False
while not done1:
screen.fill(black)
font=pygame.font.SysFont(None, 90)
text_width,text_height=font.size('Survival')
drawText('Survival', font, screen, (screen_width/2-text_width/2), (screen_height / 2-375),white)
font=pygame.font.SysFont(None, 40)
start_button=Button(screen_width/2-125,650,250,50,white,black,'Start')
start_button.draw()
back_button=Button(screen_width/2-125,725,250,50,white,black,'Back')
back_button.draw()
pygame.display.flip()
done2=False
while not done2:
for event in pygame.event.get():
if event.type==QUIT:
terminate()
elif event.type==pygame.MOUSEBUTTONDOWN:
if start_button.check()==True:
done3=False
pause_back=False
while not done3:
tick+=1
for event in pygame.event.get():
if event.type==pygame.QUIT:
terminate()
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_a:
player.changespeed(-(player.speed), 0)
elif event.key==pygame.K_d:
player.changespeed(player.speed, 0)
elif event.key==pygame.K_w:
player.changespeed(0, -(player.speed))
elif event.key==pygame.K_s:
player.changespeed(0, player.speed)
elif event.key==pygame.K_p:
font=pygame.font.SysFont(None, 90)
text_width,text_height=font.size('Paused')
drawText('Paused', font, screen, (screen_width / 2-(text_width/2)), (screen_height / 2-375),white)
resume_button=Button(screen_width/2-125,650,250,50,white,black,'Resume')
resume_button.draw()
back_button.draw()
pygame.display.flip()
back=False
while not back:
for event in pygame.event.get():
if event.type==QUIT:
terminate()
elif event.type==pygame.MOUSEBUTTONDOWN:
if resume_button.check()==True:
back=True
elif back_button.check()==True:
done3=True
done2=True
pause_back=True
back=True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.changespeed(player.speed, 0)
elif event.key == pygame.K_d:
player.changespeed(-(player.speed), 0)
elif event.key == pygame.K_w:
player.changespeed(0, player.speed)
elif event.key == pygame.K_s:
player.changespeed(0, -(player.speed))
camera.update(player)
if player.rect.x<0:
player.rect.x=0
if player.rect.right>total_level_width:
player.rect.right=total_level_width
if player.rect.y<0:
player.rect.y=0
if player.rect.bottom>total_level_height:
player.rect.bottom=total_level_height
screen.fill(dark_green)
player.update(camera)
for resource in resources:
resource.update(camera.apply(resource))
player_health_bar.draw(player_health)
player_food_bar.draw(player_food)
player_thirst_bar.draw(player_thirst)
#player_endergy_bar.draw(player_energy)
font=pygame.font.SysFont(None, 40)
drawText('Health:',font,screen,20,10,green)
drawText('Food:',font,screen,350,10,red)
drawText('Thirst:',font,screen,650,10,blue)
#drawText('Energy:', font, screen, 975, 10, yellow)
pygame.draw.rect(screen,forest,(50,675,100,100))
pygame.draw.rect(screen,forest,(175,675,100,100))
pygame.draw.rect(screen,forest,(300,675,100,100))
#player.check()
for item in player.inventory:
if item=='wood' and player.inventory['wood']>0:
wood=True
if wood:
screen.blit(wood_image,(62.5,687.5))
drawTextcenter(str(player.inventory['wood']),font,screen,100,735,black)
pygame.display.flip()
clock.tick(100)
if pause_back==True:
break
if pause_back==True:
break
screen.fill(black)
font=pygame.font.SysFont(None, 90)
text_width,text_height=font.size("Game Over")
drawText('Game Over', font, screen, (screen_width/2-text_width/2), (screen_height / 2-200),white)
font=pygame.font.SysFont(None, 40)
retry_button=Button(screen_width/2-125,650,250,50,white,black,'Retry')
retry_button.draw()
back_button.draw()
pygame.display.flip()
back=False
while not back:
for event in pygame.event.get():
if event.type==QUIT:
terminate()
elif event.type==pygame.MOUSEBUTTONDOWN:
if retry_button.check()==True:
back=True
if back_button.check()==True:
back=True
done2=True
done3=True
elif back_button.check()==True:
done2=True
done1=True
I haven't checked out your complete program because it's too much code, but I can see a mistake in the Player class. You're assigning the (self.rect.x,self.rect.y) (the top left) coordinates to the center of the new rect in the rotate method, but you have to assign the center coords of the previous rect.
self.rect = self.image.get_rect(center=(self.rect.x,self.rect.y))
Change the line above to:
self.rect = self.image.get_rect(center=self.rect.center)
Here's a minimal, complete example:
import math
import pygame as pg
class Player(pg.sprite.Sprite):
def __init__(self, pos):
super(Player,self).__init__()
x, y = pos
self.image = pg.Surface((50, 30), pg.SRCALPHA)
pg.draw.polygon(
self.image,
pg.Color('dodgerblue1'),
((1, 1), (49, 15), (1, 29)))
self.original_image = self.image
self.rect=self.image.get_rect(center=(x,y))
self.change_x=0
self.change_y=0
self.speed=7.5
self.inventory={'wood':0}
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self):
self.rotate()
self.rect.x+=self.change_x
self.rect.y+=self.change_y
def rotate(self):
mouse_x, mouse_y = pg.mouse.get_pos()
rel_x, rel_y = mouse_x - self.rect.x, mouse_y -self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = pg.transform.rotate(self.original_image, angle)
# rect.x and .y are the topleft coords, but you need the to
# pass the center coordinates of the previous rect to the new one.
self.rect = self.image.get_rect(center=self.rect.center)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
player = Player((300, 200))
all_sprites.add(player)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_d:
player.change_x = 4
elif event.key == pg.K_a:
player.change_x = -4
elif event.type == pg.KEYUP:
if event.key == pg.K_d:
player.change_x = 0
elif event.key == pg.K_a:
player.change_x = 0
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
The problem is caused by the camera. You have to apply the camera offset to the mouse position as well.
Here's a simple solution. First, pass the camera instead of the rect to the player.update method.
player.update(camera)
Pass it to the rotate method as well.
def update(self, camera):
self.rotate(camera)
self.rect.x += self.change_x
self.rect.y += self.change_y
rect = camera.apply(self)
screen.blit(self.image, rect)
In the rotate method you have to subtract the camera position from the mouse position to get the world coordinates.
def rotate(self, camera):
mouse_x, mouse_y = pygame.mouse.get_pos()
# Subtract the camera offset from the mouse position.
mouse_x -= camera.state.x
mouse_y -= camera.state.y
# Use the center coordinates of the rect.
rel_x, rel_y = mouse_x - self.rect.centerx, mouse_y -self.rect.centery
angle = -math.degrees(math.atan2(rel_y, rel_x))
self.image = pygame.transform.rotate(self.original_image, angle)
# Set the new center to the previous center coords.
self.rect = self.image.get_rect(center=self.rect.center)
I also had to modify the complex_camera function, because it should work with the center coordinates.
def complex_camera(camera, target_rect):
# The camera works with the center instead of
# the topleft coordinates now.
l, t = target_rect.center
_,_,w,h = camera
l,t,_,_ = -l+screen_width/2, -t+screen_height/2, w, h
l = min(0, l)
l = max(-(camera.width-screen_width), l)
t = max(-(camera.height-screen_height), t)
t = min(0, t)
return pygame.Rect(l, t, w, h)

Attribute error: 'module' object has no attribute - No more help needed

I am new to programming and I keep getting this error in my code. Here is my full code:
import sys, pygame, math;
from pygame.locals import *;
from sys import exit
pygame.init()
blk = pygame.Color(0,0,0)
BG = ('BG.png')
pygame.init()
screen = pygame.display.set_mode((800, 600))
#mousec = pygame.image.load(mouse_c).convert_alpha()
B_G = pygame.image.load(BG).convert_alpha()
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
fpsclock = pygame.time.Clock()
Shrek = pygame.image.load('SHORK.png').convert_alpha()
Shrek_Rect = Shrek.get_rect()
class Shork(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('SHORK.png')
screen = pygame.display.get_surface()
self.x = 62
self.y = 50
self.direction = "down"
def Moving(self):
if self.direction == "right":
self.x += 2
elif self.direction == "left":
self.x -= 2
elif self.direction == "down":
self.y += 2
elif self.direction == "up":
self.y -= 2
def Path(self): # Moves the "SHORK" image on the screen
if self.x == 62 and self.y == 538:
self.direction = "right"
if self.x == 246 and self.y == 538:
self.direction = "up"
if self.x == 246 and self.y == 366:
self.direction = "left"
if self.x == 176 and self.y == 366:
self.direction = "up"
if self.x == 176 and self.y == 114:
self.direction = "right"
if self.x == 530 and self.y == 114:
self.direction = "down"
if self.x == 530 and self.y == 366:
self.direction = "left"
if self.x == 460 and self.y == 366:
self.direction = "down"
if self.x == 460 and self.y == 538:
self.direction = "right"
if self.x == 644 and self.y == 538:
self.direction = "up"
if self.y == 0:
sys.exit()
Shork = Shork()
Run = True
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
print("test1")
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
print("test3")
while Run:
fpsclock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
Run = False
pos = pygame.Shrek_Rect
angle = 360-math.atan2(pos[1]-300,pos[0]-400)*180/math.pi
rotimage = pygame.transform.rotate(B_G,angle)
screen.fill(blk)
Shork.Moving()
Shork.Path()
screen.blit(Shork.image, (Shork.x, Shork.y))
pygame.display.update()
rect = rotimage.get_rect(center=(400,300))
screen.blit(rotimage,rect)
pygame.display.update()
#Rotates the BG image on the screen
This is where my error occurs:
pos = pygame.Shrek_Rect
angle = 360-math.atan2(pos[1]-300,pos[0]-400)*180/math.pi
rotimage = pygame.transform.rotate(B_G,angle)
The error says:
Traceback (most recent call last):
File "turny.py", line 96, in (module)
pos = pygame.Shork_Rect
AttributeError: 'module' object has no attribute 'Shrek_Rect'
The point of the program is to rotate the BG image towards the SHORK image as it moves on the screen. I repurposed this code from some old code that was meant to make BG follow the cursor. I am stuck on this error and cannot figure it out.
Also, if you replace Shrek_Rect with "mouse.get_pos()" where the error is BG will follow the player mouse.
Your issue is one with namespaces. Shrek_Rect is not defined within pygame, it's defined within the local namespace.
Try changing:
pos = pygame.Shrek_Rect
To:
pos = Shrek_Rect

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

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)