Python Pygame中的2D光线投射和直线碰撞(视线、障碍物)
我的新程序员有三个月的Python经验(只有几周的pygame) 我正在设计一个2-D自上而下的游戏,并且一直在尝试设置障碍物来打破精灵视轴。我目前的尝试是让每个精灵向可视范围内的其他精灵投射一条光线,如果光线与视轴拦截器的rect发生碰撞,则停止,并仅将可见精灵添加到列表中 到目前为止,精灵们总是隔着墙互相看。我认为这是一个迭代/缩进/太多循环的问题。我尝试过布尔类型的中断,并将代码放入一个带有return的函数中(在代码中的几个不同点),但没有帮助——怪物总是直奔玩家,直到他们撞到墙为止 图片: 几天来,谷歌一直在进行研究和反复尝试,但毫无乐趣可言。任何解决方案或推动正确的方向非常感谢 相关代码如下 墙类 实施Python Pygame中的2D光线投射和直线碰撞(视线、障碍物),python,pygame,2d,collision-detection,raycasting,Python,Pygame,2d,Collision Detection,Raycasting,我的新程序员有三个月的Python经验(只有几周的pygame) 我正在设计一个2-D自上而下的游戏,并且一直在尝试设置障碍物来打破精灵视轴。我目前的尝试是让每个精灵向可视范围内的其他精灵投射一条光线,如果光线与视轴拦截器的rect发生碰撞,则停止,并仅将可见精灵添加到列表中 到目前为止,精灵们总是隔着墙互相看。我认为这是一个迭代/缩进/太多循环的问题。我尝试过布尔类型的中断,并将代码放入一个带有return的函数中(在代码中的几个不同点),但没有帮助——怪物总是直奔玩家,直到他们撞到墙为止 图
wall = GraphBoxSprite('wall', (WIDTH // 2 - 25, HEIGHT // 2 - 10), 50, 20, BLACK, False, walls, obstacles, losBlockers, allSprites)
character = CreatureSprite('character', (WIDTH // 2, HEIGHT // 2 - 50), MEDIUM, QUICK, QUIET, BLUE_LINE, CANDLELIGHT, None, None, characters, allCreatures, allSprites)
代码
class GraphBoxSprite(pygame.sprite.Sprite):
# Creates a sprite the size and shape of supplied graph size
def __init__(self, ID, position, squareWidth, squareHeight, colour, transparent=False, *groups):
super().__init__(*groups)
self.ID = ID
self.position = position
self.image = pygame.Surface((squareWidth, squareHeight)) # Blank surface
self.rect = self.image.get_rect(topleft=position)
self.image.fill(colour)
if transparent is True:
self.image.set_colorkey(colour) # Transparent background
class CreatureSprite(pygame.sprite.Sprite):
# Creates a sprite to order
def __init__(self, ID, position, size, speed, noiseLevel=None, colour=GREY, lightRadius=None, infravision=None, image=None, *groups):
super().__init__(*groups)
self.image = pygame.Surface(size) # Blank surface for image
self.image.fill(WHITE)
self.image.set_colorkey(WHITE) # Transparent background
self.rect = self.image.get_rect(topleft=position)
self.ID = ID
self.size = size
self.colour = colour
self.lightRadius = lightRadius
self.losLength = 600 # view distance
self.losWidth = 150 # degrees
self.position = Vector2(position)
self.speed = int(speed / (GRID_SCALE * 10))
self.velocity = Vector2(0, 0)
self.destination = Vector2(position)
self.destinationRadius = 40
self.heading = self.destination - self.position
def update(self, walls, dungeonDressings, obstacles, losBlockers, monsters, characters, allCreatures, dungeonLights, creatureLights, allLights, allSprites):
# Draw the creature
pygame.draw.circle(self.image, self.colour, (self.size[0] // 2, self.size[1] // 2), self.size[0] // 2)
pygame.draw.line(self.image, WHITE, (self.size[0] // 2, self.size[1] // 2), (self.size[0] // 2, self.size[1] - 20), 1)
# Draw light over darkness and colour over light
if self.lightRadius:
spritePosition = (int(self.position[0]), int(self.position[1])) # syntactic sugar
pygame.draw.circle(DARKNESS, (COLOURKEY), spritePosition, self.lightRadius * GRID_SCALE)
pygame.draw.circle(LIGHTNESS, (GOLDENROD), spritePosition, self.lightRadius * GRID_SCALE)
# Movement
self.position += self.velocity # Update the position vector first
self.rect.center = self.position # Update the rect afterwards
# This vector points to the destination
self.heading = self.destination - self.position
distance = self.heading.length()
# Normalize heading for scale to desired length/speed below
if self.heading: # Cannot normalize a zero vector
self.heading.normalize_ip()
# Sprite rotation to heading
#self.image = pygame.transform.rotate(self.image, self.heading) --- won't accept Vector2, only real number ---
# Slow down when approaching destination
if distance > self.destinationRadius:
self.velocity = self.heading * self.speed
elif distance <= 15:
for sprite in allSprites:
if sprite in allCreatures or sprite in obstacles: # Creatures maintain personal space
self.velocity = Vector2(0, 0)
else:
self.velocity = self.heading * (distance / self.destinationRadius * self.speed)
# Line of Sight
targets = []
visible = []
seen = []
for target in allSprites:
if target != self:
targets.append(target.ID)
lineOfSight = target.position - self.position
lineOfSightDist = lineOfSight.length()
if lineOfSightDist < self.losLength: # Target in range
#if self.heading < self.losWidth: # Target in field of view --- need to convert to comparable format ---
x = self.position[0]
y = self.position[1]
for i in range(int(lineOfSightDist)): # Try to reach target with a cast ray
x += lineOfSight[0]
y += lineOfSight[1]
for sprite in allSprites:
if sprite.rect.collidepoint(int(round(x)), int(round(y))):
if sprite in losBlockers: # Stop ray
seen.append(sprite)
visible.append(target.ID)
break
else:
seen.append(sprite)
visible.append(target.ID)
break
print('{} sees {} out of {} in range'.format(self.ID, visible, targets))
# Creature AI
if self.ID == 'monster':
if seen:
for sprite in seen:
if sprite.ID == 'character':
self.destination = sprite.position
# When sprites collide
for sprite in allSprites:
if self.rect.colliderect(sprite):
if self in allCreatures and sprite in obstacles:
self.velocity = Vector2(0, 0)
def changeColour(self, colour):
self.colour = colour
生物类
实施
wall = GraphBoxSprite('wall', (WIDTH // 2 - 25, HEIGHT // 2 - 10), 50, 20, BLACK, False, walls, obstacles, losBlockers, allSprites)
character = CreatureSprite('character', (WIDTH // 2, HEIGHT // 2 - 50), MEDIUM, QUICK, QUIET, BLUE_LINE, CANDLELIGHT, None, None, characters, allCreatures, allSprites)
代码
class GraphBoxSprite(pygame.sprite.Sprite):
# Creates a sprite the size and shape of supplied graph size
def __init__(self, ID, position, squareWidth, squareHeight, colour, transparent=False, *groups):
super().__init__(*groups)
self.ID = ID
self.position = position
self.image = pygame.Surface((squareWidth, squareHeight)) # Blank surface
self.rect = self.image.get_rect(topleft=position)
self.image.fill(colour)
if transparent is True:
self.image.set_colorkey(colour) # Transparent background
class CreatureSprite(pygame.sprite.Sprite):
# Creates a sprite to order
def __init__(self, ID, position, size, speed, noiseLevel=None, colour=GREY, lightRadius=None, infravision=None, image=None, *groups):
super().__init__(*groups)
self.image = pygame.Surface(size) # Blank surface for image
self.image.fill(WHITE)
self.image.set_colorkey(WHITE) # Transparent background
self.rect = self.image.get_rect(topleft=position)
self.ID = ID
self.size = size
self.colour = colour
self.lightRadius = lightRadius
self.losLength = 600 # view distance
self.losWidth = 150 # degrees
self.position = Vector2(position)
self.speed = int(speed / (GRID_SCALE * 10))
self.velocity = Vector2(0, 0)
self.destination = Vector2(position)
self.destinationRadius = 40
self.heading = self.destination - self.position
def update(self, walls, dungeonDressings, obstacles, losBlockers, monsters, characters, allCreatures, dungeonLights, creatureLights, allLights, allSprites):
# Draw the creature
pygame.draw.circle(self.image, self.colour, (self.size[0] // 2, self.size[1] // 2), self.size[0] // 2)
pygame.draw.line(self.image, WHITE, (self.size[0] // 2, self.size[1] // 2), (self.size[0] // 2, self.size[1] - 20), 1)
# Draw light over darkness and colour over light
if self.lightRadius:
spritePosition = (int(self.position[0]), int(self.position[1])) # syntactic sugar
pygame.draw.circle(DARKNESS, (COLOURKEY), spritePosition, self.lightRadius * GRID_SCALE)
pygame.draw.circle(LIGHTNESS, (GOLDENROD), spritePosition, self.lightRadius * GRID_SCALE)
# Movement
self.position += self.velocity # Update the position vector first
self.rect.center = self.position # Update the rect afterwards
# This vector points to the destination
self.heading = self.destination - self.position
distance = self.heading.length()
# Normalize heading for scale to desired length/speed below
if self.heading: # Cannot normalize a zero vector
self.heading.normalize_ip()
# Sprite rotation to heading
#self.image = pygame.transform.rotate(self.image, self.heading) --- won't accept Vector2, only real number ---
# Slow down when approaching destination
if distance > self.destinationRadius:
self.velocity = self.heading * self.speed
elif distance <= 15:
for sprite in allSprites:
if sprite in allCreatures or sprite in obstacles: # Creatures maintain personal space
self.velocity = Vector2(0, 0)
else:
self.velocity = self.heading * (distance / self.destinationRadius * self.speed)
# Line of Sight
targets = []
visible = []
seen = []
for target in allSprites:
if target != self:
targets.append(target.ID)
lineOfSight = target.position - self.position
lineOfSightDist = lineOfSight.length()
if lineOfSightDist < self.losLength: # Target in range
#if self.heading < self.losWidth: # Target in field of view --- need to convert to comparable format ---
x = self.position[0]
y = self.position[1]
for i in range(int(lineOfSightDist)): # Try to reach target with a cast ray
x += lineOfSight[0]
y += lineOfSight[1]
for sprite in allSprites:
if sprite.rect.collidepoint(int(round(x)), int(round(y))):
if sprite in losBlockers: # Stop ray
seen.append(sprite)
visible.append(target.ID)
break
else:
seen.append(sprite)
visible.append(target.ID)
break
print('{} sees {} out of {} in range'.format(self.ID, visible, targets))
# Creature AI
if self.ID == 'monster':
if seen:
for sprite in seen:
if sprite.ID == 'character':
self.destination = sprite.position
# When sprites collide
for sprite in allSprites:
if self.rect.colliderect(sprite):
if self in allCreatures and sprite in obstacles:
self.velocity = Vector2(0, 0)
def changeColour(self, colour):
self.colour = colour
class CreatureSprite(pygame.sprite.sprite):
#按顺序创建精灵
def _初始__(自身、ID、位置、大小、速度、噪音级别=无、颜色=灰色、光半径=无、红外线=无、图像=无、*组):
超级()
self.image=pygame.Surface(大小)#图像的空白表面
self.image.fill(白色)
self.image.set_colorkey(白色)#透明背景
self.rect=self.image.get_rect(左上方=位置)
self.ID=ID
self.size=大小
颜色
self.lightRadius=lightRadius
self.losLength=600#视距
self.losWidth=150度
self.position=Vector2(位置)
self.speed=int(速度/(网格刻度*10))
self.velocity=Vector2(0,0)
self.destination=Vector2(位置)
self.destinationRadius=40
self.heading=self.destination-self.position
def更新(自我、墙壁、地下城服装、障碍物、丢失拦截器、怪物、角色、所有生物、地下城灯光、创造者灯光、所有灯光、所有精灵):
#画生物
pygame.draw.circle(self.image,self.color,(self.size[0]//2,self.size[1]//2),self.size[0]//2)
pygame.draw.line(self.image,白色,(self.size[0]//2,self.size[1]//2),(self.size[0]//2,self.size[1]-20),1)
#在黑暗中画光明,在光明中画色彩
如果self.lightRadius:
spritePosition=(int(self.position[0]),int(self.position[1])#语法糖
pygame.draw.circle(黑暗,(彩色键),精灵位置,self.lightRadius*栅格刻度)
pygame.draw.circle(亮度,(黄花)、精灵位置、自发光半径*栅格刻度)
#运动
self.position+=self.velocity#首先更新位置向量
self.rect.center=self.position#之后更新rect
#这个向量指向目的地
self.heading=self.destination-self.position
距离=self.heading.length()
#将标度的航向标准化为以下所需的长度/速度
如果self.heading:#无法规范化零向量
self.heading.normalize_ip()
#精灵旋转到航向
#self.image=pygame.transform.rotate(self.image,self.heading)--不接受矢量2,只接受实数---
#接近目的地时减速
如果距离>自身目标半径:
自速度=自航向*自速度
elif distance我想我在这里有办法做到这一点。
这是你想要的吗
import pygame
import random
Randomx = random.randint(1,450)
Randomy = random.randint(1,450)
Wallx = Randomx - 50
Wally = Randomy
screen = pygame.display.set_mode([500,500])
x = 225
y = 225
running = True
while running:
keys = pygame.key.get_pressed()
screen.fill([255,255,255])
if keys[pygame.K_LEFT]:
#move
x -= .2
#make shure it isn't off the screen
if x <= 0:
x = 0
if keys[pygame.K_RIGHT]:
x += .2
if x >= 450:
x = 450
if keys[pygame.K_UP]:
y -= .2
if y <= 0:
y = 0
if keys[pygame.K_DOWN]:
y += .2
if y >= 450:
y = 450
#Red's Vision
vision1 = pygame.draw.rect(screen, [0,0,0], (0,y,500,50))
vision2 = pygame.draw.rect(screen, [0,0,0], (x,0,50,500))
#-----------------------------------------------
#Red
red = pygame.draw.rect(screen, [255,0,0], (x,y,50,50))
#Blue
blue = pygame.draw.rect(screen, [0,0,255], (Randomx,Randomy,50,50))
#Wall
wall = pygame.draw.rect(screen, [0,255,0], (Wallx,Wally,25,50))
#Collisoin Detection/Vision
if vision1.colliderect(blue):
SeeBlock = True
if x >= Randomx:
w = x - Randomx
if x <= Randomx:
w = Randomx - x
if x >= Randomx:
vision3 = pygame.draw.rect(screen, [0,0,0], (x,y,-w,50))
if vision3.colliderect(wall):
SeeBlock = False
if x <= Randomx:
vision3 = pygame.draw.rect(screen, [0,0,0], (x,y,w,50))
if vision3.colliderect(wall):
SeeBlock = False
red = pygame.draw.rect(screen, [255,0,0], (x,y,50,50))
blue = pygame.draw.rect(screen, [0,0,255], (Randomx,Randomy,50,50))
wall = pygame.draw.rect(screen, [0,255,0], (Wallx,Wally,25,50))
if SeeBlock == True:
print("I SEE IT!")
if vision2.colliderect(blue):
SeeBlock = True
if y >= Randomx:
w = y - Randomy
if y <= Randomx:
w = y + Randomy
if y >= Randomy:
vision3 = pygame.draw.rect(screen, [0,0,0], (x,y,-w,50))
if vision3.colliderect(wall):
SeeBlock = False
if y <= Randomy:
vision3 = pygame.draw.rect(screen, [0,0,0], (x,y,w,50))
if vision3.colliderect(wall):
SeeBlock = False
red = pygame.draw.rect(screen, [255,0,0], (x,y,50,50))
blue = pygame.draw.rect(screen, [0,0,255], (Randomx,Randomy,50,50))
wall = pygame.draw.rect(screen, [0,255,0], (Wallx,Wally,25,50))
if SeeBlock == True:
print("I SEE IT!")
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
pygame.quit()
导入pygame
随机输入
Randomx=random.randint(1450)
Randomy=random.randint(1450)
Wallx=Randomx-50
沃利=随机
screen=pygame.display.set_模式([500500])
x=225
y=225
运行=真
运行时:
keys=pygame.key.get_pressed()
屏幕填充([255255])
如果键[pygame.K_左]:
#移动
x-=0.2
#让舒尔不在屏幕上
如果x=450:
x=450
如果键[pygame.K_UP]:
y-=0.2
如果y=450:
y=450
#红色的愿景
vision1=pygame.draw.rect(屏幕,[0,0,0],(0,y,500,50))
vision2=pygame.draw.rect(屏幕,[0,0,0],(x,0,50500))
#-----------------------------------------------
#红色的
红色=pygame.draw.rect(屏幕,[255,0,0],(x,y,50,50))
#蓝色的
蓝色=pygame.draw.rect(屏幕[0,0255],(随机X,随机Y,50,50))
#墙
wall=pygame.draw.rect(屏幕[0255,0],(Wallx,Wally,25,50))
#碰撞检测/视觉
如果vision1.colliderect(蓝色):
SeeBlock=True
如果x>=Randomx:
w=x-x
如果x=Randomx:
vision3=pygame.draw.rect(屏幕,[0,0,0],(x,y,-w,50))
如果vision3.colliderect(墙):
SeeBlock=False
如果x=Randomx:
w=y-随机性
如果y=Randomy:
vision3=pygame.draw.rect(屏幕,[0,0,0],(x,y,-w,50))
如果vision3.colliderect(墙):
SeeBlock=False
如果仅仅从静态代码的一部分我看不出你的程序为什么不能工作,那么看起来应该是这样的
但是,我可以提供自己的解决方案:
对于每个NPC/对手:
- 计算每个玩家和玩家之间的线。这里我们只使用精灵矩形的中心,因为它很简单。如果你喜欢的话,可以把它想象成“射线”
- 确定是否有任何墙/阻挡对象位于该线/光线上。
- 如果是这样,视线就会中断,我们可以停止检查
所以视线就是(Player-x,Player-y)到(NPC-x,NPC-y),定义了一条线
每个墙对象