Sprite mask collision doesnt work (rect collision works) - python

The collision works when I use:
pygame.sprite.collide_rect
but it doesnt work when I use:
pygame.sprite.collide_mask
Here are my 2 classes:
class Ball():
def __init__(self):
self.rect = balltexture.get_rect()
self.rect.x=225
self.rect.y=400
self.radius = 100
self.velx=0
self.vely=0
self.action=False
self.switchx=0
self.switchy=0
pygame.sprite.Sprite.__init__(self)
self.mask = pygame.mask.from_surface(balltexture)
def draw(self):
win.blit(balltexture, self.rect)
class rock():
def __init__(self, y):
self.rect = rocktexture.get_rect()
self.rect.x = screensize.current_w
self.rect.y = y
self.width = 120
pygame.sprite.Sprite.__init__(self)
self.mask = pygame.mask.from_surface(rocktexture)
def draw(self, win):
win.blit(rocktexture, self.rect)
and here is the collision check
for i in rocks:
if pygame.sprite.collide_rect(i, Player):
print(1)
if pygame.sprite.collide_mask(i, Player):
print(2)
So when they collide it prints 1 all the time.
BTW I'm moving rect coordinates (e.g. Player.rect.x) to move the Player/rocks and I also tried the mask overlap method but it didn't work either
offset = (Player.rect.x-i.rect.x, Player.rect.y-i.rect.y)
if i.mask.overlap(Player.mask, offset):
print(2)

Not quite sure what's going on, but here's some things to try:
You can set the mask to all 1s with self.mask.fill(). At this point, the mask collision should be identical to the rect-based collision.
You can call print(self.mask.count()), which will show the number of set pixels. Make sure it's not 0 or something.
You can call i.mask.overlap(Player.mask (0, 0)), and confirm that those overlap when they have the same upper-left corner.
This is either going to be an issue with the mask creation, or the offsets are wrong in some way; this should narrow down the issue.

One of my images had no transparency, that was the problem

Related

How do I use sprite collisions correctly here?

list_zombies = pygame.sprite.Group()
walls = pygame.sprite.Group()
class Zombie(pygame.sprite.Sprite):
def __init__(self, x, y, speed):
pygame.sprite.Sprite.__init__(self, list_zombies)
self.x = x
self.y = y
self.speed = zombie_speed
self.image = pygame.image.load("Zombie.png").convert()
self.rect = self.image.get_rect()
self.pos1 = self.x
self.pos2 = self.y
def update(self):
self.y += zombie_speed
def main(self, display):
self.update()
display.blit(zombie_image_copy, (self.x, self.y))
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self, walls)
self.x = x
self.y = y
self.image = wall_image.convert()
self.rect = self.image.get_rect()
def main(display):
display.blit(wall_image, (-100, 950))
main loop:
for zombie in list_zombies:
zombie.rect.topleft = (zombie.x, zombie.y)
if pygame.sprite.groupcollide(list_zombies, walls, 0, 0, collided= None):
zombie.kill()
These are my classes and i want to kill the zombie if it hits the wall. I have used groupcollide since spritecollide wouldn't work and I would get the error that Wall doesn't have the attribute rect. Somehow it is not working but also not giving me an error. Maybe you can help me.
See How do I detect collision in pygame? and read the documentation! 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. [...]
The rect attribute is also used for collision detection functions like pygame.sprite.groupcollide. You don't need the .x and .y attributes at all. If you use the .rect attribute (.rect.x and .rect.y) insterad, collision detection works immediately.
If you want to store sprite positions with floating point accuracy, you have to store the location of the object in separate attributes. Synchronize the pygame.Rect object when the position of the object is changed in the update method:
def update(self):
self.x = # change
self.y = # change
self.rect.topleft = round(self.x), round(self.y)

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.

How to change the size of a collision box of a circle sprite in pygame

I am designing a game however I have postponed my development to focus on the collisions of sprites in the shape of circles.
I am not 100% sure how the 'pygame.sprite.collide_circle' command works with its parenthesis however I have included it within my Collidecheck variable as well as created a new variable called 'collided' to then include within Collidecheck as 'collided = None'.
My Ball class uses the ball objects which are all circles. There are two which should interact being ball and llab.
class Ball(pygame.sprite.Sprite):
def __init__(self, width, height, herex, herey):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
pygame.draw.circle(self.image, BLACK, [int(width/2),int(height/2)], int(width/2), 10)
self.rect.x = herex
self.rect.y = herey
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
def moveUp(self, pixels):
self.rect.y -= pixels
def moveDown(self, pixels):
self.rect.y += pixels
The 2 circles are in sprite groups as I am practising using them
ball = Ball(100, 100, 50, 50)
llab = Ball(100, 100, 350, 250)
g = pygame.sprite.Group(ball)
h = pygame.sprite.Group(llab)
Within the main game loop, ball moves so I can test the hitboxes. If you watch the console as it is printing the collision check, you can see it working.
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
ball.moveLeft(5)
ballFollow.moveLeft(5)
if keys[pygame.K_RIGHT]:
ball.moveRight(5)
ballFollow.moveRight(5)
if keys[pygame.K_UP]:
ball.moveUp(5)
ballFollow.moveUp(5)
if keys[pygame.K_DOWN]:
ball.moveDown(5)
ballFollow.moveDown(5)
Collidecheck = pygame.sprite.spritecollide(ball, h, False, pygame.sprite.collide_circle)
print(Collidecheck)
screen.fill(RED)
g.draw(screen)
h.draw(screen)
I originally expected to see within the console the collision be between ball and llab but ball and llab have a really large hitbox which I don't want. I want to only see the hitboxes of both these circles extend to their actual shape and no larger. It seems their hitboxes are double their sizes I think although I am not sure
P.S I understand I could use maths and might have to, I was wondering if anyone had better knowledge of the sprite functions to help me.
See documentation for pygame.sprite.collide_circle
If the sprites have a "radius" attribute, that is used to create the circle, otherwise a circle is created that is big enough to completely enclose the sprites rect as given by the "rect" attribute.
So you have to create self.radius in Ball and it can be smaller then ball's size.
How it works ? It gets centers of both sprites and use Pythagorean theorem (a**2 + b**2 = c**2) to calculate distance between these points. If it is smaller then sprite1.radius1 + sprite2.radius2 then you have collision.
BTW: PyGame has pygame.math.Vector2.distance_to() to calculate it.

How to show objects from different files on the same surface with Pygame?

I'm making a very simple 2D game in pygame and I've made several files with classes in them that defines the characteristics of a few particles and how the terrain is going to look. The problem is that I can't get them to both show on the same surface. I've tried fiddling around with it but when I run it, the terrain ends up showing without the particles. The particles appears in a separate window only after I've exited the first window.
So my question is:
How do I show objects from different files on the same surface/screen with Pygame?
I'm using the pygame.draw function to draw the player and the particles.
Part of one of the classes:
class Volcano():
def __init__(self, x, y, size):
self.x = x
self.y = y
self.size = size
self.colour = (0, 0, 255)
self.thickness = 1
self.speed = 0
self.angle = 0
def display(self):
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
The target Surface needs to be an argument of the draw method:
class Volcano():
# [...]
def display(self, surf):
pygame.draw.circle(surf, self.colour,
(int(self.x), int(self.y)), self.size, self.thickness)
Pass the screen Surface to the draw method when it is called:
volcano = Volcano(x, y, size)
volcano.draw(screen)

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?

Categories

Resources