atan2并没有为我提供我想要的角度

时间:2017-05-24 14:42:15

标签: python pygame

我试图用pygame写一个游戏,涉及一个带有"炮塔的移动物体"旋转跟随鼠标。截至目前,我主要是试图扩展示例,所以代码并不完全是我的(感谢Sean J. McKiernan的示例程序);但是,这部分是。以下是我的代码;我使用rect的中心("基座"形状和"转塔"旋转的点)作为基点,以及鼠标的位置作为基点其他一点。通过从"中心的位移中减去鼠标的位移,"我有效地在两个点之间得到一个向量,并找到该向量与x轴与atan2之间的角度。以下是我用来做的代码:

def get_angle(self, mouse):
    x_off = (mouse[0]-self.rect.centerx)
    y_off = (mouse[1]-self.rect.centery)
    self.angle = math.degrees(math.atan2(-y_off, x_off) % 2*math.pi)
    self.hand = pg.transform.rotate(self.original_hand, self.angle)
    self.hand_rect = self.hand.get_rect(center=self.hand_rect.center)

根据我已经审核的多个教程,这应该是正确的代码;然而,我后来发现那些教程(实际上,本教程)都是针对Python 2.7的,而我正在尝试用Python 3.6编写。不过,我认为不应该在这种情况下有所作为。就目前而言,这种观点似乎完全取决于"字符"在屏幕上的位置。如果"字符"在一个角落里,“炮塔”#34;如果"字符"不同于反应。在屏幕中间。但是,这不重要; "字符的位置"无论屏幕在哪里,相对于鼠标都是完全相同的。任何想法,还是我需要提供更多代码?

编辑:显然,需要更多代码。我没有尝试仅完全解决完全必要的部分,而是提供了整个代码示例,因此每个人都可以运行它。作为旁注,"博尔特"事情(用于点燃简单的黄色块)也不起作用,但我只是在开始调试之前试图让手臂工作。

编辑第二个:我发现了" Bolt"系统在原点的某个距离内工作(窗口坐标系中为0,0),并且臂也可以在更小的距离内工作。我在"墙壁下添加了Block(pg.Color("chocolate"), (0,0,100,100))行"分组作为决策点,块位于左上角。我通过在控制循环中将screen_rect更改为viewport来纠正Bolt;但是,我不知道为什么" arm"摆动取决于与原点的相邻性。鼠标和"字符的位置"应该是绝对的。我错过了什么吗?

"""
Basic moving platforms using only rectangle collision.

-Written by Sean J. McKiernan 'Mekire'
Edited for a test of "arms"
"""

import os
import sys
import math
import pygame as pg


CAPTION = "Moving Platforms"
SCREEN_SIZE = (700,700)
BACKGROUND_COLOR = (50, 50, 50)
COLOR_KEY = (255, 255, 255)


class _Physics(object):
    """A simplified physics class. Psuedo-gravity is often good enough."""
    def __init__(self):
        """You can experiment with different gravity here."""
        self.x_vel = self.y_vel = 0
        self.grav = 0.4
        self.fall = False

    def physics_update(self):
        """If the player is falling, add gravity to the current y velocity."""
        if self.fall:
            self.y_vel += self.grav
        else:
            self.y_vel = 0


class Player(_Physics, object):
    def __init__(self,location,speed):
        _Physics.__init__(self)
        HAND = pg.image.load("playertst2.png").convert()
        HAND.set_colorkey(COLOR_KEY)
        self.image = pg.image.load('playertst.png').convert()
        self.rect = self.image.get_rect(topleft=location)
        self.speed = speed
        self.jump_power = -9.0
        self.jump_cut_magnitude = -3.0
        self.on_moving = False
        self.collide_below = False

        self.original_hand = HAND
        self.fake_hand = self.original_hand.copy()
        self.hand = self.original_hand.copy()
        self.hand_rect = self.hand.get_rect(topleft=location)
        self.angle = self.get_angle(pg.mouse.get_pos())

    def check_keys(self, keys):
        """Find the player's self.x_vel based on currently held keys."""
        self.x_vel = 0
        if keys[pg.K_LEFT] or keys[pg.K_a]:
            self.x_vel -= self.speed
        if keys[pg.K_RIGHT] or keys[pg.K_d]:
            self.x_vel += self.speed

    def get_position(self, obstacles):
        """Calculate the player's position this frame, including collisions."""
        if not self.fall:
            self.check_falling(obstacles)
        else:
            self.fall = self.check_collisions((0,self.y_vel), 1, obstacles)
        if self.x_vel:
            self.check_collisions((self.x_vel,0), 0, obstacles)

    def check_falling(self, obstacles):
        """If player is not contacting the ground, enter fall state."""
        if not self.collide_below:
            self.fall = True
            self.on_moving = False

    def check_moving(self,obstacles):
        """
        Check if the player is standing on a moving platform.
        If the player is in contact with multiple platforms, the prevously
        detected platform will take presidence.
        """
        if not self.fall:
            now_moving = self.on_moving
            any_moving, any_non_moving = [], []
            for collide in self.collide_below:
                if collide.type == "moving":
                    self.on_moving = collide
                    any_moving.append(collide)
                else:
                    any_non_moving.append(collide)
            if not any_moving:
                self.on_moving = False
            elif any_non_moving or now_moving in any_moving:
                self.on_moving = now_moving

    def check_collisions(self, offset, index, obstacles):
        """
        This function checks if a collision would occur after moving offset
        pixels. If a collision is detected, the position is decremented by one
        pixel and retested. This continues until we find exactly how far we can
        safely move, or we decide we can't move.
        """
        unaltered = True
        self.rect[index] += offset[index]
        self.hand_rect[index] += offset[index]
        while pg.sprite.spritecollideany(self, obstacles):
            self.rect[index] += (1 if offset[index]<0 else -1)
            self.hand_rect[index] += (1 if offset[index]<0 else -1)
            unaltered = False
        return unaltered

    def check_above(self, obstacles):
        """When jumping, don't enter fall state if there is no room to jump."""
        self.rect.move_ip(0, -1)
        collide = pg.sprite.spritecollideany(self, obstacles)
        self.rect.move_ip(0, 1)
        return collide

    def check_below(self, obstacles):
        """Check to see if the player is contacting the ground."""
        self.rect.move_ip((0,1))
        collide = pg.sprite.spritecollide(self, obstacles, False)
        self.rect.move_ip((0,-1))
        return collide

    def jump(self, obstacles):
        """Called when the user presses the jump button."""
        if not self.fall and not self.check_above(obstacles):
            self.y_vel = self.jump_power
            self.fall = True
            self.on_moving = False

    def jump_cut(self):
        """Called if player releases the jump key before maximum height."""
        if self.fall:
            if self.y_vel < self.jump_cut_magnitude:
                self.y_vel = self.jump_cut_magnitude

    def get_angle(self, mouse):
        x_off = (mouse[0]-self.rect.centerx)
        y_off = (mouse[1]-self.rect.centery)
        self.angle = math.degrees(math.atan2(-y_off, x_off) % (2*math.pi))
        self.hand = pg.transform.rotate(self.original_hand, self.angle)
        self.hand_rect = self.hand.get_rect(center=self.hand_rect.center)

        """
        offset = (mouse[1]-self.hand_rect.centery, mouse[0]-self.hand_rect.centerx)
        self.angle = math.atan2(-offset[0], offset[1]) % (2 * math.pi)
        self.angle = math.degrees(self.angle)
        self.hand = pg.transform.rotate(self.original_hand, self.angle)
        self.hand_rect = self.hand.get_rect(center=self.rect.center)


        self.angle = 135-math.degrees(math.atan2(*offset))
        self.hand = pg.transform.rotate(self.original_hand, self.angle)
        self.hand_rect = self.hand.get_rect(topleft=self.rect.topleft)
        """

    def pre_update(self, obstacles):
        """Ran before platforms are updated."""
        self.collide_below = self.check_below(obstacles)
        self.check_moving(obstacles)

    def update(self, obstacles, keys):
        """Everything we need to stay updated; ran after platforms update."""
        self.check_keys(keys)
        self.get_position(obstacles)
        self.physics_update()

    def get_event(self, event, bolts):
        if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
            bolts.add(Bolt(self.rect.center))
        elif event.type == pg.MOUSEMOTION:
            self.get_angle(event.pos)

    def draw(self, surface):
        """Blit the player to the target surface."""
        surface.blit(self.image, self.rect)
        surface.blit(self.hand, self.hand_rect)

class Bolt(pg.sprite.Sprite):
    def __init__(self, location):
        pg.sprite.Sprite.__init__(self)
        """self.original_bolt = pg.image.load('bolt.png')"""
        """self.angle = -math.radians(angle-135)"""
        """self.image = pg.transform.rotate(self.original_bolt, angle)"""
        """self.image = self.original_bolt"""
        self.image=pg.Surface((5,10)).convert()
        self.image.fill(pg.Color("yellow"))
        self.rect = self.image.get_rect(center=location)
        self.move = [self.rect.x, self.rect.y]
        self.speed_magnitude = 5
        """self.speed = (self.speed_magnitude*math.cos(self.angle), self.speed_magnitude*math.sin(self.angle))"""
        """self.speed = (5,0)"""
        self.done = False

    def update(self, screen_rect, obstacles):
        self.move[0] += self.speed_magnitude
        """self.move[1] += self.speed[1]"""
        self.rect.topleft = self.move
        self.remove(screen_rect, obstacles)

    def remove(self, screen_rect, obstacles):
        if not self.rect.colliderect(screen_rect):
            self.kill()


class Block(pg.sprite.Sprite):
    """A class representing solid obstacles."""
    def __init__(self, color, rect):
        """The color is an (r,g,b) tuple; rect is a rect-style argument."""
        pg.sprite.Sprite.__init__(self)
        self.rect = pg.Rect(rect)
        self.image = pg.Surface(self.rect.size).convert()
        self.image.fill(color)
        self.type = "normal"


class MovingBlock(Block):
    """A class to represent horizontally and vertically moving blocks."""
    def __init__(self, color, rect, end, axis, delay=500, speed=2, start=None):
        """
        The moving block will travel in the direction of axis (0 or 1)
        between rect.topleft and end. The delay argument is the amount of time
        (in miliseconds) to pause when reaching an endpoint; speed is the
        platforms speed in pixels/frame; if specified start is the place
        within the blocks path to start (defaulting to rect.topleft).
        """
        Block.__init__(self, color, rect)
        self.start = self.rect[axis]
        if start:
            self.rect[axis] = start
        self.axis = axis
        self.end = end
        self.timer = 0.0
        self.delay = delay
        self.speed = speed
        self.waiting = False
        self.type = "moving"

    def update(self, player, obstacles):
        """Update position. This should be done before moving any actors."""
        obstacles = obstacles.copy()
        obstacles.remove(self)
        now = pg.time.get_ticks()
        if not self.waiting:
            speed = self.speed
            start_passed = self.start >= self.rect[self.axis]+speed
            end_passed = self.end <= self.rect[self.axis]+speed
            if start_passed or end_passed:
                if start_passed:
                    speed = self.start-self.rect[self.axis]
                else:
                    speed = self.end-self.rect[self.axis]
                self.change_direction(now)
            self.rect[self.axis] += speed
            self.move_player(now, player, obstacles, speed)
        elif now-self.timer > self.delay:
            self.waiting = False

    def move_player(self, now, player, obstacles, speed):
        """
        Moves the player both when on top of, or bumped by the platform.
        Collision checks are in place to prevent the block pushing the player
        through a wall.
        """
        if player.on_moving is self or pg.sprite.collide_rect(self,player):
            axis = self.axis
            offset = (speed, speed)
            player.check_collisions(offset, axis, obstacles)
            if pg.sprite.collide_rect(self, player):
                if self.speed > 0:
                    self.rect[axis] = player.rect[axis]-self.rect.size[axis]
                else:
                    self.rect[axis] = player.rect[axis]+player.rect.size[axis]
                self.change_direction(now)

    def change_direction(self, now):
        """Called when the platform reaches an endpoint or has no more room."""
        self.waiting = True
        self.timer = now
        self.speed *= -1

"""class Spell(pg.sprite.Sprite):
    def __init__(self, location, angle)"""

class Control(object):
    """Class for managing event loop and game states."""
    def __init__(self):
        """Initalize the display and prepare game objects."""
        self.screen = pg.display.get_surface()
        self.screen_rect = self.screen.get_rect()
        self.clock = pg.time.Clock()
        self.fps = 60.0
        self.keys = pg.key.get_pressed()
        self.done = False
        self.player = Player((50,875), 4)
        self.viewport = self.screen.get_rect()
        self.level = pg.Surface((1000,1000)).convert()
        self.level_rect = self.level.get_rect()
        self.win_text,self.win_rect = self.make_text()
        self.obstacles = self.make_obstacles()
        self.bolts = pg.sprite.Group()

    def make_text(self):
        """Renders a text object. Text is only rendered once."""
        font = pg.font.Font(None, 100)
        message = "You win. Celebrate."
        text = font.render(message, True, (100,100,175))
        rect = text.get_rect(centerx=self.level_rect.centerx, y=100)
        return text, rect

    def make_obstacles(self):
        """Adds some arbitrarily placed obstacles to a sprite.Group."""
        walls = [Block(pg.Color("chocolate"), (0,980,1000,20)),
                 Block(pg.Color("chocolate"), (0,0,20,1000)),
                 Block(pg.Color("chocolate"), (980,0,20,1000))]
        static = [Block(pg.Color("darkgreen"), (250,780,200,100)),
                  Block(pg.Color("darkgreen"), (600,880,200,100)),
                  Block(pg.Color("darkgreen"), (20,360,880,40)),
                  Block(pg.Color("darkgreen"), (950,400,30,20)),
                  Block(pg.Color("darkgreen"), (20,630,50,20)),
                  Block(pg.Color("darkgreen"), (80,530,50,20)),
                  Block(pg.Color("darkgreen"), (130,470,200,215)),
                  Block(pg.Color("darkgreen"), (20,760,30,20)),
                  Block(pg.Color("darkgreen"), (400,740,30,40))]
        moving = [MovingBlock(pg.Color("olivedrab"), (20,740,75,20), 325, 0),
                  MovingBlock(pg.Color("olivedrab"), (600,500,100,20), 880, 0),
                  MovingBlock(pg.Color("olivedrab"),
                              (420,430,100,20), 550, 1, speed=3, delay=200),
                  MovingBlock(pg.Color("olivedrab"),
                              (450,700,50,20), 930, 1, start=930),
                  MovingBlock(pg.Color("olivedrab"),
                              (500,700,50,20), 730, 0, start=730),
                  MovingBlock(pg.Color("olivedrab"),
                              (780,700,50,20), 895, 0, speed=-1)]
        return pg.sprite.Group(walls, static, moving)

    def update_viewport(self):
        """
        The viewport will stay centered on the player unless the player
        approaches the edge of the map.
        """
        self.viewport.center = self.player.rect.center
        self.viewport.clamp_ip(self.level_rect)

    def event_loop(self):
        """We can always quit, and the player can sometimes jump."""
        for event in pg.event.get():
            if event.type == pg.QUIT or self.keys[pg.K_ESCAPE]:
                self.done = True
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_SPACE:
                    self.player.jump(self.obstacles)
            elif event.type == pg.KEYUP:
                if event.key == pg.K_SPACE:
                    self.player.jump_cut()
            elif event.type == pg.MOUSEMOTION or event.type == pg.MOUSEBUTTONDOWN:
                self.player.get_event(event, self.bolts)

    def update(self):
        """Update the player, obstacles, and current viewport."""
        self.keys = pg.key.get_pressed()
        self.player.pre_update(self.obstacles)
        self.obstacles.update(self.player, self.obstacles)
        self.player.update(self.obstacles, self.keys)
        self.update_viewport()
        self.bolts.update(self.screen_rect, self.obstacles)

    def draw(self):
        """
        Draw all necessary objects to the level surface, and then draw
        the viewport section of the level to the display surface.
        """
        self.level.fill(pg.Color("lightblue"))
        self.obstacles.draw(self.level)
        self.level.blit(self.win_text, self.win_rect)
        self.player.draw(self.level)
        self.bolts.draw(self.level)
        self.screen.blit(self.level, (0,0), self.viewport)

    def display_fps(self):
        """Show the programs FPS in the window handle."""
        caption = "{} - FPS: {:.2f}".format(CAPTION, self.clock.get_fps())
        pg.display.set_caption(caption)

    def main_loop(self):
        """As simple as it gets."""
        while not self.done:
            self.event_loop()
            self.update()
            self.draw()
            pg.display.update()
            self.clock.tick(self.fps)
            self.display_fps()


if __name__ == "__main__":
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pg.init()
    pg.display.set_caption(CAPTION)
    pg.display.set_mode(SCREEN_SIZE)
    PLAYERIMG = pg.image.load("playertst.png").convert()
    PLAYERIMG.set_colorkey(COLOR_KEY)
    run_it = Control()
    run_it.main_loop()
    pg.quit()
    sys.exit()

1 个答案:

答案 0 :(得分:1)

% 2*pi不必要,而您的get_angle函数没有return值,但您对self.angle = self.get_angle进行了分配,但这不是问题。问题是鼠标位置是相对于屏幕的(例如,如果您的屏幕是640x480,则点击游戏屏幕右上方区域将始终产生(0,480)),而(字符)矩形的位置在您的屏幕中给出游戏区域比屏幕大,如果移动角色并因此视图移动,则会获得两个不同坐标系中的坐标。您必须跟踪视图在游戏区域中的位置,并将偏移量添加到鼠标坐标。