Pyglet开发RTS游戏时精灵左键点击旋转的最短路径优化问题
Pyglet开发RTS游戏时精灵左键点击旋转的最短路径优化问题
嘿,这个旋转路径的问题我之前做RTS游戏的时候也踩过坑!核心原因是你现在的代码只简单比较了当前角度和目标角度的大小,没考虑到**-180°和180°其实是同一个方向**,导致跨这个边界的时候,精灵会绕远路而不是走最短路径。
问题分析
你当前的旋转逻辑是直接通过角度大小判断旋转方向:
if self.sprite.rotation < self.angle: self.sprite.rotation += rotationSpeed else: self.sprite.rotation -= rotationSpeed
当目标角度是179°、当前角度是-179°时,代码会认为-179 < 179,于是一直加旋转速度,让精灵绕358°的远路过去,完全忽略了只需要逆时针转2°就能到达的最短路径。
解决方案
我们需要计算两个角度之间的最短差值,把差值限制在-180°到180°之间,这样差值的正负就代表了最短路径的旋转方向,同时处理最后一步的过冲问题。
具体修改步骤:
- 将当前旋转角度和目标角度统一转换为
0-360°的范围,避免负数干扰 - 计算差值并调整到
-180°到180°区间,正数表示顺时针最短路径,负数表示逆时针最短路径 - 根据差值方向调整旋转速度,当剩余角度差小于单次旋转步长时,直接跳到目标角度并停止旋转
修改后的完整代码
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.spriteWidth, self.spriteHeight = 100, 100 # 修正拼写错误 # SPRITE image = load('img/sprite.png', decoder=PNGImageDecoder()) image.anchor_x, image.anchor_y = self.spriteWidth // 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, 255), # 补充alpha通道,避免显示问题 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: {round(self.sprite.rotation, 1)}' if self.rotate: rotationSpeed = 1 # 转换为0-360°范围,消除负数角度的干扰 current_rot = self.sprite.rotation % 360 target_rot = self.angle % 360 # 计算最短路径的角度差,限制在-180到180之间 diff = target_rot - current_rot if diff > 180: diff -= 360 elif diff < -180: diff += 360 # 根据差值方向旋转,处理最后一步的过冲 if abs(diff) < rotationSpeed: self.sprite.rotation = self.angle self.rotate = False # 到达目标后停止旋转 else: if diff > 0: 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))) 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()
额外说明
- 修正了原代码中
self.sptiteWidth的拼写错误,改为self.spriteWidth - 给Label的color参数补充了alpha通道(255),避免部分环境下文字不显示
- 在update方法中增加了到达目标角度后停止旋转的逻辑(
self.rotate = False),避免精灵一直抖动
备注:内容来源于stack exchange,提问作者True Explorer
相关产品推荐
相关产品推荐

