How can i add collision rectangles on click? (python & pygame) - python

Can anyone show me how to add a rectangle wherever i click somewhere and make the ball bounce off it for example when i click somewhere on the screen it place a rectangle at that coordinate and when they make the ball collide with it the ball will bounce.
Also my is their a way of making it HD?
import sys
import pygame
import os
image_file = os.path.expanduser("ball.png")
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
gravity = +1
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.speed = [0, 0]
area = pygame.display.get_surface().get_rect()
self.width, self.height = area.width, area.height
def update(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > self.height:
self.speed[1] = -self.speed[1]
self.rect.left = clip(self.rect.left, 0, self.width)
self.rect.right = clip(self.rect.right, 0, self.width)
self.rect.top = clip(self.rect.top, 0, self.height)
self.rect.bottom = clip(self.rect.bottom, 0, self.height)
def clip(val, minval, maxval):
return min(max(val, minval), maxval)
class Main(object):
def __init__(self):
self.setup()
def setup(self):
pygame.init()
size = (self.width, self.height) = (640,360)
self.screen = pygame.display.set_mode((1000,1000),pygame.FULLSCREEN)
self.ball = Ball()
self.setup_background()
def setup_background(self):
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill((0, 0, 0))
self.screen.blit(self.background, (0, 0))
pygame.display.flip()
def draw(self):
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.ball.image, self.ball.rect)
pygame.display.flip()
def event_loop(self):
ball = self.ball
friction = 1
while True:
for event in pygame.event.get():
if ((event.type == pygame.QUIT) or
(event.type == pygame.KEYDOWN and
event.key == pygame.K_ESCAPE)):
sys.exit()
elif event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
friction = 1
elif event.type == pygame.KEYUP:
friction = 0.99
ball.speed = [friction*s for s in ball.speed]
ball.speed[1] += gravity
ball.update()
self.draw()
pygame.time.delay(10)
if __name__ == '__main__':
app = Main()
app.event_loop()

You have to be able to perform several steps:
Detect when the mouse changes from unpressed to pressed state.
If that happens:
Add a Rectange object to a list (consists of x, y, width and height).
In each loop:
For each Rectange in the list: Check if it intersects with the ball. You can model the ball as a rectangle, which is a little bit easier, or as a circle. A circle touches a rectangle if a corner of the rectange is inside of it or if the center is below/above/beneath the rectangle and less than its radius away. (It's easier to explain with a picture.)
If the ball collides: Change the speed as if it had touched the corner of the screen.
Do you have further problems regarding one of these steps?
"Also my is their a way of making it HD?"
I was able to change the resolution to 1600 x 900. Each graphics card only supports a certain set of fullscreen resolutions and 1000x1000 didn't work for mine.

Related

Bullet not moving upwards because of OOP

I have a simple shooter game using pygame. I'm having some problems making the bullet's y coordinate slowly increasing up. I know this is something to do with the way I've programmed the Player class even though a bullet Rect is in it. I think I have to change the update function inside it. This is my code:
import pygame, random, sys, time
pygame.init()
#Constants
WIDTH = 800
HEIGHT = 500
BLACK = (0, 0, 0)
WHITE = (255, 255, 255) # Background Colour
RED = (255, 0, 0)
GREEN = (0, 255, 0)
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pygame Shooter Game")
clock = pygame.time.Clock()
fps = 60
run = True
class Player():
def __init__(self, width, colour, x, y):
self.width = width
self.colour = colour
self.x = x
self.y = y
self.vel = 5
self.shoot = False
self.player = pygame.Rect(self.x, self.y, self.width, self.width)
self.cartridge = pygame.Rect(0, 0, self.width/2, self.width/2)
self.bullet = pygame.Rect(0, 0, 10, 20)
self.shoot = False
def draw(self, win):
self.win = win
pygame.draw.rect(self.win, self.colour, self.player) # Draw player(rect)
pygame.draw.rect(self.win, GREEN, self.cartridge) #Draw cartridge
if self.shoot:
pygame.draw.rect(self.win, BLACK, self.bullet)
def move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.x > 0: self.x -= self.vel # We don't do elif cuz we want them to be able to move diagonally
if keys[pygame.K_RIGHT] and self.x < WIDTH-self.width: self.x += self.vel
if keys[pygame.K_UP] and self.y > 0: self.y -= self.vel
if keys[pygame.K_DOWN] and self.y < HEIGHT-self.width: self.y += self.vel
if keys[pygame.K_SPACE]:
self.shoot = True
def update(self):
self.player = pygame.Rect(self.x, self.y, self.width, self.width)
self.cartridge.midbottom = self.player.midtop
self.bullet.midbottom = self.cartridge.midtop
if self.shoot:
while self.bullet.y > 0:
self.bullet.y -= 1
def main(win):
run = True
player = Player(50, RED, WIDTH/2, HEIGHT/2)
while run:
win.fill(WHITE)
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
player.move()
player.update()
player.draw(win)
pygame.display.update()
pygame.quit()
sys.exit()
main(window)
Also, how can I make the create classes for each individual Cartridge and Bullet, to make the whole code more efficient?
update is invoked continuously in the main application loop. Therefore, no additional animation loops are required for the update. Change the loop to a selection (change while to if):
while self.bullet.y > 0:
if self.bullet.y > 0:
self.bullet.y -= 1
The starting position of the bullet must be set when the bullet is shot, rather than continuously when the bullet is updated:
class Player():
# [...]
def move(self):
# [...]
if keys[pygame.K_SPACE]:
self.shoot = True
self.bullet.midbottom = self.cartridge.midtop # <--- INSERT
def update(self):
self.player = pygame.Rect(self.x, self.y, self.width, self.width)
self.cartridge.midbottom = self.player.midtop
# self.bullet.midbottom = self.cartridge.midtop <--- DELETE
if self.shoot:
if self.bullet.y > 0: # <--- if (not while)
self.bullet.y -= 1
See also:
How can i shoot a bullet with space bar?
How do I stop more than 1 bullet firing at once?

How do I break up my PyGame game into separate files?

I am teaching a fortnightly coding class to a group of dozen super bright young enthusiasts. We have already covered OOP and created a text based adventure using OOP.
Now I am planning to teach PyGame and continue using objects, and I am wondering if games could be built in such a way where the code for each object is in a separate file?, this would be really neat and easier to build on.
Well For the code below I tried making separate files for each object. This was only partially successful because the draw method never works quite well, I believe the issue that I cannot have separate files referencing the same pygame screen.
import pygame
import random
import time
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0,0,255)
SCREEN_WIDTH = 700
SCREEN_HEIGHT = 500
pygame.init()
class Paddle:
'''Class to keep players location'''
def __init__(self,x=350, y=480, width =70,height=20):
self.x = x
self.y = y
self.change_x = 0
self.change_y = 0
self.width = width
self.height = height
self.score = 0
def move(self):
self.x += self.change_x
self.y += self.change_y
def draw(self):
pygame.draw.rect(screen, BLUE, [self.x,self.y, self.width, self.height])
def check_collision(self,ball):
if ball.y>460:
if abs(35+ self.x - ball.x) < 30:
self.score += 1
ball.draw(BLUE)
ball.y = 0
ball.x = random.randint(0,650)
ball.change_y = random.randint(2,3+int(self.score/5))
class Ball:
"""Class to keep track of a ball's location and vector."""
def __init__(self,x=350,y=250,size=25):
self.x = x
self.y = y
self.change_x = 0
self.change_y = 0
self.size = size
def move(self):
self.x += self.change_x
self.y += self.change_y
def draw(self,colour = WHITE):
pygame.draw.circle(screen,WHITE, [self.x, self.y], self.size)
# Set the height and width of the screen
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Bouncing Balls")
done = False
clock = pygame.time.Clock()
screen.fill(BLACK)
ball = Ball()
player = Paddle()
ball.change_y = 2
ball.draw()
while not done:
screen.fill(BLACK)
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
pass
if event.key == pygame.K_LEFT:
player.change_x = -5
if event.key == pygame.K_RIGHT:
player.change_x = 5
else:
ball.change_x = 0
player.change_x = 0
if ball.y > 500:
print('YOUR SCORE: ',player.score)
time.sleep(2)
pygame.quit()
#move ball and player and check if they collide
ball.move()
player.move()
player.check_collision(ball)
#draw ball and player
ball.draw()
player.draw()
#render frame
clock.tick(60)
pygame.display.flip()
# Print score and exit
print('YOUR SCORE: ',player.score)
pygame.quit()
When I had separate files this is the error that I got in relation to screen
line 20, in draw
pygame.draw.circle(screen,WHITE, [self.x, self.y], self.size)
NameError: name 'screen' is not defined
Add a surface argument to the draw() methods of the classes Paddle and Ball and draw the object on the surface which is passed to the method:
class Paddle:
# [...]
def draw(self, surface):
pygame.draw.rect(surface, BLUE, [self.x,self.y, self.width, self.height])
class Ball:
# [...]
def draw(self, surface, colour = WHITE):
pygame.draw.circle(surface, colour, [self.x, self.y], self.size)
Now you can draw the objects on any pygame.Surface you want, e.g. screen:
ball.draw(screen)
player.draw(screen)

Pygame Transformation: rotate shape on key press

I'm fairly new to Pygame, and can't seem to find a solid answer on this. I have a shape, specifically an ellipse, that I want to rotate both left and right. The key bind would be a and d, as the arrow keys are already binded to move left and right on an x,y axis.
I know that it involves pygame.transform.rotate, however I can't seem to implement this right.
def main():
#Setup basic variables and methods for pygame
pygame.init()
windowWidth = 800
windowHeight = 700
fps = 45
clock = pygame.time.Clock()
gameWindow = pygame.display.set_mode((windowWidth, windowHeight))
surface = pygame.Surface((50,50))
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
shipX = windowWidth/2
shipY = windowWidth/2
shipSpeed = 4
while(True):
pygame.draw.ellipse(gameWindow, WHITE, (shipX, shipY, 20, 30))
#Monitor the FPS of the game
clock.tick(fps)
for event in pygame.event.get():
# ________________________________________
if event.type == pygame.QUIT:
gameExit()
rotate = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP] and shipY > shipSpeed: shipY -= shipSpeed
if pressed[pygame.K_DOWN] and shipY < windowHeight - shipSpeed - 20: shipY += shipSpeed
if pressed[pygame.K_LEFT] and shipX > shipSpeed:shipX -= shipSpeed
if pressed[pygame.K_RIGHT] and shipX < windowWidth - shipSpeed - 20: shipX += shipSpeed
if pressed[ord('a')]: rotate = pygame.transform.rotate(surface, -20)
if pressed[ord('d')]: rotate = pygame.transform.rotate(surface, 20)
gameWindow.fill(BLACK)
# 'flip' display - always after drawing...
pygame.display.flip()
The expected result is that the shape will change it's angle, and then move accordingly.
Again, I'm very new to pygame, so any detailed help would be appreciated.
Your problem is that you draw the ellipse directly on the screen, but you should draw your ellipse on another Surface.
Then you can rotate that new Surface with pygame.transform.rotate.
Here's a simple example:
import pygame
import random
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
surface = pygame.Surface((100, 200))
surface.set_colorkey((2, 3, 4))
surface.fill((2, 3, 4))
rect = surface.get_rect(center=(100, 100))
pygame.draw.ellipse(surface, pygame.Color('white'), (0, 0, 100, 200))
angle = 0
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]: rect.move_ip(0, -5)
if pressed[pygame.K_DOWN]: rect.move_ip(0, 5)
if pressed[pygame.K_LEFT]: rect.move_ip(-5, 0)
if pressed[pygame.K_RIGHT]: rect.move_ip(5, 0)
if pressed[pygame.K_a]: angle += 1
if pressed[pygame.K_d]: angle -= 1
rotated = pygame.transform.rotate(surface, angle)
rect = rotated.get_rect(center=rect.center)
rect.clamp_ip(screen_rect)
screen.fill(pygame.Color('dodgerblue'))
screen.blit(rotated, rect.topleft)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Note that I use a Rect to store the position of the object because it's easy then to rotate the Surface around its center (by setting its center attribute), and to ensure the Surface does not go outside the screen (by using clamp_ip).
Also, it's important to always rotate the source Surface, and not the already rotated Surface. Otherwise, you'll get distortions.
Note that we have three things here: an image, a position, and some behaviour logic. Whenever you see these things together, consider putting them together into a class. Pygame already offers a nice class for this, called Sprite.
Here's the same example, but Sprite-based:
import pygame
import random
class Thingy(pygame.sprite.Sprite):
def __init__(self, area):
super().__init__()
# image is what get's painted on the screen
self.image = pygame.Surface((100, 200))
self.image.set_colorkey((2, 3, 4))
self.image.fill((2, 3, 4))
pygame.draw.ellipse(self.image, pygame.Color('white'), (0, 0, 100, 200))
# we keep a reference to the original image
# since we use that for rotation to prevent distortions
self.original = self.image.copy()
# rect is used to determine the position of a sprite on the screen
# the Rect class also offers a lot of useful functions
self.rect = self.image.get_rect(center=(100, 100))
self.angle = 0
self.area = area
def update(self, events, dt):
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]: self.rect.move_ip(0, -5)
if pressed[pygame.K_DOWN]: self.rect.move_ip(0, 5)
if pressed[pygame.K_LEFT]: self.rect.move_ip(-5, 0)
if pressed[pygame.K_RIGHT]: self.rect.move_ip(5, 0)
if pressed[pygame.K_a]: self.angle += 1
if pressed[pygame.K_d]: self.angle -= 1
# let's rotate the image, but ensure that we keep the center position
# so it doesn't "jump around"
self.image = pygame.transform.rotate(self.original, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
self.rect.clamp_ip(self.area)
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
sprites = pygame.sprite.Group(Thingy(screen_rect))
dt = 0
while True:
# nice clean main loop
# all game logic goes into the sprites
# handle "global" events
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
# update all sprites
sprites.update(events, dt)
# draw everything
screen.fill(pygame.Color('dodgerblue'))
sprites.draw(screen)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
You should make a class for your object:
class myRect(pygame.Surface):
def __init__(self, parent, xpos, ypos, width, height):
super(myRect, self).__init__(width, height)
self.xpos = xpos
self.ypos = ypos
self.parent = parent
def update(self, parent):
parent.blit(self, (self.xpos, self.ypos))
def rotate(self, angle):
#(your rotation code goes here)

Spawn moving objects from border

I'm trying to make objects from the Angryball class randomly spawn from the right border and move from right to left outside the screen. The idea is that these ogjects must spawn on random y coordinates from the window's right border (better if i can make them seem like if they were coming from outside the border, but that's another point i will check later) and then move at speed 5 until the reach the opposite border. Once one object gets out, another one is spawned again.
I don't have any error when i run this but it doesn't work as expected :) Can you help me figuring this out?
import pygame
import os
import random
size = width, height = 750, 422
screen = pygame.display.set_mode(size)
img_path = os.path.join(os.getcwd())
background_image = pygame.image.load('background.jpg').convert()
bg_image_rect = background_image.get_rect()
pygame.mixer.pre_init(44100, 16, 2, 4096)
pygame.display.set_caption("BallGame")
class Ball(object):
def __init__(self):
self.image = pygame.image.load("ball.png")
self.image_rect = self.image.get_rect()
self.image_rect.x
self.image_rect.y
self.facing = 'LEFT'
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_DOWN] and self.image_rect.y < 321:
self.facing = 'DOWN'
self.image_rect.y += dist
elif key[pygame.K_UP] and self.image_rect.y > 0:
self.facing = 'UP'
self.image_rect.y -= dist
if key[pygame.K_RIGHT] and self.image_rect.x < 649:
self.facing = 'RIGHT'
self.image_rect.x += dist
elif key[pygame.K_LEFT] and self.image_rect.x > 0:
self.facing = 'LEFT'
self.image_rect.x -= dist
def draw(self, surface):
if self.facing == "RIGHT":
surface.blit(pygame.transform.flip(self.image, True, False),(self.image_rect.x,self.image_rect.y))
elif self.facing == "DOWN":
surface.blit(pygame.image.load("ball_down.png"),(self.image_rect.x,self.image_rect.y))
if self.facing == "UP":
surface.blit(pygame.image.load("ball_up.png"),(self.image_rect.x,self.image_rect.y))
elif self.facing == "LEFT":
surface.blit(self.image,(self.image_rect.x,self.image_rect.y))
mob_images = [pygame.image.load("image1.png").convert_alpha(),pygame.image.load("image2.png").convert_alpha(),pygame.image.load("image3.png").convert_alpha(),pygame.image.load("image4.png").convert_alpha(),pygame.image.load("image5.png").convert_alpha()]
mob_image = random.choice(mob_images)
class Angryball(pygame.sprite.Sprite):
def __init__(self, image, pos_x, pos_y):
super(Angryball, self).__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.x = pos_x
self.rect.y = pos_y
self.facing = 'LEFT'
def update(self, screen):
if self.rect.x <= 0:
self.rect.right = screen.get_rect().width
self.rect.top = random.randint(0, screen.get_rect().height)
else:
self.rect.move_ip(-5, 0)
pygame.init()
screen = pygame.display.set_mode((750, 422))
ball = Ball()
angryball = Angryball(mob_image, 700, random.randrange(400))
sprites = pygame.sprite.Group()
sprites.add(angryball)
clock = pygame.time.Clock()
pygame.mixer.music.load("bg_music.mp3")
pygame.mixer.music.play(-1, 0.0)
running = True
while running:
esc_key = pygame.key.get_pressed()
for event in pygame.event.get():
if esc_key[pygame.K_ESCAPE]:
pygame.display.quit()
pygame.quit()
running = False
ball.handle_keys()
sprites.update(screen)
sprites.draw(screen)
screen.blit(background_image, bg_image_rect)
screen.blit(background_image, bg_image_rect.move(bg_image_rect.width, 0))
bg_image_rect.move_ip(-2, 0)
if bg_image_rect.right <= 0:
bg_image_rect.x = 0
ball.draw(screen)
pygame.display.update()
clock.tick(60)
Use pygame's Sprites.
Let Angryball inherit from pygame.sprite.Sprite and rename image_rect to rect.
Give it an update function like this:
def update(self, screen):
if self.rect.x <= 0:
self.rect.right = screen.get_rect().width
self.rect.top = random.randint(0, screen.get_rect().height)
else:
self.rect.move_ip(-5, 0)
Instead of manually blitting it to the screen, use a Group:
angryball = Angryball(mob_image, 700, random.randrange(400))
sprites = pygame.sprite.Group()
sprites.add(angryball)
and in your main loop simply call
sprites.update(screen)
sprites.draw(screen)
Your code does not work because you try to use angryball_image_rect in the line before you actually declare it.

How do I turn my pygame into a side scroller?

I'm building a game with pygame and I have a problem with this game, I don't know how to make the background and the platform scroll slowly to the right as my player sprite moves to the right.
I want the scrolling to happen in def shift_world().
Maybe someone can teach me how to add an image to the background as well. It would be great if you can utilize the libraries I am using. I have three files: The first for the game, the second for sprites, and the third for the settings.
main.py:
# Sarada's Blaze! - side scrolling platform shooting game
import pygame as pg
import random
import os
from settings import *
from sprites import *
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)
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)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
self.run()
def run(self):
# Game loop
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self):
# Game Loop - Update
self.all_sprites.update()
self.platforms.update()
# check if player hits a platform - only if falling
if self.player.vel.y 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
def events(self):
# Game Loop - events
for event in pg.event.get():
# check for closing window
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
self.player.jump()
def draw(self):
# Game Loop - draw
self.screen.fill(bgcolor) # I want to change the background color to an image
self.all_sprites.draw(self.screen)
self.draw_text(str(self.score), 22, white, WIDTH / 2, 15)
# after drawing everything, flip the display
pg.display.flip()
# How far the world has been scrolled right
# This is where i want the side scroller stuff to happen
def shift_world(self):
# when the user moves left/right, i want to scroll everything
self.world_shift += shift_x
# i want all my platforms and the background to scroll when
# my player sprite moves closer to the right
for plat in PLATFORM_LIST:
self.platform.rect.x += shift_x
if self.pos.x = 500:
diff = self.pos.x - 500
self.pos.x = 500
self.shift_world(- diff)
if self.pos.x <= 120:
diff = 120 - self.pos.x
self.pos.x -= 120
self.shift_world(diff)
def show_start_screen(self):
# game splash/start screen
self.screen.fill(greenblue)
self.draw_text(TITLE, 48, red, WIDTH / 2, HEIGHT / 4)
self.draw_text("left arrow to move left, right arrow to move right, up arrow to jump", 22, blue, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press any key to begin", 22, green, WIDTH / 2, HEIGHT * 3 /4)
pg.display.flip()
self.wait_for_key()
def show_go_screen(self):
# game over/continue
if not self.running:
return
self.screen.fill(greenblue)
self.draw_text("Game Over!", 48, red, WIDTH / 2, HEIGHT / 4)
self.draw_text("Score: " + str(self.score), 22, blue, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press any key to continue", 22, green, WIDTH / 2, HEIGHT * 3 /4)
pg.display.flip()
self.wait_for_key()
def wait_for_key(self):
waiting = True
while waiting:
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
waiting = False
self.running = False
if event.type == pg.KEYUP:
waiting = False
def draw_text(self, text, size, color, x, y):
font = pg.font.Font(self.font_name, size)
text_surface = font.render(text, True, color)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
self.screen.blit(text_surface, text_rect)
g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen()
pg.quit()
sprites.py:
# Sprite classes for platform shooting game
import pygame as pg
import random
from settings import *
import os
vec = pg.math.Vector2
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, "img")
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.image = pg.image.load(os.path.join(img_folder, "sarada_shooting.png")).convert()
self.image.set_colorkey(black)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel =vec(0, 0)
self.acc = vec(0, 0)
def jump(self):
# jump only if standing on a platform
self.rect.x += 1
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.x -= 1
if hits:
self.vel.y = -PLAYER_JUMP
def update(self):
self.acc = vec(0, PLAYER_GRAV)
keys = pg.key.get_pressed()
if keys[pg.K_LEFT]:
self.acc.x = -PLAYER_ACC
if keys[pg.K_RIGHT]:
self.acc.x = PLAYER_ACC
# apply friction
self.acc.x += self.vel.x * PLAYER_FRICTION
# equations of motion
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# wrap around the sides of the screen
if self.pos.x WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
self.rect.midbottom = self.pos
# i want the platform graphic changed
class Platform(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((WIDTH, h))
self.image.fill(greenblue)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
settings.py:
# game options/settings
TITLE = "Sarada's Blaze"
WIDTH = 800
HEIGHT = 600
FPS = 60
FONT_NAME = 'times new roman'
# Player properties
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.20
PLAYER_GRAV = 0.8
PLAYER_JUMP = 20
# starting platforms
PLATFORM_LIST = [(0, HEIGHT - 40, WIDTH, 40)]
# define colors
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
purple = (255, 0, 255) # ENEMY COLOR
greenblue = (0, 155, 155)
bgcolor = red
You need to iterate over the self.platforms list and subtract the player's vel.x from the x-position of every sprite in order to move them. Also, give your platform sprites a vector attribute for the position self.pos = vec(x, y) because the coordinates of pygame.Rects are truncated and turned into ints, so the movement will be inaccurate if your velocity vectors consist of floats.
def update(self):
# ...
self.shift_world()
def shift_world(self):
# Scroll when the player sprite moves closer to the right.
if self.player.pos.x >= 500:
self.player.pos.x = 500 # Stop at 500.
self.shift_platforms()
# Scroll when the player sprite moves closer to the left.
if self.player.pos.x <= 120:
self.player.pos.x = 120 # Stop at 120.
self.shift_platforms()
def shift_platforms(self):
for plat in self.platforms: # Iterate over the platform sprites.
plat.pos.x -= self.player.vel.x # Update the platform's pos vector.
plat.rect.x = plat.pos.x # Update the rect.
self.world_shift -= self.player.vel.x # For the background.
As for the background surface, you can use the self.world_shift attribute and subtract the self.player.vel.x in the shift_platforms method as well. Then blit it twice in the draw method and use the modulo operator to blit it at the correct x-positions.
I assume the background has the size of the screen and should be repeated.
# Load it once in the global scope.
BACKGROUND = pg.image.load(os.path.join(img_folder, "background.png")).convert()
def draw(self):
x = self.world_shift
self.screen.blit(BACKGROUND, (x % WIDTH, 0))
self.screen.blit(BACKGROUND, (x % WIDTH - WIDTH - 1, 0))

Categories