问题
Hello I am relatively new to creating games/coding so sorry before hand for the multitude of issues I am going to be bringing and the god awful organisation of my code, I'm attempting to have my class' have a function to render themselves into the game instead of using an outside source
`class Animal(pygame.sprite.Sprite):
def __init__(Self,):
super().__init__()
Self.Image=pygame.image.load('Blank.png').convert_alpha()
Self.rect=Self.image.get_rect()
Self.x=x
Self.y=y
Self.Screen= screen
#Self.Width=Width
#Self.Height=Height
#Self.Energy=0
def BoundryX(entityX):
if entityX<=0:
entityX=0
elif entityX>=600:
entityX=600
def BoundryY(entityY):
if entityY<=0:
entityY=0
elif entityY>=800:
entityY=800
class Predator(Animal):
def __init__(Self):
#super().__init__()
Self.Img=pygame.image.load('Icon.png')
Self.PredatorX=0
Self.PredatorY=0
Self.Screen= screen
def Render(Self,Img,X,Y):
Self.screen.blit(Img,(X,Y))
`
I'm having issues as it says the class doesn't have the attribute "screen" and I do not know what that means, on a side note what would be the best way to create a function to create more of a set class after they have eaten enough of their food chain as well as a function to remove them as well as organise all of the different sprites(I understand this isn't apart of my main issue so if it doesn't get answer that's fine)
here is the full code: (sorry for any pain caused by my terrible formatting
#imports
import math
import random
import pygame,sys
import random
import pdb
from pygame.locals import *
timmer=1
class Animal(pygame.sprite.Sprite):
def __init__(Self,):
super().__init__()
Self.Image=pygame.image.load('Blank.png').convert_alpha()
Self.rect=Self.image.get_rect()
Self.x=x
Self.y=y
Self.Screen= screen
#Self.Width=Width
#Self.Height=Height
#Self.Energy=0
def BoundryX(entityX):
if entityX<=0:
entityX=0
elif entityX>=600:
entityX=600
def BoundryY(entityY):
if entityY<=0:
entityY=0
elif entityY>=800:
entityY=800
class Predator(Animal):
def __init__(Self):
#super().__init__()
Self.Img=pygame.image.load('Icon.png')
Self.PredatorX=0
Self.PredatorY=0
Self.Screen= screen
def Render(Self,Img,X,Y):
Self.screen.blit(Img,(X,Y))
class prey(pygame.sprite.Sprite):
def __init__():
Self.preyImg=pygame.image.load('Prey.png')
Self.preyX=300
Self.preyY=700
Self.PreyX_change=0
def Render(Self):
Self.screen.blit(preyImg,(preyX,preyY))
def delete(Self):
i.delete()
CarrotImg=pygame.image.load('carrot.png')
CarrotX=100
CarrotY=300
foodamount=7
def food():
#CarrotX=random.randint(10,950)
#CarrotY=random.randint(10,750)
screen.blit(CarrotImg,(CarrotX,CarrotY))
#setup pygame
pygame.init()
#caption and Icons
pygame.display.set_caption("Game Of Life")
#predator icon
predatorImg=pygame.image.load('Icon.png')
predatorX=900
predatorY=100
predatorX_change=0
#Prey Icon
preyImg=pygame.image.load('Prey.png')
preyX=300
preyY=700
PreyX_change=0
#def delete():
#prey.delete()
preyImg=pygame.image.load('Prey.png')
preyX=300
preyY=700
PreyX_change=0
#def Prey():
#screen.blit(preyImg,(preyX,preyY))
class setup():
def __init__():
x=1
def Predator1(Self):
screen.blit(predatorImg,(predatorX,predatorY))
#Finding closest prey
def FindClosestItem(AgressorX,DefenderX,AgressorY,DefenderY):
dist = math.sqrt((AgressorX-DefenderX)**2 + (AgressorY-DefenderY)**2)#finds distance in pixels
#create pop out for game
screen=pygame.display.set_mode((1000,800))
def Tracking(AgressorX,DefenderX,AgressorY,DefenderY):
global XMovement#make variables global so it actually works
global YMovement
if AgressorX > DefenderX:#finds whether its position then moves left/righ,up/down depending on its location
XMovement=-0.25
elif AgressorX< DefenderX:
XMovement=0.25
else:
XMovement=0
if AgressorY > DefenderY:
YMovement=-0.25
elif AgressorY < DefenderY:
YMovement=0.25
else:
YMovement=0
def EatPrey(predatorX,PreyX,predatorY,preyY):
dist = math.sqrt((predatorX-preyX)**2 + (predatorY-preyY)**2)
if dist < 20:
return True
else:
return False
#setup test
predator=Predator()
#Sprite groups
all_sprites_Wolves=pygame.sprite.Group()
all_sprites_Rabbits=pygame.sprite.Group()
all_sprites_Carrots=pygame.sprite.Group()
#game loop
running=True
while running:
#Back ground colour
screen.fill((0,128,0))
for event in pygame.event.get():
if event.type==pygame.QUIT:
running=False
predator.Render(pygame.image.load('Icon.png'),600,700)
#Prey.Render()
ClosestPrey=FindClosestItem(predatorX,preyX,predatorY,preyY)
food()
Track(predatorX,preyX,predatorY,preyY)
predatorX+=XMovement
predatorY+=YMovement
#predatorX=BoundryX(predatorX)
#predatorY=BoundryY(predatorY)
Track(preyX,CarrotX,preyY,CarrotY)
preyX+=XMovement
preyY+=YMovement
#preyX=BoundryX(preyX)
#preyY=BoundryY(preyY)
#Eat=EatPrey(preyX,preyY,predatorX,predatorY)
#if Eat==True:
#delete()
#T=1
#Boundry(prey)
if preyX<=0:
preyX=0
elif preyX>=950:
preyX=950
if preyY<=0:
preyY=0
elif preyY>=750:
preyY=750
#preyY-=1
#Boundry(predator)
if predatorX<=0:
predatorX=0
elif predatorX>=950:
predatorX=950
elif predatorY<=0:
predatorY=0
elif predatorY>=750:
predatorY=750
pygame.display.update()
timmer=timmer+1
回答1:
Your code is quite a mess, but don't worry, let's create a simple pygame game step for step. Try to understand what each step does.
First, let's start with a basic skeleton for a game, something like this:
import pygame
def main():
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill('grey')
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Here, we have a simple main
function and a simple game loop that does nothing but listen for the QUIT
event, make everything grey and limit the framerate to 60. You don't have to have a main
function and the __name__
check but doing this is good practice as it allows you to import file without running the game. Also, I helps to not pollute the global namespace.
OK, let's create some Sprites
:
import pygame
from random import randint, choice
class Animal(pygame.sprite.Sprite):
colors = ['lightblue', 'blue', 'darkblue', 'dodgerblue']
def __init__(self, pos=None, color=None):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(color if color else choice(Animal.colors))
self.rect = self.image.get_rect(center = pos if pos else (randint(100, 540), randint(100, 380)))
def main():
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
animals = pygame.sprite.Group()
animals.add(Animal())
animals.add(Animal())
animals.add(Animal())
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill('grey')
animals.draw(screen)
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
To use the Sprite class in pygame, the class needs an image
attribute, which is a Surface
, and a rect
attribute, which is a Rect and contains the position and the size of the Surface
. If you create a Sprite
like this, you can make use of the Group class (or its subclasses) to draw and update your sprites. To draw somethin, I added three Animals to a group animals
and call the draw
function, passing the screen
Surface as an argument so the Group
knows where to blit the Animals' images.
Since this is quite boring so far, let's add some behaviour to our sprites:
import pygame
from random import randint, choice
class Animal(pygame.sprite.Sprite):
colors = ['lightblue', 'blue', 'darkblue', 'dodgerblue']
def __init__(self, pos=None, color=None):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(color if color else choice(Animal.colors))
self.rect = self.image.get_rect(center = pos if pos else (randint(100, 540), randint(100, 380)))
self.pos = pygame.Vector2(*self.rect.center)
self.speed = 3
self.direction = pygame.Vector2(1, 0).rotate(randint(0, 360))
def update(self, dt):
v = self.direction * self.speed
while not pygame.display.get_surface().get_rect().contains(self.rect.move(v)):
self.direction = pygame.Vector2(1, 0).rotate(randint(0, 360))
v = self.direction * self.speed
self.pos += v
self.rect.center = self.pos
def main():
screen = pygame.display.set_mode((640, 480))
clock, dt = pygame.time.Clock(), 0
animals = pygame.sprite.Group()
animals.add(Animal())
animals.add(Animal())
animals.add(Animal())
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
animals.update(dt/1000)
screen.fill('grey')
animals.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()
As you can see, we keep all the moving logic in the Animal
class. The only thing that changed in the game loop is that we keep track of the delta time dt
to ensure a constant frame rate (honestly, that's not that important in a small example like this, but again, good practice), and pass it to the groups update
function, which in turn will call the update
function of each sprite it contains.
In the Animal class, we use some simple vector math to move the sprite: we have a speed
and a direction
(which is a vector); and also an additional pos
vector. Changing the position is easy with vectors because we can just do something like pos = pos + direction * speed
, and changing the direction is simple too if we just randomly rotate the direction vector.
If you want to create a 2D game with moving parts I recommend that you learn a litte bit of vector math if you didn't already learn it in school. You don't need to know much more than the fact that you can easily add them up or multiply them etc.
Remember that the sprite is drawn at the coordinate of its rect
, so we need to update the rect's position, too.
Pygame's Rect
class has some handy functions, too. Just look how we check if the sprite would go out of screen. We can simply grab the Rect
of the display surface, move the Sprite's Rect
and check if it's still inside the screen rect. If not, we randomly rotate our direction
vector (well, it's not 100% perfect, but KISS).
So what about other sprites? Let's just subclass our Animal
class, change the speed and color and the behaviour by overwriting the update function:
import pygame
import pygame.freetype
from random import randint, choice
from math import hypot
class Animal(pygame.sprite.Sprite):
colors = ['lightblue', 'blue', 'darkblue', 'dodgerblue']
def __init__(self, pos=None, color=None, *grps):
super().__init__(*grps)
self.image = pygame.Surface((32, 32))
self.color = color if color else choice(Animal.colors)
self.image.fill(self.color)
self.rect = self.image.get_rect(center = pos if pos else (randint(100, 540), randint(100, 380)))
self.pos = pygame.Vector2(*self.rect.center)
self.speed = 3
self.direction = pygame.Vector2(1, 0).rotate(randint(0, 360))
def update(self, dt):
v = self.direction * self.speed
while not pygame.display.get_surface().get_rect().contains(self.rect.move(v)):
self.direction = pygame.Vector2(1, 0).rotate(randint(0, 360))
v = self.direction * self.speed
self.pos += v
self.rect.center = self.pos
class Preditor(Animal):
def __init__(self, animals, pos=None, color=None, *grps):
super().__init__(pos, color or 'red', *grps)
self.speed = 4
self.target = None
self.animals = animals
self.eaten = 0
self.font = pygame.freetype.SysFont(None, 16)
self.font.render_to(self.image, (10, 10), str(self.eaten), 'white')
def get_nearest_animal(self):
target = None
distance = None
for animal in self.animals:
pygame.draw.line(pygame.display.get_surface(), 'darkgrey', self.pos, animal.pos)
if not target:
target = animal
distance = hypot(animal.pos.x - self.pos.x, animal.pos.y - self.pos.y)
else:
new_distance = hypot(animal.pos.x - self.pos.x, animal.pos.y - self.pos.y)
if new_distance < distance:
target = animal
distance = new_distance
if target:
pygame.draw.line(pygame.display.get_surface(), 'green', self.pos, target.pos)
return target
def update(self, dt):
self.target = self.get_nearest_animal()
if self.target:
self.direction = (self.target.pos - self.pos).normalize()
else:
self.direction = pygame.Vector2(0, 0)
self.pos += self.direction * self.speed
self.rect.center = self.pos
if self.target and self.rect.colliderect(self.target.rect):
self.target.kill()
self.image.fill(self.color)
self.eaten += 1
self.font.render_to(self.image, (10, 10), str(self.eaten), 'white')
def main():
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock, dt = pygame.time.Clock(), 0
animals = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
for _ in range(5):
Animal(None, None, animals, all_sprites)
Preditor(animals, None, None, all_sprites)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.KEYDOWN:
Animal(None, None, animals, all_sprites)
screen.fill('grey')
all_sprites.update(dt/1000)
all_sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Sprites can be part of multiple groups, and we use that here to give the Predator
a list of all its victims it can hunt and eat. Again, see how all sprite behaviour is inside the sprite classes. The only thing the main loop does is creating the initial state telling the sprite groups to update and draw all their sprites (and adding new Animals by pressing a key, because why not). Also note how we can use kill
to remove it from all it groups, basically removing it from the game.
Hope that helps and gives you an idea on how to organize your pygame game.
Just play around a little. How about Animals that need to eat to stay alive and reproduce? And predators that will only need slower animals within reach, and if there are none, eat other predators or even plants?
import pygame
import pygame.freetype
from random import randint, choice
from math import hypot
from dataclasses import dataclass
class Plant(pygame.sprite.Sprite):
colors = ['green', 'lightgreen', 'darkgreen']
def __init__(self, pos=None, color=None, *grps):
self._layer = -10
super().__init__(*grps)
self.image = pygame.Surface((24, 24))
self.color = color if color else choice(Plant.colors)
self.image.fill(self.color)
self.rect = self.image.get_rect(center = pos if pos else (randint(10, 630), randint(10, 470)))
self.pos = pygame.Vector2(*self.rect.center)
class Animal(pygame.sprite.Sprite):
font = None
colors = ['lightblue', 'blue', 'darkblue', 'dodgerblue']
def __init__(self, system, pos=None, color=None, *grps):
super().__init__(*grps)
self.image = pygame.Surface((24, 24))
self.color = color if color else choice(Animal.colors)
self.image.fill(self.color)
self.rect = self.image.get_rect(center = pos if pos else (randint(100, 540), randint(100, 380)))
self.pos = pygame.Vector2(*self.rect.center)
self.speed = randint(20, 50) / 10
self.direction = pygame.Vector2(1, 0).rotate(randint(0, 360))
self.reproduce = randint(1, 5)
self.sleep = 0
self.eaten = 0
self.energy = 10
self.system = system
if not self.font:
Animal.font = pygame.freetype.SysFont(None, 16)
def base_update(self, dt):
self.energy -= dt * (self.speed / 2)
if self.energy <= 0:
self.kill()
return False
if self.sleep >= 0:
self.sleep -= dt
return False
self.reproduce -= dt
if self.reproduce <= 0 and self.energy >= 4:
self.reproduce = randint(1, 7)
self.__class__(self.system, self.pos, None, *self.groups())
self.sleep = 0.5
self.energy -= 1.5
return True
def update_image(self):
self.image.fill(self.color)
self.image.set_alpha(122 if self.sleep > 0 else 255)
pygame.draw.rect(self.image, 'green', (0, 0, self.rect.width * self.energy/10, 3))
self.font.render_to(self.image, (7, 7), str(self.eaten), 'white')
def get_nearest_target(self, targets, max_distance=250):
target = None
distance = None
for possible_target in targets:
if possible_target == self or hasattr(possible_target, 'speed') and possible_target.speed > self.speed:
continue
new_distance = hypot(possible_target.pos.x - self.pos.x, possible_target.pos.y - self.pos.y)
pygame.draw.line(pygame.display.get_surface(), 'darkgrey' if new_distance > max_distance else 'white', self.pos, possible_target.pos)
if new_distance <= max_distance:
if not target or new_distance < distance:
target = possible_target
distance = new_distance
if target:
pygame.draw.line(pygame.display.get_surface(), 'green', self.pos, target.pos)
return target
def update(self, dt):
if not self.base_update(dt) or len(self.groups()) == 0:
return
v = self.direction * self.speed
while not pygame.display.get_surface().get_rect().contains(self.rect.move(v)):
self.direction = pygame.Vector2(1, 0).rotate(randint(0, 360))
v = self.direction * self.speed
for plant in self.system.plants:
if plant.rect.colliderect(self.rect) and self.energy < 8:
plant.kill()
self.eaten += 1
self.energy = 10
continue
self.pos += v
self.rect.center = self.pos
self.update_image()
class Preditor(Animal):
def __init__(self, system, pos=None, color=None, *grps):
super().__init__(system, pos, color or 'red', *grps)
self.speed = randint(20, 40) / 10
self.target = None
self.update_image()
def update(self, dt):
if not self.base_update(dt) or len(self.groups()) == 0:
return
self.target = self.get_nearest_target(self.system.animals)
if not self.target:
self.target = self.get_nearest_target(self.system.preditors)
if not self.target:
self.target = self.get_nearest_target(self.system.plants)
if self.target:
self.direction = (self.target.pos - self.pos).normalize()
else:
self.direction = pygame.Vector2(0, 0)
self.pos += self.direction * self.speed
self.rect.center = self.pos
if self.target and self.rect.colliderect(self.target.rect):
self.target.kill()
self.eaten += 1
self.sleep = 0.5
self.energy += 3
if self.energy > 10:
self.energy = 10
self.update_image()
@dataclass
class System:
plants: object
animals: object
preditors: object
def main():
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock, dt = pygame.time.Clock(), 0
GROW = pygame.USEREVENT + 1
pygame.time.set_timer(GROW, 1000)
animals = pygame.sprite.Group()
plants = pygame.sprite.Group()
preditors = pygame.sprite.Group()
all_sprites = pygame.sprite.LayeredUpdates()
system = System(plants, animals, preditors)
for _ in range(4):
Animal(system, None, None, animals, all_sprites)
for _ in range(5):
Plant(None, None, plants, all_sprites)
Preditor(system, None, None, preditors, all_sprites)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.KEYDOWN:
Animal(system, None, None, animals, all_sprites)
if e.type == GROW:
for _ in range(5):
Plant(None, None, plants, all_sprites)
screen.fill('grey')
all_sprites.update(dt/1000)
all_sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(30)
if __name__ == '__main__':
main()
来源:https://stackoverflow.com/questions/65191469/having-issues-setting-up-and-displaying-the-sprites-in-my-classes-subclasses-in