Python Pygame collision detection between objects [duplicate] - python

This question already has answers here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
How do I detect collision in pygame?
(5 answers)
Closed 2 years ago.
Hi sorry for the many questions but again I know python but am trying to learn pygame. In the game I'm making im trying to make it so when a block/image touches the sprite it goes back to the top of the screen. I have looked at many tutorials but can't seem to figure it our in a way that would work good. Any advice? Heres the code thanks!!!:
import pygame
import sys
from random import randint
import os
x = 250
y = 30
os.environ["SDL_VIDEO_WINDOW_POS"] = "%d,%d" % (x, y)
width = 1024
height = 768
icon1 = pygame.image.load("Firstpygamegame/santa-claus.png")
pygame.display.set_icon(icon1)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Gift Catcher")
background_image = pygame.image.load("Firstpygamegame/wintervillage.png")
sprite1 = pygame.image.load("Firstpygamegame/santabag2.png")
spriterect = sprite1.get_rect()
speed = 2.5
# 442 or 467
spriterect.x = 442
icon2 = pygame.image.load("Firstpygamegame/present1.png")
icon3 = pygame.image.load("Firstpygamegame/present2.png")
icon4 = pygame.image.load("Firstpygamegame/present3.png")
icon5 = pygame.image.load("Firstpygamegame/present4.png")
cubes = [[
randint(1, 1000), # X coordinate
randint(-1500, -350)] # Y coordinate, -Y is above screen (top of screen is zero)
for x in range(2)] # 20 cubes
cubes1 = [[
randint(1, 1000), # X coordinate
randint(-1500, -150)] # Y coordinate, -Y is above screen (top of screen is zero)
for x in range(2)] # 20 cubes
cubes2 = [[
randint(1, 1000), # X coordinate
randint(-1500, -550)] # Y coordinate, -Y is above screen (top of screen is zero)
for x in range(2)] # 20 cubes
cubes3 = [[
randint(1, 1000), # X coordinate
randint(-1500, -450)] # Y coordinate, -Y is above screen (top of screen is zero)
for x in range(2)] # 20 cubes
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
spriterect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * speed
spriterect.y = 600
screen.blit(background_image, (0, 0))
screen.blit(sprite1, spriterect)
for cb in cubes:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon2, cb) # draw cube
if cb[1] > 800: # if cube passed bottom of screen
cb[1] = -100 # move to above screen
cb[0] = randint(1, 1000)
for cb in cubes1:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon3, cb) # draw cube
if cb[1] > 800: # if cube passed bottom of screen
cb[1] = -100 # move to above screen
cb[0] = randint(1, 1000) # random X position
for cb in cubes2:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon4, cb) # draw cube
if cb[1] > 800: # if cube passed bottom of screen
cb[1] = -100 # move to above screen
cb[0] = randint(1, 1000) # random X position
for cb in cubes3:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon5, cb) # draw cube
if cb[1] > 800: # if cube passed bottom of screen
cb[1] = -100 # move to above screen
cb[0] = randint(1, 1000) # random X position
pygame.display.flip()

Use a pygame.Rect object and colliderect() to find a collision between a rectangle and an object.
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. The position of the rectangle can be specified by a keyword argument:
while running:
# [...]
for cb in cubes:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon2, cb) # draw cube
icon_rect = icon2.get_rect(topleft = cb)
if cb[1] > 800 or icon_rect.colliderect(spriterect):
cb[1] = -100 # move to above screen
cb[0] = randint(1, 1000)
# [...]
However, you can simplify your code:
icon_list = [icon2, icon3, icon4, icon5]
cube_lists = [[[
randrange(screen.get_width() - icon.get_width()),
randint(-1500, -350)]
for x in range(2)]
for icon in icon_list]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
spriterect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * speed
spriterect.y = 600
screen.blit(background_image, (0, 0))
screen.blit(sprite1, spriterect)
for icon, cubes in zip(icon_list, cube_lists):
for cb in cubes:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon, cb) # draw cube
icon_rect = icon.get_rect(topleft = cb)
if cb[1] > screen.get_height() or icon_rect.colliderect(spriterect):
cb[:] = randrange(screen.get_width() - icon.get_width()), -800
pygame.display.flip()
pygame.quit()
sys.exit()
Please note, the application loop terminates if running is False. There is no point in setting running = False and to call pygame.quit() and sys.exit() in the event loop. Let the loop run to the end and call pygame.quit() and sys.exit() after the event loop.

Related

Generate tiles in piano tiles consecutively in pygame

I have created a simple piano tiles game clone in pygame.
Everything working fine except the way i am generating tiles after every certain interval, but as the game speed increases this leaves a gap between two tiles.
In the original version of the game, there's no lag (0 distance ) between two incoming tiles.
Here's a preview of the game:
Currently I am generating tiles like this:
ADDBLOCK = pygame.USEREVENT + 1
ADDTIME = 650
pygame.time.set_timer(ADDBLOCK, ADDTIME)
if event.type == ADDBLOCK:
x_col = random.randint(0,3)
block = Block(win, (67.5 * x_col, -120))
block_group.add(block)
But with time, these tiles speed increase so there's remain a gap between generation of two tiles as shown by red line in the preview. Is there any way to generate tiles consecutively?
Source Code
Use a variable number to know how many tiles have been generated since the start. This variable will start with 0, then you will add 1 to this variable every time a tile is generated.
Then, you can use a variable like scrolling which increases continuously. You will add this scrolling to every tile y pos to render them.
Now you just have to add a tile which y position is like -tile_height - tile_height * number.
If that doesn't make sense to you, look at this MRE:
import pygame
from pygame.locals import *
from random import randint
pygame.init()
screen = pygame.display.set_mode((240, 480))
clock = pygame.time.Clock()
number_of_tiles = 0
tile_height = 150
tile_surf = pygame.Surface((60, tile_height))
tiles = [] # [column, y pos]
scrolling = 0
score = 0
speed = lambda: 200 + 5*score # increase with the score
time_passed = 0
while True:
click = None # used to click on a tile
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
if event.type == MOUSEBUTTONDOWN:
click = event.pos
screen.fill((150, 200, 255))
if scrolling > number_of_tiles * tile_height:
# new tile
# use "while" instead of "if" if you want to go at really high speed
tiles.append([randint(0, 3), -tile_height - number_of_tiles * tile_height])
number_of_tiles += 1
for x, y in tiles:
screen.blit(tile_surf, (60 * x, y + scrolling))
if y + scrolling > 480: # delete any tile that is no longer visible
tiles.remove([x, y])
if click is not None and Rect((60 * x, y + scrolling), tile_surf.get_size())
.collidepoint(click):
tiles.remove([x, y]) # delete any tile that has been clicked
score += 1 # used to calculate speed
scrolling += speed() * time_passed
pygame.display.flip()
time_passed = clock.tick() / 1000

How can i make a block follow another block in pygame [duplicate]

This question already has answers here:
How to make smooth movement in pygame
(2 answers)
Closed 2 years ago.
I have two blocks, one is controlled by the user. When i move my block, i want the other block to follow me. I tried doing something like this
def follow():
distance = math.hypot(abs(m.x - p.x), abs(m.y - p.y))
angle_radians = math.atan2(abs(m.y - p.y), abs(m.x - p.x))
if distance != 0:
p.y += math.sin(angle_radians)
p.x += math.cos(angle_radians)
However, the block ends up moving in the complete opposite direction to me . Any help would be appreciated.
To make the algorithm work, you have to operate with floating point numbers. If m and p are pygame.Rect objects, then the algorithm won't work, pygame.Rect operates with integral numbers and the fraction part gets lost.
Note math.sin(angle_radians) and math.cos(angle_radians) is <= 1.
That means you have to store the positions of the objects in separate variables. Let's assume you have the floating point coordinates (mx, my) and (py, py)
You have to find the Unit vector from (mx, my) to (px, py).
The unit vector can be found by dividing the vector from (mx, m.y) to (px, py) by its length.
The length of a vector can be computed by the Euclidean distance.
Finally multiply the vector by a scale (step) that is not greater than the distance between the points and add it to the position. e.g:
stepDist = 1
# vector from (`mx`, `my`) to (`px`, `py`)
dx, dy = p.y - mx, py - px
# [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance)
len = math.sqrt(dx*dx + dy*dy)
if len > 0:
# [Unit vector](https://en.wikipedia.org/wiki/Unit_vector)
ndx, ndy = dx/len, dy/len
# minimum of step size and distance to target
step = min(len, stepDist)
# step forward
px += ndx * step
py += ndy * step
If a pygame.Rect object is of need, then the position of the rectangle can be set. e.g:
m.topleft = round(mx), round(my)
p.topleft = round(px), round(py)
But not you have to store the positions in (mx, my) respectively (px, py). If you would do mx, my = m.topleft respectively px, py = p.topleft, then the algorithm will break down, because the fraction component gets lost.
Code works for me if I remove abs() from atan2()
import pygame
import random
import math
# --- constants --- (UPPER_CASE_NAMES)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 25 # for more than 220 it has no time to update screen
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# --- classes --- (CamelCaseNames)
class Player(pygame.sprite.Sprite):
def __init__(self, x=SCREEN_WIDTH//2, y=SCREEN_HEIGHT//2):
super().__init__()
self.image = pygame.image.load("image.png").convert()
#self.rect = self.image.get_rect(x=x, y=y)
self.rect = self.image.get_rect(centerx=x, centery=y)
def update(self):
#self.rect.centerx = random.randint(0, SCREEN_WIDTH)
#self.rect.centery = random.randint(0, SCREEN_HEIGHT)
move_x = random.randint(-15, 15)
move_y = random.randint(-15, 15)
self.rect.move_ip(move_x,move_y)
def draw(self, surface):
surface.blit(self.image, self.rect)
class Follower(Player):
def update(self, player):
distance = math.hypot(abs(player.rect.x - self.rect.x), abs(player.rect.y - self.rect.y))
angle_radians = math.atan2((player.rect.y - self.rect.y), (player.rect.x - self.rect.x))
if distance != 0:
self.rect.y += 5*math.sin(angle_radians)
self.rect.x += 5*math.cos(angle_radians)
# --- functions --- (lower_case_names)
# --- main ---
pygame.init()
screen = pygame.display.set_mode( (SCREEN_WIDTH, SCREEN_HEIGHT) )
player = Player()
follower = Follower(0, 0)
# --- mainloop ---
clock = pygame.time.Clock()
running = True
while running:
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
running = False
# --- changes/moves/updates ---
if not pygame.key.get_pressed()[pygame.K_SPACE]:
player.update()
follower.update(player)
# --- draws ---
screen.fill(BLACK)
player.draw(screen)
follower.draw(screen)
pygame.display.flip()
# --- FPS ---
ms = clock.tick(FPS)
#pygame.display.set_caption('{}ms'.format(ms)) # 40ms for 25FPS, 16ms for 60FPS
fps = clock.get_fps()
pygame.display.set_caption('FPS: {}'.format(fps))
# --- end ---
pygame.quit()

How to make ball bounce off triangle in pygame?

Hello I'm a pretty new programmer and I'm trying to make a ball bounce off a 45 degree triangle. Here is my code:
This program makes the ball bounce when it hits the sides of the window, but I don't know how to make it bounce off a triangle.
import pygame # importing the pygame
import sys # importing the system libraries
import time # importing timer
import random
from pygame.locals import * # importing the locals functions from the pygame library set
pygame.init() # the function from pygame that initializes all relevant variable
# setting length and width
width = 500
length = 300
# colour variables
WHITE = (255,255,255)
BLUE = (0,0,255)
# importing ball image
ball = pygame.image.load('ball.png')
ballRect = ball.get_rect()
ballRect.left = 300
ballRect.right = 300
# setting speed
x_speed = 2
y_speed = 2
# setting window size
WINDOW = pygame.display.set_mode((width, length))# setting the size of the window
pygame.display.update()
# loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
ballRect = ballRect.move(x_speed,y_speed)
WINDOW.fill(WHITE) # changing screen colour to white
WINDOW.blit(ball,ballRect) # printing the ball to screen
pygame.display.update()
pygame.display.flip()
time.sleep(0.002) # to slow down the speed of bouncing
pygame.display.update()
# if the left side of ballRect is in a position less than 0, or the right side of ballRect is greater than 500
if ballRect.left < 0 or ballRect.right > (width):
x_speed = x_speed * -1
# if the top of ballRect is in a position less than 0, or the bottom of ballRect is greater than the length
elif ballRect.top < 0 or ballRect.bottom > (length):
y_speed = y_speed * -1
pygame.display.update()
I haven't drawn in the triangle because I don't know where to, but I expect the ball to bounce off the triangle like it does when it hits the sides of the window. Any help would be great!
Interesting task. A triangle can be defined by a simple list:
triangle = [(250, 220), (400, 300), (100, 300)]
The triangle can be drawn by pygame.draw.polygon()
pygame.draw.polygon(WINDOW, RED, triangle, 0)
Use pygame.math.Vector2 to define the position and the motion vector of the ball:
ballvec = pygame.math.Vector2(1, 1)
ballpos = pygame.math.Vector2(150, 250)
balldiameter = 64
Create a function, which does the collision detection. The function has to detect if the ball hits a line. If the line is hit, then the motion vector of the ball is reflected on the line.
The line is represented by 2 points (lp0, lp1), which are pygame.math.Vector2 objects.
The position of the ball (pt) and the motion vector (dir) are pygame.math.Vector2 objects, too:
def isect(lp0, lp1, pt, dir, radius):
# direction vector of the line
l_dir = (lp1 - lp0).normalize()
# normal vector to the line
nv = pygame.math.Vector2(-l_dir[1], l_dir[0])
# distance to line
d = (lp0-pt).dot(nv)
# intersection point on endless line
ptX = pt + nv * d
# test if the ball hits the line
if abs(d) > radius or dir.dot(ptX-pt) <= 0:
return dir
if (ptX-lp0).dot(l_dir) < 0 or (ptX-lp1).dot(l_dir) > 0:
return dir
# reflect the direction vector on the line (like a billiard ball)
r_dir = dir.reflect(nv)
return r_dir
Append the window rectangle and the triangle to a list of lines. Ech line is represented by a tuple of 2 pygame.math.Vector2 objects:
# add screen rect
screen_rect = [(0, 0), (0, 300), (500, 300), (500, 0)]
for i in range(len(screen_rect)):
p0, p1 = screen_rect[i], screen_rect[(i+1) % len(screen_rect)]
line_list.append((pygame.math.Vector2(p0[0], p0[1]), pygame.math.Vector2(p1[0], p1[1])))
# add red trianlge
triangle = [(250, 220), (400, 300), (100, 300)]
for i in range(len(triangle)):
p0, p1 = triangle[i], triangle[(i+1) % len(triangle)]
line_list.append((pygame.math.Vector2(p0[0], p0[1]), pygame.math.Vector2(p1[0], p1[1])))
Do the collision detection in a loop, which traverse the lines. If the ball hits a line, then the motion vector is replaced by the reflected motion vector:
for line in line_list:
ballvec = isect(*line, ballpos, ballvec, balldiameter/2)
Finally update the position of the ball an the ball rectangle:
ballpos = ballpos + ballvec
ballRect.x, ballRect.y = ballpos[0]-ballRect.width/2, ballpos[1]-ballRect.height/2
See the example code, where I applied the suggested changes to your original code. My ball image has a size of 64x64. The ball diameter has to be set to this size (balldiameter = 64):
Minimal example
import pygame
pygame.init()
window = pygame.display.set_mode((500, 300))
try:
ball = pygame.image.load("Ball64.png")
except:
ball = pygame.Surface((64, 64), pygame.SRCALPHA)
pygame.draw.circle(ball, (255, 255, 0), (32, 32), 32)
ballvec = pygame.math.Vector2(1.5, 1.5)
ballpos = pygame.math.Vector2(150, 250)
balldiameter = ball.get_width()
def reflect_circle_on_line(lp0, lp1, pt, dir, radius):
l_dir = (lp1 - lp0).normalize() # direction vector of the line
nv = pygame.math.Vector2(-l_dir[1], l_dir[0]) # normal vector to the line
d = (lp0-pt).dot(nv) # distance to line
ptX = pt + nv * d # intersection point on endless line
if (abs(d) > radius or dir.dot(ptX-pt) <= 0 or # test if the ball hits the line
(ptX-lp0).dot(l_dir) < 0 or (ptX-lp1).dot(l_dir) > 0):
return dir
r_dir = dir.reflect(nv) # reflect the direction vector on the line (like a billiard ball)
return r_dir
triangle1 = [(250, 220), (400, 300), (100, 300)]
triangle2 = [(250, 80), (400, 0), (100, 0)]
screen_rect = [(0, 0), (0, window.get_height()), window.get_size(), (window.get_width(), 0)]
line_list = []
for p0, p1 in zip(triangle1, triangle1[1:] + triangle1[:1]):
line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))
for p0, p1 in zip(triangle2, triangle2[1:] + triangle2[:1]):
line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))
for p0, p1 in zip(screen_rect, screen_rect[1:] + screen_rect[:1]):
line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))
clock = pygame.time.Clock()
run = True
while run:
clock.tick(250)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
for line in line_list:
ballvec = reflect_circle_on_line(*line, ballpos, ballvec, balldiameter/2)
ballpos = ballpos + ballvec
window.fill((64, 64, 64))
pygame.draw.polygon(window, (255, 0, 0), triangle1, 0)
pygame.draw.polygon(window, (0, 0, 255), triangle2, 0)
window.blit(ball, (round(ballpos[0]-balldiameter/2), round(ballpos[1]-balldiameter/2)))
pygame.display.flip()
pygame.quit()

Pygame resizing a surface and window based on a rect size

I have a display area and a surface that is blitted on the display. On the surface is an image, in this case a rect. In the future it may be multiple rects or lines drawn on the surface keep that in mind.
I am trying to enlarge (by pressing x) the Rect named Sprite that is on SpriteSurface and SpriteSurface as well as the whole display window. The SpriteSurface image should be centered despite the resize. Currently the window will enlarge and the image stays centered, but if you uncomment the spritesizeX and Y lines the image gets larger but too big too fast and the window doesn't seem to enlarge big enough. Lowering the values shows that the offset of centering gets thrown off after the first resize. I feel like the solution should be relatively easy but im stumped. Any help would be appreciated.
Settings.py
spriteSizeX = 30
spriteSizeY = 30
SpHalfX = int(round(spriteSizeX / 2))
SpHalfY = int(round(spriteSizeY / 2))
multiplyer = 3
windowSizeX = int(round(spriteSizeX * multiplyer))
windowSizeY = int(round(spriteSizeY * multiplyer))
HalfWinX = int(round((windowSizeX / 2) - SpHalfX))
HalfWinY = int(round((windowSizeY / 2) - SpHalfY))
Orange = (238,154,0)
Gold = (255,215,0)
Black = (0,0,0)
Blue = (0,0,255)
Gray = (128,128,128)
DarkGray = (100,100,100)
Green = (0,128,0)
Lime = (0,255,0)
Purple = (128,0,128)
Red = (255,0,0)
Teal = (0,200, 128)
Yellow = (255,255,0)
White = (255,255,255)
run = True
SpriteCapture.py
#!/usr/local/bin/python3.6
import sys, pygame
from pygame.locals import *
from settings import *
pygame.init()
pygame.display.set_caption("Sprite Capture")
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
while run == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if pygame.key.get_pressed()[pygame.K_s]:
pygame.image.save(SpriteSurface, 'img1.png')
run = False
if pygame.key.get_pressed()[pygame.K_q]:
run = False
if pygame.key.get_pressed()[pygame.K_z]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += -10
windowSizeY += -10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
if pygame.key.get_pressed()[pygame.K_x]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += 10
windowSizeY += 10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
Screen.fill(Black)
pygame.draw.rect(SpriteSurface,Orange,Sprite)
Screen.blit(SpriteSurface, (HalfWinX,HalfWinY))
pygame.display.flip()
If you want to scale your surfaces or rects according to the screen size, you can define a zoom_factor variable which you can just increase when a key gets pressed and then use it to scale the window and the surfaces. Multiply it by the original screen width and height to scale the window, and also scale your surfaces with pygame.transform.rotozoom and pass the zoom_factor as the scale argument.
import sys
import pygame
from pygame.locals import *
width = 30
height = 30
multiplyer = 3
window_width = round(width * multiplyer)
window_height = round(height * multiplyer)
zoom_factor = 1
ORANGE = (238,154,0)
BLACK = (0,0,0)
pygame.init()
screen = pygame.display.set_mode((window_width, window_height), RESIZABLE)
screen_rect = screen.get_rect() # A rect with the size of the screen.
clock = pygame.time.Clock()
# Keep a reference to the original image to preserve the quality.
ORIG_SURFACE = pygame.Surface((width, height))
ORIG_SURFACE.fill(ORANGE)
surface = ORIG_SURFACE
# Center the rect on the screen's center.
rect = surface.get_rect(center=screen_rect.center)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
run = False
elif event.key == pygame.K_z:
zoom_factor = round(zoom_factor-.1, 1)
# Scale the screen.
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect() # Get a new rect.
# Scale the ORIG_SURFACE (the original won't be modified).
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center) # Get a new rect.
elif event.key == pygame.K_x:
zoom_factor = round(zoom_factor+.1, 1)
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect()
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center)
# Note that the rect.w/screen_rect.w ratio is not perfectly constant.
print(zoom_factor, screen_rect.w, rect.w, rect.w/screen_rect.w)
screen.fill(BLACK)
screen.blit(surface, rect) # Blit the surface at the rect.topleft coords.
pygame.display.flip()
clock.tick(60)
Alternatively, you could just blit all of your surfaces onto a background surface, then scale this background with pygame.transform.rotozoom each frame and blit it onto the screen. However, scaling a big background surface each frame will be bad for the performance.

Adding gradient to moving objects in pygame

I want to add gradient to the ball in this program & also possibly the waves drawn to fade into the colour of the background (as if glowing) instead of one colour fills.
I've looked at tons of tutorials however none of them are making much sense to my syntax, the general idea to me is confusing as I have moving objects that draw the space I want to add gradient to quite slowly. Can anyone give an insight into how I can do this?
code:
import sys, pygame, math
from pygame.locals import *
# set up of constants
WHITE = (255, 255, 255)
DARKRED = (128, 0, 0)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
BGCOLOR = WHITE
screen = pygame.display.set_mode()
WINDOWWIDTH = 800 # width of the program's window, in pixels
WINDOWHEIGHT = 800 # height in pixels
WIN_CENTERX = int(WINDOWWIDTH / 2) # the midpoint for the width of the window
WIN_CENTERY = int(WINDOWHEIGHT / 2) # the midpoint for the height of the window
screen = pygame.display.get_surface()
FPS = 160 # frames per second to run at
AMPLITUDE = 80 # how many pixels tall the waves with rise/fall.
# standard pygame setup code
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), pygame.RESIZABLE)
pygame.display.set_caption('Window title')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# variables that track visibility modes
showSine = True
showSquare = True
pause = False
xPos = 0
step = 0 # the current input f
posRecord = {'sin': [], 'square': []} # keeps track of the ball positions for drawing the waves
yPosSquare = AMPLITUDE # starting position
# main application loop
while True:
# event handling loop for quit events
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
# sine wave
yPos = -1 * math.sin(step) * AMPLITUDE
posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY))
if showSine:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10)
sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20)
DISPLAYSURF.blit(sinLabelSurf, sinLabelRect)
# draw the waves from the previously recorded ball positions
if showSine:
for x, y in posRecord['sin']:
pygame.draw.circle(DISPLAYSURF, DARKRED, (x,y), 4)
#drawing horizontal lines
# square
posRecord['square'].append((int(xPos), int(yPosSquare) + WIN_CENTERY))
if showSquare:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, GREEN, (int(xPos), int(yPosSquare) + WIN_CENTERY), 10)
squareLabelRect.center = (int(xPos), int(yPosSquare) + WIN_CENTERY + 20)
DISPLAYSURF.blit(squareLabelSurf, squareLabelRect)
# draw the waves from the previously recorded ball positions
if showSquare:
for x, y in posRecord['square']:
pygame.draw.circle(DISPLAYSURF, BLUE, (x, y), 4)
# draw the border
pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1)
pygame.display.update()
FPSCLOCK.tick(FPS)
if not pause:
xPos += 1
#wave movement
if xPos > WINDOWWIDTH:
#sine
xPos = 0
posRecord['sin'] = []
step = 0
# square
yPosSquare = AMPLITUDE
posRecord['square'] = []
else:
#sine
step += 0.008
#step %= 2 * math.pi
# square
# jump top and bottom every 100 pixels
if xPos % 100 == 0:
yPosSquare *= -1
# add vertical line
for x in range(-AMPLITUDE, AMPLITUDE):
posRecord['square'].append((int(xPos), int(x) + WIN_CENTERY))
Use SPACE to change background color.
First line use only transparency - and has no problem with different background color.
Second line changes only circles color - and depends on background color.
Third and fourth line (it is the same line with different starting color) change circles color and transparency - and depends on background color.
Second and last line look good on one color background and need more work to find good-looking fading.
import pygame
pygame.init()
screen = pygame.display.set_mode((600,200))
#--------------------------------------
# circles positions and transparency (x,y, alpha)
circles = []
for x in range(100):
circles.append( [100+x*3, 200, x*2] )
#--------------------------------------
white = True # background color
#--------------------------------------
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE:
white = not white
#--------------------------------------
if white:
screen.fill((255,255,255))
else:
screen.fill((0,0,0))
#--------------------------------------
# first
circle_img = pygame.Surface((20,20))
pygame.draw.circle(circle_img, (255,0,0), (10,10), 10)
circle_img.set_colorkey(0)
for x in circles:
circle_img.set_alpha(x[2])
screen.blit(circle_img, (x[0],40))
#--------------------------------------
# second
circle_img = pygame.Surface((20,20))
for x in circles:
pygame.draw.circle(circle_img, (255,255-x[2],255-x[2]), (10,10), 10)
circle_img.set_colorkey(0)
screen.blit(circle_img, (x[0],90))
#--------------------------------------
# last
circle_img = pygame.Surface((20,20))
for x in circles:
pygame.draw.circle(circle_img, (255,255-x[2],255-x[2]), (10,10), 10)
circle_img.set_colorkey(0)
circle_img.set_alpha(x[2])
screen.blit(circle_img, (x[0],140))
#--------------------------------------
pygame.display.flip()
pygame.quit()

Categories

Resources