This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 1 year ago.
Hello Stackoverflow Community,
could someone help me with my problem.
When I move to the left I am quite fast but when I want to go to the right it takes way to lang. I deleted the dt from my move function and the went the same speed. I changed the buttons but the problem seems only to occure when I use the +=. Does one of you dealt with the problem before and could help me?
import pygame
import time
import random
#0 = Menu, 1 = Game, 2 = Quit
game_state = 1
WHITE = (255, 255, 255)
class Player(pygame.sprite.Sprite):
speed = 100
def __init__(self, WIDTH):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png")
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, 480)
def move(self, keys, dt):
if(keys[0]):
self.rect.x += self.speed * dt
print(self.speed)
print(dt)
print(self.rect.x)
if(keys[1]):
print(self.speed)
print(dt)
self.rect.x -= self.speed * dt
print(self.rect.x)
class Main():
prev_time = time.time()
dt = 0
#initalizing menu or game depending on variables
def __init__(self):
global game_state
while game_state !=2:
WIDTH, HEIGHT = 800, 600
screen = self.initialize_pygame("Alien Slaughter", WIDTH, HEIGHT)
self.all_sprites = pygame.sprite.Group()
self.player = Player(WIDTH)
self.all_sprites.add(self.player)
if(game_state == 1):
self.prev_time = time.time()
self.game(screen, WIDTH, HEIGHT)
#calculating deltatime
def calc_deltatime(self):
self.now = time.time()
self.dt = self.now - self.prev_time
self.prev_time = self.now
#initializing pygame and create window
def initialize_pygame(self, title, width, height):
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(title)
icon = pygame.image.load("icon.png")
pygame.display.set_icon(icon)
pygame.init()
return screen
def handle_inputs(self, keys, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
keys[0] = True
if event.key == pygame.K_a:
keys[1] = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_d:
keys[0] = False
if event.key == pygame.K_a:
keys[1] = False
def game(self, screen, width, height):
global game_state
BLACK = (100, 100, 100)
keys = [False, False]
#Game loop
while game_state == 1:
#Process input (events)
for event in pygame.event.get():
#Check for Closing windows
if event.type == pygame.QUIT:
game_state = 2
self.handle_inputs(keys, event)
self.calc_deltatime()
self.player.move(keys, self.dt)
#Update
self.all_sprites.update()
#Draw / render
screen.fill(BLACK)
self.all_sprites.draw(screen)
pygame.display.update()
game = Main()
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the movement is added to the coordinates of the Rect object. If this is done every frame, the position error will accumulate over time.
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinate and assign it to the location of the rectangle:
class Player(pygame.sprite.Sprite):
speed = 100
def __init__(self, WIDTH):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png")
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, 480)
self.x = self.rect.x
def move(self, keys, dt):
if keys[0]:
self.x += self.speed * dt
if keys[1]:
self.x -= self.speed * dt
self.rect.x = round(self.x)
Hey can someone tell me why I dont see the objects falling from the top? In this case The "Münzen, Scheine, and Sack". I am new to pygame and maybe there is an easy way. My game should be like this:
The walking man should catch money falling down and get points for it.
https://gyazo.com/d2ce52cb54e8658e92ae9e5f3d1e7cca
import random
from pygame.sprite import Sprite
pygame.init()
#Display
win = pygame.display.set_mode((900,780))
pygame.display.set_caption("The Collector")
#Laufanimation
walkRight = [pygame.image.load('Assets/R1.png'), pygame.image.load('Assets/R2.png'), pygame.image.load('Assets/R3.png'), pygame.image.load('Assets/R4.png'), pygame.image.load('Assets/R5.png'), pygame.image.load('Assets/R6.png'), pygame.image.load('Assets/R7.png'), pygame.image.load('Assets/R8.png'), pygame.image.load('Assets/R9.png')]
walkLeft = [pygame.image.load('Assets/L1.png'), pygame.image.load('Assets/L2.png'), pygame.image.load('Assets/L3.png'), pygame.image.load('Assets/L4.png'), pygame.image.load('Assets/L5.png'), pygame.image.load('Assets/L6.png'), pygame.image.load('Assets/L7.png'), pygame.image.load('Assets/L8.png'), pygame.image.load('Assets/L9.png')]
#Hintergrund
bg = pygame.image.load('City.jpg')
bg = pygame.transform.scale(bg, (900,780))
#Charakter
char = pygame.image.load('Assets/R1.png')
# Geld
Münzen2 = pygame.image.load("Assets/coin.png")
Schein2 = pygame.image.load("Assets/schein.png")
Sack2 = pygame.image.load("Assets/sack.png")
# Geld
Münzen = pygame.image.load("Assets/coin.png")
Schein = pygame.image.load("Assets/schein.png")
Sack = pygame.image.load("Assets/sack.png")
clock = pygame.time.Clock()
class player():
def __init__(self,x,y,width,height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 20
self.left = False
self.right = False
self.walkCount = 0
self.hitbox = (self.x + 20, self.y, 28, 60)
def draw(self, win):
if self.walkCount + 1 >= 27:
self.walkCount = 0
if self.left:
win.blit(walkLeft[self.walkCount//3], (self.x,self.y))
self.walkCount += 1
elif self.right:
win.blit(walkRight[self.walkCount//3], (self.x,self.y))
self.walkCount +=1
else:
win.blit(char, (self.x,self.y))
self.hitbox = (self.x + 215, self.y + 230, 220, 70) # NEW
pygame.draw.rect(win, (255,0,0), self.hitbox,2)
class Laser:
def __init__(self, x, y, img):
self.x = x
self.y = y
self.img = img
def draw(self, window):
window.blit(self.img, (self.x, self.y))
def move(self, vel):
self.y += vel
def off_screen(self, height):
return not(self.y <= height and self.y >= 0)
def collision(self, obj):
return collide(self, obj)
def redrawGameWindow():
win.blit(bg, (0,0))
collector.draw(win)
pygame.display.update()
class Ship:
COOLDOWN = 30
def __init__(self, x, y, health=100):
self.x = x
self.y = y
self.health = health
self.ship_img = None
self.laser_img = None
self.lasers = []
self.cool_down_counter = 0
def draw(self, window):
window.blit(self.ship_img, (self.x, self.y))
for laser in self.lasers:
laser.draw(window)
def move_lasers(self, vel, obj):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
elif laser.collision(obj):
obj.health -= 10
self.lasers.remove(laser)
def cooldown(self):
if self.cool_down_counter >= self.COOLDOWN:
self.cool_down_counter = 0
elif self.cool_down_counter > 0:
self.cool_down_counter += 1
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def get_width(self):
return self.ship_img.get_width()
def get_height(self):
return self.ship_img.get_height()
class Enemy(Ship):
COLOR_MAP = {
"red": (Münzen, Münzen2),
"green": (Schein, Schein2),
"blue": (Sack, Sack2)
}
def __init__(self, x, y, color, health=100):
super().__init__(x, y, health)
self.ship_img, self.laser_img = self.COLOR_MAP[color]
self.mask = pygame.mask.from_surface(self.ship_img)
def move(self, vel):
self.y += vel
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x-20, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def collide(obj1, obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask, (offset_x, offset_y)) != None
#mainloop
collector = player(200, 410, 64,64)
run = True
while run:
clock.tick(27)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and collector.x > -180 -collector.width -collector.vel:
collector.x -= collector.vel
collector.left = True
collector.right = False
elif keys[pygame.K_RIGHT] and collector.x < 550 - collector.width - collector.vel:
collector.x += collector.vel
collector.right = True
collector.left = False
else:
collector.right = False
collector.left = False
collector.walkCount = 0
redrawGameWindow()
pygame.quit()````
[1]: https://i.stack.imgur.com/FYFkB.png
It seems like you copy/pasted your code together, so let's drop it and start from scratch. Feel free to look some stuff up the documentation as we go along.
First, we need a mainloop to keep our game running. I usually start with something like this:
import pygame
RESOLUTION = 800, 600
FPS = 60
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill('black')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()
This will create a window and manage our sprites that we will create later on and add to the sprites container. Not much to see here currently:
Now, we want a player that can move left and right. Here's how I would do it, making use of some pygame features, like the Rect and Sprite class. There's no magic involved. A Sprite is basically just a class that has an image attribute (you already guessed that this is, well, the image to use), and a rect attribute, with contains the position of where we want to draw the image.
Here's the Player class:
TILESIZE = 32
PLAYER_SPEED = 600
class Player(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill('dodgerblue')
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]: d -= 1
if pressed[pygame.K_d]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
and now let's add a Player to our game:
...
sprites = pygame.sprite.Group()
Player((300, 500), sprites)
while True:
...
Note that you don't have to draw/blit anything manually. Give our class an image and rect attribute, subclass Sprite, add the instance to a Group, and call draw on this group.
Now for the falling coins and other stuff. We should create a class for them, something like this:
from random import choice
FALLING_SPEED = 400
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(choice(['red', 'yellow', 'green']))
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
In your final game, you can easily replace the mono coloured image with an image you loaded from a file. Here, I just randomly choose red, yellow or green.
As you have already guessed, all the behaviour of our game objects is inside the update method of the sprite classes. The FallingStuff just moves its rect downward and kills itself when it goes out of screen. 'killing' as sprite just means it removes itself from all of its Group-containers. Another reason to use these instead of plain lists.
Of course, we want to generate multiple falling objects. There are dozen ways to do this but let's use pygame's time module to create an event because why not:
from random import randint
...
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), sprites)
screen.fill('black')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
The only thing left is to check if we could catch one of the falling objects.
Full code below:
import pygame
import pygame.freetype
from random import choice, randint
RESOLUTION = 800, 600
FPS = 60
TILESIZE = 32
PLAYER_SPEED = 600
FALLING_SPEED = 400
class Player(pygame.sprite.Sprite):
def __init__(self, pos, falling_stuff, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill('dodgerblue')
self.rect = self.image.get_rect(topleft=pos)
self.falling_stuff = falling_stuff
self.score = 0
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]: d -= 1
if pressed[pygame.K_d]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
for stuff in pygame.sprite.spritecollide(self, self.falling_stuff, True):
self.score += 1
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(choice(['red', 'yellow', 'green']))
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
falling_stuff = pygame.sprite.Group()
player = Player((300, 500), falling_stuff, sprites)
font = pygame.freetype.SysFont('Arial', 54)
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), falling_stuff, sprites)
screen.fill('black')
font.render_to(screen, (20, 20), f'Score: {player.score}', 'white')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()
And here's the same with animations and a hitbox:
import pygame
import pygame.freetype
import os
from random import choice, randint
from itertools import cycle
RESOLUTION = 800, 600
FPS = 60
TILESIZE = 32
PLAYER_SPEED = 300
FALLING_SPEED = 400
ANIM_THRESHOLD = 0.05
def load_images(dir):
path = os.path.dirname(os.path.realpath(__file__))
path = os.path.join(path, dir)
for f in os.listdir(path):
yield pygame.image.load(os.path.join(path, f)).convert_alpha()
class Player(pygame.sprite.Sprite):
def __init__(self, pos, falling_stuff, *grps):
super().__init__(*grps)
idle_images = list(load_images('1-Idle'))
run_images = list(load_images('2-Run'))
self.images = {
'IDLE': {
'RIGHT': cycle(idle_images),
'LEFT': cycle(pygame.transform.flip(s, True, False) for s in idle_images)
},
'RUN': {
'RIGHT': cycle(run_images),
'LEFT': cycle(pygame.transform.flip(s, True, False) for s in run_images)
}
}
self.state = 'IDLE'
self.direction = 'RIGHT'
self.image = next(self.images['IDLE']['RIGHT'])
self.animation_counter = 0
self.rect = self.image.get_rect(topleft=pos)
self.falling_stuff = falling_stuff
self.score = 0
self.hitbox = pygame.Rect(0, 0, 30, 20)
self.hitbox.center = self.rect.center
self.hitbox.move_ip(-10, -10)
def update_image(self, dt, new_state):
self.animation_counter += dt
if self.animation_counter > ANIM_THRESHOLD or self.state != new_state:
self.image = next(self.images[new_state][self.direction])
self.animation_counter = 0
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]: d -= 1
if pressed[pygame.K_d]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
if d == 1:
new_state = 'RUN'
self.direction = 'RIGHT'
if d == -1:
new_state = 'RUN'
self.direction = 'LEFT'
if d == 0:
new_state = 'IDLE'
for stuff in self.falling_stuff:
if self.hitbox.colliderect(stuff.rect):
stuff.kill()
self.score += 1
self.update_image(dt, new_state)
self.state = new_state
self.hitbox.center = self.rect.center
self.hitbox.move_ip(10 if self.direction == 'LEFT' else -10, -10)
pygame.draw.rect(pygame.display.get_surface(), 'red', self.hitbox, 2)
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(choice(['red', 'yellow', 'green']))
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
falling_stuff = pygame.sprite.Group()
player = Player((300, 500), falling_stuff, sprites)
font = pygame.freetype.SysFont('Arial', 54)
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), falling_stuff, sprites)
screen.fill('black')
font.render_to(screen, (20, 20), f'Score: {player.score}', 'white')
sprites.draw(screen)
sprites.update(dt, events)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()
Sprites by Pixel Frog
First, you should consider to use "ae, oe, ue" or just the English translations instead of "ä, ö, ü", this can cause a few problems. Also, why don't you have included pygame?
Second of all, try not to use fixed values for window size, if you want to change it later on, you have to change a few values and easily run into problems when you forget one. I would recommend using a variable for width and height you can change later on.
But to your specific problem:
I think the reason you don't see any objects falling is that there are none. The part of your code that actually runs is in the while-loop and as long as I didn't miss anything, neither is there anything about your dropping objects, nor about them falling (any kind of acceleration or velocity).
Also I would highly recommend (as the comment of "oskros" already said) to test your code before asking. This is also an important part of programming and can be done like this for example:
You go in every function and write a "print" statement there, so you know that this function has run (by the output on the console). If one doesn't run, try to look at the point where it was supposed to be run and go on fixing from this point.
You can apply this technique to basically any debugging, stackoverflow is mostly for asking more specific questions and not "why does this not work, can you fix my code?" (at least for a whole program)
import pygame.freetype
from random import choice, randint
pygame.init()
RESOLUTION = 900, 780
FPS = 60
TILESIZE = 32
PLAYER_SPEED = 600
FALLING_SPEED = 400
coin = pygame.image.load('coin.png')
coin = pygame.transform.scale(coin, (60, 60))
sack = pygame.image.load('sack.png')
sack = pygame.transform.scale(sack, (60, 60))
money = pygame.image.load('schein.png')
money = pygame.transform.scale(money, (60, 60))
char = pygame.image.load ("L1.png")
walkRight = [pygame.image.load('Assets/R1.png'), pygame.image.load('Assets/R2.png'), pygame.image.load('Assets/R3.png')]
bg = pygame.image.load('City.jpg')
bg = pygame.transform.scale(bg, (900,780))
pygame.mixer.music.load('Music/Titel.mp3')
pygame.mixer.music.play(0)
class Player(pygame.sprite.Sprite):
def __init__(self, pos, falling_stuff, *grps):
super().__init__(*grps)
self.image = char
self.rect = self.image.get_rect(topleft=pos)
self.falling_stuff = falling_stuff
self.score = 0
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: d -= 1
if pressed[pygame.K_RIGHT]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
for stuff in pygame.sprite.spritecollide(self, self.falling_stuff, True):
self.score += 1
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = choice([coin,sack,money])
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
falling_stuff = pygame.sprite.Group()
player = Player((400, 700), falling_stuff, sprites)
font = pygame.freetype.SysFont('ComicSans', 70)
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), falling_stuff, sprites)
screen.blit(bg, (0,0))
font.render_to(screen, (30, 30), f'Score: {player.score}', 'white')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()
I'm in the process of making a snake game, it mostly went well so far, but it displayed a block of the snake at the top left that I can't get rid of.
I checked that I didn't draw the surface there(0,0). I'm stuck. Please help me out, thanks!!
BTW it's my first time asking a question so any suggestion on that is also appreciated.
Edit: I found that using a regular class instead of sprite solved the problem, but I need the collide and other functions in sprite.
import pygame
class snake(pygame.sprite.Sprite):
speed=5
init_length=10
direction=0
x=[]
y=[]
updateCountMax = 2
updateCount = 0
length=10
# image=pygame.Surface((11,11)).convert().fill((0,128,255))
def __init__(self,init_x,init_y,image,screen):
pygame.sprite.Sprite.__init__(self)
for i in range(0,self.init_length):
self.x.append(init_x)
self.y.append(init_y)
# for i in range(0,self.length):
# print(f"{self.x[i]},{self.y[i]}")
for x in self.x:
print(x)
for y in self.y:
print(y)
self.image=image
self.screen=screen
self.rect=self.image.get_rect()
# self.rect.center=(self.x,self.y)
def move_R(self):
# self.x+=self.speed
self.direction=0
def move_L(self):
# self.x-=self.speed
self.direction=1
def move_U(self):
# self.y-=self.speed
self.direction=2
def move_D(self):
# self.y+=self.speed
self.direction=3
def update(self):
# self.updateCount = self.updateCount + 1
# if self.updateCount < self.updateCountMax:
for i in range(self.length-1,0,-1):
# print("self.x[" + str(i) + "] = self.x[" + str(i-1) + "]")
self.x[i] = self.x[i-1]
self.y[i] = self.y[i-1]
if(self.direction==0):
self.x[0]+=self.speed
elif(self.direction==1):
self.x[0]-=self.speed
elif(self.direction==2):
self.y[0]-=self.speed
elif(self.direction==3):
self.y[0]+=self.speed
# self.rect.center=(self.x,self.y)
# self.updateCount = 0
# for i in range(0,self.length):
# print(f"{self.x[i]},{self.y[i]}")
self.draw()
def draw(self):
for i in range(0,self.length):
self.screen.blit(self.image,(self.x[i],self.y[i]))
# print(f"rendered at {self.x[i]},{self.y[i]}")
# self.rect.center=(self.x[i],self.y[i])
class app:
width=1200
height=900
title="Snake"
done=False
def __init__(self):
pygame.init()
self.image=pygame.Surface((11,11))
self.image.fill((0,128,255))
pygame.display.set_caption(self.title)
self.screen = pygame.display.set_mode((self.width, self.height))
self.screen.fill((0,0,0))
self.clock=pygame.time.Clock()
self.snakes=pygame.sprite.Group()
self.player1=snake(500,10,self.image,self.screen)
self.snakes.add(self.player1)
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill((255,255,255))
def loop(self):
while(not self.done):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
pygame.event.pump()
keys = pygame.key.get_pressed()
if (keys[pygame.K_RIGHT]):
self.player1.move_R()
if (keys[pygame.K_LEFT]):
self.player1.move_L()
if (keys[pygame.K_UP]):
self.player1.move_U()
if (keys[pygame.K_DOWN]):
self.player1.move_D()
if (keys[pygame.K_ESCAPE]):
self.done = True
self.screen.blit(self.background,(0,0))
self.screen.fill((0,0,0))
self.player1.update()
self.snakes.draw(self.screen)
pygame.display.update()
self.clock.tick(60)
pygame.quit()
if __name__ == "__main__" :
theApp = app()
theApp.loop()
You add player1 to the snakes sprite group and draw that with self.snakes.draw(self.screen). However, you also draw the player in self.player1.update(), in the last line.
Remove self.snakes.draw(self.screen) to get rid of the phantom snake.
BTW: you create and set a self.background but you immediately overwrite it with self.screen.fill((0,0,0)), so you don't need a background at all.
What you see in the top left corner is the self.image of the player1 sprite. The draw method of sprite groups blits the images at the rect.topleft coordinates of the sprites and since you never move the player1.rect, the image will be blitted at the default (0, 0) coordinates. So just remove the line self.snakes.draw(self.screen) to fix this.
I also suggest that you use pygame.Rects instead of the self.x and self.y lists. You can create rect instances with the init_x and init_y coords as the topleft attribute and put them into a self.rects list. That allows you to simplify the update and draw methods, and the rects can also be used for the collision detection. I've already refactored your code (it kind of became a mini code review):
import pygame
class Snake(pygame.sprite.Sprite): # Use upper camelcase names for classes (see PEP 8).
def __init__(self, init_x, init_y, image,screen):
pygame.sprite.Sprite.__init__(self)
# These are instance attributes now (use class attributes if
# the values should be shared between the instances).
self.speed = 5
self.init_length = 10
self.direction = 0
self.updateCountMax = 2
self.updateCount = 0
self.length = 10
# The body parts are rects now.
self.rects = []
for i in range(self.init_length):
# Append pygame.Rect instances.
self.rects.append(pygame.Rect(init_x, init_y, 11, 11))
self.image = image
self.screen = screen
self.rect = self.rects[0] # I use the first rect as the self.rect.
def update(self):
for i in range(self.length-1, 0, -1):
# Update the topleft (x, y) positions of the rects.
self.rects[i].topleft = self.rects[i-1].topleft
if self.direction == 0:
self.rects[0].x += self.speed
elif self.direction == 1:
self.rects[0].x -= self.speed
elif self.direction == 2:
self.rects[0].y -= self.speed
elif self.direction == 3:
self.rects[0].y += self.speed
def draw(self):
# Iterate over the rects to blit them (I draw the outlines as well).
for rect in self.rects:
self.screen.blit(self.image, rect)
pygame.draw.rect(self.screen, (0, 255, 0), rect, 1)
class App:
width = 1200
height = 900
title = "Snake"
done = False
def __init__(self):
pygame.init()
self.image = pygame.Surface((11, 11))
self.image.fill((0, 128, 255))
pygame.display.set_caption(self.title)
self.screen = pygame.display.set_mode((self.width, self.height))
self.clock = pygame.time.Clock()
self.snakes = pygame.sprite.Group()
self.player1 = Snake(500, 10, self.image, self.screen)
self.snakes.add(self.player1)
def loop(self):
while not self.done:
# Handle the events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
keys = pygame.key.get_pressed()
# In Python we simply set the values of the
# attributes directly instead of using getter
# and setter methods.
if keys[pygame.K_RIGHT]:
self.player1.direction = 0
if keys[pygame.K_LEFT]:
self.player1.direction = 1
if keys[pygame.K_UP]:
self.player1.direction = 2
if keys[pygame.K_DOWN]:
self.player1.direction = 3
if keys[pygame.K_ESCAPE]:
self.done = True
# Update the game.
self.player1.update()
# Draw everything.
self.screen.fill((0, 0, 0))
self.player1.draw()
pygame.draw.rect(self.screen, (255, 0, 0), self.player1.rect, 1)
pygame.display.update()
self.clock.tick(60)
pygame.quit()
if __name__ == "__main__" :
the_app = App()
the_app.loop()