Cloning issues when trying to move a sprite pygame [duplicate] - python-3.x

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)

You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

Related

My score system in Pygame overlaps over and over again [duplicate]

This question already has an answer here:
How can I move the ball instead of leaving a trail all over the screen in pygame?
(1 answer)
Closed 2 years ago.
I have looked at many YouTube videos and information online but is seems I can't find the answer. Every time I run the code the text appears with a zero but when clicking the enemy to earn points the number 1 overlaps with the zero and it doesn't change. This problem repeats over and over again. What do/can I do? I am new to the python code and there might be some unnecessary code so please bare that in mind if you see a rookie mistake thank you.
import pygame
import time
import random
pygame.init()
from pygame.locals import *
white = (255,255,255)
window = pygame.display.set_mode((800,600))
pygame.display.set_caption("Extended Essay game")
BADGUY = pygame.image.load('enemy.png')
pygame.font.init()
#Enemy char
class Enemy():
def __init__(self, x, y):
self.x = x
self.y = y
def main():
run = True
FPS = 60
x = 0
enemies = 0
clock = pygame.time.Clock()
font = pygame.font.Font('freesansbold.ttf', 32)
window.fill((255,255,255))
window.blit(BADGUY,(0,0))
def update():
text = font.render("Enemies: " + str(enemies), True, (0,0,0))
window.blit(text, (200, 300))
pygame.display.update()
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
window.blit(BADGUY, (random.randint(0, 700), random.randint(0 , 700)))
enemies += 1
update()
main()
You need to clear the screen before putting new stuff on it. Put this at the beginning of update() or right before it is called in the loop:
window.fill((255, 255, 255))

Pygame fade to black function

I'm coding a game in python 3 using pygame latest version. I have a function that is intended to slowly fade the screen until it is totally black. It should do so by blitting a low-alpha black surface many times on the screen.
But when I test it, it only blocks the game until the loop is finished. I suspect a problem with the alpha of black_surface.
I've seen some questions on forums about fade in pygame, but none of them concerned fade directly in functions.
Here is the code:
def fade_to_black(screen):
black_surface = Surface((screen.get_width(), screen.get_height()), flags= SRCALPHA)
color = (255, 255, 255, 1)
black_surface.fill(color)
alpha_key = 1
while alpha_key <= 255:
screen.blit(black_surface, screen.get_rect())
display.flip()
alpha_key = alpha_key + 1
time.wait(1)
I have looked in documentation and forums but can't find any solution. Hope it isn't an obvious issue I would have missed... Thanks for the help!
You create a surface called black_surface, but you fill it with white. Fill it with black (eg. (0, 0, 0, 1)) and may work, but there's another problem:
When you call display.flip() inside a loop where you change the screen surface, the display may not actually update if you don't let pygame handle events (e.g. by calling pygame.event.get()), depending on your OS. Also, while your loop runs, you can't handle events manually, e.g. the QUIT event. So while your screen fades to black, you can't quit your game.
Generally, you should only have one main loop and not call blocking functions like pygame.time.sleep, but there are exceptions, of course).
Here's simple Sprite-based example:
import pygame
class Fade(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.rect = pygame.display.get_surface().get_rect()
self.image = pygame.Surface(self.rect.size, flags=pygame.SRCALPHA)
self.alpha = 0
self.direction = 1
def update(self, events):
self.image.fill((0, 0, 0, self.alpha))
self.alpha += self.direction
if self.alpha > 255 or self.alpha < 0:
self.direction *= -1
self.alpha += self.direction
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
sprites = pygame.sprite.Group(Fade())
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events)
screen.fill((30, 30, 30))
pygame.draw.rect(screen, pygame.Color('dodgerblue'), (100, 100, 100, 100))
sprites.draw(screen)
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()

How to detect collision/mouse-over between the mouse and a sprite?

Goal: to know when a sprite is clicked / active with the mouse
Using: Python 3.2 64bit, Pygame 1.92 64bit, windows 7 64bit
I spend 6 hours to no avail...I've tried :
s.rect.collidepoint(pygame.mouse.get_pos())
s.collidepoint(pygame.mouse.get_pos())
s.sprite.spritecollide(pygame.mouse.get_pos())
s.spritecollide(pygame.mouse.get_pos())
s.sprite.collide_rect(pygame.mouse.get_pos())
s.collide_rect(pygame.mouse.get_pos())
I've also tried turning the mouse location, which I really don't want to do, like someone else mentioned here on another post, into a sprite and collide like that with the same results ;(
I'm able to successfully mouse-collide with an image, but as soon as I turn the image into a sprite class, it becomes a nightmare...what's wrong with the sprite class? Or am I wasting time trying to use sprites for the nice collision features and just use images with rect collision instead?
Keep on getting the AttributeError: 'Sheldon' object has no attribute 'Rect' (s.Rect.collidepoint)
or AttributeError: 'tuple' object has no attribute 'collidepoint' (s.collidepoint)
or AttributeError: 'Rake' object has no attribute 'sprite' (s.sprite.collidepoint)
Since I'm new to python/pygame, should I be putting this detection in an Update/Render method in the sprite class itself, or am I using the wrong event polling???
I haven't bothered trying to recode the mousedown/up/dragging since I can't even get the mouse-over to work
Hopefully this time the post gets a working response...the others didn't ;(
Thanks for your help.
Code:
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Rake(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Sprite_Mouse_Location(Sprite):
def __init__(self,x,y):
Sprite.__init__(self)
self.rect = pygame.Rect(x,y,1,1)
print(self.rect)
pygame.init()
window = display.set_mode( (800,600) )
sheldon = Sheldon()
sheldon.rect = (10,10)
all_sprites = Group(sheldon)
rake = Rake()
rake.rect = (400,250)
all_sprites.add(rake)
x,y = pygame.mouse.get_pos()
mouse_sprite = Sprite_Mouse_Location(x,y)
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEMOTION :
for s in all_sprites :
if pygame.sprite.collide_rect(s,mouse_sprite):
print("hit")
window.fill( (0,0,0) )
all_sprites.update()
all_sprites.draw(window)
display.update()
You don't need Sprite_Mouse_Location.
BTW: to set position you need
rake.rect.topleft = (400, 250)
# or
rake.rect.x = 400
rake.rect.y = 250
not
rake.rect = (400, 250)
because it replace pygame.Rect() with tuple
Example code:
I use Surface instead of image.load() so everyone can run it without images.
import pygame
# --- constants --- (UPPER_CASE names)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# --- classes --- (CamelCase names)
class Sheldon(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((230, 310))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def check_click(self, mouse):
if self.rect.collidepoint(mouse):
print("hit RED")
class Rake(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((230, 310))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def check_click(self, mouse):
if self.rect.collidepoint(mouse):
print("hit GREEN")
# --- main --- (lower_case names)
# - init -
pygame.init()
window = pygame.display.set_mode((800,600))
# - objects -
sheldon = Sheldon(10, 10)
#sheldon.rect.topleft = (10, 10)
rake = Rake(400, 250)
#rake.rect.topleft = (400, 250)
all_sprites = pygame.sprite.Group()
all_sprites.add(sheldon, rake)
# - mainloop -
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
for s in all_sprites:
s.check_click(event.pos)
window.fill(BLACK)
all_sprites.update()
all_sprites.draw(window)
pygame.display.update()
# - end -
pygame.quit()
There are a lot of things that are going on with your code. Coding can be hard at first, but keep sticking to! You'll better as long as you keep trying and don't give up! Your error is occurring because above the loop, you are setting your various sprites' rect variables to tuples instead of rects. Rects are not tuples, they have a bunch of variables that tuples don't have and collide_rect needs some of those variables. I put a couple comments in the code below about your other questions.
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Rake(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Sprite_Mouse_Location(Sprite):
def __init__(self,x,y):
Sprite.__init__(self)
self.rect = pygame.Rect(x,y,1,1)
print(self.rect)
pygame.init()
window = display.set_mode( (800,600) )
sheldon = Sheldon()
sheldon.rect = (10,10) # (10,10) is not a rect!
# pygame.Rect(0,0,10,10) is a rect!
all_sprites = Group(sheldon)
rake = Rake()
rake.rect = (400,250) # Same as above comment.
all_sprites.add(rake)
x,y = pygame.mouse.get_pos() # If you are going to make the mouse
mouse_sprite = Sprite_Mouse_Location(x,y) # a sprite, then update your mouse position in the loop.
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEMOTION :
# You could have this instead.
# mouse_sprite.rect.x, mouse_sprite.rect.y = pygame.mouse.get_pos()
# Then move the for loop below to be out of the event loop.
for s in all_sprites :
if pygame.sprite.collide_rect(s,mouse_sprite):
print("hit")
window.fill( (0,0,0) )
all_sprites.update()
all_sprites.draw(window)
display.update()
I'd also recommend going through a pygame tutorial as well as looking at other people's pygame code and trying to understand it line by line. Both those tips helped me out. Good luck.
thanks to alanxoc3 and a new day, code working
posting here because I'm sure another newbie is going to want to do this type of mouse click
# to create a group of sprites and use the mouse over them individually
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self,self.groups) #have to have groups here, not documented well
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
def clickCheck(self,smouse):
if pygame.sprite.collide_rect(smouse, self): #could not use sritecollison because it would flag whole group
print('hit sheldon') # this if the check mouse that its working
#------------------------------------------
class Rake(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self,self.groups)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
def clickCheck(self,smouse):
if pygame.sprite.collide_rect( smouse, self ):
print('hit rake')
#-------------------------------------------
class Sprite_Mouse_Location(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect( 0 , 0 , 1 , 1 ) # no updating needed here
#--------------------------------------------
pygame.init()
window = display.set_mode( (800,600) )
mouse_sprite = Sprite_Mouse_Location() # had to create a mouse as sprite to use the sprite collision feature
all_sprites = pygame.sprite.Group() # have to declare first, no well documented
Sheldon.groups = all_sprites # better than all_sprites = Group(sheldon), and can be assigned before instantiation of sheldon!!!
sheldon = Sheldon()
sheldon.rect = (10,10,230,310)
Rake.groups = all_sprites
rake = Rake()
rake.rect = (400,250,230,310)
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1:
mouse_sprite.rect.x , mouse_sprite.rect.y = pygame.mouse.get_pos() # have to have this to update mouse here or get wrong location
for s in all_sprites: #can't have outside the event or it will continuously check
s.clickCheck(mouse_sprite)
window.fill( (0,0,0) )
all_sprites.update() # have to have this for group sprites
all_sprites.draw(window) # have to have this for sprites
display.flip()

Pygame sprite clamping isse

So Ive been having issues with getting a sprite to stay withing the bounds of the screen. I got it to work with a simple rect(0,0,16,16), but i cant seem to get it to work with a sprite being blit onto the screen. What do i need to change in order to keep my sprite clamped within the screen res? I only just started today using classes to orgonize code so any input is appreciated and helpful.
import pygame
from pygame.locals import *
from pygame import Color
class Game():
""" Lets try to get this going by simple steps
One by one. First step, lets figure how to make a class
that can do the display stuff. Lord have mercy on my soul"""
def __init__(self, wi=256, hi=224, multii=3):
"""Initialization"""
pygame.init()
self.runGame = True
self.width = wi*multii
self.height = hi*multii
self.spritesize = 16*multii
self.clock = pygame.time.Clock()
self.fps = self.clock.get_fps()
self.screen = pygame.display.set_mode((self.width, self.height))
self.kl = []
self.walk = [0, 0]
self.speed = multii*1.5
self.x,self.y = self.width/2, self.height/2
self.playerSpr = pygame.image.load('images/'+'link1.png').convert_alpha()
self.playerRec = Rect(self.playerSpr.get_rect())
def mainLoop(self):
"""Loop through the main game routines
1. Drawing 2. Input handling 3. Updating
Then loop through it until user quits"""
while self.runGame:
self.clock.tick(60)
self.events()
self.draw()
def events(self):
"""Time to handle some events"""
for e in pygame.event.get():
if (e.type == pygame.QUIT) or (e.type == KEYDOWN and e.key == K_ESCAPE):
self.runGame = False
break
if e.type==KEYDOWN:
if e.key==pygame.K_a: self.kl.append(1)
if e.key==pygame.K_d: self.kl.append(2)
if e.key==pygame.K_w: self.kl.append(3)
if e.key==pygame.K_s: self.kl.append(4)
if e.type==pygame.KEYUP:
if e.key==pygame.K_a: self.kl.remove(1)
if e.key==pygame.K_d: self.kl.remove(2)
if e.key==pygame.K_w: self.kl.remove(3)
if e.key==pygame.K_s: self.kl.remove(4)
if self.kl[-1:]==[1]: self.walk=[-self.speed, 0]
elif self.kl[-1:]==[2]: self.walk=[ self.speed, 0]
elif self.kl[-1:]==[3]: self.walk=[0,-self.speed]
elif self.kl[-1:]==[4]: self.walk=[0, self.speed]
else: self.walk=[0, 0]
self.x+=self.walk[0]
self.y+=self.walk[1]
def draw(self):
"""Draw and update the main screen"""
self.fps = self.clock.get_fps()
self.screen.fill(Color('purple'))
#print self.screen.get_rect()
#print player_rect
self.playerSpr.clamp_ip(self.screen.get_rect())
#pygame.draw.rect(self.screen, (255, 255, 255), self.playerrect)
self.screen.blit(self.playerSpr, (self.x,self.y), self.playerRec)
pygame.display.set_caption('Grid2. FPS: '+str(self.fps))
pygame.display.update()
game = Game()
game.mainLoop()
Why not use playerRec to keep track of the position of your player instead of the additional x and y attributes?
I suggest also using the move method (or move_ip):
def events(self):
for e in pygame.event.get():
...
self.playerRec.move_ip(*self.walk) # instead of self.x+=self.walk[0] / self.y+=self.walk[1]
def draw(self):
...
# probably do this right after 'move_ip'
self.playerRec.clamp_ip(self.screen.get_rect())
# note that 'blit' accepts a 'Rect' as second parameter
self.screen.blit(self.playerSpr, self.playerRec)
as a side note: You should consider using a Sprite, since it basically combines an Image and a Rect.
Two things:
You are not stopping movement of the sprite when it comes off the screen.
Make an move functions that will get a direction and will decide if it can move more to the side. That way when the right side of the sprite will be of screen, you will not move more to the right.
Since you put your direction keys in a list that works like a stack, you are only getting 1 direction per keypress. If you also want to move diagonally either make two lists one for both directions or use a easier method such as this:
if KEYDOWN == K_LEFT: direction_x = -1
if KEYUP == K_LEFT AND direction_x == -1: direction_x = 0
do this for every key.

pygame graphics grid

Hello, I am new to Python and graphics programming in general. At present I am messing around with creating grids as practice, and I am having problems with getting pygame to put objects on top of the surface window.
Below is my code with comments. I reckon that the problem may be to do with the blit function but I am unsure. Any help would be appreciated. Also, the Python shell does not highlight any exceptions or errors, but the program does not run.
import sys, random, pygame
from pygame.locals import *
def main():
#settings
boardDims = (20,20) #number of cells on board
#pygame settings
cellDims = (20,20) #number of pixels on cells
framerate = 50
colours = {0:(0,0,0), 1:(255,255,255)}
#pygame
pygame.init()
#sets x and y pixels for the window
dims = (boardDims[0] * cellDims[0],
boardDims[1] * cellDims[1])
#sets window, background, and framrate
screen = pygame.display.set_mode(dims)
background = screen.convert()
clock = pygame.time.Clock()
#create new board
board = {}
#iterates over x and y axis of the window
for x in range(boardDims[0]):
for y in range(boardDims[1]):
board[(x,y)] = 0 #sets whole board to value 0
board[(1,1)] = 1 #sets one square at co-ordinates x=1,y=1 to cell
# value 1
return board
running = 1
while running:
# 1 PYGAME
#get input
for event in pygame.event.get():
if event.type == QUIT or \
(event.type == KEYDOWN and event.key == K_ESCAPE):
running = 0
return
#link pygames clock to set framerate
clock.tick(framerate)
for cell in board:
#adds cells dimensions and co-ordinates to object rectangle
rectangle = (cell[0]*cellDims[0], cell[1]* cellDims[1],
cellDims[0], cellDims[1])
#pygame draws the rectabgle on the background using the relevant
#colour and dimensions and co-ordinates outlined in 'rectangle'
square = pygame.draw.rect(background, colours[board[cell]],
rectangle)
#blits and displays object on the background
background.blit(square)
screen.blit(background, (0,0))
pygame.display.flip()
if __name__ == "__main__":
main()
The "return board" statement is exiting your program before any blitting is done.
Also, you can blit the squares directly on the screen variable, ie: you don't need the background variable.

Resources