How do i fix the hitbox for my pygame sprite barring resizing every image? - python

class Entity():
def __init__(self, char_type, x, y, scale):
self.char_type = char_type
self.flip = False
self.direction = 1
self.vel_y = 0
self.jump = False
self.attacking = False
self.animation_list = []
self.frame_index = 0
self.action = 0
self.update_time = pygame.time.get_ticks()
#load all images
animation_types = ['idle', 'run', 'jump', 'attack', 'death', 'hit']
for animation in animation_types:
#reset temporary list of images
temp_list = []
#count number of files in the folder
num_of_frames = len(os.listdir(f"img/{self.char_type}/{animation}"))
for i in range(num_of_frames-2):
img = pygame.image.load(f"img/{self.char_type}/{animation}/{i}.png")
img = pygame.transform.scale(img, (img.get_width()*scale,img.get_height()*3))
temp_list.append(img)
self.animation_list.append(temp_list)
self.image = self.animation_list[self.action][self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
This is my first time attempting a pygame project and i'm having issues. Basically the frames are really big so the rectangle I made with self.rect = self.image.get_rect() to create the hitbox for my playable character is massive. what it looks like I tried solving this by using self.rect = self.image.get_bounding_rect()This did solve the issue with having a massive rectangle however it made the image which I drew using the following method
def draw(self, surface):
img = pygame.transform.flip(self.image, self.flip, False)
pygame.draw.rect(surface, (255, 0 , 0), self.rect)
surface.blit(img, self.rect)
to not be centered over the rectangle which should be its hitbox. That ended up looking like this. I think that the issue is that

See How to get the correct dimensions for a pygame rectangle created from an image and How can I crop an image with Pygame?:
Use pygame.Surface.get_bounding_rect() and pygame.Surface.subsurface to get a cropped region from your image.
e.g.:
original_image = pygame.image.load(f"img/{self.char_type}/{animation}/{i}.png")
crop_rect = original_image.get_bounding_rect()
cropped_image = original_image.subsurface(crop_rect).copy()
img = pygame.transform.scale(cropped_image,
(cropped_image.get_width()*scale, cropped_image.get_height()*3))

Related

Rotating a sprite on pygame [duplicate]

This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
Closed last year.
I have created a class for the zombie I blit into the screen, but the rotation does not work at all, it rotates a lot and moves the image around, is there some way to rotate the image around its center? how could I change the rotate def so that it works properly?
class zombieObj:
def __init__(self, x, y, vel, angle):
tempZombie = pygame.image.load('zombieSprite.png')
self.zombieSpriteSurface = pygame.transform.scale(tempZombie, (64, 64))
self.x = x
self.y = y
self.vel = 1
self.angle = angle
def rotate(self, image, angle):
self.zombieSpriteSurface = pygame.transform.rotate(image, angle)
and this is how I called it in the loop:
zombieSprite.angle = zombieSprite.angle + 5
zombieSprite.rotate(zombieSprite.zombieSpriteSurface, zombieSprite.angle)
See How do I rotate an image around its center using PyGame?. The trick is to get the center of the image before rotation and set it after rotation. Also, you need to rotate the original image to avoid distortion:
class ZombieObj:
def __init__(self, x, y, vel, angle):
tempZombie = pygame.image.load('zombieSprite.png')
self.originalImage = pygame.transform.scale(tempZombie, (64, 64))
self.vel = 1
self.angle = 0
self.rect = self.originalImage.get_rect(topleft = (x, y))
self.roatate(angle)
def rotate(self, angle):
self.angle = angle
self.zombieSpriteSurface = pygame.transform.rotate(self.originalImage, self.angle)
self.rect = self.zombieSpriteSurface.get_rect(center = self.rect.center)
If you want to increase the rotation by a certain angle, you need to add the additional angle to the current angle:
class ZombieObj:
# [...]
def rotate(self, deltaAngle):
self.angle += deltaAngle
self.zombieSpriteSurface = pygame.transform.rotate(self.originalImage, self.angle)
self.rect = self.zombieSpriteSurface.get_rect(center = self.rect.center)

How can i get the first element from a sprite group in pygame

I create the sprite group land. For that, i use the class Landschaft and the while loop
How can i get from first sprite in the sprite group land rect.x , rect.y rect.w and rect.h ?
class Landschaft(pygame.sprite.Sprite):
def __init__(self,image):
pygame.sprite.Sprite.__init__(self)
self.image = image
#self.mask = pygame.mask.from_surface(self.image )
self.rect = self.image.get_rect()
#pygame.draw.ellipse(self.image, red, [self.rect.x,self.rect.y,self.rect.width,self.rect.height],3)
x=random.randrange(0, breite -60)
y=random.randrange(200, hoehe - 200)
self.rect.center = (x,y)
land = pygame.sprite.Group()
while len(land) < anzahl_gegenstaende:
ii = len(land)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
if ii == 0:
img = pygame.transform.scale(img,(breite))
else:
zb,zh = img.get_rect().size
scale_hoehe =100
scale_breite = int(100 * zb / zh)
#img = img.convert_alpha()
img = pygame.transform.scale(img,(scale_breite,scale_hoehe))
m = Landschaft(img)
if not pygame.sprite.spritecollide(m, land, False):
land.add(m)
A list of sprites from a pygame.sprite.Group can be obtained with the sprites() method:. Therefore the first sprite in the Group is:
first_sprite = group.sprites()[0]
Problem?
Your land = pygame.sprite.Group() prevents you from getting the first element from a sprite group in pygame. This is a fairly common problem. To get it you have to use sprites() as you did, but in a different way. You approached the solution with your code. You came close.
You have to write, as a solution to your problem, group.sprites() (with [0]) (not pygame.sprite.Group ()) to get from first sprite in the sprite group land rect.x, rect.y rect.w and rect.h
Solution?
The solution to your problem is:
class Landschaft(pygame.sprite.Sprite):
def __init__(self,image):
pygame.sprite.Sprite.__init__(self)
self.image = image
#self.mask = pygame.mask.from_surface(self.image )
self.rect = self.image.get_rect()
#pygame.draw.ellipse(self.image, red, [self.rect.x,self.rect.y,self.rect.width,self.rect.height],3)
x=random.randrange(0, breite -60)
y=random.randrange(200, hoehe - 200)
self.rect.center = (x,y)
land = group.sprites()[0]
while len(land) < anzahl_gegenstaende:
ii = len(land)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
if ii == 0:
img = pygame.transform.scale(img,(breite))
else:
zb,zh = img.get_rect().size
scale_hoehe =100
scale_breite = int(100 * zb / zh)
#img = img.convert_alpha()
img = pygame.transform.scale(img,(scale_breite,scale_hoehe))
m = Landschaft(img)
if not pygame.sprite.spritecollide(m, land, False):
land.add(m)

Pygame collision detection not working with rotated image

I don't understand why the sprite collision detection is not taking the image rotation into account.
I tried different functions but they didn't work out for me.
CarSprites.py:
import pygame, math, random
class CarSprite(pygame.sprite.Sprite):
MIN_FORWARD_SPEED = 5
ACCELERATION = 2
TURN_SPEED = 5
IS_DUMMY = False
def __init__(self, image, position, direction):
pygame.sprite.Sprite.__init__(self)
self.src_image = pygame.transform.scale(pygame.image.load(image), (51, 113))
self.position = position
self.rect = self.src_image.get_rect()
self.rect.center = self.position
self.speed = 0
self.direction = direction
self.k_left = self.k_right = self.k_down = self.k_up = 0
def update(self, deltat):
# SIMULATION
#speed
self.speed += (self.k_up + self.k_down)
if self.speed < self.MIN_FORWARD_SPEED:
self.speed = self.MIN_FORWARD_SPEED
self.speed += (self.k_up + self.k_down)
if self.speed > self.MIN_FORWARD_SPEED * 2:
self.speed = self.MIN_FORWARD_SPEED * 2
#direction
self.direction += (self.k_right + self.k_left)
x, y = self.position
rad = self.direction * math.pi / 180
x += -self.speed*math.sin(rad)
y += -self.speed*math.cos(rad)
self.position = (x, y)
self.image = pygame.transform.rotate(self.src_image, self.direction)
self.rect = self.image.get_rect()
self.rect.center = self.position
#Emulate friction with road and wind
if self.speed > self.MIN_FORWARD_SPEED :
self.speed += -0.1
class DummyCarSprite(pygame.sprite.Sprite):
#MIN_FORWARD_SPEED = 5
#MIN_REVERSE_SPEED = 10.1
#MAX_FORWARD_SPEED_ABOVE_MIN = 5
#ACCELERATION = 2
#TURN_SPEED = 5
def __init__(self, image, position, direction):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(pygame.image.load(image), (51, 113))
self.position = position
self.rect = self.image.get_rect()
self.rect.center = self.position
self.speed = 0
self.direction = direction
self.k_left = self.k_right = self.k_down = self.k_up = 0
if random.randint(0,1) == 1 :
self.direction = self.direction + 180
game.py
def GetDummyCars() :
allDummyCars = [
#Row1
#DummyCarSprite(getCarImage(), (211.9915431212928, 209.36603413022453), 180),
#DummyCarSprite(getCarImage(), (268.9915431212928, 209.36603413022453), 180),
DummyCarSprite(getCarImage(), (325.9915431212928, 209.36603413022453), 180),
DummyCarSprite(getCarImage(), (382.9915431212928, 209.36603413022453), 180)
#etc. etc.
]
dummyCars = []
for dummyCar in allDummyCars :
if random.randint(0,1) == 1 :
dummyCars.append(dummyCar)
return pygame.sprite.RenderPlain(*dummyCars)
playerCar = CarSprite(playerCarImagePath, (1550, 100), 90)
playerCar_group = pygame.sprite.RenderPlain(playerCar)
dummyCar_group = GetDummyCars()
#collisions with dummy cars
dummyCarCollisions = pygame.sprite.groupcollide(playerCar_group, dummyCar_group)
if dummyCarCollisions != {}:
lose_condition = True
playerCar.src_image = pygame.image.load('images/collision.png')
seconds = 0
playerCar.speed = 0
playerCar.MIN_FORWARD_SPEED = 0
playerCar.MAX_FORWARD_SPEED_ABOVE_MIN = 0
playerCar.k_right = 0
playerCar.k_left = 0
I would like to find a way to detect collision between the sprites in the 2 car groups, or collision between the player sprite and the dummycar_group (each would work out for me) that takes the rotation of the image into account.
What happens now is when I steer the car, the car image rotates but it looks like the collision detection doesn't see that.
Is there a better function i can use that could handle this?
My full source code: dropbox
I found this question very interesting and fun to work on and fix! Thanks for posting!
I have found the problem and it is rather unfortunate. Your code runs perfectly from what I have seen. The problem is that pygame uses rectangles for collision detection which are not precise enough.
You are applying the rotation to the image but that just makes it bigger and less accurate. I have highlighted the problem with the addition of rendiering debug lines in the GameLoop function.
# draw some debug lines
pygame.draw.rect(screen, (255, 0, 0), playerCar.rect, 1)
for dummyCar in dummyCar_group.sprites():
pygame.draw.rect(screen, (0, 0, 255), dummyCar.rect, 1)
Add these lines in and you shall see for yourself.
The only solution that I can think of is to add in the functionality to use polygons for collision detection yourself.
The way I would implement this is to:
Stop using the rect attribute of all Sprites for collision detection and stop using any methods for collision detection that use the underlying Rects, e.g pygame.sprite.spritecollide().
add a pointlist field to all sprites that need it which will store all the points of the polygon
Implement your own function that takes in two lists of points and returns if they overlap
I hope that this answer helped you and if you have any further questions please feel free to post a comment below!

Spritesheet trouble [duplicate]

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?

Pygame Collision Detection: AttributeError

I've been banging my head against this for a while. I am trying to make a game with PyGame and I got up to the collision segment and have been stuck for a while and have checked a few threads.
This is the code I have (removed other methods and conditional statements in between, but left the relevant parts). I am a little confused by the error because I do a self.imageRect = self.image.get_rect() in both classes init, yet I have this error. The error was specifically:
"AttributeError: 'Pebble' object has no attribute 'rect'" when the program attempts to carry out the collision detection part in the dog class. What have I been doing wrong?
import random
import pygame, sys
pygame.init()
clock = pygame.time.Clock() # fps clock
screenSize = WIDTH, HEIGHT = [800, 600]
screen = pygame.display.set_mode(screenSize)
background = pygame.Surface(screen.get_size())
bgColorRGB = [153, 204, 255]
background.fill(bgColorRGB)
pebbleGroup = pygame.sprite.Group()
pebbleSingle = pygame.sprite.GroupSingle()
dogSingle = pygame.sprite.GroupSingle()
#----------------------------------------------------------------------
class Dog(pygame.sprite.Sprite):
def __init__(self, path, speed):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load(path) # load sprite from path/file loc.
self.imageRect = self.image.get_rect() # get bounds of image
self.imageWidth = self.image.get_width()
self.imageHeight = self.image.get_height()
self.speed = speed
# sets location of the image, gets the start location of object
# sets the start location as the image's left and top location
def setLocation(self, location):
self.imageRect.left, self.imageRect.top = location
def checkCollision(self, pebble, dogGroup):
if pygame.sprite.spritecollide(pebble, dogGroup, False):
print "collided"
#---------------------------------------------------------------------
class Pebble(pygame.sprite.Sprite):
def __init__(self, path, speed, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(path)
self.imageRect = self.image.get_rect()
self.imageWidth = self.image.get_width()
self.imageHeight = self.image.get_height()
self.imageRect.left, self.imageRect.top = location
self.speed = speed # initialize speed
self.isDragged = False
#----------------------------------------------------------------------
def startGame():
pebblePaths = ['images/pebble/pebble1.jpg', 'images/pebble/pebble2.jpg']
for i in range(3):
pebblePath = pebblePaths[random.randrange(0, len(pebblePaths))]
pebbleSpeed = [random.randrange(1, 7), 0]
pebbleLocation = [0, random.randrange(20, HEIGHT - 75)]
pebble = Pebble(pebblePath, pebbleSpeed, pebbleLocation)
pebbleGroup.add(pebble)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
for pebble in pebbleGroup:
dog.checkCollision(pebble, dogSingle)
pygame.display.flip()
clock.tick(30) # wait a little before starting again
startGame()
It's expecting Sprite.rect, so change from
self.imageRect = self.image.get_rect()
#to
self.rect = self.image.get_rect()
Note
self.imageWidth = self.image.get_width()
self.imageHeight = self.image.get_height()
self.imageRect.left, self.imageRect.top = location
These are not necessary, since rect's have many properties, like self.rect.width or self.rect.topleft = location . Another useful one is centerx.
full list at: pygame Rect docs

Categories

Resources