如何检测物体是否在视线范围内#34;使用math.atan2函数的另一个对象?

时间:2016-10-28 02:47:27

标签: python pygame trigonometry

我在尝试理解如何检测"时遇到严重问题。如果有什么东西在假想的视线中#34;玩家我创造了一个简单的墙。想法是在玩家瞄准墙壁并同时点击鼠标点击按钮时打印一些东西。

这是我的代码:

import sys
import pygame
import math
pygame.init()

screen_width = 640
screen_height = 480
screen = pygame.display.set_mode((screen_width, screen_height))
running = True

class Actor:

    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

class Wall:

    def __init__(self):
        Actor.__init__(self, 300, 100, 128, 32)
        self.surface = pygame.Surface((self.w, self.h))
        self.surface.fill((0, 0, 0))

    def draw(self):
        screen.blit(self.surface, (self.x, self.y))

class Player(Actor):

    def __init__(self):
        Actor.__init__(self, 0, 0, 64, 64)
        self.surface = pygame.transform.scale(pygame.image.load("GFX/player.png"), (self.w, self.h))
        self.rotated_surface = self.surface.copy()
        self.rect = self.surface.get_rect()
        self.directions = [False, False, False, False]
        self.speed = 0.1
        self.running = False

    def rotate(self):
        mouse_x = pygame.mouse.get_pos()[0]
        mouse_y = pygame.mouse.get_pos()[1]
        angle = math.atan2(mouse_y - (self.y + (self.w / 2)), mouse_x - (self.x + (self.w / 2)))
        angle = angle * (180 / math.pi)
        rot_image = pygame.transform.rotozoom(self.surface, -angle + 270, 1)
        rot_rect = self.rect.copy()
        rot_rect.center = rot_image.get_rect().center
        self.rotated_surface = rot_image

    def move(self):
        if self.directions[0]:
            self.y -= self.speed
        if self.directions[1]:
            self.y += self.speed
        if self.directions[2]:
            self.x -= self.speed
        if self.directions[3]:
            self.x += self.speed
        if self.running:
            self.speed = 0.2
        else:
            self.speed = 0.1

    def draw(self):
        screen.blit(self.rotated_surface, (self.x, self.y))
        pygame.draw.aaline(screen, (0, 255, 0), (player.x + (player.w / 2), player.y), pygame.mouse.get_pos())

    def fire(self, actor):
        bullet_x_pos = self.x + (self.w / 2)
        bullet_y_pos = self.y
        # ...

player = Player()
wall = Wall()

def redraw():
    screen.fill((75, 0, 0))
    player.draw()
    player.move()
    wall.draw()
    pygame.display.flip()

while (running):
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            sys.exit()
        elif e.type == pygame.KEYDOWN:
            if e.key == pygame.K_ESCAPE:
                sys.exit()
            if e.key == pygame.K_w:
                player.directions[0] = True
            if e.key == pygame.K_s:
                player.directions[1] = True
            if e.key == pygame.K_a:
                player.directions[2] = True
            if e.key == pygame.K_d:
                player.directions[3] = True
            if e.key == pygame.K_LSHIFT:
                player.running = True
        elif e.type == pygame.KEYUP:
            if e.key == pygame.K_w:
                player.directions[0] = False
            if e.key == pygame.K_s:
                player.directions[1] = False
            if e.key == pygame.K_a:
                player.directions[2] = False
            if e.key == pygame.K_d:
                player.directions[3] = False
            if e.key == pygame.K_LSHIFT:
                player.running = False
        elif e.type == pygame.MOUSEMOTION:
            player.rotate()
        elif e.type == pygame.MOUSEBUTTONDOWN:
            print("Yep")

    redraw()

这是图像:

enter image description here

网上关于它的资料不足,特别是对于Python / Pygame(我猜摄影师在图书馆中并不常见)。我不知道如何根据atan2返回的角度返回一个布尔值,告诉我玩家瞄准的是对象。

1 个答案:

答案 0 :(得分:1)

Python的atan2有点神奇,人们不必为了获得完整的360度角而使用“x”和“y”方向的符号。 Ant Python的数学模型甚至有一个函数可以将弧度结果转换回度数(然后,只将负数加360到0:360范围而不是-180:180)。

考虑到这一点,只需要小心地确定位置 - 您可以通过检查所有矩形角方向并选择最多来检查矩形的“视线”相对于给定位置的最小和最大角度极端值。 也就是说,由于发现了循环值,有很多极端情况 - 我想我已经覆盖了所有这些。

class Actor(object):
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

    def __len__(self):
        return 4

    def __getitem__(self, item):
        return {0: self.x, 1: self.y, 2: self.w, 3: self.h}[item]


def get_direction_range(rect, position):
    min_d = 181
    max_d = -181
    # Due to the cases where the target rect crosses the 180, -180 line, always keep the bottom corners last:
    for corner in (('top', 'left'), ('top', 'right'), ('bottom', 'left'), ('bottom', 'right')):
        tx, ty = getattr(rect, corner[1]), getattr(rect, corner[0])
        print(tx, ty)
        # Calculate vector from given position to target corner
        vx = tx - position[0]
        vy = -(ty - position[1])

        direction = math.degrees(math.atan2(vy, vx))
        if direction < min_d and (not (direction < 0 and min_d > 0 and min_d > 90) or min_d == 181) :
            min_d = direction
        if direction > max_d or (direction < 0 and max_d > 0 and max_d > 90):
            print(direction, "max")
            max_d = direction
    min_d += 0 if min_d > 0 else 360
    max_d += 0 if max_d > 0 else 360
    return min_d, max_d


def check_target(direction, position, rect):
    if not isinstance(rect, pygame.Rect):
        rect = pygame.Rect(rect)
    if position in rect:
        return True

    min_d, max_d = get_direction_range(rect, position)
    if max_d < min_d:
        max_d, min_d = min_d, max_d
    if abs(max_d - min_d) > 180:
        return max_d <= direction <= 360 or 0 <= direction <= min_d
    return min_d <= direction <= max_d


def dl(scr, pos, direction):
    xx = math.cos(math.radians(direction)) * 200
    yy = -math.sin(math.radians(direction)) * 200
    pygame.draw.line(scr, (0,255,0), pos, (pos[0] + xx, pos[1] + yy))
    pygame.display.flip()

你可以让那个“Actor”类派生自pygame.Rect(如果你不能使用Rect本身) - 我在它上面添加了方法,所以它至少可以转换为Rect,这样我们就可以轻松地选择角落坐标。 无论如何,请注意,如果您使用的是Python 2.x(我相信,由于在Python3下安装pygame很困难),所有类应该继承自object。< / p>

我以前在活动会话中调试的dl函数 - 它对你也很有用。