Два спрайта замечают друг друга

Привет еще раз Переполнение стека. вы, вероятно, помните меня по проблеме порождения моего подразделения в моей программе pygame "Table Wars". Я решил изменить сферу своей игры на стратегию в реальном времени, а не на пошаговую игру. Я хочу, чтобы игра шла в русле топовой флеш игры: "Age of War". В игре работает практически все: нерестовые юниты, HUD для игры и даже базовое здоровье. К сожалению, я не могу понять, как реализовать способность юнитов атаковать врагов или вражескую базу. Вот концепции, которые применяются к самим подразделениям:

  • Подразделение появляется при нажатии клавиши вокруг базы команды: K_1 порождает спрайт из Red_Infantry учебный класс
  • Единица, когда появляется, добавляется к Group учебный класс. Есть два Groups, по одному на каждую команду.
  • Устройство движется через move_ip позвонить в течение def update пока он не достигнет точки, близкой к вражеской базе, где он останавливается.

Вот как я хочу вести бой для разных юнитов:

  • Подразделение останавливается всякий раз, когда обнаруживает врага в радиусе его атаки. Разные юниты имеют разные дальности атаки
  • Затем отряд атакует с интервалом в одну секунду.
  • Если юнит успешно снижает здоровье вражеского юнита до 0, враг умирает, а другой может продолжать
  • Этот цикл повторяется до тех пор, пока отряд не достигнет базы противника, где он будет атаковать базу противника с интервалом в одну секунду. Один из юнитов сможет нанести в три раза больше обычного урона по базе.

Вот пример моего кода, показывающий Red_Infantry учебный класс:

class Red_Infantry(pygame.sprite.Sprite):
def __init__(self, screen):
    pygame.sprite.Sprite.__init__(self)
    self.image, self.rect = load_image('Soldier_red.png', -1)
    self.rect.move_ip(random.randint(75, 100), random.randint(275, 325))
    self.selected = 0
    self.area = screen.get_rect()
    self.health = 100 #Soldiers are have mediocre toughness.
    self.attack_damage = 25 #The amount of damage it deals
    self.range = 20 #The attack range of the unit.
    self.update()
def update(self):
    self.rect.move_ip(1, 0)
    if self.rect.right >= 725: #This position is close to the enemy base...
        self.rect.right = 725 #...where it will then stop
    if self.health <= 0:  
        self.kill() #a simple code that kills the sprite if his health reaches 0

Основной цикл содержит только возможность порождения каждого из юнитов.

2 ответа

Решение

Вот отправная точка

class RedInfantry(pygame.sprite.Sprite):
    def __init__(self):
        self.screen = pygame.display.get_surface()
        self.image, self.rect = load_image('Soldier_red.png', -1)    
        self.rect.move_ip(random.randint(75, 100), random.randint(275, 325))
        self.target = None
        self.range = 20
        self.attack_cooldown = 200
        self.attack_last = 0

    def move_toward(self, target):
        # move toward players click destination, if one is set.
        # else toward my attack target

    def update(self):
        # move...
        self.find_target()
        self.move_toward(self.target)
        self.attack()
        # check HP
        if self.health <= 0:
            self.kill()

    def find_target(self):
        """finds new targets in range:
        for speed: only call this once every 200ms."""
        if self.target is not None: return
        for enemy in B.sprites():
            if distance(self.rect.center, enemy.rect.center) <= self.range:
                self.target = enemy
                return
        # else no targets in range
        self.target = None  


    def attack(self):
        """attack, if able.
        target exists? still alive? gun cooldown good?"""
        if self.target is None: return
        if self.target.health <= 0: return
        if not self.cooldown_ready(): return

        # all good, fire. could verify still in range. 
        self.target.damage(self.attack_damage)

    def cooldown_ready(self):
        # gun ready to fire? has cooldown in MS elapsed.
        now = pygame.time.get_ticks()
        if now - self.attack_last >= self.attack_cooldown:
            self.attack_last = now
            return True
        return False

Из вашего вопроса не совсем понятно, как группы взаимодействуют друг с другом для выявления. Далее я собираюсь предположить, что Группа A "замечает" Группу B, если какой-либо член a из A находится в некотором указанном диапазоне любого члена b из B.

Самый простой способ сделать это - просто пройти по всем (a,b) парам. Для этого вы можете использовать библиотеку itertools, что-то вроде...

spotted = False
for a, b in itertools.product(A.sprites( ), B.sprites( )):
    if is_in_range(a, b):
        spotted = True
        break

Проблема с этим подходом состоит в том, что вычислительные затраты довольно высоки. (Это n**2 по сложности.) Кроме того, без какой-либо обрезки и оптимизации вы должны запустить этот блок кода для каждой пары дружественных / вражеских групп. Теперь, если гарантируется, что каждая группа имеет некоторую постоянную геометрию, мы можем сделать стоимость вычислений НАМНОГО дешевле. Однако, предполагая, что у групп нет фиксированной геометрии, я бы рекомендовал использовать пакет геометрии, чтобы выполнить большую часть работы за вас. Эти пакеты очень мощные и очень эффективные... и многие из них также удивительно просты в использовании. Могут быть пакеты геометрии, определенные для PyGame... Я не могу думать ни о каком в настоящее время.

Я часто использую стройную упаковку. Если мы используем корректно, то проблема определения групп, которые находятся в пределах диапазона обнаружения, является чем-то вроде...

import shapely
import shapely.geometry

#-- Build a polygon envelope around the members of the group.
pointsA = shapely.geometry.MultiPoint(map(lambda r: r.center, A.sprites( )))
pointsB = shapely.geometry.MultiPoint(map(lambda r: r.center, B.sprites( )))

#-- Ask shapely to give the minimum distance between the hulls that
#-- encompass the two groups.
distance = pointsA.convex_hull.distance(pointsB.convex_hull)

if distance < RANGE_OF_DETECTION:
   detected = True
else:
   detected = False

Обратите внимание, что я не тестировал приведенный выше код... это только для демонстрации общей идеи использования библиотеки shapely для помощи в вычислениях геометрии.

Если вы новичок в программировании игр, вам также может понадобиться использовать дерево квадратов в качестве средства сокращения объема вычислений геометрии.

Другие вопросы по тегам