[英]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.