2016-10-28 3 views
3

私は、プレーヤーの想像上の「視線」に何かがあるかどうかを「検出」する方法を理解しようとすると、深刻な問題を抱えています。私は単純な壁を作りました。このアイデアは、プレーヤーが壁を狙ってマウスのクリックボタンを同時に押すと何かを印刷することです。ここでmath.atan2関数を使ってオブジェクトが別のオブジェクトの「視線」にあるかどうかを検出する方法は?

は私のコードです:

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

と言われています。これは基本的な高校の数学で、ウェブ上でも見ることができます。あなたは、あなたが理解していないことをより正確にする必要があります。 – Evert

+0

線と矩形の交差点を評価しようとしています。 'atan2'関数は必要ありません。 –

答えて

1

Pythonのatan2は、完全な360度の角度を得るために、 "x"と "y"の方向性の符号を付けて遊ぶ必要がない、魔法のようなものです。 Ant Pythonの数学モデルでは、ラジアンの結果を度に変換する機能が用意されています(さらに、負の数に360を加えて-180:180の代わりに0:360の範囲を得る)。

これは、ポジションを正しく取得するのに少し注意を払って、すべての矩形のコーナー方向を確認して、矩形の「視線」の特定の位置に対する最小と最大の角度を確認し、最も極端な値。 それは、環状の価値があるために、コーナーケースがたくさんあることです。私はそれらをすべてカバーしたと思います。

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() 

あなたは(あなたがのRect自体を使用できない場合)pygame.Rectから派生する「俳優」クラスことを確認できた - それは、少なくとも私たちがすることを可能にするのRect、にキャストすることができますので、私はそれにメソッドを追加しましたコーナー座標を簡単に選択できます。 とにかく、Python 2.xを使用している場合は、Python3の下にPygameをインストールするのが難しいため、からobjectを継承する必要があります。

私はライブセッションでデバッグするために使用した関数 - それはあなたにとっても有益です。

関連する問題