0

My teacher gave us this code to analyze and I am not sure why he put 27 there for collision.. I asked him he said if the distance between the player and the enemy is less than 27 then I will call it a collision, but I still don't understand, can someone please kindly explain it to me in simpler terms. I don't understand where the number 27 comes from.. when my dimensions are so big?

import math
import random
import pygame

# Intialize the pygame
pygame.init()

# create the screen
screen = pygame.display.set_mode((1000, 700))

# Background
background = pygame.image.load('undersea.png')


# Player
playerImg = pygame.image.load('space-invaders.png')
playerX = 500
playerY = 600
playerX_change = 0

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

for i in range(num_of_enemies):
    enemyImg.append(pygame.image.load('plastic.png'))
    enemyX.append(random.randint(0, 636))
    enemyY.append(random.randint(50, 150))
    enemyX_change.append(4)
    enemyY_change.append(40)

# Bullet

# Ready - You can't see the bullet on the screen
# Fire - The bullet is currently moving

bulletImg = pygame.image.load('bullet (1).png')
bulletX = 0
bulletY = 480
bulletX_change = 0
bulletY_change = 10
bullet_state = "ready"

# Score

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

textX = 10
testY = 10

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


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))


def player(x, y):
    screen.blit(playerImg, (x, y))


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 = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
    distance = math.sqrt(distance)
    print(distance)
    if distance < 27:
        return True
    else:
        return False


# Game Loop
running = True
while running:

    # RGB = Red, Green, Blue
    screen.fill((0, 0, 0))
    # Background Image
    screen.blit(background, (0, 0))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # if keystroke is pressed check whether its right or left
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                playerX_change = -5
            if event.key == pygame.K_RIGHT:
                playerX_change = 5
            if event.key == pygame.K_SPACE:
                if bullet_state is "ready":
                    # Get the current x cordinate of the spaceship
                    bulletX = playerX
                    fire_bullet(bulletX, bulletY)

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

    # 5 = 5 + -0.1 -> 5 = 5 - 0.1
    # 5 = 5 + 0.1

    playerX += playerX_change
    if playerX <= 0:
        playerX = 0
    elif playerX >= 636:
        playerX = 636

    # 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

        enemyX[i] += enemyX_change[i]
        if enemyX[i] <= 0:
            enemyX_change[i] = 4
            enemyY[i] += enemyY_change[i]
        elif enemyX[i] >= 736:
            enemyX_change[i] = -4
            enemyY[i] += enemyY_change[i]

        # Collision
        collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
        if collision:
            bulletY = 480
            bullet_state = "ready"
            score_value += 1
            enemyX[i] = random.randint(0, 736)
            enemyY[i] = random.randint(50, 150)

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

    # Bullet Movement
    if bulletY <= 0:
        bulletY = 480
        bullet_state = "ready"

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

    player(playerX, playerY)
    show_score(textX, testY)
    pygame.display.update()
unknownmac
  • 41
  • 3

3 Answers3

3

The value is 27, because you compute the Euclidean distance between (enemyX, enemyY) and (bulletX, bulletY) in the function isCollision:

def isCollision(enemyX, enemyY, bulletX, bulletY):
   distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
   distance = math.sqrt(distance)
   print(distance)

The Euclidean distance between 2 points (Ax, Ay) and (Bx, By) is

d = sqrt((Bx-Ax)**2 + (By-Ay)**2) = hypot(Bx-Ax, By-Ay)

In 2 dimensional space this is the same as the Pythagorean theorem. The length of the diagonal in a square with a side length of 19 is approximately 27.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    And what is special about the number 19? – mkrieger1 Jun 23 '20 at 15:03
  • @mkrieger1 it's your application not mine. Probably it is empircal value. The value depends on the sprites. – Rabbid76 Jun 23 '20 at 15:05
  • @mkrieger1 The value does not only depend on the size of the image, it also depends on the size of the area which is covered in the image. – Rabbid76 Jun 23 '20 at 15:11
  • It’s not my application, I’m not the OP. I was just wondering, now we have just shifted the question from "why 27?" to "why 19?". – mkrieger1 Jun 23 '20 at 15:39
  • @mkrieger1 I've given you the answer in the comment above. It is an empirical value, that depends on the sprites (on the drawing) and gives proper results for this application and sprites. – Rabbid76 Jun 23 '20 at 15:55
0

Try changing the '27' to a small no. like '5' and you will see that the collision is happening too late on screen. OR if you replace the enemy/bullet image you are loading with some other small/large image you will notice that you will have to change you '27' to some other number to make things work as intended.

Simple Words The reason for the hard-coding '27' could be because the width of enemy image and the width of bullet image you load in your code seem to touch (collide) eachother at the distance '27' from the point where they are drawn.

Explanation When we load an image pygame draws it from top-left corner. For Example your background image is so large in code but when you ask your code where is this 'background' located it well tell you it is on (0,0) although it's on your full screen not a single point. Infered from screen.blit(background, (0, 0)) line in your code

Same for the images/sprites of enemy and bullets. The position you use in collision enemyX, enemyY, bulletX, bulletY are the top-left coordinates. Now to show them actually touching/colliding you may have to tell the code that 'My enemy is more than one pixel on the position. So I want to take care of it by using the constant '27' (depends on width of enemy/bullet). As soon as the both positions have a Euclidean distance of '27' this means they are colliding on my screen hence proceed as true.

If you are still confused about the positioning issue and how center of image or top-left of image matters read these, it will help.

https://stackoverflow.com/a/51182238/11672352

Why is the pygame rect not in the right place?

glory9211
  • 741
  • 7
  • 18
-1

It seems to me like the game triggers the collision once the bullet enters a circle of radius 27 pixels around the enemy. Although your enemy may not be a perfect circle, it is a lot simpler to treat them as if they were when calculating collisions as it is relatively easy to check whether a point has collided with a circle, in this case, the bullet with the enemy. the lines:

distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
distance = math.sqrt(distance)

are used to calculate the distance between the middle of the enemy and the bullet by using Pythagoras' theorem (a^2 + b^2 = c^2 where a and b are two different sides of a right angled triangle and c is your hypotenuse, aka the longest side) The value 27 is likely used because the enemy is likely around 52 pixels (27 * 2) wide and tall

Pzet
  • 470
  • 2
  • 4
  • 11