This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
Closed 3 years ago.
I'm trying to build a simple game and so far I'm just learning the basics.
I'm trying to draw a rectangle tilted 45°, but I couldn't figure how to keep it centered even after reading some previous question here on SO.
So I tried making a rectangle that keeps rotating; this is the corresponding code.
alpha=0
while True:
w, h=screen.get_size()
s=pygame.Surface((w/2, h))
pygame.draw.rect(s, col, (300,150,50,10))
s=pygame.transform.rotozoom(s, alpha, 1)
alpha+=2
s.set_colorkey((0,0,0))
background.blit(s, (0, 0))
# flip screen, etc
The surface should keep rotating forever around some center (I wanted to use this to understand clearly which it was), but it moves in an irregular way.
This is the video of what happens [...].
EDIT:marked as duplicate, I'm removing the video link
Rotating a rectangle (not image) in pygame provides you an answer:
Example code:
import pygame as py
# define constants
WIDTH = 500
HEIGHT = 500
FPS = 30
# define colors
BLACK = (0 , 0 , 0)
GREEN = (0 , 255 , 0)
# initialize pygame and create screen
py.init()
screen = py.display.set_mode((WIDTH , HEIGHT))
# for setting FPS
clock = py.time.Clock()
rot = 0
rot_speed = 3
# define a surface (RECTANGLE)
image_orig = py.Surface((100 , 100))
# for making transparent background while rotating an image
image_orig.set_colorkey(BLACK)
# fill the rectangle / surface with green color
image_orig.fill(GREEN)
# creating a copy of orignal image for smooth rotation
image = image_orig.copy()
image.set_colorkey(BLACK)
# define rect for placing the rectangle at the desired position
rect = image.get_rect()
rect.center = (WIDTH // 2 , HEIGHT // 2)
# keep rotating the rectangle until running is set to False
running = True
while running:
# set FPS
clock.tick(FPS)
# clear the screen every time before drawing new objects
screen.fill(BLACK)
# check for the exit
for event in py.event.get():
if event.type == py.QUIT:
running = False
# making a copy of the old center of the rectangle
old_center = rect.center
# defining angle of the rotation
rot = (rot + rot_speed) % 360
# rotating the orignal image
new_image = py.transform.rotate(image_orig , rot)
rect = new_image.get_rect()
# set the rotated rectangle to the old center
rect.center = old_center
# drawing the rotated rectangle to the screen
screen.blit(new_image , rect)
# flipping the display after drawing everything
py.display.flip()
py.quit()
Related
Hello everyone I am learning the basics of pygame.
I recently ran into a problem;
I decided to load an image and gave it a random location well within the pygame window.But the problem was that sometimes it just wont appear in the window. So I drew a black pointer at the place where the image was to be loaded. Then I found out that the black pointer doesnot coincide with the image and hence the image is not appearing in the location I want it to be in.
So I want help solving this problem.
Thank you in advance.
Code:
import pygame
import random
pygame.init()
#Pygame starters
display_width = 800
display_height = 600
game_display = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Blob runner")
clock = pygame.time.Clock()
targetpng = pygame.image.load("target.png")
#Quit checker
crashed = False
#COLOURS
white = (255, 255, 255)
black = (0, 0, 0)
def draw_environment(x,y):
game_display.fill(white)
#Image
game_display.blit(targetpng,(x, y))
#Black pointer
pygame.draw.circle(game_display, black, (x, y), 5 )
pygame.display.update()
x, y = random.randrange(0,display_width), random.randrange(0,display_height)
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
# print(event)
draw_environment(x,y)
clock.tick(30)
pygame.quit()
quit()
Image:
The image has a size. it may happen that the image is out of the window at the right or at the bottom. The origin of the blit puts the the top left corner of the image to the specified position.
If you wan that the image is centered at (x, y), the you have to get a pygame.Rect with the size of the image (get_rect) and set the center of the rectangle to the specified position (keyword argument). Use the rectangle to specify the position for the blit operation.
img_rect = targetpng.get_rect(center = (x, y))
game_display.blit(targetpng, img_rect)
Th image has a size, thus the random position for the image has to be in range [image_size/2, window_sizw - image_size/2].
The width and height of a pygame.Surface can be get by get_width()/get_height() or get_size(). For instance:
img_size = targetpng.get_size()
x = random.randrange(img_size[0]//2, display_width - img_size[0]//2)
y = random.randrange(img_size[1]//2, display_height - img_size[1]//2)
I now understand what went wrong with the insight provided by #Rabbid76.
The thing is I drew the picture in paint and then opened the file in gimp to convert whites in the image to alpha(the checkerboard-like background) . But the problem occurred when I did not clear of the unwanted alpha as I thought the computer will not recognise the alpha but as matter of fact it does.
So when I loaded the image, the corner of the image is not of the actual "target spot" but of the entire image, so the corner will be on the alpha. The black dot was pointing at the corner of the image all along and the image sometimes did not appear because the corner was too close to the border so the image would trespass to the un-displayed portion of the window.
So the fix is to clear of the alpha or the white background to minimum and only then convert it into alpha. Then do as #Rabbid76 says :D.
We would like to create a rectangle with width w and height h, on a surface whose width is a and whose height is b.
What would be the values of left, top, width, height that we have to pass to the pygame.Rect function, if the rectangle needs to be at a distance x_offset from the left edge of the surface and centered vertically on the surface?
I got this question and I know that left would be equal to x_offset but I have no idea how to figure out any of the other ones I've tried drawing it out.
Just create your Rect object first with the values you know:
rect = pygame.Rect(x_offset, 0, w, h)
and center the rect by using its centery attribute:
rect.centery = the_other_surface.get_rect().centery
It can help if you rename the variables into something readable.
For example:
rect_width = w
rect_height = h
surface_width = a
surface_height = b
rect_width and rect_height will be the Rect's width and height.
The x_offset will be the left.
rect_left = x_offset
To center it vertically, find the vertical center:
surface_center_v = surface_height / 2
Then place the rectangle there.
rect_top = surface_center_v
However this only places the rectangle's top edge, and we want the vertical center.
So adjust the rectangle's position upwards by half of the rectangle's height, to make the rectangle's vertical center align with the surface's vertical center.
rect_top -= rect_height / 2
Now you have all of rect_left, rect_top, rect_width and rect_height.
To use all of the specified variables (rather than hard-coding absolute sizes into the program), I would make use of pygame's Rectangle object for both the surface and the rectangle we want to draw.
import pygame
from time import sleep
pygame.init()
# Declare our variables and initialize
a,b = 0,0 # Declare screen dimensions.
w,h = 0,0 # Declare rectangle dimensions.
x,y = 0,0 # Declare placement locations on screen surface.
# Now set specific values to our variables
a = 500 # Screen Width
b = 500 # Screen Height
# We could use any surface here (such as for a sprite), but let's use the screen.
screen = pygame.display.set_mode((a,b),32) # Create a screen surface of size a X b
screen.fill(pygame.Color("DARKBLUE")) # Fill it with Dark Blue.
screen_rect = screen.get_rect() # Create a rectangle object for the screen.
w = 200 # Make the rectangle 200 wide.
h = 100 # Make the rectangle 100 high.
rec = pygame.Rect((x,y,w,h)) # Create a rectangle object for our drawn rectangle.
# Rec holds all of the placement and size values for the rectangle we want to draw.
rec_color = (255,0,0) # Let's draw a red rectangle.
distance_x = 50 # Place the rectangle 50 pixels over from the left side of the screen.
rec.x = distance_x # Set rec's x value equal to distance_x.
rec.centery = screen_rect.centery # Set rec's y value to center of the screen y-axis
# Pygame's Rectangle Object handles the calculations for us. NICE!
pygame.draw.rect(screen,rec_color,rec,2) # Draw a rectangle on the screen using a line
# that is 2-pixels wide. (Use 0 to fill.)
pygame.display.flip() # Show the screen.
print("\nrec =",rec,end="\n\n") # Let's see rec's final values.
sleep(3) # Sleep for 3 seconds.
exit() # Quit
This question already has an answer here:
Nested for loop chess board coloring not working Python
(1 answer)
Closed 1 year ago.
I made a chess board, but I need help with coloring the different squares. I only have white squares but I need black squares. Here is my code.
import pygame
def board():
width=480 # measurements for the window
height=480
block_size=59
window = pygame.display.set_mode((width,height))
background_color = (0,0,0) # This is how I make the lines
window.fill(background_color)
for y in range(height):
for x in range(width):
rect = pygame.Rect(x*(block_size+1), y*(block_size+1), block_size, block_size)
pygame.draw.rect(window, (255,255,255), rect) # Leaves space for lines to be visible.
pygame.display.flip()
board()
# It's just the board.
I already know I violated PEP 8.
You can do it like: I changed your coord-manipulation to benefit directly from the given range values not using a mult inside it. Color is flipped on each rect drawn and also on line-change to get alternating row colorings:
import pygame
def board():
def flipColor(color):
white = (240,240,240)
black = (30,30,30)
if not color or color == white:
color = black
else:
color = white
return color
width=480 # measurements for the window
height=480
block_size= 60
window = pygame.display.set_mode((width,height))
background_color = (0,0,0) # This is how I make the lines
window.fill(background_color)
c = None
pygame.draw.rect(window,(255,0,0),pygame.Rect(0,0,width,height)) # red background
for y in range(0,height,block_size):
c = flipColor(c)
for x in range(0,width,block_size):
c = flipColor(c)
rect = pygame.Rect(x , y , x+block_size , y+block_size )
pygame.draw.rect(window, c , rect, 0) # Leaves space for lines to be visible.
for i in range(0,height+1,block_size):
pygame.draw.line(window,(233,33,187),(i,0),(i,width),2)
pygame.draw.line(window,(233,33,187),(0,i),(height,i),2)
pygame.draw.line(window,(233,33,187),(height-2,0),(height-2,width),2) # fix for out of window line
pygame.draw.line(window,(233,33,187),(0,width-2),(height,width-2),2) # fix for out of wondow line
pygame.display.flip()
board()
You can also use itertools.cycle, pass an iterable with the colors and then just call next to cycle through them. I'd create the background surface when the program starts and then just blit it in the while loop. If you need a background with extra lines, just draw them on the same background surface as well or create a copy.
import itertools
import pygame as pg
pg.init()
screen = pg.display.set_mode((480, 480))
clock = pg.time.Clock()
width, height = screen.get_size()
block_size = 60
# Create a surface onto which we'll blit the rectangles.
background = pg.Surface((width, height))
colors = itertools.cycle((pg.Color('white'), pg.Color('black')))
for y in range(0, height, block_size):
for x in range(0, width, block_size):
rect = (x, y, block_size, block_size)
pg.draw.rect(background, next(colors), rect)
next(colors) # Skip the next color.
# Then you can just blit the background in the while loop.
screen.blit(background, (0, 0))
pg.display.flip()
You can also calculate the color.
If the index of the column and row are both equal or not equal the color is white, else black.
For example field A8:
Index of A is 0 --> equal
8 --> equal
==> Field is white
Example field A1:
Index of A is 0 --> equal
1 --> unequal
==> Field is black
I have been trying to make an image rotate in pygame, using python 3.6, however when I do it either distorts the image into an unrecognizable image, or when it rotates it bumps all over the place
Just using pygame.transform.rotate(image, angle) makes the distorted mess.
And using something like:
pygame.draw.rect(gameDisplay, self.color, [self.x, self.y, self.width, self.height]) makes the image bump all over the place.
I have looked at many questions on this site and others and so far none of them have worked perfectly.
To anyone who is interested here is the link to my code so far.
https://pastebin.com/UQJJFNTy
My image is 64x64.
Thanks in advance!
Per the docs (http://www.pygame.org/docs/ref/transform.html):
Some of the transforms are considered destructive. These means every time they are performed they lose pixel data. Common examples of this are resizing and rotating. For this reason, it is better to re-transform the original surface than to keep transforming an image multiple times.
Each time you call transform.rotate you need to do it on the original image, not on the previously rotated one. For example, if I want the image rotated 10 degrees each frame:
image = pygame.image.load("myimage.png").convert()
image_clean = image.copy()
rot = 0
Then in your game loop (or object's update):
rot += 10
image = pygame.transform.rotate(image_clean, rot)
Here's a complete example. Don't modify the original image and in the while loop use pygame.transform.rotate or rotozoom to get a new rotated surface and assign it to another name. Use a rect to keep the center.
import sys
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
BG_COLOR = pg.Color('darkslategray')
# Here I just create an image with per-pixel alpha and draw
# some shapes on it so that we can better see the rotation effects.
ORIG_IMAGE = pg.Surface((240, 180), pg.SRCALPHA)
pg.draw.rect(ORIG_IMAGE, pg.Color('aquamarine3'), (80, 0, 80, 180))
pg.draw.rect(ORIG_IMAGE, pg.Color('gray16'), (60, 0, 120, 40))
pg.draw.circle(ORIG_IMAGE, pg.Color('gray16'), (120, 180), 50)
def main():
clock = pg.time.Clock()
# The rect where we'll blit the image.
rect = ORIG_IMAGE.get_rect(center=(300, 220))
angle = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# Increment the angle, then rotate the image.
angle += 2
# image = pg.transform.rotate(ORIG_IMAGE, angle) # rotate often looks ugly.
image = pg.transform.rotozoom(ORIG_IMAGE, angle, 1) # rotozoom is smoother.
# The center of the new rect is the center of the old rect.
rect = image.get_rect(center=rect.center)
screen.fill(BG_COLOR)
screen.blit(image, rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
sys.exit()
I'm writing a class in pygame to create a sprite object, and I'd like to be able to rotate it. It works fine with an image, and rotates without issue. But when rotating a surface with a plain colour, the box appears to grow and shrink. I know that this is a result of the surface changing size to fit the vertices of the rectangle inside, but how do I stop it? I'd like to see a visual rotation.
I've created some sample code to show the problem that I'm facing, running it causes the box to simply change in size.
import sys, pygame
from pygame.locals import *
pygame.init()
SCREEN = pygame.display.set_mode((200, 200))
CLOCK = pygame.time.Clock()
surface = pygame.Surface((50 , 50))
surface.fill((0, 0, 0))
rotated_surface = surface
rect = surface.get_rect()
angle = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
SCREEN.fill((255, 255, 255))
angle += 5
rotated_surface = pygame.transform.rotate(surface, angle)
rect = rotated_surface.get_rect(center = (100, 100))
SCREEN.blit(rotated_surface, (rect.x, rect.y))
pygame.display.update()
CLOCK.tick(30)
How do I fix this issue, to make the surface rotate how I want?
Any help would be appreciated!
You have to create the Surface objects you create to stamp on the display surface in a way they use transparency information (That is - they have to have an alpha channel).
To do that, is just a question of passing the appropriate flag when creating your surface objects - simply replace this:
surface = pygame.Surface((50 , 50))
with:
surface = pygame.Surface((50 , 50), pygame.SRCALPHA)
and it should work.