I am just getting started with Pygame and I am currently trying out some basic movement functions.
I ran into a problem when trying to code my movement conditions into my object class rather than in the game loop.
My first attempt which works is as follow:
classes.py:
import pygame, sys
from pygame.locals import *
class GameObject:
def __init__(self, image, height, speed):
self.speed = speed
self.image = image
self.pos = image.get_rect().move(0, height) #initial placement
def move_south(self):
self.pos = self.pos.move(0, self.speed)
if self.pos.right > 600:
self.pos.left = 0
def move_east(self):
self.pos = self.pos.move(self.speed , 0)
if self.pos.right > 600:
self.pos.left = 0
main.py:
import pygame, sys
from pygame.locals import *
from classes import *
screen = pygame.display.set_mode((640, 480))
#Importing Chars
player = pygame.image.load('green_hunter_small.png').convert()
#player.set_alpha(100) #makes whole player transparent
player.set_colorkey((0,0,0)) #sets background colour to transparent
ennemi = pygame.image.load('red_hunter_small.png').convert()
ennemi.set_colorkey((0,0,0))
background = pygame.image.load('grass_map_640x640.png').convert()
screen.blit(background, (0, 0))
objects = []
objects.append(GameObject(player, 80, 0))
for x in range(2): #create 2 objects
o = GameObject(ennemi, x*40, 0)
objects.append(o)
while True:
for event in pygame.event.get(): #setting up quit
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_DOWN:
for o in objects:
screen.blit(background, o.pos, o.pos) #erases players by bliting bg
for o in objects:
o.speed = 4
o.move_south() #moves player
o.speed = 0
screen.blit(o.image, o.pos) #draws player
if event.key == K_RIGHT:
for o in objects:
screen.blit(background, o.pos, o.pos) #erases players by bliting bg
for o in objects:
o.speed = 4
o.move_east() #moves player
o.speed = 0
screen.blit(o.image, o.pos) #draws player
pygame.display.update()
pygame.time.delay(50)
My second attempt which didn't work was to dp:
classes.py:
import pygame, sys
from pygame.locals import *
class GameObject:
def __init__(self, image, height, speed):
self.speed = speed
self.image = image
self.pos = image.get_rect().move(0, height) #initial placement
def move_south(self):
self.pos = self.pos.move(0, self.speed)
if self.pos.right > 600:
self.pos.left = 0
def move_east(self):
self.pos = self.pos.move(self.speed , 0)
if self.pos.right > 600:
self.pos.left = 0
def move(self):
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_DOWN:
screen.blit(background, self.pos, self.pos) #erases players by bliting bg
self.speed = 4
self.move_south() #moves player
self.speed = 0
if event.key == K_RIGHT:
screen.blit(background, self.pos, self.pos) #erases players by bliting bg
self.speed = 4
self.move_east() #moves player
self.speed = 0
screen.blit(self.image, self.pos) #draws player
main.py:
import pygame, sys
from pygame.locals import *
from classes import *
screen = pygame.display.set_mode((640, 480))
#Importing Chars
player = pygame.image.load('green_hunter_small.png').convert()
#player.set_alpha(100) #makes whole player transparent
player.set_colorkey((0,0,0)) #sets background colour to transparent
ennemi = pygame.image.load('red_hunter_small.png').convert()
ennemi.set_colorkey((0,0,0))
background = pygame.image.load('grass_map_640x640.png').convert()
screen.blit(background, (0, 0))
objects = []
objects.append(GameObject(player, 80, 0))
for x in range(2): #create 2 objects
o = GameObject(ennemi, x*40, 0)
objects.append(o)
while True:
for event in pygame.event.get(): #setting up quit
if event.type == QUIT:
pygame.quit()
sys.exit()
for o in objects:
o.move()
pygame.display.update()
pygame.time.delay(50)
So it seems that the code struggles to go and check the event loop from the instance. The reason I wanted to code the movement as a method rather than straight in main was to save space and make it easier to add characters later on.
Your code has a race condition (to use the term really loosely).
The reason that your characters are not moving is that the first pygame.event.get call (when you are checking for a QUIT event) consumes all the KEYDOWN events that are on the queue. Then (unless you manage to press a key while the first loop is running), there are no KEYDOWN events in the queue when the first GameObject checks for events. Diddo for all other GameObjects.
You need to handler all pygame events in one loop. Example code:
class GameObject():
#rest of class
def move(self,event):
if event.key == K_DOWN:
screen.blit(background, self.pos, self.pos) #erases players by bliting bg
self.speed = 4
self.move_south() #moves player
self.speed = 0
#repeat for all other directions
screen.blit(self.image, self.pos) #draws player
#initialize objects
while True:
for event in pygame.event.get():
if event.type == QUIT: #handle quit event
elif event.type == KEYDOWN:
for o in objects:
o.move(event)
#do non-eventhandling tasks.
Related
Hi am pretty new to programming in general so this might be a pretty dumb problem. Am currently trying to learn more about OOP by making a game.
while the value of player_x is changing for some reasons its not moving as I expected.
main.py
import pygame, settings, obstacles, npc, player
from utils import *
pygame.init()
window = pygame.display.set_mode((settings.WIDTH, settings.HEIGHT))
FPS = pygame.time.Clock()
player = player.Player(window)
Loop
run = True
while run:
FPS.tick(settings.FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
settings.PLAYER_X -= 5
print(settings.PLAYER_X)
pygame.display.update()
player.py
import pygame, settings
pygame.init()
class Player():
def __init__(self, window) -> None:
pygame.draw.rect(window, settings.OLIVE, (settings.PLAYER_X, settings.PLAYER_Y, settings.PLAYER_WIDTH, settings.PLAYER_HEIGHT)) # (x, y, width, height)
def player_control(self):
pass
settings.py
WIDTH = 1440
HEIGHT = 720
FPS = 60
PLAYER_X = 100
PLAYER_Y = 200
PLAYER_WIDTH = 50
PLAYER_HEIGHT = 50
OLIVE = (136, 196, 23)
To move your player, you need to:
Render your player in every while-loop
Render your player with the updated PLAYER_X
And to achieve it, you need a few modification in your code:
Create a function to render in your player.py and call it in the loop instead of render a player in __init__, which will only be called once.
Reference settings.PLAYER_X only once in the __init__ in player.py and update the variable of the player object when key are pressed.
At the end of every loop, render the player object.
player.py
import pygame, settings
pygame.init()
class Player():
def __init__(self, window) -> None:
self.window = window
self.color = settings.OLIVE
self.X = settings.PLAYER_X
self.Y = settings.PLAYER_Y
self.W = settings.PLAYER_WIDTH
self.H = settings.PLAYER_HEIGHT
self.render()
def render(self):
pygame.draw.rect(self.window, self.color, (self.X, self.Y, self.W, self.H))
def player_control(self):
pass
main.py
import pygame, settings, player
pygame.init()
window = pygame.display.set_mode((settings.WIDTH, settings.HEIGHT))
FPS = pygame.time.Clock()
player = player.Player(window)
run = True
while run:
FPS.tick(settings.FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
player.X -= 5
print(player.X)
window.fill((0, 0, 0))
player.render()
pygame.display.update()
im trying to get my image (bird) to move up and down on the screen but i cant figure out how to do it here is what i tried im sure its way off but im trying to figure it out if anyone can help that would be great!
import pygame
import os
screen = pygame.display.set_mode((640, 400))
running = 1
while running:
event = pygame.event.poll()
if event.type == pygame.QUIT:
running = 0
screen.fill([255, 255, 255])
clock = pygame.time.Clock()
clock.tick(0.5)
pygame.display.flip()
bird = pygame.image.load(os.path.join('C:\Python27', 'player.png'))
screen.blit( bird, ( 0, 0 ) )
pygame.display.update()
class game(object):
def move(self, x, y):
self.player.center[0] += x
self.player.center[1] += y
if event.key == K_UP:
player.move(0,5)
if event.key == K_DOWN:
player.move(0,-5)
game()
im trying to get it to move down on the down button press and up on the UP key press
As stated by ecline6, bird is the least of your worries at this point.
Consider reading this book..
For now, First let's clean up your code...
import pygame
import os
# let's address the class a little later..
pygame.init()
screen = pygame.display.set_mode((640, 400))
# you only need to call the following once,so pull them out of the while loop.
bird = pygame.image.load(os.path.join('C:\Python27', 'player.png'))
clock = pygame.time.Clock()
running = True
while running:
event = pygame.event.poll()
if event.type == pygame.QUIT:
running = False
screen.fill((255, 255, 255)) # fill the screen
screen.blit(bird, (0, 0)) # then blit the bird
pygame.display.update() # Just do one thing, update/flip.
clock.tick(40) # This call will regulate your FPS (to be 40 or less)
Now the reason that your "bird" is not moving is:
When you blit the image, ie: screen.blit(bird, (0, 0)),
The (0,0) is constant, so it won't move.
Here's the final code, with the output you want (try it) and read the comments:
import pygame
import os
# it is better to have an extra variable, than an extremely long line.
img_path = os.path.join('C:\Python27', 'player.png')
class Bird(object): # represents the bird, not the game
def __init__(self):
""" The constructor of the class """
self.image = pygame.image.load(img_path)
# the bird's position
self.x = 0
self.y = 0
def handle_keys(self):
""" Handles Keys """
key = pygame.key.get_pressed()
dist = 1 # distance moved in 1 frame, try changing it to 5
if key[pygame.K_DOWN]: # down key
self.y += dist # move down
elif key[pygame.K_UP]: # up key
self.y -= dist # move up
if key[pygame.K_RIGHT]: # right key
self.x += dist # move right
elif key[pygame.K_LEFT]: # left key
self.x -= dist # move left
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, (self.x, self.y))
pygame.init()
screen = pygame.display.set_mode((640, 400))
bird = Bird() # create an instance
clock = pygame.time.Clock()
running = True
while running:
# handle every event since the last frame.
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # quit the screen
running = False
bird.handle_keys() # handle the keys
screen.fill((255,255,255)) # fill the screen with white
bird.draw(screen) # draw the bird to the screen
pygame.display.update() # update the screen
clock.tick(40)
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or a step-by-step movement.
If you want to achieve a continuously movement, you have to use pygame.key.get_pressed(). pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
See also Key and Keyboard event and How can I make a sprite move when key is held down.
Minimal example:
import pygame
import os
class Bird(object):
def __init__(self):
self.image = pygame.image.load(os.path.join('C:\Python27', 'player.png'))
self.center = [100, 200]
def move(self, x, y):
self.center[0] += x
self.center[1] += y
def draw(self, surf):
surf.blit(self.image, self.center)
class game(object):
def __init__(self):
self.screen = pygame.display.set_mode((640, 400))
self.clock = pygame.time.Clock()
self.player = Bird()
def run(self):
running = 1
while running:
self.clock.tick(60)
event = pygame.event.poll()
if event.type == pygame.QUIT:
running = 0
keys = pygame.key.get_pressed()
move_x = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
move_y = keys[pygame.K_DOWN] - keys[pygame.K_UP]
self.player.move(move_x * 5, move_y * 5)
self.screen.fill([255, 255, 255])
self.player.draw(self.screen)
pygame.display.update()
g = game()
g.run()
project game
import pygame
import os
import random from pygame.locals import * # Constants
import math
import sys
import random
pygame.init()
screen=pygame.display.set_mode((1280,720)) #(length,height)
screen_rect=screen.get_rect()
background = pygame.Surface(screen.get_size())
background.fill((255,255,255)) # fill the background white
White = (255,255,255)
---------------------------------------------------------------------
background = pygame.image.load('stage.png').convert()
screen.blit(background, (0, 0))
class Player(pygame.sprite.Sprite):
def __init__(self):
self.rect = pygame.draw.rect(screen, (0,0,128), (50,560,50,25)) #(colour)(x-position,y-position,width,height)
self.dist = 100
def draw_rect(self,x,y): # This is my code which should make the player move
screen.blit(background, (0, 0)) #If this isn't included then when the rectangle moves it's old positon will still be on the screen
self.rect = self.rect.move(x*self.dist, y*self.dist); pygame.draw.rect(screen, (0, 0, 128), self.rect)
pygame.display.update()
def handle_keys(self): # code to make the character move when the arrow keys are pressed
for e in pygame.event.get():
if e.type == KEYDOWN:
key = e.key
if key == K_LEFT:
self.draw_rect(-0.5,0)
elif key == K_RIGHT:
self.draw_rect(0.5,0)
elif key == K_UP:
self.draw_rect(0,-0.5)
elif key == K_DOWN:
self.draw_rect(0,0.5)
elif key == K_SPACE:
self.draw_rect(2,-3)
if self.rect.right > 1400:
self.rect.right = 1400
if self.rect.left < 0:
self.rect.left = 0
if self.rect.bottom > 500:
self.rect.bottom = 500
if self.rect.top < 0:
self.rect.top = 0
player = Player()
------------------------------------------------------------------------
class Enemy(pygame.sprite.Sprite): # the enemy class which works fine
def __init__(self):
x = random.randint(50,450)
self.rect = pygame.draw.rect(screen, (128,0,0), (300,x,50,25))
enemy = Enemy()
pygame.display.flip() #updates the whole screen
def main(): #my main loop
while True:
player.handle_keys()
for event in pygame.event.get
if event.type == pygame.QUIT:
running = False
if __name__ == '__main__': main()
The player does move but when the keys are pressed and the player moves the enemy disappears so i need help to fix it
Ext the player doesn't move when the arrow keys are held only when pressed
You were checking for keydown events in your player's handle_keys method. Any keydown events only register once until the next keyup. You want to use pygame.key.get_pressed() which returns a dict of all the pressed keys at any given moment. I've fixed it for you as well as cleaned the code up a little bit. You may want to consider using a delay or pygame's built-in Clock class for controlling framerates. If you notice, I had to change your movements to very tiny numbers (0.05 from 0.5) because of how quickly you update your frames. Adding in time.sleep() or the pygame clock would fix this.
import pygame
import os
import random
from pygame.locals import * # Constants
import math
import sys
import random
pygame.init()
screen=pygame.display.set_mode((1280,720)) #(length,height)
screen_rect=screen.get_rect()
background = pygame.Surface(screen.get_size())
background.fill((255,255,255)) # fill the background white
White = (255,255,255)
class Player(pygame.sprite.Sprite):
def __init__(self):
self.rect = pygame.draw.rect(screen, (0,0,128), (50,560,50,25)) #(colour)(x-position,y-position,width,height)
self.dist = 100
def draw_rect(self,x,y): # This is my code which should make the player move
screen.blit(background, (0, 0)) #If this isn't included then when the rectangle moves it's old positon will still be on the screen
self.rect = self.rect.move(x*self.dist, y*self.dist); pygame.draw.rect(screen, (0, 0, 128), self.rect)
pygame.display.update()
def handle_keys(self): # code to make the character move when the arrow keys are pressed
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
self.draw_rect(-0.05,0)
elif keys[K_RIGHT]:
self.draw_rect(0.05,0)
elif keys[K_UP]:
self.draw_rect(0,-0.05)
elif keys[K_DOWN]:
self.draw_rect(0,0.05)
elif keys[K_SPACE]:
self.draw_rect(2,-3)
if self.rect.right > 1400:
self.rect.right = 1400
if self.rect.left < 0:
self.rect.left = 0
if self.rect.bottom > 500:
self.rect.bottom = 500
if self.rect.top < 0:
self.rect.top = 0
class Enemy(pygame.sprite.Sprite): # the enemy class which works fine
def __init__(self):
x = random.randint(50,450)
self.rect = pygame.draw.rect(screen, (128,0,0), (300,x,50,25))
player = Player()
enemy = Enemy()
def main(): #my main loop
running = True
while running:
player.handle_keys()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip() #updates the whole screen
if __name__ == '__main__': main()
I'm trying to make a character move across the screen from the top to the bottom, where it will disappear. However, the code I have doesn't return any errors, but it won't move the character either. Here is my code:
import pygame
import sys
import random
pygame.init()
width , height = 600 , 500
display = pygame.display.set_mode((width, height ) )
pygame.display.set_caption("Class Test")
primoimage = pygame.image.load("/home/pi/Downloads/PRIMO/primo_0.png").convert()
class Enemy:
def __init__(self, name, shoot, speed, image):
self.name = name
self.shoot = shoot
self.speed = speed
self.image = image
def move(self):
enemyRack = []
if len(enemyRack) == 0:
enemyRack.append([width/2, 0])
for enemy in enemyRack:
display.blit(self.image, pygame.Rect(enemy[0], enemy[1], 0,0))
for e in range(len(enemyRack)):
enemyRack[e][1]+=2
for enemy in enemyRack:
if enemy[1] > height:
enemyRack.remove(enemy)
primo = Enemy("primo", 2, False, primoimage)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
pass
primo.move()
pygame.display.update()
pygame.quit()
sys.exit()
The problem is in the move() function.
If you could figure it out that would be great! Thanks.
I added some indentation so things work again. (I'm assuming you just broke the indentation when posting your code). Your error was re-initializing enemyRack = [] every time move is called. That way you'll always have [300, 0] in the enemyRack.
import pygame
import sys
import random
pygame.init()
width , height = 600 , 500
display = pygame.display.set_mode((width, height ) )
pygame.display.set_caption("Class Test")
primoimage = pygame.image.load("player.png").convert()
class Enemy:
def __init__(self, name, shoot, speed, image):
self.name = name
self.shoot = shoot
self.speed = speed
self.image = image
self.enemyRack = [] # let's make this a class variable so we don't lose the contents
def move(self):
if len(self.enemyRack) == 0:
self.enemyRack.append([width/2, 0])
for enemy in self.enemyRack:
display.blit(self.image, pygame.Rect(enemy[0], enemy[1], 0,0))
for e in range(len(self.enemyRack)):
self.enemyRack[e][1]+=2
for enemy in self.enemyRack:
if enemy[1] > height:
self.enemyRack.remove(enemy)
primo = Enemy("primo", 2, False, primoimage)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
pass
primo.move()
pygame.display.update()
pygame.quit()
sys.exit()
I have a few questions about pygame. I am completely new to python/pygame and curious if for one, I am doing this properly, or if I am writing this sloppy.
And for my other question, when I use spritecollide, object seems to still be there even after the image disappears. Let me share the code
import pygame, time, random, sys, player, creep, weapon
from pygame.locals import *
pygame.init()
#Variables for the game
width = 700
height = 500
clock = pygame.time.Clock()
screen = pygame.display.set_mode((width, height), 0, 32)
pygame.display.set_caption('Creep')
#Create Characters of the game
player1 = player.Player()
player1.rect.x = 0
player1.rect.y = 0
comp = creep.Creep()
comp.rect.x = random.randrange(width)
comp.rect.y = random.randrange(height)
bullet = weapon.Weapon()
bullet.rect.x = -1
bullet.rect.y = -1
#Make Character Groups
good = pygame.sprite.Group(player1)
bad = pygame.sprite.Group(comp)
weap = pygame.sprite.Group(bullet)
while True:
clock.tick(60)
screen.fill((0,0,0))
#set up for game to get input
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
if event.type == KEYDOWN and event.key == K_c:
bullet.rect.x = player1.rect.x + 25
bullet.rect.y = player1.rect.y
#main controls
key = pygame.key.get_pressed()
if key[K_RIGHT]:
player1.rect.x = player1.moveRight(player1.rect.x)
if key[K_LEFT]:
player1.rect.x = player1.moveLeft(player1.rect.x)
if key[K_DOWN]:
player1.rect.y = player1.moveDown(player1.rect.y)
if key[K_UP]:
player1.rect.y = player1.moveUp(player1.rect.y)
if bullet.rect.x > -1:
weap.draw(screen)
bullet.rect.x = bullet.rect.x +5
pygame.sprite.spritecollide(bullet, bad, True)
pygame.sprite.spritecollide(comp, good, True)
#game functions
good.draw(screen)
bad.draw(screen)
pygame.display.flip()
So I have an image of a gun (player1, 'good' group), an image for the computer (comp, 'bad' group), and an image for a "bullet" when LCTRL is hit (bullet, 'weap' group).. when the bullet hits the image from the bad group, it disappears, which is what I want. But then when I move the player1 image in that direction, it will disappear as if the 'bad group' was still there. I hope this makes sense.
An example code of the classes I am calling on look like this:
import pygame
class Creep(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('creep.jpg')
self.rect = self.image.get_rect()
Any idea? and if there is a better way of going at this please let me know, I only started learning a week ago, and don't know if I am going in the proper direction, or not. Thanks!
I wasn't sure what some of your variables were. Note: If you put a sprite in multiple groups, then kill it, it will kill it in all groups automatically.
I started cleaning up the code.
#import time, sys, # not longer required
import player, creep, weapon
import random
import pygame
from pygame.locals import *
pygame.init()
#Variables for the game
width = 700
height = 500
clock = pygame.time.Clock()
screen = pygame.display.set_mode((width, height), 0, 32)
pygame.display.set_caption('Creep')
#Create Characters of the game
player1 = player.Player()
player1.rect.x = 0
player1.rect.y = 0
comp = creep.Creep()
comp.rect.topleft = random.randrange(width), random.randrange(height)
bullet = pygame.sprite.Sprite()# weapon.Weapon()
bullet.rect.topleft = (-1, -1)
#Make Character Groups
good = pygame.sprite.Group(player1)
bad = pygame.sprite.Group(comp)
weap = pygame.sprite.Group(bullet)
done = False
while not done:
clock.tick(60)
#set up for game to get input
for event in pygame.event.get():
if event.type == pygame.QUIT: done = True
if event.type == KEYDOWN:
if event.key == K_ESCAPE: done = True
elif event.key == K_c:
bullet.rect.x = player1.rect.x + 25
bullet.rect.y = player1.rect.y
#main controls
key = pygame.key.get_pressed()
if key[K_RIGHT]:
player.rect.x += 10
if key[K_LEFT]:
player1.rect.x -= 10
if key[K_DOWN]:
player.rect.y += 10
if key[K_UP]:
player.rect.y -= 10
# movement, collisions
pygame.sprite.spritecollide(bullet, bad, True)
pygame.sprite.spritecollide(comp, good, True)
# not sure what this was for? If you meant 'onscreen' or?
# You can kill it if it goes offscreen. Otherwise draw works if offscreen.
if bullet.rect.x > -1:
bullet.rect.x += 5
screen.fill(Color("black"))
weap.draw(screen)
#game functions
good.draw(screen)
bad.draw(screen)
pygame.display.flip()