im fairly new to coding and python, i was messing around with pygame and i was wondering if theres a way i could limit the amount of circles that spawn in this game im making? when i run the code, it just spawns in circles all over the place really fast. i tried doing the time.sleep thing, but all it does is slow down the entire game.
import pygame
import random
import time
pygame.init()
y = 0
x = 0
point = 0
is_blue = True
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode([500, 500])
def food():
pygame.draw.circle(screen, WHITE, (random.randint(1, 400), random.randint(1, 400)), 5)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pygame.display.set_caption("Collect the balls to win!")
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 3
if pressed[pygame.K_DOWN]:
y += 3
if pressed[pygame.K_LEFT]:
x -= 3
if pressed[pygame.K_RIGHT]:
x += 3
if x <= -1:
x = 0
if x >= 441:
x = 440
if y <= -1:
y = 0
if y >= 441:
y = 440
screen.fill((0, 0, 0))
if is_blue:
color = (0, 128, 255)
else:
color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 30, 30))
food()
pygame.display.flip()
clock.tick(144)
You have to use a list. Create a list for the food positions:
food_list = []
Fill the list in a loop:
while len(food_list) < 10:
food_list.append((random.randint(1, 400), random.randint(1, 400)))
Draw the foods in the list in a loop:
for food_pos in food_list:
pygame.draw.circle(screen, WHITE, food_pos, 5)
You can also spawn the food with an time interval. Use pygame.time.get_ticks() to measure the time. Define a time interval after which a new object should appear. Create an object when the point in time is reached and calculate the point in time for the next object:
food_list = []
time_interval = 1000 # 1000 milliseconds == 1 seconds
next_food_time = pygame.time.get_ticks()
done = False
while not done:
# [...]
current_time = pygame.time.get_ticks()
if current_time > next_food_time and len(food_list) < 10:
next_food_time += time_interval
food_list.append((random.randint(1, 400), random.randint(1, 400)))
See also Spawning multiple instances of the same object concurrently in python
Complete example:
import pygame
import random
import time
pygame.init()
y = 0
x = 0
point = 0
is_blue = True
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode([500, 500])
food_list = []
time_interval = 1000 # 1000 milliseconds == 1 seconds
next_food_time = pygame.time.get_ticks()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pygame.display.set_caption("Collect the balls to win!")
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 3
if pressed[pygame.K_DOWN]:
y += 3
if pressed[pygame.K_LEFT]:
x -= 3
if pressed[pygame.K_RIGHT]:
x += 3
if x <= -1:
x = 0
if x >= 441:
x = 440
if y <= -1:
y = 0
if y >= 441:
y = 440
current_time = pygame.time.get_ticks()
if current_time > next_food_time and len(food_list) < 10:
next_food_time += time_interval
food_list.append((random.randint(1, 400), random.randint(1, 400)))
screen.fill((0, 0, 0))
if is_blue:
color = (0, 128, 255)
else:
color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 30, 30))
for food_pos in food_list:
pygame.draw.circle(screen, WHITE, food_pos, 5)
pygame.display.flip()
clock.tick(144)
Related
This question already has an answer here:
Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?
(1 answer)
Closed 5 months ago.
I'm trying to create a game in pygame where you are a rectangle trying to shoot a bullet towards another rectangle, when you click a mousebutton. I'm using this with the pygame.MOUSEBUTTONDOWN, as you can see here:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
**I'll include more code for reference later.
However, when I click the mouse button, it only shoots a bullet every few times I click. I checked multiple times, and there doesn't seem to be a pattern of when it does/doesn't shoot. It shoots about every 4-5 times, but that's just an estimate.
Full code, for reference:
import pygame
import math
import random
import time
pygame.init()
# Setting all variables to use later \/
width, height = 798, 552
white = pygame.Color('white')
black = pygame.Color('black')
green = pygame.Color('green')
blue = pygame.Color('blue')
red = pygame.Color('red')
grey = pygame.Color('gray')
yellow = pygame.Color('yellow')
orange = pygame.Color('orange')
azure = pygame.Color('azure')
size_x = 100
size_y = 50
pos_x = 100
pos_y = 275
size_x_2 = 100
size_y_2 = 50
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(0, 552)
pos_x_3 = random.randint(0, 798)
pos_y_3 = random.randint(0, 552)
window = pygame.display.set_mode((width, height))
bullet_pos_y = random.randint(0, 552)
bullet_pos_x = 798
ammunition = 5
enemy_health = 1000
player_health = 50
font = pygame.font.SysFont('Times New Roman', 32)
shooting_x = 0
shooting_y = 0
# Setting All Variables to use later /\
class Bullet:
def __init__(self, x, y):
self.pos = (x, y)
self.dir = (shooting_x - x, shooting_y - y)
length = math.hypot(*self.dir)
if length == 0.0:
self.dir = (0, -1)
else:
self.dir = (self.dir[0]/length, self.dir[1]/length)
angle = math.degrees(math.atan2(-self.dir[1], self.dir[0]))
self.bullet = pygame.Surface((27.5, 17.5)).convert_alpha()
self.bullet.fill((red))
self.bullet = pygame.transform.rotate(self.bullet, angle)
self.speed = 1
def update(self):
self.pos = (self.pos[0] + self.dir[0] * self.speed, self.pos[1] + self.dir[1] * self.speed)
def draw(self, surf):
bullet_rect = self.bullet.get_rect(center = self.pos)
surf.blit(self.bullet, bullet_rect)
bullets = []
keys = pygame.key.get_pressed()
# EVERYTHING BELOW THIS IS IN THE GAME LOOP, EVERYTHING ABOVE ISN'T
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(black)
# Drawing Everything
character = pygame.draw.rect(window, white, (pos_x, pos_y, size_x, size_y))
enemy = pygame.draw.rect(window, blue, (shooting_x, shooting_y, size_x, size_y))
coin = pygame.draw.circle(window, yellow, [pos_x_2, pos_y_2], 15)
# Setting Text
enemy_health_text = font.render(str(enemy_health), True, white, blue)
enemy_health_textRect = enemy_health_text.get_rect()
enemy_health_textRect.center = (enemy.center)
player_health_text = font.render(str(player_health), True, black, white)
player_health_textRect = enemy_health_text.get_rect()
player_health_textRect.center = (character.center[0] + 9, character.center[1])
ammunition_text = font.render("ammunition remaining: " + str(ammunition), True, azure, black)
ammunition_textRect = ammunition_text.get_rect()
ammunition_textRect.center = (205, 25)
bullet_pos = character.center
enemy_pos = enemy.center
# Shooting a bullet
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
# Enemy Dealing Damage to Player
if enemy.colliderect(character):
player_health -= 1
white = pygame.Color('red')
if not enemy.colliderect(character):
player_health -= 0
white = pygame.Color('white')
mouse_pos_x, mouse_pos_y = pygame.mouse.get_pos()
pos_x, pos_y = pygame.mouse.get_pos()
# If Character collides with coin
if character.colliderect(coin):
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(100, 552)
num = random.randint(0, 20)
if num == 17:
yellow = pygame.Color('purple')
ammunition += 5
if num != 17:
yellow = pygame.Color('yellow')
ammunition += 2
elif enemy.colliderect(coin):
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(100, 552)
enemy_health += 3
# Setting the Enemy Movement
if shooting_x < pos_x_2:
shooting_x += 0.1
if shooting_x > pos_x_2:
shooting_x -= 0.1
if shooting_y < pos_y_2:
shooting_y += 0.1
if shooting_y > pos_y_2:
shooting_y -= 0.1
# Updating/Drawing Bullets
for bullet in bullets:
bullet.update()
''' WORK ON THIS '''
if not window.get_rect().collidepoint(bullet.pos):
bullets.remove(bullet)
for bullet in bullets:
bullet.draw(window)
# Making sure the player doesn't leave boundaries
if pos_y >= 552:
pos_y = 552
if pos_y <= 0:
pos_y = 0
if pos_x <= 0:
pos_x = 0
if pos_x >= 700:
pos_x = 700
# Drawing all text on screen
window.blit(ammunition_text, ammunition_textRect)
window.blit(enemy_health_text, enemy_health_textRect)
window.blit(player_health_text, player_health_textRect)
pygame.display.update()
The function pygame.event.get() returns an object with all events that happens. It clears all events afterwards so nothing will be executed twice when an event occurs. So you shouldn't call it twice without checking all needed events both times. Some events might get ignored when you don't call pygame.event.get() often because it has a limit of 128 events.
As a solution you can save the events in a variable (Some events might be delayed):
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
run = False
...
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
Or you only have one event loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
...
I've been trying to make Space Invaders using PyGame in Python and make a list for the enemy images, and I make a for loop twice to load the images in and append them to the lists; 2 times for 2 rows. Though, the only pictures that appear are the ones from the first for loop.
There are no errors either, so it seems it is working. I also checked to see if the two rows were overlapping each other, but from the way I tested it, I found that they weren't.
I made this before here without using OOP and now I'm using OOP to learn it better, and it isn't working properly, please help.
For Loops Below
EnemyRowBottom = EnemyClass()
EnemyRowTop = EnemyClass()
for i in range(EnemyRowBottom.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowBottom.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowBottom.EnemyX.append( x )
EnemyRowBottom.EnemyY.append( 150 )
for i in range(EnemyRowTop.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowTop.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowTop.EnemyX.append( x )
EnemyRowTop.EnemyY.append( 50 )
Full Code below
import pygame
import random
import math
from pygame import mixer
import time
# initialize pygame
pygame.init()
# create screen
screenX = 800
screenY = 800
screen = pygame.display.set_mode( (screenX, screenY) )
# background
background = pygame.image.load("background.jpg")
#sounds
mixer.music.load('bgMusic.mp3')
mixer.music.play(-1)
# title and icon
pygame.display.set_caption("Space Invaders")
icon = pygame.image.load('Icon.jpg')
pygame.display.set_icon(icon)
# score
scoreValue = 0
font = pygame.font.Font('freesansbold.ttf', 32)
textX = 10
textY = 10
def show_score(x, y):
global scoreValue
score = font.render("Score: " + str(scoreValue), True, (255, 255, 255))
screen.blit(score, (x, y))
# player
PlayerImage = pygame.image.load('SpaceShip.png')
PlayerImage = pygame.transform.scale(PlayerImage, (84, 84))
playerX = 370
playerY = 650
xChangePlayer = 0
def player(x, y):
screen.blit(PlayerImage, (x, y))
# check collision
def Collision(EX, EX2, EY, EY2, LX, LY):
distance = math.sqrt((math.pow(EX - LX, 2) + math.pow(EY - LY, 2)))
distance2 = math.sqrt((math.pow(EX2 - LX, 2) + math.pow(EY2 - LY, 2)))
if distance <= 35:
return 1
elif distance2 <= 35:
return 2
def DistanceEnemy(EX, EX2, EY, EY2):
distance = math.sqrt((math.pow(EX2 - EX, 2) + math.pow(EY2 - EY, 2)))
if distance <= 70:
return True
# bottom line
Line = pygame.image.load('BottomLine.jpg')
LineX = 0
LineY = 655
def BottomLine(x, y):
global Line
screen.blit(Line, (x, y))
# Laser
LaserImage = pygame.image.load('PlayerLaser.png')
LaserImage = pygame.transform.scale(LaserImage, (50, 50))
LaserX = random.randint(15, (screenX - 85) )
LaserY = 750
xLaserChange = 0
yLaserChange = 1
# can't see bullet on screen until Ready changes to Fire
LaserState = "Ready"
def Firelaser(x, y):
global LaserState
LaserState = "Fire"
screen.blit(LaserImage, (x+17, y+10))
GameOverfont = pygame.font.Font('freesansbold.ttf', 82)
def GameOver():
global GameOverfont
text = GameOverfont.render("GAME OVER", True, (255, 255, 255))
screen.blit(text, (140, 250))
# enemy
class EnemyClass:
EnemyImage = []
EnemyX = []
EnemyY = []
numEnemies = 4
xEnemyChange = 0.2
yEnemyChange = 50
def enemyMovement(self, i):
EnemyClass.EnemyX[i] += EnemyClass.xEnemyChange
if EnemyClass.EnemyX[i] >= 736:
EnemyClass.xEnemyChange = -EnemyClass.xEnemyChange
for x in range(len(EnemyClass.EnemyY)):
EnemyClass.EnemyY[x] += EnemyClass.yEnemyChange
elif EnemyClass.EnemyX[i] <= 0:
EnemyClass.xEnemyChange = .2
for x in range(len(EnemyClass.EnemyY)):
EnemyClass.EnemyY[x] += EnemyClass.yEnemyChange
def enemy(self, i):
screen.blit(EnemyClass.EnemyImage[i], (EnemyClass.EnemyX[i], EnemyClass.EnemyY[i]))
def enemyGameOver(self, x, y):
screen.blit(EnemyClass.EnemyImage[i], (x, y))
def CollisionDetection(self, i, scoreValue, lasery, laserstate):
global LaserY, LaserState
ExplosionSound = mixer.Sound('explosion.wav')
ExplosionSound.play()
LaserY = lasery
LaserState = laserstate
scoreValue += 1
# enemy respawn
OldEnemyY = EnemyClass.EnemyY[i]
EnemyClass.EnemyY[i] = -20
if EnemyClass.EnemyY[i] >= OldEnemyY:
EnemyClass.EnemyY[i] -= 40
EnemyRowBottom = EnemyClass()
EnemyRowTop = EnemyClass()
for i in range(EnemyRowBottom.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowBottom.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowBottom.EnemyX.append( x )
EnemyRowBottom.EnemyY.append( 150 )
for i in range(EnemyRowTop.numEnemies):
EnemyImage1 = pygame.image.load('enemy.png')
EnemyRowTop.EnemyImage.append( pygame.transform.scale(EnemyImage1, (70, 70)) )
y = i * 10
x = y * 10
EnemyRowTop.EnemyX.append( x )
EnemyRowTop.EnemyY.append( 50 )
# game loop
running = True
while running:
# setting background
screen.fill( (0, 0, 0) )
screen.blit(background, (0, 0))
# make function that closes program when x pressed
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# keystroke detection
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
xChangePlayer = -0.4
if event.key == pygame.K_RIGHT:
xChangePlayer = +0.4
if event.key == pygame.K_SPACE:
if LaserState == "Ready":
LaserSound = mixer.Sound('shoot.wav')
LaserSound.play()
LaserX = playerX
LaserY = 600
Firelaser(LaserX, LaserY)
LaserY -= yLaserChange
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
xChangePlayer = 0
# player movement
playerX += xChangePlayer
# setting boudries for the player
if playerX < 0:
playerX = 0
elif playerX >= 736:
playerX = 735
# setting boudries for the enemy
for i in range(EnemyRowTop.numEnemies):
BlitEnemy1 = EnemyRowTop.enemy(i)
BlitEnemy2 = EnemyRowBottom.enemy(i)
gameSoundPlayed = 0
if EnemyRowTop.EnemyY[i] >= 600 or EnemyRowBottom.EnemyY[i] >= 600 and (gameSoundPlayed == 0):
GameOver()
BottomLine(LineX, LineY)
player(playerX, playerY)
show_score(textX, textY)
pygame.display.update()
gameSoundPlayed += 1
EnemyRowTop.xEnemyChange = 0
EnemyRowTop.yEnemyChange = 0
EnemyRowBottom.xEnemyChange = 0
EnemyRowBottom.yEnemyChange = 0
mixer.music.stop()
mixer.music.load('GameOverSound.wav')
mixer.music.play()
time.sleep(3)
break
EnemyRowTop.enemyMovement(i)
EnemyRowBottom.enemyMovement(i)
# collision detection
collision = Collision(EnemyRowTop.EnemyX[i], EnemyRowBottom.EnemyX[i], EnemyRowTop.EnemyY[i], EnemyRowBottom.EnemyY[i], LaserX, LaserY)
if collision == 1:
EnemyRowTop.CollisionDetection(i, scoreValue, 600, "Ready")
EnemyDistance = DistanceEnemy(EnemyRowTop.EnemyX[i], EnemyRowBottom.EnemyX[i], EnemyRowTop.EnemyY[i], EnemyRowBottom.EnemyY[i])
if EnemyDistance == True:
EnemyClass.EnemyY[i] -= 50
if collision == 2:
EnemyRowBottom.CollisionDetection(i, scoreValue, 600, "Ready")
EnemyDistance = DistanceEnemy(EnemyRowTop.EnemyX[i], EnemyRowBottom.EnemyX[i], EnemyRowTop.EnemyY[i], EnemyRowBottom.EnemyY[i])
if EnemyDistance == True:
EnemyClass.EnemyY[i] -= 50
BlitEnemy1
BlitEnemy2
# laser movement
if LaserY <= -20:
LaserY = 750
LaserState = "Ready"
if LaserState == "Fire":
Firelaser(LaserX, LaserY)
LaserY -= yLaserChange
# outputting characters
BottomLine(LineX, LineY)
player(playerX, playerY)
show_score(textX, textY)
pygame.display.update()
if gameSoundPlayed == 1:
break
I cant test it but I think I found the problem. You should add a _init_ method to your EnemyClass. There you can define these variables so each row has its own lists of enemies. Because your enemies have the same image you dont need to load the image more than once.
EnemyImage = []
EnemyX = []
EnemyY = []
numEnemies = 4
xEnemyChange = 0.2
yEnemyChange = 50
class EnemyClass:
EnemyImage = pygame.transform.scale(pygame.image.load('enemy.png'), (70, 70))
def __init__(self):
self.EnemyX = []
self.EnemyY = []
self.numEnemies = 4
self.xEnemyChange = 0.2
self.yEnemyChange = 50
EnemyRowBottom = EnemyClass()
EnemyRowTop = EnemyClass()
But it would make more sense to have an object for each enemy instead of the enemy rows.
The problem of your code is, that the list containing the images in your EnemyClass is a class-attribute of your EnemyClass. As a result, every instance of that class accesses the same list of EnemyClass, and has not its own list in EnemyRowBottom and EnemyRowTop.
You can fix this problem by making EnemyImage an object-attribute:
class EnemyClass:
def __init__(self):
self.EnemyImage = []
self.EnemyX = []
self.EnemyY = []
self.numEnemies = 4
self.xEnemyChange = 0.2
self.yEnemyChange = 50
You can then access your images with "self.EnemyImage" inside the methods.
Moreover, to avoid the same image twice, you can put your pygame.image.load-method outside and before the loops, so that the game starts quicker.
So I keep trying to detect two different moving rects in pygame colliding but its just constantly registering a collision. As in the console constantly print lose, but the enemy isn't even touching the ball. The effect I want is just a simple game where the ball has to avoid the tack so that the ball doesn't pop.
import pygame
import sys
import math
import random
pygame.init()
size = width, height = 800, 600
white = 255, 255, 255
red = 255, 0, 0
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
character = pygame.image.load("intro_ball.gif")
charrect = character.get_rect()
x = 340
y = 480
enemy = pygame.image.load("ho.png")
enrect = enemy.get_rect()
ex = random.randint(0, 690)
ey = 0
lose = False
while 1:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
if ey >= 600 and lose != True:
ey = 0
ex = random.randint(0, 690)
collide = pygame.Rect.colliderect(charrect, enrect)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT and x < 690:
x += 4
if event.key == pygame.K_LEFT and x > 0:
x -= 4
if collide:
lose = True
else: lose = False
if lose == True: print("lose")
ey += 2
screen.fill(white)
screen.blit(enemy, (ex, ey))
screen.blit(character, (x, y))
pygame.display.update()
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. A Surface is blit at a position on the screen. The position of the rectangle can be specified by a keyword argument. For example, the top lelft of the rectangle can be specified with the keyword argument topleft. These keyword argument are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a full list of the keyword arguments):
while 1:
# [...]
charrect = character.get_rect(topleft = (x, y))
enrect = enemy.get_rect(topleft = (ex, ey))
collide = pygame.Rect.colliderect(charrect, enrect)
# [...]
On the other hand you do not need the variables x, y, ex, ey at all. Use the features of the pygame.Rect objects.
Additionally read How can I make a sprite move when key is held down
Minimal example:
import pygame, sys, math, random
pygame.init()
size = width, height = 800, 600
white = 255, 255, 255
red = 255, 0, 0
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
#character = pygame.image.load("intro_ball.gif")
character = pygame.Surface((20, 20))
character.fill((0, 255, 0))
charrect = character.get_rect(topleft = (340, 480))
#enemy = pygame.image.load("ho.png")
enemy = pygame.Surface((20, 20))
enemy.fill((255, 0, 0))
enrect = enemy.get_rect(topleft = (random.randint(0, 690), 0))
lose = False
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
charrect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 4
charrect.clamp_ip(screen.get_rect())
enrect.y += 2
if enrect.y >= 600 and lose != True:
enrect.y = 0
enrect.x = random.randint(0, 690)
collide = pygame.Rect.colliderect(charrect, enrect)
if collide:
lose = True
else:
lose = False
if lose == True:
print("lose")
screen.fill(white)
screen.blit(enemy, enrect)
screen.blit(character, charrect)
pygame.display.update()
pygame.quit()
exit()
This question already has an answer here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
Closed 2 years ago.
I was trying to make a game where you chop down some trees and sell it, but in doing so I managed to find a mind-breaking bug - the first tree would invert all the other trees! I know it doesn't make sense, but what i mean is that I have sorted the trees into a class and I have created a variable instance called self.tree_property which determines if the tree has been cut down or not. When you chop down the first tree all the other tree_property get set to True again. When you chop down any other tree, the first tree's tree_property gets set to True again Can anyone tell me how to fix it, and why it is happening?
The code is below:
import pygame
import time
pygame.init()
print('Loading game...')
icon = pygame.image.load('C:\Program Files\Foraging Simulator\icon.png')
market = pygame.image.load('C:\Program Files\Foraging Simulator\market.png')
house = pygame.image.load('C:\Program Files\Foraging Simulator\house.png')
pygame.display.set_icon(icon)
market = pygame.transform.scale(market, (100, 100))
house = pygame.transform.scale(house, (100, 100))
root = pygame.display.set_mode((603, 573))
pygame.display.set_caption("Foraging Simulator")
window_is_open = True
white = (255, 255, 255)
black = (0, 0, 0)
width = 10
leaves_width = 30
height = 20
leaves_height = 10
x = 0
tree_trunk_x = 10
y = 0
tree_trunk_y = 10
vel = 5
brown = (150, 75, 0)
green = (58, 95, 11)
grass = (124, 252, 0)
score = 0
chop_wood = 'C:\Program Files\Foraging Simulator\chopping.mp3'
clock = pygame.time.Clock()
time = 0
happiness = 10
def test(string):
print(string)
def reset_trees():
for tree in trees:
tree.tree_property = True
def open_house():
reset_trees()
score = 0
def open_market():
happiness = score / 2
class Tree: # The class for the trees
def __init__(self, tree_x, tree_y):
self.tree_x = tree_x
self.tree_y = tree_y
self.tree_property = True # Creating instance tree_property
self.trunk = None
self.leaves = None
def destroy(self):
self.tree_property = False
def create_tree(self):
if self.tree_property:
trunk_x = self.tree_x + 10
trunk_y = self.tree_y + 10
self.trunk = pygame.draw.rect(root, brown, (trunk_x, trunk_y, width, height))
self.leaves = pygame.draw.rect(root, green, (self.tree_x, self.tree_y, leaves_width, leaves_height))
def redraw(self):
self.create_tree()
trees = []
for x in range(5):
for y in range (5):
trees.append(Tree(x*50, y*50))
root.fill(grass)
destroy_tree = None
countdown = 3
clock.tick(60)
say = True
print('Loading and attributes finsihed! Using mainloop...')
while window_is_open:
if say:
print('Mainloop loaded! Ready to go.')
say = False
time = pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
if market.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
mouse_x, mouse_y = event.pos
if house.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees: # Clicking detection
mouse_x, mouse_y = pygame.mouse.get_pos()
if tree.trunk.collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
x += vel
if keys[pygame.K_LEFT]:
x -= vel
if keys[pygame.K_UP]:
y -= vel
if keys[pygame.K_DOWN]:
y += vel
if destroy_tree != None:
if countdown == 3:
pygame.time.delay(950)
countdown = countdown - 1
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
elif countdown > 0:
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
pygame.time.delay(950)
countdown = countdown - 1
else:
destroy_tree.destroy()
destroy_tree = None
countdown = 3
score = score + 1
font = pygame.font.SysFont('Tahoma', 18, True, False)
count = font.render(str(countdown), True, (0, 0, 0))
screen_score = font.render("Score: " + str(score), True, (0, 0, 0))
rendered_happiness = font.render("Happines: " + str(happiness), True, (0, 0, 0))
root.blit(rendered_happiness, (410, 40))
root.blit(count, (410, 0))
root.blit(screen_score, (410, 20))
rectangle = pygame.draw.rect(root, (0, 0, 0), (x, y, width, 10))
for tree in trees:
tree.redraw()
root.blit(market, (400, 300))
seconds = clock.tick()
pre = time + seconds / 1000
time = int(pre)
root.blit(house, (400, 100))
pygame.display.update()
root.fill(grass)
pygame.quit()
Thanks!
A pygame.Surface object has no location. The position of the rectangle which is returned by get_rect() is always (0, 0).
Note, you specify a location when the surface is blit:
root.blit(house, (400, 100))
You have to set the same location, when you retrieve the rectangle for the collision. You can pass keyword argument values to this function, which are set to the pygame.Rect object:
house.get_rect(topleft = (400, 100))
For instance:
while window_is_open:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
dx = mouse_x - x
dy = mouse_y - y
if market.get_rect(topleft = (400, 300)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
if house.get_rect(topleft = (400, 100)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees:
if tree.trunk.collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
# [...]
Furthermore in you have to use the global statement to treat happiness as a variable in global namespace in the function open_market:
def open_market():
global happiness
happiness = score / 2
I am making a simple box moving "game" to learn the pygame import, but I would like to set the box back to gray after there are no keys pressed.
import pygame
from pygame.locals import *
pygame.init()
width = 400
height = 400
screen = pygame.display.set_mode((width,height))
done = False
clock = pygame.time.Clock()
color = (150, 150, 150)
x = 0
y = 0
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP] or pressed[pygame.K_w] and y > 0:
y -= 3
color = (0,0,255)
if pressed[pygame.K_DOWN] or pressed[pygame.K_s] and y+60 < height: #Adding 60 because height of block = 60
y += 3
color = (255,255,0)
if pressed[pygame.K_LEFT] or pressed[pygame.K_a] and x > 0:
x -= 3
color = (0,255,0)
if pressed[pygame.K_RIGHT] or pressed[pygame.K_d] and x+60 < width: #Adding 60 because width of block = 60
x += 3
color = (255,0,0)
screen.fill((0, 0, 0))
pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))
pygame.display.flip()
clock.tick(60)
Just add the set of the color=(150,150,150) inside the while loop(:
Put it as the first line inside the loop.