1

I want to know if my enemy is within 200 pixels of a defense tower so that I can start taking lives of the enemy. The enemy is moving and the defense is still FYI. if anyone can give me advice on how to do this that would be amazing. If I put my code up it will just confuse everyone because my code is very messy so just give me advice on how to do it thanks. Nick. I have added my code because I know I have done something wrong if anyone has the time to read through it and tell me what I am doing wrong which is probably everything that would be much appreciated.

import pygame
import math
from pygame.locals import *

def text():
    font = pygame.font.SysFont("monospace", 14)
    text = font.render("Start Round", True, black)
    textpos = text.get_rect()
    textpos.center = (790,675)
    Background.blit(text, textpos)

def newRound():
    pos = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    if 730 < pos[0] < 850 and 650 < pos[1] < 800:
        pygame.draw.rect(Background, (150,150,150), (730,650,120,50))
        if click[0] == 1:
            startGame()        
    else:
        pygame.draw.rect(Background, (100,100,100), (730,650,120,50))

def startGame():
    global startRound, endRound, intro
    intro = 0
    createRound()
    intro = 1
    startRound = True
    endRound = False

def lifeText(lifes):
    font = pygame.font.SysFont("monospace", 20)
    text = font.render("Lives %s" % (lifes) , True, black)
    textpos = text.get_rect()
    textpos.center = (60,30)
    Background.blit(text, textpos)

def life(self):
    global hit, endRound, startRound, noEnemies, lifes
    if noEnemies == 0 and lifes > 0:
        startRound = False
        endRound = True

    if self.rect.x == 960:
        hit = hit + 1
        lifes = lifes - 1
        if lifes == 0:
            print("You have 0 lives Game Over")
            pygame.quit()
    if hit == 4:
        startRound = False
        endRound = True
        hit = 0
        noEnemies = noEnemies + 1


def createRound():
    global enemies, noEnemies

    enemies = []

    x = -40
    y = 210
    for e in range(noEnemies):
        x = x - 80
        enemies.append(yellowEnemy(x, y, Background))
    noEnemies = len(enemies)

def displayTower():
    for tower in towers:
        Background.blit(redtower, (tower))


class yellowEnemy(object):

    image1 = pygame.image.load("enemySpriteFullHealth.jpg")
    image2 = pygame.image.load("enemySpriteHalfHealth.jpg")
    image3 = pygame.image.load("enemySpriteDead.jpg")

    def __init__(self, x, y, Background):
        self.Background = Background
        self.Background_rect = Background.get_rect()
        self.rect = self.image1.get_rect()
        self.rect = self.image2.get_rect()
        self.rect = self.image3.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.health = 20
        self.dist_x = 2
        self.dist_y = 0

    def update(self):
        self.rect.x += self.dist_x
        self.rect.y += self.dist_y

    def draw(self, Background):
        timeDead = 0
        if self.health > 9 and self.health < 21:
            Background.blit(self.image1, self.rect)
        elif self.health < 10 and self.health > 1:
            Background.blit(self.image2, self.rect)
        elif self.health < 1:
            Background.blit(self.image3, self.rect)
            self.dist_x = 0
        life(self)

pygame.init()

width = 960
height = 720

black = (0,0,0)
lifes = 10
hit = 0
intro = 1
FPS = 200
noEnemies = 4
bx = 1000
by = 1000
towers = []

endRound = True
startRound = False
clicked = False
mx, my = pygame.mouse.get_pos()
clock = pygame.time.Clock()

test= False
mapImg = pygame.image.load("mapimage.jpg")
redtower = pygame.image.load("redTower.jpg")

Background = pygame.display.set_mode((width, height))
Background_rect = Background.get_rect()

while intro == 1:
    mousePos = pygame.mouse.get_pos()
    mousePressed = pygame.mouse.get_pressed()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

        if 530 < mousePos[0] < 590 and 650 < mousePos[1] < 710:
            if mousePressed[0] == 1:
                clicked = True

        if clicked == True:
            mx, my = pygame.mouse.get_pos()
            pygame.display.update()
            bx = 30
            by = 30
            if mousePressed[0] == 0:
                clicked = False
                tx = mx - bx
                ty = my - by
                towerCords = tx, ty
                towers.append(towerCords)  

    if endRound == True:
        Background.blit(mapImg, (0,0))
        newRound()
        text()

    if startRound == True:
        for enemy in enemies:
            enemy.update()
        Background.blit(mapImg, (0,0))
        for enemy in enemies:
            enemy.draw(Background)

    Background.blit(redtower, (mx-bx, my-by))
    if clicked == True:
        pygame.draw.circle(Background, (220, 0, 0), (mx, my), 200, 4)
    displayTower()
    lifeText(lifes)
    Background.blit(redtower, (530,650))
    pygame.display.update()
    clock.tick(FPS)
tristansokol
  • 4,054
  • 2
  • 17
  • 32

3 Answers3

0

If they are sprites, you can simply do:

import math

defense_rect = defense.get_rect()

if math.abs(enemy.rect.center - defense_rect.rect.center) <= 200:
    # *do something*

The logic is to see if the enemy's center is 200 pixels from the defense's center from any position (hence the usage of math.abs() which is absolute value). When it is, you replace the comment with your code. Why does this work? Check here.

Community
  • 1
  • 1
Anthony Pham
  • 3,096
  • 5
  • 29
  • 38
0

To find the distance between 2 points, you can use this code:

def get_dist(pos1, pos2):              
    return math.hypot(pos1[0] - pos2[0], pos1[1] - pos2[1])

This also requires you to import math at the beginning of your program.

Luke B
  • 2,075
  • 2
  • 18
  • 26
  • This would work perfectly but I have two lists which are long so it would be difficult for me to implement this in. – Nicholas Cole Jan 02 '17 at 12:21
  • how long are the lists (on average)? – Luke B Jan 02 '17 at 13:53
  • about 5 or 6 maybe 7 – Nicholas Cole Jan 02 '17 at 14:00
  • well, I have done a very similar thing with similar length lists, and it seems to work just fine. I timed it and it uses a very small amount of time. – Luke B Jan 02 '17 at 14:17
  • I have got it to work, however I found out that only the first tower placed will actually do damage to the enemy. Do you know how to do it so that all the towers will fire at the enemy. Thanks Nick – Nicholas Cole Jan 02 '17 at 17:47
  • I have now changed it so I think it will work with multiple towers however I get this error. TypeError: unsupported operand type(s) for -: 'tuple' and 'int' Does anyone know how to fix this? it is on this line of code dist = math.hypot(towers[0] - self.rect[0], towers[1] - self.rect[1]) – Nicholas Cole Jan 02 '17 at 17:54
0

Pygame has pygame.Rect() to keep object position and size.

Tower 200x200 with top left corner in point (0,0)

tower_rect = pygame.Rect(0,0, 300, 300)

or you can move it to have (0,0) in center

tower_rect = pygame.Rect(0,0, 300, 300)
tower_rect.center = (0, 0)

To check if other Rect() is fully inside tower

enemy_rect = pygame.Rect(10, 10, 50, 50)

if tower_rect.contains(enemy_rect):

or if it fully or only partially in tower (it coollides with tower)

if tower_rect.colliderect(enemy_rect):

You can event test with list of enemies

if tower_rect.collidelistall(list_of_enemies_rect):

or check one enemy with all towers

if enemy_rect.collidelistall(list_of_towers_rect):
furas
  • 134,197
  • 12
  • 106
  • 148
  • if self.rect.collidelistall(redtower_rect): TypeError: Argument must be a sequence of rectstyle objects. This is the error I get – Nicholas Cole Jan 02 '17 at 12:21
  • I specially used names with word `list` because some functions expect list, not single object. To check collision with one tower you have `colliderect()` – furas Jan 02 '17 at 12:23
  • I dont know what to do I am so stuck. I have my enemy which is self.rect and then I have my towers which I store the x and y coordinates in a list for, is there a way I can tuern this list into a rect? – Nicholas Cole Jan 02 '17 at 12:28
  • use list of rects for towers at the beginning - `tower_rect = pygame.Rect(x, y, 200, 200)` and `all_towers.append(tower_rect)`. Or if your `x,y` has to be in center of tower `tower_rect = pygame.Rect(0, 0, 200, 200)` and `tower_rect.center = (x, y)`. You can use `rect` to check collision and for blit image `blit(tower_image, tower_rect)` or in `pygame.draw.rect( ... , tower_rect, ...)` – furas Jan 02 '17 at 12:44
  • I want to change a list of coordinates into a rect not the other way around, is that possible? – Nicholas Cole Jan 02 '17 at 12:47
  • you can put here link to code or send on mail furas@tlen.pl – furas Jan 02 '17 at 13:48