Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Pygame中的2D光线投射和直线碰撞(视线、障碍物)_Python_Pygame_2d_Collision Detection_Raycasting - Fatal编程技术网

Python Pygame中的2D光线投射和直线碰撞(视线、障碍物)

Python Pygame中的2D光线投射和直线碰撞(视线、障碍物),python,pygame,2d,collision-detection,raycasting,Python,Pygame,2d,Collision Detection,Raycasting,我的新程序员有三个月的Python经验(只有几周的pygame) 我正在设计一个2-D自上而下的游戏,并且一直在尝试设置障碍物来打破精灵视轴。我目前的尝试是让每个精灵向可视范围内的其他精灵投射一条光线,如果光线与视轴拦截器的rect发生碰撞,则停止,并仅将可见精灵添加到列表中 到目前为止,精灵们总是隔着墙互相看。我认为这是一个迭代/缩进/太多循环的问题。我尝试过布尔类型的中断,并将代码放入一个带有return的函数中(在代码中的几个不同点),但没有帮助——怪物总是直奔玩家,直到他们撞到墙为止 图

我的新程序员有三个月的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),定义了一条线

每个墙对象