bl_info = {
    "name": "Animate Text Body",
    "author": "Your Name",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "Properties > Object > Animated Text",
    "description": "Позволяет анимировать содержание текстового объекта с помощью кастомного свойства",
    "category": "Object",
}

import bpy

# -------------------------------------------------------------------
# ПОЛЬЗОВАТЕЛЬСКИЕ НАСТРОЙКИ (измените под свой проект)
# -------------------------------------------------------------------
# Здесь вы определяете, какой текст будет показываться при каких значениях свойства.
# Ключ словаря — значение свойства "Text Animator", значение — текст, который будет установлен.
TEXTS = {
    0.0: "Первый кадр",
    1.0: "Второй кадр",
    2.0: "Анимация текста работает!",
    3.0: "Можно добавить",
    4.0: "Любое количество вариантов",
    5.0: "И менять их плавно",
}

# Значение по умолчанию, если текущее значение не найдено в словаре
FALLBACK_TEXT = "Текст по умолчанию"

# Имя объекта, текст которого мы анимируем (оставьте пустым, чтобы использовать активный объект)
TARGET_OBJECT_NAME = "Text"
# -------------------------------------------------------------------

# Добавляем кастомное свойство к объектам, чтобы его можно было анимировать
def register_properties():
    bpy.types.Object.text_animator_value = bpy.props.FloatProperty(
        name="Text Animator",
        description="Измените это значение, чтобы менять текст объекта",
        default=0.0,
        min=0.0,
        # max можно не ставить, будет работать по словарю
        soft_min=0.0,
        soft_max=float(len(TEXTS) - 1) if TEXTS else 1.0,
        step=1,
        precision=1,
        subtype='NONE'
    )

def unregister_properties():
    del bpy.types.Object.text_animator_value

# Основная функция, которая вызывается при смене кадра
@bpy.app.handlers.persistent
def update_text_body(scene):
    # Определяем целевой объект
    if TARGET_OBJECT_NAME:
        text_obj = bpy.data.objects.get(TARGET_OBJECT_NAME)
    else:
        # Если имя не задано, пробуем использовать активный объект
        text_obj = bpy.context.active_object
    
    # Проверяем, что объект существует и это текстовый объект (имеет атрибут body)
    if text_obj is None or not hasattr(text_obj.data, 'body'):
        return
    
    # Получаем текущее значение анимируемого свойства
    anim_val = text_obj.text_animator_value
    
    # Ищем ближайшее значение в словаре (проще всего — целые числа)
    # Здесь реализована логика "точного соответствия" (для ключей вида 1.0, 2.0 и т.д.)
    # При желании можно усложнить до интерполяции.
    
    best_key = None
    # Ищем точное совпадение или ближайшее целое
    # Для простоты примера — ищем точное совпадение или используем FALLBACK
    if anim_val in TEXTS:
        new_text = TEXTS[anim_val]
    else:
        # Пытаемся округлить до ближайшего целого, если нужно
        rounded_val = round(anim_val)
        if rounded_val in TEXTS:
            new_text = TEXTS[rounded_val]
        else:
            new_text = FALLBACK_TEXT
    
    # Меняем содержимое текстового объекта
    if text_obj.data.body != new_text:
        text_obj.data.body = new_text

# Панель в свойствах объекта для удобного доступа к свойству
class OBJECT_PT_animated_text(bpy.types.Panel):
    bl_label = "Animated Text"
    bl_idname = "OBJECT_PT_animated_text"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"
    
    @classmethod
    def poll(cls, context):
        # Показываем панель только если объект существует и это текстовый объект
        return context.object and hasattr(context.object.data, 'body')
    
    def draw(self, context):
        layout = self.layout
        obj = context.object
        
        layout.prop(obj, "text_animator_value")
        layout.label(text="Анимируйте это свойство")
        layout.label(text="Текст будет меняться автоматически")

# Регистрация классов и обработчиков
classes = [OBJECT_PT_animated_text]

def register():
    register_properties()
    for cls in classes:
        bpy.utils.register_class(cls)
    # Добавляем обработчик смены кадра
    bpy.app.handlers.frame_change_post.append(update_text_body)
    # Также можно добавить обработчик на случай изменения свойства вручную без смены кадра
    # Но для старта достаточно frame_change_post

def unregister():
    # Убираем обработчик
    if update_text_body in bpy.app.handlers.frame_change_post:
        bpy.app.handlers.frame_change_post.remove(update_text_body)
    
    for cls in classes:
        bpy.utils.unregister_class(cls)
    unregister_properties()

if __name__ == "__main__":
    register()