I can't get to move a polygon with the function move_ip() in pygame, the code works good if I draw a rectangle, but not with polygon. It doesn't respond when I press any arrow key.
I think I don't need spaceship rectangle because polygons are rects with pygame, but it seems not right, I don't know, i've got a mess.
This is my pygame code:
import sys, os
import pygame
from pygame.locals import *
import numpy as np
if sys.platform in ["win32","win64"]: os.environ["SDL_VIDEO_CENTERED"]='1'
width_screen = 600
height_screen = 480
screen_size = (width_screen, height_screen)
positionx_screen = 0
positiony_screen = 32
title_window = 'ASTEROIDS'
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
spaceship = pygame.Rect(200, 475, 120, 20)
def quit_screen(event):
if event.type == QUIT:
pygame.quit()
sys.exit()
class Spaceship():
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.colour = WHITE
self.thickness = 0
self.movex = 20
def draw_nave(self):
vertices = np.array([[screen_size[0]/2, screen_size[1] /2 - 30],[screen_size[0]/2 + 15, screen_size[1]/2],
[screen_size[0]/2-15, screen_size[1] / 2]])
pygame.draw.polygon(screen_game, self.colour, vertices, self.thickness)
def move_spaceship_right(self):
spaceship.move_ip(self.movex, 0)
def move_spaceship_left(self):
spaceship.move_ip(- self.movex, 0)
def game():
pygame.init()
running = True
global screen_game
screen_game = pygame.display.set_mode(screen_size,
positionx_screen, positiony_screen)
pygame.display.set_caption(title_window)
clock = pygame.time.Clock()
my_spaceship = Spaceship(200, 475, 120, 20)
screen_game.fill(BLACK)
while running:
screen_game.fill(BLACK)
for event in pygame.event.get():
quit_screen(event)
if event.type == pygame.KEYDOWN:
if event.key == K_RIGHT:
my_spaceship.move_spaceship_right()
if event.key == K_LEFT:
my_spaceship.move_spaceship_left()
my_spaceship.draw_nave()
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
try:
game()
except:
pygame.quit()
Thanks
The problem is that your Rect is in no way related to the polygon you draw. You simply draw it at the same position every time.
Better create a Surface once, draw your polygon on that Surface once, and use spaceship as position when drawing that new Surface.
I recommend subclassing from Sprite, create that Surface in the __init__ function, and save the Rect in self.rect instead of the spaceship variable.
Then either use the draw function of Sprite or put your Sprite into a Group.
Related
I don't know how to detect when my mouse is on 2 rect and choose (if two rect has an action) the action. For example, in Windows, when two windows are one over the other, it's the first window that will be selected. I want to do exactly the same thing.
import pygame
class Rectangle(pygame.sprite.Sprite):
def __init__(self, screen, rect, x, y, color, name):
super().__init__()
self.screen = screen
self.name = name
self.screen_str = str(screen)
self.rect = rect
self.color = color
self.x, self.y = x, y
self.r = pygame.Surface((self.rect[2], self.rect[3]), pygame.SRCALPHA)
self.rect = self.r.get_rect()
self.rect.x, self.rect.y = x, y
self.r.fill(self.color)
pygame.init()
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption("PyStoneTest")
width, height = screen.get_size()
background_default = "image\Settings\Wallpaper\default_1.jpg"
D = {}
D["Rect2"] = Rectangle(screen, (0, 200, width, 70), 0,
50, (255, 255, 0), "Rect2")
D["Rect1"] = Rectangle(screen, (0, 100, width-200, 200), 0,
100, (255, 0, 255), "Rect1")
Programme = ["Rect1", "Rect2"]
while True:
background = pygame.image.load(background_default).convert()
background = pygame.transform.scale(background, (width, height))
for event in pygame.event.get():
x,y = pygame.mouse.get_pos()
if event.type == pygame.QUIT:
pygame.quit()
for element in Programme:
if D[element].rect.collidepoint(x,y) and event.type == pygame.MOUSEBUTTONDOWN:
del Programme[Programme.index(D[element].name)]
Programme.append(D[element].name)
print(Programme)
screen.blit(background, (0, 0))
for element in Programme:
screen.blit(D[element].r, D[element].rect)
pygame.display.update()
You should first run for-loop to check all windows and use last one which collides with mouse.
elif event.type == pygame.MOUSEBUTTONDOWN:
last = None
for element in Programme:
if D[element].rect.collidepoint(event.pos):
last = element
if last:
Programme.remove(last)
Programme.append(last)
print(Programme)
Or you would have to check in reverse order - from last to first - and break loop on first matching rectangle.
elif event.type == pygame.MOUSEBUTTONDOWN:
last = None
for element in reversed(Programme):
if D[element].rect.collidepoint(event.pos):
last = element
break
if last:
Programme.remove(last)
Programme.append(last)
print(Programme)
Minimal working code with other changes
import pygame
# --- classes ---
class Rectangle(pygame.sprite.Sprite):
# I skip `x,y` because I have it in `rect`
def __init__(self, screen, rect, color, name):
super().__init__()
self.screen = screen
self.color = color
self.name = name
self.rect = pygame.Rect(rect)
self.image = pygame.Surface(self.rect.size, pygame.SRCALPHA)
self.image.fill(self.color)
def draw(self):
self.screen.blit(self.image, self.rect)
# --- functions ---
# empty
# --- main ---
pygame.init()
screen = pygame.display.set_mode((1280, 720))
screen_rect = screen.get_rect() # it can be useful to center elements on screen - `d[name].rect.center = screen_rect.center`
pygame.display.set_caption("PyStoneTest")
# raw string
background_default = r"image\Settings\Wallpaper\default_1.jpg"
# load and rescale before `while`-loop
background = pygame.image.load(background_default).convert()
background = pygame.transform.scale(background, screen_rect.size)
d = {} # PEP8: `lower_case_names` for variable
d["Rect2"] = Rectangle(screen, (0, 0, screen_rect.width-100, 70), (255, 255, 0), "Rect2")
d["Rect2"].rect.center = screen_rect.center
d["Rect1"] = Rectangle(screen, (0, 0, 70, screen_rect.height-100), (255, 0, 255), "Rect1")
d["Rect1"].rect.center = screen_rect.center
programme = ["Rect1", "Rect2"] # PEP8: `lower_case_names` for variable
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit() # to skip rest of while-loop
elif event.type == pygame.MOUSEBUTTONDOWN:
selected = None
for name in programme:
if d[name].rect.collidepoint(event.pos):
selected = name
if selected and selected != programme[-1]:
programme.remove(selected)
programme.append(selected)
print('after replace:', programme)
screen.blit(background, (0, 0))
for name in programme:
d[name].draw()
pygame.display.update()
I cannot help with your code since I cannot understand what you are doing but I can offer my own solution. Since pygame renders thigs that are drawn later on the top, you can change the rendering order of your rectangles by checking which rectangle is being clicked and swapping it with the last rectangle in your list.
Here is an example. The colors in my example change weirdly but that's because I am generating them on the fly just to be able to tell the different between the different rects. You shouldn't have this problem.
import pygame
pygame.init()
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption("PyStoneTest")
rects = []
for i in range(10):
rects.append(pygame.Rect(i * 25, 100, 30, 30))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
screen.fill((0, 0, 0))
c = 1
for rect in rects:
pygame.draw.rect(screen, (c, 100, 100), rect)
c += 20
clicked = pygame.mouse.get_pressed()
x,y = pygame.mouse.get_pos()
for rect in rects:
if (rect.collidepoint(x, y) and clicked[0]):
#swap it
rects[-1][:], rect[:] = rect[:], rects[-1][:]
pygame.display.update()
I did a lot of research on how to delete a drawing on the screen after clicking it, and I couldn't do that
Try1 how to remove draw objects from pygame window?
Try2 How to remove a drawn circle in pygame? (without "going over" other things)
When clicked on the circle it will remove itself, and thus create another circle to be able to click.
import pygame, sys
from pygame.locals import *
from pygame import mixer
pygame.init()
musica = 'circles.mp3'
mixer.music.load(musica)
mixer.music.play()
pygame.init()
screen = pygame.display.set_mode((500, 500))
pygame.display.set_caption("OSU DA DEEP WEB")
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
screen.fill(WHITE)
#posição
width = 500 -30
height = 500 - 30
widthposition = random.randrange(width)
heightposition = random.randrange(width)
#sistema de pontos
points = 0
circle = pygame.draw.circle(screen, (0, 0, 0), (400, 300), 25)
def draw():
print('CLicked')
circle = pygame.draw.circle(screen, (0, 0, 0), (400, 300), 25)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
pygame.display.flip()
You have to check event to catch when left button was click and then you can draw white background and draw circle
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
screen.fill(WHITE)
draw(screen)
But this still doesn't check if you clicked on circle.
Minimal working code.
import sys
import random
import pygame
# --- constants ---
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
WIDTH = 500
HEIGHT = 500
# --- functions ---
def draw(screen):
print('Clicked')
x = random.randrange(30, WIDTH-30)
y = random.randrange(30, HEIGHT-30)
pygame.draw.circle(screen, (0, 0, 0), (x, y), 25)
# --- main ---
musica = 'circles.mp3'
pygame.init()
pygame.mixer.music.load(musica)
pygame.mixer.music.play()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(WHITE)
draw(screen)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
screen.fill(WHITE)
draw(screen)
pygame.display.flip()
draw.circle gives object pygame.Rect() with rectangle area used by circle and you could use it to "check collision" with mouse position
circle_rect = pygame.draw.circle(...)
#... code ...
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
if circle_rect.collidepoint(even.pos):
screen.fill(WHITE)
draw(screen)
and it check position in rectangle area so it works better but not ideal
import sys
import random
import pygame
# --- constants ---
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
WIDTH = 500
HEIGHT = 500
# --- functions ---
def draw(screen):
print('Clicked')
x = random.randrange(30, WIDTH-30)
y = random.randrange(30, HEIGHT-30)
circle_rect = pygame.draw.circle(screen, (0, 0, 0), (x, y), 25)
return circle_rect
# --- main ---
musica = 'circles.mp3'
pygame.init()
pygame.mixer.music.load(musica)
pygame.mixer.music.play()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(WHITE)
circle_rect = draw(screen)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
if circle_rect.collidepoint(event.pos):
screen.fill(WHITE)
circle_rect = draw(screen)
pygame.display.flip()
There is function to check collicion in cicle area but it works for two pygame.sprite.Sprite, not for single pygame.Rect and mouse position (single point). You would have to convert mouse position and pygame.Rect to pygame.sprite.Sprite but it is to complex for this problem.
Doc: pygame.sprite.collide_circle()
OR you can use pygame.math.Vector2.distance_to() to calculate distance between mouse position and center of circle - it has to be equal or smaller then 25
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
pos = pygame.math.Vector2(event.pos)
if pos.distance_to(circle_rect.center) <= 25:
screen.fill(WHITE)
circle_rect = draw(screen)
import sys
import random
import pygame
# --- constants ---
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
WIDTH = 500
HEIGHT = 500
# --- functions ---
def draw(screen):
print('Clicked')
x = random.randrange(30, WIDTH-30)
y = random.randrange(30, HEIGHT-30)
circle_rect = pygame.draw.circle(screen, (0, 0, 0), (x, y), 25)
return circle_rect
# --- main ---
musica = 'circles.mp3'
pygame.init()
pygame.mixer.music.load(musica)
pygame.mixer.music.play()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(WHITE)
circle_rect = draw(screen)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
pos = pygame.math.Vector2(event.pos)
if pos.distance_to(circle_rect.center) <= 25:
screen.fill(WHITE)
circle_rect = draw(screen)
pygame.display.flip()
EDIT:
If you will want to add other object which will move then you will have to organize it in different way. In while True in every loop you will have to clear screen and draw again all object. And this needs more changes
import sys
import random
import pygame
# --- constants ---
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
WIDTH = 500
HEIGHT = 500
FPS = 25
# --- classes ---
class Circle():
def __init__(self, x, y, r, color, random=False):
self.x = x
self.y = y
self.r = r
self.color = color
# vector to check collision
self.center = pygame.math.Vector2((x, y))
if random:
self.set_random_position()
def draw(self, screen):
#pygame.draw.circle(screen, self.color, (self.x, self.y), self.r)
pygame.draw.circle(screen, self.color, self.center, self.r)
def check_collision(self, position):
return self.center.distance_to(position) <= self.r
def set_random_position(self):
self.x = random.randint(30, WIDTH-30) # `randint` better then `randrange`
self.y = random.randint(30, HEIGHT-30) # `randint` better then `randrange`
# vector to check collision
self.center = pygame.math.Vector2(self.x, self.y)
def move_random(self):
dx = random.randint(-5, 5) # `randint` better then `randrange`
dy = random.randint(-5, 5) # `randint` better then `randrange`
self.x += dx
self.y += dy
if self.x < self.r:
self.x = self.r
if self.x > WIDTH-self.r:
self.x = WIDTH-self.r
if self.y < self.r:
self.y = self.r
if self.y > HEIGHT-self.r:
self.y = HEIGHT-self.r
self.center = pygame.math.Vector2(self.x, self.y)
# --- functions ---
# ... empty ...
# --- main ---
musica = 'circles.mp3'
pygame.init()
#pygame.mixer.music.load(musica)
#pygame.mixer.music.play()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
# create circle
circle = Circle(0, 0, 25, RED, random=True)
# create other objects and keep on list
others = [Circle(0, 0, 10, GREEN, random=True) for _ in range(100)]
clock = pygame.time.Clock()
while True:
# - events - (without draws)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
if circle.check_collision(event.pos):
# only change `x,y` without drawing
circle.set_random_position()
# - updates - (without draws)
# move other objects from list
for item in others:
item.move_random()
# - draws - (without events and updates)
# clear screen
screen.fill(WHITE)
# draw circle
circle.draw(screen)
# draw other objects from list
for item in others:
item.draw(screen)
# send on monitor
pygame.display.flip()
# - FPS -
# to keep the same speed on different computers (with different CPU speed)
clock.tick(FPS)
I know that there are several similar questions online, but none of them really helped me. I simply want to draw a grid and give the user the option to click into those grid cells. Every time the user clicks, the color/fill of the cell should change from black to white.
What I'm doing at the moment is the following:
BLACK = (0, 0, 0)
WHITE = (200, 200, 200)
def drawGrid(h, w, blocksize):
for x in range(w):
for y in range(h):
rect = pygame.Rect(x*blocksize, y*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, WHITE, rect, 1)
def handle_events():
col = WHITE
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
col = WHITE
# determine mouse position
mpos_x, mpos_y = event.pos
# determine cell number
coord = mpos_x // blocksize, mpos_y // blocksize
rect = pygame.Rect(coord[0]*blocksize, coord[1]*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, col, rect, 1)
pygame.display.update()
def main():
global SCREEN, CLOCK, blocksize
w = int( sys.argv[1] )
h = int( sys.argv[2] )
blocksize = int( sys.argv[3] )
pygame.init()
SCREEN = pygame.display.set_mode((h, w))
CLOCK = pygame.time.Clock()
SCREEN.fill(BLACK)
drawGrid(h,w,blocksize)
handle_events()
if __name__ == "__main__":
main()
The program is printing the grid. However, when I click somewhere nothing happens. I know this is not the best code, so I would appreciate for any suggestion.
I changed the code a little and it worked properly, pygame.draw.rect(SCREEN, col, rect, 1) you draw same thing and you can't see the change. You should use pygame.draw.rect(SCREEN, col, rect):
import pygame
import sys
BLACK = (0, 0, 0)
WHITE = (200, 200, 200)
# WINDOW_HEIGHT = 400
# WINDOW_WIDTH = 400
def drawGrid(h, w, blocksize):
#blockSize = 20 #Set the size of the grid block
for x in range(w):
for y in range(h):
rect = pygame.Rect(x*blocksize, y*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, WHITE, rect, 1)
def handle_events():
#coords_list = []
col = WHITE
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
col = WHITE
# determine mouse position
mpos_x, mpos_y = event.pos
# determine cell number
coord = mpos_x // 32, mpos_y // 32
rect = pygame.Rect(coord[0]*32, coord[1]*32,
32, 32)
pygame.draw.rect(SCREEN, col, rect)
#coords_list.append(coord)
pygame.display.update()
#return coords_list
def main():
global SCREEN, CLOCK, blocksize
pygame.init()
SCREEN = pygame.display.set_mode((480, 640))
CLOCK = pygame.time.Clock()
SCREEN.fill(BLACK)
drawGrid(480,640,32)
handle_events()
if __name__ == "__main__":
main()
As a suggestion, I think you should use sprite for every cell. For example like this:
class Cell(pygame.sprite.Sprite):
def __init__(self, sprite_group, x, y, cell_dimension, color=BLACK):
self.groups = sprite_group
pygame.sprite.Sprite.__init__(self, self.groups)
self.image = pygame.Surface((cell_dimension, cell_dimension))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x * cell_dimension
self.rect.y = y * cell_dimension
self.clicked = False
def update(self):
if self.clicked:
self.image.fill(WHITE)
I'm fairly new to Pygame, and can't seem to find a solid answer on this. I have a shape, specifically an ellipse, that I want to rotate both left and right. The key bind would be a and d, as the arrow keys are already binded to move left and right on an x,y axis.
I know that it involves pygame.transform.rotate, however I can't seem to implement this right.
def main():
#Setup basic variables and methods for pygame
pygame.init()
windowWidth = 800
windowHeight = 700
fps = 45
clock = pygame.time.Clock()
gameWindow = pygame.display.set_mode((windowWidth, windowHeight))
surface = pygame.Surface((50,50))
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
shipX = windowWidth/2
shipY = windowWidth/2
shipSpeed = 4
while(True):
pygame.draw.ellipse(gameWindow, WHITE, (shipX, shipY, 20, 30))
#Monitor the FPS of the game
clock.tick(fps)
for event in pygame.event.get():
# ________________________________________
if event.type == pygame.QUIT:
gameExit()
rotate = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP] and shipY > shipSpeed: shipY -= shipSpeed
if pressed[pygame.K_DOWN] and shipY < windowHeight - shipSpeed - 20: shipY += shipSpeed
if pressed[pygame.K_LEFT] and shipX > shipSpeed:shipX -= shipSpeed
if pressed[pygame.K_RIGHT] and shipX < windowWidth - shipSpeed - 20: shipX += shipSpeed
if pressed[ord('a')]: rotate = pygame.transform.rotate(surface, -20)
if pressed[ord('d')]: rotate = pygame.transform.rotate(surface, 20)
gameWindow.fill(BLACK)
# 'flip' display - always after drawing...
pygame.display.flip()
The expected result is that the shape will change it's angle, and then move accordingly.
Again, I'm very new to pygame, so any detailed help would be appreciated.
Your problem is that you draw the ellipse directly on the screen, but you should draw your ellipse on another Surface.
Then you can rotate that new Surface with pygame.transform.rotate.
Here's a simple example:
import pygame
import random
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
surface = pygame.Surface((100, 200))
surface.set_colorkey((2, 3, 4))
surface.fill((2, 3, 4))
rect = surface.get_rect(center=(100, 100))
pygame.draw.ellipse(surface, pygame.Color('white'), (0, 0, 100, 200))
angle = 0
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]: rect.move_ip(0, -5)
if pressed[pygame.K_DOWN]: rect.move_ip(0, 5)
if pressed[pygame.K_LEFT]: rect.move_ip(-5, 0)
if pressed[pygame.K_RIGHT]: rect.move_ip(5, 0)
if pressed[pygame.K_a]: angle += 1
if pressed[pygame.K_d]: angle -= 1
rotated = pygame.transform.rotate(surface, angle)
rect = rotated.get_rect(center=rect.center)
rect.clamp_ip(screen_rect)
screen.fill(pygame.Color('dodgerblue'))
screen.blit(rotated, rect.topleft)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Note that I use a Rect to store the position of the object because it's easy then to rotate the Surface around its center (by setting its center attribute), and to ensure the Surface does not go outside the screen (by using clamp_ip).
Also, it's important to always rotate the source Surface, and not the already rotated Surface. Otherwise, you'll get distortions.
Note that we have three things here: an image, a position, and some behaviour logic. Whenever you see these things together, consider putting them together into a class. Pygame already offers a nice class for this, called Sprite.
Here's the same example, but Sprite-based:
import pygame
import random
class Thingy(pygame.sprite.Sprite):
def __init__(self, area):
super().__init__()
# image is what get's painted on the screen
self.image = pygame.Surface((100, 200))
self.image.set_colorkey((2, 3, 4))
self.image.fill((2, 3, 4))
pygame.draw.ellipse(self.image, pygame.Color('white'), (0, 0, 100, 200))
# we keep a reference to the original image
# since we use that for rotation to prevent distortions
self.original = self.image.copy()
# rect is used to determine the position of a sprite on the screen
# the Rect class also offers a lot of useful functions
self.rect = self.image.get_rect(center=(100, 100))
self.angle = 0
self.area = area
def update(self, events, dt):
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]: self.rect.move_ip(0, -5)
if pressed[pygame.K_DOWN]: self.rect.move_ip(0, 5)
if pressed[pygame.K_LEFT]: self.rect.move_ip(-5, 0)
if pressed[pygame.K_RIGHT]: self.rect.move_ip(5, 0)
if pressed[pygame.K_a]: self.angle += 1
if pressed[pygame.K_d]: self.angle -= 1
# let's rotate the image, but ensure that we keep the center position
# so it doesn't "jump around"
self.image = pygame.transform.rotate(self.original, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
self.rect.clamp_ip(self.area)
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
sprites = pygame.sprite.Group(Thingy(screen_rect))
dt = 0
while True:
# nice clean main loop
# all game logic goes into the sprites
# handle "global" events
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
# update all sprites
sprites.update(events, dt)
# draw everything
screen.fill(pygame.Color('dodgerblue'))
sprites.draw(screen)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
You should make a class for your object:
class myRect(pygame.Surface):
def __init__(self, parent, xpos, ypos, width, height):
super(myRect, self).__init__(width, height)
self.xpos = xpos
self.ypos = ypos
self.parent = parent
def update(self, parent):
parent.blit(self, (self.xpos, self.ypos))
def rotate(self, angle):
#(your rotation code goes here)
I'm having a great difficulty to create a single sprite rotation using pygame.
I'm following this code (https://stackoverflow.com/a/47688650/5074998) to obtain a center rotation of a sprite. And here is my current code:
import pygame
FPS: int = 100
W = 600
H = 400
MAGENTA = (255, 0, 255)
BLACK = (0, 0, 0)
pygame.init()
screen = pygame.display.set_mode([W, H])
pygame.display.set_caption("Cars")
clock = pygame.time.Clock()
class sp():
def __init__(self):
self.image = pygame.Surface((122, 70), pygame.SRCALPHA)
pygame.draw.polygon(self.image, pygame.Color('dodgerblue1'),
((1, 0), (120, 35), (1, 70)))
self.orig_image = self.image
self.rect = self.image.get_rect(center=[W / 2, H / 2])
self.angle = 0
def update(self):
self.angle += 2
self.rotate()
screen.blit(self.image, [100, 100])
def rotate(self):
self.image = pygame.transform.rotozoom(self.orig_image, self.angle, 1)
self.rect = self.image.get_rect(center=self.rect.center)
sp1 = sp()
out = pause = False
while not out:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
out = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
pause = not pause
if not pause:
screen.fill(BLACK)
sp1.update()
pygame.display.flip()
But I can't understand why I get this:
Could anyone help me?
That happens because you're blitting the self.image at the position [100, 100] instead of the self.rect (that means at the self.rect.topleft coordinates). The size of the image is different after each rotation and if you just blit it at the same top left coordinates, the image will wobble like that because the center is somewhere else each frame. You can see that by drawing the bounding rect at [100, 100]:
pygame.draw.rect(screen, (255, 0, 0), [(100, 100), self.image.get_size()], 1)
To fix this problem, you have to create a new rect each time and set its center coordinates to the center coords of the previous rect. That will adjust the topleft coordinates (where the image gets blitted) as well, so that the image stays centered. Then just blit the image at the rect:
screen.blit(self.image, self.rect)
Draw the self.rect to see how the topleft coord changes all the time now whereas the center won't move:
pygame.draw.rect(screen, (255, 0, 0), self.rect, 1)