I have been struggling for few days know trying to figure out how to make for example a image move in circular path. I have looked other posts here but i just can't get it.
So how do i move image in circular path. My code only moves my image 45 degrees and stops then. I would need it to go full circle and continue doing it.
Current Code:
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 400))
CENTER = (200, 200)
RADIUS = 100
x = 0
y = 0
satelliteCenter = (CENTER[0]+RADIUS, CENTER[1])
run = 1
while run == 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = 0
pygame.quit()
mouse = pygame.mouse.get_pos()
vector = x-CENTER[0], y-CENTER[1]
x +=1
distance = (vector[0]**2 + vector[1]**2)**0.5
if distance > 0:
scalar = RADIUS / distance
satelliteCenter = (
int(round( CENTER[0] + vector[0]*scalar )),
int(round( CENTER[1] + vector[1]*scalar )) )
screen.fill((255,255,255))
pygame.draw.circle(screen, (71,153,192), CENTER, RADIUS)
pygame.draw.circle(screen, (243,79,79), satelliteCenter, 16)
pygame.display.update()
You can just use a pygame.math.Vector2 and rotate it each frame, scale it by the radius and add it to the CENTER position to get the current center of the small circle.
import sys
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
CENTER = (200, 200)
RADIUS = 100
# A unit vector pointing to the right.
direction = pygame.math.Vector2(1, 0)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
direction.rotate_ip(4) # By 4 degrees.
# Normalize it, so that the length doesn't change because
# of floating point inaccuracies.
direction.normalize_ip()
# Scale direction vector, add it to CENTER and convert to ints.
ball_pos = [int(i) for i in CENTER+direction*RADIUS]
screen.fill((255,255,255))
pygame.draw.circle(screen, (71,153,192), CENTER, RADIUS)
pygame.draw.circle(screen, (243,79,79), ball_pos, 16)
pygame.display.update()
clock.tick(30)
Edit: If you want the red ball to follow the mouse, then your example actually works if you set x and y to the mouse pos x, y = pygame.mouse.get_pos().
import sys
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 400))
CENTER = (200, 200)
RADIUS = 100
x = 0
y = 0
satelliteCenter = (CENTER[0]+RADIUS, CENTER[1])
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
x, y = pygame.mouse.get_pos()
vector = x-CENTER[0], y-CENTER[1]
distance = (vector[0]**2 + vector[1]**2)**0.5
if distance > 0:
scalar = RADIUS / distance
satelliteCenter = (
int(round( CENTER[0] + vector[0]*scalar )),
int(round( CENTER[1] + vector[1]*scalar ))
)
screen.fill((255,255,255))
pygame.draw.circle(screen, (71,153,192), CENTER, RADIUS)
pygame.draw.circle(screen, (243,79,79), satelliteCenter, 16)
pygame.display.update()
Related
I'm a beginner programmer who is starting with python and I'm starting out by making a game in pygame.
The game basically spawns circles at random positions and when clicked, it gives you points.
Recently I've hit a roadblock when I want to spawn multiple instances of the same object (in this case circles) at the same time.
I've tried stuff like sleep() and some other code related to counters, but it always results in the next circle spawned overriding the previous one (i.e the program spawns circle 1, but when circle 2 comes in, circle 1 disappears).
Does anyone know a solution to this? I would really appreciate your help!
import pygame
import random
import time
pygame.init()
window = pygame.display.set_mode((800,600))
class circle():
def __init__(self, color, x, y, radius, width,):
self.color = color
self.x = x
self.y = y
self.radius = radius
self.width = width
def draw(self, win, outline=None):
pygame.draw.circle(win, self.color, (self.x, self.y, self.radius, self.width), 0)
run=True
while run:
window.fill((0, 0, 0))
pygame.draw.circle(window, (255, 255, 255), (random.randint(0, 800),random.randint(0, 600)), 20, 20)
time.sleep(1)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run=False
pygame.quit()
quit()
It does not work that way. time.sleep, pygame.time.wait() or pygame.time.delay is not the right way to control time and gameplay within an application loop. The game does not respond while you wait. The application loop runs continuously. You have to measure the time in the loop and spawn the objects according to the elapsed time.
pygame.Surface.fill clears the entire screen. Add the newly created objects to a list. Redraw all of the objects and the entire scene in each frame.
See also Time, timer event and clock
You have 2 options. Use pygame.time.get_ticks() to measure the time. Define a time interval after which a new object should appear. Create an object when the point in time is reached and calculate the point in time for the next object:
object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
next_object_time = 0
while run:
# [...]
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
Minimal example:
repl.it/#Rabbid76/PyGame-TimerSpawnObjects
import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))
class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)
object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
next_object_time = 0
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()
pygame.quit()
exit()
The other option is to use the pygame.event module. Use pygame.time.set_timer() to repeatedly create a USEREVENT in the event queue. The time has to be set in milliseconds. e.g.:
object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT (24) and pygame.NUMEVENTS (32). In this case pygame.USEREVENT+1 is the event id for the timer event.
Receive the event in the event loop:
while run:
for event in pygame.event.get():
if event.type == timer_event:
object_list.append(Object())
The timer event can be stopped by passing 0 to the time argument of pygame.time.set_timer.
Minimal example:
repl.it/#Rabbid76/PyGame-TimerEventSpawn
import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))
class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)
object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
object_list.append(Object())
window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()
pygame.quit()
exit()
I have an image in pygame. I want to apply antialiasing to it. How can I do this? Ideally, I would be able to do this just with pygame and built-in modules but I'm open to other options if necessary.
More specifically, I've got an image of a square split into 4. Each of the quadrants has a different colour. I want to blur it so it looks more like a gradient, so instead of the colours switching instantly where the quadrants meet, it would fade slowly. I believe anti-aliasing is the best way to accomplish this? Here's an image of what I mean:left: what I've got, right: what I need to have
It's not clear what specific image smoothing algorithm you want to use. SciPy provides some relevant functions, so I've constructed an example using ndimage.gaussian_filter().
Clicking a mouse button will apply the filter, or you can scroll the wheel.
import pygame
from scipy.ndimage import gaussian_filter
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html
def smooooooth(screen):
"""Apply a gaussian filter to each colour plane"""
# Get reference pixels for each colour plane and then apply filter
r = pygame.surfarray.pixels_red(screen)
gaussian_filter(r, sigma=72, mode="nearest", output=r)
g = pygame.surfarray.pixels_green(screen)
gaussian_filter(g, sigma=72, mode="nearest", output=g)
b = pygame.surfarray.pixels_blue(screen)
gaussian_filter(b, sigma=72, mode="nearest", output=b)
height = 300
width = 300
pygame.init()
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
pygame.display.set_caption("Smooth Image")
clock = pygame.time.Clock()
# draw initial pattern
screen.fill((194, 198, 199)) # fill with gray
# fill top left quadrant with white
screen.fill(pygame.Color("white"), pygame.Rect(0, 0, width // 2, height // 2))
# fill bottom left quadrant with black
screen.fill(pygame.Color("black"), pygame.Rect(width // 2, height // 2, width, height))
count = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONUP:
smooooooth(screen)
count += 1
pygame.display.set_caption(f"Smooth Image {count}")
pygame.display.update()
# limit framerate
clock.tick(30)
pygame.quit()
If you play with the sigma argument to the gaussian_filter function, you might be able to get closer to what you're after. E.g. with sigma 72, two passes looks like this:
I've modified the example code to properly support screen resizing, which will redraw the base pattern and using the + and - keys to increment the sigma value so you can do it interactively. Hold Shift to adjust by ten instead of one.
import pygame
from scipy.ndimage import gaussian_filter
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html
def smooooooth(screen, sisgma):
"""Apply a gaussian filter to each colour plane"""
# Get reference pixels for each colour plane and then apply filter
r = pygame.surfarray.pixels_red(screen)
gaussian_filter(r, sigma=sigma, mode="nearest", output=r)
g = pygame.surfarray.pixels_green(screen)
gaussian_filter(g, sigma=sigma, mode="nearest", output=g)
b = pygame.surfarray.pixels_blue(screen)
gaussian_filter(b, sigma=sigma, mode="nearest", output=b)
def draw_grey_squares(screen):
"""Draw the base pattern"""
screen.fill((194, 198, 199)) # fill with grey
# fill top left quadrant with white
screen.fill(pygame.Color("white"), (0, 0, width // 2, height // 2))
# fill bottom left quadrant with black
screen.fill(pygame.Color("black"), (width // 2, height // 2, width, height))
height = 300
width = 300
pygame.init()
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
draw_grey_squares(screen)
pygame.display.set_caption("Smooth Image")
clock = pygame.time.Clock()
sigma = 72
count = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.VIDEORESIZE:
# redraw on window resize
width, height = event.w, event.h
draw_grey_squares(screen)
count = 0
elif event.type == pygame.KEYUP:
# support + and - to modify sigma
if event.key in (pygame.K_PLUS, pygame.K_KP_PLUS):
if event.mod & pygame.KMOD_SHIFT:
sigma += 10
else:
sigma += 1
elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
if event.mod & pygame.KMOD_SHIFT:
sigma -= 10
else:
sigma -= 1
sigma = max(sigma, 1) # sigma below one doesn't make sense
elif event.type == pygame.MOUSEBUTTONUP:
smooooooth(screen, sigma)
count += 1
pygame.display.set_caption(f"Smooth Image {count} σ {sigma}")
pygame.display.update()
clock.tick(30) # limit framerate
pygame.quit()
Here's an example with sigma 200:
This question already has an answer here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
Closed 1 year ago.
I read an article about how a mouse cursor can detect a rect, and it includes the line ".get_rect()" but somehow it doesnt work
heres the articles code ->
import pygame
pygame.init()
width=350;
height=400
screen = pygame.display.set_mode( (width, height ) )
pygame.display.set_caption('clicked on image')
redSquare = pygame.image.load("images/red-square.png").convert()
x = 20; # x coordnate of image
y = 30; # y coordinate of image
screen.blit(redSquare , ( x,y)) # paint to screen
pygame.display.flip() # paint screen one time
running = True
while (running):
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
# Set the x, y postions of the mouse click
x, y = event.pos
if redSquare.get_rect().collidepoint(x, y):
print('clicked on image')
#loop over, quite pygame
pygame.quit()
heres my code ->
import pygame
import os
import sys
pygame.init()
width,height = (1100,800)
WIN = pygame.display.set_mode((width,height))
global bcard
bg_filename = os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','background.jpg')
bg = pygame.image.load(bg_filename)
bg = pygame.transform.scale(bg, (width, height)).convert()
card_width=130
card_height=160
blue_card=pygame.image.load(os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','blue_card.png'))
red_card=pygame.image.load(os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','red_card.png'))
bcard=pygame.transform.scale(blue_card,(card_width,card_height)).convert()
rcard=pygame.transform.scale(red_card,(card_width,card_height)).convert()
text=pygame.image.load(os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','text.png'))
global clicking
clicking = False
def pictures():
global card1
card1=WIN.blit(bcard,(30,200))
card2=WIN.blit(rcard,(200,200))
card3=WIN.blit(bcard,(370,200))
card4=WIN.blit(rcard,(550,200))
card5=WIN.blit(bcard,(730,200))
card6=WIN.blit(rcard,(900,200))
card7=WIN.blit(rcard,(30,400))
card8=WIN.blit(bcard,(200,400))
card9=WIN.blit(rcard,(370,400))
card10=WIN.blit(bcard,(550,400))
card11=WIN.blit(rcard,(730,400))
card12=WIN.blit(bcard,(900,400))
card13=WIN.blit(bcard,(30,600))
card14=WIN.blit(rcard,(200,600))
card15=WIN.blit(bcard,(370,600))
card16=WIN.blit(rcard,(550,600))
card17=WIN.blit(bcard,(730,600))
card18=WIN.blit(rcard,(900,600))
card1_rect=pygame.Rect(30,200,130,160)
card1_rect=pygame.Rect(200,200,130,160)
card1_rect=pygame.Rect(370,200,130,160)
card1_rect=pygame.Rect(550,200,130,160)
card1_rect=pygame.Rect(730,200,130,160)
card1_rect=pygame.Rect(900,200,130,160)
WIN.blit(text,(25,0))
def draw():
WIN.blit(bg,(0,0))
pictures()
def main():
global clicking
global card1
global bcard
run = True
mx , my = pygame.mouse.get_pos()
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if bcard.get_rect().collidepoint(mx, my):
print('clicked on image')
draw()
pygame.display.flip()
main()
its suppose to be a picture match game btw, heres the error code "AttributeError: 'pygame.Surface' object has no attribute 'get_rect'"
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. A Surface is blit at a position on the screen. The position of the rectangle can be specified by a keyword argument. For example, the top left of the rectangle can be specified with the keyword argument topleft:
if bcard.get_rect().collidepoint(mx, my):
if bcard.get_rect(topleft = (30, 200)).collidepoint(mx, my):
print('clicked on image')
I have been working on a pygame project where a player controls a rectangle that hits a ball. When the ball hit the side walls, it bounces off perfectly, same with the floor, but when it hits the top, it works very weirdly in a way I can't describe. If anyone wants to test this, my code is below, just hit the ball onto the roof and it will show what I am trying to explain. I would like it so you can't ram the ball into the ceiling so it goes off the screen and for it to be a clean bounce instead of what it dose right now and sort of rolls down if it touches the ceiling.
import pygame as pg
from pygame.math import Vector2
pg.init()
LIGHTBLUE = pg.Color('lightskyblue2')
DARKBLUE = pg.Color(11, 8, 69)
screen = pg.display.set_mode((800, 600))
width, height = screen.get_size()
clock = pg.time.Clock()
# You need surfaces with an alpha channel for the masks.
bluecar = pg.Surface((60, 30), pg.SRCALPHA)
bluecar.fill((0,0,255))
BALL = pg.Surface((30, 30), pg.SRCALPHA)
pg.draw.circle(BALL, [0,0,0], [15, 15], 15)
ball_pos = Vector2(395, 15)
ballrect = BALL.get_rect(center=ball_pos)
ball_vel = Vector2(0, 0)
mask_blue = pg.mask.from_surface(bluecar)
mask_ball = pg.mask.from_surface(BALL)
pos_blue = Vector2(740, 500) # Just use the pos vector instead of x, y.
bluerect = bluecar.get_rect(center = pos_blue)
vel_blue = Vector2(0, 0) # Replace x_change, y_change with vel_blue.
# A constant value that you add to the y-velocity each frame.
GRAVITY = .5
on_ground = False
ground_y = height - 100
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_a:
vel_blue.x = -5
elif event.key == pg.K_d:
vel_blue.x = 5
elif event.key == pg.K_w:
#if on_ground: # Only jump if the player is on_ground.
vel_blue.y = -12
on_ground = False
elif event.type == pg.KEYUP:
if event.key == pg.K_a and vel_blue.x < 0:
vel_blue.x = 0
elif event.key == pg.K_d and vel_blue.x > 0:
vel_blue.x = 0
ball_vel.y += GRAVITY # Accelerate downwards.
ball_pos += ball_vel # Move the ball.
ballrect.center = ball_pos # Update the rect.
# Bounce when the ball touches the bottom of the screen.
if ballrect.bottom >= ground_y:
# Just invert the y-velocity to bounce.
ball_vel.y *= -0.7 # Change this value to adjust the elasticity.
ball_vel.x *= .95 # Friction
# Don't go below the ground.
ballrect.bottom = ground_y
ball_pos.y = ballrect.centery
# Left and right wall collisions.
if ballrect.left < 0:
ball_vel.x *= -1
ballrect.left = 0
ball_pos.x = ballrect.centerx
elif ballrect.right > width:
ball_vel.x *= -1
ballrect.right = width
ball_pos.x = ballrect.centerx
if ballrect.top <= 0:
# Just invert the y-velocity to bounce.
ball_vel.y *= 0.4 # Change this value to adjust the elasticity.
# Add the GRAVITY value to vel_blue.y, so that
# the object moves faster each frame.
vel_blue.y += GRAVITY
pos_blue += vel_blue
bluerect.center = pos_blue # You have to update the rect as well.
# Stop the object when it's near the bottom of the screen.
if bluerect.bottom >= ground_y:
bluerect.bottom = ground_y
pos_blue.y = bluerect.centery
vel_blue.y = 0
on_ground = True
if bluerect.x < 0:
bluerect.x = 0
pos_blue.x = bluerect.centerx
elif bluerect.right > width:
bluerect.right = width
pos_blue.x = bluerect.centerx
offset_blue = bluerect[0] - ballrect[0], bluerect[1] - ballrect[1]
overlap_blue = mask_ball.overlap(mask_blue, offset_blue)
if overlap_blue: # Blue collides with the ball.
if vel_blue.x != 0: # Player is moving.
ball_vel = Vector2(vel_blue.x, -17)
else: # If the player is standing, I just update the vel.y.
ball_vel.y = -17
# Draw everything.
screen.fill(LIGHTBLUE)
pg.draw.line(screen, (0, 0, 0), (0, ground_y), (width, ground_y))
screen.blit(bluecar, bluerect) # Blit it at the rect.
screen.blit(BALL, ballrect)
pg.display.update()
clock.tick(60)
pg.quit()
Just set the ballrect.top coordinate to 1, so that it stays in the game area and also update the ball_pos.y afterwards.
if ballrect.top <= 0:
# Just invert the y-velocity to bounce.
ball_vel.y *= -0.4 # Change this value to adjust the elasticity.
ballrect.top = 1
ball_pos.y = ballrect.centery
As the background scrolls along the side the the end of the image stretches and the rest of the image doesn't appear just the same hill elongated. It then suddenly resets. Also a portion of the image (a rectangle)is displaced from the rest of the image making it uneven and stays like that until it disappears from view.
Here is the code I use to side scroll
import pygame, sys, time, random
from pygame.locals import *
class Background(pygame.sprite.Sprite): #Creates space background
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.bgimage = pygame.image.load(image_file)
self.bgimage = pygame.transform.scale(self.bgimage, (1333, 600))
self.rectBGimg = self.bgimage.get_rect()
self.bgY1 = 0
self.bgX1 = 0
self.bgY2 = 0
self.bgX2 = self.rectBGimg.width
self.movingUpSpeed = 5
def update(self):
self.bgX1 -= self.movingUpSpeed
self.bgX2 -= self.movingUpSpeed
if self.bgX1 <= -self.rectBGimg.height:
self.bgX1 = self.rectBGimg.height
if self.bgX2 <= -self.rectBGimg.height:
self.bgY2 = self.rectBGimg.height
def render(self):
screen.blit(self.bgimage, (self.bgX1, self.bgY1))
screen.blit(self.bgimage, (self.bgX2, self.bgY2))
pygame.init()
FPS = 15 # frames per second setting
clock = pygame.time.Clock()
window_width = 1333
window_height = 600
# set up the window
screen = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption('Deterred Journey')
BackGround = Background('scrollingBackground.png', [0,0])
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#Adds images and text
BackGround.render()
BackGround.update()
pygame.display.flip()
pygame.display.update()
clock.tick(30)
pygame.quit()
sys.exit()
Looks like my update function was checking bgX1 and bgX2 against height causing displacement and I set self.bgY2 = self.rectBGimg.heightcausing elongation. Since it's scrolling vertically bgX2 must be set to the width
updated function should look like
def update(self):
self.bgX1 -= self.movingUpSpeed
self.bgX2 -= self.movingUpSpeed
if self.bgX1 <= -self.rectBGimg.width:
self.bgX1 = self.rectBGimg.width
if self.bgX2 <= -self.rectBGimg.width:
self.bgX2 = self.rectBGimg.width