How can I check if circle on separate surface is clicked?
from pygame.locals import *
import pygame, sys, math
pygame.init()
screen = pygame.display.set_mode((640, 480))
s = pygame.Surface((100, 100))
s.fill((255, 0, 0))
s_rect = s.get_rect(center = (300, 300))
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
x = pygame.mouse.get_pos()[0]
y = pygame.mouse.get_pos()[1]
sqx = (x - 20)**2
sqy = (y - 20)**2
if math.sqrt(sqx + sqy) < 20:
print ('inside')
screen.fill((200, 200, 255))
screen.blit(s, s_rect)
pygame.draw.circle(s, (0, 0, 0), (20, 20), 20) # this doesn't work
pygame.draw.circle(screen, (0, 0, 0), (20, 20), 20) # this works
pygame.display.update()
pygame.quit()
Nothing happens when I run this code. However if I draw the circle on the main screen it works!!!
You have to compute the mouse position relative to the Surface.
You have 2 possibilities. Either subtract the (top left) position of the Surface on the screen from the mouse position:
x = event.pos[0] - s_rect.left
y = event.pos[1] - s_rect.top
Or add the (top left) position of the Surface on the screen to the center point of the circle:
sqx = (x - (s_rect.left + 20))**2
sqy = (y - (s_rect.top + 20))**2
Complete example
from pygame.locals import *
import pygame, math
pygame.init()
screen = pygame.display.set_mode((640, 480))
s = pygame.Surface((100, 100))
s.fill((255, 0, 0))
s_rect = s.get_rect(center = (300, 300))
clicked = False
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
x = event.pos[0]
y = event.pos[1]
sqx = (x - (s_rect.left + 20))**2
sqy = (y - (s_rect.top + 20))**2
if math.sqrt(sqx + sqy) < 20:
clicked = not clicked
print ('inside')
screen.fill((200, 200, 255))
color = (255, 255, 255) if clicked else (0, 0, 0)
pygame.draw.circle(s, color, (20, 20), 20)
screen.blit(s, s_rect)
pygame.display.update()
pygame.quit()
Related
I wanted to be able to "scroll" around a pygame window using just mouse gestures, or in this case, "two fingers scrolling" (don't know the right term for this).
I managed to make an example implementation:
import pygame
pygame.init()
size = (width, height) = (800, 600)
screen = pygame.display.set_mode(size)
class SceneElement:
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
class Scene:
def __init__(self):
self.elements = [
SceneElement(150, 150, 200, 200, (55, 55, 10, 0.3)),
SceneElement(250, 300, 200, 200, (155, 200, 10, 0.5)),
]
def render(self, offset):
screen.fill((255, 255, 255))
for element in self.elements:
x = element.x + offset[0]
y = element.y + offset[1]
pygame.draw.rect(screen, element.color, (x, y, element.width, element.height))
scene = Scene()
offset = [0, 0]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == 1027:
if event.x == 0 and event.y == -1:
print(event.x, event.y)
offset[1] -= 10
elif event.x == -1 and event.y == 0:
offset[0] += 10
print(event.x, event.y)
elif event.x == 1 and event.y == 0:
offset[0] -= 10
print(event.x, event.y)
elif event.x == 0 and event.y == 1:
offset[1] += 10
print(event.x, event.y)
scene.render(offset)
pygame.display.flip()
pygame.quit()
The above works. Here is a gif showing that it works.
Now problem is, I don't think this is how this is supposed to be implemented. I didn't see any example or existing code online that did this.
So while the above works, it doesn't feel "smooth". Trying to move in a circle or diagonally feels very unnatural (as can be seen in the gif near the end). Is there a better way to do the above (moving around using mouse gestures) or is this the right implementation?
Mainly what you have to deal with here is that your diagonal movement is not normalized, to fix this the easiest would be to just use pygame.Vector2 for positions and such, it also has a normalized method that will do the normalizing for you:
scene = Scene()
offset = pygame.Vector2()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEWHEEL:
direction = pygame.Vector2(event.x, event.y).normalize()
offset += direction * 10
scene.render(offset)
pygame.display.flip()
This won't affect functionality of the rest of the code as vectors can be indexed just like lists, however, I'd suggest you use pygame.Vector2 for positions and velocities and accelerations and other physics related things in general as they are faster and far more convenient.
Also use constants instead of some arbitrary integer values for event types and other stuff as it makes reading the code a lot easier.
Thanks to Matiiss's answer, I managed to find a clue on how to do this:
import pygame
pygame.init()
size = (width, height) = (800, 600)
screen = pygame.display.set_mode(size)
class SceneElement:
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
class Scene:
def __init__(self):
self.elements = [
SceneElement(150, 150, 200, 200, (55, 55, 10, 0.3)),
SceneElement(250, 300, 200, 200, (155, 200, 10, 0.5)),
]
def render(self, offset):
screen.fill((255, 255, 255))
for element in self.elements:
x = element.x + offset[0]
y = element.y + offset[1]
pygame.draw.rect(screen, element.color, (x, y, element.width, element.height))
scene = Scene()
offset = pygame.Vector2()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEWHEEL:
print(event.x, event.y)
if event.x == 0 and event.y == 1:
direction = pygame.Vector2(event.x, event.y).normalize()
offset += direction * 10
elif event.x == 0 and event.y == -1:
direction = pygame.Vector2(event.x, event.y).normalize()
offset += direction * 10
elif event.x == -1 and event.y == 0:
direction = pygame.Vector2(1, event.y).normalize()
offset += direction * 10
elif event.x == 1 and event.y == 0:
direction = pygame.Vector2(-1, event.y).normalize()
offset += direction * 10
scene.render(offset)
pygame.display.flip()
pygame.quit()
This seems to work much more smoothly, but I feel like this could be improved still: https://imgur.com/a/7WNNQIl
EDIT: Based on Matiiss's comment, using their answer and replacing event.x with -event.x seems to work like the above without if/elif:
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEWHEEL:
direction = pygame.Vector2(-event.x, event.y).normalize()
offset += direction * 10
scene.render(offset)
pygame.display.flip()
I use python3 and pygame and I have player class, in which I have my controls of the player defined as in the function:
def get_input(self):
#xbox example code, which doesnt work:
buttons=pygame.joystick.Joystick.get_button()
if buttons(button=0):
self.jump()
#more code for xbox controller.
#keyboard, this works perfectly:
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.direction.x = 1
elif keys[pygame.K_LEFT]:
self.direction.x = -1
elif keys[pygame.K_d]:
self.direction.x = 1
elif keys[pygame.K_a]:
self.direction.x = -1
else:
self.direction.x = 0
if (keys[pygame.K_LEFT] and keys[pygame.K_RIGHT]):
self.direction.x = 0
if (keys[pygame.K_SPACE] and self.on_ground):
self.jump()
Question how do I add XBOX controls as in example above for the keys? (I wanna use axis for left-right movement and one button for jumping)
This code for reason I don't understand gives me following error:
File [...], line 37, in get_input
buttons=pygame.joystick.Joystick.get_button()
AttributeError: 'builtin_function_or_method' object has no attribute 'get_button'
What's going on here?
You have to create an instance object of a pygame.joystick.Joystick.
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
rect = pygame.Rect(0, 0, 20, 20)
rect.center = window.get_rect().center
vel = 5
color = (255, 0, 0)
joystick = None
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
print("joystick initialized")
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:
print(pygame.key.name(event.key))
if joystick:
button = joystick.get_button(0)
rect.x += round(joystick.get_axis(0) * vel)
rect.y += round(joystick.get_axis(1) * vel)
if joystick.get_button(0):
color = (0, 255, 0)
elif joystick.get_button(1):
color = (255, 0, 0)
elif joystick.get_button(2):
color = (0, 0, 255)
elif joystick.get_button(3):
color = (255, 255, 0)
keys = pygame.key.get_pressed()
rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel
rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * vel
rect.centerx = rect.centerx % window.get_width()
rect.centery = rect.centery % window.get_height()
window.fill(0)
pygame.draw.rect(window, color, rect)
pygame.display.flip()
pygame.quit()
exit()
I want to make my character jump. In my current attempt, the player moves up as long as I hold down SPACEv and falls down when I release SPACE.
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
rect = pygame.Rect(135, 220, 30, 30)
vel = 5
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
rect.centerx = (rect.centerx + (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel) % 300
if keys[pygame.K_SPACE]:
rect.y -= 1
elif rect.y < 220:
rect.y += 1
window.fill((0, 0, 64))
pygame.draw.rect(window, (64, 64, 64), (0, 250, 300, 100))
pygame.draw.circle(window, (255, 0, 0), rect.center, 15)
pygame.display.flip()
pygame.quit()
exit()
However, I want the character to jump if I hit the SPACE once. I want a smooth jump animation to start when SPACE is pressed once.
How would I go about this step by step?
To make a character jump you have to use the KEYDOWN event, but not pygame.key.get_pressed(). pygame.key.get_pressed () is for continuous movement when a key is held down. The keyboard events are used to trigger a single action or to start an animation such as a jump. See alos How to get keyboard input in pygame?
pygame.key.get_pressed() returns a sequence with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
jump = True
Use pygame.time.Clock ("This method should be called once per frame.") you control the frames per second and thus the game speed and the duration of the jump.
clock = pygame.time.Clock()
while True:
clock.tick(100)
The jumping should be independent of the player's movement or the general flow of control of the game. Therefore, the jump animation in the application loop must be executed in parallel to the running game.
When you throw a ball or something jumps, the object makes a parabolic curve. The object gains height quickly at the beginning, but this slows down until the object begins to fall faster and faster again. The change in height of a jumping object can be described with the following sequence:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
Such a series can be generated with the following algorithm (y is the y coordinate of the object):
jumpMax = 10
if jump:
y -= jumpCount
if jumpCount > -jumpMax:
jumpCount -= 1
else:
jump = False
A more sophisticated approach is to define constants for the gravity and player's acceleration as the player jumps:
acceleration = 10
gravity = 0.5
The acceleration exerted on the player in each frame is the gravity constant, if the player jumps then the acceleration changes to the "jump" acceleration for a single frame:
acc_y = gravity
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if vel_y == 0 and event.key == pygame.K_SPACE:
acc_y = -acceleration
In each frame the vertical velocity is changed depending on the acceleration and the y-coordinate is changed depending on the velocity. When the player touches the ground, the vertical movement will stop:
vel_y += acc_y
y += vel_y
if y > ground_y:
y = ground_y
vel_y = 0
acc_y = 0
See also Jump
Example 1: replit.com/#Rabbid76/PyGame-Jump
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
rect = pygame.Rect(135, 220, 30, 30)
vel = 5
jump = False
jumpCount = 0
jumpMax = 15
run = True
while run:
clock.tick(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if not jump and event.key == pygame.K_SPACE:
jump = True
jumpCount = jumpMax
keys = pygame.key.get_pressed()
rect.centerx = (rect.centerx + (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel) % 300
if jump:
rect.y -= jumpCount
if jumpCount > -jumpMax:
jumpCount -= 1
else:
jump = False
window.fill((0, 0, 64))
pygame.draw.rect(window, (64, 64, 64), (0, 250, 300, 100))
pygame.draw.circle(window, (255, 0, 0), rect.center, 15)
pygame.display.flip()
pygame.quit()
exit()
Example 2: replit.com/#Rabbid76/PyGame-JumpAcceleration
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
player = pygame.sprite.Sprite()
player.image = pygame.Surface((30, 30), pygame.SRCALPHA)
pygame.draw.circle(player.image, (255, 0, 0), (15, 15), 15)
player.rect = player.image.get_rect(center = (150, 235))
all_sprites = pygame.sprite.Group([player])
y, vel_y = player.rect.bottom, 0
vel = 5
ground_y = 250
acceleration = 10
gravity = 0.5
run = True
while run:
clock.tick(100)
acc_y = gravity
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if vel_y == 0 and event.key == pygame.K_SPACE:
acc_y = -acceleration
keys = pygame.key.get_pressed()
player.rect.centerx = (player.rect.centerx + (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel) % 300
vel_y += acc_y
y += vel_y
if y > ground_y:
y = ground_y
vel_y = 0
acc_y = 0
player.rect.bottom = round(y)
window.fill((0, 0, 64))
pygame.draw.rect(window, (64, 64, 64), (0, 250, 300, 100))
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
exit()
I want to make my character jump. In my current attempt, the player moves up as long as I hold down SPACEv and falls down when I release SPACE.
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
rect = pygame.Rect(135, 220, 30, 30)
vel = 5
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
rect.centerx = (rect.centerx + (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel) % 300
if keys[pygame.K_SPACE]:
rect.y -= 1
elif rect.y < 220:
rect.y += 1
window.fill((0, 0, 64))
pygame.draw.rect(window, (64, 64, 64), (0, 250, 300, 100))
pygame.draw.circle(window, (255, 0, 0), rect.center, 15)
pygame.display.flip()
pygame.quit()
exit()
However, I want the character to jump if I hit the SPACE once. I want a smooth jump animation to start when SPACE is pressed once.
How would I go about this step by step?
To make a character jump you have to use the KEYDOWN event, but not pygame.key.get_pressed(). pygame.key.get_pressed () is for continuous movement when a key is held down. The keyboard events are used to trigger a single action or to start an animation such as a jump. See alos How to get keyboard input in pygame?
pygame.key.get_pressed() returns a sequence with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
jump = True
Use pygame.time.Clock ("This method should be called once per frame.") you control the frames per second and thus the game speed and the duration of the jump.
clock = pygame.time.Clock()
while True:
clock.tick(100)
The jumping should be independent of the player's movement or the general flow of control of the game. Therefore, the jump animation in the application loop must be executed in parallel to the running game.
When you throw a ball or something jumps, the object makes a parabolic curve. The object gains height quickly at the beginning, but this slows down until the object begins to fall faster and faster again. The change in height of a jumping object can be described with the following sequence:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
Such a series can be generated with the following algorithm (y is the y coordinate of the object):
jumpMax = 10
if jump:
y -= jumpCount
if jumpCount > -jumpMax:
jumpCount -= 1
else:
jump = False
A more sophisticated approach is to define constants for the gravity and player's acceleration as the player jumps:
acceleration = 10
gravity = 0.5
The acceleration exerted on the player in each frame is the gravity constant, if the player jumps then the acceleration changes to the "jump" acceleration for a single frame:
acc_y = gravity
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if vel_y == 0 and event.key == pygame.K_SPACE:
acc_y = -acceleration
In each frame the vertical velocity is changed depending on the acceleration and the y-coordinate is changed depending on the velocity. When the player touches the ground, the vertical movement will stop:
vel_y += acc_y
y += vel_y
if y > ground_y:
y = ground_y
vel_y = 0
acc_y = 0
See also Jump
Example 1: replit.com/#Rabbid76/PyGame-Jump
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
rect = pygame.Rect(135, 220, 30, 30)
vel = 5
jump = False
jumpCount = 0
jumpMax = 15
run = True
while run:
clock.tick(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if not jump and event.key == pygame.K_SPACE:
jump = True
jumpCount = jumpMax
keys = pygame.key.get_pressed()
rect.centerx = (rect.centerx + (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel) % 300
if jump:
rect.y -= jumpCount
if jumpCount > -jumpMax:
jumpCount -= 1
else:
jump = False
window.fill((0, 0, 64))
pygame.draw.rect(window, (64, 64, 64), (0, 250, 300, 100))
pygame.draw.circle(window, (255, 0, 0), rect.center, 15)
pygame.display.flip()
pygame.quit()
exit()
Example 2: replit.com/#Rabbid76/PyGame-JumpAcceleration
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
player = pygame.sprite.Sprite()
player.image = pygame.Surface((30, 30), pygame.SRCALPHA)
pygame.draw.circle(player.image, (255, 0, 0), (15, 15), 15)
player.rect = player.image.get_rect(center = (150, 235))
all_sprites = pygame.sprite.Group([player])
y, vel_y = player.rect.bottom, 0
vel = 5
ground_y = 250
acceleration = 10
gravity = 0.5
run = True
while run:
clock.tick(100)
acc_y = gravity
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if vel_y == 0 and event.key == pygame.K_SPACE:
acc_y = -acceleration
keys = pygame.key.get_pressed()
player.rect.centerx = (player.rect.centerx + (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel) % 300
vel_y += acc_y
y += vel_y
if y > ground_y:
y = ground_y
vel_y = 0
acc_y = 0
player.rect.bottom = round(y)
window.fill((0, 0, 64))
pygame.draw.rect(window, (64, 64, 64), (0, 250, 300, 100))
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
exit()
This question already has an answer here:
How can I move the ball instead of leaving a trail all over the screen in pygame?
(1 answer)
Closed 1 year ago.
I would like to draw a white circle moving naturally on the screen, but I get lots of white circles drawn in a discontinuous way without giving the illusion of movement. How can I modify this code?
import pygame
from sys import exit
import math
BLACK = [0, 0, 0]
WHITE = [255, 255, 255]
class Game():
def __init__(self):
pygame.init()
self.window = pygame.display.set_mode((500, 500), 0, 32)
pygame.display.set_caption('Jumping ball')
self.clock = pygame.time.Clock()
def mainloop(self):
while True:
EVENTS = pygame.event.get()
for event in EVENTS:
if event.type == pygame.QUIT:
exit()
time = self.clock.tick(60) # [seconds]
x, y = 100 + 50 * math.sin(time), 100 + 50 * math.cos(time)
x, y = int(x), int(y)
radius = 5
pygame.draw.circle(self.window, WHITE, (x, y), radius)
#self.screen.blit(ball, (x, y))
pygame.display.update()
if __name__ == '__main__':
game = Game()
game.mainloop()
You have to first fill the screen in black and then draw on it again.
def mainloop(self):
while True:
EVENTS = pygame.event.get()
for event in EVENTS:
if event.type == pygame.QUIT:
exit()
time = self.clock.tick(60) # [seconds]
x, y = 100 + 50 * math.sin(time), 100 + 50 * math.cos(time)
x, y = int(x), int(y)
radius = 5
self.window.fill((0, 0, 0)) #Black
pygame.draw.circle(self.window, WHITE, (x, y), radius)
#self.screen.blit(ball, (x, y))
You should clear the screen every tick, e.g. by filling it with a solid color.
Also, you set x and y new every tick, which does not allow you to create most movement patterns. I suggest introducing x and y variables that you can modify in small increments each tick, like so:
import pygame
from sys import exit
import math
BLACK = [0, 0, 0]
WHITE = [255, 255, 255]
class Game():
def __init__(self):
pygame.init()
self.window = pygame.display.set_mode((500, 500), 0, 32)
pygame.display.set_caption('Jumping ball')
self.clock = pygame.time.Clock()
self.ball_x = 100
self.ball_y = 100
def mainloop(self):
while True:
self.window.fill(BLACK)
EVENTS = pygame.event.get()
for event in EVENTS:
if event.type == pygame.QUIT:
exit()
time = self.clock.tick(60) # [seconds]
self.ball_x -= math.sin(time)
self.ball_y -= math.cos(time)
x, y = int(self.ball_x), int(self.ball_y)
radius = 5
pygame.draw.circle(self.window, WHITE, (x, y), radius)
# self.screen.blit(ball, (x, y))
pygame.display.update()
if __name__ == '__main__':
game = Game()
game.mainloop()