简体   繁体   English

两个精灵互相发现

[英]Two sprites spotting each other

Hello again Stack Overflow. 您好再次堆栈溢出。 you probably remember me from my unit spawning problem in my pygame program, "Table Wars." 您可能从pygame程序“ Table Wars”中的单元生成问题中记得我。 I decided to change the scope of my game to a real-time strategy rather than a turn-based game. 我决定将游戏范围改为实时策略,而不是回合制游戏。 I want the game to play along the lines of top Flash game: "Age of War." 我希望游戏能够按照Flash游戏的最高水平:“战争年代”进行游戏。 Almost everything works in the game: spawning units, the HUD for the game, and even base health. 游戏中几乎所有的东西都起作用:生成单元,游戏的HUD甚至基础生命值。 Unfortunately, I can't seem to figure out how to implement the ability for units to attack enemies or the enemy base. 不幸的是,我似乎无法弄清楚如何实现部队攻击敌人或敌人基地的能力。 Here is the concepts going on for the units themselves: 这是单元本身正在进行的概念:

  • The unit spawns on a keypress around the team's base: K_1 spawns a sprite from the Red_Infantry class 该单位在围绕团队基地的按键上生成: K_1Red_Infantry类生成一个Sprite
  • The unit, when spawned, is added to a Group class. 生成该单元后,会将其添加到Group类。 There are two Group s, one for each team. 有两个Group ,每个团队一个。
  • The unit moves via a move_ip call within a def update until it reaches a point close to the enemy base, where it stops. 该单位在def update通过move_ip调用移动,直到到达靠近敌人基地的地点为止。

Here is how I want combat to go for the different units: 这是我希望战斗适用于不同单位的方式:

  • The unit stops whenever it spots an enemy within it's attack range. 只要发现攻击范围内的敌人,该部队就会停止。 Different units have different attack ranges 不同的单位有不同的攻击范围
  • The unit then attacks in one-second intervals. 然后,部队每隔一秒发动一次进攻。
  • If the unit sucessfully reduces the enemy unit's health to 0, the enemy dies, and the other may continue 如果单位成功将敌方单位的生命值降低至0,敌方死亡,另一方可能继续
  • This cycle repeats until the unit reaches the enemy base, where it will then attack the enemy base on one-second intervals. 重复此循环直到单位到达敌方基地,然后以一秒的间隔攻击敌方基地。 One of the units will be able to deal triple the normal damage to the base. 其中一个单位将能够对基地造成正常伤害的三倍。

Here is a sample of my code, showing the Red_Infantry class: 这是我的代码示例,显示了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

The main loop only contains the ability to spawn each of the units. 主循环仅包含生成每个单元的功能。

It's not entirely clear from your question how groups interact with each for spotting. 从您的问题尚不清楚,小组如何与每个小组互动以进行发现。 In the following, I'm going to assume that Group A "spots" Group B if any member a of A is within some specified range of any member b of B. 在下文中,如果A的任何成员a在B的任何成员b的指定范围内,我将假定A组“发现” B组。

The simplest way to do this would be to just iterate over all (a,b) pairs. 最简单的方法是对所有(a,b)对进行迭代。 To do this, you can use the itertools library, something like... 为此,您可以使用itertools库,例如...

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

The problem with this approach is that the computational cost is rather high. 这种方法的问题是计算成本相当高。 (It's n**2 in complexity.) Furthermore, without some kind of pruning and optimization, you have to run this block of code for each pair of friendly/enemy group, too. (复杂度为n ** 2。)此外,如果不进行任何修剪和优化,则也必须为每对友好/敌对组运行此代码块。 Now, if each group is guaranteed to have some constant geometry, then we can make the cost of computing MUCH cheaper. 现在,如果保证每个组都具有恒定的几何形状,那么我们可以使MUCH的计算成本降低。 However, assuming that groups have no fixed geometry, then I'd recommend looking into using a geometry package to do a lot of the work for you. 但是,假设组没有固定的几何图形,那么我建议您考虑使用几何图形软件包为您完成很多工作。 These packages are very powerful and very efficient...and a lot of them are also surprisingly easy to use. 这些软件包功能非常强大且非常高效……而且其中许多还非常易于使用。 There may be geometry packages specific to PyGame...I can't think of any at the moment. 可能会有特定于PyGame的几何图形包...我暂时无法想到。

I often use the shapely package . 我经常使用匀称的包装 If we use shapely, then the problem of determining groups that are within detection range is something more like... 如果我们使用得体,则确定检测范围内的组的问题更像是...

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

NOTE that I haven't tested the code above...it's only to demonstrate the general idea of using the shapely library to help with the geometry computations. 请注意,我还没有测试上面的代码...这只是演示使用形状库帮助进行几何计算的一般想法。

If you're new to game programming, you might also want to look into using quadtrees as a means of pruning how much geometry computation needs to be done. 如果您是游戏编程的新手,您可能还想研究使用四叉树作为修剪需要完成多少几何计算的一种方法。

Here's a starting point 这是一个起点

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM