how to restrict rotation to a set amount of degree

2019-02-28 09:47发布

问题:

My question is related to the answer of the question below.

rotating a rectangle.

How do i restrict the rotation to the area marked in red as in this picture? I tried various ways but was unsuccesfull. The code always keeps the poiter in the other area. my modified code below.

import pygame, math, base64
pygame.init()
screen = pygame.display.set_mode((200, 200))

surf = pygame.image.load("D:\\PYTHON\\SoftwareDG\\Games\\Platform_Suvivor\\assets\\rg.png").convert_alpha()

def rot_center(image, angle):
    orig_rect = image.get_rect()
    rot_image = pygame.transform.rotate(image, angle)
    rot_rect = orig_rect.copy()
    rot_rect.center = rot_image.get_rect().center
    rot_image = rot_image.subsurface(rot_rect).copy()
    return rot_image

current_angle = 0

while True:
    if pygame.event.get(pygame.QUIT): break
    pygame.event.get()

    mouseX, mouseY = pygame.mouse.get_pos()
    rect = surf.get_rect(center=(92, 92))
    target_angle = math.degrees(math.atan2(mouseY - rect.centery, mouseX - rect.centerx))
    if target_angle < -120:
        target_angle = -120
    if target_angle > 120:
        target_angle = 120
    print target_angle
    if current_angle > target_angle:
        current_angle -= 0.03
    if current_angle < target_angle:
        current_angle += 0.03

    screen.fill((40, 140, 40))
    screen.blit(rot_center(surf, -current_angle), rect)
    pygame.display.update()

回答1:

Are you looking for a clamp function like this?

def clamp(value, min_, max_):
    """Clamp value to a range between min_ and max_."""
    return max(min_, min(value, max_))

In your case you also have to check if the current_angle is greater or less than 0.

if current_angle <= 0:
    current_angle = clamp(current_angle, -180, -120)
elif current_angle > 0:
    current_angle = clamp(current_angle, 120, 180)

Update: Here's the example with vectors. I use the vectors to figure out in which direction the sprite needs to be rotated. Note that right is now 0 degrees, left is 180° and it goes from 0° to 360°.

And here are some interesting links that helped me to learn how to do this: http://docs.godotengine.org/en/stable/tutorials/matrices_and_transforms.html http://www.wildbunny.co.uk/blog/vector-maths-a-primer-for-games-programmers/

import math
import pygame


pygame.init()
screen = pygame.display.set_mode((300, 300))
font = pygame.font.Font(None, 24)
GRAY = pygame.Color('gray90')


def clamp(value, min_value, max_value):
    """Clamp value to a range between min_value and max_value."""
    return max(min_value, min(value, max_value))


def main():
    current_angle = 0
    clock = pygame.time.Clock()
    surf = pygame.Surface((80, 50), pygame.SRCALPHA)
    pygame.draw.polygon(surf, (40, 100, 200), ((0, 0), (80, 25), (0, 50)))
    orig_surf = surf
    rect = surf.get_rect(center=(150, 150))
    orig_direction = pygame.math.Vector2(0, 1)

    playing = True

    while playing:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                playing = False

        # Here I figure out if the target is closer in clock-
        # or counterclockwise direction. `orientation` is positive
        # if the target is closer in clockwise and negative
        # if it's in counterclockwise direction.
        vec_to_target = pygame.math.Vector2(pygame.mouse.get_pos()) - rect.center
        direction = orig_direction.rotate(current_angle)
        orientation = vec_to_target.dot(direction)
        # I use orientation > 3 and < -3 instead of 0 to
        # avoid jittering when the target angle is reached.
        if orientation > 3:
            current_angle += 3
        elif orientation < -3:
            current_angle -= 3

        # You can use this modulo operation to keep the angle between
        # 0° and 360°, but this is not needed because of the clamp.
        # current_angle %= 360
        # Clamp the value to the desired range.
        current_angle = clamp(current_angle, 120, 240)

        surf = pygame.transform.rotate(orig_surf, -current_angle)
        rect = surf.get_rect(center=rect.center)

        # Draw
        screen.fill((40, 40, 40))
        screen.blit(surf, rect)
        txt = font.render('angle {:.1f}'.format(current_angle), True, GRAY)
        screen.blit(txt, (10, 10))
        txt = font.render('orientation {:.1f}'.format(orientation), True, GRAY)
        screen.blit(txt, (10, 25))

        pygame.display.update()
        clock.tick(30)


if __name__ == '__main__':
    main()
    pygame.quit()


回答2:

I managed to restrict the rotation of the gun to a limited area by just adding a one line check just before rotation. The code is far from perfect. When crossing from the + quadrant to the - quadrant and vice versa, the gun rotates in the opposite direction to get to the other side. More checks before incrementing the angle will correct the direction of rotation but for now the problem is solved in a basic way. The code is given below.

import pygame, math, base64
pygame.init()
screen = pygame.display.set_mode((200, 200))

surf = pygame.image.load("put your image here").convert_alpha()

def rot_center(image, angle):
    orig_rect = image.get_rect()
    rot_image = pygame.transform.rotate(image, angle)
    rot_rect = orig_rect.copy()
    rot_rect.center = rot_image.get_rect().center
    rot_image = rot_image.subsurface(rot_rect).copy()
    return rot_image

current_angle = -180

clock = pygame.time.Clock()
while True:
    if pygame.event.get(pygame.QUIT): break
    pygame.event.get()

    mouseX, mouseY = pygame.mouse.get_pos()
    rect = surf.get_rect(center=(92, 92))
    target_angle = math.degrees(math.atan2(mouseY - rect.centery, mouseX - rect.centerx))

    # the new line is just below
    if 180 > target_angle > 120 or - 120 > target_angle > -179:
        print "ok", target_angle
        if current_angle > target_angle:
            current_angle -= 2
        if current_angle < target_angle:
            current_angle += 2

    screen.fill((40, 140, 40))
    screen.blit(rot_center(surf, -current_angle), rect)
    pygame.display.update()
    clock.tick(60)