init uniWindowController 0.9.8

This commit is contained in:
2026-06-14 22:49:28 +08:00
commit bfce70eb6f
95 changed files with 30126 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [0.1.0] - 2026-06-14
### This is the first release of *XUniWindowController*.
*Initial port of UniWindowController as a UPM package.*
#### Added
- Window transparency (Alpha / ColorKey modes)
- Click-through support (Opacity / Raycast / Manual hit-test)
- Topmost / Bottommost z-order control
- Maximize / Restore window state
- Borderless window mode
- Window position and size get/set
- Multi-monitor window fitting
- File drag-and-drop support
- Native file open / save dialogs
- UI drag-move handle
- Inspector editor integration with PlayerSettings validation
- All 5 sample scenes ported as UPM samples
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 816edf7e62e07ff48b6595a4f95d1903
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 21a499598678f954687f9bb157795380
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+91
View File
@@ -0,0 +1,91 @@
# XUniWindowController
**XUniWindowController** is a Unity native plugin that provides comprehensive standalone application window control for Windows and macOS builds.
It is a UPM package port of [UniWindowController](https://github.com/kirurobo/UniWindowController) by Kirurobo.
## Usage
Add the `UniWindowController` component to any GameObject in your scene. Access it via:
```csharp
var winc = UniWindowController.current;
winc.isTransparent = true;
winc.isTopmost = true;
winc.windowSize = new Vector2(1280, 720);
```
### Properties
| Property | Type | Description |
|----------|------|-------------|
| `isTransparent` | `bool` | Enable/disable window transparency |
| `alphaValue` | `float` | Window opacity (0.0 ~ 1.0) |
| `isTopmost` | `bool` | Keep window always on top |
| `isBottommost` | `bool` | Keep window always on bottom |
| `isZoomed` | `bool` | Maximize/restore window |
| `isClickThrough` | `bool` | Enable/disable mouse click-through |
| `windowPosition` | `Vector2Int` | Get/set window position (screen coordinates) |
| `windowSize` | `Vector2Int` | Get/set window size (pixels) |
| `clientSize` | `Vector2Int` | Get client area size (read-only) |
| `allowDropFiles` | `bool` | Enable/disable file drag-and-drop |
| `hitTestType` | `HitTestType` | Click-through detection mode |
| `transparentType` | `TransparentType` | Transparency algorithm |
| `shouldFitMonitor` | `bool` | Fit window to a specific monitor |
| `monitorToFit` | `int` | Target monitor index |
### Events
| Event | Description |
|-------|-------------|
| `OnStateChanged` | Fired when window style, z-order, or size changes |
| `OnMonitorChanged` | Fired when display configuration changes (resolution, monitor count) |
| `OnDropFiles` | Fired when files are dragged and dropped onto the window |
## Samples
Import samples via **Package Manager > XUniWindowController > Samples**.
The following samples are included:
- **Menu** — Navigation menu for browsing all sample scenes
- **SimpleSample** — Minimal setup demonstrating basic window control
- **UiSample** — Complete UI with toggles, sliders, dropdowns for all window properties
- **Fullscreen** — Fullscreen mode with right-click context menu and a 3D snowman scene
- **FileDialog** — Native file open/save dialog integration
## Technical Details
### Windows Architecture
The plugin uses `LibUniWinC.dll` (C++ Win32 DLL) to control window behavior through native Win32 API calls:
- `SetWindowLong` / `GetWindowLong` — Window style modification (borderless, layered, click-through)
- `SetWindowPos` — Position, size, and Z-order (topmost/bottommost)
- `DwmExtendFrameIntoClientArea` — DWM alpha transparency
- `SetLayeredWindowAttributes` — ColorKey transparency mode
- `SetWindowsHookEx` / custom `WNDPROC` — Window message interception (file drop, display change, resize)
- `DragAcceptFiles` — File drag-and-drop
- `GetOpenFileNameW` / `GetSaveFileNameW` — Native file dialogs
### macOS Architecture
The plugin uses `LibUniWinC.bundle` (Swift / Cocoa) to control window behavior through Cocoa APIs:
- `NSWindow.styleMask` — Borderless mode
- `NSWindow.level` — Z-order (topmost/bottommost)
- `NSWindow.isOpaque` / `backgroundColor` — Transparency
- `NSWindow.ignoresMouseEvents` — Click-through
- `NSDraggingDestination` protocol — File drag-and-drop
- `NSOpenPanel` / `NSSavePanel` — Native file dialogs
- `constrainFrameRect` method swizzling — Free window positioning
- `NotificationCenter` — Window state change observation
### Package Contents
| Location | Description |
|----------|-------------|
| `Runtime/Scripts/` | Core C# scripts (UniWindowController, UniWinCore, FilePanel, etc.) |
| `Runtime/Plugins/` | Native plugin binaries (.dll for Windows, .bundle for macOS) |
| `Editor/Scripts/` | Editor extensions (Inspector UI, batch build, PlayerSettings validation) |
| `Samples~/` | Sample scenes and scripts |
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1c67695e4c6a6d24f9fcdc3374c61073
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f7d50d96d22abb249aa89d22ffeceb31
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

+114
View File
@@ -0,0 +1,114 @@
fileFormatVersion: 2
guid: 77aeb9a6b2955d14cbc743feb1a80851
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e98c5a3fed598e34c9be86e1c42c95a6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
{
"name": "Lrss3.Xuniwindowcontroller.Editor",
"references": [
"Lrss3.Xuniwindowcontroller"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f2ccb850d779ff749a565c6e5d95eaff
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 136bc4b3fb1edb04a814503bea6b7ed5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,67 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
// ReSharper disable UnusedMember.Local
namespace Kirurobo
{
class UniWindowControllerBatch
{
//[MenuItem("Build/Build OSX")]
static void PerformBuild()
{
// コマンドライン引数の最後が出力パスだとする
//string outputPath = System.Environment.GetCommandLineArgs().Last();
// var buildPlayerOptions = new BuildPlayerOptions();
// buildPlayerOptions.scenes = sceneList.ToArray();
// buildPlayerOptions.locationPathName = outputPath;
// buildPlayerOptions.target = BuildTarget.StandaloneOSX;
// buildPlayerOptions.options = BuildOptions.None;
// 事前にエディタから設定したビルド設定を利用
var scenes = EditorBuildSettings.scenes;
var buildTarget = EditorUserBuildSettings.activeBuildTarget;
var locationPath = EditorUserBuildSettings.GetBuildLocation(buildTarget);
// ビルド対象は環境に合わせて上書き
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
buildTarget = BuildTarget.StandaloneOSX;
locationPath = "Builds/macOS/" + Application.productName;
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
buildTarget = BuildTarget.StandaloneWindows64;
locationPath = "Builds/Win64/" + Application.productName;
#endif
var buildPlayerOptions = new BuildPlayerOptions
{
scenes = EditorBuildSettingsScene.GetActiveSceneList(scenes),
locationPathName = locationPath,
target = buildTarget,
options = BuildOptions.None
};
// // 内容チェック用
// foreach (var scene in buildPlayerOptions.scenes)
// {
// Debug.Log(scene);
// }
// Debug.Log(buildPlayerOptions.locationPathName);
// return;
var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
var summary = report.summary;
if (summary.result == BuildResult.Succeeded)
{
Debug.Log("构建成功");
} else if (summary.result == BuildResult.Failed)
{
Debug.Log("构建失败");
//EditorApplication.Exit(1);
throw new BuildFailedException(report.summary.ToString());
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9aa8110448ede05409f3ce652b3ad2d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+591
View File
@@ -0,0 +1,591 @@
/*
* UniWindowControllerEditor.cs
*
* Author: Kirurobo http://x.com/kirurobo
* License: MIT
*/
// Assembry Definition を有効にしてから、ビルド時に Editor クラスがないとエラーが出る。
// そこで丸ごと UNITY_EDITOR が無い場合は無視するものとした
#if UNITY_EDITOR
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Reflection;
using UnityEngine.Rendering;
namespace Kirurobo
{
/// <summary>
/// UniWindowControllerのためのエディタカスタマイズ部分
/// </summary>
[CustomEditor(typeof(UniWindowController))]
public class UniWindowControllerEditor : Editor
{
/// <summary>
/// カーソル下の色を表示するためのプロパティ
/// </summary>
SerializedProperty pickedColor;
/// <summary>
/// ゲームビューのウィンドウ
/// </summary>
private EditorWindow gameViewWindow;
/// <summary>
/// プロジェクト設定に関する警告を閉じておくか
private bool isWarningDismissed = false;
/// <summary>
/// URP に関する警告を閉じておくか
/// </summary>
private bool isUrpWarningDismissed = true;
/// <summary>
/// URP が有効かどうか
/// </summary>
private bool hasUrp = false;
void OnEnable()
{
LoadSettings();
pickedColor = serializedObject.FindProperty("pickedColor");
// URP が有効か否かを判定
hasUrp = GetUrpSettings();
}
void OnDisable()
{
SaveSettings();
}
/// <summary>
/// URPが有効か否かを検出
/// </summary>
/// <returns></returns>
private bool GetUrpSettings()
{
var renderPipelineAsset = GraphicsSettings.defaultRenderPipeline;
if (renderPipelineAsset == null || renderPipelineAsset.GetType().Name != "UniversalRenderPipelineAsset")
{
// URP が設定されていない
return false;
}
return true;
}
private void LoadSettings()
{
isWarningDismissed = EditorUserSettings.GetConfigValue("WindowController_IS_WARNING DISMISSED") == "1";
}
private void SaveSettings()
{
EditorUserSettings.SetConfigValue("WindowController_IS_WARNING DISMISSED", isWarningDismissed ? "1" : "0");
}
/// <summary>
/// インスペクタでの表示をカスタマイズ
/// </summary>
/// <description>
/// 参考情報および、推奨設定の変更欄を表示します。
/// </description>
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
// カーソル下の色が得られていれば、その透明度を参考として表示
if (pickedColor != null)
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.LabelField("选取的Alpha值", pickedColor.colorValue.a.ToString("P0"));
EditorGUI.EndDisabledGroup();
}
// Project Settings の推奨設定を表示
isWarningDismissed = ShowPlayerSettingsValidation(isWarningDismissed);
// URP 関連の推奨設定を表示
isUrpWarningDismissed = ShowUrpSettingsValidation(isUrpWarningDismissed);
}
/// <summary>
/// Project Settings に関する推奨設定の自動設定欄を表示
/// </summary>
private bool ShowPlayerSettingsValidation(bool dismissed) {
// 以下は Project Settings 関連
EditorGUILayout.Space();
bool enableValidation = EditorGUILayout.Foldout(!dismissed, "播放器设置验证");
// チェックするかどうかを記憶
if (enableValidation == dismissed)
{
dismissed = !enableValidation;
}
// 推奨設定のチェック
//if (!isWarningDismissed)
if (enableValidation)
{
if (ValidateSettings(false))
{
// 应用所有推荐设置
GUI.backgroundColor = Color.green;
if (GUILayout.Button(
"✔ 将所有设置修复为推荐值",
GUILayout.MinHeight(25f)
))
{
ValidateSettings(true);
}
// 关闭验证
GUI.backgroundColor = Color.yellow;
if (GUILayout.Button(
"✘ 关闭此验证",
GUILayout.MinHeight(25f)
))
{
dismissed = true;
//SaveSettings(); // 如果需要立即保存,取消注释
}
EditorGUILayout.Space();
}
else
{
GUI.color = Color.green;
GUILayout.Label("OK");
}
// 打开播放器设置页面
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUI.color = Color.white;
GUI.backgroundColor = Color.white;
if (GUILayout.Button(
"打开播放器设置",
GUILayout.MinHeight(25f), GUILayout.Width(200f)
))
{
SettingsService.OpenProjectSettings("Project/Player");
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
return dismissed;
}
/// <summary>
/// URP に関する推奨設定の自動設定欄を表示
/// </summary>
private bool ShowUrpSettingsValidation(bool dismissed) {
// URP が無効ならば何もしない
if (!hasUrp) return dismissed;
// 以下は URP 関連の自動設定
EditorGUILayout.Space();
bool enableValidation = EditorGUILayout.Foldout(!dismissed, "URP 设置验证");
// チェックするかどうかを記憶
if (enableValidation == dismissed)
{
dismissed = !enableValidation;
}
// 推奨設定のチェック
//if (!isWarningDismissed)
if (enableValidation)
{
if (ValidateUrpSettings(false))
{
// 应用所有推荐设置
GUI.backgroundColor = Color.green;
if (GUILayout.Button(
"✔ 将所有设置修复为推荐值",
GUILayout.MinHeight(25f)
))
{
ValidateUrpSettings(true);
}
// 关闭验证
GUI.backgroundColor = Color.yellow;
if (GUILayout.Button(
"✘ 关闭此验证",
GUILayout.MinHeight(25f)
))
{
dismissed = true;
}
EditorGUILayout.Space();
}
else
{
GUI.color = Color.green;
GUILayout.Label("OK");
}
EditorGUILayout.Space();
}
return dismissed;
}
private delegate void FixMethod();
/// <summary>
/// 显示或修复设置
/// </summary>
/// <param name="message">警告消息</param>
/// <param name="fixAction">修复操作</param>
/// <param name="silentFix">false: 显示警告和修复按钮, true: 静默修复</param>
private void FixSetting(string message, FixMethod fixAction, bool silentFix = false)
{
if (silentFix)
{
// 修复
fixAction.Invoke();
}
else
{
// 显示消息和修复按钮
EditorGUILayout.BeginHorizontal();
EditorGUILayout.HelpBox(message, MessageType.Warning, true);
GUILayout.FlexibleSpace();
EditorGUILayout.BeginVertical();
EditorGUILayout.Space();
if (GUILayout.Button("修复", GUILayout.Width(60f))) { fixAction.Invoke(); }
//GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
}
/// <summary>
/// 仅显示建议
/// </summary>
/// <param name="message">警告消息</param>
private void ShowInfo(string message, Object target = null)
{
// 显示消息和定位按钮
EditorGUILayout.BeginHorizontal();
EditorGUILayout.HelpBox(message, MessageType.Info, true);
GUILayout.FlexibleSpace();
// 自動設定できない対象は、プロジェクトウィンドウで示すのみ
if (target != null)
{
EditorGUILayout.BeginVertical();
EditorGUILayout.Space();
if (GUILayout.Button("定位", GUILayout.Width(60f))) { EditorGUIUtility.PingObject(target); }
//GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndHorizontal();
}
/// <summary>
/// 验证播放器设置
/// </summary>
/// <param name="silentFix">false: 显示警告和修复按钮, true: 静默修复</param>
/// <returns>如果有无效项则返回 true</returns>
private bool ValidateSettings(bool silentFix = false)
{
bool invalid = false;
// バックグラウンドでも実行する。クリックスルー切替などで必要
if (!PlayerSettings.runInBackground)
{
invalid = true;
FixSetting(
"强烈建议启用'后台运行'。",
() => PlayerSettings.runInBackground = true,
silentFix
);
}
// サイズ変更可能なウィンドウとする。必須ではないがウィンドウ枠無効時にサイズも変わるので変更可能である方が自然
if (!PlayerSettings.resizableWindow)
{
invalid = true;
FixSetting(
"建议启用'可调整窗口大小'。",
() => PlayerSettings.resizableWindow = true,
silentFix
);
}
// フルスクリーンでなくウィンドウとする
#if UNITY_2018_1_OR_NEWER
// Unity 2018 からはフルスクリーン指定の仕様が変わった
if (PlayerSettings.fullScreenMode != FullScreenMode.Windowed)
{
invalid = true;
FixSetting(
"在'全屏模式'中选择'窗口化'。",
() => PlayerSettings.fullScreenMode = FullScreenMode.Windowed,
silentFix
);
}
#else
if (PlayerSettings.defaultIsFullScreen)
{
invalid = true;
FixSetting(
"不建议启用'默认全屏'。",
() => PlayerSettings.defaultIsFullScreen = false,
silentFix
);
}
#endif
// フルスクリーンとウィンドウの切替を無効とする
if (PlayerSettings.allowFullscreenSwitch)
{
invalid = true;
FixSetting(
"禁止全屏切换。",
() => PlayerSettings.allowFullscreenSwitch = false,
silentFix
);
}
// Windowsでは Use DXGI Flip Mode Swapchain を無効にしないと透過できない
// ↓Unity 2019.1.6未満だと useFlipModelSwapchain は無いはず
// なので除外のため書き連ねてあるが、ここまでサポートしなくて良い気もする。
#if UNITY_2019_1_6
#elif UNITY_2019_1_5
#elif UNITY_2019_1_4
#elif UNITY_2019_1_3
#elif UNITY_2019_1_2
#elif UNITY_2019_1_1
#elif UNITY_2019_1_0
#elif UNITY_2019_1_OR_NEWER
// Unity 2019.1.7 以降であれば、Player 設定 の Use DXGI Flip... 無効化を推奨
if (PlayerSettings.useFlipModelSwapchain)
{
invalid = true;
FixSetting(
"禁用'使用 DXGI 翻转模式交换链'以使窗口透明。",
() => PlayerSettings.useFlipModelSwapchain = false,
silentFix
);
}
// Direct3D12 は透過ウィンドウに対応していないので、Graphics APIs for Windows から除外することを推奨
if (PlayerSettings.GetUseDefaultGraphicsAPIs(BuildTarget.StandaloneWindows))
{
// 自動の場合も警告を出す
ShowInfo(
"Direct3D12 不支持透明窗口。" +
"请考虑在播放器设置中使用 Direct3D11 替代'自动图形 API for Windows'设置。",
null
);
}
else if (PlayerSettings.GetGraphicsAPIs(BuildTarget.StandaloneWindows).Contains(GraphicsDeviceType.Direct3D12))
{
// Graphhics APIs for Windows に Direct3D12 が含まれている場合は警告を出す
ShowInfo(
"Direct3D12 不支持透明窗口。" +
"请从播放器设置的'图形 API for Windows'中移除 Direct3D12。",
null
);
}
#endif
return invalid;
}
/// <summary>
/// 验证播放器设置
/// </summary>
/// <param name="silentFix">false: 显示警告和修复按钮, true: 静默修复</param>
/// <returns>如果有无效项则返回 true</returns>
private bool ValidateUrpSettings(bool silentFix = false)
{
bool invalid = false;
// Universal Render Pipelineが有効ならば、HDRの無効化を推奨
foreach (var cam in Camera.allCameras)
{
if (cam.allowHDR) {
string name = cam.name;
invalid = true;
FixSetting(
$"{name}:禁用摄像机中的'HDR'以使窗口透明。",
() => cam.allowHDR = false,
silentFix
);
}
if (cam.allowMSAA) {
string name = cam.name;
invalid = true;
FixSetting(
$"{name}:禁用摄像机中的'MSAA'以使窗口透明。",
() => cam.allowMSAA = false,
silentFix
);
}
}
var urpAsset = GraphicsSettings.defaultRenderPipeline;
if (hasUrp && urpAsset != null)
{
// hasUrp == true の時点で urpAsset は UniversalRenderPipelineAsset であるはず。そのため allowPostProcessAlphaOutput があるはず
var alphaProcessingProperty = urpAsset.GetType().GetProperty("allowPostProcessAlphaOutput", BindingFlags.Public | BindingFlags.Instance);
if (alphaProcessingProperty != null)
{
var alphaProcessing = alphaProcessingProperty.GetValue(urpAsset);
if (!(bool)alphaProcessing)
{
invalid = true;
ShowInfo(
"在 URP 资源中启用'Alpha 处理'",
urpAsset
);
}
}
}
return invalid;
}
}
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class UniWindowControllerReadOnlyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
/// <summary>
/// 设置布尔属性为可编辑
/// 参考: http://ponkotsu-hiyorin.hateblo.jp/entry/2015/10/20/003042
/// 参考: https://forum.unity.com/threads/c-class-property-with-reflection-in-propertydrawer-not-saving-to-prefab.473942/
/// </summary>
[CustomPropertyDrawer(typeof(EditablePropertyAttribute))]
public class UniWindowControllerDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
//base.OnGUI(position, property, label);
Object obj = property.serializedObject.targetObject;
// Range(min, max) が設定されていれば取得
FieldInfo fieldInfo = obj.GetType().GetField(
property.name,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static
);
var rangeAttrs = fieldInfo?.GetCustomAttributes(typeof(RangeAttribute), true) as RangeAttribute[];
RangeAttribute range = (rangeAttrs?.Length > 0 ? rangeAttrs.First() : null);
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
// 変数の先頭が '_' であることが動作の条件
if (property.name[0] == '_')
{
string propertyName = property.name.Substring(1); // '_' なしをプロパティ名として取得
PropertyInfo info = obj.GetType().GetProperty(propertyName);
MethodInfo getMethod = default(MethodInfo);
MethodInfo setMethod = default(MethodInfo);
if (info.CanRead) { getMethod = info.GetGetMethod(); }
if (info.CanWrite) { setMethod = info.GetSetMethod(); }
if (property.type == "bool")
{ var oldValue = property.boolValue;
if (getMethod != null)
{
oldValue = (bool)getMethod.Invoke(obj, null);
}
GUI.enabled = (setMethod != null);
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
var newValue = property.boolValue;
if ((setMethod != null) && (oldValue != newValue))
{
setMethod.Invoke(obj, new[] { (object)newValue });
}
}
else if (property.type == "float")
{
var oldValue = property.floatValue;
if (getMethod != null)
{
oldValue = (float) getMethod.Invoke(obj, null);
}
GUI.enabled = (setMethod != null);
if (range != null)
{
EditorGUI.Slider(position, property, range.min, range.max, label);
}
else
{
EditorGUI.PropertyField(position, property, label, true);
}
GUI.enabled = true;
var newValue = property.floatValue;
if ((setMethod != null) && (oldValue != newValue))
{
setMethod.Invoke(obj, new[] {(object) newValue});
}
}
else
{
// bool, float 以外は今のところ非対応で Readonly とする
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
else
{
// 只读
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
else
{
// Range 指定があればスライダー
if (range != null)
{
EditorGUI.Slider(position, property, range.min, range.max, label);
}
else
{
EditorGUI.PropertyField(position, property, label, true);
}
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4132cf6e84b9d6e4488bce4df8f1bb67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+86
View File
@@ -0,0 +1,86 @@
# XUniWindowController
**XUniWindowController** is a Unity native plugin that provides comprehensive **standalone application window control** for Windows and macOS builds.
It allows you to control window transparency, click-through, topmost/bottommost z-order, maximize, file drop, native file dialogs, and multi-monitor window fitting — all from C# scripts through a simple Unity component.
## Features
- **Window Transparency** — Alpha blending or ColorKey-based transparency
- **Click-Through** — Make the window ignore mouse events
- **Z-Order Control** — Set window topmost, bottommost, or normal
- **Maximize / Restore** — Themed maximize and restore
- **Borderless Mode** — Hide the title bar and border
- **Window Position & Size** — Get/set window position and size
- **Multi-Monitor Support** — Fit window to any connected display
- **File Drop** — Receive file paths from drag-and-drop
- **Native File Dialogs** — Open and save file dialogs (Windows/macOS native)
- **Drag Move** — Drag the window by any UI element
## Requirements
- Unity 2022.3 or later
- Windows or macOS standalone build target
## Installation
### Via Package Manager
1. Open **Window > Package Manager**
2. Click the **+** button > **Add package from git URL**
3. Enter the repository URL
### Via disk (local package)
1. Copy the `com.lrss3.xuniwindowcontroller` folder to your project's `Packages/` directory
2. Package Manager will automatically pick it up
## Getting Started
1. Add a **UniWindowController** component to any GameObject in your scene (or use the prefab from samples)
2. Configure the Inspector properties:
- **Is Transparent** — Enable window transparency
- **Is Topmost** — Keep window on top
- **Hit Test Type** — Choose opacity-based or raycast-based click-through detection
3. Call methods or toggle properties at runtime:
```csharp
var winc = UniWindowController.current;
winc.isTransparent = true; // Enable transparency
winc.isTopmost = true; // Keep on top
winc.isClickThrough = false; // Disable click-through
```
## Samples
The package includes 5 sample scenes:
| Sample | Description |
|--------|-------------|
| **Menu** | Navigation menu for browsing all samples |
| **SimpleSample** | Minimal window control setup |
| **UiSample** | Full UI with toggles, sliders, dropdowns for all features |
| **Fullscreen** | Fullscreen mode + right-click context menu + 3D scene |
| **FileDialog** | Native open/save file dialog demonstration |
Import samples via **Package Manager > XUniWindowController > Samples**.
## How It Works
On **Windows**, the plugin uses `LibUniWinC.dll` (C++ Win32 API) to call:
- `SetWindowLong` — modify window styles
- `SetWindowPos` — control z-order and position
- `DwmExtendFrameIntoClientArea` — DWM transparency
- `SetLayeredWindowAttributes` — layered window transparency
- `DragAcceptFiles` / window subclassing — file drop handling
On **macOS**, the plugin uses `LibUniWinC.bundle` (Swift / Cocoa) to call:
- `NSWindow.styleMask` — borderless mode
- `NSWindow.level` — z-order control
- `NSWindow.isOpaque` / `backgroundColor` — transparency
- `NSDraggingDestination` protocol — file drop
- `NSOpenPanel` / `NSSavePanel` — native dialogs
## License
This package is based on [UniWindowController](https://github.com/kirurobo/UniWindowController) by Kirurobo, licensed under the MIT License.
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 288347fd9d4e9a2488d0b8cc74fb4da2
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f24a9f4fa29049144bf79a526b2bb586
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,6 @@
{
"name": "Lrss3.Xuniwindowcontroller",
"references": [],
"includePlatforms": [],
"excludePlatforms": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4df3d67771614d24192762dbffd67d98
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b9161ad40887a5746afa1d4eb780f60a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 186ab6f8e4a960342b3bac311f6b2eb5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 04ad9f4fa34d2ed43a2c2a2b15c9959e
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>25C56</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>LibUniWinC</string>
<key>CFBundleIdentifier</key>
<string>com.kirurobo.LibUniWinC</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>LibUniWinC</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>0.9.8</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>25B74</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>26.1</string>
<key>DTSDKBuild</key>
<string>25B74</string>
<key>DTSDKName</key>
<string>macosx26.1</string>
<key>DTXcode</key>
<string>2610</string>
<key>DTXcodeBuild</key>
<string>17B55</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019-2025 kirurobo. All rights reserved.</string>
</dict>
</plist>
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d40a6da1308b51499d69d09998a1672
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 52438bdad5ba3524daae6d7c7050491e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 2587e857c903c7648bb243f26d8388c5
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1afe0de787d9c9b419c0083741340611
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 94d4ed4ccee04184c93f70e00c6f442c
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5807c8581d7c4be4ca838592a3a5ce8e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4ecd974a2286b994b8432ec73c8e5bd2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+209
View File
@@ -0,0 +1,209 @@
using AOT;
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Kirurobo
{
/// <summary>
/// 提供打开原生文件对话框的静态方法
/// </summary>
public class FilePanel
{
protected class LibUniWinC
{
[DllImport("LibUniWinC", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenFilePanel(in PanelSettings settings, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder buffer, UInt32 bufferSize);
[DllImport("LibUniWinC", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenSavePanel(in PanelSettings settings, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder buffer, UInt32 bufferSize);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PanelSettings : IDisposable {
public Int32 structSize;
public Int32 flags;
public IntPtr lpszTitle;
public IntPtr lpszFilter;
public IntPtr lpszInitialFile;
public IntPtr lpszInitialDir;
public IntPtr lpszDefaultExt;
public PanelSettings(Settings settings)
{
this.structSize = 0;
//this.structSize = 4 * 2 + Marshal.SizeOf<IntPtr>() * 3;
this.flags = (Int32)settings.flags;
//this.lpTitleText = IntPtr.Zero;
//this.lpFilterText = IntPtr.Zero;
//this.lpDefaultPath = IntPtr.Zero;
this.lpszTitle = Marshal.StringToHGlobalUni(settings.title);
this.lpszFilter = Marshal.StringToHGlobalUni(Filter.Join(settings.filters));
this.lpszInitialFile = Marshal.StringToHGlobalUni(settings.initialFile);
this.lpszInitialDir = Marshal.StringToHGlobalUni(settings.initialDirectory);
//this.lpszDefaultExt = Marshal.StringToHGlobalUni(settings.defaultExtension);
this.lpszDefaultExt = IntPtr.Zero;
//this.structSize = Marshal.SizeOf(this);
this.structSize = Marshal.SizeOf(this);
}
public void Dispose()
{
if (this.lpszTitle != IntPtr.Zero)
{
Marshal.FreeHGlobal(lpszTitle);
this.lpszTitle = IntPtr.Zero;
}
if (this.lpszFilter!= IntPtr.Zero)
{
Marshal.FreeHGlobal(lpszFilter);
this.lpszFilter= IntPtr.Zero;
}
if (this.lpszInitialFile!= IntPtr.Zero)
{
Marshal.FreeHGlobal(lpszInitialFile);
this.lpszInitialFile= IntPtr.Zero;
}
if (this.lpszInitialDir != IntPtr.Zero)
{
Marshal.FreeHGlobal(lpszInitialDir);
this.lpszInitialDir = IntPtr.Zero;
}
if (this.lpszDefaultExt != IntPtr.Zero)
{
Marshal.FreeHGlobal(lpszDefaultExt);
this.lpszDefaultExt = IntPtr.Zero;
}
}
}
}
/// <summary>
/// 对话框的设置标志
/// </summary>
[Flags]
public enum Flag
{
None = 0,
FileMustExist = 1, // 仅 Windows
FolderMustExist = 2, // 仅 Windows
AllowMultipleSelection = 4,
CanCreateDirectories = 16,
OverwritePrompt = 256, // macOS 上始终启用
CreatePrompt = 512, // macOS 上始终启用
ShowHiddenFiles = 4096,
RetrieveLink = 8192,
}
/// <summary>
/// 文件对话框的参数
/// </summary>
public struct Settings
{
public string title;
public Filter[] filters;
public string initialDirectory;
public string initialFile;
public string defaultExtension; // 未实现
public Flag flags;
}
/// <summary>
/// 文件过滤器
/// </summary>
public class Filter
{
protected string title;
protected string[] extensions;
/// <summary>
///
/// </summary>
/// <param name="title">过滤器标题(macOS 上暂不可用)</param>
/// <param name="extensions">扩展名数组,如 ["png", "jpg", "txt"]</param>
public Filter(string title, params string[] extensions)
{
this.title = title;
this.extensions = extensions;
}
public override string ToString()
{
return title + "\t" + String.Join("\t", extensions);
}
/// <summary>
/// 返回由 Filter 数组转换后的字符串
/// </summary>
/// <param name="filters"></param>
/// <returns></returns>
public static string Join(Filter[] filters)
{
if (filters == null) return "";
string result = "";
bool isFirstItem = true;
foreach (var filter in filters) {
if (!isFirstItem) result += "\n";
result += filter.ToString();
isFirstItem = false;
}
return result;
}
}
/// <summary>
/// 用于传递文件或文件夹路径的 UTF-16 缓冲区字符数
/// 因为多个路径以换行符分隔,260 字符不够用。
/// </summary>
private const int pathBufferSize = 2560;
/// <summary>
/// 打开文件选择对话框
/// </summary>
/// <param name="settings"></param>
/// <param name="action"></param>
public static void OpenFilePanel(Settings settings, Action<string[]> action)
{
LibUniWinC.PanelSettings ps = new LibUniWinC.PanelSettings(settings);
StringBuilder sb = new StringBuilder(pathBufferSize);
if (LibUniWinC.OpenFilePanel(in ps, sb, (uint)sb.Capacity))
{
string[] files = UniWinCore.parsePaths(sb.ToString());
action.Invoke(files);
}
ps.Dispose(); // 通过传入 Settings 的构造函数分配了内存,因此需要释放
}
/// <summary>
/// 打开保存文件选择对话框
/// </summary>
/// <param name="settings"></param>
/// <param name="action"></param>
public static void SaveFilePanel(Settings settings, Action<string[]> action)
{
LibUniWinC.PanelSettings ps = new LibUniWinC.PanelSettings(settings);
StringBuilder sb = new StringBuilder(pathBufferSize);
if (LibUniWinC.OpenSavePanel(in ps, sb, (uint)sb.Capacity))
{
string[] files = UniWinCore.parsePaths(sb.ToString());
action.Invoke(files);
}
ps.Dispose(); // 通过传入 Settings 的构造函数分配了内存,因此需要释放
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ade63dbb28ba23c40bfc1795ae2b605c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+926
View File
@@ -0,0 +1,926 @@
/*
* UniWinCore.cs
*
* Author: Kirurobo http://twitter.com/kirurobo
* License: MIT 许可证
*/
using System;
using System.Runtime.InteropServices;
using AOT;
using UnityEngine;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Kirurobo
{
/// <summary>
/// LibUniWinC 的原生插件包装
/// </summary>
internal class UniWinCore : IDisposable
{
/// <summary>
/// 仅 Windows 下的透明方法类型
/// </summary>
public enum TransparentType : int
{
None = 0,
Alpha = 1,
ColorKey = 2,
}
/// <summary>
/// 状态变更事件类型(实验性)
/// </summary>
[Flags]
public enum WindowStateEventType : int
{
None = 0,
StyleChanged = 1,
Resized = 2,
// 后续规格可能会有变更
TopMostEnabled = 16 + 1 + 8,
TopMostDisabled = 16 + 1,
BottomMostEnabled = 32 + 1 + 8,
BottomMostDisabled = 32 + 1,
WallpaperModeEnabled = 64 + 1 + 8,
WallpaperModeDisabled = 64 + 1,
};
#region Native functions
protected class LibUniWinC
{
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void StringCallback([MarshalAs(UnmanagedType.LPWStr)] string returnString);
[UnmanagedFunctionPointer((CallingConvention.Winapi))]
public delegate void IntCallback([MarshalAs(UnmanagedType.I4)] int value);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsActive();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsTransparent();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsBorderless();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsTopmost();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsBottommost();
[DllImport("LibUniWinC", CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsMaximized();
[DllImport("LibUniWinC", CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsFreePositioningEnabled();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AttachMyWindow();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AttachMyOwnerWindow();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AttachMyActiveWindow();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DetachWindow();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void Update();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetTransparent([MarshalAs(UnmanagedType.U1)] bool bEnabled);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetBorderless([MarshalAs(UnmanagedType.U1)] bool bEnabled);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetAlphaValue(float alpha);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetClickThrough([MarshalAs(UnmanagedType.U1)] bool bEnabled);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetTopmost([MarshalAs(UnmanagedType.U1)] bool bEnabled);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetBottommost([MarshalAs(UnmanagedType.U1)] bool bEnabled);
[DllImport("LibUniWinC", CallingConvention = CallingConvention.Winapi)]
public static extern void SetMaximized([MarshalAs(UnmanagedType.U1)] bool bZoomed);
[DllImport("LibUniWinC", CallingConvention = CallingConvention.Winapi)]
public static extern void EnableFreePositioning([MarshalAs(UnmanagedType.U1)] bool bEnabled);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetPosition(float x, float y);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetPosition(out float x, out float y);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetSize(float x, float y);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetSize(out float x, out float y);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetClientSize(out float width, out float height);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetClientRectangle(out float x, out float y, out float width, out float height);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RegisterDropFilesCallback([MarshalAs(UnmanagedType.FunctionPtr)] StringCallback callback);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnregisterDropFilesCallback();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RegisterMonitorChangedCallback([MarshalAs(UnmanagedType.FunctionPtr)] IntCallback callback);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnregisterMonitorChangedCallback();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RegisterWindowStyleChangedCallback([MarshalAs(UnmanagedType.FunctionPtr)] IntCallback callback);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnregisterWindowStyleChangedCallback();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetAllowDrop([MarshalAs(UnmanagedType.U1)] bool enabled);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern int GetCurrentMonitor();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern int GetMonitorCount();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorRectangle(int index, out float x, out float y, out float width, out float height);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetCursorPosition(float x, float y);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorPosition(out float x, out float y);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern int GetMouseButtons();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern int GetModifierKeys();
#region Windows
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetTransparentType(int type);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern void SetKeyColor(uint colorref);
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
public static extern int GetDebugInfo();
[DllImport("LibUniWinC",CallingConvention=CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AttachWindowHandle(IntPtr hWnd);
#endregion
}
#endregion
static string[] lastDroppedFiles;
static bool wasDropped = false;
static bool wasMonitorChanged = false;
static bool wasWindowStyleChanged = false;
static WindowStateEventType windowStateEventType = WindowStateEventType.None;
#if UNITY_EDITOR
/// <summary>
/// 获取 Unity 编辑器窗口
/// </summary>
/// <returns></returns>
/// <seealso href="http://baba-s.hatenablog.com/entry/2017/09/17/135018"/>
public static EditorWindow GetGameView()
{
var assembly = typeof(EditorWindow).Assembly;
var type = assembly.GetType("UnityEditor.GameView");
var gameView = EditorWindow.GetWindow(type);
return gameView;
}
#endif
/// <summary>
/// 判断窗口是否已附加且可用
/// </summary>
/// <value><c>true</c> 如果此实例处于活动状态;否则为 <c>false</c>。</value>
public bool IsActive { get; private set; } = false;
/// <summary>
/// 判断附加窗口是否始终置顶
/// </summary>
public bool IsTopmost { get { return (IsActive && _isTopmost); } }
private bool _isTopmost = false;
/// <summary>
/// 判断附加窗口是否始终置底
/// </summary>
public bool IsBottommost { get { return (IsActive && _isBottommost); } }
private bool _isBottommost = false;
/// <summary>
/// 判断附加窗口是否透明
/// </summary>
public bool IsTransparent { get { return (IsActive && _isTransparent); } }
private bool _isTransparent = false;
/// <summary>
/// 判断附加窗口是否点击穿透(即不接收任何鼠标操作)
/// </summary>
public bool IsClickThrough { get { return (IsActive && _isClickThrough); } }
private bool _isClickThrough = false;
/// <summary>
/// 判断附加窗口是否无边框(无标题栏和边框)
/// </summary>
public bool IsBorderless { get { return (IsActive && _isBorderless); } }
private bool _isBorderless = false;
/// <summary>
/// 判断附加窗口是否可以自由定位(仅 macOS)
/// </summary>
public bool IsFreePositioningEnabled { get { return (IsActive && _isFreePositioningEnabled); } }
private bool _isFreePositioningEnabled = false;
/// <summary>
/// Windows 下的透明方法类型
/// </summary>
private TransparentType transparentType = TransparentType.Alpha;
/// <summary>
/// 当 transparentType 为 ColorKey 时用于透明的颜色
/// </summary>
private Color32 keyColor = new Color32(1, 0, 1, 0);
#region Constructor or destructor
/// <summary>
/// 窗口控制构造函数
/// </summary>
public UniWinCore()
{
IsActive = false;
}
/// <summary>
/// 析构函数
/// </summary>
~UniWinCore()
{
Dispose();
}
/// <summary>
/// 结束时的处理
/// </summary>
public void Dispose()
{
// 由于最后恢复窗口状态会引起注意,所以特意不恢复,因此注释掉
//DetachWindow();
// 替代 DetachWindow()
LibUniWinC.UnregisterDropFilesCallback();
LibUniWinC.UnregisterMonitorChangedCallback();
LibUniWinC.UnregisterWindowStyleChangedCallback();
}
#endregion
#region Callbacks
/// <summary>
/// 显示器或分辨率变化时的回调
/// 此处的处理保持最低限度,仅设置标志
/// </summary>
/// <param name="monitorCount"></param>
[MonoPInvokeCallback(typeof(LibUniWinC.IntCallback))]
private static void _monitorChangedCallback([MarshalAs(UnmanagedType.I4)] int monitorCount)
{
wasMonitorChanged = true;
}
/// <summary>
/// 窗口样式、最大化、最小化等调用的回调
/// 此处的处理保持最低限度,仅设置标志
/// </summary>
/// <param name="e"></param>
[MonoPInvokeCallback(typeof(LibUniWinC.IntCallback))]
private static void _windowStyleChangedCallback([MarshalAs(UnmanagedType.I4)] int e)
{
wasWindowStyleChanged = true;
windowStateEventType = (WindowStateEventType)e;
}
/// <summary>
/// ファイル、フォルダがドロップされた時に呼ばれるコールバック
/// 文字列を配列に直すことと、フラグを立てるまで行う
/// </summary>
/// <param name="paths"></param>
[MonoPInvokeCallback(typeof(LibUniWinC.StringCallback))]
private static void _dropFilesCallback([MarshalAs(UnmanagedType.LPWStr)] string paths)
{
// 将以 LF 分隔的字符串分割为路径数组
//char[] delimiters = { '\n', '\0' };
//string[] files = paths.Split(delimiters).Where(s => s != "").ToArray();
string[] files = parsePaths(paths);
if (files.Length > 0)
{
lastDroppedFiles = new string[files.Length];
files.CopyTo(lastDroppedFiles, 0);
wasDropped = true;
}
}
/// <summary>
/// 将双引号包围、LF(或null)分隔的字符串转换为数组并返回
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
internal static string[] parsePaths(string text)
{
System.Collections.Generic.List<string> list = new System.Collections.Generic.List<string>();
bool inEscaped = false;
int len = text.Length;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++)
{
char c = text[i];
if (c == '"')
{
if (inEscaped)
{
if (((i + 1) < len) && text[i + 1] == '"')
{
i++;
sb.Append(c); // 连续双引号视为一个双引号
continue;
}
}
inEscaped = !inEscaped; // 非连续则切换是否在引号内
}
else if (c == '\n')
{
if (inEscaped)
{
// 如果在引号内,则作为路径的一部分
sb.Append(c);
}
else
{
// 如果不在引号内,则作为分隔符,移到下一个路径
if (sb.Length > 0)
{
list.Add(sb.ToString());
//sb.Clear(); // for .NET 4 or later
sb.Length = 0; // for .NET 2
}
}
}
else if (c == '\0')
{
// 空字符始终作为分隔符,移到下一个路径
if (sb.Length > 0)
{
list.Add(sb.ToString());
//sb.Clear(); // for .NET 4 or later
sb.Length = 0; // for .NET 2
}
}
else
{
sb.Append(c);
}
}
if (sb.Length > 0)
{
list.Add(sb.ToString());
}
// 移除空字符串元素
list.RemoveAll(v => v.Length == 0);
return list.ToArray();
}
#endregion
#region Find, attach or detach
/// <summary>
/// 将窗口状态恢复至最初并从操作对象中解除
/// </summary>
public void DetachWindow()
{
#if UNITY_EDITOR
// エディタの場合、ウィンドウスタイルでは常に最前面と得られていない可能性があるため、
// 最前面ではないのが本来と決め打ちで、デタッチ時無効化する
EnableTopmost(false);
#endif
LibUniWinC.DetachWindow();
}
/// <summary>
/// 查找自己的窗口(如果游戏视图是独立窗口则查找它)并作为操作对象
/// </summary>
/// <returns></returns>
public bool AttachMyWindow()
{
#if UNITY_EDITOR_WIN
// 似乎没有可靠的方法获取游戏视图,因此给予焦点后立即获取活动窗口
var gameView = GetGameView();
if (gameView)
{
gameView.Focus();
LibUniWinC.AttachMyActiveWindow();
}
#else
LibUniWinC.AttachMyWindow();
#endif
// 添加事件处理程序
LibUniWinC.RegisterDropFilesCallback(_dropFilesCallback);
LibUniWinC.RegisterMonitorChangedCallback(_monitorChangedCallback);
LibUniWinC.RegisterWindowStyleChangedCallback(_windowStyleChangedCallback);
IsActive = LibUniWinC.IsActive();
return IsActive;
}
public bool AttachWindowHandle(IntPtr hWnd)
{
LibUniWinC.AttachWindowHandle(hWnd);
IsActive = LibUniWinC.IsActive();
return IsActive;
}
/// <summary>
/// 选择自己进程中当前活动的窗口
/// 编辑器情况下,窗口会关闭或停靠,因此在聚焦时调用
/// </summary>
/// <returns></returns>
public bool AttachMyActiveWindow()
{
LibUniWinC.AttachMyActiveWindow();
IsActive = LibUniWinC.IsActive();
return IsActive;
}
#endregion
#region About window status
/// <summary>
/// 定期调用以维持窗口样式
/// </summary>
public void Update()
{
LibUniWinC.Update();
}
string GetDebubgWindowSizeInfo()
{
float x, y, cx, cy;
LibUniWinC.GetSize(out x, out y);
LibUniWinC.GetClientSize(out cx, out cy);
return $"W:{x},H:{y} CW:{cx},CH:{cy}";
}
/// <summary>
/// 设置/取消透明
/// </summary>
/// <param name="isTransparent"></param>
public void EnableTransparent(bool isTransparent)
{
// 编辑器无法透明或边框与正常不同,因此跳过
#if !UNITY_EDITOR
LibUniWinC.SetTransparent(isTransparent);
LibUniWinC.SetBorderless(isTransparent);
#endif
this._isTransparent = isTransparent;
}
/// <summary>
/// 设置窗口透明度
/// </summary>
/// <param name="alpha">0.0 - 1.0</param>
public void SetAlphaValue(float alpha)
{
// Windows 编辑器下,一旦半透明化后显示不会更新,因此禁用。Mac 则没问题
#if !UNITY_EDITOR_WIN
LibUniWinC.SetAlphaValue(alpha);
#endif
}
/// <summary>
/// 设置窗口 Z 顺序(是否置顶)。
/// </summary>
/// <param name="isTopmost">如果设为 <c>true</c> 则置顶。</param>
public void EnableTopmost(bool isTopmost)
{
LibUniWinC.SetTopmost(isTopmost);
this._isTopmost = isTopmost;
this._isBottommost = false; // 互斥
}
/// <summary>
/// 设置窗口 Z 顺序(是否置底)。
/// </summary>
/// <param name="isBottommost">如果设为 <c>true</c> 则置底。</param>
public void EnableBottommost(bool isBottommost)
{
LibUniWinC.SetBottommost(isBottommost);
this._isBottommost = isBottommost;
this._isTopmost = false; // 互斥
}
/// <summary>
/// 设置/取消点击穿透
/// </summary>
/// <param name="isThrough"></param>
public void EnableClickThrough(bool isThrough)
{
// 编辑器下点击穿透可能导致无法操作,因此跳过
#if !UNITY_EDITOR
LibUniWinC.SetClickThrough(isThrough);
#endif
this._isClickThrough = isThrough;
}
/// <summary>
/// 最大化窗口(Mac 上为缩放)
/// 最大化后可能还会调整大小,目前可能无法可靠工作
/// </summary>
public void SetZoomed(bool isZoomed)
{
LibUniWinC.SetMaximized(isZoomed);
}
/// <summary>
/// 获取窗口是否已最大化(Mac 上为缩放)
/// 最大化后可能还会调整大小,目前可能无法可靠工作
/// </summary>
public bool GetZoomed()
{
return LibUniWinC.IsMaximized();
}
/// <summary>
/// 设置窗口位置。
/// </summary>
/// <param name="position">位置。</param>
public void SetWindowPosition(Vector2 position)
{
LibUniWinC.SetPosition(position.x, position.y);
}
/// <summary>
/// 获取窗口位置。
/// </summary>
/// <returns>位置。</returns>
public Vector2 GetWindowPosition()
{
Vector2 pos = Vector2.zero;
LibUniWinC.GetPosition(out pos.x, out pos.y);
return pos;
}
/// <summary>
/// 设置窗口大小。
/// </summary>
/// <param name="size">x 为宽度,y 为高度</param>
public void SetWindowSize(Vector2 size)
{
LibUniWinC.SetSize(size.x, size.y);
}
/// <summary>
/// 获取窗口大小。
/// </summary>
/// <returns>x 为宽度,y 为高度</returns>
public Vector2 GetWindowSize()
{
Vector2 size = Vector2.zero;
LibUniWinC.GetSize(out size.x, out size.y);
return size;
}
/// <summary>
/// 获取客户区大小。
/// </summary>
/// <returns>x 为宽度,y 为高度</returns>
public Vector2 GetClientSize()
{
Vector2 size = Vector2.zero;
LibUniWinC.GetClientSize(out size.x, out size.y);
return size;
}
/// <summary>
/// 获取客户区矩形。
/// </summary>
/// <returns>x 为宽度,y 为高度</returns>
public Rect GetClientRectangle()
{
Vector2 pos = Vector2.zero;
Vector2 size = Vector2.zero;
LibUniWinC.GetClientRectangle(out pos.x, out pos.y, out size.x, out size.y);
return new Rect(pos.x, pos.y, size.x, size.y);
}
#endregion
#region File opening
public void SetAllowDrop(bool enabled)
{
LibUniWinC.SetAllowDrop(enabled);
}
#endregion
#region Event observers
/// <summary>
/// 检查文件拖放并取消拖放标志
/// </summary>
/// <param name="files"></param>
/// <returns>如果文件被拖放则返回 true</returns>
public bool ObserveDroppedFiles(out string[] files)
{
files = lastDroppedFiles;
if (!wasDropped || files == null) return false;
wasDropped = false;
return true;
}
/// <summary>
/// 检查显示器数量或分辨率变化,并取消标志
/// </summary>
/// <returns>如果已变化则返回 true</returns>
public bool ObserveMonitorChanged()
{
if (!wasMonitorChanged) return false;
wasMonitorChanged = false;
return true;
}
/// <summary>
/// 检查窗口样式是否已变更,并取消标志
/// </summary>
/// <returns>如果窗口样式已变更则返回 true</returns>
public bool ObserveWindowStyleChanged()
{
if (!wasWindowStyleChanged) return false;
windowStateEventType = WindowStateEventType.None;
wasWindowStyleChanged = false;
return true;
}
/// <summary>
/// 检查窗口样式是否已变更,并取消标志
/// </summary>
/// <returns>如果窗口样式已变更则返回 true</returns>
public bool ObserveWindowStyleChanged(out WindowStateEventType type)
{
if (!wasWindowStyleChanged)
{
type = WindowStateEventType.None;
return false;
}
type = windowStateEventType;
windowStateEventType = WindowStateEventType.None;
wasWindowStyleChanged = false;
return true;
}
#endregion
#region About mouse cursor
/// <summary>
/// 设置鼠标指针位置。
/// </summary>
/// <param name="position">位置。</param>
public static void SetCursorPosition(Vector2 position)
{
LibUniWinC.SetCursorPosition(position.x, position.y);
}
/// <summary>
/// 获取鼠标指针位置。
/// </summary>
/// <returns>位置。</returns>
public static Vector2 GetCursorPosition()
{
Vector2 pos = Vector2.zero;
LibUniWinC.GetCursorPosition(out pos.x, out pos.y);
return pos;
}
/// <summary>
/// Get pressed mouse buttons.
/// </summary>
/// <returns>Bit flags of pressed buttons</returns>
public static int GetMouseButtons()
{
return LibUniWinC.GetMouseButtons();
}
/// <summary>
/// 获取按下的修饰键。
/// </summary>
/// <returns>按下键的位标志</returns>
public static int GetModifierKeys()
{
return LibUniWinC.GetModifierKeys();
}
// 未实现
public static bool GetCursorVisible()
{
return true;
}
#endregion
#region for Windows only
/// <summary>
/// 指定透明方法(仅 Windows 支持)
/// </summary>
/// <param name="type"></param>
public void SetTransparentType(TransparentType type)
{
LibUniWinC.SetTransparentType((Int32)type);
transparentType = type;
}
/// <summary>
/// 单色透明时指定透明色(仅 Windows 支持)
/// </summary>
/// <param name="color"></param>
public void SetKeyColor(Color32 color)
{
LibUniWinC.SetKeyColor((UInt32)(color.b * 0x10000 + color.g * 0x100 + color.r));
keyColor = color;
}
#endregion
#region for macOS only
/// <summary>
/// 设置/取消窗口的自由配置(仅 macOS 支持)
/// </summary>
/// <param name="enabled"></param>
public void EnableFreePositioning(bool enabled)
{
LibUniWinC.EnableFreePositioning(enabled);
_isFreePositioningEnabled = LibUniWinC.IsFreePositioningEnabled();
}
#endregion
#region About monitors
/// <summary>
/// 获取窗口所在显示器的索引
/// </summary>
/// <returns>显示器索引</returns>
public int GetCurrentMonitor()
{
return LibUniWinC.GetCurrentMonitor();
}
/// <summary>
/// 获取已连接显示器的数量
/// </summary>
/// <returns>数量</returns>
public static int GetMonitorCount()
{
return LibUniWinC.GetMonitorCount();
}
/// <summary>
/// 获取显示器的位置和大小
/// </summary>
/// <param name="index"></param>
/// <param name="position"></param>
/// <param name="size"></param>
/// <returns></returns>
public static bool GetMonitorRectangle(int index, out Vector2 position, out Vector2 size)
{
return LibUniWinC.GetMonitorRectangle(index, out position.x, out position.y, out size.x, out size.y);
}
/// <summary>
/// 将窗口适配到指定显示器
/// </summary>
/// <param name="monitorIndex"></param>
/// <returns></returns>
public bool FitToMonitor(int monitorIndex)
{
float dx, dy, dw, dh;
if (LibUniWinC.GetMonitorRectangle(monitorIndex, out dx, out dy, out dw, out dh))
{
// 如果处于最大化状态则先恢复
if (LibUniWinC.IsMaximized()) LibUniWinC.SetMaximized(false);
// 指定显示器的中心坐标
float cx = dx + (dw / 2);
float cy = dy + (dh / 2);
// 将窗口中心移动到指定显示器中心
float ww, wh;
LibUniWinC.GetSize(out ww, out wh);
float wx = cx - (ww / 2);
float wy = cy - (wh / 2);
LibUniWinC.SetPosition(wx, wy);
// 最大化
LibUniWinC.SetMaximized(true);
//Debug.Log(String.Format("显示器 {4} : {0},{1} - {2},{3}", dx, dy, dw, dh, monitorIndex));
return true;
}
return false;
}
/// <summary>
/// 打印显示器列表
/// </summary>
[Obsolete]
public static void DebugMonitorInfo()
{
int monitors = LibUniWinC.GetMonitorCount();
int currentMonitorIndex = LibUniWinC.GetCurrentMonitor();
string message = "当前显示器: " + currentMonitorIndex + "\r\n";
for (int i = 0; i < monitors; i++)
{
float x, y, w, h;
bool result = LibUniWinC.GetMonitorRectangle(i, out x, out y, out w, out h);
message += String.Format(
"显示器 {0}: X:{1}, Y:{2} - W:{3}, H:{4}\r\n",
i, x, y, w, h
);
}
Debug.Log(message);
}
/// <summary>
/// 获取用于调试的信息
/// </summary>
/// <returns></returns>
[Obsolete]
public static int GetDebugInfo()
{
return LibUniWinC.GetDebugInfo();
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4930552cf3596b040954b14d8b7c47e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d327b245537480646bd85e511002d6ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+206
View File
@@ -0,0 +1,206 @@
/*
* UniWindowDragMove.cs
*
* 作者: Kirurobo http://twitter.com/kirurobo
* 许可证: MIT
*/
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
#if ENABLE_LEGACY_INPUT_MANAGER
#elif ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace Kirurobo
{
public class UniWindowMoveHandle : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler, IPointerUpHandler
{
private UniWindowController _uniwinc;
/// <summary>
/// 窗口最大化时是否禁用移动
/// </summary>
[Tooltip("窗口已最大化(缩放)时禁用拖拽移动。")]
public bool disableOnZoomed = true;
/// <summary>
/// 拖动中则为 true
/// </summary>
public bool IsDragging
{
get { return _isDragging; }
}
private bool _isDragging = false;
/// <summary>
/// 是否进行拖动
/// </summary>
private bool IsEnabled
{
get { return enabled && (!disableOnZoomed || !IsZoomed); }
}
/// <summary>
/// 是否适配显示器或最大化
/// </summary>
private bool IsZoomed
{
get { return (_uniwinc && (_uniwinc.shouldFitMonitor || _uniwinc.isZoomed)); }
}
/// <summary>
/// 记录拖动前自动命中测试是否启用
/// </summary>
private bool _isHitTestEnabled;
/// <summary>
/// 拖动开始时窗口内坐标[像素]
/// </summary>
private Vector2 _dragStartedPosition;
// 首次帧更新前调用 Start
void Start()
{
// 获取场景中的 UniWindowController
_uniwinc = GameObject.FindAnyObjectByType<UniWindowController>();
if (_uniwinc) _isHitTestEnabled = _uniwinc.isHitTestEnabled;
//// 下面的代码似乎不需要,所以注释掉以免擅自更改
//Input.simulateMouseWithTouches = false;
}
/// <summary>
/// 拖动开始时的处理
/// </summary>
public void OnBeginDrag(PointerEventData eventData)
{
if (!IsEnabled)
{
return;
}
// 仅通过鼠标左键拖动
if (eventData.button != PointerEventData.InputButton.Left) return;
// Mac 上行为会有所不同
// 实际上仅在 Retina 支持启用时,但 eventData.position 的坐标系与窗口坐标系的缩放会不一致
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
_dragStartedPosition = _uniwinc.windowPosition - _uniwinc.cursorPosition;
#else
_dragStartedPosition = eventData.position;
#endif
// 如果 _isDragging 为 false,则判断即将开始拖动
if (!_isDragging)
{
// 拖动期间禁用命中测试
_isHitTestEnabled = _uniwinc.isHitTestEnabled;
_uniwinc.isHitTestEnabled = false;
_uniwinc.isClickThrough = false;
}
_isDragging = true;
}
/// <summary>
/// 拖动结束时的处理
/// </summary>
public void OnEndDrag(PointerEventData eventData)
{
EndDragging();
}
/// <summary>
/// 鼠标抬起时也视为拖动结束
/// </summary>
/// <param name="eventData"></param>
public void OnPointerUp(PointerEventData eventData)
{
EndDragging();
}
/// <summary>
/// 结束拖动
/// </summary>
private void EndDragging()
{
if (_isDragging)
{
_uniwinc.isHitTestEnabled = _isHitTestEnabled;
}
_isDragging = false;
}
/// <summary>
/// 非最大化时,通过鼠标拖动移动窗口
/// </summary>
public void OnDrag(PointerEventData eventData)
{
if (!_uniwinc || !_isDragging) return;
// 如果拖动移动已被禁用,则结束拖动
if (!IsEnabled)
{
EndDragging();
return;
}
// // 如果鼠标左键未按下,则结束拖动
// if (eventData.button != PointerEventData.InputButton.Left) return;
// [Shift]、[Ctrl]、[Alt]、[Command] 键按下期间不视为拖动
var modifiers = UniWindowController.GetModifierKeys();
if (modifiers != UniWindowController.ModifierKey.None) return;
// 如果鼠标按钮已松开,则结束拖动
var buttons = UniWindowController.GetMouseButtons();
if ((buttons & UniWindowController.MouseButton.Left) == UniWindowController.MouseButton.None) {
EndDragging();
return;
}
// #if ENABLE_LEGACY_INPUT_MANAGER
// // 在 Mac 上,如果在多显示器之间移动,EventSystem 的 OnEndDrag 可能无法正确调用,因此始终监控鼠标按钮
// if (!Input.Mouse.Button(0).IsPressed) {
// EndDragging();
// return;
// }
// if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)
// || Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)
// || Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) return;
// #elif ENABLE_INPUT_SYSTEM
// // 在 Mac 上,如果在多显示器之间移动,EventSystem 的 OnEndDrag 可能无法正确调用,因此始终监控鼠标按钮
// if (!Mouse.current.leftButton.isPressed) {
// EndDragging();
// return;
// }
// if (Keyboard.current[Key.LeftShift].isPressed || Keyboard.current[Key.RightShift].isPressed
// || Keyboard.current[Key.LeftCtrl].isPressed || Keyboard.current[Key.RightCtrl].isPressed
// || Keyboard.current[Key.LeftAlt].isPressed || Keyboard.current[Key.RightAlt].isPressed) return;
// #endif
// 如果是全屏则不移动窗口
// 在编辑器中可能会变为 true,因此仅在非编辑器环境下确认
#if !UNITY_EDITOR
if (Screen.fullScreen)
{
EndDragging();
return;
}
#endif
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
// 在 Mac 上,通过原生插件获取/设置光标位置
_uniwinc.windowPosition = _uniwinc.cursorPosition + _dragStartedPosition;
//Debug.Log("Drag start: " + _dragStartedPosition);
#else
// 在 Windows 上,为支持触控操作而使用 eventData.position
// 将窗口移动与起始位置一致的屏幕位置偏移量
_uniwinc.windowPosition += eventData.position - _dragStartedPosition;
#endif
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd641513c2924f7488734c8cac43310f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 326ddcba926f5e849bde9a0e0fb87ee1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+35
View File
@@ -0,0 +1,35 @@
using UnityEngine;
namespace Kirurobo {
/// <summary>
/// 使附加的对象以恒定速度进行偏航旋转
/// </summary>
public class AutoRotator : MonoBehaviour {
/// <summary>
/// 旋转速度 [度/秒]
/// </summary>
public float angularVelocity = 90f;
/// <summary>
/// 旋转轴(偏航旋转,方向向上)
/// </summary>
Vector3 rotationAxis = Vector3.up;
/// <summary>
/// 初始姿态
/// </summary>
Quaternion initialLocalRotation;
// 用于初始化
void Start () {
// 记录初始姿态
initialLocalRotation = transform.localRotation;
}
// 每帧调用 Update
void Update () {
var rotation = Quaternion.Euler(0f, Time.time * angularVelocity, 0f);
transform.localRotation = initialLocalRotation * rotation;
}
}
}
+12
View File
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f985bf036f5416a45b9dd4e31bc85075
timeCreated: 1545989238
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

+88
View File
@@ -0,0 +1,88 @@
fileFormatVersion: 2
guid: 2b5d2690e20d6584e909fd2bdfd93579
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 0
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 8, y: 8, z: 8, w: 8}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 533df9bf30503d349b91598d325d5fcd
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

+88
View File
@@ -0,0 +1,88 @@
fileFormatVersion: 2
guid: 4713fc586389c694f9e384c7f3a02289
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 0
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 8, y: 8, z: 8, w: 8}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 47fdf1b4c9bb7764d916b229e9a2d98c
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
+27
View File
@@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if ENABLE_LEGACY_INPUT_MANAGER
#elif ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace Kirurobo {
/// <summary>
/// 为快速兼容 Legacy InputManager 和 InputSystem 而准备的类
/// </summary>
#if ENABLE_LEGACY_INPUT_MANAGER
public class InputModuleProxy : UnityEngine.EventSystems.StandaloneInputModule
{
}
#elif ENABLE_INPUT_SYSTEM
public class InputModuleProxy : UnityEngine.InputSystem.UI.InputSystemUIInputModule
{
}
#else
public class InputModuleProxy
{
}
#endif
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce6b387a66b0e654d9eb8712d70fff48
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+138
View File
@@ -0,0 +1,138 @@
/**
* UniWindowController 的示例脚本
*
* 作者: Kirurobo http://twitter.com/kirurobo
* 许可证: MIT
*/
using System;
using UnityEngine;
#if ENABLE_LEGACY_INPUT_MANAGER
// Don't use InputSystem in this script to prevent TouchPhase duplication
#elif ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace Kirurobo {
/// <summary>
/// Input System と Input Manager の違いを吸収するためのプロキシ
/// </summary>
public class InputProxy
{
public static Vector3 mousePosition {
get {
return GetMousePosition();
}
}
/// <summary>
/// Input System の利用に合わせてキーアップを取得
/// </summary>
/// <returns></returns>
public static bool GetKeyUp(String key)
{
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.GetKeyUp(key);
#elif ENABLE_INPUT_SYSTEM
// 簡易的な実装。keyが1文字で、かつアルファベットか数字に対応
// 1文字以外は escape, space のみ対応
if (key.Length == 1) {
Key k = Key.None;
char c = key[0];
if (c >= '0' && c <= '9') {
// 数字の場合はDigit0Digit9とNumpad0Numpad9の両方に反応
k = (Key)Enum.ToObject(typeof(Key), (int)Key.Numpad0 + (int)(c - '0'));
if (Keyboard.current[k].wasReleasedThisFrame) return true;
// Digitの場合はDigit0の値が最大
k = (Key)Enum.ToObject(typeof(Key), (int)Key.Digit1 + (int)((c == '0' ? 9 : c - '1')));
if (Keyboard.current[k].wasReleasedThisFrame) return true;
}
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
// アルファベットの場合は大文字・小文字どちらでも可とする
k = (Key)Enum.ToObject(typeof(Key), (int)Key.A + (int)(Char.ToUpper(c) - 'A'));
if (Keyboard.current[k].wasReleasedThisFrame) return true;
}
} else if (key == "escape") {
return Keyboard.current.escapeKey.wasReleasedThisFrame;
} else if (key == "space") {
return Keyboard.current.spaceKey.wasReleasedThisFrame;
}
return false;
#else
return false;
#endif
}
/// <summary>
/// Input System の利用に合わせてマウス座標を取得
/// </summary>
/// <returns></returns>
private static Vector3 GetMousePosition()
{
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.mousePosition;
#elif ENABLE_INPUT_SYSTEM
return Mouse.current.position.ReadValue();
#else
return Vector3.zero;
#endif
}
/// <summary>
/// 判断鼠标按钮当前是否被按下
/// </summary>
/// <param name="button"></param>
/// <returns></returns>
public static bool GetMouseButton(int button)
{
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.GetMouseButton(button);
#elif ENABLE_INPUT_SYSTEM
if (button == 0) return Mouse.current.leftButton.isPressed;
if (button == 1) return Mouse.current.rightButton.isPressed;
if (button == 2) return Mouse.current.middleButton.isPressed;
return false;
#else
return false;
#endif
}
/// <summary>
/// このフレームでマウスボタンが押されたか判定
/// </summary>
/// <param name="button"></param>
/// <returns></returns>
public static bool GetMouseButtonDown(int button)
{
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.GetMouseButtonDown(button);
#elif ENABLE_INPUT_SYSTEM
if (button == 0) return Mouse.current.leftButton.wasPressedThisFrame;
if (button == 1) return Mouse.current.rightButton.wasPressedThisFrame;
if (button == 2) return Mouse.current.middleButton.wasPressedThisFrame;
return false;
#else
return false;
#endif
}
/// <summary>
/// 判断本帧是否松开了鼠标按钮
/// </summary>
/// <param name="button"></param>
/// <returns></returns>
public static bool GetMouseButtonUp(int button) {
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.GetMouseButtonUp(button);
#elif ENABLE_INPUT_SYSTEM
if (button == 0) return Mouse.current.leftButton.wasReleasedThisFrame;
if (button == 1) return Mouse.current.rightButton.wasReleasedThisFrame;
if (button == 2) return Mouse.current.middleButton.wasReleasedThisFrame;
return false;
#else
return false;
#endif
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93a3055c4733041a1a83f6c90996f3ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+282
View File
@@ -0,0 +1,282 @@
/*
* ModelController
*
* 旋转、平移和缩放对象
*
* 作者: Kirurobo http://twitter.com/kirurobo
* 许可证: MIT
*/
using System;
using UnityEngine;
namespace Kirurobo
{
public class ModelController : MonoBehaviour
{
[Flags]
public enum RotationAxes : int
{
None = 0,
Pitch = 1,
Yaw = 2,
PitchAndYaw = 3
}
[Flags]
public enum DragState
{
None,
Rotating,
Translating,
}
public RotationAxes axes = RotationAxes.PitchAndYaw;
public float yawSensitivity = 1f;
public float pitchSensitvity = 1f;
public float scaleSensitivity = 0.5f;
public Vector2 minimumAngles = new Vector2(-90f, -360f);
public Vector2 maximumAngles = new Vector2(90f, 360f);
[Tooltip("Restrict to move out from screen")]
public bool confineTranslation = true; // 並進移動をウィンドウ(Screen)の範囲に制限するか
[Tooltip("Default is the parent transform")]
public Transform centerTransform; // 回転中心
[Tooltip("Default is the main camera")]
public Camera currentCamera;
internal GameObject centerObject = null; // 当未指定旋转中心Transform时创建的对象
internal Vector3 rotation;
internal Vector3 translation;
internal Vector3 lastMousePosition; // 上一帧的鼠标坐标
internal DragState dragState; // 拖动时根据开始时的按钮设置对应状态
internal Vector3 relativePosition;
internal Quaternion relativeRotation;
internal Vector3 originalLocalScale;
internal float zoom;
void Start()
{
Initialize();
SetupTransform();
}
void OnDestroy()
{
// 回転中心を独自に作成していれば、削除
if (centerObject) GameObject.Destroy(centerObject);
}
void Update()
{
if (!currentCamera.isActiveAndEnabled) return;
{
HandleMouse();
}
}
/// <summary>
/// 必要なオブジェクトを取得・準備
/// </summary>
internal void Initialize()
{
if (!centerTransform)
{
centerTransform = this.transform.parent;
if (!centerTransform || centerTransform == this.transform)
{
centerObject = new GameObject();
centerTransform = centerObject.transform;
centerTransform.position = Vector3.zero;
}
}
if (!currentCamera)
{
currentCamera = Camera.main;
}
lastMousePosition = InputProxy.mousePosition;
}
/// <summary>
/// 初期位置・姿勢の設定
/// 対象となるオブジェクトがそろった後で実行する
/// </summary>
internal void SetupTransform()
{
relativePosition = transform.position- centerTransform.position; // 从对象到中心坐标的向量
relativeRotation = transform.rotation * Quaternion.Inverse(centerTransform.rotation);
originalLocalScale = transform.localScale;
ResetTransform();
}
/// <summary>
/// Reset rotation and translation.
/// </summary>
public void ResetTransform()
{
rotation = relativeRotation.eulerAngles;
translation = relativePosition;
zoom = 0f;
UpdateTransform();
}
/// <summary>
/// 应用旋转和平移
/// </summary>
internal void UpdateTransform()
{
Quaternion rot = Quaternion.Euler(rotation);
transform.rotation = rot;
transform.position = centerTransform.position + translation;
transform.localScale = originalLocalScale * Mathf.Pow(10f, zoom);
}
internal virtual void HandleMouse()
{
Vector3 mousePos = InputProxy.mousePosition;
if (InputProxy.GetMouseButtonDown(0))
{
// 左键(0)拖动时进行平移
if (dragState == DragState.None && IsHit(mousePos))
{
dragState = DragState.Translating;
// 限制在屏幕范围内
if (confineTranslation)
{
Vector3 screenMax = new Vector3(Screen.width, Screen.height);
mousePos = Vector3.Max(Vector3.Min(mousePos, screenMax), Vector3.zero);
}
lastMousePosition = mousePos; // ドラッグ開始時にはリセット
}
}
else if (InputProxy.GetMouseButtonDown(1))
{
// 右ボタン(1)ドラッグでは回転を行う
if (dragState == DragState.None && IsHit(mousePos))
{
dragState = DragState.Rotating;
lastMousePosition = mousePos; // ドラッグ開始時にはリセット
}
}
// 通过拖动旋转
if (dragState == DragState.Rotating)
{
// 仅在按下按钮时操作
if (InputProxy.GetMouseButton(1))
{
// 通过拖动旋转
if ((axes & RotationAxes.Yaw) > RotationAxes.None)
{
rotation.y -= (mousePos.x - lastMousePosition.x) * 360f / Screen.width * yawSensitivity;
rotation.y = ClampAngle(rotation.y, minimumAngles.y, maximumAngles.y);
}
if ((axes & RotationAxes.Pitch) > RotationAxes.None)
{
rotation.x += (mousePos.y - lastMousePosition.y) * 360f / Screen.height * pitchSensitvity;
rotation.x = ClampAngle(rotation.x, minimumAngles.x, maximumAngles.x);
}
UpdateTransform();
}
else
{
// 如果右键已松开则结束旋转
dragState = DragState.None;
}
}
// 通过拖动平移
if (dragState == DragState.Translating)
{
// 仅在按下按钮时操作
if (InputProxy.GetMouseButton(0))
{
// 限制在屏幕范围内
if (confineTranslation)
{
Vector3 screenMax = new Vector3(Screen.width, Screen.height);
mousePos = Vector3.Max(Vector3.Min(mousePos, screenMax), Vector3.zero);
}
Vector3 screenPos = currentCamera.WorldToScreenPoint(transform.position);
Vector3 deltaPos = mousePos - lastMousePosition;
deltaPos.z = 0f;
Vector3 worldPos = currentCamera.ScreenToWorldPoint(screenPos + deltaPos);
translation = worldPos - centerTransform.position;
UpdateTransform();
}
else
{
// ボタンが離されていれば並進は終了
dragState = DragState.None;
}
}
// 暂时仅在使用 Legacy Input Manager 时接受滚轮操作
#if ENABLE_LEGACY_INPUT_MANAGER
// 如果滚轮转动,则进行缩放
if (!Mathf.Approximately(Input.GetAxis("Mouse ScrollWheel"), 0f) && IsHit(mousePos))
{
// 滚轮操作量
float wheelDelta = Input.GetAxis("Mouse ScrollWheel") * scaleSensitivity;
// 更改缩放倍率
zoom -= wheelDelta;
zoom = Mathf.Clamp(zoom, -1f, 2f); // 视场角的对数范围 [度]
UpdateTransform();
}
#endif
lastMousePosition = mousePos;
}
/// <summary>
/// 判断鼠标操作时是否点击到对象
/// </summary>
/// <returns></returns>
internal bool IsHit(Vector3 screenPosition)
{
RaycastHit hit;
Ray ray = currentCamera.ScreenPointToRay(screenPosition);
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.IsChildOf(transform)) return true;
}
return false;
}
/// <summary>
/// 若角度超出指定范围,则进行修正
/// </summary>
/// <param name="angle"></param>
/// <param name="min"></param>
/// <param name="max"></param>
/// <returns></returns>
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -min) angle = -((-angle) % 360f);
if (angle > max) angle = angle % 360f;
return Mathf.Clamp(angle, min, max);
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 035ad1913e9c28f4492641ca36127790
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+84
View File
@@ -0,0 +1,84 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: UiMaterial
m_Shader: {fileID: 10760, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _ColorMask: 15
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UVSec: 0
- _UseUIAlphaClip: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 0.78431374}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 57c048a21c6552643bb464f9bcd0cf1a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
+5
View File
@@ -0,0 +1,5 @@
{
"displayName": "FileDialog",
"description": "Demonstrates native file open/save dialog integration.",
"createSeparatePackage": false
}
+97
View File
@@ -0,0 +1,97 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Kirurobo
{
/// <summary>
/// 基础文件面板示例
/// </summary>
public class FileDialogSample : MonoBehaviour
{
public Button openFileButton;
public Button openMultipleFilesButton;
public Button saveFileButton;
public Text messageText;
// 首次更新前调用 Start
void Start()
{
openFileButton.onClick.AddListener(OpenSingleFile);
openMultipleFilesButton.onClick.AddListener(OpenMultipleFiles);
saveFileButton.onClick.AddListener(OpenSaveFile);
messageText.text = "点击按钮!";
}
// 每帧调用 Update
void Update()
{
}
/// <summary>
/// 打开单个文件的对话框。
/// </summary>
private void OpenSingleFile() {
FilePanel.Settings settings = new FilePanel.Settings();
settings.filters = new FilePanel.Filter[]
{
new FilePanel.Filter("所有文件", "*"),
new FilePanel.Filter("图片文件 (*.png;*.jpg;*.jpeg;*.tiff;*.gif;*.tga)", "png", "jpg", "jpeg", "tiff", "gif", "tga"),
new FilePanel.Filter("文档 (*.txt;*.rtf;*.doc;*.docx)", "txt", "rtf", "doc", "docx"),
};
settings.title = "打开文件!";
settings.initialDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
messageText.text = "";
FilePanel.OpenFilePanel(settings, (files) =>
{
messageText.text = "打开文件\n" + string.Join("\n", files);
});
}
/// <summary>
/// 打开多个文件的对话框。
/// </summary>
private void OpenMultipleFiles() {
FilePanel.Settings settings = new FilePanel.Settings();
settings.filters = new FilePanel.Filter[]
{
new FilePanel.Filter("图片文件 (*.png;*.jpg;*.jpeg;*.tiff;*.gif;*.tga)", "png", "jpg", "jpeg", "tiff", "gif", "tga"),
new FilePanel.Filter("文档 (*.txt;*.rtf;*.doc;*.docx)", "txt", "rtf", "doc", "docx"),
new FilePanel.Filter("所有文件", "*"),
};
settings.flags = FilePanel.Flag.AllowMultipleSelection;
settings.title = "打开多个文件!";
settings.initialDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
messageText.text = "";
FilePanel.OpenFilePanel(settings, (files) =>
{
messageText.text = "打开多个文件\n" + string.Join("\n", files);
});
}
/// <summary>
/// 打开保存文件对话框。
/// </summary>
private void OpenSaveFile() {
FilePanel.Settings settings = new FilePanel.Settings();
settings.filters = new FilePanel.Filter[]
{
new FilePanel.Filter("文本文件 (*.txt;*.log)", "txt", "log"),
new FilePanel.Filter("图片文件 (*.png;*.jpg;*.jpeg;*.tiff;*.gif;*.tga)", "png", "jpg", "jpeg", "tiff", "gif", "tga"),
new FilePanel.Filter("所有文件", "*"),
};
settings.title = "实际上不会执行保存操作";
settings.initialFile = "Test.txt";
messageText.text = "";
FilePanel.SaveFilePanel(settings, (files) =>
{
messageText.text = "选中的文件\n" + string.Join("\n", files);
});
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!850595691 &4890085278179872738
LightingSettings:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FileDialogSampleSettings
serializedVersion: 3
m_GIWorkflowMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 1
m_RealtimeEnvironmentLighting: 1
m_BounceScale: 1
m_AlbedoBoost: 1
m_IndirectOutputScale: 1
m_UsingShadowmask: 1
m_BakeBackend: 1
m_LightmapMaxSize: 1024
m_BakeResolution: 40
m_Padding: 2
m_TextureCompression: 1
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAO: 0
m_MixedBakeMode: 2
m_LightmapsBakeMode: 1
m_FilterMode: 1
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_RealtimeResolution: 2
m_ForceWhiteAlbedo: 0
m_ForceUpdates: 0
m_FinalGather: 0
m_FinalGatherRayCount: 256
m_FinalGatherFiltering: 1
m_PVRCulling: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVREnvironmentSampleCount: 500
m_PVREnvironmentReferencePointCount: 2048
m_LightProbeSampleCountMultiplier: 4
m_PVRBounces: 2
m_PVRMinBounces: 2
m_PVREnvironmentMIS: 0
m_PVRFilteringMode: 2
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
+5
View File
@@ -0,0 +1,5 @@
{
"displayName": "Fullscreen",
"description": "Fullscreen mode example with right-click context menu and 3D snowman scene.",
"createSeparatePackage": false
}
+304
View File
@@ -0,0 +1,304 @@
/**
* 全屏示例的 UI 控制器
*
* Author: Kirurobo http://twitter.com/kirurobo
* License: MIT
*/
using UnityEngine;
using UnityEngine.UI;
namespace Kirurobo
{
/// <summary>
/// 使用 Toggle 开关 WindowController 设置的示例
/// </summary>
public class FullscreenSample : MonoBehaviour
{
private UniWindowController uniwinc;
private RectTransform canvasRect;
private float mouseMoveSS = 0f; // 鼠标轨迹平方和。[px^2]
private float mouseMoveSSThreshold = 36f; // 点击(非拖拽)阈值。[px^2]
private Vector3 lastMousePosition; // 右键点击位置。
private float touchDuration = 0f;
private float touchDurationThreshold = 0.5f; // 长按时间阈值。[s]
public Toggle transparentToggle;
public Toggle topmostToggle;
public Toggle bottommostToggle;
public Dropdown fitWindowDropdown;
public Button quitButton;
public Button menuCloseButton;
public RectTransform menuPanel;
/// <summary>
/// 设置
/// </summary>
void Start()
{
// 查找 UniWindowController
uniwinc = GameObject.FindAnyObjectByType<UniWindowController>();
// 获取 Canvas 的 RectTransform
if (menuPanel) canvasRect = menuPanel.GetComponentInParent<Canvas>().GetComponent<RectTransform>();
// 根据有效显示器数量创建选项
UpdateMonitorDropdown();
// 将 Toggle 的选中状态与当前状态同步
UpdateUI();
// 初始状态下关闭菜单
CloseMenu();
if (uniwinc)
{
// 操作 UI 时将其反映到窗口
transparentToggle?.onValueChanged.AddListener(val => uniwinc.isTransparent = val);
topmostToggle?.onValueChanged.AddListener(val => uniwinc.isTopmost = val);
bottommostToggle?.onValueChanged.AddListener(val => uniwinc.isBottommost = val);
fitWindowDropdown?.onValueChanged.AddListener(val => SetFitToMonitor(val));
quitButton?.onClick.AddListener(Quit);
menuCloseButton?.onClick.AddListener(CloseMenu);
// Add events
uniwinc.OnStateChanged += (type) =>
{
UpdateUI();
//ShowEventMessage("Window state changed: " + type);
};
uniwinc.OnMonitorChanged += () => {
UpdateMonitorDropdown();
UpdateUI();
//ShowEventMessage("Resolution changed!");
};
}
}
/// <summary>
/// 每帧执行
/// </summary>
private void Update()
{
// 鼠标右键点击时显示上下文菜单。
// 如果鼠标移动距离在阈值以下,则视为点击
if (InputProxy.GetMouseButtonDown(1))
{
lastMousePosition = InputProxy.mousePosition;
touchDuration = 0f;
}
if (InputProxy.GetMouseButton(1))
{
mouseMoveSS += (InputProxy.mousePosition - lastMousePosition).sqrMagnitude;
}
if (InputProxy.GetMouseButtonUp(1))
{
if (mouseMoveSS < mouseMoveSSThreshold)
{
ShowMenu(lastMousePosition);
}
mouseMoveSS = 0f;
touchDuration = 0f;
}
// 暂时仅在 Legacy Input Manager 中处理触摸
#if ENABLE_LEGACY_INPUT_MANAGER
// 长按也可显示菜单
if (Input.touchSupported && (Input.touchCount > 0))
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
lastMousePosition = Input.mousePosition;
touchDuration = 0f;
}
if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
{
mouseMoveSS += touch.deltaPosition.sqrMagnitude;
touchDuration += touch.deltaTime;
}
if (touch.phase == TouchPhase.Ended)
{
if ((mouseMoveSS < mouseMoveSSThreshold) && (touchDuration >= touchDurationThreshold))
{
ShowMenu(lastMousePosition);
}
mouseMoveSS = 0f;
touchDuration = 0f;
}
}
#endif
// 按下 [Space] 键也可显示菜单
if (InputProxy.GetKeyUp("space"))
{
if (menuPanel)
{
if (menuPanel.gameObject.activeSelf) {
CloseMenu();
} else {
Vector2 pos = new Vector2(Screen.width / 2, Screen.height / 2);
ShowMenu(pos);
}
}
}
// 按下 [ESC] 时退出或停止播放
if (InputProxy.GetKeyUp("escape"))
{
Quit();
}
}
void Quit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
/// <summary>
/// 适配显示器下拉列表更改时的处理
/// </summary>
/// <param name="val"></param>
void SetFitToMonitor(int val)
{
if (!uniwinc) return;
if (val < 1)
{
// 下拉列表第一项为不适配
uniwinc.shouldFitMonitor = false;
}
else
{
// 由于从第二项开始,显示器编号减1
uniwinc.monitorToFit = val - 1;
uniwinc.shouldFitMonitor = true; // 从false变为true时才会移动窗口,因此先指定显示器编号再更改
}
}
/// <summary>
/// 在指定坐标显示上下文菜单
/// </summary>
/// <param name="position">指定中心坐标</param>
private void ShowMenu(Vector2 position)
{
if (menuPanel)
{
Vector2 pos = position * (canvasRect.sizeDelta.x / Screen.width);
float w = menuPanel.rect.width;
float h = menuPanel.rect.height;
// 以指定坐标为中心进行位置调整
pos.y = Mathf.Max(Mathf.Min(pos.y, Screen.height - h / 2f), h / 2f); // 如果超出则向上移动
pos.x = Mathf.Max(Mathf.Min(pos.x, Screen.width - w / 2f), w / 2f); // 如果超出右侧则向左移动
menuPanel.pivot = Vector2.one * 0.5f; // 设置为中心
menuPanel.anchorMin = Vector2.zero;
menuPanel.anchorMax = Vector2.zero;
menuPanel.anchoredPosition = pos;
menuPanel.gameObject.SetActive(true);
}
}
/// <summary>
/// 关闭上下文菜单
/// </summary>
private void CloseMenu()
{
if (menuPanel)
{
menuPanel.gameObject.SetActive(false);
}
}
/// <summary>
/// 将实际状态反映到 UI 显示
/// </summary>
private void UpdateUI()
{
if (uniwinc)
{
if (transparentToggle)
{
transparentToggle.isOn = uniwinc.isTransparent;
}
if (topmostToggle)
{
topmostToggle.isOn = uniwinc.isTopmost;
}
if (bottommostToggle)
{
bottommostToggle.isOn = uniwinc.isBottommost;
}
if (fitWindowDropdown)
{
if (uniwinc.shouldFitMonitor)
{
fitWindowDropdown.value = uniwinc.monitorToFit + 1;
}
else
{
fitWindowDropdown.value = 0;
}
fitWindowDropdown.RefreshShownValue();
}
}
}
/// <summary>
/// 更新显示器选择下拉列表的选项
/// 之后需要调用 UpdateUI()
/// </summary>
void UpdateMonitorDropdown()
{
if (!fitWindowDropdown) return;
// 删除除第一项以外的选项
fitWindowDropdown.options.RemoveRange(1, fitWindowDropdown.options.Count - 1);
if (!uniwinc)
{
fitWindowDropdown.value = 0;
}
else
{
int count = UniWindowController.GetMonitorCount();
for (int i = 0; i < count; i++)
{
fitWindowDropdown.options.Add(new Dropdown.OptionData("适配显示器 " + i));
}
if (uniwinc.monitorToFit >= count)
{
uniwinc.monitorToFit = count - 1;
}
}
}
/// <summary>
/// 显示带超时功能的消息
/// </summary>
/// <param name="message"></param>
private void ShowEventMessage(string message)
{
Debug.Log(message);
}
/// <summary>
/// 如果 UI 中有文本框,则显示消息。否则输出到控制台
/// </summary>
/// <param name="text"></param>
public void OutputMessage(string text)
{
Debug.Log(text);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,66 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!850595691 &4890085278179872738
LightingSettings:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FullscreenSampleSettings
serializedVersion: 6
m_GIWorkflowMode: 1
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_RealtimeEnvironmentLighting: 1
m_BounceScale: 1
m_AlbedoBoost: 1
m_IndirectOutputScale: 1
m_UsingShadowmask: 0
m_BakeBackend: 0
m_LightmapMaxSize: 1024
m_BakeResolution: 50
m_Padding: 2
m_LightmapCompression: 0
m_AO: 1
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAO: 0
m_MixedBakeMode: 1
m_LightmapsBakeMode: 1
m_FilterMode: 1
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_RealtimeResolution: 1
m_ForceWhiteAlbedo: 0
m_ForceUpdates: 0
m_FinalGather: 0
m_FinalGatherRayCount: 256
m_FinalGatherFiltering: 1
m_PVRCulling: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVREnvironmentSampleCount: 512
m_PVREnvironmentReferencePointCount: 2048
m_LightProbeSampleCountMultiplier: 4
m_PVRBounces: 2
m_PVRMinBounces: 2
m_PVREnvironmentImportanceSampling: 0
m_PVRFilteringMode: 0
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_PVRTiledBaking: 0
m_NumRaysToShootPerTexel: -1
m_RespectSceneVisibilityWhenBakingGI: 0
+108
View File
@@ -0,0 +1,108 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: SnowParticle
m_Shader: {fileID: 210, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _ALPHABLEND_ON
- _EMISSION
- _FADING_ON
m_InvalidKeywords:
- _COLORCOLOR_ON
m_LightmapFlags: 0
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 3000
stringTagMap:
RenderType: Transparent
disabledShaderPasses:
- GRABPASS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 10300, guid: 0000000000000000f000000000000000, type: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _BlendOp: 0
- _BumpScale: 1
- _CameraFadingEnabled: 1
- _CameraFarFadeDistance: 5
- _CameraNearFadeDistance: 0.5
- _ColorMode: 4
- _Cull: 2
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DistortionBlend: 0.5
- _DistortionEnabled: 0
- _DistortionStrength: 1
- _DistortionStrengthScaled: 0
- _DstBlend: 10
- _EmissionEnabled: 1
- _FlipbookMode: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _LightingEnabled: 1
- _Metallic: 0
- _Mode: 2
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SoftParticlesEnabled: 0
- _SoftParticlesFarFadeDistance: 1
- _SoftParticlesNearFadeDistance: 0
- _SpecularHighlights: 1
- _SrcBlend: 5
- _UVSec: 0
- _ZWrite: 0
m_Colors:
- _CameraFadeParams: {r: 0.5, g: 0.22222222, b: 0, a: 0}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _ColorAddSubDiff: {r: 1, g: 0, b: 0, a: 0}
- _EmissionColor: {r: 1, g: 1, b: 1, a: 1}
- _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
+77
View File
@@ -0,0 +1,77 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: SnowmanArm
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.263
- _GlossyReflections: 1
- _Metallic: 0.683
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 0.9433962, g: 0.64307785, b: 0.3693485, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+78
View File
@@ -0,0 +1,78 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: SnowmanBody
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords: _EMISSION _GLOSSYREFLECTIONS_OFF _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
_SPECULARHIGHLIGHTS_OFF
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 1
- _SpecularHighlights: 0
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0.3490566, g: 0.3490566, b: 0.3490566, a: 1}
+77
View File
@@ -0,0 +1,77 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: SnowmanFace
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords: _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 0
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 1
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 0.21698111, g: 0.21698111, b: 0.21698111, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+5
View File
@@ -0,0 +1,5 @@
{
"displayName": "Menu",
"description": "Sample menu scene for navigating between different example scenes.",
"createSeparatePackage": false
}
+73
View File
@@ -0,0 +1,73 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
namespace Kirurobo
{
public class SampleManager : MonoBehaviour
{
private static SampleManager _instance;
public static SampleManager Instance => _instance ?? (_instance = GameObject.FindAnyObjectByType<SampleManager>() ?? new SampleManager());
public Canvas canvas;
private void Awake()
{
// 设为单例。若已有实例则销毁自身
if (this != Instance)
{
Destroy(this.gameObject);
return;
}
DontDestroyOnLoad(Instance);
DontDestroyOnLoad(UniWindowController.current);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
}
/// <summary>
/// 场景加载时记录主相机
/// </summary>
/// <param name="arg0"></param>
/// <param name="arg1"></param>
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
UniWindowController.current.SetCamera(Camera.main);
}
/// <summary>
/// 打开指定名称的场景
/// </summary>
/// <param name="name">场景名</param>
public void LoadScene(string name)
{
if (name == "SimpleSample")
{
// SimpleSample 时无脚本控制,在此处设置透明
UniWindowController.current.isTransparent = true;
}
else if (name == "FullScreenSample")
{
// FullScreenSample 时强制最大化
UniWindowController.current.shouldFitMonitor = true;
}
SceneManager.LoadScene(name);
}
/// <summary>
/// 退出
/// </summary>
public void Quit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}
}
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!850595691 &4890085278179872738
LightingSettings:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: SampleMenuSettings
serializedVersion: 3
m_GIWorkflowMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 1
m_RealtimeEnvironmentLighting: 1
m_BounceScale: 1
m_AlbedoBoost: 1
m_IndirectOutputScale: 1
m_UsingShadowmask: 1
m_BakeBackend: 1
m_LightmapMaxSize: 1024
m_BakeResolution: 40
m_Padding: 2
m_TextureCompression: 1
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAO: 0
m_MixedBakeMode: 2
m_LightmapsBakeMode: 1
m_FilterMode: 1
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_RealtimeResolution: 2
m_ForceWhiteAlbedo: 0
m_ForceUpdates: 0
m_FinalGather: 0
m_FinalGatherRayCount: 256
m_FinalGatherFiltering: 1
m_PVRCulling: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVREnvironmentSampleCount: 500
m_PVREnvironmentReferencePointCount: 2048
m_LightProbeSampleCountMultiplier: 4
m_PVRBounces: 2
m_PVRMinBounces: 2
m_PVREnvironmentMIS: 0
m_PVRFilteringMode: 2
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
+5
View File
@@ -0,0 +1,5 @@
{
"displayName": "SimpleSample",
"description": "A minimal example demonstrating basic window control setup.",
"createSeparatePackage": false
}
+795
View File
@@ -0,0 +1,795 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_AmbientEquatorColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_AmbientGroundColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 3
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.44657815, g: 0.49641186, b: 0.57481647, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &4
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 1
m_BakeResolution: 50
m_AtlasSize: 1024
m_AO: 1
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 0
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 1
m_BakeBackend: 0
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 500
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 0
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 0
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_LightingSettings: {fileID: 2074312002}
--- !u!196 &5
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666666
manualTileSize: 0
tileSize: 256
buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &144805234
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 144805236}
- component: {fileID: 144805235}
m_Layer: 0
m_Name: Directional light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!108 &144805235
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 144805234}
m_Enabled: 1
serializedVersion: 10
m_Type: 1
m_Shape: 0
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 1
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize: 10
m_Shadows:
m_Type: 0
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ShadowRadius: 0
m_ShadowAngle: 0
--- !u!4 &144805236
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 144805234}
serializedVersion: 2
m_LocalRotation: {x: 0.40821794, y: -0.23456973, z: 0.10938166, w: 0.8754261}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1001 &269992499
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 9167954367673589911, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_Name
value: DragMoveCanvas
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_Pivot.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_Pivot.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_RootOrder
value: 4
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_AnchorMax.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9167954367673589915, guid: d7dcf50428b152040847878685fe0746,
type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: d7dcf50428b152040847878685fe0746, type: 3}
--- !u!1 &452327621
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 452327624}
- component: {fileID: 452327623}
- component: {fileID: 452327625}
m_Layer: 0
m_Name: EventSystem
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &452327623
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 452327621}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_FirstSelected: {fileID: 0}
m_sendNavigationEvents: 1
m_DragThreshold: 10
--- !u!4 &452327624
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 452327621}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &452327625
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 452327621}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ce6b387a66b0e654d9eb8712d70fff48, type: 3}
m_Name:
m_EditorClassIdentifier:
m_SendPointerHoverToParent: 1
m_MoveRepeatDelay: 0.5
m_MoveRepeatRate: 0.1
m_XRTrackingOrigin: {fileID: 0}
m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018,
type: 3}
m_DeselectOnBackgroundClick: 1
m_PointerBehavior: 0
m_CursorLockBehavior: 0
--- !u!1001 &1158200202
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_RootOrder
value: 3
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626842, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626843, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: _isTopmost
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626843, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: _isTransparent
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626844, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: m_Name
value: UniWindowController
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626845, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: _isTopmost
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2416199871598626845, guid: e893aefd93740714b999573b02916984,
type: 3}
propertyPath: _isTransparent
value: 1
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: e893aefd93740714b999573b02916984, type: 3}
--- !u!850595691 &2074312002
LightingSettings:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Settings.lighting
serializedVersion: 6
m_GIWorkflowMode: 1
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_RealtimeEnvironmentLighting: 1
m_BounceScale: 1
m_AlbedoBoost: 1
m_IndirectOutputScale: 1
m_UsingShadowmask: 0
m_BakeBackend: 2
m_LightmapMaxSize: 1024
m_BakeResolution: 50
m_Padding: 2
m_LightmapCompression: 0
m_AO: 1
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAO: 0
m_MixedBakeMode: 1
m_LightmapsBakeMode: 1
m_FilterMode: 1
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_RealtimeResolution: 1
m_ForceWhiteAlbedo: 0
m_ForceUpdates: 0
m_FinalGather: 0
m_FinalGatherRayCount: 256
m_FinalGatherFiltering: 1
m_PVRCulling: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVREnvironmentSampleCount: 512
m_PVREnvironmentReferencePointCount: 2048
m_LightProbeSampleCountMultiplier: 4
m_PVRBounces: 2
m_PVRMinBounces: 2
m_PVREnvironmentImportanceSampling: 0
m_PVRFilteringMode: 0
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_PVRTiledBaking: 0
m_NumRaysToShootPerTexel: -1
m_RespectSceneVisibilityWhenBakingGI: 0
--- !u!1 &2084589444
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2084589449}
- component: {fileID: 2084589448}
- component: {fileID: 2084589445}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &2084589445
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2084589444}
m_Enabled: 1
--- !u!20 &2084589448
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2084589444}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 30
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &2084589449
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2084589444}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -5}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2124152612
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2124152616}
- component: {fileID: 2124152615}
- component: {fileID: 2124152614}
- component: {fileID: 2124152613}
m_Layer: 0
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!23 &2124152613
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2124152612}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10302, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 0
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &2124152614
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2124152612}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!33 &2124152615
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2124152612}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &2124152616
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2124152612}
serializedVersion: 2
m_LocalRotation: {x: 0.35355338, y: 0.35355338, z: -0.1464466, w: 0.8535535}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 45, y: 45.000004, z: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 144805236}
- {fileID: 2084589449}
- {fileID: 2124152616}
- {fileID: 1158200202}
- {fileID: 269992499}
- {fileID: 452327624}
+5
View File
@@ -0,0 +1,5 @@
{
"displayName": "UiSample",
"description": "Complete UI demonstration with toggles and sliders for controlling window properties (transparency, topmost, click-through, etc.).",
"createSeparatePackage": false
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+603
View File
@@ -0,0 +1,603 @@
/**
* UniWindowController 的示例脚本
*
* Author: Kirurobo http://twitter.com/kirurobo
* License: MIT
*/
using System;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace Kirurobo
{
/// <summary>
/// 使用 Toggle 开关 WindowController 设置的示例
/// </summary>
public class UiSampleController : MonoBehaviour
{
private UniWindowController uniwinc;
private UniWindowMoveHandle uniWinMoveHandle;
private RectTransform canvasRect;
private float mouseMoveSS = 0f; // 鼠标轨迹平方和。[px^2]
private float mouseMoveSSThreshold = 36f; // 点击(非拖拽)阈值。[px^2]
private Vector3 lastMousePosition; // 右键点击位置。
private float lastEventOccurredTime = -5f; // 上次事件发生的时间戳 [s]
private float eventMessageTimeout = 1f; // 在此时间段内显示事件消息 [s]
#if ENABLE_LEGACY_INPUT_MANAGER
private float touchDuration = 0f;
private float touchDurationThreshold = 0.5f; // 长按时间阈值。[s]
#endif
public Toggle transparentToggle;
public Slider alphaSlider;
public Toggle topmostToggle;
public Toggle bottommostToggle;
[FormerlySerializedAs("maximizedToggle")] public Toggle zoomedToggle;
public Toggle dragMoveToggle;
public Toggle allowDropToggle;
public Dropdown fitWindowDropdown;
public Toggle showBorderlineToggle;
public Button widthDownButton;
public Button widthUpButton;
public Button heightDownButton;
public Button heightUpButton;
public Dropdown transparentTypeDropdown;
public Dropdown hitTestTypeDropdown;
public Toggle clickThroughToggle;
public Image pickedColorImage;
public Text pickedColorText;
public Text messageText;
public Text clientSizeText;
public Button menuCloseButton;
public RectTransform menuPanel;
public RectTransform borderlinePanel;
/// <summary>
/// 初始化
/// </summary>
void Start()
{
// 查找 UniWindowController
uniwinc = UniWindowController.current;
// 查找 UniWindowDragMove
uniWinMoveHandle = GameObject.FindAnyObjectByType<UniWindowMoveHandle>();
// 获取 Canvas 的 RectTransform
if (menuPanel) canvasRect = menuPanel.GetComponentInParent<Canvas>().GetComponent<RectTransform>();
// 根据有效显示器数量创建选项
UpdateMonitorDropdown();
// 将 Toggle 的选中状态与当前状态同步
UpdateUI();
if (uniwinc)
{
// 操作 UI 时将其反映到窗口
transparentToggle?.onValueChanged.AddListener(val => uniwinc.isTransparent = val);
alphaSlider?.onValueChanged.AddListener(val => uniwinc.alphaValue = val);
topmostToggle?.onValueChanged.AddListener(val => uniwinc.isTopmost = val);
bottommostToggle?.onValueChanged.AddListener(val => uniwinc.isBottommost = val);
zoomedToggle?.onValueChanged.AddListener(val => uniwinc.isZoomed = val);
allowDropToggle?.onValueChanged.AddListener(val => uniwinc.allowDropFiles = val);
fitWindowDropdown?.onValueChanged.AddListener(val => SetFitToMonitor(val));
widthDownButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(-100, 0));
widthUpButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(+100, 0));
heightDownButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(0, -100));
heightUpButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(0, +100));
clickThroughToggle?.onValueChanged.AddListener(val => uniwinc.isClickThrough = val);
transparentTypeDropdown?.onValueChanged.AddListener(val => uniwinc.SetTransparentType((UniWindowController.TransparentType)val));
hitTestTypeDropdown?.onValueChanged.AddListener(val => uniwinc.hitTestType = (UniWindowController.HitTestType)val);
menuCloseButton?.onClick.AddListener(CloseMenu);
if (uniWinMoveHandle) dragMoveToggle?.onValueChanged.AddListener(val => uniWinMoveHandle.enabled = val);
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
// 如果不是 Windows,则禁用透明方式选择
//if (transparentTypeDropdown) transparentTypeDropdown.interactable = false;
//if (transparentTypeDropdown) transparentTypeDropdown.enabled = false;
if (transparentTypeDropdown) transparentTypeDropdown.gameObject.SetActive(false);
#endif
// 添加事件
uniwinc.OnStateChanged += (type) =>
{
UpdateUI();
//Debug.Log("Window state changed: " + type);
ShowEventMessage("状态改变: " + type);
//ShowEventMessage("State changed: " + type + "4:isKey 2:canBecomeKey, 1:canBecomeMain : " + uniwinc.GetDebugInfo().ToString());
ShowClientSize();
};
uniwinc.OnMonitorChanged += () => {
UpdateMonitorDropdown();
UpdateUI();
ShowEventMessage("分辨率已改变!");
ShowClientSize();
};
uniwinc.OnDropFiles += files =>
{
ShowEventMessage(string.Join(Environment.NewLine, files));
};
}
// 即使 UinWinC 未就绪也能运行的 Listener
showBorderlineToggle?.onValueChanged.AddListener(val => borderlinePanel.gameObject.SetActive(val));
}
/// <summary>
/// 显示带超时功能的消息
/// </summary>
/// <param name="message"></param>
private void ShowEventMessage(string message)
{
lastEventOccurredTime = Time.time;
if (messageText) messageText.text = message;
Debug.Log(message);
}
/// <summary>
/// 每帧执行的处理
/// </summary>
private void Update()
{
// 更新点击测试相关的显示
UpdateHitTestUI();
// 显示窗口位置和大小以确认运行状态
if ((lastEventOccurredTime + eventMessageTimeout) < Time.time)
{
ShowWindowMetrics();
}
// 鼠标右键点击显示菜单。移动距离在阈值以下视为点击。
if (InputProxy.GetMouseButtonDown(1))
{
lastMousePosition = InputProxy.mousePosition;
ResetTouchDuration();
}
if (InputProxy.GetMouseButton(1))
{
mouseMoveSS += (InputProxy.mousePosition - lastMousePosition).sqrMagnitude;
}
if (InputProxy.GetMouseButtonUp(1))
{
if (mouseMoveSS < mouseMoveSSThreshold)
{
ShowMenu(lastMousePosition);
}
mouseMoveSS = 0f;
ResetTouchDuration();
}
#if ENABLE_LEGACY_INPUT_MANAGER
// 长按也可显示菜单
if (Input.touchSupported && (Input.touchCount > 0))
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
lastMousePosition = Input.mousePosition;
ResetTouchDuration();
}
if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
{
mouseMoveSS += touch.deltaPosition.sqrMagnitude;
touchDuration += touch.deltaTime;
}
if (touch.phase == TouchPhase.Ended)
{
if ((mouseMoveSS < mouseMoveSSThreshold) && (touchDuration >= touchDurationThreshold))
{
ShowMenu(lastMousePosition);
}
mouseMoveSS = 0f;
ResetTouchDuration();
}
}
#elif ENABLE_INPUT_SYSTEM
// 目前 New Input System 不支持触摸
// EnhancedTouch 不能与 InputAction 同时使用?
#endif
// 按键也可更改设置
if (uniwinc)
{
// 切换透明模式
if (InputProxy.GetKeyUp("t"))
{
uniwinc.isTransparent = !uniwinc.isTransparent;
}
// 切换始终置顶
if (InputProxy.GetKeyUp("f"))
{
uniwinc.isTopmost = !uniwinc.isTopmost;
}
// 切换始终置底
if (InputProxy.GetKeyUp("b"))
{
uniwinc.isBottommost = !uniwinc.isBottommost;
}
// 切换最大化
if (InputProxy.GetKeyUp("z"))
{
uniwinc.isZoomed = !uniwinc.isZoomed;
}
// 切换自由定位
if (InputProxy.GetKeyUp("p"))
{
uniwinc.isFreePositioningEnabled = !uniwinc.isFreePositioningEnabled;
}
}
// 测试打开文件面板
if (InputProxy.GetKeyUp("o"))
{
FilePanel.Settings ds = new FilePanel.Settings
{
flags = FilePanel.Flag.AllowMultipleSelection,
title = "打开!",
filters = new FilePanel.Filter[]{
new FilePanel.Filter("Image files", "png", "jpg", "jpeg"),
new FilePanel.Filter("All files", "*"),
},
initialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
initialFile = "test.png",
};
FilePanel.OpenFilePanel(ds, (files) => ShowEventMessage(string.Join(Environment.NewLine, files)));
}
// 测试保存文件面板
if (InputProxy.GetKeyUp("s"))
{
FilePanel.Settings ds = new FilePanel.Settings
{
flags = FilePanel.Flag.AllowMultipleSelection,
title = "保存!",
filters = new FilePanel.Filter[]{
//// TODO: 指定文件类型时,macOS 保存对话框打开会失败
//// 给 NSSavePanel.accessoryView 指定内容时会发生此问题。
//// NSOpenPanel 继承自它,但不会发生此问题。
// new FilePanel.Filter("Shell script", "sh"),
// new FilePanel.Filter("Log", "log"),
// new FilePanel.Filter("Plain text", "txt"),
// new FilePanel.Filter("All files", "*"),
},
initialFile = "Test.txt",
initialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
};
FilePanel.SaveFilePanel(ds, (files) => ShowEventMessage(string.Join(Environment.NewLine, files)));
}
// 按下 [ESC] 时退出或停止播放
if (InputProxy.GetKeyUp("escape"))
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}
/// <summary>
/// 重置持续触摸时间的记录
/// 仅在 Legacy Input Manager 中处理以避免警告
/// </summary>
void ResetTouchDuration() {
#if ENABLE_LEGACY_INPUT_MANAGER
touchDuration = 0f;
#endif
}
/// <summary>
/// 适配显示器下拉列表更改时的处理
/// </summary>
/// <param name="val"></param>
void SetFitToMonitor(int val)
{
if (!uniwinc) return;
if (val < 1)
{
// 下拉列表第一项为不适配
uniwinc.shouldFitMonitor = false;
// 允许更改最大化状态
if (zoomedToggle) zoomedToggle.interactable = true;
}
else
{
// 由于从第二项开始,显示器编号减1
uniwinc.monitorToFit = val - 1;
uniwinc.shouldFitMonitor = true; // 从false变为true时才会移动窗口,因此先指定显示器编号再更改
// 禁止更改最大化状态
if (zoomedToggle) zoomedToggle.interactable = false;
}
}
/// <summary>
/// 显示窗口位置和坐标
/// </summary>
void ShowWindowMetrics()
{
if (uniwinc)
{
var winPos = uniwinc.windowPosition;
//var curPos = uniwinc.GetClientCursorPosition();
OutputMessage(
"位置: " + winPos
+ "\n大小: " + uniwinc.windowSize
+ "\n相对鼠标:" + (uniwinc.cursorPosition - winPos)
//+ "\nScr. Cur.:" + curPos
+ "\nUnity鼠标:" + (Vector2)InputProxy.mousePosition
);
ShowClientSize();
}
}
/// <summary>
/// 聚焦时刷新 UI
/// </summary>
/// <param name="hasFocus"></param>
private void OnApplicationFocus(bool hasFocus)
{
if (hasFocus)
{
UpdateUI();
if (uniwinc)
{
OutputMessage("已聚焦");
}
else
{
OutputMessage("未找到 UniWindowController");
}
}
}
/// <summary>
/// 在指定坐标显示上下文菜单
/// </summary>
/// <param name="position">指定中心坐标</param>
private void ShowMenu(Vector2 position)
{
if (menuPanel)
{
Vector2 pos = position * (canvasRect.sizeDelta.x / Screen.width);
float w = menuPanel.rect.width;
float h = menuPanel.rect.height;
// 以指定坐标为中心进行位置调整
pos.y = Mathf.Max(Mathf.Min(pos.y, Screen.height - h / 2f), h / 2f); // 如果超出则向上移动
pos.x = Mathf.Max(Mathf.Min(pos.x, Screen.width - w / 2f), w / 2f); // 如果超出右侧则向左移动
menuPanel.pivot = Vector2.one * 0.5f; // 设置为中心
menuPanel.anchorMin = Vector2.zero;
menuPanel.anchorMax = Vector2.zero;
menuPanel.anchoredPosition = pos;
menuPanel.gameObject.SetActive(true);
}
}
/// <summary>
/// 关闭上下文菜单
/// </summary>
private void CloseMenu()
{
if (menuPanel)
{
menuPanel.gameObject.SetActive(false);
}
}
/// <summary>
/// 将实际状态反映到 UI 显示
/// </summary>
private void UpdateUI()
{
if (uniwinc)
{
if (transparentToggle)
{
transparentToggle.SetIsOnWithoutNotify(uniwinc.isTransparent);
}
if (alphaSlider)
{
alphaSlider.SetValueWithoutNotify(uniwinc.alphaValue);
}
if (topmostToggle)
{
topmostToggle.SetIsOnWithoutNotify(uniwinc.isTopmost);
}
if (bottommostToggle)
{
bottommostToggle.SetIsOnWithoutNotify(uniwinc.isBottommost);
}
if (zoomedToggle)
{
zoomedToggle.SetIsOnWithoutNotify(uniwinc.isZoomed);
}
if (allowDropToggle)
{
allowDropToggle.SetIsOnWithoutNotify(uniwinc.allowDropFiles);
}
if (dragMoveToggle)
{
dragMoveToggle.isOn = (uniWinMoveHandle && uniWinMoveHandle.isActiveAndEnabled);
}
if (fitWindowDropdown)
{
if (uniwinc.shouldFitMonitor)
{
fitWindowDropdown.value = uniwinc.monitorToFit + 1;
if (zoomedToggle) zoomedToggle.interactable = false;
}
else
{
fitWindowDropdown.value = 0;
if (zoomedToggle) zoomedToggle.interactable = true;
}
fitWindowDropdown.RefreshShownValue();
}
if (transparentTypeDropdown)
{
transparentTypeDropdown.value = (int)uniwinc.transparentType;
transparentTypeDropdown.RefreshShownValue();
}
if (hitTestTypeDropdown)
{
hitTestTypeDropdown.value = (int)uniwinc.hitTestType;
hitTestTypeDropdown.RefreshShownValue();
}
// 同时更新点击测试部分的显示
UpdateHitTestUI();
}
// 即使没有 UniWinC 也能运行的部分
if (showBorderlineToggle && borderlinePanel)
{
borderlinePanel.gameObject.SetActive(showBorderlineToggle.isOn);
}
}
/// <summary>
/// 更新点击测试相关的 UI
/// 由于会自动变化,需要比 UpdateUI() 更频繁地更新
/// </summary>
public void UpdateHitTestUI()
{
if (uniwinc)
{
if (clickThroughToggle)
{
clickThroughToggle.SetIsOnWithoutNotify(uniwinc.isClickThrough);
if (uniwinc.hitTestType == UniWindowController.HitTestType.None)
{
clickThroughToggle.interactable = true;
}
else
{
clickThroughToggle.interactable = false;
}
}
if (uniwinc.hitTestType == UniWindowController.HitTestType.Opacity && uniwinc.isTransparent)
{
if (pickedColorImage)
{
pickedColorImage.color = uniwinc.pickedColor;
}
if (pickedColorText)
{
pickedColorText.text = $"Alpha:{uniwinc.pickedColor.a:P0}";
pickedColorText.color = Color.black;
}
}
else
{
if (pickedColorImage)
{
pickedColorImage.color = Color.gray;
}
if (pickedColorText)
{
pickedColorText.text = $"颜色拾取器已禁用";
pickedColorText.color = Color.gray;
}
}
}
}
/// <summary>
/// 更新显示器选择下拉列表的选项
/// 之后需要调用 UpdateUI()
/// </summary>
void UpdateMonitorDropdown()
{
if (!fitWindowDropdown) return;
// 删除除第一项以外的选项
fitWindowDropdown.options.RemoveRange(1, fitWindowDropdown.options.Count - 1);
if (!uniwinc)
{
fitWindowDropdown.value = 0;
}
else
{
int count = UniWindowController.GetMonitorCount();
for (int i = 0; i < count; i++)
{
fitWindowDropdown.options.Add(new Dropdown.OptionData("适配显示器 " + i));
}
if (uniwinc.monitorToFit >= count)
{
uniwinc.monitorToFit = count - 1;
}
}
}
/// <summary>
/// 如果 UI 中有文本框,则显示消息。否则输出到控制台
/// </summary>
/// <param name="text"></param>
public void OutputMessage(string text)
{
if (messageText)
{
messageText.text = text;
}
else
{
Debug.Log(text);
}
}
/// <summary>
/// 如果 UI 中有客户端大小文本框,则显示消息。否则输出到控制台
/// </summary>
public void ShowClientSize()
{
if (!uniwinc) return;
string text = "Client " + uniwinc.clientSize;
if (clientSizeText)
{
clientSizeText.text = text;
}
else
{
Debug.Log(text);
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4f4b26af82054284b89cba410c27b75d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8878429ebc524544d849c1bc8884262a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+29
View File
@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
namespace Lrss3.Xuniwindowcontroller.Editor.Tests
{
class EditorExampleTest
{
[Test]
public void EditorSampleTestSimplePasses()
{
// Use the Assert class to test conditions.
}
// A UnityTest behaves like a coroutine in PlayMode
// and allows you to yield null to skip a frame in EditMode
[UnityTest]
public IEnumerator EditorSampleTestWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// yield to skip a frame
yield return null;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b16c3acbffcdc7478199eacc7ec8497
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
{
"name": "Lrss3.Xuniwindowcontroller.Editor.Tests",
"references": [
"Lrss3.Xuniwindowcontroller.Editor",
"Lrss3.Xuniwindowcontroller"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9cdd0222c2019eb4f984a69d349fa385
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e6602e0b6b65ef7438c1a83bcf07d1b2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
{
"name": "Lrss3.Xuniwindowcontroller.Tests",
"references": [
"Lrss3.Xuniwindowcontroller"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [],
"excludePlatforms": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 536bc02f6b95d694cbf83ddcac597970
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+28
View File
@@ -0,0 +1,28 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
namespace Lrss3.Xuniwindowcontroller.Tests
{
class RuntimeExampleTest
{
[Test]
public void PlayModeSampleTestSimplePasses()
{
// Use the Assert class to test conditions.
}
// A UnityTest behaves like a coroutine in PlayMode
// and allows you to yield null to skip a frame in EditMode
[UnityTest]
public IEnumerator PlayModeSampleTestWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// yield to skip a frame
yield return null;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f5802e9dd8c9bf4fb15aa0be21a95fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+23
View File
@@ -0,0 +1,23 @@
This package is based on [UniWindowController](https://github.com/kirurobo/UniWindowController) by Kirurobo.
Licensed under the MIT License.
Copyright (c) 2024 Kirurobo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6f366de21623c6e428017761c1d29bc5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+49
View File
@@ -0,0 +1,49 @@
{
"name": "com.lrss3.xuniwindowcontroller",
"displayName": "XUniWindowController",
"version": "0.1.0",
"unity": "2022.3",
"description": "A Unity native plugin for controlling standalone application windows on Windows and macOS. \nSupports transparency, click-through, topmost/bottommost, maximize, file drop, native file dialogs, and multi-monitor window fitting.",
"author": {
"name": "Lrss3",
"url": "https://github.com/Lrss3"
},
"keywords": [
"window",
"transparent",
"click-through",
"native",
"plugin",
"windows",
"macos"
],
"category": "library",
"dependencies": {},
"samples": [
{
"displayName": "Menu",
"description": "Sample menu scene for navigating between different example scenes.",
"path": "Samples~/Menu"
},
{
"displayName": "SimpleSample",
"description": "A minimal example demonstrating basic window control setup.",
"path": "Samples~/SimpleSample"
},
{
"displayName": "UiSample",
"description": "Complete UI demonstration with toggles and sliders for controlling window properties (transparency, topmost, click-through, etc.).",
"path": "Samples~/UiSample"
},
{
"displayName": "Fullscreen",
"description": "Fullscreen mode example with right-click context menu and 3D snowman scene.",
"path": "Samples~/Fullscreen"
},
{
"displayName": "FileDialog",
"description": "Demonstrates native file open/save dialog integration.",
"path": "Samples~/FileDialog"
}
]
}
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7a9376ee4260d5a4a94bfab85cb4351a
PackageManifestImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: