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
Related
This is my Sprite1.py program which I import to my main.py program. It contains all the classes I use in my main.py. I tried to create bullets based on a program I saw online but it doesn't work for me. Could anyone help me figure out how to successfully make it work? I don't know how to add the Bullet class from my Sprite.py program to the main.py one.
import pygame
import sys
import os
import time
import random
from pygame import mixer
from pygame.locals import *
def showStartScreen(surface):
show = True
while show == True:
startbg = pygame.image.load(os.path.join('images', 'Starting_scr.png'))
surface.blit(startbg, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
show = False
pygame.init()
class Player(pygame.sprite.Sprite):
'''
Spawn a player
'''
def __init__(self):
pygame.sprite.Sprite.__init__(self)
super().__init__()
self.movex = 0
self.movey = 0
self.frame = 0
self.images = []
self.imagesleft = []
self.imagesright = []
self.alpha = (0,0,0)
self.ani = 4 # animation cycles
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesright.append(img)
self.image = self.imagesright[0]
self.rect = self.image.get_rect()
#self.rect = self.image.get_rect(center=pos)
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.bullets = bullets
self.bullet_timer = .1
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img = pygame.transform.flip(img, True, False)
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesleft.append(img)
self.image = self.imagesleft[0]
self.rect = self.image.get_rect()
#self.rect = self.image.get_rect(center=pos)
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.bullets = bullets
self.bullet_timer = .1
def control(self,x,y):
'''
control player movement
'''
self.movex += x
self.movey -= y
self.bullets
def update(self, dt):
'''
Update sprite position
'''
self.rect.center = pg.mouse.get_pos()
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesleft[self.frame//self.ani]
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesright[self.frame//self.ani]
mouse_pressed = pg.mouse.get_pressed()
self.bullet_timer -= dt # Subtract the time since the last tick.
if self.bullet_timer <= 0:
self.bullet_timer = 0 # Bullet ready.
if mouse_pressed[0]: # Left mouse button.
# Create a new bullet instance and add it to the groups.
Bullet(pygame.mouse.get_pos(), self.all_sprites, self.bullets)
self.bullet_timer = .1 # Reset the timer.
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, *sprite_groups):
super().__init__(*sprite_groups)
BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png')).convert_alpha()
self.image = BULLET_IMG
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.math.Vector2(pos)
self.vel = pygame.math.Vector2(0, -450)
self.damage = 10
def update(self, dt):
# Add the velocity to the position vector to move the sprite.
self.pos += self.vel * dt
self.rect.center = self.pos # Update the rect pos.
if self.rect.bottom <= 0:
self.kill()
This is my main.py program
import pygame
import os
import time
from pygame import mixer
from pygame.locals import *
import Sprite1
width = 960
height = 720
fps = 40 # frame rate
#ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISLAYSURF = pygame.display.set_mode((width,height))
surface = pygame.display.set_mode([width,height])
pygame.display.set_caption('B.S.G.!!!')
background = pygame.image.load(os.path.join('images','Bg.png')).convert()
backdropbox = surface.get_rect()
pygame.mixer.music.load('.\\sounds\\Fairy.mp3')
pygame.mixer.music.play(-1, 0.0)
player = Sprite1.Player() # spawn player
fire = Sprite1.Bullet()
player.rect.x = 50
player.rect.y = 500
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
Sprite.showStartScreen(surface)
while main == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
main = False
#if event.type == pygame.MOUSEBUTTONDOWN:
#if Bullet.bullet_timer <= 0:
#Bullet.bullet_timer = 0 # Bullet ready.
#if mouse_pressed[0]: # Left mouse button.
## Create a new bullet instance and add it to the groups.
#Bullet(pg.mouse.get_pos(), self.all_sprites, self.bullets)
#Bullet.bullet_timer = .1 # Reset the timer.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.control(-steps,0)
if event.key == pygame.K_RIGHT:
player.control(steps,0)
if event.key == pygame.K_UP:
player.rect.y -= 100
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.control(steps,0)
if event.key == pygame.K_RIGHT:
player.control(-steps,0)
if event.key == pygame.K_UP:
player.rect.y += 100
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
surface.blit(background, (0, 0))
player.update()
player_list.draw(surface) #refresh player position
pygame.display.flip()
clock.tick(fps)
You already import the sprite file in your main.py on line 6:
import Sprite1
So now your code can call functions from this file:
Sprite1.showStartScreen( window )
Alternatively, instead on line 6 you could use:
from Sprite1 import *
And then your code can reference everything in Sprite1.py as if it was defined in the main.py itself.
The import section of the Python Documentation makes for fairly dry reading, but might just be worth a quick look if you get stuck.
Your bullet sprite code looks mostly OK now, and should work. It seems to be adding the newly created sprite to a bullets - which should be a SpriteGroup, but the supplied code does not show a definition of this.
Typically to move the bullet sprites, the code would call bullets.update() inside the main loop every frame. I can see the code calling player.update() in the last few lines of the main loop. Maybe it just needs a similar call to update() for the bullets group.
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
I don't get why the rectangle is not changing it's y position when i press the up key. I don't get any errors and everything is showing up.
import pygame
from pygame.locals import *
class SnekHead(object):
def __init__(self, screensize):
self.screensize = screensize
self.center_x = int(screensize[0]*0.5)
self.center_y = int(screensize[1]*0.5)
self.width = 50
self.height = 50
self.rect = pygame.Rect(self.center_x-25, self.center_y-50, self.width, self.height)
self.color = (100, 255, 100)
self.speed = 10
self.direction = 0
def update(self):
self.center_y += self.direction*self.speed
def render(self, screen):
pygame.draw.rect(screen, self.color, self.rect, 0)
def run_game():
pygame.init()
screensize = (640, 480)
background_image = pygame.image.load('Sky_back_layer.png')
screen = pygame.display.set_mode(screensize)
clock = pygame.time.Clock()
snake = SnekHead(screensize)
running = True
while running:
clock.tick(64)
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.key == K_UP:
snake.direction = -1
snake.update()
screen.blit(background_image, (0, 0))
snake.render(screen)
pygame.display.flip()
pygame.quit()
run_game()
Ask yourself this question. What is the value of self.rect after the key is pressed? (You decrement snake.direction when the key is pressed, then you update snake.center_y, but snake.rect remains the same and so does the position of the rectangle because that is what you are passing to pygame.draw.rect() in your render() function)
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()
I'm working on a lab 8 on programarcadegames: http://programarcadegames.com/index.php?chapter=lab_classes_and_graphics - and got stuck with move method in a Rectangle class. Will appreciate any hints.
import pygame
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
class Rectangle():
def draw(self, x, y, height, width, screen):
self.x = x
self.y = y
self.height = height
self.width = width
self.screen = screen
pygame.draw.rect(self.screen, white, [self.x, self.y, self.height, self.width])
print "meth draw: x,y", self.x, self.y
def move(self, change_x, change_y):
self.change_x = change_x
self.change_y = change_y
self.x += self.change_x
self.y += self.change_y
if self.x > 300 or self.x < 0:
self.change_x = -self.change_x
if self.y > 300 or self.y < 0:
self.change_y = -self.change_y
print "x,y,s_x,s_y", self.x, self.y, self.change_x, self.change_y # debug
pygame.init()
size = [300,300]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
myObject = Rectangle()
# -------- Main Program Loop -----------
while done == False:
# ALL EVENT PROCESSING SHOULD GO BELOW THIS COMMENT
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
screen.fill(black)
myObject.draw(100, 100, 50, 50, screen)
myObject.move(10, 10)
pygame.display.flip()
clock.tick(20)
pygame.quit()
You have to change draw() because in every loop you draw retangle in the same place so you can't see result of your move.
class Rectangle:
def __init__(self, x, y, width, height, screen):
self.x = x
self.y = y
self.width = width
self.height = height
self.screen = screen
def draw(self):
pygame.draw.rect(self.screen, white, [self.x, self.y, self.height, self.width])
print "meth draw: x,y", self.x, self.y
def move(self, change_x, change_y):
# rest of your code
# rest of your code
myObject = Rectangle(100, 100, 50, 50, screen)
# rest of your code
myObject.move(10, 10) # move to new place
myObject.draw() # draw in new place
Very good hints furas - especially on order of drawing on screen. I did play a little bit more, set few debugs (prints) and eventually got it sorted. The issue was with "move" method - as each time the screen was drawn - I was resetting "change_x" and "change_y" to values given in method call. Of course that was wrong. What I did is I created extra method "speed" and I'm calling it BEFORE main program loop.
The correct version is listed below. For the sake of clarity I changed "change_x" and "change_y" to "speed_x" and "speed_y".
We may close this topic
import pygame
# Define some colors
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
class Rectangle():
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.height = height
self.width = width
def speed(self,*speed):
self.speed_x = speed[0]
self.speed_y = speed[1]
def draw(self, screen):
self.screen = screen
pygame.draw.rect(self.screen, white, [self.x, self.y, self.height, self.width])
print "meth draw: x,y", self.x, self.y
def move(self):
print "self.x", self.x
print "self.speed_x:", self.speed_x
self.x += self.speed_x
self.y += self.speed_y
if self.x > 250 or self.x < 0:
self.speed_x = -self.speed_x
if self.y > 250 or self.y < 0:
self.speed_y = -self.speed_y
pygame.init()
# Set the width and height of the screen [width,height]
size = [300,300]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
myObject = Rectangle(100, 100, 50, 50,)
myObject.speed(2,4)
# -------- Main Program Loop -----------
while done == False:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
screen.fill(black)
myObject.move()
myObject.draw(screen)
pygame.display.flip()
clock.tick(20)
pygame.quit()