I'm learning OOP and I have a few questions.
When the initiliazer is called, is the code automatically processed?
Cause if that's the case, I don't understand why my game isn't drawing the rectangle i ask it to draw in the init part of the player class.
I'm very new to OOP and as such I'm not sure of what I'm doing, to some extent.
Here's my code:
import pygame
white = (255, 255, 255)
black = (0, 0, 0)
class Game():
width = 800
height = 600
screen = pygame.display.set_mode((width, height))
def __init__(self):
pass
def fill_screen(self, color):
self.color = color
self.screen.fill(self.color)
class Player(pygame.sprite.Sprite):
lead_x = 800/2
lead_y = 600/2
velocity = 0.002
block_size = 10
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.draw.rect(game.screen, black, [self.lead_x, self.lead_y, self.block_size, self.block_size])
def move_player_x_left(self):
self.lead_x += -self.velocity
def move_player_x_right(self):
self.lead_x += self.velocity
def move_player_y_up(self):
self.lead_y += -self.velocity
def move_player_y_down(self):
self.lead_y += self.velocity
game = Game()
player = Player()
exitGame = False
while not exitGame:
game.fill_screen(white)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exitGame = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
player.move_player_y_up()
if event.key == pygame.K_s:
player.move_player_y_down()
if event.key == pygame.K_d:
player.move_player_x_right()
if event.key == pygame.K_a:
player.move_player_x_left()
pygame.display.update()
pygame.quit()
quit()
You are constantly filling the screen with white in your mainloop. The Player class only draws on __init__. This means that the rect is drawn for a split second and then covered over by white.
Your assumption about the code in __init__ automatically being called is correct. These methods with double underscores are called by python in special cases, they are called magic methods. You can find a list of them here.
def __init__(self):
pygame.sprite.Sprite.__init__(self)
# The rect drawing part was moved from here.
def update(self):
# You were previously assigning this to a variable, this wasn't necessary.
pygame.draw.rect(game.screen, black, [self.lead_x, self.lead_y, self.block_size, self.block_size])
You will need to call the new update method in the mainloop after you fill the screen.
while True:
game.fill_screen(white)
player.update()
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()
So, im writing some simple code in python pygame, just to learn. I encountered problem, when i want to check collisions of ball whether it is the same as y possition of my pallete. The main problem is that the value of variable of my pallete is updating by the user, and it sits inside class pallete, in function move. How can i get it out and put it outside class, into function holding ball?
import pygame
from pygame.constants import DROPTEXT
pygame.init()
#window
w_width = 1000
w_height = 600
screen = pygame.display.set_mode((w_width, w_height))
clock = pygame.time.Clock()
open = True
#player
x,y = 100,250
#define player action variables
speed = 5
speed_x,speed_y = 5,4
moving_down = False
moving_up = False
#define ball action variables
x_ball,y_ball = 500,250
radius = 30
class palette(pygame.sprite.Sprite):
global x,y
def __init__(self, x, y, speed):
self.speed = speed
pygame.sprite.Sprite.__init__(self)
self.player_rect = pygame.Rect(x,y,30,100)
def draw(self):
pygame.draw.rect(screen, 'White', self.player_rect)
def move(self, moving_up, moving_down):
#reset movement variables
global dy
dy = 0
#assing movement variables if moving up or down
if moving_up:
dy = -self.speed
if moving_down:
dy = self.speed
#update pallete possition
self.player_rect.y += dy
def ball():
global speed_x,speed_y,x_ball,y_ball
#update pos of bal
x_ball += speed_x
y_ball += speed_y
#basic colision with screen
if x_ball>=w_width-(0.5*radius) or x_ball <=0+(0.5*radius):
speed_x*=-1
if y_ball>=w_height-(0.5*radius) or y_ball <=0+(0.5*radius):
speed_y*=-1
#collision with pallettes
pygame.draw.circle(screen, (255,255,255), (x_ball,y_ball), radius, 5)
#build players and enemies
player = palette(x,y, speed)
enemy = palette(x*9,y, speed)
#game
while open:
for event in pygame.event.get():
#quit game
if event.type == pygame.QUIT:
open = False
#keyboard presses
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
moving_up = True
if event.key == pygame.K_s:
moving_down = True
if event.key == pygame.K_ESCAPE:
open = False
#keyboard button released
if event.type == pygame.KEYUP:
if event.key == pygame.K_w:
moving_up = False
if event.key == pygame.K_s:
moving_down = False
screen.fill((0,0,0))
clock.tick(60)
pygame.display.flip
#keys
#init players
player.draw()
enemy.draw()
enemy.move(moving_up, moving_down)
player.move(moving_up, moving_down)
ball()
pygame.display.update()
Read about Classes and Instance Objects. The "variables" in a class are called "attributes". You can access the instance attributes through the instance object. In your case player and enemy are instances of the class palette. The palette class has a player_rect attribute which is a pygame.Rect object. Therefore:
y = player.pylyer_rect.y
However read the documentation. pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The latter delegates to the update method of the contained pygame.sprite.Sprites — you have to implement the method. See pygame.sprite.Group.update():
Calls the update() method on all Sprites in the Group. [...]
The former uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects — you have to ensure that the pygame.sprite.Sprites have the required attributes. See pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]
So you should rename player_rect to rect. Class Names should normally use the CapWords convention. So rename palette in Palette:
class Palette(pygame.sprite.Sprite):
def __init__(self, x, y, speed):
self.speed = speed
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(x,y,30,100)
# [...]
player = Palette(x, y, speed)
enemy = Palette(x*9, y, speed)
I wanted to make a simple dance game using pygame.
class Sword():
def __init__(self, image, speed, center):
self.image = pygame.image.load(image)
self.rect = self.image.get_rect()
self.rect.center = center
self.speed = speed # object speed
self.alive = True # draw object if not hit the enemey
def live(self, enemy): # check if hit the enemy
if self.rect.colliderect(enemy):
self.alive = False
mixer.music.play()
def update(self): # move object
self.rect.x += self.speed
def draw(self, surface): # draw object
if self.alive:
surface.blit(self.image, (self.rect))
I can draw and move my object on the screen.
sword = [Sword('sword.png',1,(100,420))],
[Sword('sword.png',5,(200,420))],
[Sword('sword.png',3,(300,420))]]
while (True):#Mainloop
kare = pygame.draw.rect(display,((255,0,0)), pygame.Rect(700,420,100,100))
for i, tile in sorted(enumerate(sword), reverse=True):
tile.update()
tile.live(kare)
tile.draw()
if not tile.live:
sword.pop(i)#delete object
my code works flawlessly so far;=)
but when I want to draw 4 objects on the screen every time, things don't go as I want.
def main():
extend = []
running = True
while running:
if len(extend) >= 3:
running = False
else:
for i in range(4):
extend.append(Sword('sword.png',1,(100,420)))
return extend
i have a simple function to draw 4 objects at a time to the screen but when i use this in my main loop my objects don't move
while True:
sword = main()#call function
kare = pygame.draw.rect(display,((255,0,0)), pygame.Rect(700,420,100,100))
for i, tile in sorted(enumerate(sword), reverse=True):
tile.update()
tile.live(kare)
tile.draw(display)
if not tile.alive:
sword.pop(i)
First of all, I apologize for this long post, but I wanted the topic to be clear, how do I edit my code so that there are 4 on the screen each time?
Thank you for your interest...
Here is the full code of the solution:
import pygame, sys, math, random
from pygame import mixer
from pygame.locals import *
mainClock = pygame.time.Clock()
pygame.init()
pygame.display.set_caption('game base')
WINDOW_SIZE = (980,570)
screen = pygame.display.set_mode((WINDOW_SIZE), 0, 32)
display = pygame.Surface((WINDOW_SIZE))
RIGHT_KEY,UP_KEY,LEFT_KEY,DOWN_KEY = False,False,False,False
class Sword():
def __init__(self, image, speed, center):
self.image = pygame.image.load(image)
self.rect = self.image.get_rect()
self.rect.center = center
self.speed = speed
self.alive = True
def live(self, enemy):
if self.rect.colliderect(enemy):
self.alive = False
#mixer.music.play()
def update(self):
self.rect.x += self.speed
def draw(self, surface):
if self.alive:
surface.blit(self.image, (self.rect))
def main():
extend = []
running = True
if len(extend) >= 3:
running = False
while running:
if len(extend) >= 4:
running = False
else:
for i in range(5):
extend.append(Sword('sword.png',i,(100,420)))
return extend
sword = main()
while True:
display.fill((250,198,14))
kare = pygame.draw.rect(display,((255,0,0)), pygame.Rect(700,420,100,100))
all_not_alive = True
for tile in sword:
tile.update()
tile.live(kare)
tile.draw(display)
if not tile.alive:
tile.rect.x = 100
tile.alive = True
# Buttons ------------------------------------------------ #
RIGHT_KEY,UP_KEY,LEFT_KEY,DOWN_KEY = False,False,False,False
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == pygame.K_UP:
UP_KEY = True
if event.key == pygame.K_DOWN:
DOWN_KEY = True
if event.key == pygame.K_LEFT:
LEFT_KEY = True
if event.key == pygame.K_RIGHT:
RIGHT_KEY = True
# Update -------------------------------------------------
screen.blit(display,(0,0))
pygame.display.update()
mainClock.tick(60)
The problem was that you have recreated all swords every time when the game updated. You should recreate the sword only if it is not alive.
EDIT
I have moved the code which generates the Swords out of the loop:
sword = main()
and I am moving all swords to the start after they collide:
if not tile.alive:
tile.rect.x = 100
tile.alive = True
So im trying to make a game where the character should dodge enemies which are randomly spawned from above. The problem is, I dont know how to make the Random values different without instanciating another class, which I dont want to do. Also, with Screen.fill(), the enemies wont show up since they're being overlapped by the colour every frame, and if I dont use Screen.fill(), my character would leave a trail everytime it moves. Any Suggestions?
import random
pygame.init()
Running = True
Screen = pygame.display.set_mode((800, 600))
player_img = pygame.image.load('/Users/kevinhadinata/Downloads/ufo.png')
player_updated = pygame.transform.scale(player_img,(60,60))
enemy_list = []
enemy_img = pygame.image.load('/Users/kevinhadinata/Downloads/alien.png')
SPAWNENEMY = pygame.USEREVENT
pygame.time.set_timer(SPAWNENEMY,1000)
class Player:
def __init__(self):
self.ypos = 540
self.xpos = 325
self.height = 60
self.width = 60
self.playerUpdated = player_updated
def create_player(self):
Playerss = pygame.Rect(self.xpos,self.ypos,self.height,self.width)
pygame.draw.ellipse(Screen, (0, 0, 0), Playerss)
Screen.blit(player_updated, (Playerss.x, Playerss.y))
sizee = random.randint(10,40)
randomX = random.randint(0,700)
class Enemy:
def __init__(self):
self.xval = random.randint(0,700)
self.size = random.randint(10,40)
def create_enemy(self):
Enemy = pygame.Rect(self.xval, 0, self.size,self.size)
#enemy_updated = pygame.transform.scale(enemy_img,(self.size,self.size))
enemy_list.append(Enemy)
pygame.draw.ellipse(Screen,(255,255,0),Enemy)
Player = Player()
Enemys = Enemy()
while Running:
Screen.fill((0,0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
Running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
Player.xpos -= 20
if event.key == pygame.K_RIGHT:
Player.xpos += 20
if event.type == SPAWNENEMY:
Enemys.create_enemy()
Player.create_player()
pygame.display.update()
It's a good idea to not instantiate a new enemy, because just recycling it back to the top of the screen is enough. This amounts to simply changing the size & x,y.
Some of your code is occluding the Enemy class name with an Enemy variable name. I've changed this variable name to new_enemy.
class Enemy:
def __init__(self):
self.recycle() # set the position & size initially
def recycle( self ):
# start or re-start an enemy position
self.size = random.randint(10,40)
self.xval = random.randint(0,700)
self.yval = -self.size # off the screen-top
self.rect = pygame.Rect( self.xval, self.yval, self.size, self.size )
def draw( self, screen ):
pygame.draw.ellipse( screen, (255,255,0), self.rect )
def create_enemy(self):
global enemy_list
new_enemy = Enemy() # create a new Enemy
enemy_list.append( new_enemy ) # add it to the list
Then in your main loop, you can recycle() any Enemy that goes off-screen.
[...] # in main loop
# draw all the enemies, and re-position any enemies that moved off the screen
for enemy in enemy_list:
enemy.draw( screen )
if ( enemy.ypos > 600 ): # TODO - don't use a fixed size
enemy.recycle() # move back to top
The drawing an spawning code was mixed-up, I have re-arranged this into a separate .draw() function. Using this solves the problem with clearing the screen too. Each frame, the code clears the screen, then repaints all the items.
You need to create a list of enemies and iterate the list in the main loop to draw each enemy. Use randint to randomly place the enemies.
Try this code:
import random, pygame
pygame.init()
Running = True
Screen = pygame.display.set_mode((800, 600))
player_img = pygame.image.load('/Users/kevinhadinata/Downloads/ufo.png')
player_updated = pygame.transform.scale(player_img,(60,60))
enemy_list = []
enemy_img = pygame.image.load('/Users/kevinhadinata/Downloads/alien.png')
SPAWNENEMY = pygame.USEREVENT
pygame.time.set_timer(SPAWNENEMY,1000)
class Player:
def __init__(self):
self.ypos = 540
self.xpos = 325
self.height = 60
self.width = 60
self.playerUpdated = player_updated
def create_player(self):
self.Playerss = pygame.Rect(self.xpos,self.ypos,self.height,self.width)
pygame.draw.ellipse(Screen, (0, 0, 0), self.Playerss)
Screen.blit(player_updated, (self.Playerss.x, self.Playerss.y))
def draw(self): # draw player
Screen.blit(player_updated, (self.xpos,self.ypos))
sizee = random.randint(10,40)
randomX = random.randint(0,700)
class Enemys:
def __init__(self):
#self.xval = random.randint(0,700)
self.size = random.randint(10,40)
def create_enemy(self):
Enemy = pygame.Rect(random.randint(100,700), 0, self.size,self.size)
#enemy_updated = pygame.transform.scale(enemy_img,(self.size,self.size))
enemy_list.append(Enemy)
pygame.draw.ellipse(Screen,(255,255,0),Enemy)
def draw(self): # draw all enemies
for e in enemy_list:
pygame.draw.ellipse(Screen,(255,255,0),e)
Player = Player() # player object
Enemys = Enemys() # collection of enemies
Player.create_player()
while Running:
Screen.fill((0,0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
Running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
Player.xpos -= 20
if event.key == pygame.K_RIGHT:
Player.xpos += 20
if event.type == SPAWNENEMY:
Enemys.create_enemy()
Player.draw() # draw player
Enemys.draw() # draw all enemies
pygame.display.update()
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.