首页 > 解决方案 > 派格莱特。如何动态更改顶点的图片(动画)。OpenGL

问题描述

环境:

Python:3.6.6
pyglet 版本:1.3.2

代码库:

抽象模型.py

import pyglet


def get_texture_group(file, order_group_index):
    texture = pyglet.image.load(file).texture
    order_group = pyglet.graphics.OrderedGroup(order_group_index)
    return pyglet.graphics.TextureGroup(texture, order_group)


class AbstractModel(object):

    def _create_as_vertex(self):
        v_x = self.cell_data.get("x") * 32
        v_y = self.cell_data.get("y") * -1 * 32

        texture_group = self.map_type_iamge.get(self.cell_data.get("t"))
        x_offset = self.x_offset * self.scale

        x, y, z = v_x + x_offset, v_y, self.z
        x_ = (texture_group.texture.width * self.scale + x_offset + v_x)
        y_ = (texture_group.texture.height * self.scale + v_y)

        tex_coords = ('t2f', (0, 0, 1, 0, 1, 1, 0, 1))

        self.vertices = self.batch.add(
            4, pyglet.gl.GL_QUADS,
            texture_group,
            ('v3f', (x, y, z,
                     x_, y, z,
                     x_, y_, z,
                     x, y_, z)),
            tex_coords)

    def _animate(self, dt):
        # lets assume that I have list of pyglet.graphics.TextureGroup
        # and they should somehow be drawn one after other
        print("I need change image. dt=", dt, self)
        pyglet.clock.schedule_once(self._animate, 1)

ground3d.py

import os
import pyglet

import settings
from models import abstract_model


GROUND_DIR = os.path.join(settings.STATIC_DIR, "ground")

order_group_index = 0

map_type_iamge = {
    1: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "w1.png"), order_group_index),
    2: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "t1.png"), order_group_index),
    1001: abstract_model.get_texture_group(os.path.join(GROUND_DIR, "t1_direction.png"), order_group_index),
}


class Ground3D(abstract_model.AbstractModel):

    def __init__(self, cell_data, batch):

        self.batch = batch
        self.cell_data = cell_data
        self.map_type_iamge = map_type_iamge
        self.scale = 1
        self.x_offset = 0
        self.z = 0
        self.entity = None

        self._create_as_vertex()
        pyglet.clock.schedule_once(self._animate, 1)

解释:

我有应该放置在 3 个维度上的模型(例如,只是平面矩形)。并且这些模型应该是动画的,比如图片 1,在第二张图片 2 之后,......等等。
正如我从之前的问题pyglet.sprite.Sprite()中了解到的那样,在 3D 批处理中使用并不是一个好主意。

问题:

我如何更改图片(使用 TextureGroup 或任何其他方法)self.vertices

或者我用来实现它的arroach/classes。对于某些 3 维平面模型的动画,我找不到任何示例(就我的简单愿景而言)。

有很多关于旋转/移动/调整大小的例子vertices,但是如何建立一个正确的问题(动画方面)以便在谷歌中获得答案 - 我不知道。

PS:如果您,读者,有关于这个主题的任何有用的链接(对于 pyglet 或仅适用于 OpenGL),我将非常感谢您在评论中分享此链接。

标签: pythonpython-3.xopengl3dpyglet

解决方案


纹理坐标。

您应该为所有动画的所有不同事物的所有帧提供一个纹理图集。

最好,一切都应该始终具有相同的动画速度和相同数量的帧。

假设有两个精灵有 2 帧的整个动画,它们存储在 64x64 纹理图集中。(编辑:抱歉含糊不清,64x64 PIXELS,只是因为它可能暗示我们有 64x64 平铺图集,在我提到这一点的其他地方都一样)

现在,您需要一个具有全局值的全局计时器,它指示当前动画帧,而不是游戏帧。它应该独立于帧率。

所述值应以您想要的速度每隔一段时间更新一次,如下所示:

current_frame = (current_frame + 1) % animation_length

由于在这个例子中我们有 2 帧,结果会是这样的:

# init
animation_length = 2
current_frame = 0

# updates:
current_frame = (0 + 1) % 2 # 1 % 2 -> 1
current_frame = (1 + 1) % 2 # 2 % 2 -> 0
...

现在,您需要仅在帧更改时更新所有精灵的 UV。

UV 从左到右开始,从 0 到 1(据我所知,为了这个例子,他们这样做了,嘘)。

由于我们每个有 2 帧,我们可以像这样在 UV 坐标中计算“图块”:

tile_width = 1.0 / frames # 2 frames each, width will be 0.5
tile_height = 1.0 / sprites # 2 sprites each, height will be 0.5 too, perfect

现在,在第一帧,你像正常一样生成你的 UV,你只需获取垂直 ID 或其他东西,并使用它tile_height * sprite_id来获取当前V坐标,你U的计算结果就像tile_width * current_frame.

这假设您已经有了精灵批处理,所以您所做的是在更新时检查每个精灵,并且基本上只是用新帧重新计算新的 UV,这意味着所有精灵都会将它们的帧更改为下一个,耶!

如果您想要拥有彼此独立的系统,例如,某些动画非常慢,而另一些动画则更快,您将需要不同的精灵批次或正确计算从哪里到哪里需要更新顶点缓冲区数组中的 UV。其他一切都完全相同,除了现在 current_frame 不是全局的而是包含的,最好是在管理动画计时器的某个列表或单独的对象中。

您无需更改着色器中的任何内容,它们只需要为帧设置正确的 UV 即可。

顺便说一句,这是非常基本的,你可以自己应用一些逻辑,这样你就可以在纹理中使用 32x32 像素的 16x16 网格,每行精灵有 4 个不同的动画,这些可以是精灵的状态(攻击、运行等) ),但是如何做到这一点完全取决于您,最重要的是,让它发挥作用。祝你好运。

但是如果你按照我说的那样做,那么 state 将是另一个整数,UV 代表 state,假设所有 state 的宽度完全相同,它会是这样的:

state_width = 1 / states
tile_width = 1 / (states * frames_per_state)
U = state_width * current_state + tile_width * current_frame

现在,出现了一个问题,玩家可以在最后一个攻击帧开始他的动画。

这是正常的,具有动作的实体都应该有单独的计时器,我上面描述的,是用于大量只是背景的精灵,比如草。现在,当您将其划分时,您可以在分配新状态时将当前帧重置为 0。

如果您的实体是对象,您可以编写适当的方法,在每次使用这些精灵重建精灵批次时重新计算 UV,然后计时器本身可以包含在对象中。

我们需要画点什么吗?检查动画状态,它有没有改变,不是吗?发送之前计算好的UV,否则,等一下,我们需要重新计算,然后将它们添加到VBO,然后渲染你的东西,最后它会看起来好像你有动画一样,即使真的,它是只是一个简单但很棒的紫外线操作。

祝你好运。


推荐阅读