Pygame - How to make pygame.mouse.get_pos in a rectangular area - python

Basically, the question is asking how to get the mouse location for a rectangular area.
So, instead of just finding if the mouse is on, for example, the coordinates 100, 100, it would find if the mouse is inside a rectangular area with the corners being at 100, 100, or where ever the rectangle is.
Sorry if this is a really simple question to answer, I just couldn't find it anywhere. Thanks

import pygame, sys
pygame.init(); clock = pygame.time.Clock()
scr = pygame.display.set_mode((640, 480))
image = pygame.image.load('image.png')
rect = image.get_rect()
rect.center = (320, 240)
while True:
pygame.display.flip()
clock.tick(60)
scr.fill((0, 0, 0))
scr.blit(image, rect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
if pygame.mouse.get_pressed()[0]:
if rect.collidepoint(pygame.mouse.get_pos()):
print 'The mouse was click inside the image.'

Related

Pygame mouse position not precise enough to rotate the 3D scene

I need to rotate a 3D scene or a character using the mouse position: for example, if I move the mouse to the right, I want to turn to the right / the character should turn right.
I've seen lots of games place the mouse in the center of the screen, then get its position relatively to the previous frame, update the game accordingly and move the mouse back in the center.
I tried to replicate this but I ran into the issue that pygame does not rotate reliably: with higher framerates, I tend to get a slower rotation. This is not a problem when rotating a character in a 2D game, but this is concerning when a 3D scene rotates differently if there are more objects to draw.
I used this simple code to make sure the problem actually came from this:
import pygame
from pygame.locals import *
pygame.init()
WIDTH, HEIGHT = (640, 480)
screen = pygame.display.set_mode((WIDTH, HEIGHT))
font = pygame.font.SysFont('consolas', 20)
clock = pygame.time.Clock()
angle = 0
time_passed = 0
pygame.mouse.set_pos((WIDTH//2, HEIGHT//2))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
# I did not use the MOUSEMOTION event because it used to glitch a bit
x, y = pygame.mouse.get_pos()
angle += (WIDTH//2 - x) * 360 / WIDTH # 360° every WIDTH pixels
pygame.mouse.set_pos((WIDTH//2, HEIGHT//2))
screen.fill(0)
text = font.render('Angle: %d°' %angle, True, (255, 255, 255))
w, h = text.get_size()
screen.blit(text, (WIDTH//2 - w//2, HEIGHT//2 - h//2))
pygame.display.flip()
time_passed = clock.tick(60)/1000
I think either the mouse or the OS tweak the travelled distance according to the mouse speed, so I did the following tests at similar and relatively constant speed:
Going say 5 centimeters with the framerate limited to 60 FPS did not result in the same final angle as with 120 FPS for example, even when moving the mouse at the same speed, with the same mouse with high DPI.
Do I need to implement a tick system for example, where I only update the position every few frames? I think the problem wouldn't be solved with slower framerates then, and maybe the movement would not be as responsive as I want.
Or perhaps there is an alternative to pygame.mouse.get_pos() / event.pos, getting the raw, more precise position from the mouse?
I would appreciate any help.
You get the position of the mouse with pygame.mouse.get_pos() and you reset the position with pygame.mouse.set_pos(). What if there was an event between pygame.mouse.get_pos() and pygame.mouse.set_pos()? Also pygame.mouse.get_pos() only tells you the current position of the mouse curosr, but it does not tell you how the mouse got there.
I suggest to use the MOUSEMOTION event. The event queue gives you all movements of the mouse. The relative movement of the mouse pointer since the last movement can be queried with the rel attribute:
for event in pygame.event.get():
if event.type == pygame.MOUSEMOTION:
angle += event.rel[0] * 360 / screen.get_width()
Minimal example
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
font = pygame.font.SysFont('consolas', 20)
clock = pygame.time.Clock()
angle = 0
text = font.render(f'Angle: {angle}°', True, (255, 255, 255))
pygame.mouse.set_pos(screen.get_rect().center)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
run = False
if event.type == pygame.MOUSEMOTION:
angle += event.rel[0] * 360 / screen.get_width()
text = font.render(f'Angle: {angle}°', True, (255, 255, 255))
pygame.mouse.set_pos(screen.get_rect().center)
screen.fill(0)
screen.blit(text, text.get_rect(center = screen.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()

How can i give my image an initial position before it starts moving with the mouse?

I'm working on this pygame game and i'm just getting started but got a bit confused because i want the image to move in the x-axis along with the mouse but when i run the program i want the image to show up at the center or the 'floor' but appears at the left side instead. This is my code and a screenshot of what's happening.
import pygame
import sys
pygame.init()
pygame.mixer.init()
WIDTH, HEIGHT = 400, 500
FPS = 60
TITLE = 'FOOD DROP'
SIZE = 190
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE_SKY = (152, 166, 255)
# Display
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
# Surfaces
floor_surface = pygame.Surface((WIDTH, 100))
floor_surface.fill(BLUE_SKY)
floor_rect = floor_surface.get_rect(midbottom=(200, 500))
# Images
LOAD_DITTO = pygame.image.load('Graphics/ditto.png')
DITTO = pygame.transform.scale(LOAD_DITTO, (SIZE, SIZE))
# Time
CLOCK = pygame.time.Clock()
class Figure:
def draw_figure(self, mouse_x):
SCREEN.blit(DITTO, (mouse_x - 90, 330))
# Game loop
SCREEN_UPDATE = pygame.USEREVENT
# main_game = Main()
figure = Figure()
running = True
while running:
CLOCK.tick(FPS)
mx, my = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
SCREEN.fill(WHITE)
SCREEN.blit(floor_surface, floor_rect)
figure.draw_figure(mx)
pygame.display.update()
When i run the program, this happens:
And i want the image to appear right at the center or the x-axis, not the border, i don't know why is this happening. Just to state, that screenshot was taken when the mouse hadn't been placed over the display.
If the mouse pointer is not in the window (out of focus), the initial position of the mouse pointer is (0, 0). Therefore pygame.mouse.get_pos returns (0, 0). It is also not possible to set the mouse position with pygame.mouse.set_pos if it is not in the window.
Initialize the variables mx and mx with the center of the window. Change the mouse position only when the mouse pointer is in the window (in focus). pygame.mouse.get_focused can be used to test whether the mouse is in the window.
mx, my = SCREEN.get_rect().center
running = True
while running:
CLOCK.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if pygame.mouse.get_focused():
mx, my = pygame.mouse.get_pos()
SCREEN.fill(WHITE)
SCREEN.blit(floor_surface, floor_rect)
figure.draw_figure(mx)
pygame.display.update()
pygame.quit()
sys.exit()

Fill only one half of a display in Pygame?

I am new to Pygame and I wish to fill only certain parts of my screen, for example the half. Currently I am only able to fill the complete screen. Can someone help?
import pygame
color= (255, 0, 0)
screen = pygame.display.set_mode((740, 780))
screen.fill(color)
The 2nd parameter of .fill() is a rectangle, which defines the area to be filled.
The width and height of a pygame.Surface object can be get by .get_width() respectively .get_height():
e.g.
screen.fill(color, (0, 0, screen.get_width()// 2, screen.get_height()))
import pygame
size = w,h = 300, 400
scr = pygame.display.set_mode((w,h))
pygame.display.set_caption("Hello")
scr.fill((0,255,0), rect=(0,0,w,h/2))
pygame.display.flip()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
One way to have partial fill may include "drawing" a shape (i.e. rectangle) on half of the screen.
import sys
import pygame
def half_screen():
#Initialize game and create screen object.
pygame.init()
color= (255, 0, 0)
screen = pygame.display.set_mode((200, 400))
#Draw rectangle to fill the left half of the screen.
left_half = pygame.draw.rect(screen, color,(0,0, 100, 400))
#Start loop for game- keeps screen open until you decide to quit.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
#Make the current screen visible.
pygame.display.flip()
half_screen()
You can find more information on the draw module - pygame.draw - by going to https://www.pygame.org/docs/ref/draw.html

Clicking on image will not work

I am using pygame to create a fully customizable enigma machine in python. One thing I decided to implement early is a help function. When I was testing this, nothing would show up on the console. Here is the code for the image clicking (not all of the code)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
pygame.display.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = event.pos
if img.get_rect().collidepoint(x, y):
print('test')
How do I make this work? All help would be useful.
When you call img.get_rect() you create a pygame.Rect with the size of the image/surface and the default topleft coordinates (0, 0), i.e. your rect is positioned at the top left corner of your screen. I suggest creating a rect instance for the img at the beginning of the program and use it as the blit position and for the collision detection. You can pass the topleft, center, x, y, etc., coordinates directly as an argument to get_rect: rect = img.get_rect(topleft=(200, 300)).
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
BG_COLOR = pg.Color('gray12')
img = pg.Surface((100, 50))
img.fill((0, 100, 200))
# Create a pygame.Rect with the size of the surface and
# the `topleft` coordinates (200, 300).
rect = img.get_rect(topleft=(200, 300))
# You could also set the coords afterwards.
# rect.topleft = (200, 300)
# rect.center = (250, 325)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if rect.collidepoint(event.pos):
print('test')
screen.fill(BG_COLOR)
# Blit the image/surface at the rect.topleft coords.
screen.blit(img, rect)
pg.display.flip()
clock.tick(60)
pg.quit()

Pygame moving image over rectangle with different colors

I am new to python and pygame and I'm trying to move a image over drawed rectangles with changing colors. Whenever I run this code the image moves but it creates a track of the image.
I know that I need to blit the background of the image in the game loop but how can I blit the background if the background is not an image?
Or do I need to draw my rectangles in a different way?
Full code:
import pygame
pygame.init()
screen = pygame.display.set_mode((600,200))
pygame.draw.rect(screen, (175,171,171), [0, 0, 600, 200])
pygame.draw.rect(screen, (255,192,0), [200, 0, 200, 200])
clock = pygame.time.Clock()
# load your own image here (preferably not wider than 30px)
truck = pygame.image.load('your_image.png').convert_alpha()
class Truck:
def __init__(self, image, x, y, speed):
self.speed = speed
self.image = image
self.pos = image.get_rect().move(x, y)
def move(self):
self.pos = self.pos.move(self.speed, 0)
def game_loop():
newTruck = Truck(truck, 0, 50, 1)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
newTruck.move()
screen.blit(newTruck.image, newTruck.pos)
clock.tick(60)
pygame.display.update()
game_loop()
You have to somehow redraw all the objects (rectangles) in your background at every update.
The simplest way to do this is just to call all the drawing code again, before updating your foreground object. Another way, if the background does not change after created, is to blit these background objects into a separate Surface object, and blit that one to the screen on every update.
More sophisticated ways would be to save the background under your foreground objects are prior to drawing them, and then, on the next redraw, first redraw the background and then save the background again and draw the foreground objects on the new position. It is easier to do one of the former methods.
Your code could be written like this:
import pygame
pygame.init()
SIZE = (600,200)
screen = pygame.display.set_mode(SIZE)
bg_image = None
def draw_background(screen):
global bg_image
if not bg_image:
bg_image = pygame.Surface((SIZE))
pygame.draw.rect(bg_image, (175,171,171), [0, 0, 600, 200])
pygame.draw.rect(bg_image, (255,192,0), [200, 0, 200, 200])
...
# Draw whatever you want inside this if body
screen.blit(bg_image, (0, 0))
...
class Truck:
...
def game_loop():
newTruck = Truck(truck, 0, 50, 1)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
newTruck.move()
draw_background()
screen.blit(newTruck.image, newTruck.pos)
clock.tick(60)
pygame.display.update()
game_loop()
You can just move the two lines that draw your rects into the main while loop:
def game_loop():
newTruck = Truck(truck, 0, 50, 1)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
newTruck.move()
# Draw the background items first, then the foreground images.
# If the rects don't cover the whole screen, you can use
# `screen.fill(some_color)` to clear it.
pygame.draw.rect(screen, (175,171,171), [0, 0, 600, 200])
pygame.draw.rect(screen, (255,192,0), [200, 0, 200, 200])
screen.blit(newTruck.image, newTruck.pos)
clock.tick(60)
pygame.display.update()
If the background should be static, you could also draw the rects onto a background surface once and then blit this surf onto the screen in the main loop as jsbueno suggests.

Categories

Resources