diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
index 428dea933..436850e80 100644
--- a/.github/workflows/cd.yml
+++ b/.github/workflows/cd.yml
@@ -1,12 +1,13 @@
name: Continuous Deployment
on:
schedule:
- - cron: '15 4 * * *'
+ - cron: '15 6 * * *'
workflow_dispatch:
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: false
+ GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60'
jobs:
@@ -20,7 +21,7 @@ jobs:
- name: Checkout LFS
run: |
git lfs version
- git lfs pull
+ git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
@@ -35,12 +36,12 @@ jobs:
run: |
.\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: Windows-Editor
path: Output/Editor.zip
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: Windows-EditorDebugSymbols
path: Output/EditorDebugSymbols.zip
@@ -53,7 +54,7 @@ jobs:
- name: Checkout LFS
run: |
git lfs version
- git lfs pull
+ git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
@@ -68,7 +69,7 @@ jobs:
run: |
.\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: Windows-Game
path: Output/Windows.zip
@@ -83,7 +84,7 @@ jobs:
- name: Checkout LFS
run: |
git lfs version
- git lfs pull
+ git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
@@ -101,7 +102,7 @@ jobs:
run: |
./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: Linux-Editor
path: Output/FlaxEditorLinux.zip
@@ -114,7 +115,7 @@ jobs:
- name: Checkout LFS
run: |
git lfs version
- git lfs pull
+ git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
@@ -132,7 +133,7 @@ jobs:
run: |
./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: Linux-Game
path: Output/Linux.zip
@@ -147,7 +148,7 @@ jobs:
- name: Checkout LFS
run: |
git lfs version
- git lfs pull
+ git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
@@ -162,7 +163,7 @@ jobs:
run: |
./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: Mac-Editor
path: Output/FlaxEditorMac.zip
@@ -175,7 +176,7 @@ jobs:
- name: Checkout LFS
run: |
git lfs version
- git lfs pull
+ git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
@@ -190,7 +191,7 @@ jobs:
run: |
./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: Mac-Game
path: Output/Mac.zip
diff --git a/Content/Editor/Scripting/GamePluginTemplate.cs b/Content/Editor/Scripting/GamePluginTemplate.cs
new file mode 100644
index 000000000..d110fa68d
--- /dev/null
+++ b/Content/Editor/Scripting/GamePluginTemplate.cs
@@ -0,0 +1,25 @@
+%copyright%using System;
+using System.Collections.Generic;
+using FlaxEngine;
+
+namespace %namespace%;
+
+///
+/// %class% GamePlugin.
+///
+public class %class% : GamePlugin
+{
+ ///
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ }
+
+ ///
+ public override void Deinitialize()
+ {
+ base.Deinitialize();
+
+ }
+}
\ No newline at end of file
diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax
index 355158581..2f13023ee 100644
--- a/Content/Engine/DefaultTerrainMaterial.flax
+++ b/Content/Engine/DefaultTerrainMaterial.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cce61627c31b12d2d79abf59c485889044b4b9a803c0d2b7a988dc25906d6a35
-size 30264
+oid sha256:c03a9676ce8236d2770e64fb4ec8e039aa5cd3609fd32d35d802f5a1c35c798b
+size 23658
diff --git a/Content/Shaders/Editor/Grid.flax b/Content/Shaders/Editor/Grid.flax
index 1c87a1332..440d37512 100644
--- a/Content/Shaders/Editor/Grid.flax
+++ b/Content/Shaders/Editor/Grid.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ce516159bb427e1c2cbd48f86be17b2b3c065d2ecd6943aace05451853d6b3e1
-size 4626
+oid sha256:9aa0cb389296afe4ee474395f188e61f8a1e428aac1f09fc7d54825b1d6e58df
+size 4744
diff --git a/README.md b/README.md
index 5e1374e02..c4d6e7298 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ Follow the instructions below to compile and run the engine from source.
* Install Visual Studio 2022 or newer
* Install Windows 8.1 SDK or newer (via Visual Studio Installer)
* Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer)
-* Install .NET 8 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
+* Install .NET 8 or 9 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
* Install Git with LFS
* Clone repo (with LFS)
* Run **GenerateProjectFiles.bat**
@@ -44,8 +44,9 @@ Follow the instructions below to compile and run the engine from source.
## Linux
* Install Visual Studio Code
-* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
+* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
* Ubuntu: `sudo apt install dotnet-sdk-8.0`
+ * Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
* Ubuntu: `sudo apt install vulkan-sdk`
* Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers`
@@ -67,12 +68,12 @@ Follow the instructions below to compile and run the engine from source.
## Mac
* Install XCode
-* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
+* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
* Clone repo (with LFS)
* Run `GenerateProjectFiles.command`
* Open workspace with XCode or Visual Studio Code
-* Build and run (configuration `Editor.Mac.Development`)
+* Build and run (configuration `Editor.Mac.Development`)
#### Troubleshooting
diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs
index 18c5b6d7b..77e34dc70 100644
--- a/Source/Editor/Content/Items/ContentItem.cs
+++ b/Source/Editor/Content/Items/ContentItem.cs
@@ -750,7 +750,8 @@ namespace FlaxEditor.Content
// Draw short name
Render2D.PushClip(ref textRect);
- Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f);
+ var scale = 0.95f * view.ViewScale;
+ Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, scale);
Render2D.PopClip();
if (IsBeingCut)
diff --git a/Source/Editor/Content/Proxy/CSharpProxy.cs b/Source/Editor/Content/Proxy/CSharpProxy.cs
index 8abdf8b30..f0a7997c7 100644
--- a/Source/Editor/Content/Proxy/CSharpProxy.cs
+++ b/Source/Editor/Content/Proxy/CSharpProxy.cs
@@ -106,6 +106,23 @@ namespace FlaxEditor.Content
}
}
+ ///
+ /// Context proxy object for C# GamePlugin files.
+ ///
+ ///
+ [ContentContextMenu("New/C#/C# GamePlugin")]
+ public class CSharpGamePluginProxy : CSharpProxy
+ {
+ ///
+ public override string Name => "C# GamePlugin";
+
+ ///
+ protected override void GetTemplatePath(out string path)
+ {
+ path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/GamePluginTemplate.cs");
+ }
+ }
+
///
/// Context proxy object for empty C# files.
///
diff --git a/Source/Editor/Content/Proxy/JsonAssetProxy.cs b/Source/Editor/Content/Proxy/JsonAssetProxy.cs
index 27d0e2347..dfdec8330 100644
--- a/Source/Editor/Content/Proxy/JsonAssetProxy.cs
+++ b/Source/Editor/Content/Proxy/JsonAssetProxy.cs
@@ -1,9 +1,11 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
+using System.Linq;
using FlaxEditor.Content.Create;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
+using FlaxEditor.Scripting;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
using FlaxEngine;
@@ -84,18 +86,67 @@ namespace FlaxEditor.Content
if (_element != null)
{
- // Define the rule for the types that can be used to create a json data asset
- _element.CustomControl.CheckValid += type =>
- type.Type != null &&
- type.IsClass &&
- type.Type.IsVisible &&
- !type.IsAbstract &&
- !type.IsGenericType &&
- type.Type.GetConstructor(Type.EmptyTypes) != null &&
- !typeof(FlaxEngine.GUI.Control).IsAssignableFrom(type.Type) &&
- !typeof(FlaxEngine.Object).IsAssignableFrom(type.Type);
+ _element.CustomControl.CheckValid += OnCheckValidJsonAssetType;
}
}
+
+ private static Type[] BlacklistedClasses =
+ [
+ typeof(System.Attribute),
+ typeof(FlaxEngine.Object),
+ typeof(FlaxEngine.GUI.Control),
+ ];
+
+ private static Type[] BlacklistedStructs =
+ [
+ typeof(Float2),
+ typeof(Float3),
+ typeof(Float4),
+ typeof(Double2),
+ typeof(Double3),
+ typeof(Double4),
+ typeof(Vector2),
+ typeof(Vector3),
+ typeof(Vector4),
+ typeof(Half2),
+ typeof(Half3),
+ typeof(Half4),
+ typeof(Int2),
+ typeof(Int3),
+ typeof(Int4),
+ typeof(Transform),
+ typeof(Quaternion),
+ typeof(BoundingBox),
+ typeof(BoundingSphere),
+ typeof(BoundingFrustum),
+ typeof(Ray),
+ typeof(Plane),
+ typeof(Matrix),
+ typeof(Color),
+ typeof(Color32),
+ typeof(FloatR11G11B10),
+ typeof(FloatR10G10B10A2),
+ typeof(FlaxEngine.Half),
+ ];
+
+ private static bool OnCheckValidJsonAssetType(ScriptType type)
+ {
+ // Define the rule for the types that can be used to create a json data asset
+ var mType = type.Type;
+ if (mType == null ||
+ type.IsAbstract ||
+ type.IsStatic ||
+ type.IsGenericType ||
+ !mType.IsVisible)
+ return false;
+ if (type.IsClass)
+ return mType.GetConstructor(Type.EmptyTypes) != null && BlacklistedClasses.FirstOrDefault(x => x.IsAssignableFrom(mType)) == null;
+ if (type.IsStructure)
+ return !type.IsPrimitive &&
+ !type.IsVoid &&
+ !BlacklistedStructs.Contains(mType);
+ return false;
+ }
}
}
@@ -175,7 +226,7 @@ namespace FlaxEditor.Content
{
_thumbnail = SpriteHandle.Invalid;
}
-
+
///
/// Constructor with overriden thumbnail.
///
@@ -196,7 +247,7 @@ namespace FlaxEditor.Content
{
Editor.SaveJsonAsset(outputPath, new T());
}
-
+
///
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
{
diff --git a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
index b3d7ffa49..524bebfc3 100644
--- a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
+++ b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
@@ -496,7 +496,7 @@ namespace FlaxEditor.Content.Thumbnails
// Prepare requests
bool isAnyReady = false;
int checks = Mathf.Min(10, _requests.Count);
- for (int i = 0; i < checks; i++)
+ for (int i = 0; i < checks && i < _requests.Count; i++)
{
var request = _requests[i];
try
diff --git a/Source/Editor/Cooker/GameCooker.cpp b/Source/Editor/Cooker/GameCooker.cpp
index 17392adee..36f2da11c 100644
--- a/Source/Editor/Cooker/GameCooker.cpp
+++ b/Source/Editor/Cooker/GameCooker.cpp
@@ -671,11 +671,14 @@ bool GameCookerImpl::Build()
MCore::Thread::Attach();
// Build Started
- CallEvent(GameCooker::EventType::BuildStarted);
- data.Tools->OnBuildStarted(data);
- for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
- Steps[stepIndex]->OnBuildStarted(data);
- data.InitProgress(Steps.Count());
+ if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
+ {
+ CallEvent(GameCooker::EventType::BuildStarted);
+ data.Tools->OnBuildStarted(data);
+ for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
+ Steps[stepIndex]->OnBuildStarted(data);
+ data.InitProgress(Steps.Count());
+ }
// Execute all steps in a sequence
bool failed = false;
@@ -741,10 +744,13 @@ bool GameCookerImpl::Build()
}
IsRunning = false;
CancelFlag = 0;
- for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
- Steps[stepIndex]->OnBuildEnded(data, failed);
- data.Tools->OnBuildEnded(data, failed);
- CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
+ if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
+ {
+ for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
+ Steps[stepIndex]->OnBuildEnded(data, failed);
+ data.Tools->OnBuildEnded(data, failed);
+ CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
+ }
Delete(Data);
Data = nullptr;
diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp
index eb5469b47..9e4f18b2c 100644
--- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp
@@ -364,6 +364,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
}
#endif
const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release;
+
+ if (platformSettings->BuildAAB)
+ {
+ // .aab
+ {
+ CreateProcessSettings procSettings;
+ procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT(":app:bundle") : TEXT(":app:bundleDebug"));
+ procSettings.WorkingDirectory = data.OriginalOutputPath;
+ const int32 result = Platform::CreateProcess(procSettings);
+ if (result != 0)
+ {
+ data.Error(String::Format(TEXT("Failed to build Gradle project into .aab package (result code: {0}). See log for more info."), result));
+ return true;
+ }
+ }
+ // Copy result package
+ const String aab = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/bundle/release/app-release.aab") : TEXT("app/build/outputs/bundle/debug/app-debug.aab"));
+ const String outputAab = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".aab");
+ if (FileSystem::CopyFile(outputAab, aab))
+ {
+ LOG(Error, "Failed to copy .aab package from {0} to {1}", aab, outputAab);
+ return true;
+ }
+ LOG(Info, "Output Android AAB application package: {0} (size: {1} MB)", outputAab, FileSystem::GetFileSize(outputAab) / 1024 / 1024);
+ }
+
+ // .apk
{
CreateProcessSettings procSettings;
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
@@ -371,20 +398,20 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
const int32 result = Platform::CreateProcess(procSettings);
if (result != 0)
{
- data.Error(String::Format(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result));
+ data.Error(String::Format(TEXT("Failed to build Gradle project into .apk package (result code: {0}). See log for more info."), result));
return true;
}
}
-
// Copy result package
const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk"));
const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk");
if (FileSystem::CopyFile(outputApk, apk))
{
- LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk);
+ LOG(Error, "Failed to copy .apk package from {0} to {1}", apk, outputApk);
return true;
}
- LOG(Info, "Output Android application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024);
+ LOG(Info, "Output Android APK application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024);
+
return false;
}
diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
index 6b1a6829e..489321d99 100644
--- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp
+++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
@@ -214,6 +214,33 @@ bool DeployDataStep::Perform(CookingData& data)
FileSystem::NormalizePath(srcDotnet);
LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet);
+ // Get major Version
+ Array pathParts;
+ srcDotnet.Split('/', pathParts);
+ String version;
+ for (int i = 0; i < pathParts.Count(); i++)
+ {
+ if (pathParts[i] == TEXT("runtimes"))
+ {
+ Array versionParts;
+ pathParts[i - 1].Split('.', versionParts);
+ if (!versionParts.IsEmpty())
+ {
+ const String majorVersion = versionParts[0].TrimTrailing();
+ int32 versionNum;
+ StringUtils::Parse(*majorVersion, majorVersion.Length(), &versionNum);
+ if (Math::IsInRange(versionNum, GAME_BUILD_DOTNET_RUNTIME_MIN_VER, GAME_BUILD_DOTNET_RUNTIME_MAX_VER)) // Check for major part
+ version = majorVersion;
+ }
+ }
+ }
+
+ if (version.IsEmpty())
+ {
+ data.Error(TEXT("Failed to find supported .NET version for the current host platform."));
+ return true;
+ }
+
// Deploy runtime files
const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll");
const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms"));
@@ -226,14 +253,14 @@ bool DeployDataStep::Perform(CookingData& data)
{
// AOT runtime files inside Engine Platform folder
packFolder /= TEXT("Dotnet");
- dstDotnetLibs /= TEXT("lib/net8.0");
- srcDotnetLibs = packFolder / TEXT("lib/net8.0");
+ dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
+ srcDotnetLibs = packFolder / String::Format(TEXT("lib/net{}.0"), version);
}
else
{
// Runtime files inside Dotnet SDK folder but placed for AOT
- dstDotnetLibs /= TEXT("lib/net8.0");
- srcDotnetLibs /= TEXT("../lib/net8.0");
+ dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
+ srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version);
}
}
else
@@ -241,14 +268,14 @@ bool DeployDataStep::Perform(CookingData& data)
if (srcDotnetFromEngine)
{
// Runtime files inside Engine Platform folder
- dstDotnetLibs /= TEXT("lib/net8.0");
- srcDotnetLibs /= TEXT("lib/net8.0");
+ dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
+ srcDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
}
else
{
// Runtime files inside Dotnet SDK folder
dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App");
- srcDotnetLibs /= TEXT("../lib/net8.0");
+ srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version);
}
}
LOG(Info, "Copying .NET files from {} to {}", packFolder, dstDotnet);
@@ -273,6 +300,7 @@ bool DeployDataStep::Perform(CookingData& data)
DEPLOY_NATIVE_FILE("libmonosgen-2.0.so");
DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Native.so");
+ DEPLOY_NATIVE_FILE("libSystem.Globalization.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Android.so");
break;
case BuildPlatform::iOSARM64:
diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs
index 8c3810c3c..79c00b38a 100644
--- a/Source/Editor/CustomEditors/CustomEditor.cs
+++ b/Source/Editor/CustomEditors/CustomEditor.cs
@@ -883,7 +883,7 @@ namespace FlaxEditor.CustomEditors
///
protected virtual void ClearToken()
{
- ParentEditor.ClearToken();
+ ParentEditor?.ClearToken();
}
}
}
diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
index 27e2849bd..d1cdd8780 100644
--- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
@@ -9,6 +9,7 @@ using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tree;
+using FlaxEditor.Modules;
using FlaxEditor.Scripting;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
@@ -67,12 +68,16 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Use default prefab instance as a reference for the editor
Values.SetReferenceValue(prefabInstance);
- if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter)
+ // Display prefab UI (when displaying object inside Prefab Window then display only nested prefabs)
+ var prefabId = prefab.ID;
+ Editor.GetPrefabNestedObject(ref prefabId, ref prefabObjectId, out var nestedPrefabId, out var nestedPrefabObjectId);
+ var nestedPrefab = FlaxEngine.Content.Load(nestedPrefabId);
+ var panel = layout.CustomContainer();
+ panel.CustomControl.Height = 20.0f;
+ panel.CustomControl.SlotsVertically = 1;
+ if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter || nestedPrefab)
{
- // Add some UI
- var panel = layout.CustomContainer();
- panel.CustomControl.Height = 20.0f;
- panel.CustomControl.SlotsVertically = 1;
+ var targetPrefab = nestedPrefab ?? prefab;
panel.CustomControl.SlotsHorizontally = 3;
// Selecting actor prefab asset
@@ -80,22 +85,21 @@ namespace FlaxEditor.CustomEditors.Dedicated
selectPrefab.Button.Clicked += () =>
{
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
- Editor.Instance.Windows.ContentWin.Select(prefab);
+ Editor.Instance.Windows.ContentWin.Select(targetPrefab);
};
// Edit selected prefab asset
var editPrefab = panel.Button("Edit Prefab");
- editPrefab.Button.Clicked += () =>
- {
- Editor.Instance.Windows.ContentWin.ClearItemsSearch();
- Editor.Instance.Windows.ContentWin.Select(prefab);
- Editor.Instance.Windows.ContentWin.Open(Editor.Instance.Windows.ContentWin.View.Selection[0]);
- };
-
- // Viewing changes applied to this actor
- var viewChanges = panel.Button("View Changes");
- viewChanges.Button.Clicked += () => ViewChanges(viewChanges.Button, new Float2(0.0f, 20.0f));
+ editPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Open(Editor.Instance.ContentDatabase.FindAsset(targetPrefab.ID));
}
+ else
+ {
+ panel.CustomControl.SlotsHorizontally = 1;
+ }
+
+ // Viewing changes applied to this actor
+ var viewChanges = panel.Button("View Changes");
+ viewChanges.Button.Clicked += () => ViewChanges(viewChanges.Button, new Float2(0.0f, 20.0f));
// Link event to update editor on prefab apply
_linkedPrefabId = prefab.ID;
diff --git a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs
index 5d06b4a94..f48e1b9c1 100644
--- a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
+using System.Linq;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.Surface;
using FlaxEngine;
@@ -35,6 +36,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
(instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier),
(instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value),
Values);
+ if (!parameters.Any())
+ group.Label("No parameters", TextAlignment.Center);
_parametersAdded = true;
}
}
diff --git a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs
index 81d826a04..a39297572 100644
--- a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs
@@ -15,13 +15,23 @@ namespace FlaxEditor.CustomEditors.Dedicated
private int _firstTimeShow;
private BezierCurveEditor _curve;
private Splitter _splitter;
+ private string _heightCachedPath;
///
public override void Initialize(LayoutElementsContainer layout)
{
var item = layout.CustomContainer>();
_curve = item.CustomControl;
- _curve.Height = 120.0f;
+ var height = 120.0f;
+ var presenter = Presenter;
+ if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0)
+ {
+ // Try to restore curve height
+ _heightCachedPath = layout.GetLayoutCachePath("Height");
+ if (Editor.Instance.ProjectCache.TryGetCustomData(_heightCachedPath, out float cachedHeight) && cachedHeight > 10.0f)
+ height = cachedHeight;
+ }
+ _curve.Height = height;
_curve.Edited += OnCurveEdited;
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
_splitter = new Splitter
@@ -45,7 +55,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
private void OnSplitterMoved(Float2 location)
{
- _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
+ _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
+
+ // Cache curve height
+ if (_heightCachedPath != null)
+ Editor.Instance.ProjectCache.SetCustomData(_heightCachedPath, _curve.Height);
}
///
@@ -133,13 +147,23 @@ namespace FlaxEditor.CustomEditors.Dedicated
private int _firstTimeShow;
private LinearCurveEditor _curve;
private Splitter _splitter;
+ private string _heightCachedPath;
///
public override void Initialize(LayoutElementsContainer layout)
{
var item = layout.CustomContainer>();
_curve = item.CustomControl;
- _curve.Height = 120.0f;
+ var height = 120.0f;
+ var presenter = Presenter;
+ if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0)
+ {
+ // Try to restore curve height
+ _heightCachedPath = layout.GetLayoutCachePath("Height");
+ if (Editor.Instance.ProjectCache.TryGetCustomData(_heightCachedPath, out float cachedHeight) && cachedHeight > 10.0f)
+ height = cachedHeight;
+ }
+ _curve.Height = height;
_curve.Edited += OnCurveEdited;
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
_splitter = new Splitter
@@ -164,6 +188,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
private void OnSplitterMoved(Float2 location)
{
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
+
+ // Cache curve height
+ if (_heightCachedPath != null)
+ Editor.Instance.ProjectCache.SetCustomData(_heightCachedPath, _curve.Height);
}
///
diff --git a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs
index fbb817768..3df5cb72e 100644
--- a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs
@@ -53,6 +53,9 @@ public class ModelPrefabEditor : GenericEditor
}
}
+ // Creates the import path UI
+ Utilities.Utils.CreateImportPathUI(layout, modelPrefab.ImportPath, false);
+
var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
_reimportButton = button.Button;
_reimportButton.Clicked += OnReimport;
diff --git a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
index c207f8949..980ad5fa4 100644
--- a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
@@ -117,6 +117,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
var data = SurfaceUtils.InitGraphParameters(parametersGroup);
SurfaceUtils.DisplayGraphParameters(group, data, ParameterGet, ParameterSet, Values, ParameterDefaultValue);
}
+
+ if (!parameters.Any())
+ groups.Label("No parameters", TextAlignment.Center);
}
///
diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
index 83fa1f129..e9c405207 100644
--- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
@@ -4,6 +4,7 @@ using System;
using System.Linq;
using FlaxEditor.Content;
using FlaxEditor.GUI;
+using FlaxEditor.GUI.Drag;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -122,7 +123,9 @@ namespace FlaxEditor.CustomEditors.Editors
{
base.Refresh();
- if (!HasDifferentValues)
+ var differentValues = HasDifferentValues;
+ Picker.DifferentValues = differentValues;
+ if (!differentValues)
{
_isRefreshing = true;
var value = Values[0];
@@ -156,6 +159,17 @@ namespace FlaxEditor.CustomEditors.Editors
private Rectangle DropdownRect => new Rectangle(Width - DropdownIconSize - DropdownIconMargin, DropdownIconMargin, DropdownIconSize, DropdownIconSize);
public Action ShowPicker;
+ public Action OnAssetDropped;
+
+ private DragItems _dragItems;
+ private DragHandlers _dragHandlers;
+ private bool _hasValidDragOver;
+ private Func _validate;
+
+ public void SetValidationMethod(Func validate)
+ {
+ _validate = validate;
+ }
public override void Draw()
{
@@ -164,6 +178,14 @@ namespace FlaxEditor.CustomEditors.Editors
var style = FlaxEngine.GUI.Style.Current;
var dropdownRect = DropdownRect;
Render2D.DrawSprite(style.ArrowDown, dropdownRect, Enabled ? (DropdownRect.Contains(PointFromWindow(RootWindow.MousePosition)) ? style.BorderSelected : style.Foreground) : style.ForegroundDisabled);
+
+ // Check if drag is over
+ if (IsDragOver && _hasValidDragOver)
+ {
+ var bounds = new Rectangle(Float2.Zero, Size);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
+ }
}
public override bool OnMouseDown(Float2 location, MouseButton button)
@@ -207,6 +229,68 @@ namespace FlaxEditor.CustomEditors.Editors
return result;
}
}
+
+ private DragDropEffect DragEffect => _hasValidDragOver ? DragDropEffect.Move : DragDropEffect.None;
+
+ ///
+ public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
+ {
+ base.OnDragEnter(ref location, data);
+
+ // Ensure to have valid drag helpers (uses lazy init)
+ if (_dragItems == null)
+ _dragItems = new DragItems(ValidateDragAsset);
+ if (_dragHandlers == null)
+ {
+ _dragHandlers = new DragHandlers
+ {
+ _dragItems,
+ };
+ }
+
+ _hasValidDragOver = _dragHandlers.OnDragEnter(data) != DragDropEffect.None;
+
+
+ return DragEffect;
+ }
+
+ private bool ValidateDragAsset(ContentItem contentItem)
+ {
+ // Load or get asset
+ return _validate?.Invoke(contentItem) ?? false;
+ }
+
+ ///
+ public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
+ {
+ base.OnDragMove(ref location, data);
+
+ return DragEffect;
+ }
+
+ ///
+ public override void OnDragLeave()
+ {
+ _hasValidDragOver = false;
+ _dragHandlers.OnDragLeave();
+
+ base.OnDragLeave();
+ }
+
+ ///
+ public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
+ {
+ var result = DragEffect;
+
+ base.OnDragDrop(ref location, data);
+
+ if (_dragItems.HasValidDrag)
+ {
+ OnAssetDropped(_dragItems.Objects[0]);
+ }
+
+ return result;
+ }
}
private TextBoxWithPicker _textBox;
@@ -221,13 +305,21 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (HasDifferentTypes)
return;
+
+ _validator = new AssetPickerValidator(ScriptType.Null);
_textBox = layout.Custom().CustomControl;
_textBox.ShowPicker = OnShowPicker;
+ _textBox.OnAssetDropped = OnItemDropped;
_textBox.EditEnd += OnEditEnd;
- _validator = new AssetPickerValidator(ScriptType.Null);
+ _textBox.SetValidationMethod(_validator.IsValid);
AssetRefEditor.ApplyAssetReferenceAttribute(Values, out _, _validator);
}
+ private void OnItemDropped(ContentItem item)
+ {
+ SetPickerPath(item);
+ }
+
private void OnShowPicker()
{
if (_validator.AssetType != ScriptType.Null)
@@ -285,12 +377,9 @@ namespace FlaxEditor.CustomEditors.Editors
{
base.Refresh();
- if (!HasDifferentValues)
- {
- _isRefreshing = true;
- _textBox.Text = GetPath();
- _isRefreshing = false;
- }
+ _isRefreshing = true;
+ _textBox.Text = HasDifferentValues ? "Multiple Values" : GetPath();
+ _isRefreshing = false;
}
///
diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
index 386d8d518..1dc624525 100644
--- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
@@ -9,6 +9,8 @@ using FlaxEditor.GUI.Drag;
using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.GUI;
using FlaxEditor.Scripting;
+using FlaxEditor.Windows;
+using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
@@ -40,6 +42,11 @@ namespace FlaxEditor.CustomEditors.Editors
private DragScripts _dragScripts;
private DragHandlers _dragHandlers;
+ ///
+ /// The presenter using this control.
+ ///
+ public IPresenterOwner PresenterContext;
+
///
/// Gets or sets the allowed objects type (given type and all sub classes). Must be type of any subclass.
///
@@ -129,6 +136,11 @@ namespace FlaxEditor.CustomEditors.Editors
///
public Func