惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security @ Cisco Blogs
罗磊的独立博客
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
T
The Blog of Author Tim Ferriss
美团技术团队
T
Tailwind CSS Blog
博客园 - 三生石上(FineUI控件)
博客园 - Franky
G
Google Developers Blog
Jina AI
Jina AI
Stack Overflow Blog
Stack Overflow Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
V
Visual Studio Blog
腾讯CDC
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements
博客园 - 叶小钗
Microsoft Security Blog
Microsoft Security Blog
雷峰网
雷峰网
L
LangChain Blog
Vercel News
Vercel News
Forbes - Security
Forbes - Security
PCI Perspectives
PCI Perspectives
N
News | PayPal Newsroom
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
J
Java Code Geeks
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
A
About on SuperTechFans
Attack and Defense Labs
Attack and Defense Labs
Google Online Security Blog
Google Online Security Blog
aimingoo的专栏
aimingoo的专栏
MongoDB | Blog
MongoDB | Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
Cloudbric
Cloudbric
B
Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Proofpoint News Feed
D
DataBreaches.Net
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
B
Blog RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
N
News and Events Feed by Topic

博客园 - 野生西瓜

[Unity 杂货铺] SpriteAtlas 和 SBP 打包 [Unity 杂货铺] 游戏字体选择 [Unity 杂货铺] 游戏项目的结构规划与初始化 [Unity 杂货铺] Git 配置与实践 [Unity 杂货铺] 引擎版本的选择 [Unity] 基础寻路算法 - 环境搭建 [Unity] 引擎脚本相关的字符串优化 [Unity] 基础寻路算法 - 代码实践 [Unity] 资源工作流程 - AssetPostprocessor [Unity] 资源工作流程 - ScriptedImporter [Unity] 资源工作流程 - 辅助工具 [Lua游戏AI开发指南] 笔记零 - 框架搭建 [GAME] [Civilization] 文明6字体及字体大小修改 [Unity] 编辑器运行中动态编译执行C#代码 [GAMEDEV] 个人开发如何找到合适的图片素材? [施工中] 博客导航 2021 的书 [BACKUP] Visual Studio Code 配置 [theHunterCOTW] 猎人荒野的召唤-一点资料
[Unity] 在软件标题栏显示工作路径
野生西瓜 · 2021-07-25 · via 博客园 - 野生西瓜

(一)问题

项目开发中常会有开多个分支,同时启动多个 Unity 程序的情况,来回切换的时候就容易混淆,有时候还需要用 Show In Explorer 或者其他标志来确认当前使用的是哪个分支。

于是想在标题栏上直接显示出当前的工作目录:

  • 修改前:
    20210725133002.png

    原本的标题栏由项目名、场景、工作平台等文本组成
  • 修改后:
    额外显示项目路径

    额外显示工作路径

(二)代码

1. 声明要调用的系统接口

using System;
using System.Runtime.InteropServices;
using System.Text;

public partial class UpdateUnityEditorProcess
{
    public delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData);
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern IntPtr GetWindow(HandleRef hWnd, int uCmd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool IsWindowVisible(HandleRef hWnd);

    [DllImport("user32.dll")]
    private static extern bool GetWindowText(int hWnd, StringBuilder title, int maxBufSize);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private extern static int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll", EntryPoint = "SetWindowText", CharSet = CharSet.Auto)]
    public extern static int SetWindowText(int hwnd, string lpString);
}

2. 工具类

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;

public partial class UpdateUnityEditorProcess
{
    public IntPtr hwnd = IntPtr.Zero;
    private bool haveMainWindow = false;
    private IntPtr mainWindowHandle = IntPtr.Zero;
    private int processId = 0;
    private IntPtr hwCurr = IntPtr.Zero;
    private static StringBuilder sbtitle = new StringBuilder(255);
    private static string UTitle = Application.dataPath;
    public static float lasttime = 0;

    private static UpdateUnityEditorProcess _instance;
    public static UpdateUnityEditorProcess Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new UpdateUnityEditorProcess();
                _instance.hwnd = _instance.GetMainWindowHandle(Process.GetCurrentProcess().Id);
            }
            return _instance;
        }
    }

    public void SetTitle()
    {
        //UnityEngine.Debug.Log(string.Format("{0} - {1}", Time.realtimeSinceStartup, lasttime));
        if (Time.realtimeSinceStartup > lasttime)
        {
            sbtitle.Length = 0;
            lasttime = Time.realtimeSinceStartup + 2f;
            int length = GetWindowTextLength(hwnd);

            GetWindowText(hwnd.ToInt32(), sbtitle, 255);
            string strTitle = sbtitle.ToString();
            string[] ss = strTitle.Split('-');
            if (ss.Length > 0 && !strTitle.Contains(UTitle))
            {
                SetWindowText(hwnd.ToInt32(), string.Format("{0} - {1}", UTitle, strTitle));
                UnityEngine.Debug.Log("Current Unity Title: " + UTitle);
            }
        }
    }

    public IntPtr GetMainWindowHandle(int processId)
    {
        if (!this.haveMainWindow)
        {
            this.mainWindowHandle = IntPtr.Zero;
            this.processId = processId;
            EnumThreadWindowsCallback callback = new EnumThreadWindowsCallback(this.EnumWindowsCallback);
            EnumWindows(callback, IntPtr.Zero);
            GC.KeepAlive(callback);

            this.haveMainWindow = true;
        }
        return this.mainWindowHandle;
    }

    private bool EnumWindowsCallback(IntPtr handle, IntPtr extraParameter)
    {
        int num;
        GetWindowThreadProcessId(new HandleRef(this, handle), out num);
        if ((num == this.processId) && this.IsMainWindow(handle))
        {
            this.mainWindowHandle = handle;
            return false;
        }
        return true;
    }

    private bool IsMainWindow(IntPtr handle)
    {
        return (!(GetWindow(new HandleRef(this, handle), 4) != IntPtr.Zero) && IsWindowVisible(new HandleRef(this, handle)));
    }
}

3. Editor 回调设置

#if UNITY_EDITOR_WIN
using UnityEditor;

[InitializeOnLoad]
class UpdateUnityEditorTitle
{
    private static bool isInGame = false;
    static UpdateUnityEditorTitle()
    {
        EditorApplication.delayCall += DoUpdateTitleFunc;

        EditorApplication.playmodeStateChanged += OnPlaymodeStateChanged;
    }

    static void OnPlaymodeStateChanged()
    {
        if (EditorApplication.isPlaying == isInGame) return;
        isInGame = EditorApplication.isPlaying;
        UpdateUnityEditorProcess.lasttime = 0;
        DoUpdateTitleFunc();
    }

    static void DoUpdateTitleFunc()
    {
        //UnityEngine.Debug.Log("DoUpdateTitleFunc");
        UpdateUnityEditorProcess.Instance.SetTitle();
    }
}
#endif

(三)备注

  1. [InitializeOnLoad] 的调用时机是在 Unity 初始化标题之前,所以直接在UpdateUnityEditorTitle() 中设置标题的话会被覆盖掉,这里利用EditorApplication.delayCall 调用标题修改
  2. 进入退出 GameMode 会触发 Unity 软件初始化标题,利用 EditorApplication.playmodeStateChanged 来调用自定义的标题修改