@JuliaP_Unity , I realise know that we do diferent things. My AbilityUIElement define childs for itself that it need to operate. For the CardElement in the UnityRoyal, eventhough the Card need some visual element to function, you chose to define them in the UI builder has children of the CardElement. So if someone was to remove some of the children, the CardElement would break. That’s what I try to avoid by defining the needed children of my custom element. You either use the custom element (and its’ children) or you don’t use it at all because it would not work propoerly if it misses some of it’s children.
@antoine-unity ,
I tired to build with IL2CPP with the preseve attribute without any luck. I get the same behavior building with the ‘legacy’ build settings or the new scriptable build pipeline (with IL2CPP or Mono):
First my non custom ui element (this one uses the custom element but is not one itself) don’t find it’s USS file in hte builds : Style sheet not found for path "Packages/wayn-group.mgm.ability/Runtime/UI/Resources/AbilityBar.uss"
Second my custom AbilityUIElement fail to load because of :
Element 'WaynGroup.Mgm.Ability.UI.AbilityUIElement' has no registered factory method.
Looking at where the error is thrown I tried to play with various ways of defining the namespace but nothing worked :
internal static VisualElement Create(VisualElementAsset asset, CreationContext ctx)
{
List<IUxmlFactory> factoryList;
if (!VisualElementFactoryRegistry.TryGetValue(asset.fullTypeName, out factoryList))
{
if (asset.fullTypeName.StartsWith("UnityEngine.Experimental.UIElements.") || asset.fullTypeName.StartsWith("UnityEditor.Experimental.UIElements."))
{
string experimentalTypeName = asset.fullTypeName.Replace(".Experimental.UIElements", ".UIElements");
if (!VisualElementFactoryRegistry.TryGetValue(experimentalTypeName, out factoryList))
{
Debug.LogErrorFormat("Element '{0}' has no registered factory method.", asset.fullTypeName);
return new Label(string.Format("Unknown type: '{0}'", asset.fullTypeName));
}
}
else if (asset.fullTypeName == UxmlRootElementFactory.k_ElementName)
{
// Support UXML without namespace for backward compatibility.
VisualElementFactoryRegistry.TryGetValue(typeof(UxmlRootElementFactory).Namespace + "." + asset.fullTypeName, out factoryList);
}
else
{
// FAILS HERE ####################################
Debug.LogErrorFormat("Element '{0}' has no registered factory method.", asset.fullTypeName);
return new Label(string.Format("Unknown type: '{0}'", asset.fullTypeName));
// FAILS HERE ####################################
}
}
IUxmlFactory factory = null;
foreach (IUxmlFactory f in factoryList)
{
if (f.AcceptsAttributeBag(asset, ctx))
{
factory = f;
break;
}
}
if (factory == null)
{
Debug.LogErrorFormat("Element '{0}' has a no factory that accept the set of XML attributes specified.", asset.fullTypeName);
return new Label(string.Format("Type with no factory: '{0}'", asset.fullTypeName));
}
VisualElement res = factory.Create(asset, ctx);
if (res != null)
{
AssignClassListFromAssetToElement(asset, res);
AssignStyleSheetFromAssetToElement(asset, res);
}
return res;
}
Here is the definition of my element :
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Scripting;
using UnityEngine.UIElements;
namespace WaynGroup.Mgm.Ability.UI
{
[Preserve]
class AbilityUIElement : VisualElement
{
[Preserve]
public new class UxmlFactory : UxmlFactory<AbilityUIElement, UxmlTraits> { }
[Preserve]
public new class UxmlTraits : VisualElement.UxmlTraits { }
public AbilityUIElement()
{
VisualTreeAsset visualTree = Resources.Load<VisualTreeAsset>("AbilityUIElement");
visualTree.CloneTree(this);
UnassignAbility();
SetTime(0);
SetBackGroundFill(0);
}
private ScriptableAbility _ability;
private Entity _owner;
private int _index;
private EntityManager _entityManager;
public void AssignAbility(Entity owner, int index, ScriptableAbility ability, EntityManager entityManager)
{
_owner = owner;
_index = index;
_ability = ability;
_entityManager = entityManager;
this.Q(name: "Icon").style.backgroundImage = new StyleBackground(_ability.Icon);
}
public void UnassignAbility()
{
_owner = Entity.Null;
_ability = null;
this.Q(name: "Icon").style.backgroundImage = null;
}
public bool IsAssigned => Entity.Null != _owner && _ability != null;
public void UpdateCoolDown()
{
if (!IsAssigned) return;
(float, float) remainingTime = _entityManager.GetBuffer<AbilityBuffer>(_owner)[_index].Ability.CoolDown.ComputeRemainingTime();
SetTime(remainingTime.Item1);
SetBackGroundFill(remainingTime.Item2);
}
private void SetBackGroundFill(float fill)
{
VisualElement bg = this.Q(name: "coolDownBackground");
if (fill <= 0)
{
bg.visible = false;
return;
}
if (!bg.visible) bg.visible = true;
bg.style.height = bg.parent.worldBound.height * math.clamp(fill, 0, 1);
}
private void SetTime(float seconds)
{
Label label = this.Q<Label>(name: "coolDownTime");
if (seconds <= 0)
{
label.visible = false;
return;
}
if (!label.visible) label.visible = true;
seconds = math.round(seconds * 10) / 10;
label.text = $"{seconds}s";
}
private void SetIcon(Texture2D icon)
{
this.Q(name = "Icon").style.backgroundImage = new StyleBackground(icon);
}
}
}
The UXML associated with it :
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:VisualElement name="Shape" class="abolitySlotShape">
<Style src="AbilityUIElement.uss" />
<ui:VisualElement name="Icon" class="abilityIcon" style="position: absolute;" />
<ui:VisualElement name="coolDown" style="width: 100%; height: 100%;">
<ui:VisualElement name="filler" style="background-color: rgba(0, 0, 0, 0); flex-grow: 1;" />
<ui:VisualElement name="coolDownBackground" style="background-color: rgba(255, 0, 0, 0.6); flex-grow: 0; height: 10px;" />
</ui:VisualElement>
<ui:Label text="0.5" name="coolDownTime" class="coolDownTimeText" />
<ui:Button text="Button" class="abilityButton" />
</ui:VisualElement>
</ui:UXML>
and the USS:
.abolitySlotShape {
width: 128px;
height: 128px;
background-color: rgba(128, 128, 128, 0.5);
overflow: hidden;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
border-left-width: 2px;
border-right-width: 2px;
border-top-width: 2px;
border-bottom-width: 2px;
border-left-color: rgb(255, 255, 255);
border-right-color: rgb(255, 255, 255);
border-top-color: rgb(255, 255, 255);
border-bottom-color: rgb(255, 255, 255);
}
.abilityIcon {
background-image: url('/Packages/wayn-group.mgm.ability/Runtime/UI/sword.png');
width: 100%;
height: 100%;
opacity: 0.33;
display: flex;
}
.coolDownTimeText {
-unity-font-style: bold;
font-size: 60px;
color: rgb(0, 255, 255);
-unity-text-align: middle-center;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.abilityButton {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
flex-grow: 1;
flex-shrink: 1;
align-items: center;
justify-content: center;
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
padding-bottom: 0;
color: rgba(0, 0, 0, 0);
background-color: rgba(0, 0, 0, 0);
border-left-width: 0;
border-right-width: 0;
border-top-width: 0;
border-bottom-width: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-left-color: rgba(0, 0, 0, 0);
border-right-color: rgba(0, 0, 0, 0);
border-top-color: rgba(0, 0, 0, 0);
border-bottom-color: rgba(0, 0, 0, 0);
}
Also in attachement my full package (runing in 2020.2.0a15)
If any one has any idea of what I’m doing wrong I’d welcome the help.
6110165–665249–wayn-group.mgm.ability-0.4.0-preview.tgz.zip (216 KB)