Pygame collidepoint() dont account for the blit() - python

Sorry for any gramatical mistakes as i'm a non english speaker
I'm working on a small game during this quarantine and wanted to make a map maker in order to speed up the process of designing levels.
My probleme is that even if the grid show as i expect on the screen, the "getcollide" function does not account for the "blit" of the surface. I spend two days on this and can't think of another way to work this around, here is a sample code :
import pygame
def run(l, h, fps, scene):
pygame.init()
ecran = pygame.display.set_mode((l, h))
clock = pygame.time.Clock()
scene_active = scene
while scene_active != None:
key = pygame.key.get_pressed()
filtre_touche = []
for event in pygame.event.get():
close = False
if event.type == pygame.QUIT:
close = True
elif event.type == pygame.KEYDOWN:
alt = key[pygame.K_LALT] or touche_active[pygame.K_RALT]
if event.key == pygame.K_ESCAPE:
close = True
elif event.key == pygame.K_F4 and alt:
close = True
if close :
scene.quit()
else :
mouse_pos = pygame.mouse.get_pos()
filtre_touche.append(event)
scene_active.traite_input(filtre_touche, key, mouse_pos)
scene_active.update()
scene_active.render(ecran)
scene_active = scene_active.next
pygame.display.flip()
clock.tick(fps)
surface = pygame.Surface((320,320))
M = []
for i in range(10):
x = []
for j in range(10):
x.append(pygame.draw.rect(surface, (255,255,255),(i*32, j*32, 32,32), 1))
M.append(x)
print(M)
class FirstScreen():
def __init__(self):
self.next = self
def traite_input(self, evenement, touche, mouse_pos):
for event in evenement :
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
pygame.quit()
for line in M :
for row in line:
if row.collidepoint(mouse_pos):
print('collide')
def update (self):
pass
def render(self, ecran):
ecran.fill((200,200,200))
ecran.blit(surface, (350,250), surface.get_rect())
run(800,600,60, FirstScreen())
If you run this, you will see that the surface is where we expect it to be and the tile actually are of 32 by 32. But hoovering the mouse over the grid has no effect. The print statement is called when you hoover the mouse where the grid should be if i've blit the surface at 0,0.
Am I missing something ? After searching this website, the pygame doc and all kind of forums i seems to be the only one with this trouble, what am i doing wrong ?

Its because your putting the grid on a surface, the bliting the surface on the screen, so when you make the grid, you start at 0 of the surface. Then blit the surface in the middle(ish) of the screen, when you do the collidepoint. the mouse hovers over it when near 0, the top left of the whole screen. Basically, the position of the mouse on the window is translated to the surface so when you hover the mouse over the grid, the pos of the mouse in the window is not on the grid on the surface. if that makes sense.
Easy fix:
if row.collidepoint((mouse_pos[0] - 350,mouse_pos[1] - 250)):
move the mouse pos back so 0,0 on the surface is 0,0 on the window

Related

How to move an image with the mouse in pygame?

I've written a little pygame program for moving an image by clicking and moving the mouse.
I struggle with making the moving function movableImg to work with my own x, y parameters and not with the predefined x, y parameters as it is now.
Here is my code:
import pygame
import time
pygame.init()
display_width = 800
display_height = 600
white = (255, 255, 255)
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
drag = 0 #switch with which I am seting if I can move the image
x = 100 #x, y coordinates of the image
y = 100
img = pygame.image.load('button.png') #my image and then his width and height
imgWidth = 100
imgHeight = 100
def image(imgX,imgY): #function to blit image easier
gameDisplay.blit(img, (imgX, imgY))
def movableImg(): #function in which i am moving image
global drag, x, y
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
image(x, y)
if click[0] == 1 and x + imgWidth > mouse[0] > x and y + imgHeight > mouse[1] > y: #asking if i am within the boundaries of the image
drag = 1 #and if the left button is pressed
if click[0] == 0: #asking if the left button is pressed
drag = 0
if drag == 1: #moving the image
x = mouse[0] - (imgWidth / 2) #imgWidth / 2 because i want my mouse centered on the image
y = mouse[1] - (imgHeight / 2)
def main_loop():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
movableImg()
pygame.display.update()
clock.tick(60)
main_loop()
pygame.quit()
quit()
So here's the code that I found on the internet and which is working as I want to. SOURCE
import os,sys
import pygame as pg #lazy but responsible (avoid namespace flooding)
class Character:
def __init__(self,rect):
self.rect = pg.Rect(rect)
self.click = False
self.image = pg.Surface(self.rect.size).convert()
self.image.fill((255,0,0))
def update(self,surface):
if self.click:
self.rect.center = pg.mouse.get_pos()
surface.blit(self.image,self.rect)
def main(Surface,Player):
game_event_loop(Player)
Surface.fill(0)
Player.update(Surface)
def game_event_loop(Player):
for event in pg.event.get():
if event.type == pg.MOUSEBUTTONDOWN:
if Player.rect.collidepoint(event.pos):
Player.click = True
elif event.type == pg.MOUSEBUTTONUP:
Player.click = False
elif event.type == pg.QUIT:
pg.quit(); sys.exit()
if __name__ == "__main__":
os.environ['SDL_VIDEO_CENTERED'] = '1'
pg.init()
Screen = pg.display.set_mode((1000,600))
MyClock = pg.time.Clock()
MyPlayer = Character((0,0,150,150))
MyPlayer.rect.center = Screen.get_rect().center
while 1:
main(Screen,MyPlayer)
pg.display.update()
MyClock.tick(60)
You don't need the function. All you need is some events in your for loop. Instead of a messy, complicated function, you can simply figure out when the mouse is clicked:
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN: #Remember to use: from pygame.locals import *
coordinates = pygame.mouse.get_pos()
#set x and y to respective values of coordinates
#Enter necessary code here after
Now coordinates is a pre-defined variable I made up here, in this case, it is used to store the coordinates of your mouse. When event.type is MOUSEBUTTONDOWN, it means that the mouse has been clicked and the program should begin to do the code under that if statement. The events will only activate once, whihc means you cannot drag. I recommend that you create a function that allows the event to continue if the user is holding down on the mouse like this:
def holding():
global held, coordinates
if held:
coordinates = pygame.mouse.get_pos()
#Enter code here
held will be a Boolean variable: it will be equal to True if the mouse if being clicked or to False if not. In this case to prevent the program to think you are clicking the mouse infinitely, add another if statement to check whether the mouse has been released and if so, change held to False:
if event.type == MOUSEBUTTONUP:
held = False
also, to make sure that the function will actually register the fact that the mouse is still being held down, put this line in the if statement for the MOUSEBUTTONDOWN event:
held = True
And finally, to run the function, add an if statement in front of your for loop to run the function when needed:
if held:
holding
TO move multiple images, change their positions to be equal to coordinates or somehow related to coordinates. The if statements do not have to move only one image at a time.

Python/Pygame mouse position does not update (blit function)

I'm trying to make a simple menu using Pygame but I found that whenever I use pygame.mouse.get_position, it does blit what i want but i have to keep move my mouse to make my picture keep blitting.
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800,600))
pygame.display.set_caption('cursor test')
cursorPng = pygame.image.load('resources/images/cursor.png')
start = pygame.image.load('resources/images/menuStart.jpg')
enemy = pygame.image.load('resources/images/enemy-1.png')
white = (255,255,255)
black = (0,0,0)
clock = pygame.time.Clock()
FPS = 60
while True:
screen.fill(white)
pygame.mouse.set_visible(False)
x,y = pygame.mouse.get_pos()
x = x - cursorPng.get_width()/2
y = y - cursorPng.get_height()/2
screen.blit(cursorPng,(x,y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEMOTION:
if x < 50 and y < 250:
screen.blit(enemy,(100,100))
clock.tick(FPS)
pygame.display.update()
what's wrong?
Take a look at your code:
for event in pygame.event.get():
...
elif event.type == pygame.MOUSEMOTION:
if x < 50 and y < 250:
screen.blit(enemy,(100,100))
You check for events, and if you detect that the mouse is being moved (and only then), you draw the image to the screen.
If you want to draw the image even if the mouse is not being moved, just stop checking for the MOUSEMOTION event and simply draw the image always:
while True:
screen.fill(white)
pygame.mouse.set_visible(False)
x,y = pygame.mouse.get_pos()
x = x - cursorPng.get_width()/2
y = y - cursorPng.get_height()/2
screen.blit(cursorPng,(x,y))
if x < 50 and y < 250:
screen.blit(enemy,(100,100))
for event in pygame.event.get():
...
You need to blit into the screen a Surface and a Rect.
First, use this snippet I use for loading images. It makes sure the image is loaded correctly:
def loadImage(name, alpha=False):
"Loads given image"
try:
surface = pygame.image.load(name)
except pygame.error:
raise SystemExit('Could not load image "%s" %s' %
(name, pygame.get_error()))
if alpha:
corner = surface.get_at((0, 0))
surface.set_colorkey(corner, pygame.RLEACCEL)
return surface.convert_alpha()
Second, when you get the Surface, get its rect like this:
cursorSurf = loadImage('resources/images/cursor.png')
cursorRect = cursorSurf.get_rect()
Then, inside the update do the following:
cursorRect.center = pygame.mouse.get_pos()
And finnally, blit to screen like this:
screen.blit(cursorSurf, cursorRect)
Now you will notice your Mouse is being rendered correctly without having to move your mouse.

Have two mouse events, one right after the other, and have both be saved as different variables

So I am currently using pygame to print a rectangle to the screen. Wherever the user clicks, I want the rectangle to move there instantly. I got that working, but now I want the user to be able to change the shape and size of the rectangle every time it moves. I have all of that set up, I am just having problems with the mouse event.
So I basically want to click once, have that coordinate be stored as a variable, click again, and have the new click be stored as a variable.
My code currently right now will pass in coordinates the coordinates of where I clicked in for the top left hand corner, but at the same time it puts those coordinates in for the bottom right hand corner, so basically a tiny dot appears.
import pygame
class Rectangle:
def __init__(self, corner1, corner2, color):
self.corner1 = corner1
self.corner2 = corner2
self.color = color
def setCorner1(self, newCorner1):
self.corner1 = newCorner1
def setCorner2(self, newCorner2):
self.corner2 = newCorner2
def paint(self, surface):
pygame.draw.rect(surface, self.color, (self.corner1[0], self.corner1[1], int(self.corner1[0] - self.corner2[0]), int(self.corner1[1] - self.corner2[1])), 0)
def main():
pygame.init()
size = width, height = 800,700
backgroundColor = [0, 0, 255]
#c = Circle([400,300],40,[255,128,128] )
r = Rectangle([400,300],[500,400],[255,128,128] )
screen = pygame.display.set_mode(size)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
if event.type == pygame.MOUSEBUTTONDOWN:
r.setCorner1(event.pos)
if event.type == pygame.MOUSEBUTTONDOWN:
r.setCorner2(event.pos)
# Handle Drawing:
screen.fill(backgroundColor)
r.paint(screen)
pygame.display.flip()
main()
Right now every time you click it sets both corner1 and corner2 to the same value. The code runs over the first if statement, sees it's true and executes it. Then it moves to the second if statement (independent of the first), sees that one is also true and executes that one.
I'm assuming that you want to store the first click as corner1 then a subsequent click as corner2? In order to do this you'll need to have something that differentiates the two. I added a variable named switch
switch = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
elif event.type == pygame.MOUSEBUTTONDOWN:
if switch == 0:
r.setCorner1(event.pos)
switch = 1
else:
r.setCorner2(event.pos)
switch = 0

Pygame - blitting for a preview picture

First off, this is a school assignment so I want to be upfront about that. Second I'm just asking for advice on the approach, possible help with the code. I'm working on a MSPAINT style clone using some pre-existing code from our book. The code already has the use of the draw.line when pressing mouse button 1. Teacher wants us to add ability to make circles or rectangles. I'm working on the circle part and I have figured out (thanks to the forums on here) how to implement what I wanted to do with the MOUSEBUTTONDOWN and MOUSEBUTTONUP events.. This has brought me to a new Question.. How would I blit then erase then blit a preview of the circle until it is the size the user wants and they release the MOUSEBUTTON and view the final blit...
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
elif event.type == pygame.MOUSEMOTION:
lineEnd = pygame.mouse.get_pos()
if pygame.mouse.get_pressed() == (1,0,0):
pygame.draw.line(background, drawColor, lineStart, lineEnd, lineWidth)
lineStart = lineEnd
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
circleStart = pygame.mouse.get_pos()
elif event.type == pygame.MOUSEBUTTONUP and event.button == 3:
circleEnd = pygame.mouse.get_pos()
size = (circleEnd[0] - circleStart[0])
pygame.draw.circle(background, drawColor, circleStart, size, lineWidth)
elif event.type == pygame.KEYDOWN:
myData = (event, background, drawColor, lineWidth, keepGoing)
myData = checkKeys(myData)
event, background, drawColor, lineWidth, keepGoing) = myData
Thanks so much
-Ben
So after some thinking this is the best solution I came up with using pygame. Tell me what you think and if it has helped you.
import pygame,sys,math #---- Import modules we will need
pygame.init() #---- Initialize the module
def get_rad(origin_x,origin_y,x,y): #----- Returns the appropriate radius
return math.sqrt((origin_x - x)**2 + (origin_y - y)**2) #----- Distance between 2
#----- points
screen = pygame.display.set_mode((400,400)) #----- Sets up the screen
clock = pygame.time.Clock() #------- Sets up the clock
mouse_button = 0 #--------- This variable is used to determine whether a mouse button
#--------- has been pressed
draw_final_circle = False #---------- This variable lets us know that we should draw the
#---------- final circle
while True: #------ main loop
clock.tick(60) #------ Limit the Fps
mouse_button0 = mouse_button #-------- This variable holds the previous value of
#-------- mouse_button(it will be useful later)
mouse_x,mouse_y = pygame.mouse.get_pos() #----- Get the mosue coordinates
for e in pygame.event.get(): #---- Cycle through events
if e.type == pygame.QUIT: pygame.quit();sys.exit() #--Quit when window is closed
if e.type == pygame.MOUSEBUTTONDOWN: #---- If the mouse button is pressed
if mouse_button == 0: #---- if the mouse button is released
mouse_button = 1 #----- set it to pressed basically
originx,originy = mouse_x,mouse_y #---- keep the mouse_x,mouse_y pos
if e.type == pygame.MOUSEBUTTONUP: #---- if the mouse button is released
if mouse_button == 1: #-------- if it is pressed
mouse_button = 0 #--------- set it to released
screen.fill((255,255,255)) #---- clear the screen
#-------- If a mouse button is pressed and a circle can be drawn (rad>width) ------#
if mouse_button == 1 and get_rad(originx,originy,mouse_x,mouse_y) > 1:
rad = int(get_rad(originx,originy,mouse_x,mouse_y)) #---- get the radius(as int)
pos = mouse_x,mouse_y
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #--- draw the circle
#----------------------------------------------------------------------------------#
#---------- if the button is released but in the previous loop it was pressed -----#
if mouse_button == 0 and mouse_button0 == 1:
draw_final_circle = True #----- set the final circle boolean to True
if draw_final_circle: #----- if the final circle is decided
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #---- keep drawing it
pygame.display.flip() #----- flip the buffer
I suggest you implement the different modes of your drawing program into different classes that represent the current mode and its state. This way, implementing different modes become very easy.
As for a circle drawing mode, you want to take a copy the screen surface when the user presses the mouse button, and blit that copy to the screen every frame.
Then draw your circle on that copy. This way, you basically "erase" the temporary circles.
Here's a simple example. Press SPACE to cycle between the different modes (draw, circle, rect) and TAB for different colors:
import pygame
from math import hypot
from itertools import cycle
from operator import itemgetter
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
colors = cycle(sorted(pygame.color.THECOLORS.iteritems(), key=itemgetter(0)))
color = next(colors)[1]
class DrawMode(object):
def __init__(self):
self.last = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.last = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.last = None
def draw(self, screen):
pos = pygame.mouse.get_pos()
if self.last:
pygame.draw.line(screen, color, self.last, pos)
self.last = pos
class ClickReleaseMode(object):
def __init__(self):
self.tmp = None
self.start = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.start = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.start = self.tmp = None
def draw(self, screen):
if not self.tmp:
self.tmp = screen.copy()
pos = pygame.mouse.get_pos()
screen.blit(self.tmp, (0,0))
if self.start:
self.do_draw(screen, pos)
class CircleMode(ClickReleaseMode):
def __init__(self):
super(CircleMode, self).__init__()
def do_draw(self, screen, pos):
r = hypot(pos[0] - self.start[0], pos[1] - self.start[1])
if r >= 2:
pygame.draw.circle(screen, color, self.start, int(r), 2)
class RectMode(ClickReleaseMode):#
def __init__(self):
super(RectMode, self).__init__()
def do_draw(self, screen, pos):
p = pos[0] - self.start[0], pos[1] - self.start[1]
pygame.draw.rect(screen, color, pygame.Rect(self.start, p), 2)
quit = False
modes = cycle((DrawMode, CircleMode, RectMode))
mode = next(modes)()
while not quit:
quit = pygame.event.get(pygame.QUIT)
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
mode = next(modes)()
print 'enter', mode.__class__.__name__
if e.key == pygame.K_TAB:
name, color = next(colors)
print 'changing color to', name, color
mode.handle(e)
mode.draw(screen)
pygame.display.flip()
clock.tick(60)

How do I make something happen when the mouse cursor hovers over an image [duplicate]

This question already has answers here:
How do I detect if the mouse is hovering over a button? PyGame button class is not displaying the text or changing colour on hover
(1 answer)
How to detect when a rectangular object, image or sprite is clicked
(1 answer)
Closed 2 years ago.
Ok so I am working on a home screen for my game and I have an image that acts as a button.
So when you click some where on the image the image changes then imports the next part of the game. But what I want to do as well is when the mouse hovers over the image it will play a sound. But how do I get it to detect when the mouse is being hovered over the button image?
Here is a copy of my code for the home screen. ps. I figured out my problem now you can seem my code here.(This is all the code so far for my home screen. Any was thanks to any one who can help me make it detect when the mouse in hovering over the image.)
import pygame, sys, random
import time
B_images = ["startbutton.png", "startbuttonpush.png"]
class BClass(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("startbutton.png")
self.rect = self.image.get_rect()
self.rect.center = [310, 500]
def animate():
screen.fill([0,0,0])
screen.blit(B.image,B.rect)
pygame.display.flip()
pygame.init()
x = y = 0
pos = pygame.mouse.get_pos()
pygame.display.set_caption("Skier")
screen = pygame.display.set_mode([640,640])
B = BClass()
font = pygame.font.Font(None, 50)
ButtonSound = pygame.mixer.Sound('ButtonSound.ogg')
while True:
animate()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
import End.py
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = event.pos
if ( x in range(229,391)) and (y in range(470,530)):
B.image = pygame.image.load("startbuttonpush.png")
animate()
time.sleep(0.1)
import skier.py
To detect a mouse hover, do the same thing as detecting the mouse click, except do it on the pygame.MOUSEMOTION event. This is called each time a mouse movement is detected.
if event.type == pygame.MOUSEMOTION:
x, y = event.pos
if ( x in range(229,391)) and (y in range(470,530)):
print "Hovering over image!"
Also note that x in range(229, 391) is super-inefficient, and you should do 229 <= x <= 391 instead. And you should not hard-code those coordinates, eventually.
I used to do this on my buttons in pygame as well.
You should consider this code:
def is_hovering():
mouse = pygame.mouse.get_pos()
if button_object.rect.collidepoint(mouse):
return True
else:
return False

Categories