yyl
2026-05-11 51b0f6ed9f4e1d3bb6f8144470b46908c7699a96
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System.Collections.Generic;
using Spine.Unity;
using UnityEngine;
 
/// <summary>
/// Spine批量更新管理器(参考UWA优化方案)
/// 禁用SkeletonGraphic的Update/LateUpdate自动回调,
/// 由Manager统一批量调用,减少大量Spine对象的跨语言调用开销。
/// 支持隔帧更新Mesh,进一步降低CPU开销。
/// </summary>
public class SpineUpdateManager : MonoBehaviour
{
    private static SpineUpdateManager instance;
    public static SpineUpdateManager Instance
    {
        get
        {
            if (instance == null)
            {
                var go = new GameObject("[SpineUpdateManager]");
                DontDestroyOnLoad(go);
                instance = go.AddComponent<SpineUpdateManager>();
            }
            return instance;
        }
    }
 
    private readonly List<SkeletonGraphic> managedSpines = new List<SkeletonGraphic>();
    private readonly HashSet<SkeletonGraphic> managedSet = new HashSet<SkeletonGraphic>();
    private int frameCount;
 
    /// <summary>
    /// 注册一个SkeletonGraphic到批量更新管理器。
    /// 注册后会禁用其自身的Update/LateUpdate(通过设置freeze=true),
    /// 改由Manager统一驱动。
    /// </summary>
    public void Register(SkeletonGraphic sg)
    {
        if (sg == null || managedSet.Contains(sg)) return;
        managedSpines.Add(sg);
        managedSet.Add(sg);
        sg.freeze = true; // 禁用SkeletonGraphic自身的Update/LateUpdate
    }
 
    /// <summary>
    /// 从管理器中移除一个SkeletonGraphic,恢复其自身更新。
    /// </summary>
    public void Unregister(SkeletonGraphic sg)
    {
        if (sg == null || !managedSet.Contains(sg)) return;
        managedSpines.Remove(sg);
        managedSet.Remove(sg);
        sg.freeze = false; // 恢复自身更新
    }
 
    void Update()
    {
        frameCount++;
        float dt = Time.deltaTime;
        for (int i = managedSpines.Count - 1; i >= 0; i--)
        {
            var sg = managedSpines[i];
            if (sg == null)
            {
                managedSpines.RemoveAt(i);
                managedSet.Remove(sg);
                continue;
            }
            if (!sg.isActiveAndEnabled) continue;
            sg.Update(dt);
        }
    }
 
    void LateUpdate()
    {
        bool skipMesh = managedSpines.Count > 8 && (frameCount & 1) == 0;
 
        for (int i = managedSpines.Count - 1; i >= 0; i--)
        {
            var sg = managedSpines[i];
            if (sg == null)
            {
                managedSpines.RemoveAt(i);
                managedSet.Remove(sg);
                continue;
            }
            if (!sg.isActiveAndEnabled) continue;
            if (skipMesh) continue;
            sg.UpdateMesh();
        }
    }
 
    void OnDestroy()
    {
        // 恢复所有被管理的Spine自身更新
        foreach (var sg in managedSpines)
        {
            if (sg != null)
                sg.freeze = false;
        }
        managedSpines.Clear();
        managedSet.Clear();
        instance = null;
    }
}