python pygame blit function - python

When I use blit function, it does not delete the previous loaded sprite to make sprites move until I call the "display.fill(bgcolor)" function. The problem is that I have a multicolored background. so how do I update the image without affecting my background?
NOTE - already tried "pygame.display.update()" and "pygame.display.flip()" - it doesn't help :(
class states():
def __init__(self, goku1,goku2, x, y):
self.image=goku1
keys=pygame.key.get_pressed()
if keys[K_RIGHT]:
self.image=goku2
if keys[K_LEFT]:
self.image=goku2
while True:
pygame.display.flip()
pygame.display.update()
obj=states(goku1, goku2, x, y)
call=position()
DISPLAYSURF.blit(obj.image, (x, y))
am stuck for long :(

You would blit the background first, and then blit the new location for the sprite that is moving. It would look something like this:
window= pygame.display.set_mode(WINDOWSIZE, 0, 32)
while True:
#update events
window.blit(your_multi_colored_background, (0, 0))
window.blit(obj.image, (x, y))
pygame.display.update()
Hope this helps.

Blit never delete previous element - it can't - all blitted elements create one bitmap.
You have to blit all elements again in all loop.
Or you have to keep part of background before you blit sprite and use it later to blit this part in place of sprite to remove it.
You can also use pygame.display.update() with arguments to blit only some parts of background.

Related

Can't figure out how to check mask collision between two sprites

I have two different sprites in the same group, variables 'player' and 'ground'. They both are separate classes, with a mask of their surface. This line is in both of their classes.
self.mask = pygame.mask.from_surface(self.surface)
The images used in their surfaces have 'convert_alpha()' used on them so part of them is transparent, and the mask should work on them. The ground is a few platforms, and I want to check for collision so I can keep the player on the ground, and have them fall when they are not on the non-transparent parts.
if pygame.sprite.collide_mask(player,ground):
print("collision")
else:
print("nope")
This prints "nope", even as the player sprite is falling over where the colored ground sprite pixels are. So the documentation for 'collide_mask()' says that it returns "NoneType" when there is no collision. So I tried this.
if pygame.sprite.collide_mask(player,ground)!= NoneType:
print("collision")
This prints "collision" no matter where the player is(I have jumping, left, and right movements setup for the player). I asked a question about collision yesterday with no answer that helped. And I was told to condense my code submitted in the question so hopefully I explained this well enough without posting all 90 lines. I've checked a lot of other questions on here, and they all seem to do it a little different so I'm very confused (and fairly new).
Emphasis on both sprites being in the same group, I couldn't get spritecollide() to work because of this.
The sprites do not only need the mask attribute, they also need the rect attribute. the mask defines the bitmask and rect specifies the posiotion of the sprite on the screen. See pygame.sprite.collide_mask:
Tests for collision between two sprites, by testing if their bitmasks overla. If the sprites have a mask attribute, it is used as the mask, otherwise a mask is created from the sprite's image. Sprites must have a rect attribute; the mask attribute is optional.
If sprites are used in pygame.sprite.Groups then each sprite should have image and rect attributes. pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The latter delegates to the update method of the contained pygame.sprite.Sprites — you have to implement the method. See pygame.sprite.Group.update():
Calls the update() method on all Sprites in the Group. [...]
The former uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects — you have to ensure that the pygame.sprite.Sprites have the required attributes. See pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]
Minimal example
import os, pygame
os.chdir(os.path.dirname(os.path.abspath(__file__)))
class SpriteObject(pygame.sprite.Sprite):
def __init__(self, x, y, image):
super().__init__()
self.image = image
self.rect = self.image.get_rect(center = (x, y))
self.mask = pygame.mask.from_surface(self.image)
pygame.init()
clock = pygame.time.Clock()
window = pygame.display.set_mode((400, 400))
size = window.get_size()
object_surf = pygame.image.load('AirPlane.png').convert_alpha()
obstacle_surf = pygame.image.load('Rocket').convert_alpha()
moving_object = SpriteObject(0, 0, object_surf)
obstacle = SpriteObject(size[0] // 2, size[1] // 2, obstacle_surf)
all_sprites = pygame.sprite.Group([moving_object, obstacle])
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
moving_object.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.collide_mask(moving_object, obstacle)
window.fill((255, 0, 0) if collide else (0, 0, 64))
all_sprites.draw(window)
pygame.display.update()
pygame.quit()
exit()

How do I get a shape to loop across the screen in pygame

import pygame
pygame.init()
screen = pygame.display.set_mode((1600,900))
red=(255,0,0)
blue=(0,0,204)
white=(255,255,255)
pygame.display.set_caption("Orbit")
gameLoop=True
clock=pygame.time.Clock()
while gameLoop:
dy=10
dx=10
x=600
y=250
for event in pygame.event.get():
if (event.type==pygame.QUIT):
gameLoop=False
x=x +dx
y=y +dy
screen.fill(white)
pygame.draw.circle(screen,red,[800,450],50,0)
pygame.draw.circle(screen,blue,[x,y],10,0)
pygame.display.update()
clock.tick(50)
pygame.quit()
I wrote this code here that has two circles and I want to get one (the smaller one) to loop across the screen but for some reason its not moving, I have no idea what I'm doing wrong and I've read and googled some stuff that hasn't really helped me all that much.Any advice would be greatly appreciated , thank you for your time
Your x and y values are defined inside your game loop, and so even though you're adding dx and dy to them, you're resetting them to the original values every frame.
If you move x=600 and y=250 to before the while loop, it'll start moving.

Why balls cannot move in my code in Pygame when I code 'Pong'? Can Someone help me to check my code?

I am a beginner in Pygame. I have coded a function for moving two balls in different direction and I follow the instructions coding it but it seems to be not working. I can draw two balls in screen but they will not move. I fixed it for almost 1 hour but no idea why balls aren't moving.
So, Can someone helps me check my code and just give me some hints. I will really appreciate anyone who helps me!
My code shows below
import pygame,sys,time
from pygame.locals import *
# User define function
def ball_move(Surface,white,pos,rad,speed):
size=Surface.get_size()
for item in [0,1]:
pos[item]=pos[item]+speed[item]
if pos[item]<rad:
speed[item]=-speed[item]
if pos[item]+rad>size[item]:
speed[item]=-speed[item]
# Open a brand-new window
pygame.init()
Screen_size = (500,400)
Title = ('Pong')
Frame_Delay = 0.01
Surface= pygame.display.set_mode(Screen_size,0,0)
pygame.display.set_caption(Title)
# Set up white color for drawing balls
white=pygame.Color('white')
# Now, we start to draw two balls
pos1=(100,200)
pos2=(400,200)
rad=10
ball1=pygame.draw.circle(Surface,white,pos1,rad,0)
ball2=pygame.draw.circle(Surface,white,pos2,rad,0)
pygame.display.update()
# Now, define speed
speed1=(2,-2)
speed2=(-2,2)
# Now, we define a loop
while ball1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Now, we move the ball
ball1move=ball_move(Surface,white,pos1,rad,speed1)
ball2move=ball_move(Surface,white,pos2,rad,speed2)
pygame.draw.circle(Surface,white,pos1,rad,0,0)
pygame.draw.circle(Surface,white,pos2,rad,0,0)
surface.fill(pygame.Color('black'))
Part of saulspatz answer is correct, part is incorrect. You don't have to use sprites if you dont want to. pygame.draw is not pretty but perfectly usable. The main problem does seem to be your understanding of what to do in your event loop. All this should go in it:
while running:
# Handdle your events
# update your state
# draw to your display
pygame.display.update()
Also I notice in your unreachable code after the loop you are filling after your draws. Remember whether you fill, blit, or draw the latest thing goes over the rest. So for your example:
import pygame ,sys, time
from pygame.locals import *
# User define function
def ball_move(surface, pos, rad, speed):
def _add(l_pos, l_speed, l_size):
l_pos += l_speed
if l_pos <= rad or l_pos >= l_size - rad:
l_speed = -l_speed
return l_pos, l_speed
size = surface.get_size()
pos_x, speed_x = _add(pos[0], speed[0], size[0])
pos_y, speed_y = _add(pos[1], speed[1], size[1])
return (pos_x, pos_y), (speed_x, speed_y)
pygame.init()
screen_size = (500, 400)
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption('Pong')
running = True
pos1 = (100, 200)
pos2 = (400, 200)
speed1 = (2, -2)
speed2 = (-2, 2)
rad = 10
while running:
# Handdle your events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# update your state
pos1, speed1 = ball_move(screen, pos1, rad, speed1)
pos2, speed2 = ball_move(screen, pos2, rad, speed2)
# draw to your display
screen.fill(pygame.Color('black'))
pygame.draw.circle(screen, pygame.Color('white'), pos1, rad)
pygame.draw.circle(screen, pygame.Color('white'), pos2, rad)
pygame.display.update()
There are a lot of problems with your code. The most basic is that you haven't figured out how event-driven programming works. You need to put the code that moves the balls inside your loop.
Another problem is that I don't think you want to use the pygame.draw module. It's been a long time since I wrote any pygame scripts, but as I remember, this module is useful for drawing fixed objects, like the background. A quick look at the docs seems to confirm this.
For moving objects, I think you need to look at the pygame.sprite module. Even if you got this code to work, it wouldn't move the balls. It would just draw a new ball at another position. So you would have first two balls, then four, then eight, ... . Sprites actually move. Not only is the object drawn at the new position, but it's erased at the old position. Your code don't address erasure at all.
Hope this helps.

What does this error mean: TypeError: argument 1 must be pygame.Surface, not pygame.Rect

Here is my code, it is really simple:
bif="C:\\Users\\Andrew\\Pictures\\pygame pictures\\Background(black big).png"
import pygame, sys
from pygame.locals import *
pygame.init()
screen=pygame.display.set_mode((1000,1000),0,32)
background=pygame.image.load(bif).convert()
line=pygame.draw.line(background, (255,255,255), (30,31), (20,31))
while True:
mousex, mousey = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.blit(background, (0,0))
screen.blit(line,(mousex,mousey))
pygame.display.update()
The goal of this is to have the line follow my mouse on the screen. However, whenever I try to run the program I get the error :
TypeError: argument 1 must be pygame.Surface, not pygame.Rect
What does this mean, and what am I doing wrong?
The error means that it is expecting a surface not a rectangle. This is the offending line:
screen.blit(line,(mousex,mousey))
The problem is that you are assigning the result of draw.line() to line.
line=pygame.draw.line(background, (255,255,255), (30,31), (20,31))
From the pygame docs:
All the drawing functions respect the clip area for the Surface, and
will be constrained to that area. The functions return a rectangle
representing the bounding area of changed pixels.
So you are assigning a rectangle to line. Not sure exactly what you want to do, but you can draw the line following the mouse in the following way:
pygame.draw.line(background, (255,255,255), (x,y), (x,y+10))
You use pygame.draw.line wrong. You seem under the impression this returns a new image with the line drawn on background. It doesn't. It will modify the background image.
So instead, you should
create a new surface "line" with the required dimensions, and full transparency
draw your line on that surface
use this then, not the return-value of pygame.draw.line

Pygame Erasing Images on Backgrounds

You blit an image onto your surface to use as your background. Then you press button X to blit an image on the same surface, how do you erase the image? I have this so far, but then I end up with a white rectangle in the middle of my background.
screen = pygame.display.set_mode(1280, 512)
screen.blit(background, (0,0))
while True:
pygame.display.flip() #flip is same as update
for event in pygame.event.get():
if (event.type == pygame.KEYDOWN):
if event.key == pygame.K_SPACE:
screen.blit(player, (x, y))
if event.key == pygame.K_BACKSPACE:
pygame.draw.rect(screen, [255,255,255], (x, y, 62,62))
There are basically two things you can do here. You can go the simple route and just draw the background again...
if event.key == pygame.K_BACKSPACE:
screen.blit(background, (0,0))
Or, if you want it to be a little more efficient, you can have the blit method only draw the part of the background that the player image is covering up. You can do that like this...
screen.blit(background, (x, y), pygame.Rect(x, y, 62, 62))
The Rect in the third argument will cause the blit to only draw the 62x62 pixel section of 'background' located at position x,y on the image.
Of course this assumes that the player image is always inside the background. If the player image only partially overlaps the background I'm not entirely sure what will happen. It'll probably throw an exception, or it'll just wrap around to the other side of the image.
Really the first option should be just fine if this is a fairly small project, but if you're concerned about performance for some reason try the second option.
Normally the blit workflow is just to blit the original background to screen.
In your code you would use screen.blit(background, (0,0))
When you start combining lots of surfaces you will probably want either a variable or a list of variables to keep track of screen state.
E.g.
Initialise Background
objectsonscreen = []
objectsonscreen.append(background)
screen.blit(background, (0,0))
Add Sprite
objectsonscreen.append(player)
screen.blit(player, (x, y))
Clear Sprite
objectsonscreen = [background]
screen.blit(background, (0,0))
See here for more information on sprites in Pygame. Note that if your background isn't an image background and is just a solid color you could also use screen.fill([0, 0, 0]) instead.
I personally use
pygame.draw.rect(screen, (RgbColorHere), (x, y, width, height))

Categories

Resources