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

推荐订阅源

aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
博客园_首页
爱范儿
爱范儿
腾讯CDC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
K
Kaspersky official blog
H
Help Net Security
The GitHub Blog
The GitHub Blog
G
Google Developers Blog
S
SegmentFault 最新的问题
L
LINUX DO - 热门话题
T
Tenable Blog
P
Privacy & Cybersecurity Law Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LangChain Blog
L
Lohrmann on Cybersecurity
P
Palo Alto Networks Blog
云风的 BLOG
云风的 BLOG
A
Arctic Wolf
N
News and Events Feed by Topic
AWS News Blog
AWS News Blog
美团技术团队
U
Unit 42
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Secure Thoughts
有赞技术团队
有赞技术团队
C
Cyber Attacks, Cyber Crime and Cyber Security
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
B
Blog
NISL@THU
NISL@THU
Help Net Security
Help Net Security
Y
Y Combinator Blog
J
Java Code Geeks
S
Securelist
宝玉的分享
宝玉的分享
T
Threat Research - Cisco Blogs
S
Security @ Cisco Blogs
O
OpenAI News
D
DataBreaches.Net
Know Your Adversary
Know Your Adversary
Hacker News - Newest:
Hacker News - Newest: "LLM"
Vercel News
Vercel News
Forbes - Security
Forbes - Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed

博客园 - 野生西瓜

[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 来调用自定义的标题修改