能否在Kivy的atlas文件中添加变量以动态修改y坐标?
提问
是否可以在Kivy的atlas文件中添加变量?我现在有一张包含角色四个方向动画帧的精灵图,atlas文件里已经列出了所有帧,我希望把帧的y坐标换成变量,这样就能在py文件的类里修改这个变量,根据按下的方向键切换对应行的动画。
理想的atlas文件结构如下:
{ "frames/player.png": { "f0_1": [0, var, 64, 50], "f0_2": [64, var, 64, 50], "f0_3": [128, var, 64, 50], "f0_4": [192, var, 64, 50], "f0_5": [256, var, 64, 50], "f0_6": [320, var, 64, 50], "f0_7": [384, var, 64, 50], "f0_8": [448, var, 64, 50], "f0_9": [512, var, 64, 50] } }
这样我就能在py文件里根据方向键修改var的值,切换不同方向的动画帧。
我当前的atlas文件内容是:
{ "frames/player.png": { "f0_1": [0, 0, 64, 50], "f0_2": [64, 0, 64, 50], "f0_3": [128, 0, 64, 50], "f0_4": [192, 0, 64, 50], "f0_5": [256, 0, 64, 50], "f0_6": [320, 0, 64, 50], "f0_7": [384, 0, 64, 50], "f0_8": [448, 0, 64, 50], "f0_9": [512, 0, 64, 50], "f1_1": [0, 50, 64, 50], "f1_2": [64, 50, 64, 50], "f1_3": [128, 50, 64, 50], "f1_4": [192, 50, 64, 50], "f1_5": [256, 50, 64, 50], "f1_6": [320, 50, 64, 50], "f1_7": [384, 50, 64, 50], "f1_8": [448, 50, 64, 50], "f1_9": [512, 50, 64, 50], "f2_1": [0, 101, 64, 50], "f2_2": [64, 101, 64, 50], "f2_3": [128, 101, 64, 50], "f2_4": [192, 101, 64, 50], "f2_5": [256, 101, 64, 50], "f2_6": [320, 101, 64, 50], "f2_7": [384, 101, 64, 50], "f2_8": [448, 101, 64, 50], "f2_9": [512, 101, 64, 50], "f3_1": [0, 150, 64, 50], "f3_2": [64, 150, 64, 50], "f3_3": [128, 150, 64, 50], "f3_4": [192, 150, 64, 50], "f3_5": [256, 150, 64, 50], "f3_6": [320, 150, 64, 50], "f3_7": [384, 150, 64, 50], "f3_8": [448, 150, 64, 50], "f3_9": [512, 150, 64, 50] } }
回答
Kivy的atlas是静态JSON文件,不支持直接写入变量,没法用var这种动态值控制帧坐标,但可以通过两种方式实现你的需求:
1. 按方向分组切换帧(适配现有atlas结构)
你已经把不同方向的帧按f0_*、f1_*、f2_*、f3_*分组,直接在代码里根据方向键切换帧的前缀即可:
from kivy.app import App from kivy.uix.image import Image from kivy.clock import Clock from kivy.core.window import Window class Player(Image): def __init__(self, **kwargs): super().__init__(**kwargs) self.current_dir = 0 # 0-3对应四个方向 self.frame_index = 1 Clock.schedule_interval(self.update_frame, 0.1) # 绑定键盘事件 self._keyboard = Window.request_keyboard(self._keyboard_closed, self) self._keyboard.bind(on_key_down=self._on_key_down) def _keyboard_closed(self): self._keyboard.unbind(on_key_down=self._on_key_down) self._keyboard = None def _on_key_down(self, keyboard, keycode, text, modifiers): if keycode[1] == 'w': self.current_dir = 0 elif keycode[1] == 's': self.current_dir = 1 elif keycode[1] == 'a': self.current_dir = 2 elif keycode[1] == 'd': self.current_dir = 3 return True def update_frame(self, dt): self.frame_index = self.frame_index % 9 + 1 self.source = f'atlas://frames/player/f{self.current_dir}_{self.frame_index}' class GameApp(App): def build(self): return Player() if __name__ == '__main__': GameApp().run()
2. 动态计算帧坐标(绕开atlas静态限制)
如果不想依赖atlas的帧命名,直接加载整张精灵图,在代码里动态计算uv坐标显示对应帧:
from kivy.app import App from kivy.uix.image import Image from kivy.clock import Clock from kivy.core.window import Window class Player(Image): def __init__(self, **kwargs): super().__init__(**kwargs) self.source = 'frames/player.png' self.current_dir = 0 self.frame_index = 0 self.frame_width = 64 self.frame_height = 50 self.y_offsets = [0, 50, 101, 150] # 四个方向的y偏移 Clock.schedule_interval(self.update_frame, 0.1) # 绑定键盘事件 self._keyboard = Window.request_keyboard(self._keyboard_closed, self) self._keyboard.bind(on_key_down=self._on_key_down) def _keyboard_closed(self): self._keyboard.unbind(on_key_down=self._on_key_down) self._keyboard = None def _on_key_down(self, keyboard, keycode, text, modifiers): if keycode[1] == 'w': self.current_dir = 0 elif keycode[1] == 's': self.current_dir = 1 elif keycode[1] == 'a': self.current_dir = 2 elif keycode[1] == 'd': self.current_dir = 3 return True def update_frame(self, dt): self.frame_index = (self.frame_index + 1) % 9 # 计算uv坐标(Kivy的y轴从下往上,需反向计算) x = self.frame_index * self.frame_width y = self.y_offsets[self.current_dir] self.uvpos = (x / self.texture.width, (self.texture.height - y - self.frame_height) / self.texture.height) self.uvsize = (self.frame_width / self.texture.width, self.frame_height / self.texture.height) class GameApp(App): def build(self): return Player() if __name__ == '__main__': GameApp().run()
第一种方式更贴合你现有的atlas结构,代码简洁;第二种方式灵活性更高,适合需要精细控制帧的场景。
内容的提问来源于stack exchange,提问作者Justin
相关产品推荐
相关产品推荐

