I am trying to detect when a Sprite has been hovered over by the mouse. However it is not working, it is never detecting the mouse over.
Code for Tile:
#!/usr/bin/python
import pygame
class Tile(pygame.sprite.Sprite):
def __init__(self, img_sprite, init_position):
pygame.sprite.Sprite.__init__(self)
self.position=init_position
self.image=pygame.image.load(img_sprite)
self.rect = self.image.get_rect()
Code for event
for event in pygame.event.get():
for tile in tiles:
if tile.image.get_rect().collidepoint(pygame.mouse.get_pos()):
print 'tile hovered'
You are checking if your mouse is over the rect that is the image rect. Since the rect returned by image.get_rect() is always in the form (0,0,width,height), then your check only works if the position of your tile is at (0,0).
To fix this your can keep the position in the rect, or create a new rect that will describe the actual position.
You can also filter the events, and only check for a MOUSEMOTION event type.
Related
So I am making this button and I need to find out two things
1 - When is the user hovering over it?
2 - When is the user clicking on it?
As you can see these buttons are oddly shaped
I have tried looking on the pygame forums but those only state how to find out if two sprites are colliding.
Just handle the MOUSEBUTTONDOWN event, and check the event.pos against the rect of each of your sprites. If there's a collision, that's the one that was clicked on.
It's the same for hovering, except you need to grab the mouse position each frame via the pygame.mouse.get_pos() function.
If you put all your button sprites into a SpriteGroup, it will be easy to do these checks.
For oddly shaped sprites, it may help to use a Sprite mask. This is an extra-check made (after the rectangle collision shows true) at the per-pixel level. So while the mouse may have done some action within the bounds of the Sprite's rectangle, maybe it was not truly over the visible part of the Sprite's image.
Luckily the PyGame Sprite library makes the creation of a mask easy, and if defined, it will automatically be used as part of the collision detection.
class MaskedSprite( pygame.sprite.Sprite ):
def __init__( self, image, x, y ):
pygame.sprite.Sprite.__init__( self )
self.image = image.convert_alpha()
self.mask = pygame.mask.from_surface( self.image ) # <<-- HERE
self.rect = self.image.get_rect()
self.rect.center = ( x, y )
There are a few simple rules around mask creation. If the image has a transparent background, that will be used to determine what is part/not-part of the sprite-collision.
Otherwise there needs to be a difference in colour between the background/foreground of 127 colour units (the default). This can be changed with the threshold parameter to pygame.mask.from_surface().
There is also the pygame.mask.from_threshold() function.
To be honest, just use a transparent background, and everything will be OK.
Everything.
I have this snippet of code:
end_hist_list = pygame.sprite.spritecollide(self, end_walls, False)
for end in end_hist_list:
end_sound.play()
#now need to root position of mouse/or disable mouse movement
So when that sprite (player) collides with end_wall, I need for mouse to not be able to move, to just root in that position (when that collision happened). But I can't find any function that would let disable or root mouse. I tried reseting position to end_walls coordinates, but then it resets near that sprite, but not on top of it. I think there should be some simple way to do that, I just might not see it. Any suggestions?
P.S. mouse controls player sprite (in end spritecollide it is self) like this:
def update(self):
""" Update player position """
pos = pygame.mouse.get_pos()
self.rect.x = pos[0]
self.rect.y = pos[1]
As well as mouse.get_pos, there is a mouse.set_pos. You could use this to keep returning the mouse to the appropriate position when the player tries to move it away. Effectively, you reverse the current update:
pygame.mouse.set_pos(self.rect.x, self.rect.y)
Alternatively, you could just stop dealing with mouse events. If the cursor is visible it will still move around, but the game will ignore it.
I am drawing a small colored square, using surface.fill(color, rect), on a sprite surface, which is then be displayed on top of the other surfaces in the following order:
Background surface
Maze surface
Sprite surface
Currently I am having a problem where my sprites smear across the screen because I'm not wiping the screen each time. How can I eliminate this smearing effect, while keeping the sprite layer from covering the other layers?
Layers code - Initialized, but not updated each frame.
game_surface = pygame.Surface((self.canvas.get_size()))
game_surface = game_surface.convert()
game_surface.fill((0,0,255))
maze_surface = pygame.Surface((self.canvas.get_size()))
maze_surface = maze_surface.convert_alpha()
maze_surface.fill((0,0,255,0))
play_surface = pygame.Surface((self.canvas.get_size()))
play_surface = play_surface.convert_alpha()
play_surface.fill((0,0,0,0))
Presently only the play_surface actually actually uses transparency, but eventually both the play_surface and the maze_surface will need to be transparent.
Sprites layer update - Called every time the sprite is moved.
def update(self, canvas):
# move sprite if keys pressed (not shown)
self.surface.fill((0,0,0,0)) # Newest screen fill attempt. Causes smearing effect
self.surface.fill(self.color, self.rect) # draw green square
canvas.blit(self.surface, (0,0))
Smear effect: Red = maze_layer, Green = smeared sprite
Alternate sprite fill - Altered version of the above
def update(self, canvas):
# move sprite if keys pressed (not shown)
self.surface.fill((0,0,0)) # Original screen fill. Covers up lower layers
self.surface.fill(self.color, self.rect) # draw green square
canvas.blit(self.surface, (0,0))
Non-transparency - Black = filled color (I want to do this without covering up the other layers)
How can I eliminate this smearing effect (pic 1), while keeping the sprite layer from covering up the other layers (pic 2)? Any assistance is very much appreciated.
You need to redraw the background covered by your sprites, update their positions, and then redraw the sprites in their new position.
You may be interested in exploring the pygame.sprite module which has built-in support for this functionality. You can create subclasses of pygame.sprite.Sprite that:
Override the update(self) method (optional)
Have self.image and self.rect attributes that are used to draw them to screen
Are added to pygame.sprite.Group instances
Pygame's sprite group classes (such as Group, GroupSingle, LayeredUpdates, etc.) include methods like pygame.sprite.Group.clear(surface_dest, background) (see the documentation) to facilitate the task of re-drawing background images on your sprites before moving and re-drawing them elsewhere.
I want to make a game using pygames in python and I have a background which I want to load only at the beginning of the game not at every frame and the background should still appear.
I load the background like this in a init function:
self.window = pygame.display.set_mode((self.SCREEN_WIDTH, self.SCREEN_HEIGHT))
self.screen = pygame.display.get_surface()
self.rasp = "../images/image.jpg"
self.rasp_surface = pygame.image.load(self.rasp)
self.rasp_surface = pygame.transform.scale(self.rasp_surface, (self.SCREEN_WIDTH, self.SCREEN_HEIGHT))
self.screen.blit(self.rasp_surface, (0,0))
and in a run function I do this :
while True:
...
self.screen.blit(self.rasp_surface, (0,0))
pygame.display.update()
If you have a while loop with pygame.display.update() in it, you are constantly doing this. This is unnecessary. However, it is likely you are moving sprites or images over the background, so just make sure to fill in the portion of the image that the sprite is moving over as it moves. You can make a move function to do that for you.
In the following code, there is not just one circle on the screen at any given point in time.
I want to fix this to make it so that it looks like there is only one circle, instead of leaving a smudge trail where ever the mouse cursor has been.
import pygame,sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((640,400),0,32)
radius = 25
circle = pygame.Surface([radius*2]*2,SRCALPHA,32)
circle = circle.convert_alpha()
pygame.draw.circle(circle,(25,46,100),[radius]*2,radius)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.blit(circle,(pygame.mouse.get_pos()[0],100))
pygame.display.update()
pygame.time.delay(10)
You need to specifically erase the circle before you blit it again. Depending on how complicated your scene is, you may have to try different methods. Generally what I do is have a "background" surface that a blit to the screen every frame and then blit the sprites/other surfaces in their new positions (blits in Pygame are very fast, so even in fairly large screens I haven't had speed issues doing this). For your code above, it's simple enough just to use surface.fill(COLOR) where COLOR is your background color; eg, (255,255,255) for white:
# ...
screen = pygame.display.set_mode((640,400),0,32)
backgroundColor = (255,255,255)
# ...
while True:
# ...
screen.fill(backgroundColor)
screen.blit(circle,(pygame.mouse.get_pos()[0],100))
pygame.display.update()
pygame.time.delay(10)
Edit in answer to your comment: It is possible to do this in a more object-oriented way.
You will need to have a background Surface associated with your screen (I usually have a Display or Map class (depending on the type of game) that does this). Then, make your object a subclass of pygame.sprite. This requires that you have self.image and self.rect attributes (the image being your surface and the rect being a Pygame.rect with the location). Add all of your sprites to a pygame.group object. Now, every frame, you call the draw method on the group and, after you update the display (ie, with pygame.display.update()), you call the clear method on the group. This method requires that you provide both the destination surface (ie, screen above) and a background image.
For example, your main loop may look more like this:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
circle.rect.center = (pygame.mouse.get_pos()[0],100)
circleGroup.draw(screen)
pygame.display.update()
circleGroup.clear(screen, backgroundSurface)
pygame.time.delay(10)
See the documentation on the Sprite and Group classes for more information.