简体   繁体   English

如何防止精灵相互重叠

[英]How to prevent sprites from going on top of each other

So I'm not that experienced with Pygame and I'd like to how to prevent enemies from overlapping.所以我对 Pygame 不是很有经验,我想知道如何防止敌人重叠。 I've tried a few methods with little to no success.我尝试了一些方法,但几乎没有成功。 I'd like to hear your ideas on how I could do such a thing.我想听听你对我如何做这样的事情的想法。 I think a good idea would be through collision detection.我认为一个好主意是通过碰撞检测。 Yet I am not quite sure how that would work.然而,我不太确定这将如何运作。

Here's the enemies code:这是敌人的代码:

class Enemy:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def draw(self):
        global characterradius, characterradius1, characterradius2, characterradius3, characterradius4, characterradius5
        global characterradius6, characterradius7, estanding, emoveup, emovedown, emoveleft, emoveright, enemyradiusshow
        global eradius1, eradius2, eradius3, eradius4

        self.enemyradiusshow = True
        self.estanding = False
        emoveup = False
        emovedown = False
        emoveleft = False
        emoveright = False

        if moveup:
            self.y += speed
        if movedown:
            self.y -= speed
        if moveleft:
            self.x += speed
        if moveright:
            self.x -= speed

        enemyhitbox = pygame.Rect(self.x + 6, self.y + 5, 50, 80)
        self.enemyhitbox = pygame.Rect(self.x + 6, self.y + 5, 50, 80)
        enemyhitboxvisible = True
        self.enemyhitboxvisible = False
        playerradiusvisible = False

        if self.enemyhitboxvisible:
            pygame.draw.rect(win, (255, 255, 255), (self.x + 6, self.y + 5, 50, 80))
        if enemyhitboxvisible:
            pygame.draw.rect(win, (255, 255, 255), (self.x + 6, self.y + 5, 50, 80))

        if playerradiusvisible:
            pygame.draw.rect(win, (80, 200, 50), (character_x - 180, character_y - 180, 150.5, 150.5))
            pygame.draw.rect(win, (200, 80, 50), (character_x + 80, character_y - 180, 150.5, 150.5))
            pygame.draw.rect(win, (80, 50, 200), (character_x - 180, character_y + 90, 150.5, 150.5))
            pygame.draw.rect(win, (200, 150, 50), (character_x + 80, character_y + 90, 150.5, 150.5))

            pygame.draw.rect(win, (150, 150, 50), (character_x - 180, character_y - 30, 150.5, 120))
            pygame.draw.rect(win, (200, 70, 50), (character_x + 80, character_y - 30, 150.5, 120))
            pygame.draw.rect(win, (50, 150, 200), (character_x - 30, character_y - 180, 110, 150.5))
            pygame.draw.rect(win, (50, 150, 200), (character_x - 30, character_y + 90, 110, 150.5))

        characterradius = pygame.Rect(character_x - 180, character_y - 180, 150.5, 150.5)
        characterradius1 = pygame.Rect(character_x + 80, character_y - 180, 150.5, 150.5)
        characterradius2 = pygame.Rect(character_x - 180, character_y + 90, 150.5, 150.5)
        characterradius3 = pygame.Rect(character_x + 80, character_y + 90, 150.5, 150.5)
        character_hitbox = pygame.Rect(character_x + 80, character_y + 90, 150.5, 150.5)

        characterradius4 = pygame.Rect(character_x - 180, character_y - 30, 150.5, 120)
        characterradius5 = pygame.Rect(character_x + 80, character_y - 30, 150.5, 120)
        characterradius6 = pygame.Rect(character_x - 30, character_y - 180, 110, 150.5)
        characterradius7 = pygame.Rect(character_x - 30, character_y + 90, 110, 150.5)

        self.enemyspeed = 1

        if characterradius.colliderect(self.enemyhitbox):
            self.x += self.enemyspeed
            self.y += self.enemyspeed
            emoveleft = True
        if characterradius1.colliderect(self.enemyhitbox):
            self.x -= self.enemyspeed
            self.y += self.enemyspeed
            emoveright = True
        if characterradius2.colliderect(self.enemyhitbox):
            self.x += self.enemyspeed
            self.y -= self.enemyspeed
            emoveleft = True
        if characterradius3.colliderect(self.enemyhitbox):
            self.x -= self.enemyspeed
            self.y -= self.enemyspeed
            emoveright = True
        if characterradius4.colliderect(self.enemyhitbox):
            self.x += self.enemyspeed
            emoveleft = True
        if characterradius5.colliderect(self.enemyhitbox):
            self.x -= self.enemyspeed
            emoveright = True
        if characterradius6.colliderect(self.enemyhitbox):
            self.y += self.enemyspeed
            emovedown = True
        if characterradius7.colliderect(self.enemyhitbox):
            self.y -= self.enemyspeed
            emoveup = True

        if emoveup:
            win.blit(zombieupimage, (self.x, self.y))
            self.emovedown = False
            self.emoveleft = False
            self.emoveright = False
            self.emoveup = True
        if emovedown:
            win.blit(zombieimage, (self.x - 5.5, self.y - 4))
            self.emovedown = True
            self.emoveleft = False
            self.emoveright = False
            self.emoveup = False
        if emoveleft:
            win.blit(zombieleftimage, (self.x, self.y))
            self.emovedown = False
            self.emoveleft = True
            self.emoveright = False
            self.emoveup = False
        if emoveright:
            win.blit(zombierightimage, (self.x, self.y))
            self.emovedown = False
            self.emoveleft = False
            self.emoveright = True
            self.emoveup = False

        if not emoveup:
            self.estanding = True
        if not emovedown:
            self.estanding = True
        if not emoveleft:
            self.estanding = True
        if not emoveright:
            self.estanding = True

        if characterradius.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius1.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius2.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius3.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius4.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius5.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius6.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius7.colliderect(self.enemyhitbox):
            self.estanding = False

Here's the full code in case you need it:如果您需要,这是完整的代码:

import pygame
import time
import os
import random

pygame.init()

# Screen Resolution
screen_width = 800
screen_height = 600

# Character Variables
character_x = 380
character_y = 250

# Background Variables
backgroundx = -375
backgroundy = -255

# Images
bgimage = pygame.image.load('bg.png')
characterimage = pygame.image.load('character.png')
characterbackimage = pygame.image.load('characterback.png')
characterleftimage = pygame.image.load('characterright.png')
characterrightimage = pygame.image.load('characterleft.png')

zombieimage = pygame.image.load('zombie.png')
zombieupimage = pygame.image.load('zombieback.png')
zombieleftimage = pygame.image.load('zombieleft.png')
zombierightimage = pygame.image.load('zombieright.png')

healthbarimage = pygame.image.load('healthbar.png')
hungerbarimage = pygame.image.load('hungerbar.png')
thirstbarimage = pygame.image.load('thirstbar.png')
energybarimage = pygame.image.load('energybar.png')

healthimage = pygame.image.load('health.png')
hungerimage = pygame.image.load('hunger.png')
thirstimage = pygame.image.load('thirst.png')

vignetteimage = pygame.image.load('vignette.png')

# Movement Fix
characterbackx = 6.5
characterbacky = 5
characterleftx = 5
characterlefty = 4
characterrightx = 7.5
characterrighty = 4.5

# Bars
healthbarx = 580
healthbary = 555
hungerbarx = 580
hungerbary = 520
thirstbarx = 580
thirstbary = 485
energybarx = 765
energybary = 430

energybardraw_x = 770
energybardraw_y = 432.5
energybardraw_width = 15
energybardraw_height = 1

thirstbardraw_x = 581
thirstbardraw_y = 490.7
thirstbardraw_width = 1
thirstbardraw_height = 15

hungerbardraw_x = 581
hungerbardraw_y = 524
hungerbardraw_width = 1
hungerbardraw_height = 15

thirsttimer = 0
hungertimer = 0
notsprintthirst = True
notsprinthunger = True
sprintthirst = False
sprinthunger = False

# Settings Variables
vignettex = 0
vignettey = 0

# Settings
vignette_on = True
vignette_off = False

# Movement
timerdown = 0
timerup = 0
timerleft = 0
timerright = 0

movingdown = True
movingleft = False
movingright = False
movingup = False

able_to_move = True

moveup = False
movedown = False
moveleft = False
moveright = False
sprint = False
speed = 2.5


def loadinscreen():

    loadingscreen = pygame.image.load('loadingscreen.png')
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.init()
    screen = pygame.display.set_mode((500, 80), pygame.NOFRAME)
    bg = pygame.Surface(screen.get_size())
    bg.fill((0, 244, 0))
    screen.blit(loadingscreen, (0, 0))
    pygame.display.update()
    time.sleep(1)

# Tiers / Level Ups


speedtier_1 = True

# Hitboxes Variables

win = pygame.display.set_mode((screen_width, screen_height))
character = win.blit(characterimage, (character_x, character_y))

class Enemy:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def draw(self):
        global characterradius, characterradius1, characterradius2, characterradius3, characterradius4, characterradius5
        global characterradius6, characterradius7, estanding, emoveup, emovedown, emoveleft, emoveright, enemyradiusshow
        global eradius1, eradius2, eradius3, eradius4

        self.enemyradiusshow = True
        self.estanding = False
        emoveup = False
        emovedown = False
        emoveleft = False
        emoveright = False

        if moveup:
            self.y += speed
        if movedown:
            self.y -= speed
        if moveleft:
            self.x += speed
        if moveright:
            self.x -= speed

        enemyhitbox = pygame.Rect(self.x + 6, self.y + 5, 50, 80)
        self.enemyhitbox = pygame.Rect(self.x + 6, self.y + 5, 50, 80)
        enemyhitboxvisible = True
        self.enemyhitboxvisible = False
        playerradiusvisible = False

        if self.enemyhitboxvisible:
            pygame.draw.rect(win, (255, 255, 255), (self.x + 6, self.y + 5, 50, 80))
        if enemyhitboxvisible:
            pygame.draw.rect(win, (255, 255, 255), (self.x + 6, self.y + 5, 50, 80))

        if playerradiusvisible:
            pygame.draw.rect(win, (80, 200, 50), (character_x - 180, character_y - 180, 150.5, 150.5))
            pygame.draw.rect(win, (200, 80, 50), (character_x + 80, character_y - 180, 150.5, 150.5))
            pygame.draw.rect(win, (80, 50, 200), (character_x - 180, character_y + 90, 150.5, 150.5))
            pygame.draw.rect(win, (200, 150, 50), (character_x + 80, character_y + 90, 150.5, 150.5))

            pygame.draw.rect(win, (150, 150, 50), (character_x - 180, character_y - 30, 150.5, 120))
            pygame.draw.rect(win, (200, 70, 50), (character_x + 80, character_y - 30, 150.5, 120))
            pygame.draw.rect(win, (50, 150, 200), (character_x - 30, character_y - 180, 110, 150.5))
            pygame.draw.rect(win, (50, 150, 200), (character_x - 30, character_y + 90, 110, 150.5))

        characterradius = pygame.Rect(character_x - 180, character_y - 180, 150.5, 150.5)
        characterradius1 = pygame.Rect(character_x + 80, character_y - 180, 150.5, 150.5)
        characterradius2 = pygame.Rect(character_x - 180, character_y + 90, 150.5, 150.5)
        characterradius3 = pygame.Rect(character_x + 80, character_y + 90, 150.5, 150.5)
        character_hitbox = pygame.Rect(character_x + 80, character_y + 90, 150.5, 150.5)

        characterradius4 = pygame.Rect(character_x - 180, character_y - 30, 150.5, 120)
        characterradius5 = pygame.Rect(character_x + 80, character_y - 30, 150.5, 120)
        characterradius6 = pygame.Rect(character_x - 30, character_y - 180, 110, 150.5)
        characterradius7 = pygame.Rect(character_x - 30, character_y + 90, 110, 150.5)

        self.enemyspeed = 1

        if characterradius.colliderect(self.enemyhitbox):
            self.x += self.enemyspeed
            self.y += self.enemyspeed
            emoveleft = True
        if characterradius1.colliderect(self.enemyhitbox):
            self.x -= self.enemyspeed
            self.y += self.enemyspeed
            emoveright = True
        if characterradius2.colliderect(self.enemyhitbox):
            self.x += self.enemyspeed
            self.y -= self.enemyspeed
            emoveleft = True
        if characterradius3.colliderect(self.enemyhitbox):
            self.x -= self.enemyspeed
            self.y -= self.enemyspeed
            emoveright = True
        if characterradius4.colliderect(self.enemyhitbox):
            self.x += self.enemyspeed
            emoveleft = True
        if characterradius5.colliderect(self.enemyhitbox):
            self.x -= self.enemyspeed
            emoveright = True
        if characterradius6.colliderect(self.enemyhitbox):
            self.y += self.enemyspeed
            emovedown = True
        if characterradius7.colliderect(self.enemyhitbox):
            self.y -= self.enemyspeed
            emoveup = True

        if emoveup:
            win.blit(zombieupimage, (self.x, self.y))
            self.emovedown = False
            self.emoveleft = False
            self.emoveright = False
            self.emoveup = True
        if emovedown:
            win.blit(zombieimage, (self.x - 5.5, self.y - 4))
            self.emovedown = True
            self.emoveleft = False
            self.emoveright = False
            self.emoveup = False
        if emoveleft:
            win.blit(zombieleftimage, (self.x, self.y))
            self.emovedown = False
            self.emoveleft = True
            self.emoveright = False
            self.emoveup = False
        if emoveright:
            win.blit(zombierightimage, (self.x, self.y))
            self.emovedown = False
            self.emoveleft = False
            self.emoveright = True
            self.emoveup = False

        if not emoveup:
            self.estanding = True
        if not emovedown:
            self.estanding = True
        if not emoveleft:
            self.estanding = True
        if not emoveright:
            self.estanding = True

        if characterradius.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius1.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius2.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius3.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius4.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius5.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius6.colliderect(self.enemyhitbox):
            self.estanding = False
        if characterradius7.colliderect(self.enemyhitbox):
            self.estanding = False

        if self.estanding:
            win.blit(zombieimage, (self.x, self.y))

        if self.enemyradiusshow:
            pygame.draw.rect(win, (255, 0, 0), (self.x - 11, self.y + 40, 15, 15))
            pygame.draw.rect(win, (0, 255, 0), (self.x + 23.5, self.y + 87, 15, 15))
            pygame.draw.rect(win, (0, 0, 255), (self.x + 58, self.y + 40, 15, 15))
            pygame.draw.rect(win, (155, 155, 155), (self.x + 23.5, self.y - 12, 15, 15))

        eradius1 = pygame.Rect (self.x - 11, self.y + 40, 15, 15)
        eradius2 = pygame.Rect (self.x + 23.5, self.y + 87, 15, 15)
        eradius3 = pygame.Rect (self.x + 58, self.y + 40, 15, 15)
        eradius4 = pygame.Rect (self.x + 23.5, self.y - 12, 15, 15)




enemies = []
randomposx = 500
randomposy = 500
max_enemies = 10
milliseconds_delay = 1
spawn_event = pygame.USEREVENT + 1
enemie_size = zombieimage.get_size()

pygame.time.set_timer(spawn_event, milliseconds_delay)
run = True

while run:

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

        elif event.type == spawn_event:
            if len(enemies) < max_enemies:
                x = random.randint(1, randomposx - enemie_size[0])
                y = random.randint(1, randomposy - enemie_size[1])
                e = Enemy(x, y)
                enemies.append(e)

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_w:
                moveup = True
            elif event.key == pygame.K_s:
                movedown = True
            elif event.key == pygame.K_a:
                moveleft = True
            elif event.key == pygame.K_d:
                moveright = True
            elif event.key == pygame.K_LSHIFT:
                sprint = True
        if event.type == pygame.KEYUP:
            if able_to_move:
                if event.key == pygame.K_w:
                    moveup = False
                elif event.key == pygame.K_s:
                    movedown = False
                elif event.key == pygame.K_a:
                    moveleft = False
                elif event.key == pygame.K_d:
                    moveright = False
                elif event.key == pygame.K_LSHIFT:
                    sprint = False

    keys = pygame.key.get_pressed()
    pygame.display.update()

    # Hitboxes

    def background():
        win.blit(bgimage, (backgroundx, backgroundy))

    background()
    for e in enemies:
        e.draw()

    def vignette():
        if vignette_on:
            win.blit(vignetteimage, (vignettex, vignettey))

    vignette()

    class Bars:
        win.blit(healthbarimage, (healthbarx, healthbary))
        win.blit(hungerbarimage, (hungerbarx, hungerbary))
        win.blit(thirstbarimage, (thirstbarx, thirstbary))
        win.blit(energybarimage, (energybarx, energybary))

        win.blit(healthimage, (735, 556.2))
        win.blit(hungerimage, (735, 520.5))
        win.blit(thirstimage, (733.5, 486.6))

    def drawbars():
        pygame.draw.rect(win, (0, 0, 0), (energybardraw_x, energybardraw_y, energybardraw_width, energybardraw_height))
        pygame.draw.rect(win, (0, 0, 0), (thirstbardraw_x, thirstbardraw_y, thirstbardraw_width, thirstbardraw_height))
        pygame.draw.rect(win, (0, 0, 0), (hungerbardraw_x, hungerbardraw_y, hungerbardraw_width, hungerbardraw_height))

    drawbars()

    def hitboxvisible():
        pygame.draw.rect(win, (200, 10, 50), (character_x, character_y, 50, 80))

    if movingdown:
        character = win.blit(characterimage, (character_x, character_y))
    if movingleft:
        characterleft = win.blit(characterleftimage, (character_x + characterleftx, character_y + characterlefty))
    if movingright:
        characterright = win.blit(characterrightimage, (character_x + characterrightx, character_y + characterrighty))
    if movingup:
        characterback = win.blit(characterbackimage, (character_x + characterbackx, character_y + characterbacky))

    if moveup:
        goingdown = False
        movingleft = False
        movingright = False
        movingup = True
        backgroundy += speed
    if movedown:
        movingleft = False
        movingright = False
        movingup = False
        movingdown = True
        backgroundy -= speed
    if moveleft:
        movingdown = False
        movingleft = True
        movingright = False
        movingup = False
        backgroundx += speed
    if moveright:
        movingdown = False
        movingleft = False
        movingright = True
        movingup = False
        backgroundx -= speed

    def energybar_gain():
        global energybardraw_height
        energybardraw_height -= 0.8

    if speedtier_1:
        if sprint:
            speed = 4
            energybardraw_height += 2
            sprintthirst = True
            notsprintthirst = False
            sprinthunger = True
            notsprinthunger = False
        if not sprint:
            speed = 3
            energybar_gain()
            sprintthirst = False
            notsprintthirst = True
            sprinthunger = False
            notsprinthunger = True

        if energybardraw_height < 0:
            energybardraw_height = 1
        if energybardraw_height > 146:
            energybardraw_height = 146
            sprint = False

    if notsprintthirst:
        thirsttimer += 0.5
    if sprintthirst:
        thirsttimer += 1

    if thirsttimer == 150:
        thirstbardraw_width += 2
    if thirsttimer > 150:
        thirsttimer = 0

    if notsprinthunger:
        hungertimer += 0.5
    if sprinthunger:
        hungertimer += 1
    if hungertimer == 175:
        hungerbardraw_width += 2
    if hungertimer > 175:
        hungertimer = 0

    if moveup or movedown or moveleft or moveright:
        timerdown += 1
        if timerdown > 12:
            timerdown = 0

        if timerdown == 1:
            character_y -= 1
        if timerdown == 2:
            character_y -= 1
        if timerdown == 3:
            character_y -= 1
        if timerdown == 4:
            character_y -= 1

        if timerdown == 5:
            character_y += 1
        if timerdown == 6:
            character_y += 1
        if timerdown == 7:
            character_y += 1
        if timerdown == 8:
            character_y += 1

    # Zombies Only

    pygame.display.update()

This is more of a game design question, but typically to prevent sprites from overlapping you detect collision and then set their position to a place that is just not overlapping.这更像是一个游戏设计问题,但通常是为了防止精灵重叠,您检测到碰撞,然后将它们的position设置为不重叠的位置。 You might initially think of this as janky teleportation, but if collisions are being detected 30 times every second (framerate), the tiny distance that they teleport will be invisible to the player.您可能最初认为这是 janky 瞬移,但如果每秒检测到 30 次碰撞(帧率),那么玩家将看不到它们瞬移的微小距离。

The easiest way to detect collision is to give them a circular hitbox specified by a radius.检测碰撞最简单的方法是给它们一个由半径指定的圆形碰撞箱。 This isn't a perfect solution, but it will get you on track to optimize and think of a more optimal way that will work for you.这不是一个完美的解决方案,但它会让您走上优化的轨道,并想出一种更适合您的最佳方式。

As a side note, I'm not familiar with PyGame, but I'm sure they have some sort of library for Vector math.作为旁注,我不熟悉 PyGame,但我确信他们有某种矢量数学库。 Vector2 is essentially an array of [self.xPos, self.yPos] with some extra helper functions, like finding distance using the Pythagorean theorem. Vector2 本质上是一个 [self.xPos, self.yPos] 数组,带有一些额外的辅助函数,例如使用勾股定理求距离。

The half-pseudocode/half-python looks somewhat like this:半伪代码/半蟒蛇看起来有点像这样:

for enemy1 in enemies: 
    for enemy2 in enemies:
        if enemy1 == enemy2: #if both are the same enemy
            continue
        # Find the dist between both enemies
        # *Assumes sprite anchor is in center
        distanceBetween = Vector2.DistanceBetween(enemy1, enemy2)  
        combinedHitbox = enemy1.radius + enemy2.radius

        if combinedHitbox < distanceBetween:
            # Calculate the delta position (difference between)
            positionDiff = enemy2.position - enemy1.position
            # Move them away from each other half of the distance between them.
            enemy2.position += positionDiff /2
            enemy1.position -= positionDiff /2

PyGame might also have some better way for hitbox detection that will do this behind the scenes, but I think its important that all game developers should at least once try to implement their own just to see how it works. PyGame 可能还有一些更好的碰撞盒检测方法,可以在幕后执行此操作,但我认为所有游戏开发人员至少应该尝试实现一次自己的测试以了解它是如何工作的,这一点很重要。

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

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