Asked  7 Months ago    Answers:  5   Viewed   23 times

Heres my code

import pygame, os

os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()

win = pygame.display
d = win.set_mode((1200, 600))

class player:
    def __init__(self, x, y, height, width):
        self.x = x
        self.y = y
        self.height = height
        self.width = width
        self.speed = 2

    def draw(self):
        pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))

    def move_left(self):
        self.x -= self.speed

    def move_right(self):
        self.x += self.speed


class bullet:
    def __init__(self):
        self.radius = 10
        self.speed = 20


    def shoot(self):
        x = p.x
        y = p.y
        self.shooting = True
        while self.shooting:
            d.fill((98, 98, 98))
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()

            y -= self.speed
            pygame.draw.circle(d, (255, 0, 0), (x, y), self.radius)
            pygame.time.Clock().tick(100)
            win.update()

            if y <= 0:
                self.shooting = False


b = bullet()
p = player(600, 500, 50, 30) 
while True:
    d.fill((98, 98, 98))
    p.draw()
    for event in pygame.event.get():
        pass

    if event.type ==  pygame.KEYDOWN:
        if event.key == pygame.K_SPACE:
            b.shoot()
        if event.key == pygame.K_LEFT:
            p.move_left()
        if event.key == pygame.K_RIGHT:
            p.move_right()



    win.update()

This is what i could come up with after a few Trial and errors but it is really uneffective. Firstly the player disappers when i press space bar. I guess this is obvious as i have a different loops for shooting and player but i dont know how to get around it and implement both shooting and player in the same loop. The second probllem i am having is breaking the while self.shooting: loop. I tried breaking it when y reaches a certain point by doing this

 if y <= 0:
     self.shooting = False

but this dosent break. Instead, it restarts the loop all over again. Another weird problem i am having is that everytime i move mouse(slightly fast) or press a bunch of buttons at once, it breaks the while self.shooting loop.

 Answers

67

The general approach to firing bullets is to store the positions of the bullets in a list (bullet_list). When a bullet is fired, add the bullet's starting position ([start_x, start_y]) to the list. The starting position is the position of the object (player or enemy) that fires the bullet. Use a for-loop to iterate through all the bullets in the list. Move position of each individual bullet in the loop. Remove a bullet from the list that leaves the screen (bullet_list.remove(bullet_pos)). For this reason, a copy of the list (bullet_list[:]) must be run through (see How to remove items from a list while iterating?). Use another for-loop to blit the remaining bullets on the screen:

bullet_list = []

while run == True:
    # [...]

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullet_list.append([start_x, start_y])

    for bullet_pos in bullet_list[:]:
        bullet_pos[0] += move_bullet_x
        bullet_pos[1] += move_bullet_y
        if not screen.get_rect().colliderect(bullet_image.get_rect(center = bullet_pos))
            bullet_list.remove(bullet_pos)

    # [...]

    for bullet_pos in bullet_list[:]
        screen.blit(bullet_image, bullet_image.get_rect(center = bullet_pos))

    # [...]

See also Shoot bullet.


Please note, that class names should normally use the CapWords convention. (See Style Guide for Python Code - Class names)
That means it has to be Player and Bullet rather than player and bullet

You have an application loop, so use it. All the objects are continuously updated and drawn in the main application loop, in each frame.

The class Bullet do not need any loop. The constructor has to have parameters for the position (x, y). Further it needs on method which changes the position and one which draws the bullet:

class Bullet:
    def __init__(self, x, y):
        self.radius = 10
        self.speed = 10
        self.x = x
        self.y = y

    def update(self):
        self.y -= self.speed#

    def draw(self):
        pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)

Use a list of bullets. Create a new bullet when space is pressed. Move the bullets (update) in every frame an remove a bullet if it is out of the window. Draw the remaining bullets in every frame:

bullets = []

# [...]
while run:

    for event in pygame.event.get():
        # [...]
        if event.type ==  pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullets.append(Bullet(p.x+p.width//2, p.y))

    # [...]
    for b in bullets:
        b.update()
        if b.y < 0:
            bullets.remove(b)

    # [...]
    for b in bullets:
        b.draw()

Furthermore use pygame.key.get_pressed() use to get the state of the keys in every frame and to update the position of the player:

while run:

    # [...]

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        p.move_left()
    if keys[pygame.K_RIGHT]:
        p.move_right()

Complete example:

import pygame, os

os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()

win = pygame.display
d = win.set_mode((1200, 600))
clock = pygame.time.Clock()

class Player:
    def __init__(self, x, y, height, width):
        self.x = x
        self.y = y
        self.height = height
        self.width = width
        self.speed = 2

    def draw(self):
        pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))

    def move_left(self):
        self.x -= self.speed

    def move_right(self):
        self.x += self.speed


class Bullet:
    def __init__(self, x, y):
        self.radius = 10
        self.speed = 10
        self.x = x
        self.y = y

    def update(self):
        self.y -= self.speed#
    
    def draw(self):
        pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)


bullets = []
p = Player(600, 500, 50, 30) 

run = True
while run:
    clock.tick(100)
        
    # handel events
    for event in pygame.event.get():
        if event.type ==  pygame.QUIT:
            run = False
        if event.type ==  pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullets.append(Bullet(p.x+p.width//2, p.y))

    # update objects
    keys = pygame.key.get_pressed()        
    if keys[pygame.K_LEFT]:
        p.move_left()
    if keys[pygame.K_RIGHT]:
        p.move_right()
    for b in bullets:
        b.update()
        if b.y < 0:
            bullets.remove(b)

    # clear display
    d.fill((98, 98, 98))

    # draw scene
    for b in bullets:
        b.draw()
    p.draw()

    # update display
    win.update()
Tuesday, June 1, 2021
 
altermativ
answered 7 Months ago
13

The return value of self.clock.tick() is the time which has passed since the last call. Use the return value to control the speed. Define the distance, of movement of the snake per second (e.g. self.velocity = 400 means 400 pixel per second). Get the time between to frames (delta_t) and scale the movement of the snake by the elapsed time (delta_t / 1000):

class Game:
    def __init__(self):
        # [...]

        # distance per second 
        self.velocity = 400

    # [...]

    def game(self):

        # [...]

        while self.running:
            delta_t = self.clock.tick(30)

            # [...]

            if not self.paused:
                step = delta_t / 1000 # / 1000 because unit of velocity is seconds
                self.snake_x += self.snake_x_change * step
                self.snake_y += self.snake_y_change * step
            else:
                self.game_over()

            pygame.display.flip()

With this setup it is ease to control the speed of the snake. For instance, the speed can be increased (e.g. self.velocity += 50), when the snake grows.

Of course you have to round the position of the snake (self.snake_x, self.snake_y) to a multiple of the grid size (multiple of 25) when you draw the snake and when you do the collision test. Use round to do so:

x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25

Ensure that the positions which are stored in snake_list are a multiple of 25. Just append a new head to the list, if the head of the snake has reached a new field:

if len(self.snake_list) <= 0 or snake_head != self.snake_list[-1]:
   self.snake_list.append(snake_head)

Apply that to the methods build_snake draw and check_apple_eaten:

class Game:
    # [...]

    def build_snake(self):
        snake_head = list()
        x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
        snake_head.append(x)
        snake_head.append(y)
        if len(self.snake_list) <= 0 or snake_head != self.snake_list[-1]:
            self.snake_list.append(snake_head)

        if len(self.snake_list) > self.snake_length:
            del self.snake_list[0]

        for snake in self.snake_list[:-1]:
            if snake == snake_head:
                self.snake_reset()

        self.draw("snake")

    def check_apple_eaten(self):
        x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
        if x == self.apple_x and y == self.apple_y:
            self.set_position("apple")
            self.snake_length += 1
            self.score += 1

    def snake_borders_check(self):
        x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
        if x < 0 or x > self.SCREEN_WIDTH - 25:
            self.snake_reset()
        if y < 0 or y > self.SCREEN_HEIGHT - 25:
            self.snake_reset()
Sunday, August 29, 2021
 
Zach
answered 3 Months ago
56

From a post on Reddit I found the solution just perfectly works for playing .ogg music-file.

If you're using Homebrew, type the following on Terminal: (before I run the following code, I check whether I have installed any one below by brew list)

brew install libogg
brew install libvorbis
brew install sdl_mixer --with-libvorbis

If you have installed sol_mixer, and your program still don't work(Yes, it also didn't for me),

Try:

brew reinstall sdl_mixer --with-libvorbis
Monday, August 30, 2021
 
Daniel Moses
answered 3 Months ago
10

use fgets with STDIN as the file stream. Then you can specify the amount of data you want to read and where to put it.

Friday, October 1, 2021
 
jonboy
answered 2 Months ago
95

How about using a barcode font? some alternatives too. I haven't used that one but there may be others available too

Tuesday, November 23, 2021
 
Corbin March
answered 1 Week ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share