calling attributes from other classes error - python

I am working on a project for my CS class, I am near where I fell comfortable to call it complete, but I am getting this error:
exceptions.AttributeError: type object 'protaganist' has no attribute 'hearts'
also i am trying to make my protaganist class move up.... i have tried
if keys[pygame.K_SPACE]:
self.y += self.dy # (where dy was defined as 10 in init)
i dont know what else to try
lastly how i might set somthing with a random position of x with a set y position
it highlights here: ( it is calling another classe's attribute, but i dnt know why its causing error:
class coinandheartscore(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.font = pygame.font.SysFont("None", 50)
def update(self):
self.text = "hearts X: %d, Coins X: %d" % (protaganist.hearts, protaganist.coins)
self.image = self.font.render(self.text, 1, (255, 255, 0))
self.rect = self.image.get_rect()
here is my overall code:
import gameEngine
import pygame
import math
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mixer.init()
sndAtk = pygame.mixer.Sound("OOT_AdultLink_Attack1.wav")
sndWalk = pygame.mixer.Sound("OOT_Steps_Dirt1.wav")
sndPoof = pygame.mixer.Sound("OOT_Enemy_Poof1.wav")
sndWalk.set_volume(.1)
sndAtk.set_volume(.5)
sndPoof.set_volume(.9)
#sndRun = pygame.mixer.Sound("")
#goal is to create a game
#must have menu to start game
#menu should have a start and quit button.. start runs gaming operations and quit exits program
#sprites for character and enemies and bullets maybe, use one large image and simply move visibiliy
#this saves memory as 1 image is loaded instead of many
"""
protaganist is our hero sprite
should run left and right, jump left and right
and attack left and right...
I might add in the bow and jump attack
"""
class scrollinggrass(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("gamebackground.jpg")
self.rect = self.imageMaster.get_rect()
self.setPosition((400,247))
self.checkKeys()
self.dy = 3
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
self.forward(-6)
sndWalk.play()
if keys[pygame.K_a]:
self.forward(6)
sndWalk.play()
class coins(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.loadImages()
self.image = self.imageMaster
self.frame = -1
self.pause = 0
self.delay = 3
def loadImages(self):
self.coinImgList = []
for i in range(3):
nameImage = "linkimages/coins/greenrupee%d.png" % i
self.setImage(nameImage)
tempImage = self.imageMaster
transparentColor = tempImage.get_at((1,1))
tempImage.set_colorkey(transparentColor)
self.coinImgList.append(tempImage)
def update(self):
self.pause += .25
if self.pause >= self.delay:
self.pause = 0
self.frame += 1
if self.frame >= len(self.coinImgList):
self.frame = 0
self.image = self.coinImgList[self.frame]
class hearts(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("heart.png")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.setPosition((550 , 30))
class badguy(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("badguy1.png")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.rect = self.imageMaster.get_rect()
# self.CONTINUE = 4
self.boundAction = self.CONTINUE
self.health = 2
self.DEAD = 1
self.state = 0
self.setPosition((200,375))
self.checkKeys()
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
self.forward(-3)
if keys[pygame.K_a]:
self.forward(3)
def reset(self):
self.setPosition((1000, 375))
self.health = 2
# def update(self):
class protaganist(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.imageList = []
self.pause = 0
self.delay = 3
self.rect = self.imageMaster.get_rect()
self.STANDING = 0
self.RIGHT = 0
self.LEFT = 1
self.direction = self.RIGHT
self.RUNNING = 1
self.ATTACKING = 2
self.JUMPING = 3
self.DEAD = 10
self.frame = -1
self.state = self.STANDING
self.coins = 0
self.hearts = 1
self.heartPts = self.hearts * 3
self.stats()
self.dy = 20
self.loadImages()
self.image = self.imageMaster
self.checkKeys()
def stats(self):
#sets it up so each heart is essentially 3 hit points
if self.heartPts >= 3:
self.hearts = 1
elif self.heartPts >= 6:
self.hearts = 2
elif self.heartPts == 9:
self.hearts = 3
elif self.heartPts > 9:
self.heartPts = 9
# changes state to dead if hp == 0
if self.heartPts == 0:
self.state = DEAD
def loadImages(self):
self.setImage("heroSTANDINGLeft.gif")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.imageSTANDINGLeft = self.imageMaster
self.setImage("heroSTANDING.gif")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.imageSTANDING = self.imageMaster
self.heroATKList = []
self.heroATKleft = []
self.heroDEAD = []
self.heroRUNList = []
self.heroRUNLeftList = []
for i in range(4):
nameImage = "linkimages/DEAD/heroDEAD%d.gif" % i
self.setImage(nameImage)
tempImage = self.imageMaster
transparentColor = tempImage.get_at((1,1))
tempImage.set_colorkey(transparentColor)
self.heroDEAD.append(tempImage)
for i in range(5):
nameImage = "linkimages/runningRIGHT/heroRUN%d.gif" % i
self.setImage(nameImage)
tempImage = self.imageMaster
transparentColor = tempImage.get_at((1,1))
tempImage.set_colorkey(transparentColor)
self.heroRUNList.append(tempImage)
for i in range(5):
nameImage = "linkimages/runningLEFT/heroRUN%d.gif" % i
self.setImage(nameImage)
tempImage = self.imageMaster
transparentColor = tempImage.get_at((1,1))
tempImage.set_colorkey(transparentColor)
self.heroRUNLeftList.append(tempImage)
for i in range(4):
nameImage = "linkimages/ATTACKING/heroATTACKING%d.gif" % i
self.setImage(nameImage)
tempImage = self.imageMaster
transparentColor = tempImage.get_at((1,1))
tempImage.set_colorkey(transparentColor)
self.heroATKList.append(tempImage)
for i in range(4):
nameImage = "linkimages/ATTACKING/heroATTACKINGLeft%d.gif" % i
self.setImage(nameImage)
tempImage = self.imageMaster
transparentColor = tempImage.get_at((1,1))
tempImage.set_colorkey(transparentColor)
self.heroATKleft.append(tempImage)
def update(self):
self.rect = self.imageMaster.get_rect()
self.rect.x = 275
self.rect.y = 350
if self.state == self.STANDING:
if self.direction == self.RIGHT:
self.image = self.imageSTANDING
self.setPosition((200,200))
elif self.direction == self.LEFT:
self.image = self.imageSTANDINGLeft
if self.state == self.RUNNING:
if self.direction == self.RIGHT:
self.frame += 1
if self.frame >= len(self.heroRUNList):
self.frame = 0
self.image = self.heroRUNList[self.frame]
elif self.direction == self.LEFT:
self.frame += 1
if self.frame >= len(self.heroRUNLeftList):
self.frame = 0
self.image = self.heroRUNLeftList[self.frame]
if self.state == self.DEAD:
self.frame += 1
if self.frame >= len(self.heroDEAD):
self.frame = 0
self.image = self.heroDEAD[self.frame]
self.pause += .5
if self.pause >= self.delay:
self.pause = 0
if self.state == self.ATTACKING:
if self.direction == self.RIGHT:
self.frame += 1
sndAtk.play()
if self.frame >= len(self.heroATKList):
self.frame = 0
self.image = self.heroATKList[self.frame]
elif self.direction == self.LEFT:
self.frame += 1
sndAtk.play()
if self.frame >= len(self.heroATKleft):
self.frame = 0
self.image = self.heroATKleft[self.frame]
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
self.direction = self.RIGHT
self.state = self.RUNNING
self.x += self.dx
else:
self.state = self.STANDING
if keys[pygame.K_a]:
self.state = self.RUNNING
self.direction = self.LEFT
if keys[pygame.K_g]:
self.state = self.ATTACKING
#sndAtk.play()
if keys[pygame.K_SPACE]:
self.addDY(3)
if self.state == self.DEAD:
self.image = self.deadImgList[0]
self.frame += 1
self.image = self.deadImgList[self.frame]
#self.image = self.image.get_rect()
#self.rect.center = (320, 240)
class coinandheartscore(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.font = pygame.font.SysFont("None", 50)
def update(self):
self.text = "hearts X: %d, Coins X: %d" % (protaganist.hearts, protaganist.coins)
self.image = self.font.render(self.text, 1, (255, 255, 0))
self.rect = self.image.get_rect()
class game(gameEngine.Scene):
def __init__ (self):
gameEngine.Scene.__init__(self)
pygame.display.set_caption("Link's Mediocre Adventure")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
coin = coins(self)
pro = protaganist(self)
baddy = badguy(self)
baddy1 = badguy(self)
heart = hearts(self)
grass = scrollinggrass(self)
score = coinandheartscore(self)
goodlySprites = self.makeSpriteGroup((grass, coin, pro, heart))
baddySprites = self.makeSpriteGroup((baddy, baddy1))
cnhrtSprites = self.makeSpriteGroup((score))
self.addGroup((cnhrtSprites))
self.addGroup((goodlySprites))
self.addGroup((baddySprites))
clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
baddy1.health -= 1
if baddy.health == 0:
sndPoof.play()
baddy.reset()
elif baddy1.health == 0:
sndPoof.play()
baddy1.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesGroup(baddySprites):
pro.heartPts -= 1
baddy.checkKeys()
grass.checkKeys()
pro.checkKeys()
pro.loadImages()
goodlySprites.clear(screen, background)
baddySprites.clear(screen, background)
cnhrtSprites.clear(screen, background)
goodlySprites.update()
baddySprites.update()
cnhrtSprites.update()
goodlySprites.draw(screen)
baddySprites.draw(screen)
cnhrtSprites.draw(screen)
pygame.display.flip()
def main():
game.start()
if __name__ == "__main__":
game()

At least one of the problems is in your naming of your classes: the PEP8 convention calls for capitalized class names (Protagonist) and lower-case instances of them (pro). You've called your class protagonist and instantiated as pro. It looks like you're referring to protagonist.hearts (no such class attribute exists) when you mean pro.hearts (attribute of an instance of the protagonist class.

As a general rule, if you have two questions (even about the same piece of code), you should post two separate questions on Stack Overflow. That said...
1: Moving Up
The code you quoted in your question looks OK. Unfortunately, it's nothing like the code you're actually running in protaganist.checkKeys:
if keys[pygame.K_SPACE]:
self.addDY(3)
There is no protaganist.addDY method, so trying to call it should raise an exception. The hard-wired argument to the non-existent method is 3, not 10. Finally (to correct your comment), protaganist.__init__ sets protaganist.dy to 20, not 10.
2: AttributeError
Your protaganist class has no hearts attribute, which is almost exactly what the error message said. (Note that it's typically spelled with an "o": protagonist.)
Instances of your protaganist class do have a hearts attribute, but you can't access that by going through the class name. (How would the Python interpreter know which of possibly-many protaganist instances you meant?)
To get a particular protaganist object's hearts value, you'll need a reference to that specific instance:
class coinandheartscore(gameEngine.SuperSprite):
# New protagonist parameter is required.
def __init__(self, scene, protagonist):
gameEngine.SuperSprite.__init__(self, scene)
self.font = pygame.font.SysFont("None", 50)
# Save it for later use.
self.protagonist = protagonist
def update(self):
# self.protagonist refers to _one_ specific
# protaganist instance, which _does_ have
# hearts and coins attributes.
self.text = "hearts X: %d, Coins X: %d" % (
self.protagonist.hearts,
self.protagonist.coins,
)
# Remainder unchanged...
Later, in the game.__init__ method, you'll have to provide the new argument when you instantiate coinandheartscore:
# New self.pro argument.
score = coinandheartscore(self, self.pro)
3: PEP-8
Finally, I second xnx's comments about naming conventions... The strange names made this much harder to read than it had to be.
Although xnx didn't link to PEP-8 in his answer, it is literally the first Google result. He or she was specifically talking about the section on Naming Conventions.
4: Freebies
protaganist.checkKeys: d and a are not the inverses you would expect. d adds self.dx ( which does not exist) to self.x, while a adds nothing.
protaganist.stats sets self.state to DEAD, which does not exist. You probably meant self.DEAD.
In general, test more and code less. The more you write between tests, the more bugs you will have to track down, and the more places they can be hiding.

Related

Pygame animation list index continuously goes out of range

I followed a clear code tutorial to make a platformer and ended up finishing it, however one thing always continously messed up. That being the animation, at times the game just would not run and would only run in debug mode due to the animation list being out of index which makes no sense to me since every item in the list is accounted for. Below is my player.py which has all of the functions and such that the video mentions. If more information is needed I will gladly provide it. (the animation part is specifically erroring at self.image = self.animations['idle'][self.frame_index])
import pygame
from support import import_folder
class Player(pygame.sprite.Sprite):
def __init__(self,pos,surface,create_jump_particles):
super().__init__()
self.import_character_assets()
self.frame_index = 0
self.animation_speed = 0.15
self.image = self.animations['idle'][self.frame_index]
self.rect = self.image.get_rect(topleft = pos)
# dust particles
self.import_dust_run_particles()
self.dust_frame_index = 0
self.dust_animation_speed = 0.15
self.display_surface = surface
self.create_jump_particles = create_jump_particles
# player movement
self.direction = pygame.math.Vector2(0,0)
self.speed = 8
self.gravity = 0.8
self.jump_speed = -16
# player status
self.status = 'idle'
self.facing_right = True
self.on_ground = False
self.on_ceiling = False
self.on_left = False
self.on_right = False
def import_character_assets(self):
character_path = '../graphics/character/'
self.animations = {'idle':[],'run':[],'jump':[],'fall':[]}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
def import_dust_run_particles(self):
self.dust_run_particles = import_folder('../graphics/character/dust_particles/run')
def animate(self):
animation = self.animations[self.status]
# loop over frame index
self.frame_index += self.animation_speed
if self.frame_index >= len(animation):
self.frame_index = 0
image = animation[int(self.frame_index)]
if self.facing_right:
self.image = image
else:
flipped_image = pygame.transform.flip(image,True,False)
self.image = flipped_image
# set the rect
if self.on_ground and self.on_right:
self.rect = self.image.get_rect(bottomright = self.rect.bottomright)
elif self.on_ground and self.on_left:
self.rect = self.image.get_rect(bottomleft = self.rect.bottomleft)
elif self.on_ground:
self.rect = self.image.get_rect(midbottom = self.rect.midbottom)
elif self.on_ceiling and self.on_right:
self.rect = self.image.get_rect(topright = self.rect.topright)
elif self.on_ceiling and self.on_left:
self.rect = self.image.get_rect(topleft = self.rect.topleft)
elif self.on_ceiling:
self.rect = self.image.get_rect(midtop = self.rect.midtop)
def run_dust_animation(self):
if self.status == 'run' and self.on_ground:
self.dust_frame_index += self.dust_animation_speed
if self.dust_frame_index >= len(self.dust_run_particles):
self.dust_frame_index = 0
dust_particle = self.dust_run_particles[int(self.dust_frame_index)]
if self.facing_right:
pos = self.rect.bottomleft - pygame.math.Vector2(6,10)
self.display_surface.blit(dust_particle,pos)
else:
pos = self.rect.bottomright - pygame.math.Vector2(6,10)
flipped_dust_particle = pygame.transform.flip(dust_particle,True,False)
self.display_surface.blit(flipped_dust_particle,pos)
def get_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.direction.x = 1
self.facing_right = True
elif keys[pygame.K_LEFT]:
self.direction.x = -1
self.facing_right = False
else:
self.direction.x = 0
if keys[pygame.K_SPACE] and self.on_ground:
self.jump()
self.create_jump_particles(self.rect.midbottom)
def get_status(self):
if self.direction.y < 0:
self.status = 'jump'
elif self.direction.y > 1:
self.status = 'fall'
else:
if self.direction.x != 0:
self.status = 'run'
else:
self.status = 'idle'
def apply_gravity(self):
self.direction.y += self.gravity
self.rect.y += self.direction.y
def jump(self):
self.direction.y = self.jump_speed
def update(self):
self.get_input()
self.get_status()
self.animate()
self.run_dust_animation()
I've tried everything, I've even gone to his website and downloaded his prototype and tried adding my own files to it and it still returns the same error. Whats supposed to be happening is that my character does an animation depending on which state they are in.
So it's likely that the code is not loading any of the animation frames for the 'idle' character animation.
The initialisation code first loads all the assets:
class Player(pygame.sprite.Sprite):
def __init__(self,pos,surface,create_jump_particles):
super().__init__()
self.import_character_assets()
Where self.import_character_assets() is:
def import_character_assets(self):
character_path = '../graphics/character/'
self.animations = {'idle':[],'run':[],'jump':[],'fall':[]}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
Which is iterating through 'idle', 'run', 'jump', 'fall', loading images from '../graphics/character/idle', '../graphics/character/run', etc. into a dictionary-of-lists.
We don't have the code for import_folder(), but it's obviously intended to load some set of images.
After importing, the self.animations dictionary is expected to have at least a single image in self.animations['idle']. This is because the __init__() is defaulting to 'idle', and index 0:
self.frame_index = 0
self.animation_speed = 0.15
self.image = self.animations['idle'][self.frame_index] # <<-- HERE, 0
So we can conclude that because we know, self.animations['idle'] is out of bounds, even on [0], no images were loaded.
Check that there are images available for this. Or maybe the filenames are not what's expected (e.g.: .jpg, not .png), or some other file-system error (e.g.: read only, zero byte files, ... ). Maybe the load-path is incorrect?
It might be a good idea to check the load, and write an error:
def import_character_assets(self):
character_path = '../graphics/character/'
self.animations = {'idle':[],'run':[],'jump':[],'fall':[]}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
# check we loaded something
if ( len( self.animations[animation] ) == 0 ):
print( "Failed to load any animations for \"" + animation + "\" type" )

Snake Game: How to make the snake's body gets trimmed when its head touches its body?

I have a assignment that ask me to make a snake game with Python. The assignment asks me to make the snake's body gets trimmed when its head touches the body.
The snake body is a queue, so here is the code for forming a queue:
The snake's head is defined as the rear node and the snake's tail is defined as the front node, as shown in the picture below.
#The code of forming a queue, it cannot be modified
class Node:
def __init__(self, x, y):
self.x = x
self.y = y
self.pre = None
self.next = None
class Queue:
def __init__(self):
self.front = None
self.rear = None
def len(self):
length = 0
cur = self.front
while cur:
cur = cur.next
length += 1
return length
def enQueue(self, x, y):
new = Node(x, y)
if self.len() == 0:
self.front = new
self.rear = new
else:
new.pre = self.rear
self.rear.next = new
self.rear = new
def deQueue(self):
if self.len() <= 1:
self.front = None
self.rear = None
else:
self.front = self.front.next
self.front.pre = None
def reverse(self):
cur = self.front
self.rear, self.front = self.front , self.rear
while cur:
cur.next, cur.pre = cur.pre, cur.next
cur = cur.pre
def printQueue(self):
cur = self.front
print("front", end=" ")
while cur:
print(f"[{cur.x}, {cur.y}]", end=" ")
cur = cur.next
print("rear")
And this is the main code for the game:
import pygame, sys, time, random
from pygame.locals import *
from pathlib import Path, os
from coor_queue import Node, Queue
from item_stack import Stack
class SnakeGame:
def __init__(self):
self.g = 30 #The width of each grid
self.dir = "right" #Initial Direction
self.snake = Queue() #Queue of the snake
for i in range(9):
self.snake.enQueue(i*self.g,9*self.g)
self.init_params()
self.init_pygame()
self.init_objects()
self.init_images()
#The code above cannot be modified
# =========================== Movement ===========================
def move(self):
headx = self.snake.rear.x
heady = self.snake.rear.y
if self.dir == "down":
heady += self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "up":
heady -= self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "left":
headx -= self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "right":
headx += self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
# =========================== Add Tails ===========================
def add_tail(self):
tailx = self.snake.front.x
taily = self.snake.front.y
newtail = Node(tailx, taily)
newtail.next = self.snake.front
self.snake.front.pre = newtail
self.snake.front = newtail
# =========================== Trim the snake's body ===========================
def eat_body(self):
# If the snake's head touches a grid (node) of the body
# Then the part from original tail to the grid which the snake's head touches
# Hint: Use self.snake
# ============The code below cannot be modified======================
def main(self):
while True:
self.keyboard_input()
self.check_input_valid()
self.move()
self.eat()
self.display()
if self.is_dead():
self.game_over()
self.fps.tick(8 + self.speed//5)
def keyboard_input(self): # Keyboard input
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == ord("d") or event.key == K_RIGHT: self.input_dir = "right"
if event.key == ord("a") or event.key == K_LEFT: self.input_dir = "left"
if event.key == ord("w") or event.key == K_UP: self.input_dir = "up"
if event.key == ord("s") or event.key == K_DOWN: self.input_dir = "down"
if event.key == K_ESCAPE: pygame.event.post(pygame.event.Event(QUIT))
def check_input_valid(self): #If the input direction is the opposite of the original direction, the input will be invalid
if (self.input_dir == "right" and self.dir != "left") or (self.input_dir == "left" and self.dir != "right") or \
(self.input_dir == "up" and self.dir != "down") or (self.input_dir == "down" and self.dir != "up"):
self.dir = self.input_dir
def eat(self): #Eat body or food
self.eat_food()
self.eat_body()
def display(self):
#Background
self.blit_map()
#Snake body
cur = self.snake.rear.pre
while cur:
self.blit_image("body", cur.x, cur.y, self.g, self.g)
cur = cur.pre
#Snake head
if self.snake.rear.x < self.width:
self.blit_image("head", self.snake.rear.x, self.snake.rear.y, self.g, self.g)
#Food
self.blit_image(self.food, self.foodPos.x, self.foodPos.y, self.g, self.g)
#Status Bar
self.blit_status_bar()
pygame.display.flip()
def is_dead(self):
return (self.snake.rear.x == self.width or self.snake.rear.x < 0) or \
(self.snake.rear.y == self.height or self.snake.rear.y < 0)
def game_over(self): # Game Over
self.play_theme("game_over1")
time.sleep(3)
pygame.quit()
sys.exit()
def init_params(self): #Basic parameters
self.screen_width = 1140
self.screen_height = 540
self.width = 960
self.height = 540
self.speed = 0
self.prob = 25
self.score = 0
self.spf = 10
self.satiety = 0
self.input_dir = None
def init_pygame(self): #Initialize pygame
pygame.init()
self.fps = pygame.time.Clock() #
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height)) #
pygame.display.set_caption("Snake Game") #
pygame.mixer.init()
def init_objects(self):
self.food_list = [food.split(".")[0] for food in os.listdir(Path("src/image/food"))]
self.food = None
self.select_food()
self.foodPos = Node(self.width//2, self.height//2)
def init_images(self):
self.img_StatusBar = pygame.image.load(Path("src/image/other/StatusBar.jpg")).convert_alpha()
self.img_head = pygame.image.load(Path("src/image/snake/SnakeHead.jpg")).convert_alpha()
self.img_body = pygame.image.load(Path("src/image/snake/SnakeBody.jpg")).convert_alpha()
self.img_map = pygame.image.load(Path("src/image/map/Map1.jpg")).convert_alpha()
for food in self.food_list:
exec(f"self.img_{food} = pygame.image.load(Path('src/image/food/{food}.jpg')).convert_alpha()")
def eat_food(self):
if self.snake.rear.x == self.foodPos.x and self.snake.rear.y == self.foodPos.y:
self.satiety += 1
self.speed += 1
self.spf = 10 + ((self.satiety-1)//10)*10
self.score += self.spf
self.play_effect("eat_food")
x = random.randrange(1, self.width//self.g)
y = random.randrange(1, self.height//self.g)
self.foodPos.x = x*self.g
self.foodPos.y = y*self.g
self.select_food()
self.add_tail()
def select_food(self):
while True:
next_food = self.food_list[random.randrange(len(self.food_list))]
if next_food != self.food:
self.food = next_food
break
def play_theme(self, theme):
pygame.mixer.music.load(Path(f"src/sound/theme/{theme}.mp3"))
pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play()
def play_effect(self, effect):
sound = pygame.mixer.Sound(Path(f"src/sound/effect/{effect}.mp3"))
sound.set_volume(0.7)
sound.play()
def blit_image(self, name, coor_x = 0, coor_y = 0, size_x = 0, size_y = 0):
exec(f"self.img_{name} = pygame.transform.scale(self.img_{name}, size=(size_x, size_y))")
exec(f"self.screen.blit(self.img_{name}, (coor_x, coor_y))")
def blit_rect(self, data, coor_x = 0, coor_y = 0, font_size = 80, color = (0, 0, 0)):
self.Font = pygame.font.SysFont("", font_size)
self.Surf = self.Font.render(str(data), True, color)
self.Rect = self.Surf.get_rect()
self.Rect.center = (coor_x, coor_y)
self.screen.blit(self.Surf, self.Rect)
def blit_map(self):
self.blit_image("map", 0, 0, self.width, self.height)
def blit_status_bar(self):
self.blit_image("StatusBar", 960, 0, 180, self.screen_height)
self.blit_rect(self.score, 35*self.g, 1.87*self.g, font_size = 50, color = (238, 0, 0))
self.blit_rect(int(8 + self.speed)-7, 35*self.g, 4.13*self.g, font_size = 50, color = (178, 58, 238))
self.blit_rect(self.snake.len(), 35*self.g, 6.28*self.g, font_size = 50, color = (50, 205, 50))
# ==================================================================
game = SnakeGame()
game.main()
The picture below explains how trimming snake body works, the Python logo is the head.
I understand that the condition of trimming the body is when the head reaches the same grid as a part of the body. But I have no idea how to write this condition because I'm not sure if there is a way to get the coordinate from the nodes between front node and rear node.
Some further explanation is appreciated, thanks in advance.
Use a loop to compare the head of the queue against all elements of the body. The loop is similar to the loop in the print method. Return true if the position of the head is equal to a body position, else return false:
def isHeadEqualAnyBodyItem(snake):
head = snake.front
if not head:
return False
body = head.next
while body:
if head.x == body.x and head.y == body.y
return True
body = body.next
return False

Putting the scrolling camera in a mini-window in pygame

I'm trying to create a game where the action is shown in a little box within the main screen object, freeing up the surrounding space for text and menus and what-not. Since the map is larger than the allotted window, I coded a basic "camera" that follows the player around. It mostly works, but I'm having trouble "trimming off" the area outside of this window.
Here's the relevant bits of code (EDITED to provide Working Example):
import pygame, os, sys
from pygame.locals import *
pygame.init()
RIGHT = 'RIGHT'
LEFT = 'LEFT'
UP = 'UP'
DOWN = 'DOWN'
class Camera():
def __init__(self, screen, x_ratio = 1, y_ratio = 1, x_offset = 0, y_offset = 0):
self.screen = screen.copy()
self.rec = self.screen.get_rect()
self.rec.width *= x_ratio
self.rec.height *= y_ratio
self.x_offset = x_offset
self.y_offset = y_offset
def get_pos(self):
return (self.x_offset - self.rec.x, self.y_offset - self.rec.y)
def get_window(self):
w = pygame.Rect(self.rec)
w.topleft = (0 - self.rec.x, 0 - self.rec.y)
return w
def move(self, x, y):
"""Move camera into new position"""
self.rec.x = x
self.rec.y = y
def track(self, obj):
while obj.rec.left < self.rec.left:
self.rec.x -= 1
while obj.rec.right > self.rec.right:
self.rec.x += 1
while obj.rec.top < self.rec.top:
self.rec.y -= 1
while obj.rec.bottom > self.rec.bottom:
self.rec.y += 1
class Map:
def __init__(self, width, height):
self.width = width
self.height = height
self.rec = pygame.Rect(0,0,self.width,self.height)
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
class Obj:
def __init__(self, char, x = 0, y = 0, width = 0, height = 0):
self.width = width
self.height = height
self.rec = pygame.Rect(x, y, width, height)
self.cur_map = None
self.timers = {}
#Dummying in chars for sprites
self.char = char
self.x_dir = 1
self.y_dir = 1
self.speed = 1
self.moving = False
def move(self):
if self.x_dir != 0 or self.y_dir != 0:
new_x = self.rec.x + (self.x_dir*self.speed)
new_y = self.rec.y + (self.y_dir*self.speed)
new_rec = pygame.Rect(new_x, new_y, self.width, self.height)
#Keep movement within bounds of map
while new_rec.left < self.cur_map.rec.left:
new_rec.x += 1
while new_rec.right > self.cur_map.rec.right:
new_rec.x -= 1
while new_rec.top < self.cur_map.rec.top:
new_rec.y += 1
while new_rec.bottom > self.cur_map.rec.bottom:
new_rec.y -= 1
self.rec = new_rec
def set_dir(self, d):
self.x_dir = 0
self.y_dir = 0
if d == LEFT:
self.x_dir = -1
elif d == RIGHT:
self.x_dir = 1
elif d == UP:
self.y_dir = -1
elif d == DOWN:
self.y_dir = 1
def set_moving(self, val = True):
self.moving = val
class Game:
def __init__(self):
self.screen_size = (800, 600)
self.screen = pygame.display.set_mode(self.screen_size)
self.map_screen = self.screen.copy()
self.title = 'RPG'
pygame.display.set_caption(self.title)
self.camera = Camera(self.screen, 0.75, 0.75)#, 10, 75)
self.fps = 80
self.clock = pygame.time.Clock()
self.debug = False
self.bg_color = (255,255,255)
self.text_size = 18
self.text_font = 'Arial'
self.text_style = pygame.font.SysFont(self.text_font, self.text_size)
self.key_binds = {LEFT : [K_LEFT, K_a], RIGHT : [K_RIGHT, K_d], UP : [K_UP, K_w], DOWN : [K_DOWN, K_s],
'interact' : [K_RETURN, K_z], 'inventory' : [K_i, K_SPACE], 'quit' : [K_ESCAPE]}
self.player = Obj('p', 0, 0, 10, self.text_size)
def draw(self, obj):
char = obj.char
self.draw_text(char, obj.rec.x, obj.rec.y, screen = self.map_screen)
def draw_text(self, text, x, y, color = (0,0,0), screen = None):
textobj = self.text_style.render(text, 1, color)
textrect = textobj.get_rect()
textrect.x = x
textrect.y = y
if screen == None:
"""Use default screen"""
self.screen.blit(textobj, textrect)
else:
screen.blit(textobj, textrect)
def play(self):
done = False
cur_map = Map(800, 800)
self.map_screen = pygame.Surface((cur_map.width, cur_map.height))
self.map_screen.fill(self.bg_color)
bg = pygame.Surface((cur_map.width, cur_map.height))
cur_map.draw(bg)
self.player.cur_map = cur_map
while not done:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key in self.key_binds[LEFT]:
self.player.set_dir(LEFT)
self.player.set_moving()
elif event.key in self.key_binds[RIGHT]:
self.player.set_dir(RIGHT)
self.player.set_moving()
elif event.key in self.key_binds[UP]:
self.player.set_dir(UP)
self.player.set_moving()
elif event.key in self.key_binds[DOWN]:
self.player.set_dir(DOWN)
self.player.set_moving()
elif event.type == KEYUP:
self.player.set_moving(False)
if self.player.moving:
self.player.move()
self.camera.track(self.player)
self.clock.tick()
self.screen.fill(self.bg_color)
self.map_screen.blit(bg, (0,0))
self.draw(self.player)
pygame.draw.rect(self.map_screen, (0,0,0), self.camera.rec, 1)
#self.screen.blit(self.map_screen, (0,0), [0 - self.camera.rec.x, 0 - self.camera.rec.y, self.camera.rec.width, self.camera.rec.height])
self.screen.blit(self.map_screen, self.camera.get_pos(), self.camera.get_window())
pygame.display.flip()
game = Game()
game.play()
Moving the player past past the bounds of the camera's window causes the window to roll up completely and disappear. I tried adjusting the blitting coordinates, as advised earlier, but it seems to only change the direction in which the window rolls up.
From your updated code, the blitting coordinates for self.screen.blit(...) are still changing: self.camera.get_window() changes value because rec.x and rec.y are values referring to the player position within the map. Hence you should define a constant minimap coordinate, this should be the same as the camera offset.
self.screen.blit(self.map_screen, (self.camera.x_offset,self.camera.y_offset), (*self.camera.get_pos(), self.camera.rec.width, self.camera.rec.height))
Change the Camera().get_pos() to:
def get_pos(self):
return (self.rec.x, self.rec.y)
I believe I only changed the self.screen.blit(...) and stopped using or rewrote your Camera functions as you're confusing yourself with all the rec variables.
To illustrate it working amend the Map().draw(screen) to:
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
pygame.draw.circle(screen, (255, 255, 255), (50, 50), 20, 2)
One tip as well don't draw the entire map at each loop, just the part that will be visible.

How can i keep updating variables to class from outside of it?

I'm New to python/pygame classes and I'm making a game.
So my problem is that i have a class that draws spaceships to screen but i would need somehow add spacemove_x,spacemove_y to their x and y.
These spacemove_x,spacemove_y are defined when user moves he's own spaceship
(I use spacemove_x,spacemove_y for updating places for other stuff too like stars because this game is from top down view so when i move my spacecraft other things should get closer/farther away depending what direction my character is going)
So spacemove_x,spacemove_y is always updating it self but how can I give this info to class?
CODE:
import pygame
import random
class BaseClass(pygame.sprite.Sprite):
allsprites = pygame.sprite.Group()
def __init__(self, x, y, width, height,spacemove_x,spacemove_y):
pygame.sprite.Sprite.__init__(self)
BaseClass.allsprites.add(self)
self.shipDefaultUp = pygame.image.load("textures/ships/shipDefault/shipUP.png")
self.shipDefaultRight = pygame.image.load("textures/ships/shipDefault/shipRIGHT.png")
self.shipDefaultDown = pygame.image.load("textures/ships/shipDefault/shipDOWN.png")
self.shipDefaultLeft = pygame.image.load("textures/ships/shipDefault/shipLEFT.png")
self.shipCargo1Up = pygame.image.load("textures/ships/shipCargo1/shipCargo1UP.png")
self.shipCargo1Right = pygame.image.load("textures/ships/shipCargo1/shipCargo1RIGHT.png")
self.shipCargo1Down = pygame.image.load("textures/ships/shipCargo1/shipCargo1DOWN.png")
self.shipCargo1Left = pygame.image.load("textures/ships/shipCargo1/shipCargo1LEFT.png")
self.shipCargo2Up = pygame.image.load("textures/ships/shipCargo2/shipCargo2UP.png")
self.shipCargo2Right = pygame.image.load("textures/ships/shipCargo2/shipCargo2RIGHT.png")
self.shipCargo2Down = pygame.image.load("textures/ships/shipCargo2/shipCargo2DOWN.png")
self.shipCargo2Left = pygame.image.load("textures/ships/shipCargo2/shipCargo2LEFT.png")
self.image = pygame.image.load("textures/ships/shipDefault/shipUP.png")
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.width = width
self.height = height
self.spacemove_x = spacemove_x
self.spacemove_y = spacemove_y
#self.dirrection = random.randrange(1,5)
self.dirrection = 1
self.timer = random.randrange(10,50)
self.speed = random.randrange(2,10)
self.shiptype = 3#random.randrange(1,3)
#shiptypes#
#1 = shipDefault
#2 = shipCargo1
#3 = shipCargo2
self.move1 = ((1),(2),(4))
self.move2 = ((1),(2),(3))
self.move3 = ((2),(3),(4))
self.move4 = ((1),(3),(4))
class spacecraftBOT(BaseClass):
ships = pygame.sprite.Group()
def __init__(self, x, y, width, height,spacemove_x,spacemove_y):
BaseClass.__init__(self, x, y, width, height,spacemove_x,spacemove_y)
spacecraftBOT.ships.add(self)
def motion(self):
#1 = UP
#2 = RIGHT
#3 = DOWN
#4 = LEFT
self.timer -=1
if self.dirrection == 1: ##############SHIP UP
self.rect.y -=self.speed
self.rect.y -=self.spacemove_x
if self.shiptype == 1:
self.image = self.shipDefaultUp
if self.shiptype == 2:
self.image = self.shipCargo1Up
if self.dirrection == 2:################SHIP RIGHT
self.rect.x +=self.speed
self.rect.x +=self.spacemove_x
if self.shiptype == 1:
self.image = self.shipDefaultRight
if self.shiptype == 2:
self.image = self.shipCargo1Right
if self.shiptype == 3:
self.image = self.shipCargo2Right
if self.dirrection == 3: ##############SHIP DOWN
self.rect.y +=self.speed
self.rect.y +=self.spacemove_y
if self.shiptype == 1:
self.image = self.shipDefaultDown
if self.shiptype == 2:
self.image = self.shipCargo1Down
if self.shiptype == 3:
self.image = self.shipCargo2Down
if self.dirrection == 4: ################SHIP LEFT
self.rect.x -=self.speed
self.rect.x -=self.spacemove_x
if self.shiptype == 1:
self.image = self.shipDefaultLeft
if self.shiptype == 2:
self.image = self.shipCargo1Left
if self.shiptype == 3:
self.image = self.shipCargo2Left
if self.dirrection == 5:
print("loitter")
if self.timer < 0:
self.dirrection = random.randrange(1,6)
self.timer = random.randrange(10,50)
This is how I create spacecraft to my game:
spacecraft1 = spacecraftBOT(500,500,100,34,spacemove_x,spacemove_y)
So how can I give this class updated spacemove_x,spacemove_y?
So how can i give this class updated spacemove_x,spacemove_y?
Just assigning them should work:
spacecraft1.spacemove_x = 100
spacecraft1.spacemove_y = 200
BTW, spacecraft1 is an instance of class spacecraftBOT, not a class.
You access instance members with the dot operator. In fact, you already do this with self many times. The name self is not special; it is just a variable which refers to a class instance. Similarly, spacecraft1 is a variable which refers to a class instance. This means you can use the exact same notation:
spacecraft1.spacemove_x = 100
or
spacecraft1.spacemove_x += 50
Or basically anything you want.
Alternatively, you can define a move() method:
class spacecraftBOT(BaseClass):
# ... all of the code you already have ...
def move(self, x, y):
self.spacemove_x = x
self.spacemove_y = y
Now you can call the method with something like
spaceship1.move(100, 50)
Notice that this jumps directly to the given location. If you want to increment the x and y coordinates instead, you just need to implement the correct logic. (Hint: Use += instead of =.)

Why isn't my pygame display displaying anything?

I am working on a program that evolves creatures over time using a genetic algorithm. However, for some reason, my pygame display stopped working and I have absolutely no idea why. When I run the program, the window opens but then it just sits on a black screen. I have tested to see where the program gets to and about 38 creatures die then nothing happens. However, these creatures should be displaying before their deaths also, but they aren't.Any help would be wonderful! Thank you for all your time!
Here's my code:
import numpy as np
import pygame
import random
#Initializes Pygame & Creates Window
pygame.init()
backgroundColor = (255, 255, 255)
screenSize = (800, 600)
screen = pygame.display.set_mode(screenSize)
pygame.display.set_caption("Genetic Algorithm")
screen.fill(backgroundColor)
clock = pygame.time.Clock()
#Loads Images & Rectangles
creaturePNG = pygame.image.load("Creature.png").convert_alpha()
foodPNG = pygame.image.load("Food.png").convert_alpha()
#Establishes Size Of Population
creatureCount = 40
deadCreatures = []
numGenerations = 10
#Generates Random 12 Digit DNA For First Generation
def initialDNA():
while True:
randomDNA = ""
total = 0
for i in range(12):
digit = random.randint(1, 9)
total += digit
digit = str(digit)
randomDNA = randomDNA + digit
if total <= 60:
break
return randomDNA
def reproduce(deadCreatureList, creatureCount):
reproducingCreatures = deadCreatureList[0.5*creatureCount:creatureCount]
for i in range(0.25*creatureCount):
creature1 = reproducingCreatures[0]
del reproducingCreatures[0]
creature2 = reproducingCreatures[0]
del reproducingCreatures[0]
DNA1 = str(creature1.DNA)
DNA2 = str(creature2.DNA)
crosspoint = random.randint(0, 12)
newDNA1 = int(DNA1[0:crosspoint] + DNA2[crosspoint:])
newDNA2 = int(DNA2[0:crosspoint] + DNA1[crosspoint:])
return newDNA1, newDNA2
#Creates Creatures From DNA
class Creature:
def __init__(self, DNA, image):
self.DNA = DNA
self.speed = (int(self.DNA[0:2])/100) + 1
self.strength = int(DNA[2:4])/10
self.foodCap = int(DNA[4:6])
self.maxHealth = int(DNA[6:8])
self.health = self.maxHealth
self.regeneration = int(DNA[8:10])/10
self.turnProbability = int(DNA[10:12])
self.currentFood = self.foodCap
self.image = image
self.rect = self.image.get_rect()
self.directions = [-1, 1]
self.directionX = random.choice(self.directions)
self.directionY = random.choice(self.directions)
self.isAlive = True
def spawn(self):
self.x = random.randint(25, 775)
self.y = random.randint(25, 575)
self.loc = (self.x, self.y)
self.rect = pygame.Rect(0, 0, 25, 25)
self.rect.center = self.loc
def move(self):
changeDirection = random.randint(0, 100)
if changeDirection < self.turnProbability:
self.directionX = random.choice(self.directions)
self.directionY = random.choice(self.directions)
self.x += self.directionX * self.speed
self.y += self.directionY * self.speed
if self.x > 775:
self.x = 775
elif self.x < 25:
self.x = 25
elif self.y > 575:
self.y = 575
elif self.y < 25:
self.y = 25
self.loc = (self.x, self.y)
self.rect.center = self.loc
def foodCollision(self, foodList):
foodRects = []
for i in range(25):
food = foodList[i]
foodRect = food.rect
foodRects.append(foodRect)
collision = self.rect.collidelist(foodRects)
if collision > 0:
self.currentFood += 20
if self.currentFood > self.foodCap:
self.currentFood = self.foodCap
def creatureCollision(self, creatureList, creatureCount, creatureNumber):
creatureRects = []
for i in range(creatureCount):
creature = creatures[i]
creatureRect = creature.rect
creatureRects.append(creatureRect)
collision = self.rect.collidelist(creatureRects)
creature = creatures[collision]
if collision >= 0:
if collision != creatureNumber:
if creature.health > 0:
self.health -= creature.strength
if self.health < 0:
self.health = 0
def starve(self):
if self.currentFood == 0:
self.health -= 1
def display(self):
screen.blit(self.image, self.loc)
#Creates Food Objects For Creatures To Eat
class Food:
def __init__(self, image):
self.image = image
self.rect = self.image.get_rect()
def spawn(self):
self.x = random.randint(25, 775)
self.y = random.randint(25, 575)
self.loc = (self.x, self.y)
self.rect = pygame.Rect(0, 0, 25, 25)
self.rect.center = self.loc
def creatureCollision(self, creatureList, creatureCount):
creatureRects = []
for i in range(creatureCount):
creature = creatures[i]
creatureRects.append(creature.rect)
collision = self.rect.collidelist(creatureRects)
creature = creatures[collision]
if collision >= 0:
if creature.health > 0:
self.spawn()
def display(self):
screen.blit(self.image, self.loc)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(backgroundColor)
for i in range(numGenerations):
if i == 0:
#Spawns Creatures Into World
creatures = []
for i in range(creatureCount):
DNA = initialDNA()
print (DNA)
creature = Creature(DNA, creaturePNG)
creature.spawn()
creatures.append(creature)
elif i > 0:
creatures = []
for i in range(0.5*creatureCount):
DNA1, DNA2 = reproduce(deadCreatures, creatureCount)
print (DNA1, DNA2)
creature1, creature2 = Creature(DNA1, creaturePNG), Creature(DNA2, creaturePNG)
creature.spawn()
creatures.append(creature)
#Spawns Food Into World
foodList = []
for i in range(25):
food = Food(foodPNG)
food.spawn()
foodList.append(food)
livingCreatures = True
while livingCreatures:
for i in range(25):
food = foodList[i]
food.creatureCollision(creatures, creatureCount)
food.display()
for i in range(creatureCount):
creature = creatures[i]
if creature.health > 0:
creature.move()
creature.foodCollision(foodList)
creature.creatureCollision(creatures, creatureCount, i)
creature.currentFood -= 0.5
if creature.currentFood < 0:
creature.currentFood = 0
if creature.currentFood > 0:
creature.health += creature.regeneration
if creature.health > creature.maxHealth:
creature.health = creature.maxHealth
creature.starve()
creature.display()
if creature.isAlive == True:
if creature.health == 0:
print ("DEATH")
deadCreatures.append(i)
creature.isAlive = False
if len(deadCreatures) == creatureCount:
livingCreatures = False
pygame.display.flip()
clock.tick(10)
I suspect the livingCreatures variable is never set to False, so the pygame.display.flip() never gets called--and you won't see anything on the screen at all until you flip the buffers. The fact that you're filling the screen with a color, but then still seeing black, is a dead giveaway for this sort of problem.
In the future, you should also try to reproduce the problem in a simpler example without domain-specific, irrelevant code.

Categories