test
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21f32d2f4add49b3b11fadb6889a2156
|
||||
timeCreated: 1714382287
|
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using Unity.Multiplayer.Center.Common.Analytics;
|
||||
using UnityEngine.Analytics;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Analytics
|
||||
{
|
||||
/// <summary>
|
||||
/// Package representation in the analytics data.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal struct Package
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier of the package.
|
||||
/// </summary>
|
||||
public string PackageId;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the user has selected this package for installation.
|
||||
/// </summary>
|
||||
public bool SelectedForInstall;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the package was recommended.
|
||||
/// </summary>
|
||||
public bool IsRecommended;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the package was already installed when the installation attempt event occured
|
||||
/// </summary>
|
||||
public bool IsAlreadyInstalled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single Answer to the GameSpecs questionnaire.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal struct GameSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier of the answered question (does not change).
|
||||
/// </summary>
|
||||
public string QuestionId;
|
||||
|
||||
/// <summary>
|
||||
/// The text of the question as displayed in the UI (may change with versions).
|
||||
/// </summary>
|
||||
public string QuestionText;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the question accepts multiple answers.
|
||||
/// </summary>
|
||||
public bool AcceptsMultipleAnswers;
|
||||
|
||||
/// <summary>
|
||||
/// The identifier of the answered question (does not change).
|
||||
/// </summary>
|
||||
public string AnswerId;
|
||||
|
||||
/// <summary>
|
||||
/// The text of the answer as displayed in the UI (may change with versions).
|
||||
/// </summary>
|
||||
public string AnswerText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal struct RecommendationData : IAnalytic.IData
|
||||
{
|
||||
/// <summary>
|
||||
/// The preset selected by the user.
|
||||
/// </summary>
|
||||
public int Preset;
|
||||
|
||||
/// <summary>
|
||||
/// The preset selected by the user (game genre) as displayed in the UI.
|
||||
/// </summary>
|
||||
public string PresetName;
|
||||
|
||||
/// <summary>
|
||||
/// The version defined in the Questionnaire data.
|
||||
/// </summary>
|
||||
public string QuestionnaireVersion;
|
||||
|
||||
/// <summary>
|
||||
/// All the selected answers to the questions of the game specs questionnaire.
|
||||
/// </summary>
|
||||
public GameSpec[] GameSpecs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What type of content the user Interacted with (buttons).
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal struct InteractionData : IAnalytic.IData
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier of the section that contains the button.
|
||||
/// </summary>
|
||||
public string SectionId;
|
||||
|
||||
/// <summary>
|
||||
/// Whether it is a call to action or a link.
|
||||
/// </summary>
|
||||
public InteractionDataType Type;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the button in the UI.
|
||||
/// </summary>
|
||||
public string DisplayName;
|
||||
|
||||
/// <summary>
|
||||
/// The target package for which the section is helpful.
|
||||
/// </summary>
|
||||
public string TargetPackageId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Payload of the installation event.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal struct InstallData : IAnalytic.IData
|
||||
{
|
||||
/// <summary>
|
||||
/// The preset selected by the user.
|
||||
/// </summary>
|
||||
public int Preset;
|
||||
|
||||
/// <summary>
|
||||
/// The preset selected by the user (game genre) as displayed in the UI.
|
||||
/// </summary>
|
||||
public string PresetName;
|
||||
|
||||
/// <summary>
|
||||
/// The version defined in the Questionnaire data.
|
||||
/// </summary>
|
||||
public string QuestionnaireVersion;
|
||||
|
||||
/// <summary>
|
||||
/// All the selected answers to the questions of the game specs questionnaire.
|
||||
/// </summary>
|
||||
public GameSpec[] GamesSpecs;
|
||||
|
||||
/// <summary>
|
||||
/// The packages that were in the recommendation tab of the multiplayer center
|
||||
/// </summary>
|
||||
public Package[] Packages;
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cee1dc929764056ac40ece91efef712
|
||||
timeCreated: 1714481696
|
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Unity.Multiplayer.Center.Common;
|
||||
using Unity.Multiplayer.Center.Questionnaire;
|
||||
using Unity.Multiplayer.Center.Recommendations;
|
||||
using Unity.Multiplayer.Center.Window.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Analytics
|
||||
{
|
||||
internal static class AnalyticsUtils
|
||||
{
|
||||
// hard-coded to avoid recomputing every time / resizing arrays
|
||||
public const int NumNetcodePackage = 2;
|
||||
public const int NumHostingPackages = 1;
|
||||
|
||||
/// <summary>
|
||||
/// From the recommendation view data (which contains the packages that the user sees and the user's selection),
|
||||
/// create the list of packages that will be sent to the analytics backend.
|
||||
/// </summary>
|
||||
/// <param name="data">The recommendation view data as shown in the recommendation tab</param>
|
||||
/// <param name="solutionToPackageData">The packages views</param>
|
||||
/// <returns>The list of packages to be sent along with the installation event.</returns>
|
||||
public static Package[] GetPackagesWithAnalyticsFormat(RecommendationViewData data, SolutionsToRecommendedPackageViewData solutionToPackageData)
|
||||
{
|
||||
var selectedNetcode = RecommendationUtils.GetSelectedNetcode(data);
|
||||
var selectedHostingModel = RecommendationUtils.GetSelectedHostingModel(data);
|
||||
var packages = solutionToPackageData.GetPackagesForSelection(selectedNetcode.Solution, selectedHostingModel.Solution);
|
||||
var packageCount = NumNetcodePackage + NumHostingPackages + packages.Length;
|
||||
|
||||
var result = new Package[packageCount];
|
||||
var resultIndex = 0;
|
||||
|
||||
AddSolutionPackages(data.NetcodeOptions, result, ref resultIndex);
|
||||
AddSolutionPackages(data.ServerArchitectureOptions, result, ref resultIndex);
|
||||
AddRecommendedPackages(packages, result, ref resultIndex);
|
||||
|
||||
Debug.Assert(resultIndex == packageCount, $"Expected {packageCount} packages, got {resultIndex}");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches all the inspector name attributes of the Preset enum and returns the displayNames
|
||||
/// Important! It assumes the enum values are 0, ... , N
|
||||
/// </summary>
|
||||
/// <returns>The array of preset names. The index in the array is the integer value of the enum value</returns>
|
||||
public static string[] GetPresetFullNames()
|
||||
{
|
||||
var t = typeof(Preset);
|
||||
var values = Enum.GetValues(t);
|
||||
var array = new string[values.Length];
|
||||
foreach (var value in values)
|
||||
{
|
||||
var preset = (Preset) value;
|
||||
var index = (int)preset;
|
||||
var asString = value.ToString();
|
||||
var memInfo = t.GetMember(asString);
|
||||
var attribute = memInfo[0].GetCustomAttribute<InspectorNameAttribute>(false);
|
||||
|
||||
if (attribute != null)
|
||||
{
|
||||
array[index] = attribute.displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Could not fetch the full name of the preset value {asString}");
|
||||
array[index] = asString;
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts AnswerData to game specs, providing the knowledge of the display names.
|
||||
/// It assumes there is exactly one answer in the answer list at this point.
|
||||
/// </summary>
|
||||
/// <param name="data">The answer data of the user</param>
|
||||
/// <param name="answerIdToAnswerName">Mapping answer id to display name</param>
|
||||
/// <param name="questionIdToQuestionName">Mapping question id to display name</param>
|
||||
/// <returns>The list of game spec that will be consumed by the analytics backend</returns>
|
||||
public static GameSpec[] ToGameSpecs(AnswerData data,
|
||||
IReadOnlyDictionary<string, string> answerIdToAnswerName,
|
||||
IReadOnlyDictionary<string, string> questionIdToQuestionName)
|
||||
{
|
||||
var result = new GameSpec[data.Answers.Count];
|
||||
for (var i = 0; i < result.Length; ++i)
|
||||
{
|
||||
var answer = data.Answers[i];
|
||||
var answerId = answer.Answers[0]; // TODO: make sure that this always exists
|
||||
result[i] = new GameSpec()
|
||||
{
|
||||
QuestionId = answer.QuestionId,
|
||||
QuestionText = questionIdToQuestionName[answer.QuestionId],
|
||||
AcceptsMultipleAnswers = false, // TODO: add test that verifies this assumption
|
||||
AnswerId = answerId,
|
||||
AnswerText = answerIdToAnswerName[answerId]
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the mapping from question id to question display name
|
||||
/// </summary>
|
||||
/// <param name="questionnaireData">The questionnaire data</param>
|
||||
/// <returns>The mapping</returns>
|
||||
public static IReadOnlyDictionary<string, string> GetQuestionDisplayNames(QuestionnaireData questionnaireData)
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>();
|
||||
foreach (var question in questionnaireData.Questions)
|
||||
{
|
||||
dictionary[question.Id] = question.Title;
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the mapping from answer id to answer display name
|
||||
/// </summary>
|
||||
/// <param name="questionnaireData">The questionnaire data</param>
|
||||
/// <returns>The mapping</returns>
|
||||
public static IReadOnlyDictionary<string, string> GetAnswerDisplayNames(QuestionnaireData questionnaireData)
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>();
|
||||
foreach (var question in questionnaireData.Questions)
|
||||
{
|
||||
foreach (var answer in question.Choices)
|
||||
{
|
||||
dictionary[answer.Id] = answer.Title;
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
static void AddSolutionPackages(RecommendedSolutionViewData[] options, Package[] result, ref int resultIndex)
|
||||
{
|
||||
foreach (var t in options)
|
||||
{
|
||||
if(string.IsNullOrEmpty(t.MainPackage?.PackageId))
|
||||
continue;
|
||||
|
||||
result[resultIndex] = new Package()
|
||||
{
|
||||
PackageId = t.MainPackage.PackageId,
|
||||
SelectedForInstall = t.Selected && t.RecommendationType != RecommendationType.Incompatible,
|
||||
IsRecommended = t.RecommendationType is RecommendationType.MainArchitectureChoice,
|
||||
IsAlreadyInstalled = t.MainPackage.IsInstalledAsProjectDependency
|
||||
};
|
||||
++resultIndex;
|
||||
}
|
||||
}
|
||||
|
||||
static void AddRecommendedPackages(RecommendedPackageViewData[] packageViewDatas, Package[] result, ref int resultIndex)
|
||||
{
|
||||
foreach (var viewData in packageViewDatas)
|
||||
{
|
||||
result[resultIndex] = new Package()
|
||||
{
|
||||
PackageId = viewData.PackageId,
|
||||
// TODO: remove hidden?
|
||||
SelectedForInstall = viewData.Selected && viewData.RecommendationType != RecommendationType.Incompatible,
|
||||
IsRecommended = viewData.RecommendationType.IsRecommendedPackage(),
|
||||
IsAlreadyInstalled = viewData.IsInstalledAsProjectDependency
|
||||
};
|
||||
++resultIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54407f5deee4439eb1885bee01956e9c
|
||||
timeCreated: 1714483618
|
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Analytics;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Analytics
|
||||
{
|
||||
/// <summary>
|
||||
/// Does the same as the MultiplayerCenterAnalytics, but logs the events to the console instead of sending them.
|
||||
/// It is useful to debug fast, without the EditorAnalytics Debugger package (but it does not replace it).
|
||||
/// </summary>
|
||||
internal class DebugAnalytics : MultiplayerCenterAnalytics
|
||||
{
|
||||
public DebugAnalytics(string questionnaireVersion, IReadOnlyDictionary<string, string> questionDisplayNames,
|
||||
IReadOnlyDictionary<string,string> answerDisplayNames)
|
||||
: base(questionnaireVersion, questionDisplayNames, answerDisplayNames) { }
|
||||
|
||||
protected override void SendAnalytic(IAnalytic analytic)
|
||||
{
|
||||
analytic.TryGatherData(out var data, out var _);
|
||||
switch (data)
|
||||
{
|
||||
case InstallData installData:
|
||||
Debug.Log($"Event: {analytic.GetType()} - Data: {ToString(installData)}");
|
||||
break;
|
||||
case RecommendationData recommendationData:
|
||||
Debug.Log($"Event: {analytic.GetType()} - Data: {ToString(recommendationData)}");
|
||||
break;
|
||||
case InteractionData interactionEventAnalytic:
|
||||
Debug.Log($"Event: {analytic.GetType()} - Data: {ToString(interactionEventAnalytic)}");
|
||||
break;
|
||||
default:
|
||||
Debug.Log($"Unknown event: {analytic.GetType()} - Data: {data}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static string ToString(GameSpec p) => $"GameSpec [{p.QuestionText} -> {p.AnswerText}]";
|
||||
|
||||
static string ToString(Package p) => $"Package [{p.PackageId} - Selected {p.SelectedForInstall} - Reco {p.IsRecommended} - Inst {p.IsAlreadyInstalled}]";
|
||||
|
||||
static string ToString(InstallData data)
|
||||
{
|
||||
var packageStrings = new List<string>(data.Packages.Length);
|
||||
foreach (var package in data.Packages)
|
||||
{
|
||||
packageStrings.Add(ToString(package));
|
||||
}
|
||||
return $"{data.PresetName} - Packages [{data.Packages.Length}] packages: \n{string.Join("\n", packageStrings)}";
|
||||
}
|
||||
|
||||
static string ToString(RecommendationData data)
|
||||
{
|
||||
var gameSpecStrings = new List<string>(data.GameSpecs.Length);
|
||||
foreach (var gameSpec in data.GameSpecs)
|
||||
{
|
||||
gameSpecStrings.Add(ToString(gameSpec));
|
||||
}
|
||||
return $"{data.PresetName} - GameSpecs [{data.GameSpecs.Length}] gamespecs: \n{string.Join("\n", gameSpecStrings)}";
|
||||
}
|
||||
|
||||
static string ToString(InteractionData data) => $"{data.SectionId}({data.TargetPackageId}) - {data.Type} - {data.DisplayName}";
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd20f32039084a12a8e48dffd4d58f48
|
||||
timeCreated: 1714651117
|
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Multiplayer.Center.Common;
|
||||
using Unity.Multiplayer.Center.Common.Analytics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Analytics;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Analytics
|
||||
{
|
||||
/// <summary>
|
||||
/// The interface for the Multiplayer Center Analytics provider (only one functional implementation, but the
|
||||
/// interface is needed for testing purposes)
|
||||
/// </summary>
|
||||
internal interface IMultiplayerCenterAnalytics
|
||||
{
|
||||
void SendInstallationEvent(AnswerData data, Preset preset, Package[] packages);
|
||||
void SendRecommendationEvent(AnswerData data, Preset preset);
|
||||
void SendGettingStartedInteractionEvent(string targetPackageId, string sectionId, InteractionDataType type, string displayName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The concrete implementation of the multiplayer center analytics provider.
|
||||
/// It convert
|
||||
/// </summary>
|
||||
internal class MultiplayerCenterAnalytics : IMultiplayerCenterAnalytics
|
||||
{
|
||||
const string k_VendorKey = "unity.multiplayer.center";
|
||||
const string k_InstallationEventName = "multiplayer_center_onInstallClicked";
|
||||
const string k_RecommendationEventName = "multiplayer_center_onRecommendation";
|
||||
const string k_GetStartedInteractionEventName = "multiplayer_center_onGetStartedInteraction";
|
||||
|
||||
readonly string m_QuestionnaireVersion;
|
||||
readonly IReadOnlyDictionary<string, string> m_AnswerIdToAnswerName;
|
||||
readonly IReadOnlyDictionary<string, string> m_QuestionIdToQuestionName;
|
||||
readonly string[] m_PresetFullNames = AnalyticsUtils.GetPresetFullNames();
|
||||
string PresetName(Preset v) => m_PresetFullNames[(int)v];
|
||||
|
||||
GameSpec[] FillGameSpecs(AnswerData data)
|
||||
{
|
||||
return AnalyticsUtils.ToGameSpecs(data, m_AnswerIdToAnswerName, m_QuestionIdToQuestionName);
|
||||
}
|
||||
|
||||
protected virtual void SendAnalytic(IAnalytic analytic)
|
||||
{
|
||||
EditorAnalytics.SendAnalytic(analytic);
|
||||
}
|
||||
|
||||
public MultiplayerCenterAnalytics(string questionnaireVersion, IReadOnlyDictionary<string, string> questionDisplayNames,
|
||||
IReadOnlyDictionary<string, string> answerDisplayNames)
|
||||
{
|
||||
m_QuestionnaireVersion = questionnaireVersion;
|
||||
m_QuestionIdToQuestionName = questionDisplayNames;
|
||||
m_AnswerIdToAnswerName = answerDisplayNames;
|
||||
}
|
||||
|
||||
public void SendGettingStartedInteractionEvent(string targetPackageId, string sectionId, InteractionDataType type, string displayName)
|
||||
{
|
||||
var analytic = new GetStartedInteractionEventAnalytic(sectionId, type, displayName, targetPackageId);
|
||||
SendAnalytic(analytic);
|
||||
}
|
||||
|
||||
public void SendInstallationEvent(AnswerData data, Preset preset, Package[] packages)
|
||||
{
|
||||
var analytic = new InstallationEventAnalytic(new InstallData()
|
||||
{
|
||||
Preset = (int)preset,
|
||||
PresetName = PresetName(preset),
|
||||
QuestionnaireVersion = m_QuestionnaireVersion,
|
||||
GamesSpecs = FillGameSpecs(data),
|
||||
Packages = packages
|
||||
});
|
||||
SendAnalytic(analytic);
|
||||
}
|
||||
|
||||
public void SendRecommendationEvent(AnswerData data, Preset preset)
|
||||
{
|
||||
var analytic = new RecommendationEventAnalytic(new RecommendationData()
|
||||
{
|
||||
Preset = (int)preset,
|
||||
PresetName = PresetName(preset),
|
||||
QuestionnaireVersion = m_QuestionnaireVersion,
|
||||
GameSpecs = FillGameSpecs(data)
|
||||
});
|
||||
SendAnalytic(analytic);
|
||||
}
|
||||
|
||||
[AnalyticInfo(eventName: k_InstallationEventName, vendorKey: k_VendorKey)]
|
||||
private class InstallationEventAnalytic : IAnalytic
|
||||
{
|
||||
InstallData m_Data;
|
||||
|
||||
public InstallationEventAnalytic(InstallData data)
|
||||
{
|
||||
m_Data = data;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGatherData(out IAnalytic.IData data, out Exception error)
|
||||
{
|
||||
data = m_Data;
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[AnalyticInfo(eventName: k_RecommendationEventName, vendorKey: k_VendorKey)]
|
||||
private class RecommendationEventAnalytic : IAnalytic
|
||||
{
|
||||
RecommendationData m_Data;
|
||||
|
||||
public RecommendationEventAnalytic(RecommendationData data)
|
||||
{
|
||||
m_Data = data;
|
||||
}
|
||||
|
||||
public bool TryGatherData(out IAnalytic.IData data, out Exception error)
|
||||
{
|
||||
data = m_Data;
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[AnalyticInfo(eventName: k_GetStartedInteractionEventName, vendorKey: k_VendorKey)]
|
||||
private class GetStartedInteractionEventAnalytic : IAnalytic
|
||||
{
|
||||
InteractionData m_Data;
|
||||
|
||||
public GetStartedInteractionEventAnalytic(string sectionId, InteractionDataType type, string displayName, string targetPackageId)
|
||||
{
|
||||
m_Data = new InteractionData()
|
||||
{
|
||||
SectionId = sectionId,
|
||||
Type = type,
|
||||
DisplayName = displayName,
|
||||
TargetPackageId = targetPackageId
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGatherData(out IAnalytic.IData data, out Exception error)
|
||||
{
|
||||
data = m_Data;
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 185e678d90804a8a85ab38276eca86fb
|
||||
timeCreated: 1714382339
|
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Unity.Multiplayer.Center.Questionnaire;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Analytics
|
||||
{
|
||||
internal static class MultiplayerCenterAnalyticsFactory
|
||||
{
|
||||
public static IMultiplayerCenterAnalytics Create()
|
||||
{
|
||||
var questionnaire = QuestionnaireObject.instance;
|
||||
var questionnaireVersion = questionnaire.Questionnaire.Version;
|
||||
var questionDisplayNames = AnalyticsUtils.GetQuestionDisplayNames(questionnaire.Questionnaire);
|
||||
var answerDisplayNames = AnalyticsUtils.GetAnswerDisplayNames(questionnaire.Questionnaire);
|
||||
|
||||
// Uncomment this line to use the DebugAnalytics class instead of the MultiplayerCenterAnalytics class
|
||||
// return new DebugAnalytics(questionnaireVersion, questionDisplayNames, answerDisplayNames);
|
||||
|
||||
return new MultiplayerCenterAnalytics(questionnaireVersion, questionDisplayNames, answerDisplayNames);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bce0b24c28e45d884acfcf49a3b8945
|
||||
timeCreated: 1714640827
|
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Unity.Multiplayer.Center.Common.Analytics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Analytics
|
||||
{
|
||||
/// <summary>
|
||||
/// The concrete implementation of the IOnboardingSectionAnalyticsProvider interface.
|
||||
/// It shall be created by the GettingStarted tab with the knowledge of the target package and the section id
|
||||
/// provided by the attribute of the onboarding section, so that the section implementer does not have to worry
|
||||
/// about it.
|
||||
/// </summary>
|
||||
internal class OnboardingSectionAnalyticsProvider : IOnboardingSectionAnalyticsProvider
|
||||
{
|
||||
readonly IMultiplayerCenterAnalytics m_Analytics;
|
||||
readonly string m_TargetPackageId;
|
||||
readonly string m_SectionId;
|
||||
|
||||
public OnboardingSectionAnalyticsProvider(IMultiplayerCenterAnalytics analytics, string targetPackageId, string sectionId)
|
||||
{
|
||||
Debug.Assert(analytics != null);
|
||||
m_Analytics = analytics;
|
||||
m_TargetPackageId = targetPackageId;
|
||||
m_SectionId = sectionId;
|
||||
}
|
||||
|
||||
public void SendInteractionEvent(InteractionDataType type, string displayName)
|
||||
{
|
||||
m_Analytics.SendGettingStartedInteractionEvent(m_TargetPackageId, m_SectionId, type, displayName);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9faa4a7023c43c5805642250c703f48
|
||||
timeCreated: 1714637861
|
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly:InternalsVisibleTo("Unity.Multiplayer.Center.Editor.Tests")]
|
||||
[assembly:InternalsVisibleTo("Unity.Multiplayer.Center.Integrations")]
|
||||
[assembly:InternalsVisibleTo("Unity.Multiplayer.Center.GettingStartedTab.Tests")]
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51566b3736df4282bbe76645014b0cc5
|
||||
timeCreated: 1700487414
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22d3dbf8d488d49d2b1130d698010dee
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Multiplayer.Center
|
||||
{
|
||||
internal static class PackageManagement
|
||||
{
|
||||
static PackageInstaller s_Installer;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the package manager window with selected package name and hides error
|
||||
/// </summary>
|
||||
public static void OpenPackageManager(string packageName)
|
||||
{
|
||||
try
|
||||
{
|
||||
UnityEditor.PackageManager.UI.Window.Open(packageName);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Hide the error in the PackageManager API until the team fixes it
|
||||
// Debug.Log("Error opening Package Manager: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the package is a direct dependency of the project
|
||||
/// </summary>
|
||||
/// <param name="packageId">The package name/id e.g. com.unity.netcode</param>
|
||||
/// <returns>True if the package is a direct dependency</returns>
|
||||
public static bool IsDirectDependency(string packageId)
|
||||
{
|
||||
var package = GetInstalledPackage(packageId);
|
||||
return package != null && package.isDirectDependency;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a package is installed.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The package name, e.g. com.unity.netcode</param>
|
||||
/// <returns>True if the package is installed, false otherwise</returns>
|
||||
public static bool IsInstalled(string packageId) => GetInstalledPackage(packageId) != null;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a package is embedded, linked locally, installed via Git or local Tarball.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The package name, e.g. com.unity.netcode</param>
|
||||
/// <returns>True if the package is linked locally, false otherwise</returns>
|
||||
public static bool IsLinkedLocallyOrEmbeddedOrViaGit(string packageId) =>
|
||||
GetInstalledPackage(packageId) is { source: PackageSource.Embedded or PackageSource.Local or PackageSource.Git or PackageSource.LocalTarball };
|
||||
|
||||
/// <summary>
|
||||
/// Finds the installed package with the given packageId or returns null.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The package name/id e.g. com.unity.netcode</param>
|
||||
/// <returns>The package info</returns>
|
||||
public static UnityEditor.PackageManager.PackageInfo GetInstalledPackage(string packageId)
|
||||
{
|
||||
return UnityEditor.PackageManager.PackageInfo.FindForPackageName(packageId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters out the packages that are already embedded, linked locally, installed via Git or local Tarball and returns this new list.
|
||||
/// </summary>
|
||||
/// <param name="installCandidates">A list of package IDs that are candidates for installation.</param>
|
||||
/// <returns>A new filtered list of packages.</returns>
|
||||
public static IEnumerable<string> RemoveLocallyLinkedOrEmbeddedOrViaGitPackagesFromList(IEnumerable<string> installCandidates)
|
||||
{
|
||||
var filteredList = new List<string>();
|
||||
|
||||
foreach (var packageId in installCandidates)
|
||||
{
|
||||
if (!IsLinkedLocallyOrEmbeddedOrViaGit(packageId))
|
||||
{
|
||||
filteredList.Add(packageId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Removing {packageId} from install candidates.\n" +
|
||||
"This package is already embedded, linked locally, installed via Git, or from a local tarball. " +
|
||||
"Please check the Package Manager for more information or to upgrade manually.");
|
||||
}
|
||||
}
|
||||
|
||||
return filteredList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if any of the given packageIds is installed.
|
||||
/// </summary>
|
||||
/// <param name="packageIds">List of package is e.g com.unity.netcode</param>
|
||||
/// <returns>True if any package is installed, false otherwise</returns>
|
||||
public static bool IsAnyPackageInstalled(params string[] packageIds)
|
||||
{
|
||||
var installedPackages = UnityEditor.PackageManager.PackageInfo.GetAllRegisteredPackages();
|
||||
var hashset = new HashSet<string>();
|
||||
|
||||
foreach (var package in installedPackages)
|
||||
{
|
||||
hashset.Add(package.name);
|
||||
}
|
||||
|
||||
foreach (var packageId in packageIds)
|
||||
{
|
||||
if (hashset.Contains(packageId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs a single package and invokes the callback when the package is installed/when the install failed.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The package name/id e.g. com.unity.netcode</param>
|
||||
/// <param name="onInstalled">The callback</param>
|
||||
public static void InstallPackage(string packageId, Action<bool> onInstalled = null)
|
||||
{
|
||||
s_Installer = new PackageInstaller(packageId);
|
||||
s_Installer.OnInstalled += onInstalled;
|
||||
s_Installer.OnInstalled += _ => s_Installer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register to an existing installation callback. This has no effect if no installation is ongoing (check
|
||||
/// <see cref="IsInstallationFinished"/> to see if that is the case).
|
||||
/// </summary>
|
||||
/// <param name="onInstalled">The callback</param>
|
||||
public static void RegisterToExistingInstaller(Action<bool> onInstalled)
|
||||
{
|
||||
if (s_Installer != null)
|
||||
{
|
||||
s_Installer.OnInstalled += onInstalled;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs several packages and invokes the callback when all packages are installed/when the installation failed.
|
||||
/// </summary>
|
||||
/// <param name="packageIds">The package names/ids e.g. com.unity.netcode</param>
|
||||
/// <param name="onAllInstalled">The callback</param>
|
||||
/// <param name="packageIdsToRemove">Optional package name/ids to remove</param>
|
||||
public static void InstallPackages(IEnumerable<string> packageIds, Action<bool> onAllInstalled = null, IEnumerable<string> packageIdsToRemove = null)
|
||||
{
|
||||
s_Installer = new PackageInstaller(RemoveLocallyLinkedOrEmbeddedOrViaGitPackagesFromList(packageIds), packageIdsToRemove);
|
||||
s_Installer.OnInstalled += onAllInstalled;
|
||||
s_Installer.OnInstalled += _ => s_Installer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a dictionary with package names as keys and versions as values
|
||||
/// </summary>
|
||||
/// <returns>The mapping (package id, installed version) </returns>
|
||||
internal static Dictionary<string, string> InstalledPackageDictionary()
|
||||
{
|
||||
var installedPackages = UnityEditor.PackageManager.PackageInfo.GetAllRegisteredPackages();
|
||||
var installedPackageDictionary = new Dictionary<string, string>();
|
||||
|
||||
foreach (var package in installedPackages)
|
||||
{
|
||||
var splitPackageId = package.packageId.Split('@');
|
||||
if (splitPackageId.Length == 2)
|
||||
{
|
||||
installedPackageDictionary[splitPackageId[0]] = splitPackageId[1];
|
||||
}
|
||||
}
|
||||
|
||||
return installedPackageDictionary;
|
||||
}
|
||||
|
||||
internal class VersionChecker
|
||||
{
|
||||
SearchRequest m_Request;
|
||||
public VersionChecker(string packageID)
|
||||
{
|
||||
m_Request = Client.Search(packageID, false);
|
||||
EditorApplication.update += Progress;
|
||||
}
|
||||
|
||||
public event Action<UnityEditor.PackageManager.PackageInfo> OnVersionFound;
|
||||
|
||||
void Progress()
|
||||
{
|
||||
if (!m_Request.IsCompleted) return;
|
||||
|
||||
EditorApplication.update -= Progress;
|
||||
var foundPackage = m_Request.Result;
|
||||
foreach (var packageInfo in foundPackage)
|
||||
{
|
||||
OnVersionFound?.Invoke(packageInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PackageInstaller
|
||||
{
|
||||
Request m_Request;
|
||||
string[] m_PackagesToAddIds;
|
||||
public event Action<bool> OnInstalled;
|
||||
|
||||
public PackageInstaller(string packageId)
|
||||
{
|
||||
// Add a package to the project
|
||||
m_Request = Client.Add(packageId);
|
||||
m_PackagesToAddIds = new[] {packageId};
|
||||
EditorApplication.update += Progress;
|
||||
}
|
||||
|
||||
public PackageInstaller(IEnumerable<string> packageIds, IEnumerable<string> packageIdsToRemove = null)
|
||||
{
|
||||
var packageIdsList = new List<string>();
|
||||
foreach (var id in packageIds)
|
||||
{
|
||||
packageIdsList.Add(id);
|
||||
}
|
||||
|
||||
var packageIdsArray = packageIdsList.ToArray();
|
||||
|
||||
string[] packageIdsToRemoveArray = null;
|
||||
if (packageIdsToRemove != null)
|
||||
{
|
||||
var packageIdsToRemoveList = new List<string>();
|
||||
foreach (var id in packageIdsToRemove)
|
||||
{
|
||||
packageIdsToRemoveList.Add(id);
|
||||
}
|
||||
packageIdsToRemoveArray = packageIdsToRemoveList.ToArray();
|
||||
}
|
||||
|
||||
// Add a package to the project
|
||||
m_Request = Client.AddAndRemove(packageIdsArray, packageIdsToRemoveArray);
|
||||
m_PackagesToAddIds = packageIdsArray;
|
||||
EditorApplication.update += Progress;
|
||||
}
|
||||
|
||||
public bool IsCompleted()
|
||||
{
|
||||
return m_Request == null || m_Request.IsCompleted;
|
||||
}
|
||||
|
||||
void Progress()
|
||||
{
|
||||
if (!m_Request.IsCompleted) return;
|
||||
|
||||
EditorApplication.update -= Progress;
|
||||
if (m_Request.Status == StatusCode.Success)
|
||||
{
|
||||
Debug.Log("Installed: " + GetInstalledPackageId());
|
||||
}
|
||||
else if (m_Request.Status >= StatusCode.Failure)
|
||||
{
|
||||
// if the request has more than one package, it will only prompt error message for one
|
||||
// We should prompt all the failed packages
|
||||
Debug.Log("Package installation request with selected packages: " + String.Join(", ", m_PackagesToAddIds) +
|
||||
" failed. \n Reason: "+ m_Request.Error.message);
|
||||
}
|
||||
|
||||
OnInstalled?.Invoke(m_Request.Status == StatusCode.Success);
|
||||
}
|
||||
|
||||
string GetInstalledPackageId()
|
||||
{
|
||||
switch (m_Request)
|
||||
{
|
||||
case AddRequest addRequest:
|
||||
return addRequest.Result.packageId;
|
||||
case AddAndRemoveRequest addAndRemoveRequest:
|
||||
var packageIds = new List<string>();
|
||||
foreach (var packageInfo in addAndRemoveRequest.Result)
|
||||
{
|
||||
packageIds.Add(packageInfo.packageId);
|
||||
}
|
||||
return string.Join(", ", packageIds);
|
||||
default:
|
||||
throw new InvalidOperationException("Unknown request type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects if any multiplayer package is installed by checking for services and Netcode installed packages.
|
||||
/// </summary>
|
||||
/// <returns>True if any package was detected, False otherwise</returns>
|
||||
public static bool IsAnyMultiplayerPackageInstalled()
|
||||
{
|
||||
var packagesToCheck = new []
|
||||
{
|
||||
"com.unity.netcode",
|
||||
"com.unity.netcode.gameobjects",
|
||||
"com.unity.services.multiplayer",
|
||||
"com.unity.transport",
|
||||
"com.unity.dedicated-server",
|
||||
"com.unity.services.cloudcode",
|
||||
"com.unity.multiplayer.playmode",
|
||||
"com.unity.services.vivox"
|
||||
// Note about "com.unity.services.core": it used to be installed only with multiplayer packages, but it is also a dependency of the analytics, which is now always installed.
|
||||
};
|
||||
|
||||
foreach (var package in packagesToCheck)
|
||||
{
|
||||
if (IsInstalled(package))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the installation process has finished.
|
||||
/// </summary>
|
||||
/// <returns>True if there is no current installer instance or installation is finished on the installer</returns>
|
||||
public static bool IsInstallationFinished()
|
||||
{
|
||||
return s_Installer == null || s_Installer.IsCompleted();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 676a3852811f4199813ce7f6dbac6880
|
||||
timeCreated: 1694784710
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a7dcde6448847648629a13d746ce966
|
||||
timeCreated: 1695036776
|
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using Unity.Multiplayer.Center.Analytics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Window
|
||||
{
|
||||
internal class MultiplayerCenterWindow : EditorWindow, ISerializationCallbackReceiver
|
||||
{
|
||||
const string k_PathInPackage = "Packages/com.unity.multiplayer.center/Editor/MultiplayerCenterWindow";
|
||||
const string k_SpinnerClassName = "processing";
|
||||
const string k_SessionStateDomainReloadKey = "MultiplayerCenter.InDomainReload";
|
||||
|
||||
VisualElement m_SpinningIcon;
|
||||
|
||||
/// <summary>
|
||||
/// Nest the main container in a VisualElement to allow for easy enabling/disabling of the entire window but
|
||||
/// without the spinning icon.
|
||||
/// </summary>
|
||||
VisualElement m_MainContainer;
|
||||
|
||||
Vector2 m_WindowSize = new(350, 300);
|
||||
|
||||
public int CurrentTab => m_TabGroup.CurrentTab;
|
||||
|
||||
// Testing purposes only. We don't want to set CurrentTab from window
|
||||
internal int CurrentTabTest
|
||||
{
|
||||
get => m_TabGroup.CurrentTab;
|
||||
set => m_TabGroup.SetSelected(value);
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
bool m_RequestGettingStartedTabAfterDomainReload = false;
|
||||
|
||||
[SerializeField]
|
||||
TabGroup m_TabGroup;
|
||||
|
||||
/// <summary>
|
||||
/// This is the reference Multiplayer Center analytics implementation. This class owns it.
|
||||
/// </summary>
|
||||
IMultiplayerCenterAnalytics m_MultiplayerCenterAnalytics;
|
||||
|
||||
IMultiplayerCenterAnalytics MultiplayerCenterAnalytics => m_MultiplayerCenterAnalytics ??= MultiplayerCenterAnalyticsFactory.Create();
|
||||
|
||||
[MenuItem("Window/Multiplayer/Multiplayer Center")]
|
||||
public static void OpenWindow()
|
||||
{
|
||||
var showUtility = false; // TODO: figure out if it would be a good idea to have a utility window (always on top, cannot be tabbed)
|
||||
GetWindow<MultiplayerCenterWindow>(showUtility, "Multiplayer Center", true);
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// Adjust window size based on dpi scaling
|
||||
var dpiScale = EditorGUIUtility.pixelsPerPoint;
|
||||
minSize = new Vector2(m_WindowSize.x * dpiScale, m_WindowSize.y * dpiScale);
|
||||
|
||||
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeDomainReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload -= OnAfterDomainReload;
|
||||
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeDomainReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload += OnAfterDomainReload;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeDomainReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload -= OnAfterDomainReload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes Tab from Recommendation to the Quickstart tab.
|
||||
/// </summary>
|
||||
public void RequestShowGettingStartedTabAfterDomainReload()
|
||||
{
|
||||
m_RequestGettingStartedTabAfterDomainReload = true;
|
||||
|
||||
// If no domain reload is necessary, this will be called.
|
||||
// If domain reload is necessary, the delay call will be forgotten, but CreateGUI will be called like after any domain reload
|
||||
// An extra delay is added to make sure that the visibility conditions of the Quickstart tab have been
|
||||
// fully evaluated. This solves MTT-8939.
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
rootVisualElement.schedule.Execute(CallCreateGuiWithQuickstartRequest).ExecuteLater(300);
|
||||
};
|
||||
}
|
||||
|
||||
internal void DisableUiForInstallation()
|
||||
{
|
||||
SetSpinnerIconRotating();
|
||||
m_MainContainer.SetEnabled(false);
|
||||
}
|
||||
|
||||
internal void ReenableUiAfterInstallation()
|
||||
{
|
||||
RemoveSpinnerIconRotating();
|
||||
m_MainContainer.SetEnabled(true);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Restore the GUI if it was cleared in OnBeforeSerialize.
|
||||
if (m_TabGroup == null || m_TabGroup.ViewCount < 1)
|
||||
{
|
||||
CreateGUI();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateGUI()
|
||||
{
|
||||
rootVisualElement.name = "root";
|
||||
m_MainContainer ??= new VisualElement();
|
||||
m_MainContainer.name = "recommendation-tab-container";
|
||||
m_MainContainer.Clear();
|
||||
rootVisualElement.Add(m_MainContainer);
|
||||
m_SpinningIcon = new VisualElement();
|
||||
var theme = EditorGUIUtility.isProSkin ? "dark" : "light";
|
||||
rootVisualElement.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>($"{k_PathInPackage}/UI/{theme}.uss"));
|
||||
rootVisualElement.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>($"{k_PathInPackage}/UI/MultiplayerCenterWindow.uss"));
|
||||
|
||||
if (m_TabGroup == null || m_TabGroup.ViewCount < 1 || !m_TabGroup.TabsAreValid())
|
||||
m_TabGroup = new TabGroup(MultiplayerCenterAnalytics, new ITabView[] {new RecommendationTabView(), new GettingStartedTabView()});
|
||||
else // since we are not serializing the analytics provider, we need to set it again
|
||||
m_TabGroup.MultiplayerCenterAnalytics = MultiplayerCenterAnalytics;
|
||||
|
||||
m_TabGroup.CreateTabs();
|
||||
m_MainContainer.Add(m_TabGroup.Root);
|
||||
|
||||
var installationInProgress = !PackageManagement.IsInstallationFinished();
|
||||
SetWindowContentEnabled(installationInProgress, m_RequestGettingStartedTabAfterDomainReload);
|
||||
ShowAppropriateTab(installationInProgress);
|
||||
}
|
||||
|
||||
void ShowAppropriateTab(bool installationInProgress)
|
||||
{
|
||||
if (installationInProgress)
|
||||
{
|
||||
PackageManagement.RegisterToExistingInstaller(b => RequestShowGettingStartedTabAfterDomainReload());
|
||||
m_TabGroup.SetSelected(0, force: true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_RequestGettingStartedTabAfterDomainReload)
|
||||
{
|
||||
m_RequestGettingStartedTabAfterDomainReload = false;
|
||||
m_TabGroup.SetSelected(1, force: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_TabGroup.SetSelected(m_TabGroup.CurrentTab, force: true);
|
||||
}
|
||||
}
|
||||
|
||||
void SetWindowContentEnabled(bool installationInProgress, bool quickstartRequested)
|
||||
{
|
||||
m_MainContainer.SetEnabled(!installationInProgress || quickstartRequested);
|
||||
|
||||
// if we are current already processing an installation, show the spinning icon
|
||||
if (installationInProgress)
|
||||
{
|
||||
// Wait a bit because the animation does not trigger when we call this in CreateGUI
|
||||
EditorApplication.delayCall += SetSpinnerIconRotating;
|
||||
}
|
||||
|
||||
rootVisualElement.Add(m_SpinningIcon);
|
||||
}
|
||||
|
||||
void CallCreateGuiWithQuickstartRequest()
|
||||
{
|
||||
// Interestingly, setting this before registering the delay call sometimes results in the value
|
||||
// being false when CreateGUI starts, so we set it again here.
|
||||
m_RequestGettingStartedTabAfterDomainReload = true;
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
void SetSpinnerIconRotating()
|
||||
{
|
||||
m_SpinningIcon.AddToClassList(k_SpinnerClassName);
|
||||
}
|
||||
|
||||
void RemoveSpinnerIconRotating()
|
||||
{
|
||||
m_SpinningIcon?.RemoveFromClassList(k_SpinnerClassName);
|
||||
}
|
||||
|
||||
void ClearTabs()
|
||||
{
|
||||
m_TabGroup?.Clear();
|
||||
m_TabGroup = null;
|
||||
}
|
||||
|
||||
// This will not get called when the Editor is closed.
|
||||
void OnDestroy()
|
||||
{
|
||||
ClearTabs();
|
||||
}
|
||||
|
||||
static void OnBeforeDomainReload()
|
||||
{
|
||||
SessionState.SetBool(k_SessionStateDomainReloadKey, true);
|
||||
}
|
||||
|
||||
static void OnAfterDomainReload()
|
||||
{
|
||||
SessionState.SetBool(k_SessionStateDomainReloadKey, false);
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
// ClearTabs if the Window gets serialized, but we are not in DomainReload
|
||||
// This happens when the Editor closes or the WindowLayout is saved by the user.
|
||||
// This ensures that the State of the Tabs is not serialized into the WindowLayout of the User.
|
||||
if (SessionState.GetBool(k_SessionStateDomainReloadKey, false) == false)
|
||||
{
|
||||
ClearTabs();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
// Empty on purpose.
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e006510f5f4425ca971c7de0bfbb77a
|
||||
timeCreated: 1695036798
|
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
using Unity.Multiplayer.Center.Analytics;
|
||||
using Unity.Multiplayer.Center.Common;
|
||||
using Unity.Multiplayer.Center.Questionnaire;
|
||||
using Unity.Multiplayer.Center.Recommendations;
|
||||
using Unity.Multiplayer.Center.Window.UI;
|
||||
using Unity.Multiplayer.Center.Window.UI.RecommendationView;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Window
|
||||
{
|
||||
internal class RecommendationTabView : ITabView
|
||||
{
|
||||
QuestionnaireView m_QuestionnaireView;
|
||||
RecommendationView m_RecommendationView;
|
||||
|
||||
RecommendationViewBottomBar m_BottomBarView;
|
||||
|
||||
bool m_IsVisible;
|
||||
bool m_ShouldRefresh = true;
|
||||
|
||||
[SerializeField]
|
||||
PreReleaseHandling m_PreReleaseHandling = new();
|
||||
|
||||
[field: SerializeField] // Marked as redundant by Rider, but it is not.
|
||||
public string Name { get; private set; }
|
||||
|
||||
public VisualElement RootVisualElement { get; set; }
|
||||
public IMultiplayerCenterAnalytics MultiplayerCenterAnalytics { get; set; }
|
||||
|
||||
// Access to QuestionnaireView for testing purposes
|
||||
internal QuestionnaireView QuestionnaireView => m_QuestionnaireView;
|
||||
|
||||
public RecommendationTabView(string name = "Recommendation")
|
||||
{
|
||||
Name = name;
|
||||
m_PreReleaseHandling.OnAllChecksFinished += PatchData;
|
||||
m_PreReleaseHandling.CheckForUpdates();
|
||||
Undo.undoRedoPerformed += OnUndoRedoPerformed;
|
||||
}
|
||||
|
||||
void OnUndoRedoPerformed()
|
||||
{
|
||||
UserChoicesObject.instance.Save();
|
||||
m_QuestionnaireView?.Refresh();
|
||||
UpdateRecommendation(keepSelection:true);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
|
||||
m_RecommendationView?.Clear();
|
||||
m_QuestionnaireView?.Clear();
|
||||
RootVisualElement?.Clear();
|
||||
m_QuestionnaireView = null;
|
||||
m_RecommendationView = null;
|
||||
}
|
||||
|
||||
public void SetVisible(bool visible)
|
||||
{
|
||||
RootVisualElement.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
m_IsVisible = visible;
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
Debug.Assert(MultiplayerCenterAnalytics != null, "MultiplayerCenterAnalytics != null");
|
||||
RefreshPreReleaseHandling();
|
||||
if (!m_ShouldRefresh && RootVisualElement.childCount > 0) return;
|
||||
CreateStandardView();
|
||||
m_ShouldRefresh = false;
|
||||
}
|
||||
|
||||
void RefreshPreReleaseHandling()
|
||||
{
|
||||
if (!m_PreReleaseHandling.IsReady)
|
||||
{
|
||||
m_PreReleaseHandling.OnAllChecksFinished += PatchData;
|
||||
m_PreReleaseHandling.CheckForUpdates();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PreReleaseHandling.PatchRecommenderSystemData();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateStandardView()
|
||||
{
|
||||
Clear();
|
||||
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
|
||||
Undo.undoRedoPerformed += OnUndoRedoPerformed;
|
||||
MigrateUserChoices();
|
||||
|
||||
// We need this because Bottom bar is a part of the Recommendations Tab and it should always stay
|
||||
// at the bottom of the view. So we need to make sure that the root tab element is always 100% height.
|
||||
RootVisualElement.style.height = Length.Percent(100);
|
||||
|
||||
var horizontalContainer = new TwoPaneSplitView(0, 250, TwoPaneSplitViewOrientation.Horizontal);
|
||||
horizontalContainer.AddToClassList(StyleClasses.MainSplitView);
|
||||
horizontalContainer.name = "recommendation-tab-split-view";
|
||||
|
||||
// This is used to make sure the left side does not grow to 100% as this is what would happen by default.
|
||||
// It feels not 100% correct. But it seems to be the only way to match the height of the 2 sides with how
|
||||
// our views are build currently.
|
||||
horizontalContainer.contentContainer.style.position = Position.Relative;
|
||||
|
||||
m_QuestionnaireView = new QuestionnaireView(QuestionnaireObject.instance.Questionnaire);
|
||||
m_QuestionnaireView.OnQuestionnaireDataChanged += HandleQuestionnaireDataChanged;
|
||||
m_QuestionnaireView.OnPresetSelected += OnPresetSelected;
|
||||
m_QuestionnaireView.Root.AddToClassList(StyleClasses.MainSplitViewLeft);
|
||||
horizontalContainer.Add(m_QuestionnaireView.Root);
|
||||
|
||||
m_RecommendationView = new RecommendationView();
|
||||
m_RecommendationView.Root.AddToClassList(StyleClasses.MainSplitViewRight);
|
||||
horizontalContainer.Add(m_RecommendationView.Root);
|
||||
RootVisualElement.Add(horizontalContainer);
|
||||
|
||||
m_BottomBarView = new RecommendationViewBottomBar(MultiplayerCenterAnalytics);
|
||||
m_RecommendationView.OnPackageSelectionChanged +=
|
||||
() => m_BottomBarView.UpdatePackagesToInstall(m_RecommendationView.CurrentRecommendation, m_RecommendationView.AllPackages);
|
||||
RootVisualElement.Add(m_BottomBarView);
|
||||
UpdateRecommendation(keepSelection: true);
|
||||
|
||||
m_BottomBarView.SetInfoTextForCheckingPackages(!m_PreReleaseHandling.IsReady);
|
||||
}
|
||||
|
||||
void HandleQuestionnaireDataChanged()
|
||||
{
|
||||
UpdateRecommendation(keepSelection: false);
|
||||
}
|
||||
|
||||
static void MigrateUserChoices()
|
||||
{
|
||||
var questionnaire = QuestionnaireObject.instance.Questionnaire;
|
||||
var userChoices = UserChoicesObject.instance;
|
||||
|
||||
// make sure the version of the questionnaire is the same as the one in the user choices.
|
||||
if (questionnaire.Version != userChoices.QuestionnaireVersion && userChoices.UserAnswers.Answers.Count > 0)
|
||||
{
|
||||
Logic.MigrateUserChoices(questionnaire, userChoices);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateRecommendation(bool keepSelection)
|
||||
{
|
||||
var questionnaire = QuestionnaireObject.instance.Questionnaire;
|
||||
var userChoices = UserChoicesObject.instance;
|
||||
|
||||
var errors = Logic.ValidateAnswers(questionnaire, userChoices.UserAnswers);
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Debug.LogError(error);
|
||||
}
|
||||
|
||||
var recommendation = userChoices.Preset == Preset.None? null
|
||||
: RecommenderSystem.GetRecommendation(questionnaire, userChoices.UserAnswers);
|
||||
m_PreReleaseHandling.PatchPackages(recommendation);
|
||||
if (keepSelection)
|
||||
{
|
||||
RecommendationUtils.ApplyPreviousSelection(recommendation, userChoices.SelectedSolutions);
|
||||
}
|
||||
else if (recommendation != null) // we only send the event if there is a recommendation and it is a new one
|
||||
{
|
||||
MultiplayerCenterAnalytics.SendRecommendationEvent(userChoices.UserAnswers, userChoices.Preset);
|
||||
}
|
||||
|
||||
m_RecommendationView.UpdateRecommendation(recommendation, m_PreReleaseHandling);
|
||||
m_BottomBarView.UpdatePackagesToInstall(recommendation, m_RecommendationView.AllPackages);
|
||||
}
|
||||
|
||||
void PatchData()
|
||||
{
|
||||
m_PreReleaseHandling.PatchRecommenderSystemData();
|
||||
m_PreReleaseHandling.OnAllChecksFinished -= PatchData;
|
||||
m_ShouldRefresh = true;
|
||||
if(m_IsVisible)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void OnPresetSelected(Preset preset)
|
||||
{
|
||||
var (resultAnswerData, recommendation) = Logic.ApplyPresetToAnswerData(
|
||||
UserChoicesObject.instance.UserAnswers, preset, QuestionnaireObject.instance.Questionnaire);
|
||||
|
||||
UserChoicesObject.instance.UserAnswers = resultAnswerData;
|
||||
UserChoicesObject.instance.Save();
|
||||
|
||||
if (recommendation != null)
|
||||
MultiplayerCenterAnalytics.SendRecommendationEvent(resultAnswerData, preset);
|
||||
|
||||
m_QuestionnaireView.Refresh();
|
||||
m_RecommendationView.UpdateRecommendation(recommendation, m_PreReleaseHandling);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 052c764978664486a9c04518899ddf57
|
||||
timeCreated: 1700489744
|
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Multiplayer.Center.Analytics;
|
||||
using Unity.Multiplayer.Center.Questionnaire;
|
||||
using Unity.Multiplayer.Center.Recommendations;
|
||||
using Unity.Multiplayer.Center.Window.UI;
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Window
|
||||
{
|
||||
class RecommendationViewBottomBar : VisualElement
|
||||
{
|
||||
readonly Label m_PackageCount;
|
||||
readonly Button m_InstallPackageButton;
|
||||
readonly Label m_InfoLabel;
|
||||
|
||||
IMultiplayerCenterAnalytics m_Analytics;
|
||||
|
||||
MultiplayerCenterWindow m_Window = EditorWindow.GetWindow<MultiplayerCenterWindow>();
|
||||
List<string> m_PackagesToInstallIds = new ();
|
||||
List<string> m_PackagesToInstallNames = new ();
|
||||
RecommendationViewData m_RecommendationViewData;
|
||||
SolutionsToRecommendedPackageViewData m_SolutionToPackageData;
|
||||
|
||||
public RecommendationViewBottomBar(IMultiplayerCenterAnalytics analytics)
|
||||
{
|
||||
m_Analytics = analytics;
|
||||
name = "bottom-bar";
|
||||
m_PackageCount = new Label {name = "package-count"};
|
||||
m_InfoLabel = new Label();
|
||||
|
||||
// Setup Install Button
|
||||
m_InstallPackageButton = new Button(OnInstallButtonClicked) {text = "Install Packages"};
|
||||
m_InstallPackageButton.AddToClassList(StyleClasses.NextStepButton);
|
||||
|
||||
// Put the button in a container
|
||||
var installPackageContainer = new VisualElement() {name = "install-package-container"};
|
||||
installPackageContainer.Add(m_InstallPackageButton);
|
||||
|
||||
Add(m_PackageCount);
|
||||
Add(m_InfoLabel);
|
||||
Add(installPackageContainer);
|
||||
}
|
||||
|
||||
void OnInstallButtonClicked()
|
||||
{
|
||||
if (!PackageManagement.IsAnyMultiplayerPackageInstalled() || WarnDialogForPackageInstallation())
|
||||
{
|
||||
SendInstallationAnalyticsEvent();
|
||||
InstallSelectedPackagesAndExtension();
|
||||
}
|
||||
}
|
||||
|
||||
void SendInstallationAnalyticsEvent()
|
||||
{
|
||||
var answerObject = UserChoicesObject.instance;
|
||||
m_Analytics.SendInstallationEvent(answerObject.UserAnswers, answerObject.Preset,
|
||||
AnalyticsUtils.GetPackagesWithAnalyticsFormat(m_RecommendationViewData, m_SolutionToPackageData));
|
||||
}
|
||||
|
||||
bool WarnDialogForPackageInstallation()
|
||||
{
|
||||
var warningMessage =
|
||||
"Ensure compatibility with your current multiplayer packages before installing or upgrading the following:\n" +
|
||||
string.Join("\n", m_PackagesToInstallNames);
|
||||
return EditorUtility.DisplayDialog("Install Packages", warningMessage, "OK", "Cancel");
|
||||
}
|
||||
|
||||
void InstallSelectedPackagesAndExtension()
|
||||
{
|
||||
SetInfoTextForInstallation(isInstalling:true);
|
||||
m_Window.DisableUiForInstallation();
|
||||
PackageManagement.InstallPackages(m_PackagesToInstallIds, onAllInstalled: OnInstallationFinished);
|
||||
}
|
||||
|
||||
void OnInstallationFinished(bool success)
|
||||
{
|
||||
SetInfoTextForInstallation(isInstalling:false);
|
||||
m_Window.RequestShowGettingStartedTabAfterDomainReload();
|
||||
m_Window.ReenableUiAfterInstallation();
|
||||
}
|
||||
|
||||
public void UpdatePackagesToInstall(RecommendationViewData data, SolutionsToRecommendedPackageViewData packageViewData)
|
||||
{
|
||||
m_RecommendationViewData = data;
|
||||
m_SolutionToPackageData = packageViewData;
|
||||
var packages = RecommendationUtils.PackagesToInstall(data, packageViewData);
|
||||
RecommendationUtils.GetPackagesWithAdditionalPackages(packages, out m_PackagesToInstallIds, out m_PackagesToInstallNames, out var toolTip);
|
||||
m_PackageCount.tooltip = toolTip;
|
||||
|
||||
// Note: quickstart is counted in the list of packages to install, but not the names
|
||||
m_PackageCount.text = $"Packages to install: {m_PackagesToInstallNames.Count}";
|
||||
// if the list is empty, disable the button
|
||||
m_InstallPackageButton.SetEnabled(m_PackagesToInstallNames.Count > 0);
|
||||
}
|
||||
|
||||
internal void SetInfoTextForInstallation(bool isInstalling)
|
||||
{
|
||||
SetInfoLabelTextAndVisibility("Downloading packages, please wait ...", isInstalling);
|
||||
}
|
||||
|
||||
internal void SetInfoTextForCheckingPackages(bool isChecking)
|
||||
{
|
||||
SetInfoLabelTextAndVisibility("Querying packages information ...", isChecking);
|
||||
|
||||
// Handle the case of reopening the window during the installation.
|
||||
// When reopening the window, the packages are being checked. Once that check is done, we still want to
|
||||
// display the installation package text if there is an ongoing installation.
|
||||
if(!isChecking && !PackageManagement.IsInstallationFinished())
|
||||
SetInfoTextForInstallation(isInstalling:true);
|
||||
}
|
||||
|
||||
void SetInfoLabelTextAndVisibility(string text, bool isVisible)
|
||||
{
|
||||
m_InfoLabel.text = text;
|
||||
m_InfoLabel.visible = isVisible;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1084186a4004823abee9b87c8ddf198
|
||||
timeCreated: 1710930997
|
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
using Unity.Multiplayer.Center.Analytics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Unity.Multiplayer.Center.Window
|
||||
{
|
||||
// Note: there is a TabView API in UI Toolkit, but only starting from 2023.2
|
||||
internal interface ITabView
|
||||
{
|
||||
/// <summary>
|
||||
/// The name as displayed in the tab button
|
||||
/// Should be serialized.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The root visual element of the tab view.
|
||||
/// The setter will only be used if the root visual element is null when the tab is created.
|
||||
/// </summary>
|
||||
VisualElement RootVisualElement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the tab view visible or not.
|
||||
/// </summary>
|
||||
/// <param name="visible">If true, visible.</param>
|
||||
void SetVisible(bool visible);
|
||||
|
||||
/// <summary>
|
||||
/// If true the Tab can be selected by the user.
|
||||
/// </summary>
|
||||
bool IsEnabled => true;
|
||||
|
||||
/// <summary>
|
||||
/// Tooltip which will be shown on the Tab Button.
|
||||
/// </summary>
|
||||
string ToolTip => "";
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the UI Elements according to latest data.
|
||||
/// If the UI is not created yet, it does it.
|
||||
/// </summary>
|
||||
void Refresh();
|
||||
|
||||
/// <summary>
|
||||
/// Unregister all events and clear UI Elements
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// The Multiplayer Center Analytics provider.
|
||||
/// </summary>
|
||||
IMultiplayerCenterAnalytics MultiplayerCenterAnalytics { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class TabGroup
|
||||
{
|
||||
const string k_TabViewName = "tab-view";
|
||||
const string k_TabZoneName = "tab-zone";
|
||||
const string k_TabButtonUssClass = "tab-button";
|
||||
|
||||
// The container for all the tabs
|
||||
const string k_TabsContainerUssClass ="tabs-container";
|
||||
|
||||
// Gets applied to the root of each tab
|
||||
const string k_TabContentUssClass = "tab-content";
|
||||
|
||||
[field: SerializeField]
|
||||
public int CurrentTab { get; private set; } = -1;
|
||||
|
||||
public int ViewCount => m_TabViews?.Length ?? 0;
|
||||
|
||||
VisualElement[] m_TabButtons;
|
||||
|
||||
[SerializeReference]
|
||||
ITabView[] m_TabViews;
|
||||
|
||||
public VisualElement Root { get; private set; }
|
||||
|
||||
VisualElement m_MainContainer;
|
||||
|
||||
IMultiplayerCenterAnalytics m_MultiplayerCenterAnalytics;
|
||||
|
||||
internal IMultiplayerCenterAnalytics MultiplayerCenterAnalytics
|
||||
{
|
||||
get => m_MultiplayerCenterAnalytics;
|
||||
set
|
||||
{
|
||||
m_MultiplayerCenterAnalytics = value;
|
||||
foreach (var tabView in m_TabViews)
|
||||
{
|
||||
if(tabView != null)
|
||||
tabView.MultiplayerCenterAnalytics = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TabGroup(IMultiplayerCenterAnalytics analytics, ITabView[] tabViews, int defaultIndex = 0)
|
||||
{
|
||||
m_TabViews = tabViews;
|
||||
CurrentTab = defaultIndex;
|
||||
MultiplayerCenterAnalytics = analytics;
|
||||
}
|
||||
|
||||
public void SetSelected(int index, bool force = false)
|
||||
{
|
||||
// Select the first tab, if the requested tab is not enabled.
|
||||
// This assumes the first tab is always enabled.
|
||||
if (!m_TabViews[index].IsEnabled)
|
||||
index = 0;
|
||||
|
||||
if (index == CurrentTab && !force)
|
||||
return;
|
||||
|
||||
if (CurrentTab >= 0 && CurrentTab < m_TabViews.Length)
|
||||
{
|
||||
m_TabButtons[CurrentTab].RemoveFromClassList("selected");
|
||||
m_TabViews[CurrentTab].SetVisible(false);
|
||||
}
|
||||
|
||||
EditorPrefs.SetInt(PlayerSettings.productName + "_MultiplayerCenter_TabIndex", index);
|
||||
CurrentTab = index;
|
||||
m_TabViews[CurrentTab].Refresh();
|
||||
m_TabButtons[CurrentTab].AddToClassList("selected");
|
||||
m_TabViews[CurrentTab].SetVisible(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates the visual elements for all the tabs.
|
||||
/// Use this to create the tabs for the first time the UI is shown or after a domain reload.
|
||||
/// </summary>
|
||||
public void CreateTabs()
|
||||
{
|
||||
Root ??= new VisualElement();
|
||||
m_MainContainer ??= new VisualElement();
|
||||
|
||||
if (Root.Q(k_TabZoneName) != null)
|
||||
Root.Q(k_TabZoneName).RemoveFromHierarchy();
|
||||
|
||||
var tabZone = new VisualElement() {name = k_TabZoneName};
|
||||
Root.Add(tabZone);
|
||||
Root.name = k_TabViewName;
|
||||
m_TabButtons = new VisualElement[m_TabViews.Length];
|
||||
for (var i = 0; i < m_TabViews.Length; i++)
|
||||
{
|
||||
var tabView = m_TabViews[i];
|
||||
var index = i; // copy for closure
|
||||
var tabButton = new Button(() => SetSelected(index));
|
||||
tabButton.enabledSelf = tabView.IsEnabled;
|
||||
tabButton.tooltip = tabView.ToolTip;
|
||||
tabButton.AddToClassList(k_TabButtonUssClass);
|
||||
tabButton.text = tabView.Name;
|
||||
tabZone.Add(tabButton);
|
||||
m_TabButtons[i] = tabButton;
|
||||
tabView.RootVisualElement ??= new VisualElement();
|
||||
tabView.RootVisualElement.AddToClassList(k_TabContentUssClass);
|
||||
tabView.RootVisualElement.style.display = DisplayStyle.None;
|
||||
m_MainContainer.Add(m_TabViews[i].RootVisualElement);
|
||||
}
|
||||
|
||||
m_MainContainer.AddToClassList(k_TabsContainerUssClass);
|
||||
Root.Add(m_MainContainer);
|
||||
CurrentTab = EditorPrefs.GetInt(PlayerSettings.productName + "_MultiplayerCenter_TabIndex", 0);
|
||||
}
|
||||
|
||||
static void SetVisible(VisualElement e, bool visible)
|
||||
{
|
||||
e.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if(m_TabViews == null)
|
||||
return;
|
||||
|
||||
foreach (var tabView in m_TabViews)
|
||||
{
|
||||
tabView?.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TabsAreValid()
|
||||
{
|
||||
if (m_TabViews == null)
|
||||
return false;
|
||||
|
||||
foreach (var tab in m_TabViews)
|
||||
{
|
||||
if (tab == null)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdefed6b790f4373b0feb7d25854d8ac
|
||||
timeCreated: 1700582398
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bd34ec3fe8f4aed936c3a0cf2f32e56
|
||||
timeCreated: 1695037065
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34a09eb4d6e8d44989194a25525c5147
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,127 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f78a544322c742b89e63fb68557b1d2
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 32
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 631 B |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2ce704e56cc84fb3b347499263c6244
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7a38e6eccbfc49778cb8b77f594a971
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 550 B |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad1d29f4654194951a3c8bf507914d05
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 987 B |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a23c0dd570fd44b57a03a8880002fcca
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 897 B |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dce12af736e0a4a1ba35d6424f897dc9
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7711b0cc806d430b8a95f1e33ec3649
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 657 B |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fe892784421e47f5aa40c2784a6cb3e
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38b78df4a34c94fa6a52c90239606ff1
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 432 B |
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d63245ece6d8f476c8c7ca24da9937f6
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
After Width: | Height: | Size: 802 B |