I'm making a pygame program to animate a sprite, but the problem isn't a particular error:
import sys
import os
import pygame
from pygame.locals import *
pygame.init ()
WHITE = ( 255, 255, 255 )
BLACK = ( 0, 0, 0 )
Surface = pygame.display.set_mode ((400, 400), 0, 32)
pygame.display.set_caption ('Animation Test')
class Processes (object):
#staticmethod
def load_image (imagefile):
image = pygame.image.load (imagefile) #Remember to call imagefile as a string statement
return image, image.get_rect ()
class Sprite (pygame.sprite.Sprite):
def __init__ (self):
pygame.sprite.Sprite.__init__ (self)
self.image, self.rect = Processes.load_image ('frame1.jpg')
self.blindrect = pygame.Rect ((self.rect.top - 289, self.rect.left - 289), (self.rect.width, self.rect.height))
def animation (self, key_event = None, mouse_event = None):
blindpass = 0
if key_event == K_LEFT:
self.rect.centerx = self.rect.centerx - 10
if key_event == K_RIGHT:
self.rect.centerx = self.rect.centerx + 10
if key_event == K_UP:
self.rect.centery = self.rect.centery - 10
if key_event == K_DOWN:
self.rect.centery = self.rect.centery + 10
if mouse_event == MOUSEBUTTONDOWN:
self.rect.centerx, self.rect.centery = pygame.mouse.get_pos ()
if blindpass > 0:
if key_event == K_LEFT:
self.blindrect.centerx = self.blindrect.centerx - 10
if key_event == K_RIGHT:
self.blindrect.centerx = self.blindrect.centerx + 10
if key_event == K_UP:
self.blindrect.centery = self.blindrect.centery - 10
if key_event == K_DOWN:
self.blindrect.centery = self.blindrect.centery + 10
if mouse_event == MOUSEBUTTONDOWN:
self.rect.centerx, self.rect.centery = pygame.mouse.get_pos ()
mecha = Sprite ()
allsprites = pygame.sprite.RenderPlain ((mecha, ))
while True:
Surface.fill (WHITE)
for event in pygame.event.get ():
if event.type == KEYDOWN:
mecha.animation (key_event = event.key)
if event.type == MOUSEBUTTONDOWN:
mecha.animation (mouse_event = event.type)
if event.type == QUIT:
pygame.quit ()
sys.exit (0)
allsprites.draw (Surface)
pygame.display.update ((mecha.rect, mecha.blindrect))
Now, the error is a bit weird. The output is that the sprite does appear on the screen, and it moves when I provide key and mouse input, but the problem is is that it's leaving trails. Which is why I created a rectangle to trail the sprite rectangle which is supposed to be filled with white every time the game loop loops. But it doesn't. At least, not until I minimize the window, and pull it up again, all the trails disappear and the background becomes white instead of black. But when I move the image, the trails start forming again. I minimize, they disappear as supposed to.
If your render function does:
screen.fill(white)
# blit or sprite group drawing
pygame.display.flip()
It will clear the entire screen in white, draw the sprites once. There won't be a trail.
Your code didn't pasted correctly. fLike your if QUIT statement has no indention, for a multi statement. The same with class Sprite
You might get naming problems if you use Surface or Sprite as your class/variable names.
You're not initializing and updating the blindrect correctly. That's why the correct portion of the screen is not being updated and your sprite is leaving a trail.
In your update loop, since you're only updating the portion of the display that changed, you need to update the entire screen once to initialize it before you go into the update loop. That's why the screen is not initially white.
See the updated source below (only the Sprite class and later was modified):
class Sprite (pygame.sprite.Sprite):
def __init__ (self):
pygame.sprite.Sprite.__init__ (self)
self.image, self.rect = Processes.load_image ('frame1.jpg')
#initialize blindrect to be the same rectangle as rect
self.blindrect = pygame.Rect ((self.rect.top, self.rect.left), (self.rect.width, self.rect.height))
def animation (self, key_event = None, mouse_event = None):
print(self.rect)
print(self.blindrect)
#save the current sprite position before moving the sprite
self.blindrect.centerx = self.rect.centerx
self.blindrect.centery = self.rect.centery
if key_event == K_LEFT:
self.rect.centerx = self.rect.centerx - 10
if key_event == K_RIGHT:
self.rect.centerx = self.rect.centerx + 10
if key_event == K_UP:
self.rect.centery = self.rect.centery - 10
if key_event == K_DOWN:
self.rect.centery = self.rect.centery + 10
if mouse_event == MOUSEBUTTONDOWN:
self.rect.centerx, self.rect.centery = pygame.mouse.get_pos ()
print(self.rect)
print(self.blindrect)
mecha = Sprite ()
allsprites = pygame.sprite.RenderPlain ((mecha, ))
#need to initially fill the entire surface with white
Surface.fill (WHITE)
pygame.display.update ((pygame.Rect((0,0),(400,400))))
while True:
Surface.fill (WHITE)
for event in pygame.event.get ():
if event.type == KEYDOWN:
mecha.animation (key_event = event.key)
if event.type == MOUSEBUTTONDOWN:
mecha.animation (mouse_event = event.type)
if event.type == QUIT:
pygame.quit ()
sys.exit (0)
allsprites.draw (Surface)
#update only the previous sprite position and new sprite position
pygame.display.update ((mecha.rect, mecha.blindrect))
Related
This question already has answers here:
Why is nothing drawn in PyGame at all?
(2 answers)
Closed 1 year ago.
Below is the code... Bullet class is defined first, class is called in _fire_bullet, _update_screen, and _check_KEYDOWN. When spacebar is pressed the event handler should take in the event key, call _fire_bullet function where a bullet object is created and added to sprite list. Then fired and moves across screen. As of now its going to move the wrong direction but I will correct that issue easily when I can actually see the bullet.
#make a pygame window with a blue background
import pygame
import sys
from pygame.sprite import Sprite
class Bullet(Sprite):
""" A class to manage bullets fired from the ship """
def __init__(self, game):
""" create a bullet object at the ships current position """
super().__init__()
self.screen = game.screen
self.bullet_speed = 1.0
self.bullet_width = 300
self.bullet_height = 150
self.bullet_color = (0, 200, 200)
#create a bullet at rect (0,0) and the set the correct position
self.rect = pygame.Rect(0, 0, self.bullet_width, self.bullet_height)
self.rect.midright = game.rect.midright
#store the bullets position as a decimal value
self.y = float(self.rect.y)
self.x = float(self.rect.x)
def update(self):
""" move the bullet up the screen """
#update the decimal position of the bullet.
self.y -= self.bullet_speed
self.rect.x = self.x
#uipdate the rect position
self.rect.y = self.y
def draw_bullet(self):
""" draw the bullet to the screen """
pygame.draw.rect(self.screen, self.bullet_color, self.rect)
class Game:
""" a class the creates a window with a blue screen """
def __init__(self):
pygame.init()
#--------------------------------------------------------------------------------------------
#screen size, color, caption
self.screen = pygame.display.set_mode((1200,800)) #create attribute to hold display settings
self.bg_color = (250,250,250) #create attribute to hold RGB color (blue)
pygame.display.set_caption("Blue Screen")
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#tank drawing
self.screen_rect = self.screen.get_rect() #get the screen rect dim
self.image = pygame.image.load('images/tank.bmp') #load the image from directory
self.rect = self.image.get_rect() #get the image rect dim
self.rect.center = self.screen_rect.center #store the screens center x/y coord
#tank movement
self.tank_moving_left = False
self.tank_moving_right = False
self.tank_moving_up = False
self.tank_moving_down = False
self.tank_speed = 1 #tank pixels
self.direction_right = self.image #holds right image
self.direction_left = pygame.transform.flip(self.image, True, False) #holds left image
#--------------------------------------------------------------------------------------------
self.bullets = pygame.sprite.Group()
def move(self):
""" move tnak tank_speed based on direction of movement (key pressed)
also detect collision """
if self.tank_moving_right and self.rect.right < self.screen_rect.right:
self.rect.x += self.tank_speed
if self.tank_moving_left and self.rect.left > self.screen_rect.left:
self.rect.x -= self.tank_speed
if self.tank_moving_down and self.rect.bottom < self.screen_rect.bottom:
self.rect.y += self.tank_speed
if self.tank_moving_up and self.rect.top > self.screen_rect.top:
self.rect.y -= self.tank_speed
def blitme(self):
""" draw the image of the tank """
self.screen.blit(self.image, self.rect)
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
pygame.display.flip()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
print(bullet.rect.midright)
def _check_KEYDOWN(self, event):
""" when key is press either quit, or move direction of arrow pressed and flip image """
if event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_RIGHT:
self.tank_moving_right = True
self.image = self.direction_right
elif event.key == pygame.K_LEFT:
self.tank_moving_left = True
self.image = self.direction_left
elif event.key == pygame.K_UP:
self.tank_moving_up = True
elif event.key == pygame.K_DOWN:
self.tank_moving_down = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
print(1)
def _check_KEYUP(self, event):
""" when key is let go stop moving """
if event.key == pygame.K_RIGHT:
self.tank_moving_right = False
elif event.key == pygame.K_LEFT:
self.tank_moving_left = False
elif event.key == pygame.K_UP:
self.tank_moving_up = False
elif event.key == pygame.K_DOWN:
self.tank_moving_down = False
def _fire_bullet(self):
""" create a bullet and add it to the bullets group """
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def run_game(self):
""" loops the game/ updates screen/ checks for key clicks"""
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_KEYDOWN(event)
elif event.type == pygame.KEYUP:
self._check_KEYUP(event)
self.move()
self.bullets.update()
self._update_screen()
if __name__ == '__main__':
a = Game()
a.run_game()
Your _update_screen() function is drawing your bullets after updating the display. The bullets then get overwritten next time _update_screen() is called when you fill the screen with the background color.
If you reorder your screen update function as follows you should be able to see your bullets:
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
print(bullet.rect.midright)
pygame.display.flip()
Also, you can create an image for your bullet by changing your initialization function:
…
#create a bullet at rect (0,0) and the set the correct position
self.image = pygame.Surface((self.bullet_width, self.bullet_height))
self.image.fill(self.bullet_color)
self.rect = self.image.get_rect()
Then you won't need a draw_bullet() function, you can replace the drawing of individual bullets in _update_screen() with self.bullets.draw(self.screen)
This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 2 years ago.
I tried adding gravity to my player in pygame, but I can move the player with key controls but gravity is not working, Here I used sprite class for making this. I got the rect of the image then add the x momentum and y momentum. X momentum worked while moving the player but y momentum didn't work while adding the gravity. Please help!
# Platformer
import pygame
# Basic Setup
pygame.init()
clock = pygame.time.Clock()
# Game Colors
white = (255, 255, 255)
light_blue = (105, 142, 255)
# Game Settings
game_title = "Platformer!"
screen_width = 1280
screen_height = 750
fps = 120
player_speed = 4
player_gravity = 0.2
# Main Window
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption(game_title)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("IMGs/player.png")
self.image.set_colorkey(white)
self.rect = self.image.get_rect(center=(screen_width / 2, screen_height / 2))
self.x_momentum = 0
self.y_momentum = 0
self.moving_right = False
self.moving_left = False
def move(self):
# Set the movement to zero at first
self.x_momentum = 0
self.y_momentum = 0
# Move the player on key press
if self.moving_right:
self.x_momentum += player_speed
elif self.moving_left:
self.x_momentum -= player_speed
# Add Gravity
self.y_momentum += player_gravity
# Move the player
self.rect.x += self.x_momentum
self.rect.y += self.y_momentum
def update(self):
self.move()
class Main:
def __init__(self):
# Sprites
self.all_sprites = pygame.sprite.Group()
# Player Sprite
self.player = Player()
self.all_sprites.add(self.player)
def draw(self, surface):
self.all_sprites.draw(surface)
def update(self):
self.all_sprites.update()
main_game = Main()
def main_game_loop():
while True:
# Handling Inputs
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
main_game.player.moving_right = True
elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
main_game.player.moving_left = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
main_game.player.moving_right = False
elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
main_game.player.moving_left = False
# Draw / Render
screen.fill(light_blue)
main_game.draw(screen)
main_game.update()
pygame.display.update()
# Manage Speed
clock.tick(fps)
main_game_loop()
The gravity doesn't work because self.y_momentum is set 0 at the begin of Player.move:
class Player(pygame.sprite.Sprite):
# [...]
def move(self):
# Set the movement to zero at first
self.x_momentum = 0
self.y_momentum = 0
# [...]
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 position stored in the Rect object is decremented:
# Move the player
self.rect.x += self.x_momentum
self.rect.y += self.y_momentum
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 coordinates and assign it to the location (e.g. .topleft) of the rectangle:
class Player(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.x = screen_width // 2
self.y = screen_height // 2
self.rect = self.image.get_rect(center = (self.x, self.y))
# [...]
def move(self):
# [...]
# Move the player
self.x += self.x_momentum
self.y += self.y_momentum
self.rect.topleft = round(self.x), round(self.y)
My problem is very simple. The bullets I fire sticks to the screen if I shoot fast. If I shoot slowly, they don't stick. Anyone have an idea how this phenomenon occurs?
screenshot of the bullets sticking to the screen
Below I have entered the code. I follow this default game flowchart:
I am curious about the origin of the problem. Is it the code or hardware?
import sys
import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group
# pygame initializing
pygame.init()
#create the screen surface
screen = pygame.display.set_mode((800, 700))
class Color():
def __init__(self):
self.black = (0, 0, 0)
self.white = (255, 255, 255)
self.red = (255, 0, 0)
self.green = (0, 255, 0)
self.green_lambda = (10, 255, 150)
self.blue = (0, 0, 255)
# set up the colors
color = Color() # make an instance of this class - this makes some colors available
class Spaceship(Sprite):
"""
This class represents the Spaceship.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self):
""" Constructor"""
# Call the parent class (Sprite) constructor
super().__init__()
width = 22
height = 32
self.screen = screen
self.image = pygame.Surface((width, height))
self.image.fill(color.black)
self.image.set_colorkey(color.black)
pygame.draw.polygon(self.image, color.green_lambda, [[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]],2)
self.rect = self.image.get_rect()
self.screen_rect = self.screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# As the rect method only take integers we store a
# This value is only used at the beginning, i.e. before the game loop starts
self.center_x = self.rect.centerx
self.center_y = self.rect.centery
class Bullet(Sprite):
"""
This class represents the bullets.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self):
# Call the parent class (Sprite) constructor
super().__init__()
self.image = pygame.Surface((8,10))
self.image.fill(color.red)
self.image.set_colorkey((color.red))
pygame.draw.ellipse(self.image, color.green, [1, 0, 5, 8], 2)
self.rect = self.image.get_rect()
self.rect.centerx = defender.rect.centerx
self.rect.bottom = defender.rect.top
# def function to move the bullets
def update_pos(self):
self.rect.y -= bullet_speed
# create spaceship instance
defender = Spaceship()
# create group to store sprites in
all_sprites_list = Group()
all_sprites_list.add(defender)
ship_speed = 0.5
bullet_speed = 3
def run_game():
m_right = False
m_left = False
m_up = False
m_down = False
new_bullet = False
while True:
"""This is the user interaction section"""
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
sys.exit()
elif event.key == pygame.K_RIGHT:
m_right = True
elif event.key == pygame.K_LEFT:
m_left = True
elif event.key == pygame.K_UP:
m_up = True
elif event.key == pygame.K_DOWN:
m_down = True
elif event.key == pygame.K_SPACE:
new_bullet = Bullet()
#print(dir(new_bullet))
all_sprites_list.add(new_bullet)
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
m_right = False
elif event.key == pygame.K_LEFT:
m_left = False
elif event.key == pygame.K_UP:
m_up = False
elif event.key == pygame.K_DOWN:
m_down = False
"""Below is the game logic, which gets input from the user interaction
section and more"""
# Movement of spaceship depending on the flag boolean value and on screen width and height
if m_right and defender.rect.right < defender.screen_rect.right:
defender.center_x += ship_speed
if m_left and defender.rect.left > defender.screen_rect.left:
defender.center_x -= ship_speed
if m_up and defender.rect.top > defender.screen_rect.top:
defender.center_y -= ship_speed
if m_down and defender.rect.bottom < defender.screen_rect.bottom:
defender.center_y += ship_speed
# The cumulative value (which is a float number) for the spaceships movement
# is given to the spaceship rect variable (which can only be integer) now.
# This enables fine adjusting of the speed
defender.rect.centerx = defender.center_x
defender.rect.centery = defender.center_y
all_sprites_list.update()
screen.fill(color.black)
if new_bullet:
new_bullet.update_pos()
# Below the bullets which leaves the screen display are deleted
if new_bullet.rect.bottom < defender.screen_rect.top:
all_sprites_list.remove(new_bullet)
all_sprites_list.draw(screen)
print(all_sprites_list)
pygame.display.flip()
run_game()
instead of just updating the position of new_bullet
# if new_bullet:
# new_bullet.update_pos()
# # Below the bullets which leaves the screen display are deleted
# if new_bullet.rect.bottom < defender.screen_rect.top:
# all_sprites_list.remove(new_bullet)
update the position of all bullets
for bullet in all_sprites_list:
if isinstance(bullet,Bullet):
bullet.update_pos()
if bullet.rect.bottom < defender.screen_rect.top:
all_sprites_list.remove(bullet)
del bullet
Joran Beasley's answer is correct. I'd just like to point out that you can also put the behavior of the sprites into their update methods which get called automatically when you call all_sprites_list.update(). You can actually move most of the code in the while loop to the update methods.
I've got an example with these changes and some more tips in the comments (a quick code review):
import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group
# I'd just define some global constants for the colors.
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
GREEN_LAMBDA = (10, 255, 150)
class Spaceship(Sprite):
"""This class represents the Spaceship."""
def __init__(self, screen):
"""Constructor"""
super().__init__()
self.screen = screen
# pygame.SRCALPHA makes the surface transparent.
self.image = pygame.Surface((22, 32), pygame.SRCALPHA)
pygame.draw.polygon(
self.image, GREEN_LAMBDA,
[[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]], 2
)
self.screen_rect = self.screen.get_rect()
# You can pass the position as the midbottom argument to `get_rect`.
self.rect = self.image.get_rect(midbottom=self.screen_rect.midbottom)
self.center_x = self.rect.centerx
self.center_y = self.rect.centery
# I've removed the `m_right`, etc. variables and just set the speed
# of the sprite in the event loop.
self.max_speed = 3.5
self.speed_x = 0
self.speed_y = 0
def update(self):
# Move the sprite.
self.center_x += self.speed_x
self.center_y += self.speed_y
self.rect.centerx = self.center_x
self.rect.centery = self.center_y
# Keep the sprite on the screen.
if not self.screen_rect.contains(self.rect):
self.rect.clamp_ip(self.screen_rect)
self.center_x, self.center_y = self.rect.center
class Bullet(Sprite):
"""This class represents the bullets."""
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((8, 10), pygame.SRCALPHA)
pygame.draw.ellipse(self.image, GREEN, [1, 0, 5, 8], 2)
self.rect = self.image.get_rect(midbottom=pos)
self.speed = 3 # The speed is now an attribute.
def update(self):
self.rect.y -= self.speed
if self.rect.top < 0:
self.kill() # Remove the sprite from all groups.
def run_game():
pygame.init()
screen = pygame.display.set_mode((800, 700))
clock = pygame.time.Clock() # Use a clock to limit the frame rate.
defender = Spaceship(screen)
all_sprites = Group() # Changed the name because groups are not lists.
all_sprites.add(defender)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return
elif event.key == pygame.K_RIGHT:
defender.speed_x = defender.max_speed
elif event.key == pygame.K_LEFT:
defender.speed_x = -defender.max_speed
elif event.key == pygame.K_UP:
defender.speed_y = -defender.max_speed
elif event.key == pygame.K_DOWN:
defender.speed_y = defender.max_speed
elif event.key == pygame.K_SPACE:
new_bullet = Bullet(defender.rect.midtop) # Pass the pos.
all_sprites.add(new_bullet)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT and defender.speed_x > 0:
defender.speed_x = 0
elif event.key == pygame.K_LEFT and defender.speed_x < 0:
defender.speed_x = 0
elif event.key == pygame.K_UP and defender.speed_y < 0:
defender.speed_y = 0
elif event.key == pygame.K_DOWN and defender.speed_y > 0:
defender.speed_y = 0
all_sprites.update() # Calls the update methods of all sprites.
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60) # Limit the frame rate to 60 FPS.
run_game()
Currently, I have small image that I move using the D and A keys on the keyboard. The code works just how I want it to, but it seems to be a bit unnecessarily complicated. Is there a more efficient way of doing this?
In the main loop, my code checks to see what key is pressed through the events, but after that, if a key is no longer pressed, it checks to see if another key is pressed, just in case a user pressed a key while another key was initially pressed.
Here is my code:
import pygame
import sys
from pygame.locals import *
SCREENX = 640
SCREENY = 480
LEFT = 'left'
RIGHT = 'right'
class Character(pygame.sprite.Sprite):
def __init__(self, image_file):
super().__init__()
temp_image = pygame.image.load(image_file)
self.image = pygame.transform.scale(temp_image, (100, 100))
self.rect = self.image.get_rect()
self.moving = False
self.direction = RIGHT
def start_move(self, direction):
self.moving = True
if direction != self.direction:
self.image = pygame.transform.flip(self.image, True, False)
self.direction = direction
def stop_move(self):
if self.moving:
self.moving = False
def move(self):
if self.direction == RIGHT:
self.rect.x += 5
if self.direction == LEFT:
self.rect.x -= 5
def update(self):
if self.moving:
self.move()
def main():
pygame.init()
surface = pygame.display.set_mode((SCREENX, SCREENY))
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
girl = Character("assets/girl.png")
girl.rect.x = SCREENX/2
girl.rect.y = SCREENY - girl.rect.height
sprites.add(girl)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if not girl.moving:
if event.key == K_d:
girl.start_move(RIGHT)
if event.key == K_a:
girl.start_move(LEFT)
if event.type == KEYUP:
if event.key == K_a or event.key == K_d:
girl.stop_move()
keys_pressed = pygame.key.get_pressed()
if keys_pressed[K_d]:
girl.start_move(RIGHT)
if keys_pressed[K_a]:
girl.start_move(LEFT)
surface.fill((255, 255, 255))
sprites.update()
sprites.draw(surface)
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
Yes, you can get rid of most of the event handling, you can generalize your main loop, and you can get rid of some fields of your Character class.
See my explanatory notes in the comments:
import pygame
import sys
from pygame.locals import *
SCREENX = 640
SCREENY = 480
class Character(pygame.sprite.Sprite):
def __init__(self, image_file):
pygame.sprite.Sprite.__init__(self)
temp_image = pygame.image.load(image_file)
# only to the flipping of the image once
self.image_r = pygame.transform.scale(temp_image, (100, 100))
self.image_l = pygame.transform.flip(self.image_r, True, False)
self.image = self.image_l
self.rect = self.image.get_rect()
def update(self, pressed_keys):
move = 0
if pressed_keys[K_d]: move += 1
if pressed_keys[K_a]: move -= 1
self.rect.move_ip(move*5, 0)
# check which direction we're facing and set the image
self.image = self.image_l if move < 0 else self.image_r
def main():
pygame.init()
surface = pygame.display.set_mode((SCREENX, SCREENY))
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
girl = Character("assets/girl.png")
girl.rect.x = SCREENX / 2
girl.rect.y = SCREENY - girl.rect.height
sprites.add(girl)
while True:
for event in pygame.event.get():
# since we are in a function, we can simply return.
# we don't care here what happens after we quit the main loop
if event.type == QUIT: return pygame.quit()
# get all pressed keys, and just let all sprites
# decide what they want to do with it
keys_pressed = pygame.key.get_pressed()
# you could wrap this information in a more
# general container represeting the game state
# See how the game loop does not care what a
# Sprite does with this information
sprites.update(keys_pressed)
surface.fill((255, 255, 255))
sprites.draw(surface)
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
I am making a game in Pygame. Now I have only one rectangle is moving on screen. The direction of rectangle can be changed from keyboard.
main.py :
import pygame
import sys
import player
import room
# Colors
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
blue = (0, 255, 0)
green = (0, 0, 255)
# Global variables
S_WIDTH = 800
S_HEIGHT = 800
FPS = 60
# player speed
SPEED = 2
# Initialization
pygame.init()
screen = pygame.display.set_mode([S_WIDTH, S_HEIGHT])
pygame.display.set_caption("Py-Man Alpha")
clock = pygame.time.Clock()
# Game class
class Game(object):
"""
This is main class of game.
All game events will happen here.
"""
Player = player.Player(blue, 50, 50, 0, 0)
def __init__(self, screen, running):
self.screen = screen
self.running = running
self.run()
def run(self):
while self.running:
# Events
for event in pygame.event.get():
# If user hits 'x'
if event.type == pygame.QUIT:
self.running = False
# Keyborad events
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.Player.direction = 'up'
self.Player.move(SPEED, WALLS)
elif event.key == pygame.K_DOWN:
self.Player.direction = 'down'
self.Player.move(SPEED, WALLS)
elif event.key == pygame.K_LEFT:
self.Player.direction = 'left'
self.Player.move(SPEED, WALLS)
elif event.key == pygame.K_RIGHT:
self.Player.direction = 'right'
self.Player.move(SPEED, WALLS)
elif event.key == pygame.K_p:
self.Player.direction = 'pause'
# Clear screen
self.screen.fill(white)
#Draw
self.Player.move(SPEED, WALLS)
self.Player.draw(self.screen)
for wall in WALLS:
wall.draw()
# Set clock
clock.tick(60)
# Update screen
pygame.display.flip()
# End of game
pygame.quit()
sys.exit()
# Tests
game = Game(screen, True)
player.py:
#imports
import pygame
class Player(pygame.sprite.Sprite):
"""
This class represent the player image
and has the player actions.
"""
direction = 'right'
def __init__(self, color, width, height, x, y):
# Pygame constructor
pygame.sprite.Sprite.__init__(self)
# Init. variables
self.color = color
self.width = width
self.height = height
# Create sprite
self.image = pygame.Surface([width, height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def draw(self, screen):
# Draw player on the screen
pygame.draw.rect(screen, self.color, [self.rect.x, self.rect.y, self.width, self.height], 0)
def move(self, speed, walls):
# Move the player
if self.direction == 'up':
self.rect.y -= speed
elif self.direction == 'down':
self.rect.y += speed
elif self.direction == 'left':
self.rect.x -= speed
elif self.direction == 'right':
self.rect.x += speed
so, how I can speed up my game? How I can improve speed? Where I wrong?
Thanks!
EDIT:
The refresh of monitor is slow. I press 'up' key , but monitor take command after 2-3 seconds. Why?
You can increase SPEED or clock.tick(60)
But I prefer to increase SPEED.
clock.tick(60) means 60 FPS and you could increase it but many monitors refresh its screen with 60Hz and it means 60 FPS. Bigger FPS means more work for CPU so it is hotter and you can hear CPU fan :)
BTW: Human eye needs at least 24 FPS to see animation.