docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Sample Sprite Data Source and Report

    This sample demonstrates how to create a custom data source and report for the Sprite Atlas Analyzer. It shows how to collect sprite mesh information (vertex and triangle counts) and create a filterable report to analyze sprite performance.

    Overview

    The sample consists of two main parts:

    Data Source Components

    1. SpriteDataSource.cs - Custom data source that implements IReportDataSource
    2. SpriteCaptureData.cs - Data structures for storing captured sprite information

    Report Components

    1. SpriteMeshSizeReport.cs - Custom report that inherits from AnalyzerIssueReportBase
    2. SpriteMeshSizeFilteredData.cs - Data structure for filtered report items
    3. SpriteMeshSizeReportSettings.cs - Settings UI for configuring report filters
    4. SpriteMeshSizeReportSettings.uxml - UXML layout for the settings UI
    5. SpriteMeshSizeReport.uss - Styling for the report UI

    How to Create a Custom Data Source

    1. Create Data Structures

    First, define the data structures to store your captured information:

    [Serializable]
    class YourCaptureData : ISaveData
    {
        public List<YourAssetData> assetData = new();
        public long lastCaptureTime;
    }
    
    [Serializable]
    class YourAssetData
    {
        public string assetPathGuid;
        public long fileModifiedTime;
        public long metaFileModifiedTime;
        // Add your specific data fields
    }
    

    2. Implement IReportDataSource

    Create a class that implements IReportDataSource:

    class YourDataSource : IReportDataSource
    {
        YourCaptureData m_Capture = new();
        bool m_Cancel;
        Task<YourCaptureData> m_CaptureTask;
    
        public event Action<IReportDataSource> onDataSourceChanged;
        public event Action<IReportDataSource> onCaptureStart;
        public event Action<IReportDataSource> onCaptureEnd;
    
        public bool capturing { get; private set; }
        public string name => "Your Data Source Name";
        public long lastCaptureTime => m_Capture.lastCaptureTime;
        public List<YourAssetData> data => m_Capture.assetData;
    
        public async void Capture(string[] assetSearchPath)
        {
            m_Cancel = false;
            capturing = true;
            onCaptureStart?.Invoke(this);
            m_CaptureTask = CaptureData(m_Capture, assetSearchPath);
            await m_CaptureTask;
            m_Capture = m_CaptureTask.Result;
            capturing = false;
            onCaptureEnd?.Invoke(this);
            onDataSourceChanged?.Invoke(this);
        }
    
        public void StopCapture() => m_Cancel = true;
        public void Dispose() => m_Cancel = true;
        public void Save(ISaveFile saveData) => saveData.AddSaveData(m_Capture);
        public void Load(ISaveFile saveData) { /* Load implementation */ }
    }
    

    3. Implement Data Capture Logic

    Create the asynchronous capture method:

    async Task<YourCaptureData> CaptureData(YourCaptureData prevCapture, string[] assetSearchPath)
    {
        int id = Progress.Start("Your Data Capture");
        var capture = new YourCaptureData();
    
        // Find assets of your target type
        string[] guids = AssetDatabase.FindAssets("t:YourAssetType", assetSearchPath);
        HashSet<string> pathVisited = new();
    
        for (int i = 0; i < guids.Length && !m_Cancel; ++i)
        {
            Progress.Report(id, i, guids.Length, "Capturing data");
            var path = AssetDatabase.GUIDToAssetPath(guids[i]);
    
            if (!pathVisited.Add(path))
                continue;
    
            // Check if asset changed since last capture
            if (!HasAssetChanged(prevCapture, path))
            {
                // Reuse existing data
                continue;
            }
    
            // Capture new data
            var assets = AssetDatabase.LoadAllAssetsAtPath(path);
            // Process your assets here
    
            await Task.Delay(10); // Yield control periodically
        }
    
        Progress.Remove(id);
        capture.lastCaptureTime = DateTime.UtcNow.ToFileTimeUtc();
        return capture;
    }
    

    How to Create a Custom Report

    1. Create a Report Class

    Create a class that inherits from AnalyzerIssueReportBase:

    class YourCustomReport : AnalyzerIssueReportBase
    {
        MultiColumnListView m_ReportContent;
        YourDataSource m_DataSource;
        List<YourFilteredData> m_Filtered = new();
    
        public YourCustomReport() : base(new[] { typeof(YourDataSource) })
        {
            SetReportListItemName("Your Report Name");
            SetReportListemCount("0");
            SetupReportContent();
        }
    
        public override VisualElement reportContent => m_ReportContent;
        public override VisualElement settingsContent => m_Settings; // or null
        public override string reportTitle => "Your Report Title";
    }
    

    2. Handle Data Source Changes

    Override OnReportDataSourceChanged to process data:

    protected override async void OnReportDataSourceChanged(IReportDataSource reportDataSource)
    {
        if (reportDataSource is YourDataSource dataSource)
        {
            m_DataSource = dataSource;
            await FilterData(dataSource);
        }
    }
    

    3. Implement Data Filtering

    Create asynchronous filtering logic:

    async Task FilterData(YourDataSource dataSource)
    {
        m_Filtered = new();
        var task = Task.Run(() =>
        {
            for (int i = 0; i < dataSource?.data?.Count; ++i)
            {
                var assetData = dataSource.data[i];
                // Apply your filtering logic here
                if (ShouldIncludeInReport(assetData))
                {
                    m_Filtered.Add(new YourFilteredData()
                    {
                        // Map your data
                    });
                }
            }
        });
    
        isFilteringReport = true;
        await task;
        m_ReportContent.itemsSource = m_Filtered;
        m_ReportContent.Rebuild();
        isFilteringReport = false;
        SetReportListemCount($"{m_Filtered.Count}");
    }
    

    4. Setup Report UI

    Configure the MultiColumnListView:

    void SetupReportContent()
    {
        var columns = new[]
        {
            new Column()
            {
                title = "Name",
                width = Length.Pixels(100),
                makeCell = () => new Label(),
                bindingPath = "name"
            },
            // Add more columns as needed
        };
    
        m_ReportContent = new MultiColumnListView();
        m_ReportContent.showAlternatingRowBackgrounds = AlternatingRowBackground.ContentOnly;
        m_ReportContent.selectionChanged += OnSelectionChanged;
    
        // Setup column binding
        for (int i = 0; i < columns.Length; ++i)
        {
            var bindingPath = columns[i].bindingPath;
            columns[i].bindCell = (e, k) =>
            {
                var label = e.Q<Label>();
                label.SetBinding("text", new DataBinding()
                {
                    dataSourcePath = new PropertyPath(bindingPath)
                });
                e.dataSource = m_Filtered[k];
            };
            m_ReportContent.columns.Add(columns[i]);
        }
    }
    

    5. Create Settings UI (Optional)

    If your report needs configurable settings:

    public class YourReportSettings : VisualElement
    {
        const string k_Uxml = "your-uxml-guid";
    
        public event Action<SettingsType> onApplyClickedEvent;
    
        public YourReportSettings(SettingsType initialSettings)
        {
            var path = AssetDatabase.GUIDToAssetPath(new GUID(k_Uxml));
            var uxml = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(path);
            uxml.CloneTree(this);
    
            // Setup UI elements and event handlers
        }
    }
    

    Key Features Demonstrated

    Data Source Features

    • Asynchronous Data Capture: Non-blocking data collection with progress reporting
    • Incremental Updates: Only recapture data when assets have changed
    • Save/Load Support: Persist captured data across sessions
    • Progress Reporting: Visual feedback during long-running operations

    Report Features

    • Custom Data Source Integration: Works with your custom data source
    • Asynchronous Filtering: Non-blocking data processing
    • Multi-Column Display: Flexible columnar data presentation
    • Interactive Selection: Object inspection integration
    • Configurable Settings: User-customizable report parameters
    • Real-time Updates: Automatic refresh when data changes
    In This Article
    Back to top
    Copyright © 2026 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)