Pygame - designing the code - python

My apologies for the poor title. I have experience with how game's code is written with C# (XNA) or java (LibGDX), but I have recently started using python and pygame specifically and I have trouble figuring out how to design the code. This is how I thought it would make to do:
game.py file:
import pygame
import entities
width = 400
height = 300
# Set up the window size
screen = pygame.display.set_mode((width, height), 0, 32)
player = entities.Player(width / 2, height / 2) # The player
#main game loop is here...
entities.py file:
import pygame
import game
class Player:
def __init__(self, x, y):
# Texture for the player
self.texture = pygame.image.load('player.png')
def draw(self):
""" Draws the player """
game.screen.blit(self.texture, (self.position.x, self.position.y))
#class Enemy: ...
However this code give me errors. I think the problem is that I am importing entities.py in the game.py and then I import game.py in the entities.py.
How would I go about to properly design this in python ?

I recommend to use what pygame already offer.
Take a look at the classes Sprite and Group (and their more complex subclasses).
So your player class could look like:
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
# Texture for the player
self.image = pygame.image.load('player.png')
self.rect = self.image.get_rect(topleft=(x, y))
# no need for a draw function
# the sprite class will handle this
# just make sure you store the coordinates of
# your entity in self.rect
def update(self, some_game_state):
pass # do something with some_game_state
Then, in your game, you would put all of your entities in one (or more) group(s), and just call draw and update on that group(s).
entities = pygame.sprite.Group(player) # add other sprite here, too
# main loop
while True:
...
# pass the game state to all entities
# how you design this is up to you
# (pass multiple values, pass a single instace of the game class,
# create a class that encapsulates the state necesseray for the entities etc.)
entites.update(some_game_state)
# pass the main surface to the draw function
entities.draw(screen)
...

You should pass the variables into the relevant classes, rather than making them globals and importing them. So, one possible solution would be to make screen an attribute of Player:
class Player:
def __init__(self, x, y, screen):
self.screen = screen
...
def draw(self):
""" Draws the player """
self.screen.blit(...)
Now, there is no need to import game in entities.py, and you can instantiate the player there like this:
player = entities.Player(width / 2, height / 2, screen)
There are probably other ways to do this, but this should give you the idea.

Related

Python (Turtle) Remove turtle / arrow

I am trying to make a basic Pong and I don't understand why the arrow/turtle in the middle of the screen displays.
The paddle on the screen consists of 6x turtle objects stored in self.paddle.
I know the problem has something to do with the p = Paddle() but I don't understand where the arrow-object is since it seems to not be in the list mentioned above (print(self.paddle)).
Can someone enlighten me?
from turtle import Turtle, Screen
screen = Screen()
screen.setup(width=1000, height=700)
class Paddle(Turtle):
def __init__(self):
super().__init__()
self.paddle = []
self.create_player_paddle()
def create_player_paddle(self):
for pad in range(0, 6):
x = -480
y = 30
p = Turtle(shape="square")
p.turtlesize(0.5)
p.penup()
p.goto(x, y)
self.paddle.append(p)
for part in range(len(self.paddle)):
self.paddle[part].goto(x, y)
y -= 10
p = Paddle()
screen.update()
screen.exitonclick()
I don't understand where the arrow-object is since it seems to not be
in the list mentioned above ... Can someone enlighten me?
Normally, a class like Paddle() either isa Turtle or contains one or more instances of Turtle. But you've done both, it is both a Turtle (the source of your mystery arrow) and it contains a list of turtles. The simple fix is to remove Turtle as its superclass and remove the super().__init__() code:
class Paddle():
def __init__(self):
self.paddle = []
self.create_player_paddle()
Below is my rework of your code addressing the above and other issues I saw:
from turtle import Turtle, Screen
class Paddle():
def __init__(self):
self.segments = []
self.create_player_paddle()
def create_player_paddle(self):
x = -480
y = 30
for _ in range(6):
segment = Turtle(shape='square')
segment.turtlesize(0.5)
segment.penup()
segment.goto(x, y)
self.segments.append(segment)
y -= 10
screen = Screen()
screen.setup(width=1000, height=700)
paddle = Paddle()
screen.exitonclick()
The usual approach to a Pong game in turtle is to make the paddle a subclass of Turtle and then use shapesize() to stretch the turtle to be the rectangle you desire. But this has issues with collision detection. Your approach makes the paddle somewhat more complicated, but might work better collision-detection-wise as you can test each segment.
if you need to hide the Turtle pointer, put Turtle.hideturtle(self) in your constructor init function

How do I make my player pass through an enemy when they collide in pygame?

I am testing out a platformer game. I have a player and an enemy. However, when I hit it, the enemy just acts like a wall that the player can't pass through, even though I didn't tell the program to. I also told the program to print("Hit") when there was a collision, but nothing happens. Does anyone know how to fix this? NOTE: I imported files so this code is not all in the same file.
SLIME_WALK = (52, 125, 50, 28)
class Mob(pygame.sprite.Sprite):
def __init__(self, sprite_sheet_data):
""" Mob constructor. Assumes constructed with user passing in
an array of 5 numbers like what's defined at the top of this
code. """
# Call the parent's constructor
super().__init__()
sprite_sheet = SpriteSheet('enemies_spritesheet.png')
# Grab the image for this platform
self.image = sprite_sheet.get_image(sprite_sheet_data[0],
sprite_sheet_data[1],
sprite_sheet_data[2],
sprite_sheet_data[3])
self.rect = self.image.get_rect()
class Level01(Level):
def __init__(self, player):
# Call the parent constructor
Level.__init__(self, player)
# Array with type of mob, and x, y location of the mob
level_enemies = [[mobs.SLIME_WALK, 500, 300]]
# Go through the array above and add mobs
for mob in level_enemies:
enemy = mobs.Mob(mob[0])
enemy.rect.x = mob[1]
enemy.rect.y = mob[2]
enemy.player = self.player
self.platform_list.add(enemy)
class Player(pygame.sprite.Sprite):
# -- Methods
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
def update(self):
# See if we hit anything
mob_hit_list = pygame.sprite.spritecollide(self,
self.level.enemy_list, False)
if mob_hit_list:
print("Hit")
I guess you could use the webbrowser package to do that
import webbrowser
a_website = "https://www.google.com"
# Open url in a new window of the default browser, if possible
webbrowser.open_new(a_website)
# Open url in a new page (“tab”) of the default browser, if possible
webbrowser.open_new_tab(a_website)
webbrowser.open(a_website, 1) # Equivalent to: webbrowser.open_new(a_website)
webbrowser.open(a_website, 2) # Equivalent to: webbrowser.open_new_tab(a_website)

Trying to shoot a projectile originating from a character in Pygame (with statement for all_sprites)

I am trying to fire a projectile from a character, with that projectile being the mudball image. I have referenced my class MUDBALL to be included in all_sprites. In my first approach I get an error saying: AttributeError: 'TRUMP' object has no attribute 'rect' when I try and include MUDBALL in all_sprites. That approach is in the code below along with the Game class definitions. In the second approach I try and code the all_sprites statement into the shoot method of the of the TRUMP class. I understand I only need one of these 2 pieces of code to work. I have tried to include the code necessary while omitting everything else to minimize confusion.
import pygame as pg
class Game:
def __init__(self):
# initialize game window, etc
pg.init()
pg.mixer.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.running = True
self.font_name = pg.font.match_font(FONT_NAME)
self.load_data()
def load_data(self):
# load high score
self.dir = path.dirname(__file__)
img_dir = path.join(self.dir, 'img')
with open(path.join(self.dir, HS_FILE), 'r') as f:
try:
self.highscore = int(f.read())
except:
self.highscore = 0
# load spritesheet image
self.spritesheet = Spritesheet(path.join(img_dir, SPRITESHEET))
def new(self):
# start a new game
self.score = 0
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.player = Player(self)
#ME: TRUMP added to all sprites!!!
self.trump = TRUMP(self)
self.all_sprites.add(self.trump)
#***First attempt at adding mudball to all_sprites
self.mudball = MUDBALL(TRUMP.rect.centerx, TRUMP.rect.centery)
self.all_sprites.add(self.mudball)
#game class continues...
class TRUMP(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.current_frame2 = 0
self.last_update2 = 0
self.load_images()
#TRUMP class continued etc... Second attempt with shoot def is below.
def shoot(self):
mudball = MUDBALL(self.rect.centerx, self.rect.centery)
Game.all_sprites.add(mudball)
mudballs.add(mudball)
class MUDBALL(pg.sprite.Sprite):
def __init__(self, x, y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load("MUDBALL.png")
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedx = -10
def update(self):
self.rect.x += self.speedx
# kill if moves off screen
if self.rect.centerx < 0:
self.kill
Shoot is a method of the TRUMP class! In second attempt I try and add MUDBALL to all sprites within the shoot definition of the TRUMP class. That doesn't work. again in this approach I take out the code from the previous approach. That attempt, which is just below, returns: NameError: name 'Game' is not defined. I don't know how to proceed.
The problem may be with your line
self.mudball = MUDBALL(self)
MUDBALL's __init__ method requires x, y arguments; instead, you passed it an object and nothing. That would be enough to cause the error you're seeing.
Response to OP update:
That's correct: Game is not defined. That one line is the only place in your posted code where the symbol appears. As you've coded this, you have the attachment backwards: all_sprites is an attribute (variable field) of Game. In other words, all_sprites is attached to Game, not the other way around. The only way to get to all_sprites is through Game ... except that Game isn't defined anywhere.
A friend of mine helped me. He says "TRUMP is your class ... it does not have rect as an attribute only an instance of TRUMP will have rect as an attribute." I don't understand this yet. But want to thank everyone who helped me along.
self.mudball = MUDBALL(self.trump.rect.centerx, self.trump.rect.centery)
self.all_sprites.add(self.mudball)

Does colliderect work with classes, if so then how do I use it?

I am having an issue adding "colliderect" to my game. I have this class for setting values to the objects, and then one for drawing the items to the screen.
**Edited **
Throughout edits I have updated what my code is having issues with. I have yet to make any of the bel
ow solutions work with my class. All answers that have been given are very helpful, but I am still having the same issues. Collierect just doesn't want to work, when I use pygame.sprite.Sprite for collisions I get a sprite begs flashes when it contacts another sprite.rect, but the rect's movement doesn't reverse.
Help on either of these issues would be greatly appreciated, but until I get things solved I will leave the question open.
Error I was getting
if paddle1.Render().colliderect(ball):
AttributeError: 'NoneType' object has no attribute 'colliderect'
Error I am getting now
if paddle1.Render().colliderect(ball):
AttributeError: 'Object' object has no attribute 'colliderect'
I don't know how to fix this issue.
Here's my code for the ball
#Create a class named sprite to use for the paddles and the ball.
class Object():
def __init__(self,x,y,width,height,color):
# Set X value
self.x = x
# Set Y value
self.y = y
# Set Width of object
self.width = width
# Set Height of object
self.height = height
# Set the (default) color of the object
self.color = (255,255,255)
#attirbute for drawing the sprite(s) to the screen
def Render(self):
pygame.draw.rect(screen,self.color,(self.x,self.y,self.width,self.height))
return self
#Create the sprites
paddle1 = Object(50,175,25,150,color[0])
paddle2 = Object(650,175,25,150,color[1])
ball = Object(300,250,25,25, color[2])
This is the code for colliderect
#Commands for ball collision
if paddle1.Render().colliderect(ball):
if True:
pass #do something
if paddle2.Render().colliderect(ball):
if True:
pass #do something
# --- Drawing code should go here
Aside from the if statement, what am I missing for colliderect to work?
Your method render() that your wrote does not return any value (Ok, it returns None.)
Were you intending to return the rect at the end of that method?
def render(self):
self.rect = pygame.draw.rect(screen,self.color,(self.x,self.y,self.width,self.height))
return self.rect
Then you could chain your method calls like you want:
paddle2.render().colliderect(ball):
...although you probably need to calculate and set the ball's rect and pass it:
paddle2.render().colliderect(ball.rect):
I haven't used pygame so I'm helping a little blindly, but I think you have some confusion over classes. Your sprite should be derived from pygame.sprites.Sprite. Then you specialize your sprite to be the kind of sprite you want it to be, but pygame's sprite implements most of the underlying logic.
Here's an example sprite implementation: http://www.101computing.net/creating-sprites-using-pygame/
Something like that might work. To use colliderect Pygame expects a Rect(x,y,width,height) or Rect((x,y),(width,height)).
So basically you want: myrectangle1.colliderect(myrectangle2)
Similarly to use collidepoint you need: myrectangle1.collidepoint(mypoint1)
#Create a class named sprite to use for the paddles and the ball.
class Object():
def __init__(self,x,y,width,height,color):
# Set X value
self.x = x
# Set Y value
self.y = y
# Set Width of object
self.width = width
# Set Height of object
self.height = height
# Create Rect of object=
self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
# Set the (default) color of the object
self.color = (255,255,255)
#attirbute for drawing the sprite(s) to the screen
def Render(self):
pygame.draw.rect(screen,self.color, self.rect)
return self
#Create the sprites
paddle1 = Object(50,175,25,150,color[0])
paddle2 = Object(650,175,25,150,color[1])
ball = Object(300,250,25,25, color[2])
alternatively if you don't want to have a self.rect attribute then you need:
def Render(self):
pygame.draw.rect(screen,self.color,pygame.Rect(self.x,self.y,self.width,self.height))
return self
This Render method however will just draw your rectangle.
If you want it to return the rectangle attribute of your object to test for collision then you need:
def Render(self):
pygame.draw.rect(screen,self.color, self.rect) #draws the rect
return self.rect #returns the rect
To check for collision you would need:
if paddle1.rect.colliderect(ball.rect): #this is why I think having a rect attribute is cleaner than calling pygame.Rect(x,y,w,h) all the time.
or:
if paddle1.pygame.Rect(self.x, self.y, self.width, self.height).colliderect(ball.pygame.Rect(self.x, self.y, self.width, self.height)):

Pygame convert surface into sprite

I want to make a game with cards, something like Heartstone, but a lot simplier (coz I am not pro programmer). This is just part of program
import pygame
class Card:
def AdCard(self, AdCard):
self.AdCard = AdCard
def HpCard(self, HpCard):
self.HpCard = HpCard
def Picture(self, Picture):
self.Picture = Picture
def Special(self, Special):
if Special == "Heal":
pass
pygame.init()
display = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
swordsman = Card()
swordsman_picture = pygame.image.load("Swordsman.png").convert()
swordsman.Picture(swordsman_picture)
print(type(swordsman.Picture))
Now the problem is that it prints that type of Picture is class 'pygame.Surface' but I want for this picture to be sprite.
How to do that.
Tnx.
Sprite is a class which use Surface to keep image and Rect to keep positon and size.
class Card(pygame.sprite.Sprite):
def __init__(self, surface):
pygame.sprite.Sprite.__init__(self)
self.image = surface
self.rect = self.image.get_rect() # size and position
# and then
one_card = Card(swordsman_picture)
(see Pygame documentation: pygame.sprite.Sprite )
or probably but I did't see this before
one_card = pygame.sprite.Sprite()
one_card.image = swordsman_picture
one_card.rect = one_card.image.get_rect() # size and position
BTW: Use "CamelCase" names only for classes names - to make code more readable - even StackOveflor editor treads Picture, AdCard, etc as class name and use blue color. For functions and variables use lower_case names.
This seems useless
def Picture(self, Picture):
self.Picture = Picture
swordsman.Picture(swordsman_picture)
you can do the same in one line - and make it more readable.
swordsman.Picture = swordsman_picture

Categories