This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
How can you rotate an image around an off center pivot in Pygame
(1 answer)
Closed 2 years ago.
I've got a little problem that I can't seem to solve. I can't find it on stackoverflow yet.
It's about rotating an image. I've got two 'gamepieces' in the example here, where I can show the problem. If I click on one line, it rotates 90 degrees. But after it rotated 180 degrees (which is precise, because I made sure it ends on exactly 90, 180, 270 etc degrees) the images don't connect well anymore. if I rotate it another 180 degrees, then it's fine again..
Screen 1, nothing bad here:
Screen 2, here you see the two lines don't connect well.
I tried changing the resolution to different pixel sizes (64, 64) and (81, 81) for example. But this does not cure the problem.
And here is part of the code:
class Piece:
def __init__(self, piece, x, y):
self.image = pygame.image.load(piece)
self.rotated_image = self.image
self.rect = self.rotated_image.get_rect()
self.rect.center = x, y
self.current_degrees = 0
self.degrees = 0
self.values = [0, 1, 0, 1]
def rotate_piece(self):
self.rotated_image = pygame.transform.rotozoom(self.image, lerp(self.current_degrees, self.degrees, 0.3), 1)
self.rect = self.rotated_image.get_rect(center=self.rect.center)
self.current_degrees = lerp(self.current_degrees, self.degrees, 0.3)
def rotate_values(self):
temp = self.values[0]
for i in range(len(self.values) - 1):
self.values[i] = self.values[i+1]
self.values[3] = temp
print(self.values)
pieces = []
piece1 = Piece(images['line'], 32, 32)
piece2 = Piece(images['line'], 96, 32)
pieces.append(piece1)
pieces.append(piece2)
Related
This question already has answers here:
How to set the pivot point (center of rotation) for pygame.transform.rotate()?
(5 answers)
How can you rotate an image around an off center pivot in Pygame
(1 answer)
How do I rotate an image around its center using Pygame?
(6 answers)
Closed 22 days ago.
I have an interesting problem with sprits in pygame.
Essentially, I am trying to create an archer sprite which will follow a target.
To do so, I need it to rotate. Naturally, I don't want the whole archer to rotate - just his shoulders/head etc. To do so I have create two separate spritesheets (for the top and bottom)
I can then create the sprite image using:
class Archer(pygame.sprite.Sprite):
def __init__(self, group, pos, size):
super().__init__(group)
self.pos = pos
self.assets_legs = GetAssets('../graphics/archer_fixed.png', size)
self.frames_legs = self.assets_legs.sprite_images
self.assets_body = GetAssets('../graphics/archer_rotate.png', size)
self.frames_body = self.assets_body.sprite_images
self.frame_index = 0
self.angle = 0
self.archer = [self.frames_legs[round(self.frame_index)], self.frames_body[round(self.frame_index)]]
self.image = pygame.Surface((size), pygame.SRCALPHA)
for image in self.archer:
self.image.blit(image, (0,0))
This is fine and works well. However, when I want to rotate the top half and blit this to the self.image, as in:
def rotate(self):
self.rot_img = self.archer[1]
self.rot_img = pygame.transform.rotate(self.rot_img, 1)
self.image = pygame.Surface((75,75), pygame.SRCALPHA)
self.archer = [self.frames_legs[round(self.frame_index)], self.rot_img]
for image in self.archer:
self.image.blit(image, (0,0))
self.rect = self.image.get_rect(center = self.pos)
I get strange artifacts. I suspect because I am trying to blit a surface with a rotated dimension to a self.image. The only work around I can think of is to create two separate sprites and then have them track each other, but the must be a way to do this in one class.
Any ideas?
Cheers
This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Pygame collision with masks
(1 answer)
Closed 6 months ago.
I would like to draw sprites with 2 features: a base image (let's say a square image that I pass as argument) and a togglable view of their circular detection radius, for debug purposes and checking that collisions with a circular mask actually work.
Note: the main question is not about to detect collisions as in How do I detect collision in pygame?, but rather can a sprite have several surfaces attached to it and why is my draw_mask_outline function returning croppped circles? (see attached screenshots)
Here's an example of my class:
class Robot(pygame.sprite.Sprite):
def __init__(self, x, y, image, detection_radius, mouse_group, *groups):
super().__init__(*groups)
# extra image added for changing color if mouse is detected within
# detection radius
# I have used the mouse as a debug tool but in the end the Robot
# should have no knowledge of the other groups positions (and should
# not be able to compute a euclidean distance from a sprite group) but
# simply detect if a sprite of a certain sprite group has entered its
# radius
other_img = pygame.Surface((30, 30))
other_img.fill('black')
self.img_idx = 0
self.images = [image, other_img]
self.image = self.images[self.img_idx]
self.pos = pygame.Vector2(x, y)
self.rect = image.get_rect(center=self.pos)
self.detection_radius = detection_radius
self.mask_surface = pygame.Surface((detection_radius * 2, detection_radius * 2), pygame.SRCALPHA)
self.detection_mask = self.make_circle(self.notice_radius)
self.mask = pygame.mask.from_surface(
self.make_circle_surface(detection_radius))
sekf.mouse_group = mouse_group
def draw_mask_outline(self):
# this function should draw the outline of the circular detection mask
display_surf = pygame.display.get_surface()
pts = self.mask.outline()
for pt in pts:
x = pt[0] + self.rect.centerx
y = pt[1] + self.rect.centery
pygame.draw.circle(display_surf, 'black', (x, y), 1)
def make_circle_surface(self, radius):
# I used this function to generate the circular surface
mask_surf = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)
pygame.draw.circle(mask_surf, (0, 0, 0), (self.rect.centerx, self.rect.centery), radius)
return mask_surf
def collision(self):
# collision detection function, if mouse inside detection radius do something
if pygame.sprite.spritecollide(self, self.mouse_group, False, pygame.sprite.collide_mask):
pygame.draw.circle(pygame.display.get_surface(), (0, 0, 0), (self.rect.centerx, self.rect.centery), self.notice_radius, 1)
self.img_idx = 1
self.image = self.images[self.img_idx]
else:
self.img_idx = 0
self.image = self.images[self.img_idx]
def update(self):
# the overall update function where all the robot logic will happen
self.draw_mask_outline()
As fas as I've understood how the lib works, I should create a surface around the original image, big enough to fit the circle but this does not seem compatible with already having a self.image. Then I should draw a circle on that new surface.
Will this new surface follow the sprite when it moves too?
I managed to draw the circle inside the original image but this is not what is expected since the starting is smaller than the circle. In some cases, I also managed to generate surfaces, transform them to mask, then using the mask to generate an outline (hence the self.mask) but these outlines are always drawn far away from the sprite positions, are incomplete or completely missing...
Here are some screenshots of the current version and what I could achieve.
When the mouse is outside the detection ring, outlines are drawn with a severe offset, are sometimes incomplete and some robots have no outline.
Displaced and incomplete outlines
When the mouse is inside, the collision function draws the correct circle (but this is not the mask).
Collisions debug ring
Here are links to some solutions that I have tested and that did not work (if I've understood them correctly that is...):
pygame: mask non-image type surfaces
Pygame: Why i can't draw a circle sprite
how to create a circular sprite in pygame
OOP Pygame Circle
Ultimately the goal is to have the robots roam around and detect sprites from another group of robots that enter their detection radius.
Any help would be very much appreciated!
How can I get a pygame rect to move smoothly? Like if I update the x position by 2 it looks smooth but if I update it by bigger number like 25 it teleports to the position. Also, if possible, can this work for decimals also?
Visual Representation
import pygame
import math
GREEN = (20, 255, 140)
GREY = (210, 210 ,210)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
class Dot(pygame.sprite.Sprite):
# This class represents a car. It derives from the "Sprite" class in Pygame.
def __init__(self, color, width, height):
# Call the parent class (Sprite) constructor
super().__init__()
# Pass in the color of the car, and its x and y position, width and height.
# Set the background color and set it to be transparent
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
self.color = color
self.width = width
self.height = height
pygame.draw.rect(self.image, self.color, [0, 0, self.width, self.height])
self.rect = self.image.get_rect()
How can I get a pygame rect to move smoothly?
If your rectangle has to move 25px per frame, then the it makes no sens to draw the rectangles at the positions in between. The display is updated once per frame and it make no sens at all to draw the rectangle at the positions in between.
Possibly you have to less frames per second. In that case you have to increase the framerate and you can decrease the movement. Note, the human eye can just process a certain number of images per second. The trick is that you generate enough frames, that the movement appears smooth for the human eye.
pygame.Rect can store integral values only. If you want to operate with a very high framerate and floating accuracy, then you have to store the position of the object in separate floating point attributes. Synchronize the rounded position to the rectangle attribute. Note, you cannot draw on a "half" pixel of the window (at least in pygame).
e.g.:
class Dot(pygame.sprite.Sprite):
# This class represents a car. It derives from the "Sprite" class in Pygame.
def __init__(self, color, x, y, width, height):
# Call the parent class (Sprite) constructor
super().__init__()
# Pass in the color of the car, and its x and y position, width and height.
# Set the background color and set it to be transparent
self.x = x
self.y = y
self.image = pygame.Surface([width, height])
self.image.fill(self.color)
self.rect = self.image.get_rect(center = (round(x), round(y)))
def update(self):
# update position of object (change `self.x`, ``self.y``)
# [...]
# synchronize position to `.rect`
self.rect.center = (round(x), round(y))
This question already has answers here:
How to make a circle move diagonally from corner to corner in pygame
(1 answer)
How to make smooth movement in pygame
(2 answers)
Closed 2 years ago.
When a certain value is met I want my enemy sprite to return to his intial position,but now hes just disappearing and reappearing in that position.
I'm only changing the Y position
def update(self):
self.count += 1
x_component = self.ship.rect.centerx-self.enemy2X
y_component = self.ship.rect.centery-self.enemy2Y
distance = math.hypot(x_component, y_component)
if distance < 200:
self.currentState = 1
print distance
elif distance > 200:
self.currentState = 0
self.enemy2Y=68
print self.enemy2Y
I am creating my first game ever using pygame and I've found that in order to animate things the most popular method is to use bit blit.
However I have a few questions regarding this:
From what I understood, when you use bit blit you have to "redraw" on the screen every single object that was present before in order for it to work correctly. Is this correct?
If so... I am drawing a "scene" of buildings using rects (rectangles) (the buildings each have different colors (randomly geneated), different heights (random) and they also have windows which are of 2 different alternating colors). What would be the best way for my Building class to remember every color it had for the building and its windows so that when i bit blit the building doesn't get different colors to make it more realistic?
You could have a simple Building class:
class Building:
def __init__(self, x, y, w, h, color):
self.x = x
self.y = y
self.w = w
self.h = h
self.color = color
def draw(self):
// code for drawing the rect at self.x,self.y
// which is self.w wide and self.h high with self.color here
Concerning the windows, you could specify each one in a list like [(x, y, w, h)] for each building or simply make a building class that looks like this:
class Building:
def __init__(self, x, y, w, h, color, wx, wy):
self.x = x
self.y = y
self.w = w
self.h = h
self.color = color
self.wx = wx
self.wy = wy
def draw(self):
// code for drawing the rect at self.x,self.y
// which is w wide and h high with self.color here
// Draw wx windows horizontally and wy windows vertically
for y in range(0, self.wy):
for x in range(0, self.wx):
// draw Window code here
Another approach would be that you "prerender" your buildings into an image an then just display that afterwards(that could also be faster if you have a lot of buildings).
And your gameloop could then look something like this
buildingList = [Building(0, 0, 15, 50, RED), Building(0, 0, 40, 30, BLUE)]
while gameIsRunning:
// Clear screen code here
// Show Building
for b in buildingList:
b.draw()
// More stuff
That is pretty much the most basic approach for drawing anything, you could draw characters this way, keys or even tiles that are supposed to be above you character, e.g. water tiles in a platformer like Tuff. The trees here are also in one big list(ok actually i maintain a smaller list with the trees that are on the 1 1/2 sourrounding screens for performance reasons. there are over 1500 "trees").
EDIT:
In the case of different window colors, there two possible solutions.
Using different window colors per building:
class Building:
def __init__(self, x, y, w, h, color, wx, wy, windowColor):
self.x = x
self.y = y
self.w = w
self.h = h
self.color = color
self.wx = wx
self.wy = wy
self.windowColor = windowColor
def draw(self):
// code for drawing the rect at self.x,self.y
// which is self.w wide and self.h high with self.color here
// Draw wx windows horizontally and wy windows vertically
for y in range(0, self.wy):
for x in range(0, self.wx):
// draw Window code here using self.windowColor
Possibility 2, with different colors per window:
class Building:
def __init__(self, x, y, w, h, color, windows):
self.x = x
self.y = y
self.w = w
self.h = h
self.color = color
self.wx = wx
self.wy = wy
self.windows = windows
def draw(self):
// code for drawing the rect at self.x,self.y
// which is self.w wide and self.h high with self.color here
// Draw all windows
for w in windows:
// draw Window at w[0] as x, w[1] as y with w[2] as color
// Create a building at 0,0 that is 20 wide and 80 high with GRAY color and two windows, one at 2,2 which is yellow and one at 4, 4 that's DARKBLUE.
b = Building(0, 0, 20, 80, GRAY, [(2, 2, YELLOW), (4, 4, DARKBLUE)])
Yes, consider the screen to be like a canvas you paint onto. Once the scene is finished and shown to the viewer, you start the next scene (aka 'frame') by painting over the top of it, replacing everything that was there. Movement is represented by repeatedly painting the same thing at slightly different places. It's much like traditional animation in film - show a series of subtly different pictures to present the illusion of motion. You typically do this several tens of times per second.
It's not just pygame/SDL's bit blit that works this way - pretty much all real time computer graphics for work this way. However some systems may hide this from you and do it under the covers.
For your buildings and their colours, you want what goes to the screen to be a representation of your game objects. You don't want to draw something and then try to 'remember' what you drew. The rendering should just be a view of the objects and never something authoritative. So when you generate these random heights and colours, that would be done long before the drawing phase. Store these values as part of your building objects, probably at creation time. Then when you come to draw the building each frame, all the information you need is right there and will remain consistent each time you draw it.
You may find the answer to your first question here:
http://en.wikipedia.org/wiki/Bit_blit
Yes. You need to use "painter's algorithm" to draw your scene from back to front.
So, for each frame of animation, you'd draw the background first, then the buildings, and then anything in front of the buildings. You don't need to "clear" the screen if the background covers the whole screen.