Pygame Class Variables - python

Im making a flappy bird clone game in pygame and im using classes, because I want to learn how to use classes.
I created 2 classes a "FlappyBird" and "Pipes" class.
The problem is with the collision. The variable BIRD_Y which is passed into the Pipes class from the MainWindow class and the variable BIRD_Y is chaning its value, but the problem here is that the variable does not change in the Pipes class! I tried with inheritance, still no luck.
I'm sorry if this is like the easy question but I don't have the practice and experience in pygame classes.
import pygame
pygame.init()
WIDTH = 800
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("FlapPY Bird")
clock = pygame.time.Clock()
# colors
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
# --> variables
FPS = 60
# classes
class MainWindow(object):
def __init__(self, w, h):
self.width = w
self.height = h
self.Main()
def Main(self):
loop = True
bird_width = 25
bird_height = 25
bird_x = 150
bird_y = HEIGHT/2 - int(bird_height/2)
bird_x_move = 0
bird_y_move = 0
bird = FlappyBird(bird_x, bird_y, bird_width, bird_height)
pipe_spacing = 350
pipe_speed = 3
space = 100
p1_x = 300
p1_y = 250
p1_w = 50
p1_h = HEIGHT
p2_x = p1_x + pipe_spacing
p2_y = 250
p2_w = 50
p2_h = HEIGHT
p3_x = p2_x + pipe_spacing
p3_y = 250
p3_w = 50
p3_h = HEIGHT
pipe1 = Pipes(p1_x, p1_y, p1_w, p1_h, space)
pipe2 = Pipes(p2_x, p2_y, p2_w, p2_h, space)
pipe3 = Pipes(p3_x, p3_y, p3_w, p3_h, space)
while loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bird_y_move = -7
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
bird_y_move = 3
screen.fill(white)
bird.draw()
bird.move(bird_x_move, bird_y_move)
pipe1.draw_pipes()
pipe2.draw_pipes()
pipe3.draw_pipes()
pipe1.pipe_move(pipe_speed)
pipe2.pipe_move(pipe_speed)
pipe3.pipe_move(pipe_speed)
pipe1.check_if(bird_y)
pipe2.check_if(bird_y)
pipe3.check_if(bird_y)
pygame.display.update()
clock.tick(FPS)
class FlappyBird(object):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def draw(self):
pygame.draw.rect(screen, red, (self.x, self.y, self.width, self.height))
def move(self, dx, dy):
self.y += dy
self.x += dx
class Pipes(object):
def __init__(self, x, y, width, height, space):
self.x = x
self.y = y
self.width = width
self.height = height
self.space = space
def draw_pipes(self):
pygame.draw.rect(screen, black, (self.x, self.y, self.width, self.height))
pygame.draw.rect(screen, black, (self.x, self.y-self.space-self.height, self.width, self.height))
def pipe_move(self, speed):
self.x -= speed
def check_if(self, bird_y):
if self.x < 0:
self.x = 1000
print(bird_y)
MainWindow(WIDTH, HEIGHT)

The variable you are giving to Pipe.check_if() is not updated.
You should get the real coordinate used by the bird, you can do this by calculate the new value before update the bird position (and give directly the new position, new_x and new_y).
The other solution is to add a return statement in the method FlappyBird.move() with the new coordinates, so when you call bird.move(bird_x_move, bird_y_move), you can assign the returned value to new_x and new_y.
# class bird
def move(self, dx, dy):
self.y += dy
self.x += dx
return(self.x, self.y)
# class MainWindow
new_x, new_y = bird.move(bird_x_move, bird_y_move)
#[...]
pipe1.check_if(new_y)
pipe2.check_if(new_y)
pipe3.check_if(new_y)

Related

How to add snake parts when he eats the food in pygame [duplicate]

This question already has an answer here:
How do I get the snake to grow and chain the movement of the snake's body?
(1 answer)
Closed 24 days ago.
Right now I'm learning oop so I want to practice some on this snake game project.
So when i want to add snake parts when he eat the food i don't know what to do.
here is my code .give me your opinion about it (if you want).(=
import pygame, random, math
pygame.init()
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
WHITE = (255,255,255)
WINDOW_W = 800
WINDOW_H = 600
BACKGROUND_COLOR = BLACK
clock = pygame.time.Clock()
SNAKE_WIDTH = 25
SNAKE_HEIGHT = 25
SNAKE_COLOR = GREEN
SNAKE_VEL = 25
FOOD_WIDTH = SNAKE_WIDTH
FOOD_HEIGHT = SNAKE_HEIGHT
FOOD_COLOR = RED
FPS = 10
win = pygame.display.set_mode((WINDOW_W, WINDOW_H))
pygame.display.set_caption('G_G')
#-------------------------------------------------------------------------------------------
class Snake:
def __init__(self, WIDTH ,HEIGHT, COLOR, VELOCITY, x, y):
self.WIDTH = WIDTH
self.HEIGHT = HEIGHT
self.COLOR = COLOR
self.VEL = VELOCITY
self.x = x
self.y = y
def draw(self):
pygame.draw.rect(win, self.COLOR, (self.x, self.y, self.WIDTH, self.HEIGHT))
def spawn(self):
if self.x<0:
self.x = WINDOW_W + SNAKE_WIDTH
if self.x > WINDOW_W + SNAKE_WIDTH:
self.x = 0
if self.y < 0:
self.y = WINDOW_H + SNAKE_HEIGHT
if self.y > WINDOW_H + SNAKE_HEIGHT:
self.y = 0
snake = Snake(SNAKE_WIDTH, SNAKE_HEIGHT, SNAKE_COLOR, SNAKE_VEL,1, 1)
#--------------------------------------------------------------------------------------------
class Food:
def __init__(self, WIDTH ,HEIGHT, COLOR, x=random.randrange(1, WINDOW_W+1, FOOD_WIDTH), y=random.randrange(1, WINDOW_H+1, FOOD_HEIGHT)):
self.WIDTH = WIDTH
self.HEIGHT = HEIGHT
self.COLOR = COLOR
self.x = x
self.y = y
def draw(self):
pygame.draw.rect(win, self.COLOR, (self.x, self.y, self.WIDTH, self.HEIGHT))
def snake_eats_food():
dis_snake_food = math.sqrt(((snake.x-food.x))**2+(snake.y-food.y)**2)
if abs(dis_snake_food)<4:
food.x = random.randrange(1, WINDOW_W+1, FOOD_WIDTH)
food.y = random.randrange(1, WINDOW_H+1, FOOD_HEIGHT)
return True
else:
return False
food = Food(FOOD_WIDTH, FOOD_WIDTH, FOOD_COLOR)
#-----------------------------------------------------------------------------------------
def draw():
win.fill(BACKGROUND_COLOR)
for x in range(0, WINDOW_W+1, SNAKE_WIDTH):
pygame.draw.line(win, WHITE, (x, 0), (x, WINDOW_H), 2)
for y in range(0, WINDOW_H+1, SNAKE_HEIGHT):
pygame.draw.line(win, WHITE, (0, y), (WINDOW_W, y), 2)
snake.draw()
food.draw()
def main():
run = True
up,down,right,left = False,False,False,False
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#-----------------------------------
draw()
snake_eats_food()
pygame.display.update()
#------------------------------------
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
up,down,right,left = True,False,False,False
if keys[pygame.K_DOWN]:
down,up,right,left = True,False,False,False
if keys[pygame.K_RIGHT]:
right,down,up,left = True,False,False,False
if keys[pygame.K_LEFT]:
left,down,right,up = True,False,False,False
if up:
snake.y -= snake.VEL
snake.spawn()
if down:
snake.y += snake.VEL
snake.spawn()
if right:
snake.x += snake.VEL
snake.spawn()
if left:
snake.x -= snake.VEL
snake.spawn()
#-----------------------------------
pygame.quit()
if __name__ == "__main__":
main()
I know the code is a bit messy because I wanted to try to implement OOP, since I never used it.
To render the snake:
I would recommend using a list of coordinate pairs named self.snake_array which represents points along the snake. Every unit you go forward, append the current snake coordinate pair to the end of self.snake_array and delete the first element in the coordinate pair array if the array length exceeds self.snake_length.
. I recommend having a self.snake_length variable that determines how many units long the snake is. When you run snake_eats_food, check if it returned true. If it returned true, increase the self.snake_length. All you have left to do is fix your snake draw function so that it loops through the self.snake_array and draws a square at each coordinate.
Hope that helps!

How do my make my image (rect) collide with my randomly generated circles to end the game? [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
I have been trying to solve this for weeks. This is a free-falling pit game, where my character (in this case a chimpanzee png) falls from the top of the screen to the bottom while trying to dodge the random black circles. I have tried so many angles at tackling this, I have tried the standard collision I was taught (pygame.sprite.groupcollide(Group1, Group2, False, True, collided = None) I have tried doing collisions with the colour black only, different formats of spawning my image and all that, and I haven't been able to find anything that works with my code. It has resulted in the code being very messy. I have tried to clean it up for this, but it still might be hard to understand, but if anybody has any solution to this, please let me know as I have been stumped for weeks. I just want the game to close or "game over" when they touch a circle Code:
import sys
import pygame as pg
RED = (255, 0, 0)
GRAY = (150, 150, 150)
GREEN =(34, 177, 76)
BLACK = (0,0,0)
import random
import math
def main():
width, height = 1024, 768
hbox, vbox = 80, 80
w, h = 640, 240
screen = pg.display.set_mode((width, height))
BG = pg.image.load('jungle.jpg').convert()
img = pg.image.load('MONKEY.png')
img = pg.transform.scale(img, (80, 80))
img.convert()
rect = img.get_rect()
rect.center = w//2, h//2
clock = pg.time.Clock()
Score = 0
class Circle(pg.sprite.Sprite):
def __init__(self):
#You can initialise the size to a random value
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
def draw(self):
pg.draw.circle(screen, self.color, (self.pos[0], self.pos[1]), self.radius)
circles = []
for i in range(20):
circles.append(Circle())
def checkIntersection(c1, c2):
dx = c1.pos[0] - c2.pos[0]
dy = c1.pos[1] - c2.pos[1]
d = math.hypot(dx, dy)
if d < c1.radius + c2.radius:
return True
return False
for i in range(19):
while checkIntersection(circles[i], circles[i + 1]):
circles[i].pos = random.randint(0, 1000), random.randint(0, 700)
velocity = (0, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
# booster
move = 8 if keys[pg.K_LSHIFT] else 4
if keys[pg.K_a]: #to move left
rect.x -= move
if rect.x < 0 : rect.x = 0
if keys[pg.K_d]: #to move right
rect.x += move
if rect.x > width-hbox : rect.x = width - hbox
Score += 1
rect.y += 2.5
screen.blit(BG, (0,0))
screen.blit(img,rect)
pg.draw.rect(screen, RED, rect, 1)
pg.draw.polygon(screen, GREEN, ((1024,768), (0,768), (0,640),(1024,640)))
font = pg.font.SysFont("comicsansms", 45)
text = font.render (" " + str(Score), 1, BLACK)
screen.blit(text,(480,700))
pg.event.get()
for circle in circles:
circle.draw()
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
Use a "mask" collision. See How can I made a collision mask? and Pygame mask collision
Create a Sprite class with mask (see pygame.mask.from_surface):
class Circle(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
self.image = pg.Surface((self.radius*2, self.radius*2), pg.SRCALPHA)
pg.draw.circle(self.image , self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = self.pos)
self.mask = pg.mask.from_surface(self.image)
Create a Sprite class for the player (also with a mask)
class Player(pg.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pg.image.load('MONKEY.png')
self.image = pg.transform.scale(self.image, (80, 80)).convert()
self.rect = self.image.get_rect()
self.rect.center = x, y
self.mask = pg.mask.from_surface(self.image)
Manage the Sprites in pygame.sprite.Group and use pygame.sprite.spritecollide and pygame.sprite.collide_mask() for the collision test:
if pg.sprite.spritecollide(player, circles, False, collided = pg.sprite.collide_mask):
done = True
Complete example:
import sys
import pygame as pg
RED = (255, 0, 0)
GRAY = (150, 150, 150)
GREEN =(34, 177, 76)
BLACK = (0,0,0)
import random
import math
class Circle(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
self.image = pg.Surface((self.radius*2, self.radius*2), pg.SRCALPHA)
pg.draw.circle(self.image , self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = self.pos)
self.mask = pg.mask.from_surface(self.image)
class Player(pg.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pg.image.load('MONKEY.png')
self.image = pg.transform.scale(self.image, (80, 80)).convert()
self.rect = self.image.get_rect()
self.rect.center = x, y
self.mask = pg.mask.from_surface(self.image)
def main():
width, height = 1024, 768
hbox, vbox = 80, 80
w, h = 640, 240
screen = pg.display.set_mode((width, height))
BG = pg.image.load('jungle.jpg').convert()
clock = pg.time.Clock()
Score = 0
player = Player(w//2, h//2)
all_sprites = pg.sprite.Group(player)
circles = pg.sprite.Group()
for i in range(20):
circle = Circle()
circles.add(circle)
all_sprites.add(circle)
def checkIntersection(c1, c2):
dx = c1.pos[0] - c2.pos[0]
dy = c1.pos[1] - c2.pos[1]
d = math.hypot(dx, dy)
if d < c1.radius + c2.radius:
return True
return False
c = circles.sprites()
for i in range(19):
while checkIntersection(c[i], c[i + 1]):
c[i].pos = random.randint(0, 1000), random.randint(0, 700)
velocity = (0, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
# booster
move = 8 if keys[pg.K_LSHIFT] else 4
if keys[pg.K_a]: #to move left
player.rect.x -= move
if player.rect.x < 0 : player.rect.x = 0
if keys[pg.K_d]: #to move right
player.rect.x += move
if player.rect.x > width-hbox : player.rect.x = width - hbox
Score += 1
player.rect.y += 2.5
screen.blit(BG, (0,0))
pg.draw.rect(screen, RED, player.rect, 1)
pg.draw.polygon(screen, GREEN, ((1024,768), (0,768), (0,640),(1024,640)))
font = pg.font.SysFont("comicsansms", 45)
text = font.render (" " + str(Score), 1, BLACK)
screen.blit(text,(480,700))
pg.event.get()
all_sprites.draw(screen)
pg.display.update()
clock.tick(30)
if pg.sprite.spritecollide(player, circles, False, collided = pg.sprite.collide_mask):
done = True
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()

Why is the program not stopping after the main loop is stopped?

I am trying to make my own version of Snake. I'm trying to set it up so that when a wall is hit, the main while loop stops running by setting run = False. This works when I close the program with the quit button, but for some reason is not working when a collision is detected. Any help?
Note: I did try moving the gameOver() function out of the updateScreen() function and putting it separately at the end of the loop, but that didn't work either.
import pygame
import time
import os
pygame.init()
width, height = (500, 500)
win = pygame.display.set_mode((width, height))
pygame.display.set_caption("Snake")
win.fill((0, 0, 0))
# Snake Class
class Snake:
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.len = []
self.xvel = 0
self.yvel = 0
def draw(self, window):
win.fill((0, 0, 0))
pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height))
def move(self):
self.x += self.xvel
self.y += self.yvel
def checkCol(self):
# Check if edge is hit
if self.x <= 0 or self.x + self.width >= width:
return True
elif self.y <= 0 or self.y + self.height >= height:
return True
else:
return False
sn = Snake(100, 100, 15, 15, (0, 255, 0))
# MAIN LOOP
def main():
run = True
FPS = 15
clock = pygame.time.Clock()
fontStyle = pygame.font.SysFont(None, 50)
def updateScreen():
sn.move()
sn.draw(win)
gameOver()
pygame.display.update()
def gameOver():
if sn.checkCol():
run = False
print("check")
def message(msg, color):
m = fontStyle.render(msg, True, color)
win.blit(m, [width / 2, height / 2])
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
sn.yvel = -10
sn.xvel = 0
if keys[pygame.K_s]:
sn.yvel = 10
sn.xvel = 0
if keys[pygame.K_a]:
sn.xvel = -10
sn.yvel = 0
if keys[pygame.K_d]:
sn.xvel = 10
sn.yvel = 0
updateScreen()
message("Game Over!", (255, 0, 0))
main()
use breakwhen your condition is met
if keys[pygame.K_d]:
sn.xvel = 10
sn.yvel = 0
break

Pygame sprite sheet hitbox broken

I made a simple game, where you have to jump over the stones moving towards you. The problem is that one of those sprites hitboxes is broken, even if the sprite mask is used. The player sprite is generated just dividing the player sprite sheet into 4 columns. Maybe there the problem is hiding?
Pictures for the program:
https://drive.google.com/drive/folders/1We6RDMs3Cwwprf1OY0ow9WCTHF9MHzne?usp=sharing
import pygame, os, random
pygame.init()
W, H = 800,600
HW, HH = W/2,H/2
AREA = W * H
WHITE = (255,255,255)
GREEN = (69,139,0)
FPS = 60
bg = pygame.image.load(os.path.join('Pildid', 'Taust3.png'))
bg = pygame.transform.scale(bg, (2000, 600))
DS = pygame.display.set_mode((W,H))
clock = pygame.time.Clock()
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, py, player, veerg, rida):
super(Player,self).__init__()
'''Mangija huppamine'''
clock.tick(5)
self.x = x
self.y = y
self.jumping = False
self.platform_y = py
self.velocity_index = 0
'''Sprite sheet'''
self.player = pygame.image.load(os.path.join('Pildid', 'karakter.png')).convert_alpha()#pildi uleslaadimine
#self.player = pygame.transform.scale(self.player,(200,100))
self.rect = self.player.get_rect()
'''Sprite sheeti piltide jaotamine pikslite jargi'''
self.veerg = veerg
self.rida = rida
self.kokku = veerg * rida
self.rect = self.player.get_rect()
L = self.veergL = self.rect.width/veerg
K = self.weegK = self.rect.height/rida
PL,PK = self.veergKeskel = (L/2,K/2)
self.veerg = list([(index % veerg * L, int(index/veerg) * K,L,K )for index in range(self.kokku)])
self.handle = list([ #pildi paigutamise voimalikud positsioonid
(0, 0), (-PL, 0), (-L, 0),
(0, -PK), (-PL, -PK), (-L, -PK),
(0, -L), (-PL, -K), (-L, -K),])
self.mask = pygame.mask.from_surface(self.player)
def do_jumpt(self):
'''Huppamine: kiirus, korgus, platvorm'''
global velocity
if self.jumping:
self.y += velocity[self.velocity_index]
self.velocity_index += 1
if self.velocity_index >= len(velocity) - 1:
self.velocity_index = len(velocity) - 1
if self.y > self.platform_y:
self.y = self.platform_y
self.jumping = False
self.velocity_index = 0
def draw(self, DS,veergindex,x,y,handle=0):
DS.blit(self.player,(self.x+self.handle[handle][0], self.y + self.handle[handle][1]),self.veerg[veergindex])
def do(self):
'''Funktsioonide kokkupanek'''
self.do_jumpt()
p.draw(DS,index%p.kokku,190, 359,4)
def update(self):
self.rect.center = self.x, self.y
def keys(player):
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] or keys[pygame.K_UP] and player.jumping == False:
player.jumping = True
class Obsticles(pygame.sprite.Sprite):
'''Game obsticles: **'''
#img = pygame.image.load(os.path.join('images', 'box.png'))
def __init__(self, x, y):
super(Obsticles,self).__init__()
self.img = pygame.image.load(os.path.join('Pildid', 'kivi.png')).convert_alpha()
self.img = pygame.transform.scale(self.img, (90,90))
self.rect = self.img.get_rect(center=(x, y))
self.x = x
self.y = y
self.mask = pygame.mask.from_surface(self.img)
def draw(self, DS):
'''Obsticle img blitting and hitbox'''
DS.blit(self.img, (self.x, self.y))
def update(self):
if self.x < -64: # Delete the obstacle.
# `kill()` removes this obstacle sprite from all sprite groups.
self.kill()
self.x += speed # Move the obstacle.
# Update the rect because it's used to blit the
# sprite and for the collision detection.
self.rect.center = self.x, self.y
def redrawWindow():
'''Obsticle loop'''
for i in ob:
i.draw(DS)
def text_objects(text, font):
textSurface = font.render(text, True, WHITE)
return textSurface, textSurface.get_rect()
def message_display(text):
largeText = pygame.font.Font('freesansbold.ttf',60)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((W/2),(H/4))
DS.blit(TextSurf, TextRect)
pygame.display.update()
pygame.time.wait(3000)
def crash():
message_display('Failed')
pygame.time.set_timer(pygame.USEREVENT+2, random.choice([2500, 3000, 1500]))
velocity = list([(i/ 1)-20 for i in range (0,60)]) #Huppe ulatus
index = 3
obsticles = Obsticles(832, 363)
p = Player(190, 359, 359, 'karakter.png', 4, 1)
all_sprites = pygame.sprite.Group(p, obsticles)
ob = pygame.sprite.Group(obsticles)
x = 0
x -= 1
speed = -5
running = True
while running:
# ---Handle the events.---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.USEREVENT+2:
r = random.randrange(0, 2)
if r == 0:
obsticles = Obsticles(832, 363)
# Add the obstacle to both groups.
ob.add(obsticles)
all_sprites.add(obsticles)
speed += -0.008
# ---Game logic.---
all_sprites.update()
collided = pygame.sprite.spritecollide(p, ob, True,
pygame.sprite.collide_mask)
if collided:
crash()
index += 1
# Background movement.
back_x = x % bg.get_rect().width
x -= 2
# ---Draw everything.---
DS.blit(bg, (back_x - bg.get_rect().width, 0))
if back_x < W:
DS.blit(bg, (back_x, 0))
keys(p)
p.do()
redrawWindow()
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
The problem is that you use the original image to create the Player.mask. The resulting mask covers all four poses of the character, so even if only one of the sprite sheet frames is visible, the complete original image is used for the collision detection. Your mask looks like the green area in this picture:
You should also use the rect (i.e. the rect.topleft coords) as the blit position, because it is used for the collision detection as well (to find the location of the mask).
I've got a fixed, shortened version of your program in which I've changed a few things to show you how I would solve the problem. First of all, I load the sprite sheet and cut it into several subsurfaces, then I assign the first surface/image to the self.image attribute of the Player. This image can be used to create the rect and the mask.
In the update method I increment the frame_index and assign the current image to self.image in order to animate the sprite. BTW, it would be a good idea to create separate masks for every image and swap them as well during the animation. (I'm just using the first mask here, since the animation frames are so similar.)
Note that if you give your sprites an image and a rect attribute, you can just call all_sprites.update(DS) to blit all sprite images at their corresponding rects.
import random
import pygame
pygame.init()
FPS = 60
DS = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
PLAYER_SHEET = pygame.image.load('karakter.png').convert_alpha()
# Cut the sprite sheet and append the subsurfaces to this list.
PLAYER_IMAGES = []
width = PLAYER_SHEET.get_width() / 4
height = PLAYER_SHEET.get_height()
for x in range(4):
PLAYER_IMAGES.append(PLAYER_SHEET.subsurface(x*width, 0, width, height))
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, py):
super(Player,self).__init__()
self.x = x
self.y = y
self.jumping = False
self.platform_y = py
self.velocity_index = 0
self.velocity = list([(i/ 1)-20 for i in range (0,60)])
self.frame_index = 0
self.image = PLAYER_IMAGES[self.frame_index]
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def do_jump(self):
if self.jumping:
self.y += self.velocity[self.velocity_index]
self.velocity_index += 1
if self.velocity_index >= len(self.velocity) - 1:
self.velocity_index = len(self.velocity) - 1
if self.y > self.platform_y:
self.y = self.platform_y
self.jumping = False
self.velocity_index = 0
def update(self):
self.rect.center = self.x, self.y
self.do_jump()
# Update the animation frame.
self.frame_index += 1
self.frame_index %= len(PLAYER_IMAGES) * 7 # * 7 to slow it down.
# Swap the image.
self.image = PLAYER_IMAGES[self.frame_index//7] # // 7 to slow it down.
def keys(player):
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] or keys[pygame.K_UP] and player.jumping == False:
player.jumping = True
class Obstacle(pygame.sprite.Sprite):
"""Game Obstacle."""
def __init__(self, x, y):
super(Obstacle,self).__init__()
self.image = pygame.Surface((90, 90), pygame.SRCALPHA)
self.image.fill((100, 150, 0))
self.image = pygame.transform.scale(self.image, (90,90))
self.rect = self.image.get_rect(center=(x, y))
self.x = x
self.y = y
self.mask = pygame.mask.from_surface(self.image)
def update(self):
if self.x < -64:
self.kill()
self.x += speed
self.rect.center = self.x, self.y
pygame.time.set_timer(pygame.USEREVENT+2, random.choice([2500, 3000, 1500]))
index = 3
obstacle = Obstacle(832, 363)
player = Player(190, 359, 359)
all_sprites = pygame.sprite.Group(player, obstacle)
obstacles = pygame.sprite.Group(obstacle)
speed = -5
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.USEREVENT+2:
r = random.randrange(0, 2)
if r == 0:
obstacle = Obstacle(832, 363)
# Add the obstacle to both groups.
obstacles.add(obstacle)
all_sprites.add(obstacle)
keys(player)
# ---Game logic.---
speed += -0.008
all_sprites.update()
collided = pygame.sprite.spritecollide(player, obstacles, True,
pygame.sprite.collide_mask)
if collided:
print('crash')
index += 1
# ---Draw everything.---
DS.fill((30, 30, 30))
all_sprites.draw(DS)
# Here I draw the outline (points) of the player's mask.
px, py = player.rect.topleft
for point in player.mask.outline(8):
x, y = point[0] + px, point[1] + py
pygame.draw.circle(DS, (250, 250, 0), (x, y), 2)
pygame.draw.rect(DS, (0, 0, 250), player.rect, 1)
# Draw the outlines of the obstacles.
for obstac in obstacles:
pygame.draw.rect(DS, (150, 250, 0), obstac.rect, 1)
ox, oy = obstac.rect.topleft
for point in obstac.mask.outline(6):
pygame.draw.circle(DS, (250, 0, 0), (point[0]+ox, point[1]+oy), 2)
pygame.display.update()
clock.tick(60)
pygame.quit()

Python variables change in every object

I'm playing around with pygame for the first time, trying to make multiple rectangles move across the screen. Here's my code:
import pygame
pygame.init()
scrWidth = 1200
scrHeigth = 900
done = False
rectangles = []
screen = pygame.display.set_mode((scrWidth, scrHeigth))
clock = pygame.time.Clock()
class Rectangle:
speed = [1,1]
colourSpeed = 300
colourID = 0
colour = (0, 0, 255)
size = 60
def __init__(self, name = "", x=0, y=0, speed=False, colour=False, size=False):
self.name = name
self.x = x
self.y = y
self.doesSpeed = speed
self.doesColour = colour
self.doesSize = size
def draw(self):
pygame.draw.rect(screen, self.colour, pygame.Rect(self.x, self.y, self.size, self.size))
def checkCollision(self):
if self.x < 0 or self.x > scrWidth-self.size:
self.speed[0] = -self.speed[0]
if self.y < 0 or self.y > scrHeigth-self.size:
self.speed[1] = -self.speed[1]
def move(self):
self.x += self.speed[0]
self.y += self.speed[1]
def changeColour(self):
self.colourID = (self.colourID+1)%self.colourSpeed
if 0 <= self.colourID < (self.colourSpeed/3):
self.colour = (0, 0, 255)
elif (self.colourSpeed/3) <= self.colourID < (2*self.colourSpeed/3):
self.colour = (0, 255, 0)
elif (2*self.colourSpeed/3) <= self.colourID < self.colourSpeed:
self.colour = (255, 0, 0)
rect1 = Rectangle("rect1", 30, 30, False, True, False)
rectangles.append(rect1)
rect2 = Rectangle("rect2", 300, 300)
rectangles.append(rect2)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done=True
screen.fill((0, 0, 0))
for obj in rectangles:
obj.checkCollision()
if obj.doesColour: obj.changeColour()
obj.move()
obj.draw()
pygame.display.flip()
clock.tick(60)
The problem is that when a rectangle collides with the edge of the screen all rectangles change direction(speed). However if I make rectangles with different speeds this doesn't happen.
ex: rect1 has speed [1,1] and rect2 has speed [2,2].
What's causing this and how can I fix it?
Move the speed, colourSpeed, colourID, colour, size to the init method.
At that moment that fields are defined as class fields, so they are changed globally for all rectangles.
Solution:
def __init__(self, name = "", x=0, y=0, speed=False, colour=False, size=False):
self.name = name
self.x = x
self.y = y
self.doesSpeed = speed
self.doesColour = colour
self.doesSize = size
# here it comes
self.speed = [1,1]
self.colourSpeed = 300
self.colourID = 0
self.colour = (0, 0, 255)
self.size = 60

Categories

Resources