This commit is contained in:
CortexCore
2025-02-24 23:02:43 +08:00
parent 41715e4413
commit 8261a458e2
105 changed files with 2934 additions and 696 deletions

View File

@@ -0,0 +1,3 @@
{
"reference": "GUID:14fe60d984bf9f84eac55c6ea033a8f4"
}

View File

@@ -1,7 +1,6 @@
fileFormatVersion: 2
guid: f51cb0688f8e0454e8dce6e90da31939
folderAsset: yes
DefaultImporter:
guid: d31ceb431a775db4a8757a1208d82106
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:

View File

@@ -36,7 +36,7 @@
"name": "Crouch",
"type": "Button",
"id": "eab8ff36-98a9-4a2d-8393-167d9347227b",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Tap",
"initialStateCheck": false
@@ -45,7 +45,7 @@
"name": "Crawl",
"type": "Button",
"id": "350ae177-bf96-40b1-aff0-e10865947df9",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@@ -54,7 +54,7 @@
"name": "HoldCrouch",
"type": "Button",
"id": "07dfe885-d709-4482-9686-57f671b1aed6",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press",
"initialStateCheck": false
@@ -63,7 +63,7 @@
"name": "Fire",
"type": "Button",
"id": "9db494c5-bec3-4b09-bd5f-66ba07a3a729",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Tap,Hold",
"initialStateCheck": false
@@ -81,7 +81,7 @@
"name": "Interactive",
"type": "Button",
"id": "9b32c7f1-0553-4735-b0f9-7726d59808ca",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Hold",
"initialStateCheck": false
@@ -90,7 +90,7 @@
"name": "Melee",
"type": "Button",
"id": "0afd994b-f97e-4198-8700-5f570d3b7b56",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@@ -99,7 +99,7 @@
"name": "Run",
"type": "Button",
"id": "95bba0fe-8e29-470a-a7ef-126b21ea4b5c",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,MultiTap",
"initialStateCheck": false
@@ -108,7 +108,7 @@
"name": "Reload",
"type": "Button",
"id": "53f6beb0-aeec-43f0-aa36-f4d54f068d75",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@@ -234,7 +234,7 @@
"name": "ToggleCamera",
"type": "Button",
"id": "a207eefa-8338-4ae0-95a1-8f650a3db235",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press",
"initialStateCheck": false
@@ -272,6 +272,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "bcc014bd-8bca-4b71-96b9-62fa63db2d17",
"path": "<Pointer>/delta",
"interactions": "",
"processors": "",
"groups": "",
"action": "View",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "3204e30b-6062-47f0-9af6-e72db104c7ec",
@@ -283,6 +294,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "88f9f36f-9b75-44a7-9176-b07a7daee4ab",
"path": "<XInputController>/buttonSouth",
"interactions": "",
"processors": "",
"groups": "",
"action": "Jump",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c6f71c75-cb44-418b-9dc0-4202757d20ad",
@@ -294,6 +316,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "2560f543-52ea-48c4-a2f6-df8b9deef518",
"path": "<XInputController>/buttonEast",
"interactions": "",
"processors": "",
"groups": "",
"action": "Crouch",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "ad8529d0-2894-4cb2-aea4-f07f96251032",
@@ -316,6 +349,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "d158c65a-e04f-4a62-a576-d13796c3d903",
"path": "<Gamepad>/rightTrigger",
"interactions": "",
"processors": "",
"groups": "",
"action": "Fire",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "05431a8a-4464-49e0-8a15-eec2a4351985",
@@ -327,6 +371,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "fce3a9d9-2f96-4a18-bc01-4f6a61f66e59",
"path": "<Gamepad>/leftTrigger",
"interactions": "",
"processors": "",
"groups": "",
"action": "Aim",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "9a305ecb-00ee-46fb-be5b-f58125ac34eb",
@@ -338,6 +393,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "9a2d7939-d112-4796-a50f-7e723cb0ebb4",
"path": "<XInputController>/rightStickPress",
"interactions": "",
"processors": "",
"groups": "",
"action": "Melee",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "2D Vector",
"id": "f2793bde-975d-41cf-b721-4ea21c2cfe7d",
@@ -415,6 +481,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "7475328b-a74c-4c33-90ad-0a59351a3595",
"path": "<XInputController>/leftStickPress",
"interactions": "",
"processors": "",
"groups": "",
"action": "Run",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "b8ca7195-54e8-472e-9849-378ea8b1b2d4",
@@ -536,6 +613,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "fa054fa0-fc44-4096-a9df-ea3e6cae27f3",
"path": "<XInputController>/buttonWest",
"interactions": "",
"processors": "",
"groups": "",
"action": "Interactive",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "abfe62ef-1678-40c6-85e4-7644a3ffffc3",
@@ -547,6 +635,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "ccb765ab-4913-467a-9b10-c66f7b1b836c",
"path": "<XInputController>/buttonWest",
"interactions": "Hold",
"processors": "",
"groups": "",
"action": "Reload",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c354e7ce-0648-4b49-b042-1f7dfc7657d9",
@@ -591,6 +690,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "438f83aa-f2a6-4d51-84a2-c6b334924fb0",
"path": "<XInputController>/select",
"interactions": "",
"processors": "",
"groups": "",
"action": "ToggleCamera",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c7bf69ae-febf-4280-b153-d529229f82b5",
@@ -686,7 +796,7 @@
"name": "Cancel",
"type": "Button",
"id": "60dfbf31-4ec8-4df1-be35-9f1eca07d7be",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Hold,Tap",
"initialStateCheck": false
@@ -713,7 +823,7 @@
"name": "Inventory",
"type": "Button",
"id": "80289a66-dc26-4d9e-967d-a561bb8794e3",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "Press,Hold",
"initialStateCheck": false
@@ -740,6 +850,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "16cf3841-290b-4e38-9b3f-71d4a5428c70",
"path": "<Gamepad>/leftStick",
"interactions": "",
"processors": "",
"groups": "",
"action": "Point",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "39028ab4-2f9f-43b2-99f6-6689cbc29e17",
@@ -817,6 +938,50 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "c65bdae3-6e08-4931-b364-738eb5248f5c",
"path": "<Gamepad>/start",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "e610040c-2163-48b0-892a-d17c525b4763",
"path": "<VirtualMouse>/{Cancel}",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "835f92ac-0a3f-495c-a09b-694b13942d96",
"path": "<Touchscreen>/{Cancel}",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "a490bb73-90bd-4cd2-b702-cd89bc1987d4",
"path": "<Pointer>/{Cancel}",
"interactions": "",
"processors": "",
"groups": "",
"action": "Cancel",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "5f7a54af-6a1b-4e69-8a46-57a6772019cc",
@@ -850,6 +1015,17 @@
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "6f51a8c8-9f11-40d5-a0b9-3a78dde74a22",
"path": "<Gamepad>/dpad/up",
"interactions": "",
"processors": "",
"groups": "",
"action": "Inventory",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "WASD",
"id": "a40b1b7f-8cb3-4138-80a0-ec2771a2481a",
@@ -981,7 +1157,7 @@
"name": "Vertical",
"type": "Button",
"id": "5703bb8a-1107-4f73-91fa-9bbc362d9528",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@@ -1093,7 +1269,7 @@
"name": "AscendAndDescend",
"type": "Button",
"id": "a78acdb6-b9dd-4d90-a89e-58fb62647380",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@@ -1309,7 +1485,7 @@
"name": "Vertical",
"type": "Button",
"id": "cc774816-362e-40d3-98c5-19411a7ae97a",
"expectedControlType": "Button",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false

View File

@@ -6,7 +6,6 @@ using System.IO;
using BITKit.IO;
using BITKit.UX;
using Cysharp.Threading.Tasks;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
@@ -26,9 +25,11 @@ namespace BITKit
private static void Reload()
{
Stopwatch = new Stopwatch();
#if UNITY_EDITOR
#else
SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
#endif
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.Entities;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using UnityEngine;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace BITKit.IO
{
public class ScriptableEntitiesService : IDisposable
{
private readonly IEntitiesService _entitiesService;
public string Tags = "scriptable_object";
private readonly List<IEntity> _registeredEntities = new();
public ScriptableEntitiesService(IEntitiesService entitiesService)
{
_entitiesService = entitiesService;
}
public async UniTask InitializeAsync(ILogger logger = null)
{
logger?.LogInformation("正在查找所有ScriptableObject...");
var objs = await ModService.LoadAssets<ScriptableObject>(Tags);
logger?.LogInformation($"找到{objs.Count}个资源,正在加载中...");
for (var index = 0; index < objs.Count; index++)
{
var scriptableObject = objs[index];
var entity = new Entity();
var idComponent = new IdComponent();
entity.ServiceCollection.AddSingleton(idComponent);
var type = scriptableObject.GetType();
entity.ServiceCollection.AddSingleton(type, scriptableObject);
_entitiesService.Register(entity);
logger?.LogInformation($"已加载:{scriptableObject.name}:{type.Name},剩余:{index + 1}/{objs.Count}");
_registeredEntities.Add(entity);
}
logger?.LogInformation("加载完成");
}
public void Dispose()
{
foreach (var x in _registeredEntities)
{
_entitiesService.UnRegister(x);
}
_registeredEntities.Clear();
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 054d7fc7589c04d4a8412a6f82b32d6c
guid: e1ff18f234ca249459fb3df556f90b11
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,30 +1,165 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.Logging;
using UnityEditor;
using UnityEngine;
using YooAsset;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using Object = UnityEngine.Object;
namespace BITKit.IO
{
public class YooAssetModHelper : MonoBehaviour
public class YooAssetModHelper
{
private void OnEnable()
public static string Url = "http://server.bitfall.icu:21982/com.project.b/Mods";
public static readonly ConcurrentDictionary<string, HashSet<string>> PackagesManifestDictionary = new();
public static async UniTask LoadMods(ILogger logger=null)
{
ModService.LoadAssetAsyncFactory += LoadAsset;
try
{
var modPath = Path.Combine(Environment.CurrentDirectory, "Mods");
if (Application.platform is RuntimePlatform.Android)
{
modPath = Path.Combine($"/storage/emulated/0/{Application.identifier}/Mods/");
}
foreach (var directoryInfo in new DirectoryInfo(modPath).GetDirectories())
{
var packageName = directoryInfo.Name;
logger?.LogInformation($"开始加载:{packageName}");
var package = YooAssets.CreatePackage(packageName);
var initPars = new HostPlayModeParameters()
{
BuildinQueryServices = new GameQueryServices(),
RemoteServices = new RemoteServices($"{Url}/{directoryInfo.Name}",$"file://{directoryInfo.FullName}")
};
await package.InitializeAsync(initPars);
var update = package.UpdatePackageVersionAsync();
await update.ToUniTask();
var manifest = package.UpdatePackageManifestAsync(update.PackageVersion);
await manifest.ToUniTask();
var downloader = package.CreateResourceDownloader(10, 3);
if (logger is not null)
{
downloader.OnDownloadProgressCallback = (totalDownloadCount, currentDownloadCount,
totalDownloadBytes, currentDownloadBytes) =>
{
//下载进度
var progress = (float)currentDownloadBytes / totalDownloadBytes;
logger.LogInformation($"已下载{(int)(progress*100)}%,资源数量{currentDownloadBytes}/{totalDownloadBytes}");
};
downloader.OnDownloadErrorCallback = (fileName, error) =>
{
logger.LogError($"资源[{fileName}]下载错误::{error}");
};
}
downloader.BeginDownload();
await downloader.ToUniTask();
var mod = new MyMod()
{
FolderPath = directoryInfo.FullName,
PackageName = packageName,
Name = packageName
};
await ModService.Install(mod);
await ModService.Load(mod);
YooAssetUtils.RegisterResourcePackage(package);
logger?.LogInformation($"已加载:{packageName},路径:{directoryInfo.FullName}");
}
return;
}
catch (Exception e)
{
if (logger is not null)
{
logger.LogCritical(e.Message,e);
}
else
{
BIT4Log.LogException(e);
}
}
logger?.LogInformation($"未找到Mod");
}
private void OnDisable()
private static HashSet<string> BuildPackageCache(string obj)
{
ModService.LoadAssetAsyncFactory -= LoadAsset;
var package = YooAssets.GetPackage(obj);
var playMode = package.GetType().GetField("_playModeImpl", ReflectionHelper.Flags)!.GetValue(package);
var manifest = playMode.GetType().GetProperty("ActiveManifest", ReflectionHelper.Flags)!.GetValue(playMode);
var dictionary = manifest.GetType().GetField("AssetDic", ReflectionHelper.Flags)!.GetValue(manifest) as IDictionary;
var hashset = new HashSet<string>();
foreach (var key in dictionary.Keys)
{
hashset.Add(key?.ToString());
}
var assetPathMapping1=manifest.GetType().GetField("AssetPathMapping1", ReflectionHelper.Flags)!.GetValue(manifest) as IDictionary<string,string>;
foreach (var key in assetPathMapping1.Keys)
{
hashset.Add(key);
}
return hashset;
}
private static async UniTask<object> LoadAsset(string arg)
public static async UniTask<IReadOnlyList<object>> LoadAssets( string[] arg)
{
var handle = YooAssets.LoadAssetAsync(arg);
await handle;
return handle.AssetObject;
var list = new List<object>();
foreach (var resourcePackage in YooAssetUtils.RegisteredResourcePackages)
{
foreach (var assetInfo in resourcePackage.GetAssetInfos(arg))
{
var asyncHandle = resourcePackage.LoadAssetAsync(assetInfo);
await asyncHandle;
list.Add(asyncHandle.AssetObject);
}
}
return list;
}
public static async UniTask<object> LoadAsset(string arg)
{
foreach (var resourcePackage in YooAssetUtils.RegisteredResourcePackages.Reverse())
{
if(PackagesManifestDictionary.GetOrAdd(resourcePackage.PackageName,BuildPackageCache).Contains(arg) is false)continue;
var assetInfo = resourcePackage.GetAssetInfo(arg);
if(string.IsNullOrEmpty(assetInfo.Error) is false)continue;
var handle = resourcePackage.LoadAssetAsync(arg);
await handle;
return handle.AssetObject;
}
return null;
}
}
}

View File

@@ -203,6 +203,14 @@ namespace BITKit
BITApp.Time.DeltaTime = Time.deltaTime;
BITApp.Time.TimeAsDouble = Time.timeAsDouble;
allowCursor = AllowCursor.Allow;
if (Touchscreen.current is not null && Touchscreen.current.IsPressed())
{
AllowCursor.AddElement(int.MaxValue);
}else if (Mouse.current is not null && Mouse.current.IsPressed())
{
AllowCursor.RemoveElement(int.MaxValue);
}
}
public string GetName()

View File

@@ -36,6 +36,19 @@ namespace BITKit
action.RegisterCallback(callback);
return this;
}
public InputActionGroup RegisterCallback(InputAction inputAction,Action<InputAction.CallbackContext> callback)
{
EnsureConfiguration();
var action = actions.GetOrAdd(inputAction.name, _=>inputAction.Clone());
allowInput.Invoke();
action.RegisterCallback(callback);
return this;
}
public InputAction EnsureCreated(InputActionReference reference)
{
@@ -57,6 +70,20 @@ namespace BITKit
return action;
}
public InputAction EnsureCreated(InputAction inputAction)
{
EnsureConfiguration();
var action = actions.GetOrAdd(inputAction.name, _ =>
{
var newAction = inputAction.Clone();
newAction.Rename(inputAction.name);
return newAction;
});
allowInput.Invoke();
return action;
}
public void Inherit(InputActionGroup other)
{
@@ -80,6 +107,12 @@ namespace BITKit
action.UnRegisterCallback(callback);
}
public void UnRegisterCallback(InputAction inputAction, Action<InputAction.CallbackContext> callback)
{
if(actions.TryGetValue(inputAction.name,out var action))
action.UnRegisterCallback(callback);
}
private void EnsureConfiguration()
{
if (state is not InitializationState.Initialized)

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit;
using UnityEngine;
namespace UnityEngine.InputSystem
{
public class UnityPlayerInput : MonoBehaviour
{
private readonly InputActionGroup _inputActionGroup=new();
[SerializeField] private InputActionAsset inputActionAsset;
private bool _isInitialized;
private void Start()
{
foreach (var inputActionMap in inputActionAsset.actionMaps)
{
foreach (var inputAction in inputActionMap.actions)
{
_inputActionGroup.RegisterCallback(inputAction, x =>
{
SendMessage($"On{inputAction.name}",x,SendMessageOptions.DontRequireReceiver);
});
}
}
if (_isInitialized is false)
{
_inputActionGroup.allowInput.AddElement(this);
}
_isInitialized = true;
}
private void OnEnable()
{
if (_isInitialized)
_inputActionGroup.allowInput.AddElement(this);
}
private void OnDisable()
{
_inputActionGroup.allowInput.RemoveElement(this);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b030d336e53d2c646a7b382eb83897bf
guid: 6b2748010e8669a44b12cd163ea95fa2
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,121 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BITKit.UX;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace BITKit.Mod
{
public class UnityModService : MonoBehaviour
{
[SerializeReference,SubclassSelector] private IReference[] referencedAssemblies;
private async void Start()
{
//DI.TryGet<IUXWaiting>(out var waiting);
IUXWaiting waiting = null;
var handle = waiting?.Get();
handle?.SetMessage("正在初始化Mod服务");
if (Application.isEditor is false)
{
BIT4Log.Log<UnityModService>($"UnityPlayer所在位置:{Application.dataPath}");
BIT4Log.Log<UnityModService>($"{nameof(System.Linq)}位于{typeof(Enumerable).Assembly.Location}");
}
foreach (var x in referencedAssemblies)
{
var dllName = x.Value.Contains(".dll") ? x.Value : $"{x.Value}.dll";
if (SearchDll(dllName,out var dll) is false)
{
BIT4Log.Warning<UnityModService>($"未找到:{dll}");
continue;
}
BITSharp.ReferencedAssemblies.Add(@$"""{dll}""");
}
try
{
ModService.Initialize();
}
catch (Exception e)
{
BIT4Log.Warning<UnityModService>("初始化失败");
BIT4Log.LogException(e);
return;
}
destroyCancellationToken.Register(ModService.Dispose);
ModService.OnPackageLoad+=OnPackageLoad;
var packages = await ModService.SearchPackages();
if (destroyCancellationToken.IsCancellationRequested) return;
foreach (var package in packages)
{
await ModService.LoadFromPackage(package.PackagePath);
if (destroyCancellationToken.IsCancellationRequested) return;
}
destroyCancellationToken.Register(() =>
{
ModService.OnPackageLoad-=OnPackageLoad;
});
waiting?.Release(handle);
}
private void OnPackageLoad(ModPackage obj)
{
var loadedDlls = referencedAssemblies.Cast();
var reportBuilder = new System.Text.StringBuilder();
//对比已加载的dll和当前引用的dll
foreach (var x in obj.Dlls.Except(loadedDlls))
{
if (SearchDll(x, out var dll) is false)
{
BIT4Log.Warning<UnityModService>($"未找到:{dll}");
continue;
}
BITSharp.ReferencedAssemblies.Add(@$"""{dll}""");
reportBuilder.AppendLine($"加载:{dll}");
}
BIT4Log.Log<UnityModService>(reportBuilder.ToString());
}
private bool SearchDll(string dllName,out string dll,params string[] moreFolder)
{
#if UNITY_EDITOR
dll = System.IO.Path.Combine(Environment.CurrentDirectory, "Library", "ScriptAssemblies", dllName);
var folder = EditorApplication.applicationPath;
folder = Path.GetDirectoryName(folder);
if(File.Exists(dll) is false)
{
dll = Path.Combine(folder,"Data", "MonoBleedingEdge", "lib","mono","unityjit-win32",dllName);
}
if (File.Exists(dll) is false)
{
dll = Path.Combine(folder,"Data", "MonoBleedingEdge", "lib","mono","unityjit-win32","Facades",dllName);
}
#else
dll = System.IO.Path.Combine(Environment.CurrentDirectory,$"{Application.productName}_Data", "Managed", dllName);
#endif
return File.Exists(dll);
}
}
}

View File

@@ -1,48 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace BITKit.Mod
{
public class UnityModServiceTester : MonoBehaviour
{
[Serializable]
public class TestLogGameTickMod:MyMod
{
public override void OnInitialized()
{
base.OnInitialized();
GameTickService.Add(OnTick);
}
private static void OnTick(float obj)
{
BIT4Log.Log<TestLogGameTickMod>($"On Test Mod Tick,delta:{obj}");
}
public override void OnDispose()
{
GameTickService.Remove(OnTick);
}
}
[SerializeReference, SubclassSelector] private IMod[] initialMods;
private void OnEnable()
{
foreach (var testMod in initialMods)
{
ModService.Install(testMod);
ModService.Load(testMod);
}
}
private void OnDisable()
{
foreach (var testMod in initialMods)
{
ModService.UnLoad(testMod);
ModService.UnInstall(testMod);
}
}
}
}

View File

@@ -2,7 +2,8 @@
"name": "BITKit.Physics",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4"
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:6babdba9f8b742f40904649736008000"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -0,0 +1,99 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.Physics
{
public static class GeometryUtils
{
/// <summary>
/// 获取一个点到一条线段的最近点
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static Vector3 PointToLineSegmentDistance(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
{
Vector3 lineDirection = lineEnd - lineStart;
Vector3 pointDirection = point - lineStart;
float lineLength = lineDirection.magnitude;
lineDirection.Normalize();
float dotProduct = Vector3.Dot(pointDirection, lineDirection);
dotProduct = Mathf.Clamp(dotProduct, 0f, lineLength);
Vector3 closestPoint = lineStart + lineDirection * dotProduct;
return closestPoint;
}
/// <summary>
/// 获取一个点到一个三角形内最短距离的点
/// </summary>
/// <param name="a">三角形顶点a</param>
/// <param name="b">三角形顶点b</param>
/// <param name="c">三角形顶点c</param>
/// <param name="pos"></param>
/// <returns></returns>
public static Vector3 GetPosInTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 pos)
{
Vector3 normal = Vector3.Cross(b - a, c - a).normalized;
Vector3 toPoint = pos - a;
float distance = Vector3.Dot(toPoint, normal);
Vector3 targetPos = pos - distance * normal;
if(PointInTriangle(targetPos, a, b, c))
return targetPos;
else
{
Vector3 p1 = PointToLineSegmentDistance(pos, a, b);
Vector3 p2 = PointToLineSegmentDistance(pos, a, c);
Vector3 p3 = PointToLineSegmentDistance(pos, b, c);
float d1 = Vector3.Distance(p1, pos);
float d2 = Vector3.Distance(p2, pos);
float d3 = Vector3.Distance(p3, pos);
if (d1 <= d2 && d1 <= d3)
return p1;
else if (d2 <= d3 && d2 <= d1)
return p2;
else /*if(d3 <= d1 && d3 <= d2)*/
return p3;
//return default;
}
}
/// <summary>
/// 判断一个点是否在三角形内
/// </summary>
/// <param name="pos"></param>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <returns></returns>
public static bool PointInTriangle(Vector3 pos, Vector3 a, Vector3 b, Vector3 c)
{
var v0 = c - a;
var v1 = b - a;
var v2 = pos - a;
var dot00 = Vector3.Dot(v0, v0);
var dot01 = Vector3.Dot(v0, v1);
var dot02 = Vector3.Dot(v0, v2);
var dot11 = Vector3.Dot(v1, v1);
var dot12 = Vector3.Dot(v1, v2);
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// 如果u和v都在[0,1]的范围内那么点P在三角形ABC内
return (u >= 0) && (v >= 0) && (u + v < 1);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6d0be10870bf27f4b8b1c97ed8809308
guid: ae14ae5985cea8e40946355bfdf7113b
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,7 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using DrawXXL;
using UnityEngine;
namespace BITKit.Physics
@@ -9,7 +11,7 @@ namespace BITKit.Physics
public class GetClosePointFromCollider : IClosePointProvider
{
public string Name="Default";
public Transform Transform;
public readonly Transform Transform;
public Vector3 Offset = default;
public LayerMask LayerMask=LayerMask.NameToLayer("Default");
public float Distance=1.6f;
@@ -26,68 +28,97 @@ namespace BITKit.Physics
public bool TryGetValue(out Vector3 position, out Collider collider)
{
Vector3 vector3 = default;
StringBuilder reportBuilder = new();
reportBuilder.AppendLine($"检测任务:{Name}");
position = Transform.position + Transform.rotation * Offset;
var detectedLength = UnityEngine.Physics.OverlapSphereNonAlloc(position, Distance, _mainCollider, LayerMask);
reportBuilder.AppendLine($"检测到了{detectedLength}个碰撞体");
var validMeshColliders = new Queue<(Collider collider,Vector3 targetPosition)>();
var samplePoint = position;
samplePoint.y += Distance / 2;
foreach (var collider1 in _mainCollider.Take(detectedLength).OrderBy(ByTop).Reverse())
//for (var i = 0; i <detectedLength ; i++)
{
//reportBuilder.AppendLine($"----------------------------检测到了碰撞体{_mainCollider[i].name}");
//var collider = _mainCollider[i];
if (collider1.isTrigger)
{
reportBuilder?.AppendLine("碰撞体是触发器");
continue;
}
Vector3 closePoint;
switch (collider1)
{
case MeshCollider meshCollider:
if (meshCollider.convex is false)
{
reportBuilder?.AppendLine("MeshCollider未勾选Convex");
continue;
}
case TerrainCollider terrainCollider:
{
closePoint = terrainCollider.ClosestPointOnBounds(samplePoint);
}
break;
case MeshCollider { convex: false } meshCollider:
{
var getClosestPointFromMesh =
new GetClosestPointFromMesh(meshCollider.sharedMesh,meshCollider.transform.InverseTransformPoint(samplePoint));
break;
if (getClosestPointFromMesh.TryGetValue(out var localPosition, out collider))
{
localPosition = meshCollider.transform.TransformPoint(localPosition);
Debug.DrawLine(Transform.position,localPosition,Color.magenta);
if (Vector3.Distance(localPosition, position) < Distance)
{
closePoint = localPosition;
}
else
{
continue;
}
}
else
{
continue;
}
}
break;
default:
closePoint = collider1.ClosestPoint(samplePoint);
break;
}
if(collider1.Raycast(new Ray(Transform.position,Transform.forward),out var raycastHit,64) is false)
{
continue;
}
if (MathV.IsForward(position, Transform.forward, closePoint) is false)
{
//DrawBasics.PointTag(closePoint,"not forward");
continue;
}
var bounds = collider1.bounds;
vector3 = collider1.ClosestPoint(Transform.position + Vector3.up * 64);
var top = bounds.center.y + bounds.extents.y;
Debug.DrawLine(Transform.position, Transform.position + Vector3.up * top, Color.blue, 8f);
if (Transform.position.y + MinHeight > top)
if (Transform.position.y + MinHeight > closePoint.y)
{
reportBuilder?.AppendLine("高度不足");
DrawBasics.PointTag(closePoint,"not enough height");
continue;
}
var nextPos = position;
nextPos.y = collider1.bounds.center.y;
{
var ray = new Ray(nextPos, Transform.forward);
if (collider1.Raycast(new Ray(nextPos, Transform.forward), out _, Distance) is false)
{
reportBuilder?.AppendLine("未检测到前方");
Debug.DrawRay(ray.origin,ray.direction,Color.red,Distance);
continue;
}
}
var height = Mathf.Abs(top - Transform.position.y);
var height = Mathf.Abs(closePoint.y - Transform.position.y);
if (height > MaxHeight)
{
reportBuilder?.AppendLine($"高度差距过大:{height}");
continue;
}
if (UnityEngine.Physics.Linecast(Transform.position, vector3, out var raycastHit2, LayerMask))
if (UnityEngine.Physics.Linecast(Transform.position, closePoint, out var raycastHit2, LayerMask))
{
if (raycastHit2.collider != collider1)
{
@@ -95,40 +126,61 @@ namespace BITKit.Physics
continue;
}
}
var length = UnityEngine.Physics.OverlapSphereNonAlloc(vector3, 0.01f, _colliders, LayerMask);
var length = UnityEngine.Physics.OverlapSphereNonAlloc(closePoint+Vector3.up*0.2f, 0.1f, _colliders, LayerMask);
switch (length)
{
case 1:
if (_colliders[0] != collider1)
{
reportBuilder.AppendLine($"检测到了其他碰撞体{_colliders[0].name}");
continue;
}
break;
case > 1:
case > 0:
reportBuilder.AppendLine("检测到了更多碰撞体");
for (var ii = 0; ii < length; ii++)
{
//Debug.DrawLine(vector3, _colliders[ii].ClosestPoint(vector3), Color.red, 8);
reportBuilder.AppendLine($"\t{_colliders[ii].name}");
}
continue;
}
vector3.y = top;
position = vector3;
collider = collider1;
reportBuilder.AppendLine("<color=green>成功</color>");
//BIT4Log.Log<GetClosePointFromCollider>(reportBuilder.ToString());
return true;
Debug.DrawLine(Transform.position,closePoint,Color.green);
validMeshColliders.Enqueue(new(collider1,closePoint));
}
var minDot = 64f;
Collider resultCollider = default;
Vector3 resultPosition=default;
while (validMeshColliders.TryDequeue(out var result))
{
var dot =Mathf.Abs(Vector3.Cross(Transform.forward, result.targetPosition-Transform.position).y);
DrawBasics.LineFrom(Transform.position,result.targetPosition-Transform.position,Color.red,text:dot.ToString(CultureInfo.InvariantCulture));
if(dot>minDot)continue;
resultCollider = result.collider;
resultPosition = result.targetPosition;
minDot = dot;
}
if (minDot < 64)
{
collider = resultCollider;
position = resultPosition;
return true;
}
collider = null;
//BIT4Log.Log<GetClosePointFromCollider>(reportBuilder.ToString());
return false;
}
private float ByTop(Collider arg)

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.Physics
{
[ExecuteAlways]
public class GetClosePointFromColliderDebugger : MonoBehaviour
{
private GetClosePointFromCollider _getClosePointFromCollider;
[SerializeField] private LayerMask layerMask;
private void OnEnable()
{
_getClosePointFromCollider = new GetClosePointFromCollider(transform);
}
private void Update()
{
_getClosePointFromCollider.LayerMask = layerMask;
if (_getClosePointFromCollider.TryGetValue(out Vector3 vector3, out var collider1))
{
var offset = Vector3.up * -0.01f;
Debug.DrawLine(transform.position+offset,vector3+offset,Color.cyan);
}
}
private void OnDrawGizmosSelected()
{
Gizmos.DrawWireSphere(transform.position,_getClosePointFromCollider.Distance);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: c9d7136f598a780459b866ee63545a5f
guid: e856e0f7ca7746740a416303f76dc89d
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,53 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace BITKit.Physics
{
public readonly struct GetClosestPointFromMesh:IClosePointProvider
{
private readonly Vector3 _position;
private readonly Mesh _mesh;
public GetClosestPointFromMesh(Mesh mesh, Vector3 position)
{
_mesh = mesh;
_position = position;
}
public bool TryGetValue(out Vector3 position, out Collider collider)
{
position = default;
collider = default;
if (_mesh.isReadable is false) return false;
var vertices = _mesh.vertices;
if (vertices.Length > 2048) return false;
var minPos = new Vector3(64, 64, 64);
for (var index = 0; index < _mesh.triangles.Length; index+=3)
{
var x = vertices[_mesh.triangles[index]];
var y = vertices[_mesh.triangles[index + 1]];
var z = vertices[_mesh.triangles[index + 2]];
var pos = GeometryUtils.GetPosInTriangle(x, y, z, _position);
if (Vector3.Distance(pos, _position) < Vector3.Distance(minPos, _position))
{
minPos = pos;
}
}
position = minPos;
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 979c184b4aaba6a439cbe74f0196267f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -27,7 +27,7 @@ namespace BITKit
AssetDatabase.LoadAssetAtPath<DictionaryReferenceScriptableObject>(
"Assets/Artists/Configs/reference_dictionary.asset");
#else
var task = YooAssets.LoadAssetAsync("reference_directory");
var task = YooAssets.LoadAssetAsync("reference_dictionary");
task.WaitForAsyncComplete();
_singleton=task.AssetObject as DictionaryReferenceScriptableObject;
#endif

View File

@@ -11,7 +11,7 @@ using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.Pool;
namespace BITFALL.Rig{
namespace BITKit.UX.Rig{
public class TickOverrideTranformService : MonoBehaviour
{
//[BurstCompile]

View File

@@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITFALL.Rig
namespace BITKit.UX.Rig
{
public class TickOverrideTransform : MonoBehaviour
{

View File

@@ -14,7 +14,7 @@ namespace BITKit
[Serializable]
public class IntervalTick:ITicker
{
[SerializeField] private float interval;
private float interval;
public ulong TickCount=>IntervalTickService.GetTickCount(interval);
public void Add(Action action)

View File

@@ -18,7 +18,9 @@
"GUID:517785bb4600a5140b47eac5fa49b8fc",
"GUID:838d3286f0973344ab6e99d3951012f7",
"GUID:a11ff146d38b27a44af87b4b4d9c4ecb",
"GUID:e4d11af1289097a4d9d8987f332a2ae8"
"GUID:e4d11af1289097a4d9d8987f332a2ae8",
"GUID:3abaaefa7af558d44ba20cea1c43d602",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 2e822c111559c204ba6fc483b283d193
guid: ea593ccd503f23741b162c0f5a00fb52
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -59,9 +59,10 @@ namespace BITKit.UX
GUILayout.BeginVertical();
//颜色更改为黑色
GUI.color = textColor;
GUILayout.Label(_logBuilder.ToString(),style);
GUILayout.Label(string.Join("\n",_logBuilder.ToString().Split("\n").Reverse()),style);
GUILayout.EndVertical();
GUILayout.EndArea();
}
}
}

View File

@@ -8,7 +8,8 @@
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:517785bb4600a5140b47eac5fa49b8fc"
"GUID:517785bb4600a5140b47eac5fa49b8fc",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -0,0 +1,115 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class OnScreenButton : OnScreenControl
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlFloatAttributeDescription m_PressedValueAttribute = new ()
{
name = "PressedValue",
defaultValue = 1f,
};
private readonly UxmlBoolAttributeDescription m_ReleasePressAttribute = new()
{
name = "ReleasePress"
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var table = (OnScreenButton)ve;
table.PressedValue = m_PressedValueAttribute.GetValueFromBag(bag, cc);
table.ReleasePress = m_ReleasePressAttribute.GetValueFromBag(bag, cc);
}
}
public new class UxmlFactory : UxmlFactory<OnScreenButton, UxmlTraits> { }
public float PressedValue { get; set; }= 1f;
public bool ReleasePress { get; set; }
private bool _isPressed;
private int _pointerId=-1;
private Label _label;
private readonly ValidHandle _isTriggered = new();
public OnScreenButton()
{
RegisterCallback<PointerUpEvent>(OnPointerUp);
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
this.AddManipulator(new Clickable(OnClick));
_label = this.Create<Label>();
_isTriggered.AddListener(x =>
{
if (x)
{
AddToClassList("selected");
}
else
{
RemoveFromClassList("selected");
}
var value = x?PressedValue : 0f;
SendValueToControl( value);
});
}
private void OnClick()
{
}
private void OnPointerMove(PointerMoveEvent evt)
{
if(_pointerId!=evt.pointerId)return;
if (ReleasePress)
{
_isPressed = true;
return;
}
if (_isPressed is false)
{
_isTriggered.AddElement(0);
_isPressed = true;
;
}
}
private void OnPointerDown(PointerDownEvent evt)
{
if(_pointerId is not -1)return;
_pointerId = evt.pointerId;
_isPressed = true;
_isTriggered.AddElement(0);
}
private void OnPointerUp(PointerUpEvent evt)
{
if(_pointerId!=evt.pointerId)return;
if (ReleasePress && _isPressed)
{
_isTriggered.AddElement(0);
_pointerId = -1;
return;
}
_pointerId = -1;
_isTriggered.RemoveElement(0);
}
protected override string ControlPathInternal { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8afc05714f63ce542a2e2ead78933966
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,330 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public abstract class OnScreenControl:VisualElement
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlStringAttributeDescription m_ControlPathAttribute = new ()
{
name = "ControlPath"
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var table = (OnScreenControl)ve;
table.ControlPath = m_ControlPathAttribute.GetValueFromBag(bag, cc);
}
}
protected OnScreenControl()
{
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
IsEnabled.AddDisableElements(this);
IsEnabled.AddListener(x =>
{
if (x)
OnEnable();
else OnDisable();
});
}
private void OnDetachFromPanel(DetachFromPanelEvent evt)
{
IsEnabled.AddDisableElements(32);
}
private void OnAttachToPanel(AttachToPanelEvent evt)
{
IsEnabled.RemoveDisableElements(32);
}
private void OnGeometryChanged(GeometryChangedEvent evt)
{
IsEnabled.SetDisableElements(this, !visible);
}
protected readonly ValidHandle IsEnabled = new();
/// <summary>
/// The control path (see <see cref="InputControlPath"/>) for the control that the on-screen
/// control will feed input into.
/// </summary>
/// <remarks>
/// A device will be created from the device layout referenced by the control path (see
/// <see cref="InputControlPath.TryGetDeviceLayout"/>). The path is then used to look up
/// <see cref="Control"/> on the device. The resulting control will be fed values from
/// the on-screen control.
///
/// Multiple on-screen controls sharing the same device layout will together create a single
/// virtual device. If, for example, one component uses <c>"&lt;Gamepad&gt;/buttonSouth"</c>
/// and another uses <c>"&lt;Gamepad&gt;/leftStick"</c> as the control path, a single
/// <see cref="Gamepad"/> will be created and the first component will feed data to
/// <see cref="Gamepad.buttonSouth"/> and the second component will feed data to
/// <see cref="Gamepad.leftStick"/>.
/// </remarks>
/// <seealso cref="InputControlPath"/>
public string ControlPath
{
get => ControlPathInternal;
set
{
IsEnabled.SetElements(1, string.IsNullOrEmpty(value) is false);
ControlPathInternal = value;
IsEnabled.AddDisableElements("Force");
IsEnabled.RemoveDisableElements("Force");
}
}
/// <summary>
/// The actual control that is fed input from the on-screen control.
/// </summary>
/// <remarks>
/// This is only valid while the on-screen control is enabled. Otherwise, it is <c>null</c>. Also,
/// if no <see cref="ControlPath"/> has been set, this will remain <c>null</c> even if the component is enabled.
/// </remarks>
public InputControl Control => _mControl;
private InputControl _mControl;
private OnScreenControl _mNextControlOnDevice;
private InputEventPtr _mInputEventPtr;
/// <summary>
/// Accessor for the <see cref="ControlPath"/> of the component. Must be implemented by subclasses.
/// </summary>
/// <remarks>
/// Moving the definition of how the control path is stored into subclasses allows them to
/// apply their own <see cref="InputControlAttribute"/> attributes to them and thus set their
/// own layout filters.
/// </remarks>
protected abstract string ControlPathInternal { get; set; }
private void SetupInputControl()
{
Debug.Assert(_mControl == null, "InputControl already initialized");
Debug.Assert(_mNextControlOnDevice == null, "Previous InputControl has not been properly uninitialized (m_NextControlOnDevice still set)");
Debug.Assert(!_mInputEventPtr.valid, "Previous InputControl has not been properly uninitialized (m_InputEventPtr still set)");
// Nothing to do if we don't have a control path.
var path = ControlPathInternal;
if (string.IsNullOrEmpty(path))
return;
// Determine what type of device to work with.
var layoutName = InputControlPath.TryGetDeviceLayout(path);
if (layoutName == null)
{
Debug.LogError(
$"Cannot determine device layout to use based on control path '{path}' used in {GetType().Name} component with {name}");
return;
}
// Try to find existing on-screen device that matches.
var internedLayoutName = new InternedString(layoutName);
var deviceInfoIndex = -1;
for (var i = 0; i < OnScreenDevices.Count; ++i)
{
////FIXME: this does not take things such as different device usages into account
if (OnScreenDevices[i].Device.layout != internedLayoutName) continue;
deviceInfoIndex = i;
break;
}
// If we don't have a matching one, create a new one.
InputDevice device;
if (deviceInfoIndex == -1)
{
// Try to create device.
try
{
device = InputSystem.AddDevice(layoutName);
}
catch (Exception exception)
{
Debug.LogError(
$"Could not create device with layout '{layoutName}' used in '{GetType().Name}' component");
Debug.LogException(exception);
return;
}
InputSystem.AddDeviceUsage(device, "OnScreen");
// Create event buffer.
var buffer = StateEvent.From(device, out var eventPtr, Allocator.Persistent);
// Add to list.
deviceInfoIndex = OnScreenDevices.Count;
OnScreenDevices.Add(new OnScreenDeviceInfo
{
EventPtr = eventPtr,
Buffer = buffer,
Device = device,
});
}
else
{
device = OnScreenDevices[deviceInfoIndex].Device;
}
// Try to find control on device.
_mControl = InputControlPath.TryFindControl(device, path);
if (_mControl == null)
{
Debug.LogError(
$"Cannot find control with path '{path}' on device of type '{layoutName}' referenced by component '{GetType().Name}' with {name}");
// Remove the device, if we just created one.
if (OnScreenDevices[deviceInfoIndex].FirstControl == null)
{
OnScreenDevices[deviceInfoIndex].Destroy();
OnScreenDevices.RemoveAt(deviceInfoIndex);
}
return;
}
_mInputEventPtr = OnScreenDevices[deviceInfoIndex].EventPtr;
// We have all we need. Permanently add us.
OnScreenDevices[deviceInfoIndex] =
OnScreenDevices[deviceInfoIndex].AddControl(this);
}
protected void SendValueToControl<TValue>(TValue value)
where TValue : struct
{
if (_mControl == null)
return;
if (!(_mControl is InputControl<TValue> control))
throw new ArgumentException(
$"The control path {ControlPath} yields a control of type {_mControl.GetType().Name} which is not an InputControl with value type {typeof(TValue).Name}", nameof(value));
////FIXME: this gives us a one-frame lag (use InputState.Change instead?)
_mInputEventPtr.time = InputState.currentTime;
control.WriteValueIntoEvent(value, _mInputEventPtr);
InputSystem.QueueEvent(_mInputEventPtr);
}
protected void SentDefaultValueToControl()
{
if (_mControl == null)
return;
////FIXME: this gives us a one-frame lag (use InputState.Change instead?)
_mInputEventPtr.time = InputState.currentTime;
_mControl.ResetToDefaultStateInEvent(_mInputEventPtr);
InputSystem.QueueEvent(_mInputEventPtr);
}
protected virtual void OnEnable()
{
SetupInputControl();
}
protected virtual void OnDisable()
{
if (_mControl == null)
return;
var device = _mControl.device;
for (var i = 0; i < OnScreenDevices.Count; ++i)
{
if (OnScreenDevices[i].Device != device)
continue;
var deviceInfo = OnScreenDevices[i].RemoveControl(this);
if (deviceInfo.FirstControl == null)
{
// We're the last on-screen control on this device. Remove the device.
OnScreenDevices[i].Destroy();
OnScreenDevices.RemoveAt(i);
}
else
{
OnScreenDevices[i] = deviceInfo;
// We're keeping the device , but we're disabling the on-screen representation
// for one of its controls. If the control isn't in default state, reset it
// to that now. This is what ensures that if, for example, OnScreenButton is
// disabled after OnPointerDown, we reset its button control to zero even
// though we will not see an OnPointerUp.
if (!_mControl.CheckStateIsAtDefault())
SentDefaultValueToControl();
}
_mControl = null;
_mInputEventPtr = new InputEventPtr();
Debug.Assert(_mNextControlOnDevice == null);
break;
}
}
private struct OnScreenDeviceInfo
{
public InputEventPtr EventPtr;
public NativeArray<byte> Buffer;
public InputDevice Device;
public OnScreenControl FirstControl;
public OnScreenDeviceInfo AddControl(OnScreenControl control)
{
control._mNextControlOnDevice = FirstControl;
FirstControl = control;
return this;
}
public OnScreenDeviceInfo RemoveControl(OnScreenControl control)
{
if (FirstControl == control)
FirstControl = control._mNextControlOnDevice;
else
{
for (OnScreenControl current = FirstControl._mNextControlOnDevice, previous = FirstControl;
current != null; previous = current, current = current._mNextControlOnDevice)
{
if (current != control)
continue;
previous._mNextControlOnDevice = current._mNextControlOnDevice;
break;
}
}
control._mNextControlOnDevice = null;
return this;
}
public void Destroy()
{
if (Buffer.IsCreated)
Buffer.Dispose();
if (Device != null)
InputSystem.RemoveDevice(Device);
Device = null;
Buffer = new NativeArray<byte>();
}
}
private static readonly List<OnScreenDeviceInfo> OnScreenDevices=new ();
internal string GetWarningMessage()
{
return $"{GetType()} needs to be attached as a child to a UI Canvas and have a RectTransform component to function properly.";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d6822f32ddff3b4f9fd962009912a81
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class OnScreenGamepad : OnScreenControl
{
public new class UxmlFactory : UxmlFactory<OnScreenGamepad, UxmlTraits> { }
protected override string ControlPathInternal { get; set; }
private readonly Dictionary<Vector2Int, VisualElement> _buttonMap = new();
public OnScreenGamepad()
{
IsEnabled.AddListener(OnActive);
for (var y = 1; y >=-1; y--)
{
var row = this.Create<VisualElement>();
row.style.flexGrow = 1;
row.style.flexDirection = FlexDirection.Row;
for (var x = -1; x <= 1; x++)
{
var dir = (x, y) switch
{
(-1, 1) => "NW", // 西北
(0, 1) => "N", // 北
(1, 1) => "NE", // 东北
(-1, 0) => "W", // 西
(0, 0) => "C", // 中心
(1, 0) => "E", // 东
(-1, -1) => "SW", // 西南
(0, -1) => "S", // 南
(1, -1) => "SE", // 东南
_ => "NULL" // 其他无效位置
};
var value = (x, y) switch
{
(-1, -1) => new float2(-0.707f, -0.707f), // NW 西北
(0, -1) =>new float2 (0f, -1f), // N 北
(1, -1) =>new float2 (0.707f, -0.707f), // NE 东北
(-1, 0) => new float2(-1f, 0f), // W 西
(0, 0) =>new float2 (0f, 0f), // C 中心
(1, 0) =>new float2 (1f, 0f), // E 东
(-1, 1) => new float2(-0.707f, 0.707f), // SW 西南
(0, 1) =>new float2 (0f, 1f), // S 南
(1, 1) => new float2(0.707f, 0.707f), // SE 东南
_ => new float2(0f, 0f) // 默认返回 (0, 0) 无效位置
};
var button = row.Create<VisualElement>();
_buttonMap.TryAdd(new Vector2Int(x, y), button);
button.style.flexGrow = 1;
button.AddToClassList("gamepad-button");
button.AddToClassList($"gamepad-button--{dir.ToLower()}");
button.AddManipulator(new Clickable(()=>{}));
button.RegisterCallback<PointerOverEvent>(_ =>
{
SendValueToControl((Vector2)value);
});
button.RegisterCallback<PointerUpEvent>(_ =>
{
SendValueToControl((Vector2)default);
});
// var label = button.Create<Label>();
// label.style.flexGrow = 1;
//
// label.text = $"{value.x},{value.y}";
}
}
}
private void OnActive(bool obj)
{
if (obj is false)
{
SendValueToControl((Vector2)default);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4bf58213910164e4d8cab38b29830a7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class OnScreenStick:OnScreenControl
{
public new class UxmlTraits : OnScreenControl.UxmlTraits
{
private readonly UxmlBoolAttributeDescription m_IsDelteaAttribute = new ()
{
name = "IsDelta"
};
private readonly UxmlFloatAttributeDescription m_MoveRangeAttribute = new()
{
name = "MoveRange",defaultValue = 32,
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var table = (OnScreenStick)ve;
table.IsDelta = m_IsDelteaAttribute.GetValueFromBag(bag, cc);
table.MoveRange=m_MoveRangeAttribute.GetValueFromBag(bag, cc);
}
}
public new class UxmlFactory : UxmlFactory<OnScreenStick, UxmlTraits> { }
public bool IsDelta { get; set; }
public float MoveRange { get; set; } = 32;
protected override string ControlPathInternal { get; set; }
private int _ignoreFrame=1;
private Vector2 _startPosition;
public OnScreenStick()
{
RegisterCallback<PointerDownEvent>(OnPointerDown);
RegisterCallback<PointerMoveEvent>(OnPointerMove);
RegisterCallback<PointerUpEvent>(OnPointerUp);
}
private void OnPointerUp(PointerUpEvent evt)
{
SendValueToControl(Vector2.zero);
_startPosition = evt.position;
_ignoreFrame = 1;
}
private void OnPointerDown(PointerDownEvent evt)
{
_ignoreFrame = 1;
_startPosition = evt.position;
}
private void OnPointerMove(PointerMoveEvent evt)
{
if (_ignoreFrame-- > 0)
{
return;
}
if (Vector2.Distance(evt.deltaPosition, default) > Vector2.Distance(_startPosition, evt.position))
{
return;
}
var pos = evt.deltaPosition;
if (IsDelta)
{
var newPos = evt.position;
pos = new Vector2(newPos.x, newPos.y) - _startPosition;
pos /= MoveRange;
}
SendValueToControl(new Vector2(pos.x, -pos.y));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e068da31183a7244198cbd23b00ebdbf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -11,9 +11,9 @@ namespace BITKit.UX
{
public UXInputAction(VisualElement visualElement,string controlPathInternal)
{
this.controlPathInternal = controlPathInternal;
this.ControlPathInternal = controlPathInternal;
}
protected sealed override string controlPathInternal { get; set; }
protected sealed override string ControlPathInternal { get; set; }
}
}

View File

@@ -0,0 +1,52 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class CellContainer : VisualElement
{
public new class UxmlFactory : UxmlFactory<CellContainer, UxmlTraits> { }
public int CellSize { get; set; } = 64;
public CellContainer()
{
RegisterCallback<CustomStyleResolvedEvent>(x =>
{
MarkDirtyRepaint();
});
generateVisualContent += GenerateVisualContent;
}
private void GenerateVisualContent(MeshGenerationContext obj)
{
if(contentRect.height<=0 || contentRect.width<=0)return;
var painter = obj.painter2D;
painter.lineWidth = resolvedStyle.borderTopWidth;
painter.strokeColor = resolvedStyle.borderTopColor;
var yCount = contentRect.height / CellSize;
var xCount = contentRect.width / CellSize;
for (var x = 1; x < xCount; x++)
{
painter.BeginPath();
painter.MoveTo(new Vector2(x * CellSize, 0));
painter.LineTo(new Vector2(x * CellSize, contentRect.height));
painter.Stroke();
}
for (var y = 1; y < yCount; y++)
{
painter.BeginPath();
painter.MoveTo(new Vector2(0, y * CellSize));
painter.LineTo(new Vector2(contentRect.width, y * CellSize));
painter.Stroke();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b5ba7b7494ea5d42b820dee5a7619c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
#if UNITY_5_3_OR_NEWER && UNITY_WINDOW
@@ -11,11 +12,13 @@ using AnotherFileBrowser.Windows;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
using Pointer = UnityEngine.InputSystem.Pointer;
namespace BITKit.UX
{
public class UXModService:UIToolKitPanel,IDisposable
public class UXModService:UIToolKitPanel
{
protected override string DocumentPath => "ux_mod_Service";
private const string TemplatePath = "ux_mod_service_template";
@@ -37,8 +40,7 @@ namespace BITKit.UX
private Label _modDescriptionLabel;
[UXBindPath("reload-mod-button",true)]
private Button _reloadModButton;
[UXBindPath("install-roslyn-fill")]
private VisualElement _installRoslynFill;
private readonly ConcurrentDictionary<string,VisualElement> _modContainers=new();
public UXModService(IUXService uxService) : base(uxService)
@@ -54,8 +56,6 @@ namespace BITKit.UX
private async UniTask InitializeAsync()
{
_installRoslynFill.style.width = 0;
_modTemplate =await ModService.LoadAsset<VisualTreeAsset>(TemplatePath);
UXUtils.Inject(this);
@@ -134,6 +134,7 @@ namespace BITKit.UX
private VisualElement Create(IMod mod)
{
var container =_modsContainer.Create(_modTemplate);
container.Get<Toggle>().SetValueWithoutNotify(ModService.Mods.Contains(mod));
container.Get<Toggle>().RegisterValueChangedCallback(evt =>
{
if (evt.newValue)
@@ -145,6 +146,7 @@ namespace BITKit.UX
ModService.UnLoad(mod);
}
});
container.Get<Label>().text = mod.Name +"@"+mod.PackageName;
container.tooltip = mod.Name+"\n"+mod.Description;
container.Get<Button>().clicked += () =>
@@ -155,8 +157,9 @@ namespace BITKit.UX
return container;
}
public void Dispose()
public override void Dispose()
{
base.Dispose();
ModService.OnModInstalled-=OnModInstalled;
ModService.OnModUnInstalled-=OnModUnInstalled;
ModService.OnModLoaded-=OnModLoaded;

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9d540311d69835747a74085c9e0aba6c
guid: 64e4049b9c8014d43bb7431cb56b6a09
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,111 @@
using System;
using System.Linq;
using BITKit.Mod;
using BITKit.UX.Hotkey;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UXRadialMenu : UIToolKitPanel
{
protected override string DocumentPath => "ui_radial_menu";
public override bool CloseWhenClickOutside => true;
public override bool AllowCursor => true;
private VisualTreeAsset _template;
[UXBindPath("radialMenu-container")]
private VisualElement _container;
[UXBindPath("info-label")]
private Label _infoLabel;
public IHotkeyCollection HotkeyCollection { get; set; }
public UXRadialMenu(IUXService uxService) : base(uxService)
{
OnInitiatedAsync += InitiatedAsync;
}
private async UniTask InitiatedAsync()
{
_container.Clear();
_template =await ModService.LoadAsset<VisualTreeAsset>("ui_radial_menu-template");
RootVisualElement.RegisterCallback<PointerDownEvent>(x =>
{
UXService.Return();
});
}
protected override void OnPanelEntry()
{
base.OnPanelEntry();
_infoLabel.text = "选择快速动作";
if (HotkeyCollection is null)
{
_infoLabel.text = "<color=yellow>没有快速动作</color>";
return;
}
var count = HotkeyCollection.Hotkeys.Count();
if (count is 0)
{
_infoLabel.text = "<color=yellow>目前没有快速动作</color>";
}
for (var i = 0; i < count; i++)
{
var hotkey = HotkeyCollection.Hotkeys.ElementAt(i);
var angle = 360 / count * i;
var pos = Quaternion.Euler(0, 0, angle) * Vector3.up * 384;
pos.y *= 0.64f;
var container = _container.Create<VisualElement>(_template.CloneTree);
var button = container.Get<Button>();
button.text = hotkey.Name;
button.focusable = false;
button.clickable = hotkey.HoldDuration is 0 ? new Clickable(OnClick) : null;
container.style.position = Position.Absolute;
container.transform.position = pos;
container.SetEnabled(hotkey.Enabled);
container.RegisterCallback<PointerOverEvent>(OnMouseOver);
continue;
void OnClick()
{
if (!hotkey.Enabled) return;
if (hotkey.OnPerform is not null)
{
try
{
hotkey.OnPerform();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
UXService.Return();
}
void OnMouseOver(PointerOverEvent evt)
{
_infoLabel.text = hotkey.Description;
}
}
}
protected override void OnPanelExit()
{
base.OnPanelExit();
_container.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 961781fb0b2f7004dbb5f7956301c4fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -40,7 +40,6 @@ namespace BITKit.UX
uxService.Register(this);
InitializeAsync().Forget();
}
private async UniTask InitializeAsync()
{
await _isBusy;
@@ -89,20 +88,20 @@ namespace BITKit.UX
UXUtils.Inject(this,RootVisualElement);
OnInitiated?.Invoke();
RootVisualElement.SetActive(false);
await OnInitiatedAsync.UniTaskFunc();
WaitUtilInitialized.TrySetResult();
RootVisualElement.RegisterCallback<TransitionRunEvent>(OnTransitionRun);
RootVisualElement.RegisterCallback<TransitionStartEvent>(OnTransitionStart);
RootVisualElement.RegisterCallback<TransitionStartEvent>(OnTransitionStart);
RootVisualElement.RegisterCallback<TransitionEndEvent>(OnTransitionEnd);
RootVisualElement.RegisterCallback<TransitionCancelEvent>(OnTransitionEnd);
WaitUtilTransitionCompleted.TrySetResult();
WaitUtilInitialized.TrySetResult();
OnInitiated?.Invoke();
}
}
@@ -127,6 +126,7 @@ namespace BITKit.UX
public virtual bool AllowReload { get; }
public virtual bool AllowCursor { get; }
public virtual bool AllowInput { get; }
public object Root => RootVisualElement;
public virtual string[] InitialUssClasses { get; } = Array.Empty<string>();
public bool IsDisposed { get; private set; }
@@ -152,6 +152,11 @@ namespace BITKit.UX
RootVisualElement.SetActive(true);
//await UniTask.NextFrame();
if (IsWindow)
{
RootVisualElement.BringToFront();
}
RootVisualElement.AddToClassList(USSEntry);
@@ -174,9 +179,19 @@ namespace BITKit.UX
{
BIT4Log.LogException(e);
}
await WaitUtilTransitionCompleted.Task;
try
{
var cts = new CancellationTokenSource();
cts.CancelAfter(1000);
await WaitUtilTransitionCompleted.Task.AttachExternalCancellation(cts.Token);
}
catch (OperationCanceledException)
{
}
await UniTask.SwitchToMainThread();
RootVisualElement.AddToClassList(USSEntered);
}
private void OnTransitionEnd<TChangeEvent>(TChangeEvent evt)
@@ -218,8 +233,17 @@ namespace BITKit.UX
await UniTask.NextFrame();
await OnExitAsync.UniTaskFunc();
try
{
var cts = new CancellationTokenSource();
cts.CancelAfter(1000);
await WaitUtilTransitionCompleted.Task.AttachExternalCancellation(cts.Token);
}
catch (OperationCanceledException)
{
}
await WaitUtilTransitionCompleted.Task;
}
void IEntryElement.Exited()
{
@@ -249,7 +273,7 @@ namespace BITKit.UX
{
}
public void Dispose()
public virtual void Dispose()
{
RootVisualElement?.RemoveFromHierarchy();
IsDisposed = true;

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UIToolkitSubPanel<T> :IUXPanel where T : IUXPanel
{
private readonly IServiceProvider _serviceProvider;
protected readonly T Panel;
protected readonly ValidHandle IsVisible=new();
private readonly ValidHandle _busy = new();
public UIToolkitSubPanel(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
Panel = serviceProvider.GetRequiredService<T>();
Panel.OnInitiated += OnInitiated;
Panel.OnInitiatedAsync += OnInitiatedAsync;
if (Panel is UIToolKitPanel uiToolKitPanel)
{
if (uiToolKitPanel.WaitUtilInitialized.Task.Status is UniTaskStatus.Succeeded)
{
OnInitiatedAsync().Forget();
OnInitiated();
}
}
Panel.OnEntry +=()=> IsVisible.RemoveDisableElements(this);
Panel.OnExit +=()=> IsVisible.AddDisableElements(this);
IsVisible.AddListener(IsVisibleCallback);
}
private async void IsVisibleCallback(bool x)
{
await _busy;
using var busy = _busy.GetHandle();
if (x)
{
OnEntry?.Invoke();
OnEntryAsync?.Invoke();
OnEntryCompleted?.Invoke();
}
else
{
OnExit?.Invoke();
OnExitAsync?.Invoke();
OnExitCompleted?.Invoke();
}
}
protected virtual UniTask OnInitiatedAsync()
{
UXUtils.Inject(this,Panel.Root as VisualElement);
return UniTask.CompletedTask;
}
protected virtual void OnInitiated()
{ UXUtils.Inject(this,Panel.Root as VisualElement);
}
bool IEntryElement.IsEntered
{
get => Panel.IsEntered;
set => Panel.IsEntered = value;
}
void IEntryElement.Entry()
{
Panel.Entry();
}
UniTask IEntryElement.EntryAsync()
{
return Panel.EntryAsync();
}
void IEntryElement.Entered()
{
Panel.Entered();
}
void IEntryElement.Exit()
{
Panel.Exit();
}
UniTask IEntryElement.ExitAsync()
{
return Panel.ExitAsync();
}
void IEntryElement.Exited()
{
Panel.Exited();
}
bool IUXPanel.IsWindow => Panel.IsWindow;
string IUXPanel.Index => Panel.Index;
bool IUXPanel.AllowCursor => Panel.AllowCursor;
bool IUXPanel.AllowInput => Panel.AllowInput;
object IUXPanel.Root => Panel.Root;
public event Action OnEntry;
public event Func<UniTask> OnEntryAsync;
public event Action OnEntryCompleted;
public event Action OnExit;
public event Func<UniTask> OnExitAsync;
public event Action OnExitCompleted;
event Action IUXPanel.OnInitiated
{
add => Panel.OnInitiated += value;
remove => Panel.OnInitiated -= value;
}
event Func<UniTask> IUXPanel.OnInitiatedAsync
{
add => Panel.OnInitiatedAsync += value;
remove => Panel.OnInitiatedAsync -= value;
}
void IUXPanel.OnTick(float deltaTime)
{
Panel.OnTick(deltaTime);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b6d7b7e48316f04f9130c5301091ee7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -5,7 +5,10 @@ using System.Threading;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
@@ -25,9 +28,26 @@ namespace BITKit.UX
_serviceProvider = serviceProvider;
_cancellationTokenSource = cancellationTokenSource;
_entryGroup.OnEntry += OnEntry;
_windowEntryGroup.OnEntry += OnWindowEntry;
_windowEntryGroup.OnExit += OnWindowExit;
_ticker.Add(OnTick);
}
private void OnWindowExit(IUXPanel obj)
{
BITAppForUnity.AllowCursor.RemoveElement(_windowEntryGroup);
if (obj.AllowInput is false)
BITInputSystem.AllowInput.RemoveDisableElements(_windowEntryGroup);
}
private void OnWindowEntry(IUXPanel obj)
{
BITAppForUnity.AllowCursor.SetElements(_windowEntryGroup, obj.AllowCursor);
if (obj.AllowInput is false)
BITInputSystem.AllowInput.AddDisableElements(_windowEntryGroup);
}
private readonly EntryGroup<IUXPanel> _entryGroup = new();
private readonly EntryGroup<IUXPanel> _windowEntryGroup = new();
/// <summary>
@@ -73,9 +93,9 @@ namespace BITKit.UX
public string SettingsPath { get; set; } = "ux_panel_settings";
public object Root { get; private set; }
public static VisualElement RootVisualElement { get; private set; }
public async UniTask InitializeAsync()
{
var gameObject = new GameObject("UXService");
Object.DontDestroyOnLoad(gameObject);
@@ -87,6 +107,11 @@ Object.Destroy(gameObject);
var document = gameObject.AddComponent<UIDocument>();
try
{
if (Touchscreen.current is not null && SettingsPath == "ux_panel_settings")
{
SettingsPath = "ux_panel_settings_mobile";
}
var panelSettings =await ModService.LoadAsset<PanelSettings>(SettingsPath);
document.panelSettings = panelSettings;
}
@@ -96,8 +121,12 @@ Object.Destroy(gameObject);
throw;
}
Root = document.rootVisualElement;
Root = RootVisualElement= document.rootVisualElement;
if (Touchscreen.current is not null)
{
RootVisualElement.AddToClassList("mobile");
}
}
public void Register(IUXPanel panel) => _registryQueue.Enqueue(panel);
@@ -115,6 +144,22 @@ Object.Destroy(gameObject);
public void Entry(string panelName) => _entryQueueByName.TryAdd(panelName);
public IUXPanel CurrentPanel => _currentPanel;
public event Action<IUXPanel, IUXPanel> OnPanelChanged;
public bool TryPick(float2 position, out object obj)
{
obj = null;
if (!EventSystem.current.IsPointerOverGameObject())
{
return false;
}
position.y = Screen.height - position.y;
var ve = RootVisualElement.panel.Pick(RuntimePanelUtils.ScreenToPanel(RootVisualElement.panel, position));
obj = ve;
return obj is not null;
}
public void Return()
{
@@ -143,6 +188,7 @@ Object.Destroy(gameObject);
{
try
{
while (_registryQueue.TryDequeue(out var result))
{
if (result is null) continue;
@@ -201,6 +247,13 @@ Object.Destroy(gameObject);
public async void Dispose()
{
foreach (var panelsValue in _panels.Values)
{
if (panelsValue is IDisposable disposable)
{
disposable.Dispose();
}
}
_ticker.Remove(OnTick);
await UniTask.SwitchToMainThread();
if (_currentPanel is not null)

View File

@@ -3,6 +3,8 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
#if UNITY_EDITOR
@@ -10,103 +12,106 @@ using Types = FullscreenEditor.Types;
#endif
namespace BITKit.UX.Settings
{
public class UXSettings : MonoBehaviour
public interface IUXSettings:IUXPanel{}
public class UXSettings<T,TValue> : UIToolkitSubPanel<T>,IUXSettings where T: IUXPanel
{
[SerializeField] private UIDocument document;
[UXBindPath("resolution-dropdown")]
private DropdownField _resolutionDropdown;
[UXBindPath("sensitivity-slider")]
private Slider _sensitivitySlider;
[UXBindPath("fullscreen-toggle")]
private Toggle _fullscreenToggle;
private void Start()
private readonly TValue _value;
private readonly IWrapper<TValue> _valueWrapper;
public UXSettings(IServiceProvider serviceProvider, IWrapper<TValue> valueWrapper) : base(serviceProvider)
{
UXUtils.Inject(this);
_resolutionDropdown.choices = Screen.resolutions.Select(x => x.ToString()).ToList();
_resolutionDropdown.SetValueWithoutNotify(Screen.currentResolution.ToString());
_resolutionDropdown.RegisterValueChangedCallback(x =>
{
Data.Set(Constant.Environment.mat_setvideomode, x.newValue);
});
_fullscreenToggle.SetValueWithoutNotify(Screen.fullScreen);
_fullscreenToggle.RegisterValueChangedCallback(x =>
{
Data.Set(Constant.Environment.fullscreen, x.newValue);
});
//_sensitivitySlider.SetValueWithoutNotify(PlayerConfig.Singleton.Sensitivity);
_sensitivitySlider.RegisterValueChangedCallback(x =>
{
Data.Set(Constant.Environment.sensitivity, x.newValue);
});
Data.AddListener<string>(Constant.Environment.mat_setvideomode, OnResolutionChanged);
Data.AddListener<bool>(Constant.Environment.fullscreen,OnFullScreenChanged);
Data.AddListener<float>(Constant.Environment.sensitivity,OnSensitivityChanged);
destroyCancellationToken.Register(() =>
{
Data.RemoveListender<string>(Constant.Environment.mat_setvideomode, OnResolutionChanged);
Data.RemoveListender<bool>(Constant.Environment.fullscreen,OnFullScreenChanged);
Data.RemoveListender<float>(Constant.Environment.sensitivity,OnSensitivityChanged);
PlayerPrefs.Save();
});
if(PlayerPrefs.HasKey(Constant.Environment.fullscreen))
Data.Set(Constant.Environment.fullscreen, PlayerPrefs.GetInt(Constant.Environment.fullscreen) == 1);
if(PlayerPrefs.HasKey(Constant.Environment.sensitivity))
Data.Set(Constant.Environment.sensitivity, PlayerPrefs.GetFloat(Constant.Environment.sensitivity));
_valueWrapper = valueWrapper;
_value = _valueWrapper.Value;
}
private async void OnSensitivityChanged(float obj)
{
await UniTask.SwitchToMainThread(destroyCancellationToken);
_sensitivitySlider.SetValueWithoutNotify(obj);
PlayerPrefs.SetFloat(Constant.Environment.sensitivity,obj);
}
private async void OnFullScreenChanged(bool obj)
{
await UniTask.SwitchToMainThread(destroyCancellationToken);
_fullscreenToggle.SetValueWithoutNotify(obj);
[UXBindPath("settings-container")]
private VisualElement _settingsContainer;
#if UNITY_EDITOR
if (obj)
private void Save()
{
_valueWrapper.Value = _value;
}
protected override void OnInitiated()
{
base.OnInitiated();
_settingsContainer.Clear();
foreach (var propertyInfo in typeof(TValue).GetProperties())
{
FullscreenEditor.Fullscreen.MakeFullscreen(Types.GameView);
}
else
{
foreach (var x in FullscreenEditor.Fullscreen.GetAllFullscreen())
var name = propertyInfo.GetDisplayName();
var value = propertyInfo.GetValue(_valueWrapper.Value);
switch (propertyInfo.PropertyType)
{
x.Close();
case var type when type == typeof(bool):
{
var checkBox = _settingsContainer.Create<Toggle>();
checkBox.label = name;
checkBox.SetValueWithoutNotify(value.As<bool>());
checkBox.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type == typeof(float):
{
var slider = _settingsContainer.Create<Slider>();
slider.label = name;
slider.showInputField = true;
slider.showMixedValue = true;
slider.lowValue = 1;
slider.highValue = 100;
slider.SetValueWithoutNotify(value.As<float>());
slider.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type == typeof(int):
{
var slider = _settingsContainer.Create<SliderInt>();
slider.label = name;
slider.showInputField = true;
slider.showMixedValue = true;
slider.lowValue = 1;
slider.highValue = 100;
slider.SetValueWithoutNotify(value.As<int>());
slider.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type == typeof(string):
{
var textField = _settingsContainer.Create<TextField>();
textField.label = name;
textField.SetValueWithoutNotify(value.As<string>());
textField.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
case var type when type.IsEnum:
{
var enumField = _settingsContainer.Create<EnumField>();
enumField.label = name;
enumField.Init(value as Enum);
enumField.SetValueWithoutNotify(value as Enum);
enumField.RegisterValueChangedCallback(x =>
{
propertyInfo.SetValue(_value,x.newValue);
Save();
});
}
break;
}
}
#else
Screen.fullScreen = obj;
#endif
PlayerPrefs.SetInt(Constant.Environment.fullscreen, obj ? 1 : 0);
}
private async void OnResolutionChanged(string newResolution)
{
await UniTask.SwitchToMainThread(destroyCancellationToken);
_resolutionDropdown.SetValueWithoutNotify(newResolution);
var resolution = Screen.resolutions.Single(x => x.ToString() == newResolution);
Screen.SetResolution(resolution.width,resolution.height,Data.Get<bool>(Constant.Environment.fullscreen));
}
}

View File

@@ -84,7 +84,18 @@ namespace BITKit.UX
return "";
}
var screenPosition = Mouse.current.position.ReadValue();
var screenPosition = Vector2.zero;
if (Mouse.current is not null)
{
screenPosition= Mouse.current.position.ReadValue();
}
if (Touchscreen.current is not null)
{
screenPosition = Touchscreen.current.position.ReadValue();
}
screenPosition.y = Screen.height - screenPosition.y;
VisualElement ve = panel.Pick(RuntimePanelUtils.ScreenToPanel(panel, screenPosition));

View File

@@ -204,8 +204,8 @@ namespace BITKit
} while (property.NextVisible(false));
foreach (var method in serializedObject.targetObject.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (method.GetCustomAttribute<BITAttribute>() is null) continue;
{
if (method.GetCustomAttribute<BITAttribute>() is null&& method.GetCustomAttribute<ContextMenu>() is null) continue;
if (method.GetParameters().Length is not 0) continue;
var button = new Button(() =>
{

View File

@@ -11,6 +11,8 @@ using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.Text;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace BITKit
{
public static partial class MathQ
@@ -34,6 +36,22 @@ namespace BITKit
}
public static partial class MathV
{
public static Matrix4x4 Rotate(Transform self, Vector3 center, Vector3 euler)
{
// 计算物体与中心点的相对位置
Vector3 relativePosition = self.position - center;
// 使用欧拉角来计算旋转矩阵
Quaternion rotation = Quaternion.Euler(euler);
// 计算旋转后的相对位置
Vector3 rotatedPosition = rotation * relativePosition;
var newPosition = center + rotatedPosition;
var newRotation = rotation * self.rotation;
return Matrix4x4.TRS(newPosition, newRotation, Vector3.one);
}
public static Vector3 CalculateTorque(Transform transform,Quaternion targetRotation)
{
var deltaRot = targetRotation * Quaternion.Inverse(transform.rotation);
@@ -157,6 +175,12 @@ namespace BITKit
return direction.sqrMagnitude < factor; */
return Vector3.Angle(vectorX, vectorY) < 8;
}
public static Vector2 TransientRotationAxis(Vector2 transientAxis)
{
transientAxis.x = TransientRotationAxis(transientAxis.x);
transientAxis.y = TransientRotationAxis(transientAxis.y);
return transientAxis;
}
public static Vector3 TransientRotationAxis(Vector3 transientAxis)
{
transientAxis.x = TransientRotationAxis(transientAxis.x);
@@ -218,18 +242,18 @@ namespace BITKit
}
public static float TransientRotationAxis(this float transientAxis)
{
return (transientAxis %= 360) switch
return (transientAxis %= 360f) switch
{
> 180 => transientAxis - 360,
< -180 => transientAxis + 360,
> 180f => transientAxis - 360f,
< -180f => transientAxis + 360f,
_ => transientAxis,
};
}
public static float WrapAngle(float angle)
{
angle %= 360;
if (angle > 180)
return angle - 360;
angle %= 360f;
if (angle > 180f)
return angle - 360f;
return angle;
}
@@ -455,16 +479,23 @@ namespace BITKit
self.TryGetValue(key, out value);
return value; ;
}
public static void DestoryChilds(this GameObject self)
public static void DestoryChilds(this GameObject self,bool immediately=false)
{
if (self)
if (!self) return;
var list = self.GetComponentsInChildren<Transform>().ToList();
list.TryRemove(self.transform);
foreach (var x in list)
{
var list = self.GetComponentsInChildren<Transform>().ToList();
list.TryRemove(self.transform);
list.ForEach(x =>
if (immediately)
{
GameObject.Destroy(x.gameObject);
});
Object.DestroyImmediate(x.gameObject);
}
else
{
Object.Destroy(x.gameObject);
}
}
}
public static bool Contains(this string self, IEnumerable<string> strs, out string key)

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2da90b0890b24e644a1900960dc2fb9c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,22 +0,0 @@
{
"name": "BITKit.WorldNode.Runtime",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:508392158bd966c4d9c21e19661a441d",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:1193c2664d97cc049a6e4c486c6bce71",
"GUID:d750d221812bb1d48baff92e6ef73e28"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 01cf845b40a364e4cbaee058936fa4a3
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,36 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.WorldNode
{
public sealed class UnityNode : MonoBehaviour,IWorldNode
{
[SerializeReference, SubclassSelector] private IWorldNodeService worldNodeService = new WorldNodeService();
[SerializeReference, SubclassSelector] private IWorldNode worldNode;
public int Id { get; set; }
public object WorldObject
{
get => gameObject;
set=>throw new InvalidOperationException("Cannot set WorldObject");
}
private void Start()
{
if (worldNode is null)
{
Debug.LogWarning("WorldNode is null");
return;
}
Id = gameObject.GetInstanceID();
worldNode.Id = Id;
worldNode.WorldObject = gameObject;
worldNodeService.RegisterNode(worldNode);
worldNode.Initialize();
Destroy(this);
}
}
}

View File

@@ -136,4 +136,3 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 300c66b95bcadb345ba713cc787c90de
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,16 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.GameSocket
{
public interface ISocketComponent
{
public string Name { get; }
public object Object { get; }
}
public interface ISocketService:IReadOnlyDictionary<string,ISocketComponent>
{
void Initialize();
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.GameSocket
{
public class UnitySocketComponent : MonoBehaviour,ISocketComponent
{
[SerializeReference,SubclassSelector] private IReference nameReference;
public string Name => nameReference.Value;
public object Object => transform;
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7ba6497a10fb8e14dab1acb39b2ec8aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,50 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using AYellowpaper.SerializedCollections;
using UnityEngine;
namespace BITKit.GameSocket
{
public class UnitySocketService : MonoBehaviour,ISocketService
{
private readonly Dictionary<string, ISocketComponent> _sockets=new();
public void Initialize()
{
_sockets.Clear();
foreach (var x in GetComponentsInChildren<ISocketComponent>())
{
_sockets.Add(x.Name, x);
}
}
public IEnumerator<KeyValuePair<string, ISocketComponent>> GetEnumerator()
{
return _sockets.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _sockets.GetEnumerator();
}
public int Count => _sockets.Count;
public bool ContainsKey(string key)
{
return _sockets.ContainsKey(key);
}
public bool TryGetValue(string key, out ISocketComponent value)
{
return _sockets.TryGetValue(key, out value);
}
public ISocketComponent this[string key] => _sockets[key];
public IEnumerable<string> Keys => _sockets.Keys;
public IEnumerable<ISocketComponent> Values => _sockets.Values;
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8284a08494909e147b79d71c7142ed12
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -526,3 +526,122 @@ TabContainer > * {
opacity: 0;
transition-duration: 0.1s;
}
OnScreenButton {
width: 80px;
height: 80px;
border-top-left-radius: 64px;
border-top-right-radius: 64px;
border-bottom-right-radius: 64px;
border-bottom-left-radius: 64px;
-unity-background-image-tint-color: rgb(255, 255, 255);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-color: rgb(255, 255, 255);
border-left-color: rgb(255, 255, 255);
border-right-color: rgb(255, 255, 255);
border-bottom-color: rgb(255, 255, 255);
}
OnScreenButton:hover {
background-color: rgba(200, 200, 200, 0.5);
-unity-background-image-tint-color: rgb(255, 255, 255);
}
OnScreenButton:active {
background-color: rgba(0, 0, 0, 0.5);
-unity-background-image-tint-color: rgb(255, 255, 255);
}
OnScreenButton.selected {
border-left-color: rgb(255, 0, 0);
border-right-color: rgb(255, 0, 0);
border-top-color: rgb(255, 0, 0);
border-bottom-color: rgb(255, 0, 0);
border-top-width: 4px;
border-right-width: 4px;
border-bottom-width: 4px;
border-left-width: 4px;
opacity: 0.5;
}
.gamepad-button {
border-left-color: rgb(255, 255, 255);
border-right-color: rgb(255, 255, 255);
border-top-color: rgb(255, 255, 255);
border-bottom-color: rgb(255, 255, 255);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
margin-top: 4px;
margin-right: 4px;
margin-bottom: 4px;
margin-left: 4px;
}
.gamepad-button:hover {
border-left-color: rgb(255, 255, 255);
border-right-color: rgb(255, 255, 255);
border-top-color: rgb(255, 255, 255);
border-bottom-color: rgb(255, 255, 255);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
background-color: rgba(255, 255, 255, 0.25);
}
.gamepad-button:active {
background-color: rgba(255, 255, 255, 0.5);
-unity-background-image-tint-color: rgba(0, 0, 0, 0.5);
border-left-color: rgba(0, 0, 0, 0.5);
border-right-color: rgba(0, 0, 0, 0.5);
border-top-color: rgba(0, 0, 0, 0.5);
border-bottom-color: rgba(0, 0, 0, 0.5);
}
.gamepad-button--nw {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-up-left-rounded.png?fileID=21300000&guid=e2a7dc01d1a854b429e7698649c340bd&type=3#icon_keyboard-arrow-up-left-rounded");
transform-origin: right bottom;
scale: 0.8 0.8;
}
.gamepad-button--n {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-up-rounded.png?fileID=21300000&guid=f75638302ccf8ba41acbf96be794e86d&type=3#icon_keyboard-arrow-up-rounded");
}
.gamepad-button--ne {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-up-right-rounded.png?fileID=21300000&guid=08e2edf7cdff08240a792ce62c67014d&type=3#icon_keyboard-arrow-up-right-rounded");
transform-origin: left bottom;
scale: 0.8 0.8;
}
.gamepad-button--w {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_chevron-left-rounded.png?fileID=21300000&guid=76da1f1cbb542714283e16db9aa8af7c&type=3#icon_chevron-left-rounded");
}
.gamepad-button--c {
}
.gamepad-button--e {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_chevron-right-rounded.png?fileID=21300000&guid=9e83d4b468bd75a47ab3d72155f367e5&type=3#icon_chevron-right-rounded");
}
.gamepad-button--sw {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-down-left-rounded.png?fileID=21300000&guid=93b327f2e8bb65f40a1b47d13ecfafcf&type=3#icon_keyboard-arrow-down-left-rounded");
transform-origin: right top;
scale: 0.8 0.8;
}
.gamepad-button--s {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/icon_keyboard-arrow-down.png?fileID=21300000&guid=6f14c0574452bf543b5f2f2cd2a24104&type=3#icon_keyboard-arrow-down");
}
.gamepad-button--se {
background-image: url("project://database/Assets/BITKit/Unity/Art/Icons/Arrows/icon_keyboard-arrow-down-right-rounded.png?fileID=21300000&guid=6103653931b39ca41a64af379712495c&type=3#icon_keyboard-arrow-down-right-rounded");
transform-origin: left top;
scale: 0.8 0.8;
}

View File

@@ -13,13 +13,9 @@ MonoBehaviour:
m_Name: Common_Mobile
m_EditorClassIdentifier:
themeUss: {fileID: -4733365628477956816, guid: ac803f0924e239645ac6b2fc9baebc65, type: 3}
m_DisableNoThemeWarning: 0
m_TargetTexture: {fileID: 0}
m_RenderMode: 0
m_WorldSpaceLayer: 0
m_ScaleMode: 2
m_ReferenceSpritePixelsPerUnit: 100
m_PixelsPerUnit: 100
m_Scale: 1
m_ReferenceDpi: 96
m_FallbackDpi: 96
@@ -28,11 +24,9 @@ MonoBehaviour:
m_Match: 1
m_SortingOrder: 0
m_TargetDisplay: 0
m_BindingLogLevel: 0
m_ClearDepthStencil: 1
m_ClearColor: 0
m_ColorClearValue: {r: 0, g: 0, b: 0, a: 0}
m_VertexBudget: 0
m_DynamicAtlasSettings:
m_MinAtlasSize: 8
m_MaxAtlasSize: 4096
@@ -41,6 +35,4 @@ MonoBehaviour:
m_AtlasBlitShader: {fileID: 9101, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeShader: {fileID: 9100, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeWorldShader: {fileID: 9102, guid: 0000000000000000f000000000000000, type: 0}
m_ICUDataAsset: {fileID: 0}
forceGammaRendering: 0
textSettings: {fileID: 0}

View File

@@ -12,8 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 19101, guid: 0000000000000000e000000000000000, type: 0}
m_Name: Common_PC_Overlay
m_EditorClassIdentifier:
themeUss: {fileID: -4733365628477956816, guid: ac803f0924e239645ac6b2fc9baebc65,
type: 3}
themeUss: {fileID: -4733365628477956816, guid: ac803f0924e239645ac6b2fc9baebc65, type: 3}
m_TargetTexture: {fileID: 0}
m_ScaleMode: 2
m_ReferenceSpritePixelsPerUnit: 100

View File

@@ -1,10 +0,0 @@
.rank {
}
.D1 .rank {
background-image: url("project://database/Assets/Artists/UX/USS_1/UI/UI_Settlement/%E7%AC%AC1%E5%90%8D.png?fileID=2800000&guid=24cbc98f27967534189bd6172d815874&type=3#第1名");
}
.D2 .rank {
background-image: url("project://database/Assets/Artists/UX/USS_1/UI/UI_Settlement/%E7%AC%AC2%E5%90%8D.png?fileID=2800000&guid=75f797e60a65f654cbfece5f451ec695&type=3#第2名");
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e7c7eabaaa5f13d4190e8ffb107fcaf6
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

View File

@@ -4,56 +4,51 @@
<Style src="project://database/Assets/BITKit/Unity/UX/UXModService.uss?fileID=7433441132597879392&amp;guid=4603eeaf39bae3448a93680711a15c42&amp;type=3#UXModService" />
<Style src="project://database/Assets/BITKit/Unity/UX/BITAlert.uss?fileID=7433441132597879392&amp;guid=8d0db0fee932f5342988f09217d6309a&amp;type=3#BITAlert" />
<ui:VisualElement class="root" style="background-color: rgba(0, 0, 0, 0.92);" />
<ui:VisualElement name="VisualElement" class="root --mod-service" style="flex-grow: 1;">
<ui:VisualElement class="flex-center">
<ui:VisualElement style="flex-direction: row;">
<ui:VisualElement style="flex-grow: 1;">
<ui:Label tabindex="-1" text="Mod列表" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" />
<ui:ScrollView>
<ui:VisualElement name="mods-container" style="width: 1024px; height: 768px;">
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
</ui:VisualElement>
</ui:ScrollView>
</ui:VisualElement>
<ui:VisualElement style="width: 384px; margin-left: 64px;">
<ui:Label tabindex="-1" text="Mod列表" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" />
<ui:VisualElement name="info-container" style="flex-grow: 1;">
<ui:VisualElement name="mod-image" style="height: 256px; background-color: rgba(0, 0, 0, 0); background-image: url(&quot;project://database/Assets/BITKit/Unity/Art/Images/Mod_Package.png?fileID=2800000&amp;guid=7ef0888883a3a7442989365c2dc57862&amp;type=3#Mod_Package&quot;); -unity-background-scale-mode: scale-and-crop;" />
<ui:VisualElement style="padding-top: 8px; padding-right: 0; padding-bottom: 8px; padding-left: 0; flex-grow: 1;">
<ui:Label tabindex="-1" text="加载一个Mod" parse-escape-sequences="true" display-tooltip-when-elided="true" name="mod-name-label" class="tl" />
<ui:VisualElement name="VisualElement" class="root --mod-service" style="margin-top: 32px; margin-right: 32px; margin-bottom: 32px; margin-left: 32px;">
<ui:VisualElement style="flex-direction: row; flex-grow: 1;">
<ui:VisualElement style="flex-grow: 1;">
<ui:Label tabindex="-1" text="Mod列表" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" />
<ui:ScrollView style="flex-grow: 1;">
<ui:VisualElement name="mods-container" style="flex-grow: 1;">
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
<ui:Instance template="ModTemplate" name="ModTemplate" />
</ui:VisualElement>
</ui:ScrollView>
</ui:VisualElement>
<ui:VisualElement style="width: 384px; margin-left: 64px;">
<ui:Label tabindex="-1" text="Mod列表" parse-escape-sequences="true" display-tooltip-when-elided="true" class="tl" />
<ui:VisualElement name="info-container" style="flex-grow: 1;">
<ui:VisualElement name="mod-image" style="height: 256px; background-color: rgba(0, 0, 0, 0); background-image: url(&quot;project://database/Assets/BITKit/Unity/Art/Images/Mod_Package.png?fileID=2800000&amp;guid=7ef0888883a3a7442989365c2dc57862&amp;type=3#Mod_Package&quot;); -unity-background-scale-mode: scale-and-crop;" />
<ui:VisualElement style="padding-top: 8px; padding-right: 0; padding-bottom: 8px; padding-left: 0; flex-grow: 1;">
<ui:Label tabindex="-1" text="加载一个Mod" parse-escape-sequences="true" display-tooltip-when-elided="true" name="mod-name-label" class="tl" />
<ui:ScrollView style="flex-grow: 1;">
<ui:Label tabindex="-1" text="通过Windows Explorer选择Mod后,需要等待一段时间才能加载,这是因为explorer内部的延迟&#10;&#10;可以直接加载编译好的dll&#10;加载.cs需要安装MonoBleedingEdge&#10;&#10;已加载的脚本mod无法动态完成卸载,可能需要重启应用&#10;&#10;动态加载的Mod不会保存,如需要每次启动都自动加载,需要将Mod放置在/Mods/{ModName}/文件夹中,并创建PackageInfo.json描述文件" parse-escape-sequences="true" display-tooltip-when-elided="true" name="mod-description-label" style="white-space: normal;" />
<ui:VisualElement style="flex-grow: 1;" />
<ui:Button text="打开开发手册" parse-escape-sequences="true" display-tooltip-when-elided="true" name="open-manual-button" />
<ui:Button text="打开Mod目录" parse-escape-sequences="true" display-tooltip-when-elided="true" name="open_folder-button" />
</ui:VisualElement>
</ui:ScrollView>
<ui:VisualElement style="flex-grow: 1;" />
<ui:Button text="打开开发手册" parse-escape-sequences="true" display-tooltip-when-elided="true" name="open-manual-button" />
<ui:Button text="打开Mod目录" parse-escape-sequences="true" display-tooltip-when-elided="true" name="open_folder-button" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement class="theme-dark alert-container" style="flex-grow: 1; flex-direction: row; margin-top: 16px;">
<ui:Button text="返回" parse-escape-sequences="true" display-tooltip-when-elided="true" name="return-button" />
<ui:VisualElement style="flex-grow: 1; align-items: stretch; flex-direction: row; justify-content: flex-end;">
<ui:Label tabindex="-1" text="安装Roslyn" parse-escape-sequences="true" display-tooltip-when-elided="true" style="-unity-text-align: middle-left;" />
<ui:Button text="安装" parse-escape-sequences="true" display-tooltip-when-elided="true" name="install-roslyn-button">
<ui:VisualElement name="install-roslyn-fill" class="root" style="background-color: rgba(255, 255, 255, 0.5); width: 49%;" />
</ui:Button>
<ui:Button text="卸载" parse-escape-sequences="true" display-tooltip-when-elided="true" name="uninstall-roslyn-button" />
<ui:VisualElement style="width: 384px; align-items: flex-end; flex-direction: row; justify-content: flex-end;">
<ui:Button text="重载Mod" parse-escape-sequences="true" display-tooltip-when-elided="true" name="reload-mod-button" />
<ui:Button text="加载Mod" parse-escape-sequences="true" display-tooltip-when-elided="true" name="open-mod-button" />
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement class="theme-dark alert-container" style="margin-top: 16px; flex-direction: row;">
<ui:Button text="返回" parse-escape-sequences="true" display-tooltip-when-elided="true" name="return-button" />
<ui:VisualElement style="flex-grow: 1; align-items: stretch; flex-direction: row; justify-content: flex-end;">
<ui:VisualElement style="width: 384px; align-items: flex-end; flex-direction: row; justify-content: flex-end;">
<ui:Button text="重载Mod" parse-escape-sequences="true" display-tooltip-when-elided="true" name="reload-mod-button" />
<ui:Button text="加载Mod" parse-escape-sequences="true" display-tooltip-when-elided="true" name="open-mod-button" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>

View File

@@ -1,14 +1,14 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<Style src="project://database/Assets/BITKit/Unity/UX/Common/Common.uss?fileID=7433441132597879392&amp;guid=a3a69d3518fd02b489e721f3c5b0b539&amp;type=3#Common" />
<Style src="project://database/Assets/BITKit/Unity/UX/UXModService.uss?fileID=7433441132597879392&amp;guid=4603eeaf39bae3448a93680711a15c42&amp;type=3#UXModService" />
<ui:Button parse-escape-sequences="true" display-tooltip-when-elided="true" name="main-button" class="mod-element" style="flex-direction: row; margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; height: 48px;">
<ui:Toggle name="context-toggle" style="margin-right: 8px; margin-left: 8px;" />
<ui:Button parse-escape-sequences="true" display-tooltip-when-elided="true" name="Button--0" class="mod-element" style="flex-direction: row; margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; height: 48px;">
<ui:Toggle name="Toggle--0" style="margin-right: 8px; margin-left: 8px;" />
<ui:VisualElement picking-mode="Ignore" style="flex-direction: row; flex-grow: 1;">
<ui:VisualElement picking-mode="Ignore" style="width: 48px; height: 48px;">
<ui:VisualElement name="icon-image" picking-mode="Ignore" style="flex-grow: 1; background-image: url(&apos;project://database/Assets/BITKit/Unity/Art/Icons/EditorIcons/processed/unityengine/d_GameObject%20Icon.png?fileID=2800000&amp;guid=e45835505036acc4a92dbbf8fb2d4848&amp;type=3#d_GameObject Icon&apos;); margin-top: 8px; margin-right: 8px; margin-bottom: 8px; margin-left: 8px;" />
<ui:VisualElement name="VisualElement--0" picking-mode="Ignore" style="flex-grow: 1; background-image: url(&quot;project://database/Assets/BITKit/Unity/Art/Editor_Icons/processed/unityengine/d_GameObject%20Icon.png?fileID=2800000&amp;guid=e45835505036acc4a92dbbf8fb2d4848&amp;type=3#d_GameObject Icon&quot;); margin-top: 8px; margin-right: 8px; margin-bottom: 8px; margin-left: 8px;" />
</ui:VisualElement>
<ui:VisualElement picking-mode="Ignore" style="flex-grow: 1; flex-direction: row; align-items: stretch; margin-left: 8px;">
<ui:Label tabindex="-1" text="Mod名称" parse-escape-sequences="true" display-tooltip-when-elided="true" name="title-label" picking-mode="Ignore" style="margin-top: 0; margin-bottom: 0; -unity-text-align: middle-left;" />
<ui:Label tabindex="-1" text="Mod名称" parse-escape-sequences="true" display-tooltip-when-elided="true" name="Label--0" picking-mode="Ignore" style="margin-top: 0; margin-bottom: 0; -unity-text-align: middle-left;" />
<ui:Label tabindex="-1" text="描述" parse-escape-sequences="true" display-tooltip-when-elided="true" name="description-label" picking-mode="Ignore" style="margin-top: 0; margin-bottom: 0; -unity-text-align: lower-left; display: none;" />
</ui:VisualElement>
</ui:VisualElement>