Collision in the Class - python

I am writing a code for a game (school project).
I have a Class with different images for objects.
What I want is to create a condition for collision. For example, if the image if fire collides the image of earth then get a new image.
How can I do it ?
Thank you!
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((900,800))
pygame.display.set_caption("Tiny Alchemist")
clock = pygame.time.Clock()
FPS = 90
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
ALTWHITE = (240, 240, 240)
clear = False # Flag that shows if the user wants to clear the screen
class Element(pygame.Surface):
def __init__(self, image, xpos, ypos):
self.image = image
self.xpos = xpos
self.ypos = ypos
self.width = image.get_width()
self.height = image.get_height()
self.selected = False
self.visible = False
self.rect = pygame.Rect(xpos, ypos, image.get_width(), image.get_height())
def move(self, move):
self.xpos += move[0]
self.ypos += move[1]
self.rect = pygame.Rect(self.xpos, self.ypos, self.width, self.height)
class Recycle(pygame.Surface):
def __init__(self, xpos, ypos):
self.image = pygame.image.load("ElementIcon/recycle.png").convert_alpha()
self.image = pygame.transform.scale(self.image, (75, 75))
self.image.set_colorkey(BLACK)
self.xpos = xpos
self.ypos = ypos
self.rect = pygame.Rect(xpos, ypos, self.image.get_width(), self.image.get_height())
class PanelElements(pygame.Surface):
def __init__ (self, image, xpos, ypos):
self.image = image
self.xpos = xpos
self.ypos = ypos
self.rect = pygame.Rect(xpos, ypos, image.get_width(), image.get_height())
self.clicked = False
def init():
global ImageList
global Panel
global recycle
fire = pygame.image.load("ElementIcon/fire.png").convert_alpha()
fire = pygame.transform.scale(fire, (50, 69))
fire.set_colorkey(BLACK)
fire_mini = pygame.transform.scale(fire, (40,50))
fire_mini.set_colorkey(ALTWHITE)
earth = pygame.image.load("ElementIcon/earth.png").convert_alpha()
earth = pygame.transform.scale(earth, (50, 69))
earth.set_colorkey(BLACK)
earth_mini = pygame.transform.scale(earth, (40, 50))
earth_mini.set_colorkey(ALTWHITE)
water = pygame.image.load("ElementIcon/water.png").convert_alpha()
water = pygame.transform.scale(water, (50, 69))
water.set_colorkey(BLACK)
water_mini = pygame.transform.scale(water, (40, 50))
water_mini.set_colorkey(ALTWHITE)
wind = pygame.image.load("ElementIcon/wind.png").convert_alpha()
wind = pygame.transform.scale(wind, (50, 69))
wind.set_colorkey(BLACK)
wind_mini = pygame.transform.scale(wind, (40, 50))
wind_mini.set_colorkey(ALTWHITE)
energy = pygame.image.load("ElementIcon/energy.png").convert_alpha()
energy = pygame.transform.scale(energy, (50, 69))
energy.set_colorkey(BLACK)
energy_mini = pygame.transform.scale(energy, (40, 50))
energy_mini.set_colorkey(ALTWHITE)
recycle = Recycle(650, 718)
fire_mini_obj = PanelElements(fire_mini, 750, 10)
earth_mini_obj = PanelElements(earth_mini, 750, 60)
water_mini_obj = PanelElements(water_mini, 750, 110)
wind_mini_obj = PanelElements(wind_mini, 750, 160)
fire_obj = Element(fire, 362, 460)
fire_obj.visible = True
earth_obj = Element(earth, 300, 410)
earth_obj.visible = True
water_obj = Element(water, 365, 350)
water_obj.visible = True
wind_obj = Element(wind, 420, 409)
wind_obj.visible = True
Panel = [] #adding elements to the list
Panel.append(fire_mini_obj)
Panel.append(earth_mini_obj)
Panel.append(water_mini_obj)
Panel.append(wind_mini_obj)
ImageList =[] #adding elements to the list
ImageList.append(fire_obj)
ImageList.append(earth_obj)
ImageList.append(water_obj)
ImageList.append(wind_obj)
def run():
global done
done = False
while done == False:
check_events()
update()
clock.tick(60)
def check_events():
global done
global ImageList
global Panel
global recycle
global clear
mouse_pos = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
print("User quits the game :(")
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
done = True
print("Game stopped early by user :( ")
if event.type == pygame.MOUSEBUTTONDOWN:
for im in ImageList:
if im.rect.collidepoint(mouse_pos) and (im.visible == True):
im.selected = True
if recycle.rect.collidepoint(mouse_pos):
clear = True
for mini in Panel:
if mini.rect.collidepoint(mouse_pos):
mini.clicked = True
if event.type == pygame.MOUSEBUTTONUP:
for im in ImageList:
if im.rect.collidepoint(mouse_pos) and im.visible == True:
im.selected = False
if event.type == pygame.MOUSEMOTION:
for im in ImageList:
if im.rect.collidepoint(mouse_pos) and im.selected and (im.visible == True):
xmv = event.rel[0]
ymv = event.rel[1]
if event.buttons[0]:
if xmv < 0:
if im.xpos > 0:
im.move((xmv,0))
elif event.rel[0] > 0:
if im.xpos < screen.get_width():
im.move((xmv,0))
elif ymv < 0:
if im.ypos > 0:
im.move((0,ymv))
elif event.rel[1] > 0:
if im.ypos < screen.get_height():
im.move((0,ymv))
#pygame.display.update()
def update():
global ImageList
global Panel
global recycle
global clear
screen.fill(WHITE)
#Update the screen with drawings
for im in ImageList:
if im.visible == True:
screen.blit(im.image, (im.xpos, im.ypos))
pygame.draw.rect(screen, ALTWHITE, (740, 0, 160, 800), 0)
for mini in Panel:
screen.blit(mini.image, (mini.xpos, mini.ypos))
screen.blit(recycle.image, (recycle.xpos, recycle.ypos))
if (clear == True):
for im in ImageList:
im.visible = False
clear = False
for i in range(0,len(Panel)):
if Panel[i].clicked == True:
Panel[i].clicked = False
pygame.display.update()
if __name__=="__main__":
init()
run()
pygame.quit()

perhaps something like this:
earth = pygame.image.load("earth.png").convert()
earthRect = earth.get_rect()
fire = pygame.image.load("fire.png").convert()
fireRect = fire.get_rect()
if earth.colliderect(fire):
earth = pygame.image.load("thirdimage.png")
with the first four lines defining our images and the rect objects used to detect collision, the last two line detecting the collision and changing the image file

Related

Pygame_Animation: Moving shapes and Sprites

[In pygame, I set the little spare shape to move from right to left and stop at a current point in scene 1 and it works. Then I try to set the sprites to move frame by frame so when the program run, it looks like the object is moving (just like animation) and it works too. But when I combine them together, something crashed and the sprites doesn't work. And I don't know where the problem is. Anyone can please tell me where I did wrong?
Here is the link for my program:]
https://drive.google.com/file/d/1IAr7PGt6bbU5g0EdVCufZtrcpFsH2tES/view?usp=sharing
import pygame, time, math
from pygame.locals import *
# Step 1: Setup animation - Initialize variables.
pygame.init()
clock = pygame.time.Clock()
surface = pygame.display.set_mode((1025, 575), RESIZABLE)
pygame.display.set_caption("Boat Moving")
#FontSetting
FstSceneFont = pygame.font.SysFont("Courier New", 80)
#ColorSetting
black = (0, 0 ,0)
teal = (15, 89, 114)
white = (255, 255, 255)
golden = (228, 155, 84)
maroon = (147, 62, 84)
orchid = (181, 218, 225)
chgX = 350
scene = 0
class Player(pygame.sprite.Sprite):
def __init__(self, pos_x, pos_y):
super().__init__()
self.sprites = []
self.sprites.append(pygame.image.load('Attack_1.png'))
self.sprites.append(pygame.image.load('Attack_2.png'))
self.sprites.append(pygame.image.load('Attack_3.png'))
self.sprites.append(pygame.image.load('Attack_4.png'))
self.sprites.append(pygame.image.load('Attack_5.png'))
self.sprites.append(pygame.image.load('Attack_6.png'))
self.sprites.append(pygame.image.load('Attack_8.png'))
self.sprites.append(pygame.image.load('Attack_9.png'))
self.sprites.append(pygame.image.load('Attack_10.png'))
self.sprites.append(pygame.image.load('1stScene_P11.png'))
self.current_sprite = 0
self.image = self.sprites[self.current_sprite]
self.rect = self.image.get_rect()
self.rect.topleft = [pos_x, pos_y]
def update(self):
self.current_sprite += 1
self.image = self.sprites[self.current_sprite]
moving_sprites = pygame.sprite.Group()
player = Player(0,0)
moving_sprites.add(player)
# Step 2: Set up game loop and poll for events.
while True:
event = pygame.event.poll()
if event.type == pygame.QUIT:
break
elif (event.type == pygame.KEYDOWN) and (event.key == pygame.K_ESCAPE):
break
if scene == 0:
pauseTime = 0
scene = 1
elif chgX > 10:
chgX = chgX - 1.7
pauseTime = 0.015
scene == 2
print("S1")
elif scene == 2:
BGColor = maroon
print("S2")
# Step 3: Update game and objects
if scene == 1:
BGColor = orchid
shipment1a = (chgX+718.75, 325, 28.125, 21.875)
Scene1Word1 = FstSceneFont.render("Scene1", 1, white)
scence = 2
elif scene == 2:
BGColor = orchid
Scene1Word2 = FstSceneFont.render("Scene2", 2, white)
# Step 4: Draw on each surface
if scene == 1:
surface.fill(BGColor)
pygame.draw.rect(surface, golden, shipment1a, 0)
surface.blit(Scene1Word1, ([212.5, 137.5]))
elif scene == 2:
surface.fill(BGColor)
surface.blit(Scene1Word2, ([212.5, 137.5]))
# Step 5: Show/display surface
pygame.display.flip()
time.sleep(pauseTime)
moving_sprites.draw(surface)
moving_sprites.update()
pygame.display.flip()
clock.tick(60)
pygame.display.quit()
quit()
You need to reset self.current_sprite when it exceeds the number of items in the list:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
self.current_sprite += 1
if self.current_sprite >= len(self.sprites):
self.current_sprite = 0
self.image = self.sprites[self.current_sprite]
Alter natively you can use the % (modulo) operator. The % operator computes a remainder of an integral division:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
self.current_sprite = (self.current_sprite + 1) % len(self.sprites)
self.image = self.sprites[self.current_sprite]

How do I make my pygame levels more efficient? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I've been redoing a lot of my game in order to make it more efficient, before I had functions for each level and it had about 300+ repeated lines of code for each level, I've taken it out however I'm trying to get level one to function properly now. Below is my code, any help as to how to get my code to play level1 when I run the game as right now it only displays the background screen and none of the enemies, platforms or the character.
import pygame
import time
import random
from pygame.math import Vector2 as vec
pygame.init()
# Set the screen size
screen = pygame.display.set_mode([700, 500])
# Define the colours for text
black = (0,0,0)
white = (255,255,255)
green = (0,51,0)
light_green = (51,102,0)
PLAYER_FRICTION = 0.0
PLAYER_ACC = 3
HEIGHT = 500
WIDTH = 700
FPS = 60
# Set Display
pygame.display.set_caption('Jungle Blast')
clock = pygame.time.Clock()
class Character(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("TheoHills.png")
self.rect = self.image.get_rect(topleft=(50, 300))
self.x = 50
self.y = 300
self.pos = vec(50, 300)
self.vel = vec(0,0)
self.acc = vec(0,0)
def characterJump(self,platforms):
self.rect.y += 1
hits = pygame.sprite.spritecollide(self, platforms, False)
self.rect.y -= 1
if hits:
self.vel.y = -18
def update(self):
self.acc = vec(0, 0.5)
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
self.acc.x = -PLAYER_ACC
if keys[pygame.K_d]:
self.acc.x = PLAYER_ACC
# apply friction
self.vel.x *= PLAYER_FRICTION
self.vel += self.acc
self.pos += self.vel
self.rect.midbottom = self.pos
if self.pos.y == 500:
background_image = pygame.image.load("Lose Screen.png")
if self.pos.x > 699:
level2()
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Bullet.png").convert()
self.image.set_colorkey(black)
self.rect = self.image.get_rect()
def update(self):
self.rect.x += 10
class levelInd(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("1 out of 5.png")
self.rect = self.image.get_rect(topleft=(20, 20))
self.pos = vec(20, 20)
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((100, 88))
self.image.fill(black)
self.image = pygame.image.load("Platform1.png")
self.rect = self.image.get_rect(topleft=(x, y))
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((100, 50))
self.image = pygame.image.load("RobberStanding.png")
self.rect = self.image.get_rect(topleft=(x,y))
def draw_text(surface, text, size, x, y,font_name):
font = pygame.font.SysFont(str(font_name), size)
text_surface = font.render(text, True, black)
text_rect = text_surface.get_rect()
text_rect.topleft = (x, y)
surface.blit(text_surface, text_rect)
class levels():
levelind = levelInd()
character = Character()
all_sprites = pygame.sprite.Group()
platforms = pygame.sprite.Group()
enemies = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
background_image = pygame.image.load("JungleBackground.png")
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
character.characterJump(platforms)
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
bullet = Bullet()
bullet.rect.x = character.rect.x + 10
bullet.rect.y = character.rect.y + 50
bullet_list.add(bullet)
all_sprites.add(bullet)
for bullet in bullet_list:
# See if the enemy is hit
enemyHit = pygame.sprite.spritecollide(bullet,enemies,True)
if enemyHit:
enemies.remove(Enemy)
score1 += 10
if bullet.rect.x > 700:
bullet_list.remove(bullet)
if character.rect.y >= 500:
background_image = pygame.image.load("Lose Screen.png")
pygame.display.update()
all_sprites.empty()
death = pygame.sprite.spritecollide(character, enemies, True)
if len(death) > 0:
background_image = pygame.image.load("Lose Screen.png")
all_sprites.empty()
all_sprites.update()
hits = pygame.sprite.spritecollide(character, platforms, False)
for platform in hits:
if character.vel.y > 0:
character.rect.bottom = character.rect.bottom
character.vel.y = 0
elif character.vel.y < 0:
character.rect.top = character.rect.top
character.vel.y = 3
character.pos.y = character.rect.bottom
screen.blit(background_image,[0,0])
all_sprites.draw(screen)
pygame.display.flip()
def level1():
done = False
clock = pygame.time.Clock()
font_name = pygame.font.match_font("Arial")
black = ( 0, 0, 0)
white = ( 255, 255, 255)
x = 300
y = 88
e1 = Enemy(250, 125)
enemies.add(e1)
all_sprites.add(character)
all_sprites.add(levelind)
all_sprites.add(e1)
p1 = Platform(-80, 400, WIDTH - 400, HEIGHT - 10)
p2 = Platform(175, 210, WIDTH - 400, HEIGHT - 10)
p3 = Platform(500, 400, WIDTH - 400, HEIGHT - 10)
all_sprites.add(p1, p2, p3)
platforms.add(p1, p2, p3)
score1 = 0
level1()
This is the image that shows up whenever the game is run.
I Think you should take a look at your "levels" Class.
Give it a proper init method, for example.
I got the game running by modifing the class as follows:
class levels():
def __init__(self):
self.levelind = levelInd()
self.character = Character()
self.all_sprites = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.enemies = pygame.sprite.Group()
self.bullet_list = pygame.sprite.Group()
self.background_image = pygame.image.load("1LPqX.png")
self.score1 = 0
self.running = True
def run(self):
while self.running:
self.level1()
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
pygame.display.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
self.haracter.characterJump(self.platforms)
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
bullet = Bullet()
bullet.rect.x = self.character.rect.x + 10
bullet.rect.y = self.character.rect.y + 50
self.bullet_list.add(bullet)
self.all_sprites.add(bullet)
for bullet in self.bullet_list:
# See if the enemy is hit
enemyHit = pygame.sprite.spritecollide(bullet, self.enemies, True)
if enemyHit:
self.enemies.remove(Enemy)
self.score1 += 10
if bullet.rect.x > 700:
self.bullet_list.remove(bullet)
if self.character.rect.y >= 500:
self.background_image = pygame.image.load("1LPqX.png")
pygame.display.update()
self.all_sprites.empty()
death = pygame.sprite.spritecollide(self.character, self.enemies, True)
if len(death) > 0:
self.background_image = pygame.image.load("1LPqX.png")
self.all_sprites.empty()
self.all_sprites.update()
hits = pygame.sprite.spritecollide(self.character, self.platforms, False)
for platform in hits:
if self.character.vel.y > 0:
self.character.rect.bottom = self.character.rect.bottom
self.character.vel.y = 0
elif self.character.vel.y < 0:
self.character.rect.top = self.character.rect.top
self.character.vel.y = 3
self.character.pos.y = self.character.rect.bottom
screen.blit(self.background_image, [0, 0])
self.all_sprites.draw(screen)
pygame.display.flip()
def level1(self):
done = False
clock = pygame.time.Clock()
font_name = pygame.font.match_font("Arial")
black = (0, 0, 0)
white = (255, 255, 255)
x = 300
y = 88
e1 = Enemy(250, 125)
self.enemies.add(e1)
self.all_sprites.add(self.character)
self.all_sprites.add(self.levelind)
self.all_sprites.add(e1)
p1 = Platform(-80, 400, WIDTH - 400, HEIGHT - 10)
p2 = Platform(175, 210, WIDTH - 400, HEIGHT - 10)
p3 = Platform(500, 400, WIDTH - 400, HEIGHT - 10)
self.all_sprites.add(p1, p2, p3)
self.platforms.add(p1, p2, p3)
self.score1 = 0
# level1()
(seems like the indentation was not copied properly, everything below "class levels()" has to be indented 4 more spaces)
and adding
if __name__ == '__main__':
game = levels()
game.run()
at the bottom of the file.

Why doesn't my sprite move correctly in pygame?

My second game is a platformer. I drew all the spites, drew the background and the platform, but I cant move my character more than 45 pixels one way, even when it is supposed to move 5 pixels. I also copied some code from here and there, so I cant just search it up. This is very puzzling, please help.
Here is the code:
import sys
import pygame
screen = pygame.display.set_mode((1300, 700), pygame.RESIZABLE)
global bg
bg = pygame.image.load('Pictures/badground.png')
w = (0, 0, 0)
screen.fill(w)
screen.blit(bg, (0, 0))
level = 0
walking_l = (pygame.image.load('Pictures/walk_l.png'))
walking_r = (pygame.image.load('Pictures/walk_r.png'))
walking_l_2 = (pygame.image.load('Pictures/_stand.png'))
walking_r_2 = (pygame.image.load('Pictures/stand.png'))
stand = (pygame.image.load('Pictures/rstand.png'))
pf = (pygame.image.load('Pictures/platform.png'))
pygame.display.set_caption('Platformed')
class player(object):
def __init__(self, speed, width, height):
self.speed = speed
self.x = 650
self.y = 350
self.width = width
self.height = height
self.walk_l = False
self.walk_r = False
self.stand = True
self.jump = False
self.walk_count = 0
def reprint_win(self):
screen.fill(w)
screen.blit(bg, (0, 0))
def walk(self):
k = pygame.key.get_pressed()
if k[pygame.K_a]:
self.x -= self.speed
self.stand = False
self.walk_l = True
self.walk_r = False
self.jump = False
print(self.x)
screen.fill(w)
screen.blit(bg, (0, 0))
elif k[pygame.K_d]:
self.x += self.speed
self.stand = False
self.walk_r = True
self.walk_l = False
self.jump = False
print(self.y)
screen.fill(w)
screen.blit(bg, (0, 0))
elif k[pygame.K_w] and self.walk_r and not self.jump:
self.stand = False
self.walk_r = True
self.walk_l = False
self.jump = True
screen.fill(w)
screen.blit(bg, (0, 0))
else:
self.stand = True
self.walk_r = False
self.walk_l = False
self.jump = False
screen.fill(w)
screen.blit(bg, (0, 0))
while True:
for e in pygame.event.get():
man = player(5, 64, 64)
if e.type == pygame.QUIT:
sys.exit()
if e.type == pygame.KEYDOWN:
man.walk()
screen.blit(stand, (man.x, man.y))
pygame.display.update()
elif e.type == pygame.KEYDOWN:
man.walk()
screen.blit(stand, (man.x, man.y))
pygame.display.update()
The method walk() of the class player changes the instance attribute, but id doesn't draw anything.
Add a method draw, which draw the player dependent on the instance attributes:
class player(object):
# [...]
def walk(self):
self.stand, self.walk_l, self.walk_r, self.jump = False, False, False, False
k = pygame.key.get_pressed()
if k[pygame.K_a]:
self.x -= self.speed
self.walk_l = True
elif k[pygame.K_d]:
self.x += self.speed
self.walk_r = True
elif k[pygame.K_w] and self.walk_r and not self.jump:
self.jump = True
else:
self.stand = True
def draw(self, surf):
if self.walk_l:
surf.blit(walking_l, (self.x, self.y))
elif self.walk_r:
surf.blit(walking_r, (self.x, self.y))
else:
surf.blit(stand, (self.x, self.y))
You have to draw the entire scene in every frame. Do the drawing to the application loop rather then the event loop.
Furthermore the instance of palyer has to be created before the application loop and invoke man.mvoe() in every frame.
Use pygame.time.Clock() .tick() to control the frames per second:
man = player(5, 64, 64)
clock = pygame.time.Clock()
FPS = 60
while True:
clock.tick(FPS)
for e in pygame.event.get():
if e.type == pygame.QUIT:
sys.exit()
man.walk()
screen.blit(bg, (0, 0))
man.draw(screen)
pygame.display.update()

trying creating dropdown menu pygame, but got stuck

So so far here's my code:
import pygame as pg
pg.init()
clock = pg.time.Clock()
# Generating screen
w_scr = 640
h_scr = 480
size_scr = (w_scr, h_scr)
screen = pg.display.set_mode(size_scr)
# Define color
COLOR_INACTIVE = (100, 80, 255)
COLOR_ACTIVE = (100, 200, 255)
COLOR_LIST_INACTIVE = (255, 100, 100)
COLOR_LIST_ACTIVE = (255, 150, 150)
class DropDown():
# Test List
option_list = ["Calibration", "Test"]
def __init__(self, color_menu, color_option, x, y, w, h):
self.color_menu = color_menu
self.color_option = color_option
self.x = x
self.y = y
self.w = w
self.h = h
# Draw the initial button 'select mode'
def draw_main(self, win, text=''):
pg.draw.rect(win, self.color_menu, (self.x, self.y, self.w, self.h), 0)
if text != '':
font = pg.font.SysFont(None, 30)
msg = font.render(text, 1, (0, 0, 0))
screen.blit(msg, (self.x + (self.w / 2 - msg.get_width() / 2), self.y + (self.h / 2 - msg.get_height() / 2)))
# Draw list of option 'calibration' and 'test'
def draw_opt(self, win, text=[]):
opt_list =[]
if draw:
for i, el in enumerate(text):
opt_list.append(pg.draw.rect(win, self.color_option, (self.x, self.y + (i+1)*self.h, self.w, self.h), 0))
# write each option
font = pg.font.SysFont(None, 30)
msg = font.render(text[i], 1, (0, 0, 0))
screen.blit(msg, (self.x + (self.w / 2 - msg.get_width() / 2),
self.y + (i+1)*self.h + (self.h / 2 - msg.get_height() / 2)))
# Detect when the mouse is within the 'select mode' box
def choose_main(self, pos):
if self.x < pos[0] < self.x + self.w and self.y < pos[1] < self.y + self.h:
return True
else:
return False
# Detect when the mouse is within the option list
def choose_opt(self, pos):
if self.x < pos[0] < self.x + self.w and 2*self.y < pos[1] < 2*self.y + self.h:
return True
else:
return False
That's the definition of necessary class and attributes. Here is how I run it:
# Draw flag initial value
draw = False
# Declare element
list1 = DropDown(COLOR_INACTIVE, COLOR_LIST_INACTIVE, 50, 50, 200, 50)
# Run program
menu = True
while menu:
screen.fill((255, 255, 255))
for event in pg.event.get():
pos = pg.mouse.get_pos()
if event.type == pg.QUIT:
pg.quit()
quit()
# For the menu
if event.type == pg.MOUSEMOTION:
if list1.choose_main(pos):
list1.color_menu = COLOR_ACTIVE
else:
list1.color_menu = COLOR_INACTIVE
# For the option
if event.type == pg.MOUSEMOTION:
if list1.choose_opt(pos):
list1.color_option = COLOR_LIST_ACTIVE
else:
list1.color_option = COLOR_LIST_INACTIVE
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1 and list1.choose_main(pos):
if draw == False:
draw = True
elif draw == True:
draw = False
list1.draw_main(screen, "Select Mode")
list1.draw_opt(screen, ["Calibration", "Test"])
pg.display.flip()
clock.tick(30)
pg.quit()
My Problem:
I don't know how to select the list when they are available, in other words,
I don't know how to develop further from this step
How I think it should work?
while (the option list available = True) -> choose one of them -> select it
But I failed to implement the while loop, it just runs in infinite loop, I'm stuck. So please any help is appreciated :)
Note:
I know there are GUI module available for main menu, I've also tried them, but couldn't integrate them correctly due to little to none documentation of the module, I think the closest I can get is by using thorpy, but again there's an error I couldn't solve. So I decided to make my own.
If someone who already created dropdown list module successfully would like to share theirs, I would be so thankful.
The menu title, the options and the status of the menus should be attributes of the DropDownclass:
class DropDown():
def __init__(self, color_menu, color_option, x, y, w, h, font, main, options):
self.color_menu = color_menu
self.color_option = color_option
self.rect = pg.Rect(x, y, w, h)
self.font = font
self.main = main
self.options = options
self.draw_menu = False
self.menu_active = False
self.active_option = -1
# [...]
list1 = DropDown(
[COLOR_INACTIVE, COLOR_ACTIVE],
[COLOR_LIST_INACTIVE, COLOR_LIST_ACTIVE],
50, 50, 200, 50,
pg.font.SysFont(None, 30),
"Select Mode", ["Calibration", "Test"])
The class should have a draw method that draws the entire menu:
class DropDown():
# [...]
def draw(self, surf):
pg.draw.rect(surf, self.color_menu[self.menu_active], self.rect, 0)
msg = self.font.render(self.main, 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = self.rect.center))
if self.draw_menu:
for i, text in enumerate(self.options):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
pg.draw.rect(surf, self.color_option[1 if i == self.active_option else 0], rect, 0)
msg = self.font.render(text, 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = rect.center))
The class should have an update method that receives the events, changes the status of the menus, and returns the index of the selected option:
class DropDown():
# [...]
def update(self, event_list):
mpos = pg.mouse.get_pos()
self.menu_active = self.rect.collidepoint(mpos)
self.active_option = -1
for i in range(len(self.options)):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
if rect.collidepoint(mpos):
self.active_option = i
break
if not self.menu_active and self.active_option == -1:
self.draw_menu = False
for event in event_list:
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
if self.menu_active:
self.draw_menu = not self.draw_menu
elif self.draw_menu and self.active_option >= 0:
self.draw_menu = False
return self.active_option
return -1
while run:
event_list = pg.event.get()
for event in event_list:
# [...]
selected_option = list1.update(event_list)
if selected_option >= 0:
# [...]
# [...]
Complete example:
import pygame as pg
class DropDown():
def __init__(self, color_menu, color_option, x, y, w, h, font, main, options):
self.color_menu = color_menu
self.color_option = color_option
self.rect = pg.Rect(x, y, w, h)
self.font = font
self.main = main
self.options = options
self.draw_menu = False
self.menu_active = False
self.active_option = -1
def draw(self, surf):
pg.draw.rect(surf, self.color_menu[self.menu_active], self.rect, 0)
msg = self.font.render(self.main, 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = self.rect.center))
if self.draw_menu:
for i, text in enumerate(self.options):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
pg.draw.rect(surf, self.color_option[1 if i == self.active_option else 0], rect, 0)
msg = self.font.render(text, 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = rect.center))
def update(self, event_list):
mpos = pg.mouse.get_pos()
self.menu_active = self.rect.collidepoint(mpos)
self.active_option = -1
for i in range(len(self.options)):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
if rect.collidepoint(mpos):
self.active_option = i
break
if not self.menu_active and self.active_option == -1:
self.draw_menu = False
for event in event_list:
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
if self.menu_active:
self.draw_menu = not self.draw_menu
elif self.draw_menu and self.active_option >= 0:
self.draw_menu = False
return self.active_option
return -1
pg.init()
clock = pg.time.Clock()
screen = pg.display.set_mode((640, 480))
COLOR_INACTIVE = (100, 80, 255)
COLOR_ACTIVE = (100, 200, 255)
COLOR_LIST_INACTIVE = (255, 100, 100)
COLOR_LIST_ACTIVE = (255, 150, 150)
list1 = DropDown(
[COLOR_INACTIVE, COLOR_ACTIVE],
[COLOR_LIST_INACTIVE, COLOR_LIST_ACTIVE],
50, 50, 200, 50,
pg.font.SysFont(None, 30),
"Select Mode", ["Calibration", "Test"])
run = True
while run:
clock.tick(30)
event_list = pg.event.get()
for event in event_list:
if event.type == pg.QUIT:
run = False
selected_option = list1.update(event_list)
if selected_option >= 0:
list1.main = list1.options[selected_option]
screen.fill((255, 255, 255))
list1.draw(screen)
pg.display.flip()
pg.quit()
exit()
See also UI elements

Issue extending tail in Snake game Pygame

I'm writing a basic Snake game in Pygame, but the body of the snake is not extending after its eaten 4 pieces of food, nor does it extend after the first bite. I'm creating a new body object after each bite, and storing it in a list. x and y variables are used to store the position of the head prior to its movement, which in turn I use as the coordinates for the first segment of the snake's body. I'm then trying to iterate over this list, creating new objects with the x and y coordinates of the previous body segment to create the tail effect. Why is it stopping short?
import pygame
from pygame.locals import *
import random
import sys
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
WIN_WIDTH = 680 #width of window
WIN_HEIGHT = 500 #height of window
DISPLAY = (WIN_WIDTH, WIN_HEIGHT) #variable for screen display
DEPTH = 32 #standard
FLAGS = 0 #standard
BLACK = (0, 0, 0) #black
RED = (255, 0, 0) #red
GOLD = (255, 215, 0)
IDK = (178, 154, 96)
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption('Snake')
snake_parts = [1]
Score = 0
speed = 10
snakex = 15
snakey = 70
size = 20
Snakes = 1
# --- classes ---
class Snake(pygame.Rect):
def __init__(self, x, y, screen, size, colour):
pygame.Rect.__init__(self, x, y, size, 20)
self.screen = screen
self.colour = colour
self.x = x
self.y = y
def draw(self, screen):
pygame.draw.rect(self.screen, self.colour, self)
def coordinates(self):
return self.x, self.y
class Food(pygame.Rect):
def __init__(self, x, y, screen):
pygame.Rect.__init__(self, x, y, 20, 20)
self.screen = screen
def draw(self, screen):
pygame.draw.rect(self.screen, GOLD, self)
# --- functions ---
def get_food_pos(WIN_WIDTH, WIN_HEIGHT):
WIN_WIDTH = random.randint(1, WIN_WIDTH)
WIN_HEIGHT = random.randint(1, WIN_HEIGHT)
return WIN_WIDTH, WIN_HEIGHT
eaten = True
pressed_right = True
pressed_left = False
pressed_up = False
pressed_down = False
pygame.key.set_repeat(10,10)
while True:
screen.fill(BLACK)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN: # check for key presses
if event.key == pygame.K_LEFT: # left arrow turns left
pressed_left = True
pressed_right = False
pressed_up = False
pressed_down = False
elif event.key == pygame.K_RIGHT: # right arrow turns right
pressed_right = True
pressed_left = False
pressed_up = False
pressed_down = False
elif event.key == pygame.K_UP: # up arrow goes up
pressed_up = True
pressed_right = False
pressed_left = False
pressed_down = False
elif event.key == pygame.K_DOWN: # down arrow goes down
pressed_down = True
pressed_right = False
pressed_up = False
pressed_left = False
x = snakex
y = snakey
if pressed_left:
snakex -= speed
elif pressed_right:
snakex += speed
elif pressed_up:
snakey -= speed
elif pressed_down:
snakey += speed
snake_parts[0] = Snake(snakex, snakey, screen, int(size), IDK)
snake_parts[0].draw(screen)
if eaten:
foodx, foody = get_food_pos(WIN_WIDTH, WIN_HEIGHT)
eaten = False
my_food = Food(foodx, foody, screen)
my_food.draw(screen)
if snake_parts[0].colliderect(my_food):
eaten = True
screen.fill(BLACK)
a_snake = Snake(snakex, snakey, screen, int(size), RED)
snake_parts.append(a_snake)
if len(snake_parts) >= 1:
for i in range(1, len(snake_parts)-1):
tempx, tempy = snake_parts[i].coordinates()
snake_parts[i] = Snake(x, y, screen, int(size), RED)
snake_parts[i].draw(screen)
snake_parts[i+1] = Snake(tempx, tempy, screen, int(size), RED)
x, y = tempx, tempy
Figured it out, the issue was this line:
snake_parts[i+1] = Snake(tempx, tempy, screen, int(size), RED)
removed it and it's working fine.

Categories