Spritesheet trouble [duplicate] - python

This question already has answers here:
How do I create animated sprites using Sprite Sheets in Pygame?
(1 answer)
Invalid destination position for blit error, not seeing how
(1 answer)
Closed 2 years ago.
Alright, so I got some extremely simple code going on here, any comments/suggestions are recommended. Keep in mind, SIMPLE, in other words short and concise. thnx.
My problem is loading an image from a png file. So for example i got a couple of images in the file, and i want only one row to be loaded when the user presses for example, the right arrow key. Basically i have 4 rows, 2 or 3 columns 4 rows for each arrow key respectively
#
import pygame, time, random
pygame.init()
HEIGHT = 700
WIDTH = 1350
GRIDSIZE = HEIGHT/28
BLACK = ( 0, 0, 0)
WHITE = (255,255,255)
RED = (255, 0, 0)
screen=pygame.display.set_mode((WIDTH,HEIGHT))
font = pygame.font.SysFont("arial", 36)
shift = 10
#---------------------------------------#
# classes #
#---------------------------------------#
class Sprite(pygame.sprite.Sprite):
""" (fileName)
Visible game object.
Inherits Sprite to use its Rect property.
"""
def __init__(self, picture=None):
pygame.sprite.Sprite.__init__(self)
self.x = 0
self.y = 0
self.visible = False
self.image = self.blend_image(picture)
self.rect = self.image.get_rect()
self.width, self.height = self.rect.width, self.rect.height
self.update()
def spawn(self, x, y):
""" Assign coordinates to the object and make it visible.
"""
self.x, self.y = x,y
self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
self.visible = True
def draw(self, surface):
surface.blit(self.image, self.rect)
def update(self):
# after moving a sprite, the rect attribute must be updated
self.rect = self.image.get_rect()
self.rect = pygame.Rect(self.x,self.y,self.rect.width,self.rect.height)
def moveLeft(self):
self.x -= shift
self.update()
def moveRight(self):
self.x += shift
self.update()
def moveUp(self):
self.y -= shift
self.update()
def moveDown(self):
self.y += shift
self.update()
def blend_image(self, file_name):
""" Remove background colour of an image.
Need to use it when a picture is stored in BMP or JPG format, with opaque background.
"""
image_surface = pygame.image.load(file_name)
image_surface = image_surface.convert()
colorkey = image_surface.get_at((0,0))
image_surface.set_colorkey(colorkey)
return image_surface
#---------------------------------------#
# functions #
#---------------------------------------#
def redraw_screen():
screen.fill(BLACK)
world.draw(screen)
if player.visible:
player.draw(screen)
pygame.display.update()
#---------------------------------------#
# main program #
#---------------------------------------#
world = Sprite("town.png")
world.spawn(0,0)
player = Sprite("player.png")
player.spawn(100,400)
LEFT_BORDER = 0
RIGHT_BORDER = WIDTH-1100
TOP_BORDER = 0
BOTTOM_BORDER = HEIGHT-480
#---------------------------------------#
clock = pygame.time.Clock()
FPS = 10
inPlay = True
while inPlay:
clock.tick(FPS)
# keyboard handler
pygame.event.get()
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
inPlay = False
# world moves opposite to the arrow
if keys[pygame.K_LEFT] and world.x < LEFT_BORDER:
world.moveRight()
if keys[pygame.K_RIGHT] and world.x > RIGHT_BORDER:
world.moveLeft()
if keys[pygame.K_UP] and world.y < TOP_BORDER:
world.moveDown()
if keys[pygame.K_DOWN] and world.y > -BOTTOM_BORDER:
world.moveUp()
redraw_screen()
#---------------------------------------#
pygame.quit()
Right, so this is using python 2.7 btw. Any help is appreciated thnx again.
And also, there are some things that are added that have no use, but thats for later code, and vice-versa. Some tweaks i could do, like naming, that will come later.
If the player presses right, i have an image of a guy facing right.
Left, up, and down same concept.
if he holds the button, the guy "runs"
Now i hav normal position, and running position.
4 rows, 2 columns in a png file, how to load 1 row for respective key?

Related

How to make my tile system work with pygame in python

Im making a custom tile system for my game. Its my first time and didnt expect to go that far without a tutorial. So i did my best and made something that works when i render in a tile with the blit method but when i use pygame.sprite.Sprite sprites it doesnt work. I need it to be with pygame's sprite system because it makes it easier doing other stuff and im positive its a rookie mistake i just cant find it.
Code:
# Importing libraries
try:
import pygame
from pygame.locals import *
from time import *
from os import *
from random import *
import sys
print("All Libraries imported successfully \n")
except :
print("Error while importing modules and libraries")
sys.exit()
# Creating Screen
screen_width = 640
screen_height = 640
tile_size = int(screen_width / 10)
screen_res = (screen_width, screen_height)
clock = pygame.time.Clock()
running_fps = 144
screen = pygame.display.set_mode(screen_res)
pygame.display.set_caption(f"GTA Circuit Hack FPS:{running_fps}, {screen_width} x {screen_height}, Tile Size: {tile_size}")
# Loading images
# Background image:
backgroundImg = pygame.image.load("Background.png")
backgroundImg = pygame.transform.scale(backgroundImg, screen_res)
# Not walkable tile image:
noWalkTileImg = pygame.image.load("unwalkTile.png")
noWalkTileImg = pygame.transform.scale(noWalkTileImg, (tile_size, tile_size))
# making tiles
tiles = []
tile_y = 0
tile_x = 0
for i in range(10):
if i > 0:
tile_y += tile_size
for i in range(10):
if i > 0:
tile_x += tile_size
tiles.append((tile_x, tile_y))
tile_x = 0
tile_x = 0
tile_y = 0
# creating object classes
class BadTile (pygame.sprite.Sprite):
def __init__(self, tile):
pygame.sprite.Sprite.__init__(self)
self.image = noWalkTileImg
self.tile = tile
self.rect = self.image.get_rect()
self.x = self.tile[0]
self.y = self.tile[1]
def update(self):
self.x = self.tile[0]
self.y = self.tile[1]
# creating objects
tile1 = BadTile(tiles[99])
game_tiles = pygame.sprite.Group()
game_tiles.add(tile1)
run = True
while run:
clock.tick(running_fps)
screen.blit(backgroundImg, (0,0))
# rendering
game_tiles.update()
game_tiles.draw(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
print ("Exited with no errors")
sys.exit()
Images:
pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The latter delegates to the update method of the contained pygame.sprite.Sprites — you have to implement the method. See pygame.sprite.Group.update():
Calls the update() method on all Sprites in the Group. [...]
The former uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects — you have to ensure that the pygame.sprite.Sprites have the required attributes. See pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]
So you need to set self.rect.x and self.rect.y. Instead of self.x and self.y. You don't need self.x and self.y at all:
class BadTile (pygame.sprite.Sprite):
def __init__(self, tile):
pygame.sprite.Sprite.__init__(self)
self.image = noWalkTileImg
self.tile = tile
self.rect = self.image.get_rect()
self.rect.x = self.tile[0]
self.rect.y = self.tile[1]
def update(self):
self.rect.x = self.tile[0]
self.rect.y = self.tile[1]

Circles created using pygame not showing up

I'm trying to use pygame to make a few balls (circles). The code runs but the circles do not show up. I'm wondering if there is an error somewhere in the ball class or it's in the game function class.
This is the code for the balls class:
import pygame
from pygame.sprite import Sprite
import random
class Ball(Sprite):
def __init__(self, bg_settings, screen, user):
super(Ball,self).__init__()
self.screen = screen
self.bg_settings = bg_settings
self.image = pygame.Surface((bg_settings.ball_radius*2,bg_settings.ball_radius*2))
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.velocityx = random.randint(-1 * bg_settings.ball_speed, bg_settings.ball_speed)
self.velocityy= random.randint(-1 * bg_settings.ball_speed, bg_settings.ball_speed)
self.rect.centerx = random.randint(0,bg_settings.screen_width)
self.rect.centery = random.randint(0,bg_settings.screen_height)
while (self.rect.centerx in range(user.rect.left - 30, user.rect.right + 30) and
self.rect.centery in range(0, user.rect.top + 30)):
self.rect.centerx = random.randint(0,bg_settings.screen_width)
self.rect.centery = random.randint(0,bg_settings.screen_height)
self.speed = bg_settings.ball_speed
self.color = bg_settings.ball_color
self.radius = bg_settings.ball_radius
def draw_ball(self):
pygame.draw.circle(self.image, self.color, (self.rect.centerx,self.rect.centery), self.radius)
def update_ball(self):
if not (self.rect.right < self.screen_rect.right or self.rect.left == self.screen_rect.left):
self.rect.centerx += self.velocityx
if not (self.rect.top == self.screen_rect.top or self.rect.bottom == self.screen_rect.bottom):
self.rect.centery += self.velocityy
Here is relevant code for games functions class (I think just the update screen and creating the balls parts ??):
def update_screen(ai_settings, screen, user, balls, gem):
screen.fill(ai_settings.bg_color)
user.blitme()
for ball in balls.sprites():
ball.draw_ball()
gem.blitme()
pygame.display.flip()
def set_balls(balls, bg_settings, screen, user):
for ball_number in range(bg_settings.num_balls):
new_ball = Ball(bg_settings, screen, user)
balls.add(new_ball)
def update_balls(balls, bg_settings):
balls.update()
for ball in balls.copy():
if ball.rect.left == 0 or ball.rect.right == bg_settings.screen_width:
ball.velocityx = ball.velocityx * -1
elif ball.rect.top == 0 or ball.rect.bottom == bg_settings.screen_height:
ball.velocityy = ball.velocityy * -1
Main loop:
while True:
gf.check_events(user)
gf.update_user(user)
gf.update_screen(bg_settings, screen, user, balls, gem)
gf.update_balls(balls, bg_settings)
Any help is appreciated!
You must use pygame.display.update() at the end of your main event loop to update your screen. After doing this, the window would be updated to reflect the latest state and the circles would appear.
If performance is not an issue, you may want to use pygame.display.flip() instead of pygame.display.update() as I heard that it sometimes has problems with edited zone borders.
The main difference between these two functions is that pygame.display.flip() changes the whole screen while pygame.display.update() only changes parts that it detects changed, so that's why it may have problems

Rectangle not moving on display PyGame

import pygame
width = 500
height = 500
win = pygame.display.set_mode((width,height))
pygame.display.set_caption("Client")
clientNumber = 0
class Player():
def __init__(self,x,y,width,height,color,win):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.rect = (self.x,self.y,self.width,self.height)
self.val = 3
self.win = win
def draw(self):
pygame.draw.rect(self.win, self.color, self.rect)
def move(self):
keyPressed = pygame.key.get_pressed()
if keyPressed[pygame.K_LEFT]:
self.x = self.x - self.val
if keyPressed[pygame.K_RIGHT]:
self.x = self.x + self.val
if keyPressed[pygame.K_UP]:
self.y = self.y - self.val
if keyPressed[pygame.K_DOWN]:
self.y = self.y + self.val
def refreshWindow(win,player):
win.fill((255,255,255))
player.draw()
pygame.display.update()
def main():
player = Player(50,50,100,100,(0,255,0),win)
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
player.move()
refreshWindow(win,player)
main()
The code is updating self.x and self.y and key is being pressed, but the display is not changing the
position of the rectangle. I am new to this and was learning from a tutorial. My code is the exact as that tutorial guide but its not working. Like only its not displaying the updated version. The rest is working fine, i printed statements to verify that the window object is the same and if key was pressed and if x and y coordinates were being changed or not.
Though you have answered your question and fixed your issue I want to mention something. It is not a great idea to have your game object keep both a self.rect and separate self.x, self.y, self.width, self.height attributes as you are doing.
All those attributes are contained within the rect and keeping to separate versions of the same data just means that you have to be updating them both and is large potential for errors either because one or the other did not get updated, or because the state was checked before it got updated and so it contained an old value.
just use a Rect and access the x, y, width, height values as attributes in it like this self.rect.x, self.rect.y, self.rect.width, self.rect.height.
You should check the docs for Rect here and see how much more useful it is than working with separate x,y,width, and height values. For example being able to manipulate it by the center, or being able to directly test the right edge without having to add the width, etc.
The exception with respect to keeping a separate self.x and self.y is if you are having issues related to angular movement where your velocity could be fractional and need floating point accuracy for the positions, since Rects keep int values. In that case you would keep adjust the self.x += vel_x and self.y += vel_y and then immediately assign the x,y to the rect self.rect.topleft = round(self.x), round(self.y) and then use the rect for the rest. The x and y are just for the position accuracywhen you need fractional position adjustments, which does not apply here in your example.
Never mind, i fixed it. I was not changing the self.rect values. Although the tutorial didnt change it. Anyways, its working now.

When I am trying to pass an image through pygame, it keeps saying 'pygame.Surface' has no attribute

I have tried everything I can think of to fix this, but I can't seem to find it. I know it is probably a simple fix, but I cannot find what is making this happen. This is the first part of my code :
import pygame, sys, time
from pygame.locals import *
pygame.init()
WINDOWWIDTH = 900
WINDOWHEIGHT = 400
MOVERATE = 5
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
TEXTCOLOR = (255, 255, 255)
BACKGROUNDCOLOR = (0, 0, 0)
FPS = 40
clock = pygame.time.Clock()
x = 200
y = 150
class player(pygame.sprite.Sprite):
def __init__(self, x, y):
super(player, self).__init__()
temp_image = pygame.image.load("stand_down.png").convert_alpha()
self.image_down = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standleft.png").convert_alpha()
self.image_left = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standright.png").convert_alpha()
self.image_right = pygame.transform.scale(temp_image, (100, 200))
self.image = self.image_down
# keep position and size in pygame.Rect()
# to use it in collision checking
self.rect = self.image.get_rect(x=x, y=y)
def draw(self, x, y):
screen.blit(self.image, self.rect)
def handle_event(self):#, event)
self.image = self.image_down.get_rect()
self.image = pygame.Surface((x, y))
key = pygame.key.get_pressed()
if key[K_LEFT]:
self.rect.x -= 50
self.image = self.image_left
if key[K_RIGHT]:
self.rect.x += 50
self.image = self.image_right
class room1():
#bedroom
def __init__(self):
self.x, self.y = 16, WINDOWHEIGHT/2
self.speed = 3
def draw(self):
background = pygame.image.load("bedroom.jpg").convert()
background = pygame.transform.scale(background, (WINDOWWIDTH, WINDOWHEIGHT))
screen.blit(background, (0, 0))
And this is my main function :
def main():
while True:
for event in pygame.event.get():
player.handle_event.get(event)
player.handle_event(screen)
room1.draw(screen)
player.draw(screen, x, y)
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
main()
I keep getting the same error :
File "C:\Python32\Project3\proj3pt2.py", line 220, in handle_event
self.image = self.image_down.get_rect()
AttributeError: 'pygame.Surface' object has no attribute 'image_down'
I know it's probably an easy fix, but I don't know where to look for it, and how I messed up. If someone could explain that, it would be much appreciated!
When you have an instance and call one of its methods, the instance gets automatically passed as the first argument, self. So if you have a class MyClass and an instance my_instance and you call its handle_event method, it's the same as calling MyClass.handle_event(my_instance).
In your program you never create an instance of the player class and so you're passing the screen as the self argument directly to the class (the screen is actually a pygame.Surface). That means the self in the handle_event method actually refers to the screen surface and since surfaces don't have an image_down attribute, Python raises an error when the self.image_down.get_rect() part is reached.
To fix this problem, you have to create an instance (also called object) of the player class and must not pass an argument to handle_event (unless you add more parameters to the method):
player_instance = player(x_position, y_position)
Then use the instance inside of the while and event loops:
while True:
player_instance.handle_event()
You also have to create an instance of the room1 class instead of using the class directly.
Here's a complete example with some comments about other problems:
import pygame
pygame.init()
WINDOWWIDTH = 900
WINDOWHEIGHT = 400
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
FPS = 40
clock = pygame.time.Clock()
# Load images once globally and reuse them in the program.
background = pygame.image.load("bedroom.jpg").convert()
background = pygame.transform.scale(background, (WINDOWWIDTH, WINDOWHEIGHT))
temp_image = pygame.image.load("stand_down.png").convert_alpha()
image_down = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standleft.png").convert_alpha()
image_left = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standright.png").convert_alpha()
image_right = pygame.transform.scale(temp_image, (100, 200))
class player(pygame.sprite.Sprite):
def __init__(self, x, y):
super(player, self).__init__()
self.image_down = image_down
self.image_left = image_left
self.image_right = image_right
self.image = self.image_down
# keep position and size in pygame.Rect()
# to use it in collision checking
self.rect = self.image.get_rect(x=x, y=y)
# You don't have to pass x and y, since you already
# use the `self.rect` as the blit position.
def draw(self, screen):
screen.blit(self.image, self.rect)
def handle_event(self):
# These two lines don't make sense.
#self.image = self.image_down.get_rect()
#self.image = pygame.Surface((x, y))
# I guess you want to switch back to image_down.
self.image = self.image_down
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.rect.x -= 5
self.image = self.image_left
if key[pygame.K_RIGHT]:
self.rect.x += 5
self.image = self.image_right
class room1():
def __init__(self):
self.x, self.y = 16, WINDOWHEIGHT/2
# Reference to the background image.
self.background = background
def draw(self, screen): # Pass the screen.
screen.blit(self.background, (0, 0))
def main():
# Create player and room instances.
player_instance = player(200, 150)
room1_instance = room1()
while True:
for event in pygame.event.get():
# Users can press the "X" button to quit.
if event.type == pygame.QUIT:
return
player_instance.handle_event()
room1_instance.draw(screen)
player_instance.draw(screen)
# You don't need both update and flip.
# pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
main()
pygame.quit()
Side note: PEP 8 recommends uppercase names for classes, so Player instead of player. Then you could call the instance player.
I suspect you do somewhere something like this
player = player.transform.scale(player.image)
player is Sprite but scale returns Surface - so you replace Sprite with Surface and later you have problems.
(BTW: I saw the same problem in some question few days ago)
If you have to rescale image then do it in __init__ as you already do with some images.
In real game you should create images with correct sizes using any Image Editor so you don't have to use scale()
BTW: in handle_event you do
self.image = self.image_down.get_rect()
self.image = pygame.Surface((x, y))
You assign Rect to Surface (self.image) and later you assing new empty Surface with size x, y. Surface doesn't keep positon, it uses only width, height.
You have self.rect to keep positiona and you already change it with
self.rect.x -= 50
and
self.rect.x += 50
BTW: use UpperCaseNames for classes to make code more readable
class Player(...)
class Room1(...)
Event Stackoverflow knows this rule and it uses light blue color for classes to make code more readable.
More: PEP 8 -- Style Guide for Python Code
BTW: in room1.draw() you read and rescale image again and again - it can slow down program. Do it in room.__init__

trying to use clamp_ip to keep sprites onscreen - pygame

I'm working on a game for class and this is what we've learned to do so far. I'm trying to get the sprites to stay onscreen with clamp_ip, but it keeps giving me an error message that "Butterfly instance has no attribute clamp_ip." Is there a different way I should be keeping the butterflies onscreen?
This first bit is just setting up pygame and the butterfly class (where I included a line to define a rectangle for the butterflies), I've highlighted where I'm guessing the potential errors are below.
This should be ok.
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
playground = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Butterflies')
screenRect = playground.get_rect()
steps = 1
class Butterfly:
def __init__(self):
self.x = random.randint(0, SCREEN_WIDTH)
self.y = random.randint(0, SCREEN_HEIGHT)
self.image_Butterfly = pygame.image.load('butterflya.png')
self.image_ButterflyB = pygame.image.load('butterflyb.png')
self.height = self.image_Butterfly.get_height()
self.width = self.image_Butterfly.get_width()
self.rect = (self.width, self.height)
clock = pygame.time.Clock()
fps = 10
net = []
for i in range (15):
butterflyInstance = Butterfly()
net.append(butterflyInstance)
playground.fill(cyan)
Game_Over = False
while not Game_Over:
for event in pygame.event.get():
if event.type == pygame.QUIT:
Game_Over = True
This is where I'm guessing I messed up. I tried to use the clamp_ip function at the end of this loop
for butterflyInstance in net:
butterflyInstance.x += random.randint(-10, 10)
butterflyInstance.y += random.randint(-10,10)
if steps % 2 == 0:
playground.blit(butterflyInstance.image_Butterfly, (butterflyInstance.x, butterflyInstance.y))
steps += 1
else:
playground.blit(butterflyInstance.image_ButterflyB, (butterflyInstance.x, butterflyInstance.y))
steps +=1
butterflyInstance.clamp_ip(screenRect)
Thank you so much!
See PyGame doc pygame.Rect()
clamp_ip is pygame.Rect method so you have to use butterflyInstance.rect.clamp_ip()
But you have to use self.rect.x, self.rect.y, self.rect.width, self.rect.height instead of self.x, self.y, self.width, self.height to keep position and size of object.
And use butterflyInstance.rect.x, butterflyInstance.rect.y, butterflyInstance.rect.width, butterflyInstance.rect.height
instead of butterflyInstance.x, butterflyInstance.y, butterflyInstance.width, butterflyInstance.height
PyGame use pygame.Rect in many places - it is usefull. For example: you can use rect.right instead of rect.x + rect.width, or rect.center = screenRect.center to center object on screen.

Categories

Resources