Python Coordinate system question for centering drawn objects - python

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

Related

Python 3.7 turtle drawings are wider than they should be

I'm learning how to use Turtle for Python, so I'm drawing basic shapes for now. I noticed that when I try to draw a perfect square or other perfect polygon, the result looks "squished". The shape is always wider than it is tall, even though I have one set length value.
Is something wrong with my logic of how I draw the shapes?
Here's the code I have:
import turtle
bgColor = "teal"
worldX = 100
worldY = 100
turtle.screensize(bg=bgColor)
turtle.setworldcoordinates(0,worldX,worldY,0)
#Canvas is 100 x 100, with (0,0) being the upper left corner and (100,100) being the lower right corner
tr = turtle.Turtle()
tr.speed(6)
tr.color("black")
length = 5 #Length of sides you want
sides = 4 #Number of sides for shape
tr.penup()
tr.goto(worldX/2, worldY/2) #Go to center of canvas
tr.pendown()
for i in range(sides):
tr.forward(length)
tr.right(360/sides)
turtle.done()
I figured it out, it had to do with my screen size. If my screen size is more wide than tall, then my world coordinates are off. For example, suppose my screen size is 400x800. Because my screen is twice as wide compared to it's height, moving my turtle one point to the right or left would require the turtle to move twice the distance compared to moving the turtle one point up or down. Meaning that if I draw a 5x5 square based on my coordinate system, it would actually be a rectangle, even though it's 5x5 on my coordinate system.
Anyway, here's my new code that takes this into account:
import turtle
import random
bgColor = "teal"
worldX = 100
worldY = 100
screenX = 400
screenY = 400
screen = turtle.Screen()
screen.setup(screenX,screenY)
turtle.screensize(bg=bgColor)
turtle.setworldcoordinates(0,worldX,worldY,0)
#Canvas is 100 x 100, with (0,0) being the upper left corner and (100,100) being the lower right corner
print(turtle.screensize())
tr = turtle.Turtle()
tr.speed(6)
tr.color("black")
length = 20 #Length of sides you want
sides = 4 #Number of sides for shape
tr.penup()
tr.goto(worldX/2, worldY/2) #Go to center of canvas
tr.pendown()
for i in range(sides):
tr.forward(length)
tr.right(360/sides)
turtle.done()

using the pygame: [spritesheet].subsurface function, is there a way to draw the sprite from the bottom rather than the top left corner

I have a set of sprites, also on a spritesheet, which bounces slightly up and down, so the sprite height changes slightly.
By coding: spritesheet.subsurface(x, y, width, height), it will draw the sprite starting from (x, y) and then the width and height to the right and down respectively, however, when drawing the frames from the same screen-relative position, the sprite looks like the feet are moving down and up.
So that, if I had positioned frame 1 of the sprite to have the feet touching the floor, frame 2 would have the feet through the floor.
Is there a way to draw the sprite, say, from the bottom left, or bottom centre?
Many thanks
pygame.Surface.subsurface uses a rectangular area to create a new surface that references its parent. The origin (0, 0) of the pygame coordinate system is the top left. This cannot be changed.
However, if you know the bottom left coordinate (left_x, bottom_y) of a sprit on a spritsheet the rectangle of the sprite can be computed (spritesheet is a pygame.Surface object):
sheet_height = spritsheet.get_height()
sprite = spritsheet.subsurface((left_x, sheet_height - bottom_y - height, width, height))
The code can be made more comprehensible with a pygame.Rect object:
sheet_height = spritsheet.get_height()
sprite_rect = pygame.Rect(left_x, 0, height, height)
sprite_rect.bottom = sheet_height - bottom_y
sprite = spritsheet.subsurface(sprite_rect )

How to calculate the middle of the screen for a pygame.Surface?

I am trying to calculate the middle of the screen for image pygame.Surface object. I've tried to do the simple screen.blit(my_image, (0, 0))
but that won't do it. Is there a way I can calculate where to blit an image, so it's in the middle? Each of my images have different sizes etc.
Edit:
I've figured it out! I've made a function:
screenw = 500
screenh = 500
# true_coords(pygame.Surface(...), (250, 250))
def true_coords(obj, coords):
objw = obj.get_width()
objh = obj.get_height()
true_coords = coords[0] - (objw / 2), coords[1] - (objh /2)
return(true_coords)
Use the Rect class to do all the calculation for you.
Given a Surface called surf and the display Surface called screen, you could do something like this:
screen = pygame.display.set_mode(...)
screen_rect = screen.get_rect()
surf = pygame.Surface(...)
# set the center of the new Rect to the center of the screen
surf_rect = surf.get_rect(center=screen_rect.center)
screen.blit(surf, surf_rect)
The Rect class has a number of useful virtual attributes, like center, topleft, bottomright etc which you can use to easily position a Surface. Usually you use a Surface and a Rect together wrapped in a Sprite.

Pygame touble when drawing a rectangle

So, I'm randomly generating a "world" and drawing it with pygame. That part worked perfectly fine until I decided to add something above what I already drew.
The code is as follows. What each thing is is of no consequence, but DISPLAY is the surface I'm working on, y.colour is a size 3 Tuple, y.coord is a (x,y) Tuple
for x in W_Map:
for y in x:
DISPLAY.fill(y.colour, pygame.Rect(y.coord[0]-tile_size,
y.coord[1]-tile_size,
y.coord[0]+tile_size,
y.coord[1]+tile_size))
DISPLAY.fill(lime, pygame.Rect(300,300,310,310))
According to the game above, this should create a lime coloured 10x10 square centered on 305x305. The result, however, is the following picture:
As you can see, the first part of the code draws the terrain perfectly, but when creating the lime square on top of what's already drawn, it goes completely crazy. The whole function is:
pygame.init()
DISPLAY = pygame.display.set_mode(
(shape[0]*2*tile_size, shape[1]*2*tile_size))
DISPLAY.fill((0,0,0))
#Make and draw the Rects
for x in W_Map:
for y in x:
DISPLAY.fill(y.colour, pygame.Rect(y.coord[0]-tile_size,
y.coord[1]-tile_size,
y.coord[0]+tile_size,
y.coord[1]+tile_size))
DISPLAY.fill(lime, pygame.Rect(300,300,310,310))
Pygame's Rect takes four arguments: x, y, width, and height, where x and y are relative to the top left of the viewport. Your lime rectangle is created with pygame.Rect(300,300,310,310), meaning a width and height of 310 pixels and a location of (300, 300).
To create a 10x10 rectangle centered at (305, 305) you'll need to use pygame.Rect(300, 300, 10, 10). You can also create a helper function to translate size and center point to the necessary rectangle parameters:
def center_rect(x, y, width, height):
return pygame.Rect(x - width / 2, y - height / 2, width, height)
Then you could use this helper function like so:
DISPLAY.fill(lime, center_rect(305, 305, 10, 10))

pygame surface rotating in a nonsensical way [duplicate]

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()

Categories