Can anyone help me out, I can't figure out why the code does not run. I feel like it is a really stupid mistake any maybe a better, second pair of eyes could lend me a hand?
It's saying my post is mainly code so I need to add some 'description', so you don't have to read this, I'm just doing this so it will let me post it.
#my pong game
import pygame, sys
pygame.init()
#global variables
screen_width = 1000
screen_height = 800
game_over = False
ball_speed_x = 15
ball_speed_y = 15
ball_width = 15
ball_height = 15
ball_color = (255,0,0)
ball_posx = int(screen_width/2 - (ball_width / 2))
ball_posy = int(screen_height/2 - (ball_width / 2))
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('PONG')
#player blueprint
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.height = 100
self.width = 20
self.vel = 15
self.color = (255,0,0)
self.player = pygame.Rect(self.x, self.y, self.width, self.height)
def draw(self):
pygame.draw.rect(screen, self.color, self.player)
#creating objects
player1 = Player(10, int(screen_height/2 - 5))
player2 = Player(screen_width - 30, int(screen_height/2 - 5))
ball = pygame.Rect(ball_posx, ball_posy, ball_height, ball_width)
def player_animation():
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
player2.y -= player2.vel
if keys[pygame.K_DOWN]:
player2.y += player2.vel
if keys[pygame.K_w]:
player1.y -= player1.vel
if keys[pygame.K_s]:
player1.y += player1.vel
def ball_animation():
global ball_posx, ball_width, ball_height, ball_posy, ball_posx, ball_speed_x, ball_speed_y, screen_width, screen_height
if ball.right >= screen_width - 5:
ball_speed_x *= -1
if ball.left <= 10:
ball_speed_x *= -1
if ball.bottom >= screen_height - 5:
ball_speed_y *= -1
if ball.top <= 5:
ball_speed_y *= -1
if player1.player.colliderect(ball):
ball_speed_x *= -1
if player2.player.colliderect(ball):
ball_speed_x *= -1
ball_posx += ball_speed_x
ball_posy += ball_speed_y
while not game_over:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_over = True
screen.fill((0,0,0))
ball_animation()
player_animation()
pygame.draw.ellipse(screen, (255,0,0), ball)
player1.draw()
player2.draw()
pygame.display.update()
pygame.quit()
sys.exit()
Everything in your code works fine apart from the draw functions. In the player class, you create the player's rectangle at the beginning and it's x and y values aren't changed throughout the game, you're simply changing the variable that was used to create the rectangle when instead you should be changing the rectangle's actual x and y variables. This can be fixed by adding these two lines in the player class:
def draw(self):
self.player.y = self.y
self.player.x = self.x
pygame.draw.rect(screen, self.color, self.player)
The self.player.y will update the rectangle's y value to the player's current value so the rectangle is drawn in the right place.
The ball has the same problem, the eclipse is created once but it's x and y values are never changed.
Instead of writing:
ball_posx += ball_speed_x
ball_posy += ball_speed_y
Do :
ball.x += ball_speed_x
ball.y += ball_speed_y
which directly access the eclipses x and y values so it can be redrawn in the right place. I made the changes stated here and everything started moving fine.
Related
so I am making a game where this character(a circle) has to pop balloons falling from the sky to get points. But I am having trouble making my character move in the first place.
Code:
import pygame
from pygame.locals import *
pygame.init()
#Variables
white = (255, 255, 255)
blue = (70,130,180)
black = (0,0,0)
x = 400
y = 450
#screen stuff
screenwidth = 800
screenheight = 600
screen = pygame.display.set_mode((screenwidth, screenheight))
pygame.display.set_caption("Balloon Game!")
#end of screen stuff
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
# Draw Character
character = pygame.draw.circle(screen, (blue), (x, y), 50, 50)
#End of Drawing Character
# Making Arrow Keys
keyPressed = pygame.key.get_pressed()
if keyPressed[pygame.K_LEFT]:
character.x -= 1
if keyPressed[pygame.K_RIGHT]:
character.x += 1
pygame.display.update()
I would appreciate it if someone could tell me why it wasn't
working with a fixed code. Thanks!
pygame.draw.circle returns the bounding rectangle (pygame.Rect object) of the circle. However the center of the circle is always (x, y). Therefore you need to change x instead of character.x:
while True:
# [...]
pygame.draw.circle(screen, (blue), (x, y), 50, 50)
keyPressed = pygame.key.get_pressed()
if keyPressed[pygame.K_LEFT]:
x -= 1
if keyPressed[pygame.K_RIGHT]:
x += 1
This code can even be simplified:
while True:
# [...]
pygame.draw.circle(screen, (blue), (x, y), 50, 50)
keyPressed = pygame.key.get_pressed()
x += (keyPressed[pygame.K_RIGHT] - keyPressed[pygame.K_LEFT])
This question already has an answer here:
How to fix character constantly accelerating in both directions after deceleration Pygame?
(1 answer)
Closed 1 year ago.
for my platformer game i want to make it so when it moves it slowly slows down. i tried to do it a couple ways but it didn't work it just stays on spot. can someone help me?
class Player(pygame.sprite.Sprite):
def __init__(self, game):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.speedx = 0
self.speedy = 0
self.alive = True
self.image = pygame.Surface((30, 40))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = HEIGHT - 10
self.rect.centerx = WIDTH / 2
def update(self):
self.speedx = 0
self.speedy = 0
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_a]:
self.speedx = -10
elif key_pressed[pygame.K_d]:
self.speedx = 10
if key_pressed[pygame.K_SPACE]:
self.speedy = -10
self.speedx += self.rect.x
self.speedx *= FRICTION
self.speedy += self.rect.y
if self.rect.left < 0:
self.rect.x = 0
elif self.rect.right > WIDTH:
self.rect.right = WIDTH
Personally, I prefer calculating friction like that:
If a key is pressed, I add, let's say 3000 pixels per second to the speed
Every frame, I slow down the sprite by multiplying the speed by a number very close to 1, like for example 0.95 when the game runs at 100 frames per second.
By using this technique, if you make the sprite move, it will accelerate more and more quickly. And if you stop moving it, it will slow down smoothly. Also, if you make the sprite move to the left while it is still moving right, it will "turn around" quicker.
You can mess aorund with the values: if you increase the first number, the speed will increase. If the second number is closer to 1, the friction is less significant.
Here is how to code this, when running at 100 frames per second:
The x_speed variable is in pixels per second. Simply divide it by 100 to get it in pixels per frame.
# in the game loop
pressed = pygame.key.get_pressed()
if pressed[K_RIGHT]:
x_speed += 30
if pressed[K_LEFT]:
x_speed -= 30
x_speed *= 0.95
Here is how to use it, in order to run the game at any framerate (you just need a variable, time_passed, which corresponds to the time spent on a frame, in seconds: you can use pygame.time.Clock().
The x_speed variable is in pixels per second.
# in the game loop
pressed = pygame.key.get_pressed()
if pressed[K_RIGHT]:
x_speed += 3000 * time_passed
if pressed[K_LEFT]:
x_speed -= 3000 * time_passed
x_speed *= 0.95**(100 * time_passed)
Runnable Minimal, Reproducible Example:
This question already has answers here:
Why is my PyGame application not running at all?
(2 answers)
Closed 1 year ago.
I am currently programming a pygame game where you move a spaceship around the screen. Currently, I have got to the part where I made the spaceship and tried to make it move. However, when I try to move the spaceship around, the spaceship doesn't move!
Here is my current code:
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 500))
screen.fill((255, 255, 255))
class Spaceship(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("C:/eqodqfe/spaceship.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.rect.center = (self.x, self.y)
spaceship = Spaceship(screen, 400, 400)
screen.blit(spaceship.image, spaceship.rect)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
key = pygame.key.get_pressed()
if key[pygame.K_a]:
spaceship.x -= 5
elif key[pygame.K_d]:
spaceship.x += 5
elif key[pygame.K_w]:
spaceship.y += 5
elif key[pygame.K_s]:
spaceship.y -= 5
spaceship.update()
pygame.display.update()
What is wrong with my current code?
You have to draw the Sprties in the application loop:
clock = pygame.time.Clock()
running = True
while running:
# handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
key = pygame.key.get_pressed()
if key[pygame.K_a]:
spaceship.x -= 5
elif key[pygame.K_d]:
spaceship.x += 5
elif key[pygame.K_w]:
spaceship.y -= 5
elif key[pygame.K_s]:
spaceship.y += 5
# update the position of the object
spaceship.update()
# clear the display
screen.fill((255, 255, 255))
# draw the object
screen.blit(spaceship.image, spaceship.rect)
# update the display
pygame.display.update()
# limit frames per second
clock.tick(60)
The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()
limit frames per second to limit CPU usage
Furthermore I suggest to use a pygame.sprite.Group:
pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The former delegates the to the update mehtod 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 later 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. [...]
spaceship = Spaceship(screen, 400, 400)
all_sprites = pygame.sprite.Group()
all_sprites.add(spaceship)
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
key = pygame.key.get_pressed()
if key[pygame.K_a]:
spaceship.x -= 5
elif key[pygame.K_d]:
spaceship.x += 5
elif key[pygame.K_w]:
spaceship.y += 5
elif key[pygame.K_s]:
spaceship.y -= 5
all_sprites.update()
screen.fill((255, 255, 255))
all_sprites.draw(screen)
pygame.display.update()
I am making a classic Snake game. I want to draw a green rectangle (green_rect), and whenever my snake touches it, that green rectangle moves to a new, random location. When I run colliderect() on the Worm's surface, it always returns True for some reason, though, even if the worm doesn't touch the rectangle.
So far I've tried:
width = 640
height = 400
screen = pygame.display.set_mode((width, height))
green = pygame.Surface((10,10)) #it's the rectangle
green.fill((0,255,0))
green_rect = pygame.Rect((100,100), green.get_size())
# Our worm.
w = Worm(screen, width/2, height/2, 100) # I have a class called Worm, described later...
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
w.key_event(event) # direction control method in the Worm class
if green_rect.colliderect(w.rect): # w.rect is coming from Worm class: self.rect=self.surface.get_rect()
# surface variable is 100 coming from: w = Worm(screen, width/2, height/2, 100) -- it describes the worm's length in segments / pixels.
green_rect.x, green_rect.y = (random.randrange(420),random.randrange(300))
screen.fill((0, 0, 0))
screen.blit(green, green_rect)
w.move() #function for movement in Worm class
w.draw() #function for drawing the snake in Worm class
But this doesn't work, the green rectangle is moving uncontrollably, even if I don't touch it with the snake. colliderect must not be working since the x-y coordinates of the green rectangle are changing without the snake actually touching it. It should only change location when the snake touches the green rectangle.
I didn't show all of my code, because its little long. If it necessary I can
write out Class Worm as well. Well rect method is not working on
lists, so I couldn't figure how to fix this problem.
Edit: Also I want to know how to resize the snake's segments, since my default size is a pixel (dot). I want it bigger so if it hits a big rectangle, even on a corner of the rectangle or along a side, rectangle will still move.
Edit-2: Here is my Worm class:
class Worm():
def __init__(self, surface, x, y, length):
self.surface = surface
self.x = x
self.y = y
self.length = length
self.dir_x = 0
self.dir_y = -1
self.body = []
self.crashed = False
def key_event(self, event):
""" Handle key events that affect the worm. """
if event.key == pygame.K_UP:
self.dir_x = 0
self.dir_y = -1
elif event.key == pygame.K_DOWN:
self.dir_x = 0
self.dir_y = 1
elif event.key == pygame.K_LEFT:
self.dir_x = -1
self.dir_y = 0
elif event.key == pygame.K_RIGHT:
self.dir_x = 1
self.dir_y = 0
def move(self):
""" Move the worm. """
self.x += self.dir_x
self.y += self.dir_y
if (self.x, self.y) in self.body:
self.crashed = True
self.body.insert(0, (self.x, self.y))
if len(self.body) > self.length:
self.body.pop()
def draw(self):
for x, y in self.body:
self.surface.set_at((int(x),int(y)), (255, 255, 255))
I think I understand what's going on here:
Your class Worm consists of an object that contains a list of xy-tuples, one for each pixel it occupies. The 'worm' creeps across the playfield, adding the next pixel in its path to that list and discarding excess pixel locations when it's reached its maximum length. But Worm never defines its own rect and Worm.surface is actually the surface of the display area.
The reason why your target green_rect hops around all the time is because w.surface refers to screen, not the Worm's own pixel area. w.surface.get_rect() is therefore returning a rectangle for the entire display area, as suspected. The green rectangle has nowhere to go that doesn't collide with that rect.
With that in mind, here's a solution for you:
# in class Worm...
def __init__(self, surface, x, y, length):
self.surface = surface
self.x = x
self.y = y
self.length = length
self.dir_x = 0
self.dir_y = -1
self.body = []
self.crashed = False
self.rect = pygame.Rect(x,y,1,1) # the 1,1 in there is the size of each worm segment!
# ^ Gives the 'head' of the worm a rect to collide into things with.
#...
def move(self):
""" Move the worm. """
self.x += self.dir_x
self.y += self.dir_y
if (self.x, self.y) in self.body:
self.crashed = True
self.body.insert(0, (self.x, self.y))
if len(self.body) > self.length:
self.body.pop()
self.rect.topleft = self.x, self.y
# ^ Add this line to move the worm's 'head' to the newest pixel.
Remember to import pygame, random, sys at the top of your code and throw in pygame.display.flip() (or update, if you have a list of updating rects) at the end and you should be set.
As it stands, your code for a worm that crashes into itself runs, but has no effect.
One more suggestion: you may want to rename Worm.surface to something like Worm.display_surface, since pygame has a thing called Surface as well, and it might make it a bit easier for others to understand if the two were more clearly defined.
If you're looking for a more sophisticated worm consisting of a list of Rects, that's also possible. Just replace the xy-tuples in the Worm.body list with rects and replace if (self.x, self.y) in self.body: with if self.rect.collidelist(self.body) != -1: and it ought to work the same way. (collidelist(<list of Rects>) returns the index of the first colliding rect in that list, or -1 if there are none.)
Just make sure you also change the self.rect in class Worm so that it's more like
`self.rect = pygame.Rect(x,y, segment_width, segment_height)`
...and adjust self.dir_x and self.dir_y so that they're equal to +/- segment_width and segment_height, respectively. You'll need to fill each segment or use pygame.draw.rects instead of using set_at in Worm.draw(), as well.
UPDATE: Variable-size worm segments
Here's some code that uses Rects for the worm's body instead of xy-tuples. I have it set up so that the worm will define the size of each segment during initialization, then move forward in steps of that size (depending the axis of travel). Be aware the worm moves very quickly, has nothing stopping it from escaping the play area, and is quite long at 100 units length!
Here's my solution, in any case-- First is the revised class Worm code. I've marked altered lines with comments, including the lines that are new as of the original answer.
class Worm(pygame.sprite.Sprite): # MODIFIED CONTENTS!
def __init__(self, surface, x, y, length, (seg_width, seg_height)): # REVISED
self.surface = surface
self.x = x
self.y = y
self.length = length
self.dir_x = 0
self.dir_y = -seg_width # REVISED
self.body = []
self.crashed = False
self.rect = pygame.Rect(x,y,seg_width,seg_height) # NEW/REVISED
self.segment = pygame.Rect(0,0,seg_width,seg_height) # NEW
def key_event(self, event):
""" Handle key events that affect the worm. """
if event.key == pygame.K_UP:
self.dir_x = 0
self.dir_y = -self.segment.height # REVISED
elif event.key == pygame.K_DOWN:
self.dir_x = 0
self.dir_y = self.segment.height # REVISED
elif event.key == pygame.K_LEFT:
self.dir_x = -self.segment.width # REVISED
self.dir_y = 0
elif event.key == pygame.K_RIGHT:
self.dir_x = self.segment.width # REVISED
self.dir_y = 0
def move(self):
""" Move the worm. """
self.x += self.dir_x
self.y += self.dir_y
self.rect.topleft = self.x, self.y # NEW/MOVED
if self.rect.collidelist(self.body) != -1: # REVISED
self.crashed = True
print "CRASHED!"
new_segment = self.segment.copy() # NEW
self.body.insert(0, new_segment.move(self.x, self.y)) # REVISED
if len(self.body) > self.length:
self.body.pop()
def draw(self): # REVISED
for SEGMENT in self.body: # REPLACEMENT
self.surface.fill((255,255,255), SEGMENT) # REPLACEMENT
...you'll have to revise the arguments when you assign w, too, to include your desired worm segment dimensions. In this case, I've closen 4 pixels wide by 4 pixels tall.
w = Worm(screen, width/2, height/2, 100, (4,4)) # REVISED
That's what I'd do. Note that you can assign whatever segment size you like (as long as each dimension is an integer greater than zero), even using single-pixel or rectangular (which is, 'non-square') segments if you like.
Before you criticize me for not Googling or doing research before asking, I did research beforehand but to no avail.
I am trying to create the Atari Breakout game. I am currently stuck with making the ball bounce off walls. I did research on this and I found a lot of blogs and YouTube videos (and also Stack Overflow questions: this and this) talking about PyGame's vector2 class. I also read the PyGame documentation on vector2 but I can't figure out how to make it work.
I am currently writing a script to make the ball bounce off walls. In the beginning, the player is requested to press the spacebar and the ball will automatically move towards the north-east direction. It should bounce off the top wall when it hits it, but instead, it went inside. This is my approach:
import pygame
pygame.init()
screenWidth = 1200
screenHeight = 700
window = pygame.display.set_mode((screenWidth,screenHeight))
pygame.display.set_caption('Atari Breakout')
class Circle():
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
self.vel_x = 1
self.vel_y = 1
def check_hit():
global hit
if (((screenWidth-box.x)<=box.radius) or ((box.x)<=box.radius) or ((box.y)<=box.radius) or ((screenHeight-box.y)<=box.radius)):
# meaning hit either four walls
if (((screenWidth-box.x)<=box.radius) or ((box.x)<=box.radius)):
# hit right, left
print('hit right, left')
hit = True
elif (((box.y)<=box.radius) or ((screenHeight-box.y)<=box.radius)):
# hit top, bottom
print('hit top, bottom')
hit = True
# main loop
run = True
box = Circle(600,300,10)
hit = False
# (screenWidth-box.x)<=box.radius hit right wall
while run: # (box.x)<=box.radius hit left wall
# (box.y)<=box.radius hit top wall
pygame.time.Clock().tick(60) # (screenHeight-box.y)<=box.radius hit bottom wall
for event in pygame.event.get():
if event == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] and (box.y)>box.radius:
while True:
box.y -= box.vel_y
box.x += box.vel_x
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
check_hit()
if hit == False:
continue
elif hit == True:
break
if (box.y)<=box.radius or (screenHeight-box.y)<=box.radius:
# hit top, bottom
box.vel_x *= 1
box.vel_y *= -1
print('changed')
if (box.y)<=box.radius:
# hit top
print('hi')
while True:
box.x += box.vel_x # <-- myguess is this is the problem
box.y += box.vel_y
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
elif (screenWidth-box.x)<=box.radius or (box.x)<=box.radius:
# hit right, left
box.vel_x *= -1
box.vel_y *= 1
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
print('Where are you going')
pygame.quit()
I guess the problem is where I marked. Which is here:
if (box.y)<=box.radius or (screenHeight-box.y)<=box.radius:
# hit top, bottom
box.vel_x *= 1
box.vel_y *= -1
print('changed')
if (box.y)<=box.radius:
# hit top
print('hi')
while True:
box.x += box.vel_x # <-- myguess is this is the problem
box.y += box.vel_y
window.fill((0,0,0))
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()
but I don't know why. My theory is: the ball travels upwards, it hit the top wall, check_hit() kicks in and make hit = True, then the vel_x and vel_y is changed accordingly (if hit top wall, vel_x should remain the same while vel_y should be multiplied by -1). Then it will move down, hence "bounce" off the top wall.
Note: for now I only have the top wall working. The other three will be done when I can figure out how to bounce off the top wall first.
Can you help me see what's the problem? And if this kind of operation requires the use of the vector2 class, can you explain it to me or give me a place to learn it?
The issue are the multiple nested loops. You have an application loop, so use it.
Continuously move the ball in the loop:
box.y -= box.vel_y
box.x += box.vel_x
Define a rectangular region for the ball by a pygame.Rect object:
bounds = window.get_rect() # full screen
or
bounds = pygame.Rect(450, 200, 300, 200) # rectangular region
Change the direction of movement when the ball hits the bounds:
if box.x - box.radius < bounds.left or box.x + box.radius > bounds.right:
box.vel_x *= -1
if box.y - box.radius < bounds.top or box.y + box.radius > bounds.bottom:
box.vel_y *= -1
See the example:
box = Circle(600,300,10)
run = True
start = False
clock = pygame.time.Clock()
while run:
clock.tick(120)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
start = True
bounds = pygame.Rect(450, 200, 300, 200)
if start:
box.y -= box.vel_y
box.x += box.vel_x
if box.x - box.radius < bounds.left or box.x + box.radius > bounds.right:
box.vel_x *= -1
if box.y - box.radius < bounds.top or box.y + box.radius > bounds.bottom:
box.vel_y *= -1
window.fill((0,0,0))
pygame.draw.rect(window, (255, 0, 0), bounds, 1)
pygame.draw.circle(window, (44,176,55), (box.x, box.y), box.radius)
pygame.display.update()