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))
Related
So I'm trying to draw a rectangle with another rectangle on top, using Sprites. I made a class for the player and did some basic setup, but when I tried to blit the second rectangle on top, it didn't work. I did some testing and found out that I can't even draw lines or rectangles from inside this player class.
Here's my basic testing code:
import pygame as pg
pg.init()
width, height = 800, 800
screen = pg.display.set_mode((width, height))
run = True
class Player(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pg.image.load("test_image.png")
self.rect = self.image.get_rect()
def update(self):
pg.draw.rect(screen, [0, 255, 0], (200, 200, 100, 50))
print("test")
def draw_window():
screen.fill((255, 255, 255))
playerGroup.draw(screen)
pg.display.update()
playerGroup = pg.sprite.GroupSingle()
playerGroup.add(Player())
while run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
playerGroup.update()
draw_window()
This is what i get: image
The blue thing is the player image that gets drawn normally in the top left corner. The rect that i'm trying to draw inside the update() method however is nowhere to be seen, even though i can clearly see the method gets called with the print("test"). This isn't just true for pg.draw() but also for surface.blit()
Why is this and how do i fix it?
screen.fill((255, 255, 255)) fills the entire display with a white color. Anything previously drawn will be lost. You must call playerGroup.update() after clearing the display and before updating the display. e.g.:
def draw_window():
screen.fill((255, 255, 255))
playerGroup.update()
playerGroup.draw(screen)
pg.display.update()
I need to store a Circle in a variable but after I've done that it has turned into a rect
circle_1 = pygame.draw.circle(screen, (0, 0, 0), (300, 300), 30)
Print(circle_1)
the print returns
<rect(270, 270, 60, 60)>
but I can't work with that.
My circle is predefined but it won't show it on the canvas, here is an example of the problem
> import pygame, sys
>
>
> pygame.init() screen = pygame.display.set_mode((600, 600))
> predefined_circle = pygame.draw.circle(screen,(0, 0, 0),(300, 300), 30)
>
> def update():
> screen.fill((200, 0, 0))
> while 1:
> for event in pygame.event.get():
> if event.type == pygame.QUIT: sys.exit()
> # It shows my circle if I dirctly tip pygame.draw.circle(screen,(0, 0, 0),(300, 300), 30) into it
> predefined_circle
> pygame.display.update()
>
> update()
So that you can better relate to what I'm trying to achieve here is the code of what I'm doing but it is not necessary to read as I've already tried to explain it as best as I can above.
Please note the comments should explain everything that the block of code below it is doing.
# Creating the canvas which can paint any wanted Object from another class
class Canvas:
# Initialising the screen and setting all needed variables
def __init__(self, painting):
pygame.init()
self.screen_size = (600, 600)
self.background = (25, 255, 255)
self.screen = pygame.display.set_mode(self.screen_size)
self.paint = painting
# Let the user set the name of the canvas
def set_screen_name(self):
return self.screen
# Draw the everything you want to
def update(self):
# Paint the canvas
self.screen.fill(self.background)
# Make the game be quittable
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
# Draw the defined Circle and then update the Canvas
# it only draws a circle if directly tip pygame.draw.circle(surface, color, position, radius)
self.paint
pygame.display.update()
# Draw any circle you like
class Cir:
# Get all the required Information's to Draw a circle
def __init__(self, canvas, what_color, position, radius, line=0):
self.can = canvas
self.color = what_color
self.pos = position
self.r = radius
self.line = line
self.cir = None
# Create the circle with the acquired Information's
def create(self):
self.cir = pygame.draw.circle(self.can, self.color, self.pos, self.r, self.line)
return self.cir
# So far there is no Surface for the Cir class
# And there is no Object that cloud be painted for the Canvas class
# I initialise a canvas instance without anything that needs to be painted
get_surface = Canvas(None)
# Now I can access set_screen_name from the Canvas class and give the surface a name
# Which the Cir class can now use as a surface
screen = get_surface.set_screen_name()
c1 = pygame.draw.circle(screen, (0,0,0), (300, 300), 30)
print(c1)
# I'm initialising a circle
init_circle = Cir(screen, (0, 255, 0), (300, 300), 30)
# Create the initialised circle
circle_1 = init_circle.create()
# Give the Canvas class the created circle
paint = Canvas(circle_1)
# Draw the circle
paint.update()
My circle turns to a rect.
Actually, no, it doesn't. As per the documentation for those drawing functions, the intent of the calls is to draw something immediately, not to give you an object you can draw later:
Draw several simple shapes to a Surface.
From analysis of your question, it sounds like you believe that you are storing the act of drawing the circle so that it can be done later. That is not the case. Instead, what you are doing is actually drawing the circle and saving the result of that drawing action - evaluating the result later on will not actually draw, or redraw, the circle.
So, if the draw function is not returning something for later drawing, what is it returning? That can also be found in the above-mentioned documentation:
The functions return a rectangle representing the bounding area of changed pixels.
In other words, it's telling you the smallest rectangle that was changed by the drawing action - this will be a square with sides the same length as the diameter and centered around the same point.
Obviously, the authors of PyGame thought this information may be handy for some purpose, just not the purpose of redrawing the circle :-)
One way to do what you're trying to achieve would be to simply have a function to draw the "predefined" circle and call that instead of trying to evaluate the rectangle returned from a previous call.
So I'm making an rpg project in Pygame and I need a button class that has text. This is my Code so far. I tried to use some code examples online and on this site but I couldn't make them work in the way I wanted. ;-;
What I want is a button that can drawn to my GameWindow that includes text. I'll figure out the event handling later on.
It would be greatly appreciated if someone could give me an explanation of how a button class that utilises text would work in pygame and explain it in a way I could implement in my Button Class. Previously I have tried simply placing text in the centre of the screen by dividing the width and height by two and placing coloured rects adjacent to the text to try and label the rects so I could use them as buttons. However I realised this wasn't a practical solution, as I would be needing many buttons throughout my game and this method took up large portions of my screen.
I do not understand how to blit a message onto a rect using a class. The Button class below is where I attempted to place text onto top of a rect but I found this very hard.
Ideally my goal here is to be able to call an instance of my button class which I can use as a button.
BTW asking here was a last resort. I spent almost three hours trying to figure this out and its bad for me to stare at a screen for that long.
import pygame, random, sys, math, time
from pygame.locals import *
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
GameWindow = pygame.display.set_mode((650,520))
#Variables
Blue = (0,0,255)
Green = (0,255,0)
Red = (255,0,0)
White = (255,255,255)
Black = (0,0,0)
def Button():
def__init__(self, surface, x, y, width, height, colour, message, action=None)
self.x = x
self.y = y
self.width = width
self.height = height
self.font = pygame.font.Font(None, 20)
self.message = message
background_image = pygame.image.load('map.JPG')
title_image = pygame.image.load('title.PNG')
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
GameWindow.blit(background_image, [0,0])
GameWindow.blit(title_image, [100,0])
pygame.display.flip()
fpsClock.tick(FPS)
Here is a button class:
class Button(object):
global screen_width,screen_height,screen
def __init__(self,x,y,width,height,text_color,background_color,text):
self.rect=pygame.Rect(x,y,width,height)
self.x=x
self.y=y
self.width=width
self.height=height
self.text=text
self.text_color=text_color
self.background_color=background_color
self.angle=0
def check(self):
return self.rect.collidepoint(pygame.mouse.get_pos())
def draw(self):
pygame.draw.rect(screen, self.background_color,(self.rect),0)
drawTextcenter(self.text,font,screen,self.x+self.width/2,self.y+self.height/2,self.text_color)
pygame.draw.rect(screen,self.text_color,self.rect,3)
Use the check function to see if your button is clicked on, and the draw function to draw your button.
Implemented into your main loop:
button=Button(x,y,width,height,text_color,background_color,text)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type==pygame.MOUSEBUTTONDOWN:
if button.check()==True:
#do what you want to do when button is pressed
GameWindow.blit(background_image, [0,0])
GameWindow.blit(title_image, [100,0])
pygame.display.flip()
fpsClock.tick(FPS)
I also recommend using these functions to draw text:
def drawTextcenter(text,font,screen,x,y,color):
textobj=font.render(text,True,color)
textrect=textobj.get_rect(center=(x,y))
screen.blit(textobj,textrect)
def drawText(text, font, surface, x, y,color):
textobj=font.render(text, 1, color)
textrect=textobj.get_rect()
textrect.topleft=(x, y)
surface.blit(textobj, textrect)
While you're question is still a bit confusing, I can tell you how you blit your text near or in your button. So what you just do is just place the location of the text near the button, basing the texts x and y variables on the buttons x and y variable.
Copied from your code:
def Button():
def__init__(self, surface, x, y, width, height, colour, message, action=None)
self.x = x
self.y = y
self.width = width
self.height = height
self.font = pygame.font.Font(None, 20)
self.message = message
self.font = pygame.font.SysFont('Comic Sans MS', 30) #Example Font
def draw_button(self):
pygame.draw.rect(GameWindow, Red, (self.x, self.y, self.width, self.height))
self.text = myfont.render(message, False, (0, 0, 0))
GameWindow.blit(self.text, (self.x + self.width/2, self.y + self.height/2)) #Displays text at coordinates at middle of the button.
This draws the button (it still doesn't do anything), but also displays the text in the button. HOWEVER, since the text is displayed at the top-left corner of the surface it is on, it will not be exactly in the middle, and will look odd. You can modify the exact location if you want.
I hope this answers your question.
I am struggling to move the sprite correctly. Instead of smooth move I can see blur move and I do not know how to solve it.
Is there any chance you can point what I do incorrectly ?
My target with it to drop the pizza so it hits the bottom and bounce back and bounc back if it hits the top and again the bottom -> bounce -> top -> bounce etc. etc.
import pygame
gravity = 0.5
class PizzaSprite:
def __init__(self, image, spritepos):
self.image = image
self.spos = spritepos
(x, y) = spritepos
self.posx = x
self.posy = y
self.xvel = 1
self.yvel = 1
print "x ", x
print "y ", y
def draw(self, target_surface):
target_surface.blit(self.image, self.spos)
def update(self):
self.posy -= self.yvel
self.spos = (self.posx, self.posy)
return
def main():
pygame.init()
screen_width = 800
screen_height = 600
x = screen_width
y = screen_height
screen = pygame.display.set_mode((screen_width, screen_height))
wall_image = pygame.image.load("wall.png")
sky_image = pygame.image.load("sky.png")
pizza_image = pygame.image.load("pizza.png")
screen.blit(wall_image,(0,200))
screen.blit(sky_image,(0,0))
all_sprites = []
pizza1 = PizzaSprite(pizza_image, (x/2, y/2))
all_sprites.append(pizza1)
while True:
ev = pygame.event.poll()
if ev.type == pygame.QUIT:
break
for sprite in all_sprites:
sprite.update()
for sprite in all_sprites:
sprite.draw(screen)
pygame.display.flip()
pygame.quit()
main()
in the beginning of your main game while loop add
white = (255,255,255)
screen.fill(white)
let me give you a small analogy of what is happening now,
you have paper and a lot of pizza stickers with the intent to make a flip book. To make the illusion of movement on each piece of paper you place a sticker a little bit lower. The screen.fill command essentially clears the screen with the rgb color value you give it. When you dont fill the screen essentially what you are doing is trying to make that flipbook on one piece of paper. You just keep placing more and more stickers a little bit lower making a blur when what you want is one on each page.
and place
pygame.init()
screen_width = 800
screen_height = 600
x = screen_width
y = screen_height
screen = pygame.display.set_mode((screen_width, screen_height))
wall_image = pygame.image.load("wall.png")
sky_image = pygame.image.load("sky.png")
all outside of your main game loop assuming you wont be making changes to these variables ever in your program it is tedious and inefficient to redefine screen,,x,y,and your two images over and over again if they dont change.
so to sum it all up:
use the screen.fill(white) command to reset the color of your screen
You only need to import pngs and define variables once if they are never going to change and don't need them in your main loop
hope this helps clear things up.
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.