PyGame rendering issue -- edges are fuzzy as they move [duplicate] - python

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.

Related

How to make it so that all elements of an array are drawn

I'm making a simple parody of the game Chippy. In general, the monster(alien) must shoot and we must dodge and shoot back. I made it so that the picture of the ball is added to the array with all the other balls and removed when leaving the screen, but for some reason only one ball is drawn, and the rest are added to the array but do not appear on the screen. I am using module tkinter to get screen dimensions:
P.S. the ball is the bullets that the monster(alien) shoots, it's just that the bullets are already taken
# === ball ===
import pygame
from tkinter import *
root = Tk()
class Ball():
def __init__(self, screen, alien):
self.screen = screen
self.ball_image = pygame.image.load('images/pixil-frame-0 (1).png')
self.rect = self.ball_image.get_rect()
self.rect.centerx = alien.x
self.rect.centery = alien.y
self.all_ball = []
self.test = True
self.timer = 0
def draw_ball(self, alien):
self.screen.blit(self.ball_image, (self.rect.centerx, self.rect.centery))
def move(self):
self.rect.centery += 15
def create_ball(self, screen, alien):
for ball in self.all_ball.copy():
if ball.rect.top <= 0 or ball.rect.left <= 0 or ball.rect.right >= root.winfo_screenwidth() or ball.rect.bottom >= root.winfo_screenheight():
self.all_ball.remove(ball)
if self.test == True:
self.all_ball.append(Ball(screen, alien))
For now, I want to get the monster(alien) to shoot these balls in different directions.
all_ball should not be an attribute of Ball, because Ball is a class for one single Ball object. all_ball should be a variable in global namespace.
balls = []
def update_balls(screen, alien, create_new):
screen_rect = screen.get_rect()
for ball in balls[:]:
if not screen_rect.colliderect(ball.rect):
balls.remove(ball)
if create_new:
balls.append(Ball(screen, alien))
Draw the balls in a loop:
for ball in balls:
ball.draw_ball(screen)
However the Pygame solution would be to use pygame.sprite.Sprite and pygame.sprite.Group. See How can I add objects to a "pygame.sprite.Group()"? and What does pygame.sprite.Group() do.

I'm trying to move a monkey with arrow keys but every time I try to move it, the monkeys leaves a black trail behind

import pygame
from pygame.locals import *
pygame.init()
clock = pygame.time.Clock()
fps = 60
screen_width = 864
screen_height = 936
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Flappy Bird')
#define game variables
ground_scroll = 0
scroll_speed = 4
flying = False
game_over = False
#load images
bg = pygame.image.load('forest.png')
ground_img = pygame.image.load('ground.png')
Monkey = pygame.sprite.Sprite()
Monkey.image = pygame.image.load("monkey.png")
#boundaries
Monkey.rect = Monkey.image.get_rect()
x=400
y=200
movex=0
movey=0
#draw background
screen.blit(bg, (0,0))
#draw the ground
screen.blit(ground_img, (ground_scroll, 768))
while(True):
Monkey.rect.topleft=(x,y)
screen.blit(Monkey.image, Monkey.rect)
x=x+movex
y=y+movey
pygame.display.update()
event=pygame.event.poll()
if(event.type==pygame.QUIT):
break
elif(event.type==pygame.KEYDOWN):
#KEYDOWN means a key is pressed
if(event.key==pygame.K_RIGHT):
movex=3
elif(event.key==pygame.K_LEFT):
movex=-3
elif(event.key==pygame.K_UP):
movey=-3
elif(event.key==pygame.K_DOWN):
movey=3
elif(event.type==pygame.KEYUP):
movex=0
elif (event.type==pygame.KEYUP):
movey = 0
movex = 0
pygame.display.update()
pygame.quit()
This is my code, it's supposed to be a monkey in a forest and I want to be able to control it with the arrow keys without the monkey leaving black trail behind.For some reason every time I move the monkey, these black like leave a trail and cover the whole screen. I just can't figure out how to get rid of them.
So pygame is interesting in the way it implements animation. It is not moving your monkey but actually redrawing it over and over again on the same screen. What you need to do is redraw your background over and over on top of the old images.
<code>
#draw background
screen.blit(bg, (0,0))
#draw the ground
screen.blit(ground_img, (ground_scroll, 768))
while(True):
Monkey.rect.topleft=(x,y)
</code>
instead
<code>
while(True):
#draw background
screen.blit(bg, (0,0))
#draw the ground
screen.blit(ground_img, (ground_scroll, 768))
Monkey.rect.topleft=(x,y)
</code>
Generally you need to clean the whole screen and render all itens again for don't leave a black trail through the item was moved.
FPS = Frames Per Second. For each time that something move in a frame the frame need be deleted and a new frame need be render again, with the new position of the object, without It you just are rendering new object's movement without clean the previous and make that image of "black trail".
Solution: for each frame draw the background again, then ground, etc... You'll see that the game will be fine
CAUTION: you need find the right sequence of each event, with the wrong you will just see the background's color

How do you get pygame to give warning when player touches side of screen?

I created a game using pygame, and I wanted to get pygame to give an error like "You can't touch the screen sides", when player touches screen side. I tried searching on the internet, but I didn't find any good results. I thought of adding a block off the screen and when the player touches the block, it gives warning but that took way to long and anyways didn't work. I also don't know how to give alert, and then after giving the alert, to get the game started again. Does anyone know how to do this?
Define a pygame.Rect object for the player:
player_rect = pygame.Rect(w, y, width, height)
or get a rectangle from the player image (player_image):
player_rect = player_image.get_Rect(topleft = (x, y))
Get the rectangle of the display Surface (screen)
screen_rect = screen.get_rect()
Evaluate if the player is out of the screen:
if player_rect.left < screen_rect.left or player_rect.right < screen_rect.right or \
player_rect.top < screen_rect.top or player_rect.bottom < screen_rect.bottom:
printe("You can't touch the screen sides")
See pygame.Rect.clamp() respectively pygame.Rect.clamp_ip():
Returns a new rectangle that is moved to be completely inside the argument Rect.
With this function, an object can be kept completely in the window:
player_rect.clamp_ip(screen_rect)
You can use the clamped rectangle to evaluate whether the player is touching the edge of the window:
screen_rect = screen.get_rect()
player_rect = player_image.get_Rect(topleft = (x, y))
clamped_rect = player_rect.clamp(screen_rect)
if clamped_rect.x != player_rect.x or clamped_rect.y != player_rect.y:
printe("You can't touch the screen sides")
player_rect = clamped_rect
x, y = player_rect.topleft

Working with classes (creating a class efficiently, drawing and updating all instances of the class every frame)

I've been working on a "bullet hell" game of my own, but I am struggling very much with working with classes (im new to python), below i've enclosed my code(I wouldnt include so much but i dont know howo to go about the problem). At the moment what i've been attempting to
1. Create different instances of the class
2. Draw them every frame
3. Update their position every frame according to the x and y speed
import pygame
# Define colors needed
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
bulletArray = []
BulletSprite = ("Bullet4.png")
pygame.init()
class Bullet:
def __init__(self, sprite, x, y, xSpeed, ySpeed):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.xSpeed = xSpeed
self.ySpeed = ySpeed
bulletArray.append(self)
def update(self):
pass
def draw(screen):
self.screen = screen
screen.blit(screen, (x, y))
#Set the width and height of the game screen
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Bullet stuff")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
testBullet = Bullet(BulletSprite, 200, 200, 2, 2)
#Main Program Loop
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Game logic should go here
print(bulletArray)
# --- background image.
screen.fill(WHITE)
# --- Drawing code should go here
# --- Updates the screen every frame with whats been drawn
pygame.display.flip()
# --- Limit to 60 frames per second
clock.tick(60)
# Close the window and quit.
pygame.quit()
Much of the this mechanism is already handled automatically if you use the PyGame Sprite Class.
A PyGame sprite is essentially made of:
An Image to draw to represent the object
A rectangle around the object to remember the location and check for collisions
An update() function to handle any movement of the sprite
It's handy to work with the existing PyGame Sprite layout because a lot of functionality is already provided by the library code.
So let's make a BulletSprite:
class BulletSprite( pygame.sprite.Sprite ):
def __init__( self, bitmap, x, y ):
pygame.sprite.Sprite.__init__( self )
self.image = bitmap # How the sprite looks
self.rect = bitmap.get_rect() # Bounding box the size of the image
self.rect.centerx = x # position the bullet
self.rect.centery = y # about its centre
And that's it. This will give you a stationary sprite, painted as the given bitmap at (x,y) on-screen.
So let's now use it:
# Create a group of 100 bullets
sprite_image = pygame.image.load( "bullet.png" )
all_bullets = pygame.sprite.Group() # Group to hold the sprites
for i in range( 100 ):
random_x = 50 + random.randrange( 950 ) # TODO: use proper screen size
random_y = 50 + random.randrange( 950 )
new_sprite = BulletSprite( sprite_image, random_x, random_y )
all_bullets.add( new_sprite )
# Main Window loop
while True:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
break
# Paint the window
window.fill( ( 0, 0, 0 ) ) # Paint it Black
all_bullets.update() # move all the bullets
all_bullets.draw( window ) # Paint all the bullets
pygame.display.flip()
pygame.quit()
And that's it. After the sprites are created, it takes two lines of code to move them all, and draw them all.
But they wont move yet. For them to move, the BulletSprite needs to have an update() function. This function does whatever is necessary to move the sprite - apply velocity, gravity, whatever. The net effect is the sprite's rectangle's (x,y) is changed.
So adding some changes to the sprite - first an x and y velocity, and then the movement code.
class BulletSprite( pygame.sprite.Sprite ):
def __init__( self, bitmap, x, y ):
pygame.sprite.Sprite.__init__( self )
self.image = bitmap # How the sprite looks
self.rect = bitmap.get_rect() # Bounding box the size of the image
self.rect.centerx = x # position the bullet
self.rect.centery = y # about its centre
self.x_move = random.randrange( -3, 3 ) # simple random velocity
self.y_move = random.randrange( -3, 3 )
def update( self ):
x = self.rect.centerx + self.x_move # calculate the new position
y = self.rect.centerx + self.y_move # by applying an offset
# has the sprite gone off-screen
if ( x < 0 or x > 1000 or y < 0 or y > 1000 )
self.kill() # remove from the group
else:
self.rect.centerx = x # RE-position the bullet
self.rect.centery = y
Every time the update() is called on your sprite group (all_bullets), the sprite library will go and call the update() function on every sprite in the group. The code checks to see if the bullet is off-screen (I lazily used 1000 as a placeholder for screen width & height), and if-so, the sprite.kill() function handles the removing from the group and cleaning up. It's very easy.

Software Design and Development Major: Pygame Smudge Trails

First off, i have searched online and this website for solutions and the ones i have tried are not working so i decided to post my individual question and code. This program was created using Python 3.2.2 and the corresponding compatible version of pygame. I also realize a more efficient method would be to use sprites, sprite groups and 'dirty rect' updating but i unable to convert the program and so i will continue without the added benefits of such functions.
Problem: Smudge trails where the 'asteroids' are moving are left behind.
Hypothesis: Background is blitted onto the screen however the asteroids are blitted onto the Background.
Please Reply - btw i'm a highschooler from AUS :D
import pygame
import random
import math
pygame.init()
height = 550
width = 750
screen = pygame.display.set_mode((width, height))
background = pygame.image.load("Planet.jpg")
Clock = pygame.time.Clock()
class asteroid(pygame.sprite.Sprite):
def __init__(self, x, y, size):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.size = 15
self.speed = 0.0
self.angle = 0
self.colour = (171, 130, 255)
self.thickness = 0
def display(self):
pygame.draw.circle(background, self.colour, (int(self.x),int(self.y)), self.size, self.thickness)
pygame.draw.circle(background, (255, 255, 255), (int(self.x),int(self.y)), self.size, 1)
def move(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
def boundaries(self):
if self.x > width - self.size:
self.x = 0 + self.size
elif self.x < self.size:
self.x = width - self.size
if self.y > height - self.size:
self.y = 0 + self.size
elif self.y <self.size:
self.y = height - self.size
num_target = 5
my_particles = []
num_particles = len(my_particles)
while num_particles < 5:
for n in range(num_target):
size = 20
x = random.randint(size, height - size)
y = random.randint(size, width - size)
target = asteroid(x, y, size)
target.speed = random.uniform(1.0, 1.0)
target.angle = random.uniform(0, math.pi*2)
my_particles.append(target)
num_particles = num_particles + 1
def main():
pygame.display.set_caption("Anyu's Game")
screen.blit(background, (0,0))
pygame.display.update()
score = (pygame.time.get_ticks()/1000)
print (score)
while True:
pygame.display.update()
screen.blit(background, (0,0))
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
target.display()
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit();
if __name__=='__main__':
main()
Basically, you are right! The circles are drawn directly onto the background, and everytime new circles are drawn, the old circles remain. Resulting in the smudges/trails.
You can just change background to screen in your draw method. This will fix it.
But it is really worth using the Sprite classes as intended. I've made a few changes to your code to switch it over for you. With these changes it runs without trails :)
Here are the changes and explainations:
Add this near the top:
#Create a new `pygame.Surface`, and draw a circle on it, then set transparency:
circle = pygame.Surface((30,30))
circle = circle.convert()
pygame.draw.circle(circle, (171, 130, 255), (int(15),int(15)), 15, 0)
circle.set_colorkey(circle.get_at((0, 0)), pygame.RLEACCEL)
Add this to the asteroid, __init__ method:
#Sets the asteroid image, and then the asteroids co-ords (these are in `rect`)
self.image = circle
self.rect = self.image.get_rect()
Add this to the end of def move(self):
self.rect[0] = self.x
self.rect[1] = self.y
change:
my_particles = []
to:
#This is a special pygame container class, it has a draw() method that tracks changed areas of the screen.
my_particles = pygame.sprite.RenderUpdates()
change:
my_particles.append(target)
to:
my_particles.add(target)
change:
while True:
pygame.display.update()
screen.blit(background, (0,0))
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
target.display()
pygame.display.update()
to:
#initial screen draw:
screen.blit(background, (0,0))
pygame.display.update()
while True:
#remove previous drawn sprites and replaces with background:
my_particles.clear(screen, background)
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
#draws changed sprites to the screen:
pygame.display.update(my_particles.draw(screen))
Remove the display method as it is no longer needed.
This will also run a lot faster than the your earlier code, as the time taken to draw something is proportional to the size of the drawing area, and previously it was drawing the whole background everytime - now it only draws the sprites and changes to the background!
Hope this helps :)
This already has an answer but this can be useful instead of other methods.
Make sure when you blit the images onto the screen, flip the display after blitting everything.
I would consider making a draw() function
Like this:
def draw(self):
# Blit images
self.screen.blit(image)
# Flip display
pygame.display.flip()
This will flip the display every frame and then draw the next frame without a trail.
Also quick notes, remember to do image = pygame.image.load(image).convert or .convert_alpha() else after adding more images the game will slow down.
Also, if you do import pygame as pg you don't have to type out pygame each time, instead you can just type pg.

Categories