1

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

pointer should stay in red area

Community
  • 1
  • 1
emorphus
  • 550
  • 9
  • 20
  • Side note: Use a `pygame.time.Clock` to limit the framerate, otherwise Pygame renders as many frames as possible. Before the main while loop create a clock instance `clock = pygame.time.Clock()` and at the end of the loop call `clock.tick(enter_desired_framerate_here)`. – skrx Feb 11 '17 at 13:16
  • what is the relevancy of using a clock in the above code? It slows down the code but the small increment values does that anyway. – emorphus Feb 11 '17 at 14:00
  • If you don't use a clock, Pygame uses up a lot more processing power and the animation runs slower on a PC with less CPU power. – skrx Feb 11 '17 at 15:38
  • Correct and thank you for the response but that is not relevant to this question as this is only example code. – emorphus Feb 11 '17 at 15:40

2 Answers2

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()
skrx
  • 19,980
  • 5
  • 34
  • 48
  • the above solution does not work in full. If the current angle start with + value the pointer stays in the plus section and if it is - then the pointer stays in the negative section. – emorphus Feb 11 '17 at 14:04
  • Ah, yes there's a problem because you increase the current_angle depending on the target_angle. I actually tested the code with a simpler program that just rotated the image to the target_angle immediately. You could either add more `if`s to check if the current_angle needs to be increased or decreased or use vectors to determine if the image has to be rotated clockwise or counterclockwise (that's done by calculating the dot product). I'll update my post later to show you how. – skrx Feb 11 '17 at 15:32
  • Thanks. That made it clearer. I am awaiting you update eagerly. So far the answer is incomplete so I cannot accept it. – emorphus Feb 11 '17 at 15:42
  • I've changed the example a little bit and added some links. – skrx Feb 11 '17 at 21:21
  • Your updated answer is precise, concise, and explained in detail. I accept it as the solution to this question. This is the best way for an answer be given. – emorphus Feb 12 '17 at 03:08
  • I removed the advise against `pygame.math.Vector2`, since the issues don't seem to exist anymore. – skrx Feb 14 '17 at 19:02
  • how can you fire a bullet in the direction of the rotation? Do I need to ask another question? – emorphus Feb 16 '17 at 07:31
  • Yes, better ask a new question to keep this one here clean. I already have an example for bullet firing (again with vectors). Basically, all you have to do is pass the position and the current_angle to your Bullet class, then rotate the image and the velocity vector, and in the bullet's update method increase the position and set its rect.center to the new position. – skrx Feb 16 '17 at 15:22
  • new question is here. I used the code from the accepted answer to the question above..http://stackoverflow.com/questions/42280234/firing-bullets-from-a-rotating-gun – emorphus Feb 16 '17 at 16:58
0

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)
emorphus
  • 550
  • 9
  • 20