Making a simple game that runs itself with ducks eating bread. It works fine but the images "glitch" or blit all over the screen, i have narrowed it down to: whenever a bread is "eaten" a new bread appears and blits all over the screen before settling down
Code in question:
for BreadPos in breadxy:
if (((Ducky.xpos - BreadPos[0]) (Ducky.ypos - BreadPos[1]))) > -5:
del FoodList[breadxy.index(BreadPos)]
Ducky.hunger = 60
FoodList.append(Food(random.randint(30,600),random.randint(30,600)))
#this line here is supposed to be indented properly but i am seemingly bad at this stack overflow thing.
Here is my code in its entirety:
import pygame
import pygame, sys
from pygame.locals import *
import random
def main():
pygame.init()
(width,height) = (640,640)
clock = pygame.time.Clock()
DISPLAY = pygame.display.set_mode((width,height))
blue= (0,60,200)
DISPLAY.fill(blue)
#pictures and spirtes
DuckPic = pygame.image.load("Duck Sprite.jpg")
DuckPic = pygame.transform.scale(DuckPic, (128, 72))
BreadPic = pygame.image.load("breadpic.jpg")
BreadPic = pygame.transform.scale(BreadPic, (128, 72))
class Food(pygame.sprite.Sprite):
def __init__(self, xpos,ypos):
super().__init__()
self.xpos = xpos
self.ypos = ypos
self.image = BreadPic
self.rect = self.image.get_rect()
FoodList = []
for i in range (10):
FoodList.append(Food(random.randint(30,600),random.randint(30,600)))
class Duck(pygame.sprite.Sprite):
def __init__(self,xpos,ypos,speed,hunger,movement):
super().__init__()
self.xpos = xpos
self.ypos = ypos
self.image = DuckPic
self.rect = self.image.get_rect()
self.speed = speed
self.hunger = hunger
self.movement = movement
Ducks = []
for i in range(1):
Ducks.append(Duck(xpos = random.randint(0, 320), ypos = random.randint(0, 320), speed =(random.randint(1,3)), hunger = 1000, movement = random.randint(1,100)))
###------------ GAME STUFF INSTANCES HAPPENING
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if FoodList == []:
print("ducks ate all the bread")
pygame.quit()
sys.exit()
if Ducks == []:
print("all ducks died")
pygame.quit()
sys.exit()
#display screen then food then ducks
DISPLAY.fill(blue)
breadxy = []
#gets xy pos of all food items
for i,FoodItem, in enumerate(FoodList):
xyList = [FoodItem.xpos , FoodItem.ypos]
breadxy.append(xyList)
for i,Ducky in enumerate(Ducks):
closestlist = [] #reset list of closest breads for all ducks
for count,j in enumerate(breadxy):
closestlist.append(((Ducky.xpos - j[0]) (Ducky.ypos - j[1])))
#closest list shows a value of how close a duck is to the bread
#where 0 or -1 = best score
indexofBread = closestlist.index(max(closestlist))
print(f"I'm moving to bread {indexofBread} with value of {max(closestlist)} which is at {FoodList[indexofBread].xpos,FoodList[indexofBread].ypos}, my postion is {Ducky.xpos, Ducky.ypos} other values include = {closestlist}")
if True:
#move duck to closest bread
if Ducky.xpos < FoodList[indexofBread].xpos:
Ducky.xpos = 1 * Ducky.speed
if Ducky.xpos > FoodList[indexofBread].xpos:
Ducky.xpos -= 1 * Ducky.speed
if Ducky.ypos > FoodList[indexofBread].ypos:
Ducky.ypos -= 1 * Ducky.speed
if Ducky.ypos< FoodList[indexofBread].ypos:
Ducky.ypos = 1 * Ducky.speed
Ducky.hunger = Ducky.hunger - 1*Ducky.speed
#duck hunger goes down, faster duck looses more hunger
if Ducky.hunger < 0: #if duck at 0 hunger he dies :(
del Ducks[i]
#display the bread
#check for collision thorugh seeing if score is good
for BreadPos in breadxy:
if (((Ducky.xpos - BreadPos[0]) (Ducky.ypos - BreadPos[1]))) > -5:
del FoodList[breadxy.index(BreadPos)]
Ducky.hunger = 60
FoodList.append(Food(random.randint(30,600),random.randint(30,600)))
#print(len(FoodList))
#print(Ducky.hunger)
for FoodItem in (FoodList):
DISPLAY.blit(FoodItem.image, (FoodItem.xpos, FoodItem.ypos))
#DISPLAY DUCK
DISPLAY.blit(Ducky.image, (Ducky.xpos, Ducky.ypos))
pygame.display.update()
#make the screen
main()
CodePudding user response:
So it looks like the issue is the tracking of the FoodList
index via the breadxy
list.
The breadxy
tracking is an index into FoodList
, so the bread that was eaten can be removed from the list. But as soon as the breads are removed, the indexes into the list are no longer correct. The "eating" loop doesn't stop checking after an item is consumed, so theoretically this loop can be repeated multiple times. After the first del()
, no index is valid. Sometimes it will crash because the index could be more than the new (shorted) length of the list.
I couldn't work out the distance algorithm!? So I substituted it with a member function on Duck
that calculates the
CodePudding user response:
The issue is with how you defined the collision distance. From your formula (Ducky.xpos - BreadPos[0]) (Ducky.ypos - BreadPos[1]) > -5
, it basically means you want the bread on the upper and left portion of your duck deleted, and consequently all the bread gets crowded at the lower-right portion of the screen.
Because of that issue, a food is randomly generated and blit to screen in 1 frame, but then on the next frame that food is deleted because of your distance formula condition and then another food is randomly generated and blit to screen.
Below is a sample manhattan distance formula. You may also apply pythagorean/euclidian distance formula. Also, if you want to go deeper with pygame, there are built-in collision functions such as colliderect, collidepoint, etc.
for BreadPos in breadxy:
distance = abs(Ducky.xpos - BreadPos[0]) abs(Ducky.ypos - BreadPos[1])
if distance < 5:
del FoodList[breadxy.index(BreadPos)]
Ducky.hunger = 60
FoodList.append(Food(random.randint(30, 600), random.randint(30, 600)))