Using classes in Pygame - python

Okay, so I am starting to have fun with pygame. But I've got a problem. I tried to somehow enchance my code, make it organised, so I've decided to use classes here. It looks like this:
import pygame
from pygame.locals import *
import sys
pygame.init()
class MainWindow:
def __init__(self, width, height):
self.width=width
self.height=height
self.display=pygame.display.set_mode((self.width,self.height))
pygame.display.set_caption("Caption")
def background(self)
img = pygame.image.load("image.png")
self.display.blit(img, (0,0))
mainWindow = MainWindow(800,600)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.exit()
sys.exit()
mainWindow.background()
pygame.display.update()
Okay, works. But what if I want to, for example fill the windows with white color? Then I have to define a method fill(), which will just self.display.fill(), right? Is there a way, to handle it normally, without defining hundreds of pygame-already-existing methods in my class?
And one another thing. If I do something by using my class, and I screw up, I always get this msg:
File "C:/Python35/game.py", line 23, in <module>
pygame.display.update()
pygame.error
And I actually don't know what the heck is wrong. If I do this normally, without classes, then I get erros such as, pygame object blabla has no method blablabla or something like that, I just know what's happening. Is there a way to get through this, and find what's going on?
Thanks in advance for your help!

What you are doing here is on the right track, but it is done the wrong way. Your main "game loop" should be inside the class itself as a method, rather than calling stuff from outside the class in an actual loop. Here is a basic example of what you should be doing.
# Load and initialize Modules here
import pygame
pygame.init()
# Window Information
displayw = 800
displayh = 600
window = pygame.display.set_mode((displayw,displayh))
# Clock
windowclock = pygame.time.Clock()
# Load other things such as images and sound files here
image = pygame.image.load("foo.png").convert # Use convert_alpha() for images with transparency
# Main Class
class MainRun(object):
def __init__(self,displayw,displayh):
self.dw = displayw
self.dh = displayh
self.Main()
def Main(self):
#Put all variables up here
stopped = False
while stopped == False:
window.fill((255,255,255)) #Tuple for filling display... Current is white
#Event Tasking
#Add all your event tasking things here
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
elif event.type == pygame.KEYDOWN:
stopped = True
#Add things like player updates here
#Also things like score updates or drawing additional items
# Remember things on top get done first so they will update in the order yours is set at
# Remember to update your clock and display at the end
pygame.display.update()
windowclock.tick(60)
# If you need to reset variables here
# This includes things like score resets
# After your main loop throw in extra things such as a main menu or a pause menu
# Make sure you throw them in your main loop somewhere where they can be activated by the user
# All player classes and object classes should be made outside of the main class and called inside the class
#The end of your code should look something like this
if __name__ == __main__:
MainRun()
The main loop will call itself when the object MainRun() is created.
If you need more examples on specific things such as object handling let me know and I will see if I can throw some more information up for you.
I hope this helps you with your programming and the best of luck to you.
========================= EDIT ================================
In this case for these special operations make them object specific. Instead of using one generic method to blit your objects, make each object have its own function. This is done this way to allow for more options with each object you make. The general idea for the code is below... I have created a simple player object here.
#Simple player object
class Player(object):
def __init__(self,x,y,image):
self.x = x
self.y = y
self.image = image
#Method to draw object
def draw(self):
window.blit(self.image,(self.x,self.y))
#Method to move object (special input of speedx and speedy)
def move(self,speedx,speedy):
self.x += speedx
self.y += speedy
Now here is how you use the object's methods... I have included an event loop to help show how to use the move function. Just add this code to your main loop wherever it is needed and you will be all set.
#Creating a player object
player = Player(0,0,playerimage)
#When you want to draw the player object use its draw() method
player.draw()
#Same for moving the player object
#I have included an event loop to show an example
#I used the arrow keys in this case
speedx = 0
speedy = 0
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
speedy = -5
speedx = 0
elif event.key == pygame.K_DOWN:
speedy = 5
speedx = 0
elif event.key == pygame.K_RIGHT:
speedy = 0
speedx = 5
elif event.key == pygame.K_LEFT:
speedy = 0
speedx = -5
elif event.type == pygame.KEYUP:
speedx = 0
speedy = 0
#Now after you event loop in your object updates section do...
player.move(speedx,speedy)
#And be sure to redraw your player
player.draw()
#The same idea goes for other objects such as obstacles or even scrolling backgrounds
Be sure to use the same display name of the display inside your draw function.

Related

How do I use the clock/ticks function in Pygame to spawn another object after a certain amount of time?

class Sideways:
"""Overall class to manage game assets"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.screen = pygame.display.set_mode((1200, 600))
self.screen_rect = self.screen.get_rect()
pygame.display.set_caption("Pew Pew")
self.bg_color = (204, 255, 255)
self.ship = Ship(self)
self.moving_up = False
self.moving_down = False
self.moving_left = False
self.moving_right = False
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
FIRE_EVENT = pygame.USEREVENT + 1 # This is just a integer.
pygame.time.set_timer(FIRE_EVENT, 1000) # 1000 milliseconds is 1 seconds.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == FIRE_EVENT: # Will appear once every second.
self._create_fleet()
I'm trying to spawn another ship in pygame after a certain interval of time.
I just learned about the clock/ticks function from my previous question being directed to another thread that showed me it. Thank you. I tried to incorporate that into my code during this section.
However, when I run it, the game freezes up and crashes as if it's spawning too many things at the same time to handle and overloads. How would I manage to use this function correctly?
You must create the objects in the application loop, but not in a separate loop. This extra loop freezes your system. In general, if you want to control something over time in pygame, you have two options:
Use pygame.time.get_ticks() to measure time and and implement logic that controls the object depending on the time.
Use the timer event. Use pygame.time.set_timer() to repeatedly create a USEREVENT in the event queue. Change object states when the event occurs.
For example see Spawning multiple instances of the same object concurrently in python or create clock and do action at intervals and many more similar answers.

Variable Won't Update In Class

import sys
import pygame
from pygame.locals import *
pygame.init()
class Game:
def __init__(self):
self.width = 800
self.height = 900
self.win = pygame.display.set_mode([self.width, self.height])
self.caption = pygame.display.set_caption('Clicker Game','Game')
self.money = 0
self.moneyperclick = 0
def moneytracker(self):
self.money = self.money + self.moneyperclick
print(self.money)
def mousestuff(self):
self.mousepos = pygame.mouse.get_pos()
self.clicked = pygame.mouse.get_pressed()
def mainloop(self):
self.mousestuff()
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
self.moneytracker()
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
while True:
Game().mainloop()
I'm still somewhat new to coding but I am very confused as to why the self.money variable isn't updating even though I call for it to update. I've done some tests and I know that it is looping the code where I set self.money = 0 but I don't know how to get around this. Thanks
It looks like the problem is here:
while True:
Game().mainloop()
This creates a new Game object in every iteration of the loop, which means all the values are initialized for the first time, because it's a new object.
Alternatives are to move the while True loop inside mainloop(), or try something like:
game = Game()
while True:
game.mainloop()
This creates a single Game object as game, whose mainloop() method is called repeatedly. Because the object is only created once, the attributes of the object (e.g. money, accessed as self.money) that are modified as a result of player actions will keep their values between iterations of the loop.
In the original loop structure, a new Game object was created each time, which means that a player's actions were only performed once before the object was abandoned and replaced by a new one, with newly initialized attributes.

How to move pieces in chess program

as a premice i would like to say that i'm totally new to programming and i'm not a computer science student so i'm sorry if my code makes you cringe, i recently had some python classes and enjoyed them so i wanted to deepen a little so i figured that a simple chess game would be fun to do.
As you can imagine i am using pygame.
As for now i "drew" a chessboard and i blitted the pieces in place, my idea is that i would get the coordinates of every click, if the coordinates are the same (or in range) of the blitted image the variable would update with the second click, how can i make it so that the system recognizes a "first" and "second" click.
import pygame as pg
import sys
pg.init()
schermo = pg.display.set_mode((640,540))
def coordinate():
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
if event.type == pg.MOUSEBUTTONUP:
mx1, my1 = pg.mouse.get_pos()
print (mx1,my1)
return mx1,my1
pg.display.update()
this is how i get the coords
ctorrensx = (53,53)
[...omissis]
def move():
result = coordinate()
global ctorrensx
if result == ctorrensx:
ctorrensx = (200,200)
this was my first idea for the moving function, ctorrensx is an example i wanted to try on, they are the coords of the left black rook, once i would click on it i wanted it to move to the coords (200,200) but it's not happening.
this is my first time using stack overflow so i hope that i didn't create too much confusion on my question.
thank you all.
You can set a flag to determine whether the click is a first or second.
For example:
firstClick = True
while True:
for event in pg.event.get():
if event.type == pg.MOUSEBUTTONDOWN:
if(firstClick):
firstClick = False
#Code for first click
else:
#code for second click
firstClick = True #resets the flag

Python - Pygame random obstacle height issues

I currently am working on a 'Flappy Bird' remake in Pygame using Python 3.2. I thought it would be good for practice, and relativly simple. However, it is proving to be hard. Currently, I am having a problem when drawing a rectangle at different heights but keeping the rectangle at the height it is set to.
Here is my Pipe class
class Pipe:
def __init__(self,x):
self.drawn = True
self.randh = random.randint(30,350)
self.rect = Rect((x,0),(30,self.randh))
def update(self):
self.rect.move_ip(-2,0)
def draw(self,screen):
self.drawn = True
pygame.draw.rect(screen,(0,130,30),self.rect)
My while Loop is as follows:
while True:
for event in pygame.event.get():
movey = +0.8
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_SPACE:
movey = -2
x += movex
y += movey
screen.blit(background,(0,0))
screen.blit(bird,(x,y))
Pipe1 = Pipe(scrollx)
if Pipe1.drawn == True:
Pipe1.update()
else:
Pipe1 = Pipe(scrollx)
Pipe1.draw(screen)
scrollx -= 0.3
pygame.display.update()
I have being wrestling with this code for over a week, and I really appreciate any help you can give.
I'm not following the logic of this part:
Pipe1 = Pipe(scrollx)
if Pipe1.drawn == True:
Pipe1.update()
else:
Pipe1 = Pipe(scrollx)
Pipe1.draw(screen)
The drawn attribute is set to True at the constructor, so when do you expect the else condition to be triggered? Remember you are recreating this pipe every frame.
Have you tried drawing the pipe the same you way you did with the bird?
Edit: suggestion for you for loop:
PIPE_TIME_INTERVAL = 2
pipes = [] # Keep the pipes in a list.
next_pipe_time = 0
while True:
[... existing code to handle events and draw the bird ...]
for pipe in pipes:
pipe.move(10) # You'll have to write this `move` function.
if pipe.x < 0: # If the pipe has moved out of the screen...
pipes.pop(0) # Remove it from the list.
if current_time >= next_pipe_time: # Find a way to get the current time/frame.
pipes.append(Pipe()) # Create new pipe.
next_pipe_time += PIPE_TIME_INTERVAL # Schedule next pipe creation.
You are creating a new Pipe on every loop, but never hang on to the old one(s), so you get a new random height each time. Move this line:
Pipe1 = Pipe(scrollx)
outside the while loop. Better yet, have a list of pipes you can add new ones to and easily update them all. You never set self.drawn = False within Pipe either.
Also, you are resetting movey for every event, try:
movey = 0.8 # no need for plus
for event in pygame.event.get():

Pygame Inquiry- Generating Multiple Sprites in Same Class

Unimportant Preamble:
Hello, I'm using Python and Pygame to create a game. This is for the purpose of improving my programming skills rather than a serious attempt at game creation. I've taken a break from Python lately for Objective C, but I'm now returning to it. This is a problem that I was having before I took a brief break, and I've returned to a question that was originally puzzling me. I've spent quite a while researching it, and I have a suspicion I've come across the solution and merely failed to understand it. I apologize for some of the bad naming conventions/lack of comments. I'm working on improving that.
Substance of Question:
Anyway, I've attached the four images I'm using. The program uses a simple function to position various Tiles on the screen. The mouse cursor is a sword. It is the entire image, but I'll be changing that later. I've made the program type "blue" in the shell whenever the cursor collides with a Tile. My goal is to have this happen when it collides with "ANY" tile of that color.
Long-term, I want to be able to modify the properties of these tile sprites. Various game-pieces would interact, and I would need to save the state of each sprite. I'd also be setting interactions for the other sprites.
Right now the sprites are all generating images, but my collision rectangle for the Tile is simply moving after each image is generated. I suppose that makes sense given the code, but I need a way to multiple sprites, each with a rectangle for collision.
Thanks
EDIT: I was unable to add images due to a new-user restriction. They are available enter link description here I think I read somewhere that people can (and do) edit posts here. So if anyone who the ability to move the images into this thread is welcome to do so.
import random,math,sys,os
import pygame
from pygame.locals import *
pygame.init() #Initializing Pygame
#Colors
black=(0,0,0)
#Screen
screen=pygame.display.set_mode((1200,800),0,0)
pygame.display.set_caption("Nero's Sandbox")
pygame.mouse.set_visible(False)
clock=pygame.time.Clock()
fps=40
#Game Functions:
def terminate():
pygame.quit()
sys.exit()
def numgen(x,y):
return random.randint(x,y)
#Loop Variables
tri=2
#Groups:
allsprites = pygame.sprite.Group()
alltiles = pygame.sprite.Group()
allmice = pygame.sprite.Group()
#Mouse Classes
class Pointy(pygame.sprite.DirtySprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('redsword.png').convert() #31x32 image
self.image.set_colorkey(black)
self.rect=self.image.get_rect()
self.set=pygame.sprite.Group()
self.add(allmice, allsprites, self.set)
pygame.sprite.RenderPlain((self.set,allmice,allsprites))
def update(self):
screen.fill(black)
alltiles.draw(screen)
if event.type == pygame.MOUSEMOTION:
pos = pygame.mouse.get_pos()
self.rect.topright = pos
self.set.draw(screen)
#Tile Sprites - only one rect is being recognized.
class Tile(pygame.sprite.Sprite):
def __init__(self, graphic):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(graphic).convert()
self.image = pygame.transform.scale((self.image),(50,50))
self.rect=self.image.get_rect()
self.add(alltiles, allsprites)
self.set=pygame.sprite.RenderPlain((self))
def update(self, x, y):
pos = (x,y)
self.rect.topleft = pos
#Micers
pointy1=Pointy()
#Game Loops
while True: #Ensures all loops within program are constantly called when conditions are met.
screen.fill(black)
while tri==2:
for event in pygame.event.get():
if event.type == QUIT:
terminate()
pygame.display.flip()
x = 0
y = 50
w = 0
while x!=600:
x=x+50
w = w+1
if w%2==0:
purpletile1=Tile('purplesquare.png')
purpletile1.set.update(x,y)
purpletile1.set.draw(screen)
else:
c=numgen(1,2)
if c==1:
bluetile1=Tile('lightbluesquare.png')
bluetile1.set.update(x,y)
bluetile1.set.draw(screen)
if c==2:
redtile1=Tile('redsquare.png')
redtile1.set.update(x,y)
redtile1.set.draw(screen)
if x>=600 and y!=450:
if y<450:
x = 0
y = y+50
w=w-1
if y>=450:
tri=3
while tri==3:
for event in pygame.event.get():
if event.type == QUIT:
terminate()
alltiles.draw(screen)
pointy1.set.update()
pointy1.set.draw(screen)
pygame.display.flip()
clock.tick(fps)
if pygame.sprite.collide_rect(pointy1,bluetile1):
print('blue')
I had this same problem myself! I did some debugging, and it appeared that all instances of my class shared the same instance of pygame.Rect()
You may want to change the line:
pygame.sprite.Sprite.__init__(self)
to
super.__init__(self)
This way, pygame.sprite.Sprite's init will set attributes to your tile. I could be wrong, I'm not entirely familiar with python's inheritance syntax, but that is the way I do it.
I could also be the get_rect that is causing the same rectangle to be used for all classes, but that doesn't seem to be likely.
I hope that I was some help, and just remember that pygame.Rect is an object, so somehow you are instantiating only once.

Categories

Resources