0

I am new to pygame programming and I am making a game with the help of tutorials. I finished the tutorial and want to add a new feature to my game.

Game Overview

I have created a space invader game where there are enemies moving on the x-axis and slowly coming down. In bottom there is a space ship which can move on the x axis and can shoot bullets to destroy or kill the enemies. The enemies respawn after being killed and if an enemy reaches the bottom, it is game over

Code

# importing modules
import pygame
import random
import math
from pygame import mixer

# initialize pygame
pygame.init()

# creating a window
screen = pygame.display.set_mode((800, 600))

# Background Music
mixer.music.load('background.wav')
mixer.music.play(-1)

# Background

bg_img = pygame.image.load("background3.png")
bg = pygame.transform.scale(bg_img, (800, 600))

# Title and Icon
pygame.display.set_caption("Space Shooters")
icon = pygame.image.load('spaceship.png')
pygame.display.set_icon(icon)

# Player
player = pygame.image.load('spaceship2.png')
playerX = 370
playerY = 500
playerX_change = 0

# Enemy
enemyImg = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 6

for i in range(num_of_enemies):
    enemyImg.append(pygame.image.load('enemy.png'))
    enemyX.append(random.randint(6, 730))
    enemyY.append(random.randint(45, 150))
    enemyX_change.append(0.3)
    enemyY_change.append(40)

# Bullet
# Ready - The bullet can't be seen
# Fire - The bullet has been shot
bulletImg = pygame.image.load('bullet.png')
bulletX = 0
bulletY = 480
bulletX_change = 0
bulletY_change = 1.5
bullet_state = "Ready"

# Score

score_value = 0
font = pygame.font.Font('freesansbold.ttf', 32)

textX = 10
textY = 10

# Game Over
over_font = pygame.font.Font('freesansbold.ttf', 64)


# Function for displaying text
def show_score(x, y):
    score = font.render("Score : " + str(score_value), True, (255, 255, 255))
    screen.blit(score, (x, y))


def game_over_text():
    over_text = over_font.render("GAME OVER", True, (255, 255, 255))
    screen.blit(over_text, (200, 250))


# Defining and drawing spaceship
def spaceship(x, y):
    screen.blit(player, (x, y))


# Defining and drawing enemy
def enemy(x, y, i):
    screen.blit(enemyImg[i], (x, y))


def fire_bullet(x, y):
    global bullet_state
    bullet_state = "Fire"
    screen.blit(bulletImg, (x + 16, y + 10))


def Iscollision(enemyX, enemyY, bulletX, bulletY):
    distance = math.sqrt((math.pow(enemyX - bulletX, 2)) + (math.pow(enemyY - bulletY, 2)))
    if distance < 27:
    return True
    else:
        return False


# making the game window run endlessly and game loop
running = True

while running:

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

    # Keystroke check (right, left)
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_LEFT:
            playerX_change = -0.3
        if event.key == pygame.K_RIGHT:
            playerX_change = 0.3
        if event.key == pygame.K_SPACE:
            # Checking whether bullet is there on screen, if not making it ready, if yes disabling spacebar
            if bullet_state == "Ready":
                # Get the current X cordinate of spaceship
                bulletX = playerX
                fire_bullet(bulletX, bulletY)
                bullet_sound = mixer.Sound('laser.wav')
                bullet_sound.play()
    if event.type == pygame.KEYUP:
        if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
            playerX_change = 0

# Changing X coordinates to move spaceship
playerX += playerX_change

if playerX <= 6:
    playerX = 6

elif playerX >= 730:
    playerX = 730

screen.fill(0)

screen.blit(bg, (0, 0))

# Enemy Movement

for i in range(num_of_enemies):

    # Game Over
    if enemyY[i] > 440:
        for j in range(num_of_enemies):
            enemyY[j] = 2000
        game_over_text()
        break

for i in range(num_of_enemies):
    enemyX[i] += enemyX_change[i]

    if enemyX[i] <= 6:
        enemyX_change[i] = 0.3
        enemyY[i] += enemyY_change[i]

    elif enemyX[i] >= 730:
        enemyX_change[i] = -0.3
        enemyY[i] += enemyY_change[i]

    # Collision
    collision = Iscollision(enemyX[i], enemyY[i], bulletX, bulletY)
    # if collision and if collision is true are both the same

    if collision:
        collision_sound = mixer.Sound('explosion.wav')
        collision_sound.play()
        bulletY = 480
        bullet_state = "Ready"
        score_value += 1
        enemyX[i] = random.randint(6, 730)
        enemyY[i] = random.randint(45, 150)

    enemy(enemyX[i], enemyY[i], i)

# Deleting each frame of enemy moving so that it runs smoothly

# Bullet Movement

if bulletY <= 0:
    bulletY = 480
    bullet_state = "Ready"

if bullet_state == "Fire":
    fire_bullet(bulletX, bulletY)
    bulletY -= bulletY_change

spaceship(playerX, playerY)

show_score(textX, textY)

# updating display
pygame.display.update()

New Feature

I want to add a feature that when a laser ( I have a picture) hits the spaceship, shot by the enemies, the spaceship can't move for 10 seconds. I have already tryed other stack overflow questions but they don't match my needs.

Dev
  • 43
  • 6
  • 1
    keep track of the time in the game `currentTime` (eg: seconds since the start), then you can have a variable `frozenUntil` that you set to `currentTime + 10`. And in the main loop you check `if currentTime < frozenUntil:` remain frozen, otherwise let it move. You should probably start using classes, otherwise your code will get too long and messy. –  Sep 13 '22 at 11:29

2 Answers2

0

If you only have one cooldown to manage, a simple and clean way to do it is by using a custom event :

# Custom event
COOLDOWN_END = pg.USEREVENT

When you detect a collision you simply start a timer :

# Custom event
pg.time.set_timer(COOLDOWN_END, 1000, 1)

The first parameter is the event you are going to send to your event handler, the 2nd is the time in ms (so 1 sec here) and last one is the number of iteration. Here you just want to send the event once, so put 1.

The last step is to do catch this event in your handler and do what you want with it :

for evt in pg.event.get():
    if evt.type == pg.QUIT:
        exit()
    if evt.type == COOLDOWN_END:
        print(evt)
        # Your logic goes here

Let's put everything together :
(Here, I simply used this technique to have a COOLDOWN_END event 1 sec after I clicked)

# General imports
import pygame as pg
import sys

# Init
pg.init()

# Vars
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("name")

# Clock
FPS = 60
clock = pg.time.Clock()

# Custom event
COOLDOWN_END = pg.USEREVENT

# Main functions
def update():
    pass

def draw():
    # Clear screen
    screen.fill((0,0,0))

def handle_evt():
    for evt in pg.event.get():
        if evt.type == pg.QUIT:
            exit()
        
        if evt.type == pg.KEYDOWN:
            if evt.key == pg.K_ESCAPE:
                exit()
        
        if evt.type == pg.MOUSEBUTTONDOWN:
            if evt.button == 1:
                on_click()
        
        if evt.type == COOLDOWN_END:
            print(evt)

def on_click():
    pg.time.set_timer(COOLDOWN_END, 1000, 1)

def exit():
    pg.quit()
    sys.exit()

# Main loop
if __name__ == '__main__':
    while True:
        handle_evt()
        update()
        draw()
        clock.tick(FPS)
        pg.display.flip()

Anto
  • 267
  • 1
  • 11
  • After the amount of time I give to stop my spaceship, I want the game to run normally. Will this be the case? – Dev Sep 13 '22 at 12:08
  • Well, the logic you put in place will take care of that, for example you can set a boolean `frozen = False`, when you detect a collision, you set it to True : `frozen = True` and when you receive the event after X seconds, you set it back to False : `frozen = False`. All that's left to do is to only move you spaceship if not frozen. If you really struggle I'll edit my question to provide more details but I think you can solve your problem from here. – Anto Sep 13 '22 at 12:13
0

You may use custom event with from pygame.locals import USEREVENT.

An alternative is to keep track of time of when you got hit using pygame.time.get_ticks() + some stun cooldown system, and applying only to the portion of the code involved with player movement.

stun_duration = 10000   # milliseconds
damaged_timestamp = -stun_duration      # allows to get stunned starting from pygame.init() call

running = True
while running:

    for event in pygame.event.get():

        # preceding code

        # check if beyond stun cooldown
        if pygame.time.get_ticks() - damaged_timestamp > stun_duration:
            damaged_timestamp = pygame.time.get_ticks()

            # Keystroke check (right, left)
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    playerX_change = -0.3
                if event.key == pygame.K_RIGHT:
                    playerX_change = 0.3
                if event.key == pygame.K_SPACE:
                    # Checking whether bullet is there on screen, if not making it ready, if yes disabling spacebar
                    if bullet_state == "Ready":
                        # Get the current X cordinate of spaceship
                        bulletX = playerX
                        fire_bullet(bulletX, bulletY)
                        bullet_sound = mixer.Sound('laser.wav')
                        bullet_sound.play()

            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                    playerX_change = 0

            # Changing X coordinates to move spaceship
            playerX += playerX_change

            if playerX <= 6:
                playerX = 6

            elif playerX >= 730:
                playerX = 730

        # proceeding code
Jobo Fernandez
  • 905
  • 4
  • 13