pythonpyglet

How to properly rotate sprite on mouse left button click?


I'm developing an RTS game in Python using Pyglet library. The sprite rotates in range from -180 to 180 degree. The sprite can't properly rotate from -179 to 179, it rotates clockwise, all the way to 179 passing 0, instead of rotating counterclockwise and reach 179 in just 2 degrees.

enter image description here Sprite

enter image description here

from math import trunc, degrees, atan2
from pyglet.app import run
from pyglet.clock import schedule_interval
from pyglet.window import Window
from pyglet.graphics import Batch
from pyglet.sprite import Sprite
from pyglet.image.codecs.png import PNGImageDecoder
from pyglet.window import mouse
from pyglet.image import load
from pyglet.text import Label
from pyglet.canvas import Display


class MAIN(Window):
    def __init__(self):
        window_width = Display().get_default_screen().width // 2
        window_height = Display().get_default_screen().height // 2
        super().__init__(window_width, window_height)
        self.batch = Batch()
        self.sptiteWidth, self.spriteHeight = 100, 100
# SPRITE
        image = load('img/sprite.png', decoder=PNGImageDecoder())
        image.anchor_x, image.anchor_y = self.sptiteWidth // 2, self.spriteHeight // 2
        self.sprite = Sprite(image, x = window_width // 2, y = window_height // 2, batch=self.batch)
        self.angle = 0
        self.rotate = False
# LABEL       
        self.label = Label(
            color=(255, 255, 255),
            font_name ='Times New Roman',
            font_size = 22,
            x = self.width // 2, y = self.height // 4,
            anchor_x ='center', anchor_y ='center')   


    def update(self, dt):
        self.label.text = f'MOUSE ANGLE: {self.angle} || SPRITE ANGLE: {self.sprite.rotation}'
        if self.rotate:
            rotationSpeed = 1
            if self.sprite.rotation != self.angle:
                if self.sprite.rotation < self.angle:    
                    self.sprite.rotation += rotationSpeed
                else: 
                    self.sprite.rotation -= rotationSpeed  
 

    def on_mouse_press(self, x, y, button, modifiers):
        if button == mouse.LEFT:
            self.angle = trunc(degrees(atan2(x - self.sprite.x, y - self.sprite.y)))
            # if self.angle < 0:
            #    self.angle += 360
            self.rotate = True

    
    def on_draw(self):
        self.clear()
        self.batch.draw()
        self.label.draw()


if __name__ == "__main__":
    fps = 60
    main = MAIN()
    schedule_interval(main.update, 1 / fps)
    run()

Solution

  • If your intention is to just make your sprite spin in the direction that would get you to the target fastest, then the code is heavily logic-based. Thus, I'll refer to a diagram to help with explaining.

    Here, since you are working with -180 to 180, you want to firstly figure out this: Diagram of logic

    To do this, we need to figure out whether the "difference" is less than 180, which would be the upper area (-180 to 180 zone). If it isn't, then that means that it would be faster to work on the bottom area (cross the -180 and 180 border).

    Now, we don't actually know which angle is the left angle and which one is the right angle yet. To do this, we simply ask, which one is greater?

    So then,

    1. If working on the top and the left angle is the sprite angle, we move clockwise
    2. If top and the right angle is the sprite angle, we move counterclockwise
    3. If working on the bottom and the left angle is the sprite angle, we move counterclockwise
    4. If bottom and the right angle is the sprite angle, we move clockwise

    We also need to take into account that when we cross the -180 and 180 borderline, we need to convert depending on the direction we're going (-180 to 180, 180 to -180).

    def update(self, dt):
            self.label.text = f'MOUSE ANGLE: {self.angle} || SPRITE ANGLE: {self.sprite.rotation}'
            if self.rotate:
                rotationSpeed = 1
                if self.sprite.rotation != self.angle:
    #New code
                    if abs(self.sprite.rotation-self.angle) < 180:
                        if self.sprite.rotation > self.angle:
                            self.sprite.rotation -= rotationSpeed
                        else:
                            self.sprite.rotation += rotationSpeed
                    else:
                        if self.sprite.rotation > self.angle:
                            self.sprite.rotation += rotationSpeed 
                        else:
                            self.sprite.rotation -= rotationSpeed
    
                if self.sprite.rotation > 180: self.sprite.rotation = -180
                if self.sprite.rotation < -180: self.sprite.rotation = 180
    #Your old code
                    # if self.sprite.rotation < self.angle:
                    #     self.sprite.rotation += rotationSpeed
                    # else: 
                    #     self.sprite.rotation -= rotationSpeed