Re: Unit health bars with NoesisGUI
Awesome, thanks. I'm trying that first suggestion - always learning more about the options! Two questions:1. Canvas positions the top-left corner of child elements by default, so if you want to center them you can create alternative attached properties that calculate the center position and set that instead:Code: Select allpublic static class CenteredCanvas { public static float GetX(FrameworkElement element) { return (float)element.GetValue(XProperty); } public static void SetX(FrameworkElement element, float value) { element.SetValue(XProperty, value); } public static readonly DependencyProperty XProperty = DependencyProperty.RegisterAttached( "X", typeof(float), typeof(CenteredCanvas), new PropertyMetadata(0.0f, OnPosChanged)); public static float GetY(FrameworkElement element) { return (float)element.GetValue(YProperty); } public static void SetY(FrameworkElement element, float value) { element.SetValue(YProperty, value); } public static readonly DependencyProperty YProperty = DependencyProperty.RegisterAttached( "Y", typeof(float), typeof(CenteredCanvas), new PropertyMetadata(0.0f, OnPosChanged)); private static void OnPosChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { FrameworkElement element = d as FrameworkElement; if (element != null) { element.SizeChanged -= OnElementSizeChanged; element.SizeChanged += OnElementSizeChanged; UpdatePos(element); } } private static void OnElementSizeChanged(object sender, SizeChangedEventArgs e) { UpdatePos((FrameworkElement)sender); } private static void UpdatePos(FrameworkElement element) { Canvas.SetLeft(element, GetX(element) - element.ActualWidth * 0.5); Canvas.SetTop(element, GetY(element) - element.ActualHeight * 0.5); } }
2. Is this Unity? We update our layout during MonoBehavior.LateUpdate() of the NoesisView component so you should update your viewmodels before that in order to reflect those changes in the same frame.Code: Select all<ItemsControl.ItemContainerStyle> <Style TargetType="{x:Type ContentPresenter}"> <Setter Property="CenteredCanvas.X" Value="{Binding ScreenPosX}"/> <Setter Property="CenteredCanvas.Y" Value="{Binding ScreenPosY}"/> </Style> </ItemsControl.ItemContainerStyle>
1. What is the purpose of unbinding and then rebinding the OnElementSizeChanged call?
2. How would one translate the OnPosChanged function to C++? Here is my attempt, but struggling to get the event binding right for a static member function. I suppose it could be a free function and is static just due to C# -ness? EDIT: Same problem arises with a free function. It seems to be deducing to the FromFunctor version, but it's a free/static function.
Code: Select all
class CenteredCanvas : public Noesis::BaseComponent
{
public:
static float GetX(Noesis::FrameworkElement* element) {
return element->GetValue<float>(XProperty);
}
static void SetX(Noesis::FrameworkElement* element, float value) {
element->SetValue<float>(XProperty, value);
}
inline static const Noesis::DependencyProperty* XProperty;
static float GetY(Noesis::FrameworkElement* element) {
return element->GetValue<float>(YProperty);
}
static void SetY(Noesis::FrameworkElement* element, float value) {
element->SetValue<float>(YProperty, value);
}
inline static const Noesis::DependencyProperty* YProperty;
using OnElementSizeChangedDelegate = Noesis::Delegate<void(Noesis::BaseComponent* sender, Noesis::SizeChangedEventArgs e)>;
static void OnPosChanged(Noesis::BaseComponent* obj, Noesis::DependencyPropertyChangedEventArgs e)
{
if (Noesis::FrameworkElement* element = Noesis::DynamicCast<Noesis::FrameworkElement*>(obj))
{
element->SizeChanged() -= OnElementSizeChangedDelegate(&CenteredCanvas::OnElementSizeChanged);
element->SizeChanged() += OnElementSizeChangedDelegate(&CenteredCanvas::OnElementSizeChanged);
UpdatePos(element);
}
}
static void OnElementSizeChanged(Noesis::BaseComponent* sender, Noesis::SizeChangedEventArgs e)
{
UpdatePos(Noesis::DynamicCast<Noesis::FrameworkElement*>(sender));
}
static void UpdatePos(Noesis::FrameworkElement* element)
{
Noesis::Canvas::SetLeft(element, GetX(element) - element->GetActualWidth() * 0.5f);
Noesis::Canvas::SetTop(element, GetY(element) - element->GetActualHeight() * 0.5f);
}
NS_IMPLEMENT_INLINE_REFLECTION(CenteredCanvas, BaseComponent, "rtg.Controls.CenteredCanvas")
{
Noesis::DependencyData* data = NsMeta<Noesis::DependencyData>(Noesis::TypeOf<ShipListEntryView>());
data->RegisterProperty<float>(XProperty, "X",
Noesis::PropertyMetadata::Create(0.0f, &CenteredCanvas::OnPosChanged));
data->RegisterProperty<float>(YProperty, "Y",
Noesis::PropertyMetadata::Create(0.0f, &CenteredCanvas::OnPosChanged));
}
};
Code: Select all
void OnElementSizeChanged(Noesis::BaseComponent* sender, const Noesis::SizeChangedEventArgs& e);
class CenteredCanvas : public Noesis::BaseComponent
{
public:
static float GetX(Noesis::FrameworkElement* element) {
return element->GetValue<float>(XProperty);
}
static void SetX(Noesis::FrameworkElement* element, float value) {
element->SetValue<float>(XProperty, value);
}
inline static const Noesis::DependencyProperty* XProperty;
static float GetY(Noesis::FrameworkElement* element) {
return element->GetValue<float>(YProperty);
}
static void SetY(Noesis::FrameworkElement* element, float value) {
element->SetValue<float>(YProperty, value);
}
inline static const Noesis::DependencyProperty* YProperty;
static void OnPosChanged(Noesis::DependencyObject* d, const Noesis::DependencyPropertyChangedEventArgs& e)
{
if (Noesis::FrameworkElement* element = Noesis::DynamicCast<Noesis::FrameworkElement*>(d))
{
auto sizeFunc = [](Noesis::BaseComponent* component, const Noesis::SizeChangedEventArgs& e) {
OnElementSizeChanged(component, e);
};
element->SizeChanged() -= sizeFunc;
element->SizeChanged() += sizeFunc;
UpdatePos(element);
}
}
static void UpdatePos(Noesis::FrameworkElement* element)
{
Noesis::Canvas::SetLeft(element, GetX(element) - element->GetActualWidth() * 0.5f);
Noesis::Canvas::SetTop(element, GetY(element) - element->GetActualHeight() * 0.5f);
}
NS_IMPLEMENT_INLINE_REFLECTION(CenteredCanvas, BaseComponent, "rtg.Controls.CenteredCanvas")
{
Noesis::DependencyData* data = NsMeta<Noesis::DependencyData>(Noesis::TypeOf<ShipListEntryView>());
data->RegisterProperty<float>(XProperty, "X",
Noesis::PropertyMetadata::Create(0.0f, &CenteredCanvas::OnPosChanged));
data->RegisterProperty<float>(YProperty, "Y",
Noesis::PropertyMetadata::Create(0.0f, &CenteredCanvas::OnPosChanged));
}
};
void OnElementSizeChanged(Noesis::BaseComponent* sender, const Noesis::SizeChangedEventArgs& e)
{
CenteredCanvas::UpdatePos(Noesis::DynamicCast<Noesis::FrameworkElement*>(sender));
}
And as the above may suggest; no, not Unity here - mostly custom C++. Your reply re. Unity and frame business made me re-evaluate how I ordered some things in the engine and I managed to fix the latency.
And lastly; anything you'd suggest looking into to have the UI elements scale based on distance of the unit they represent to the camera? Always having the same space on screen will, as you zoom out, make the UI elements look larger and larger relative to the world. But I'm not looking to put them in-world, at least not in terms of e.g. rotation, so no need for a 3D widget. I was thinking of stashing some rough 'distance to camera' type of value in its ViewModel which is used in the bound source for the ItemsControl, and maybe just calculating some kind of size/scaling relationship to distance? EDIT: didn't think about this until I saw it, but... overlapping icons. I guess intuition would suggest the closer units' icons should be on top of the further units' icons (although the overlap should be avoided through design etc). I wonder if that's an argument for some kind of in-world/3D UI solution instead.
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Unit health bars with NoesisGUI
1. I was unbinding first to make sure I only register once to the element's SizeChanged event.
2. In C++ a property changed function should be like this:
2. In C++ a property changed function should be like this:
Code: Select all
static void OnPosChanged(Noesis::BaseComponent* obj, const Noesis::DependencyPropertyChangedEventArgs& e)
{
if (Noesis::FrameworkElement* element = Noesis::DynamicCast<Noesis::FrameworkElement*>(obj))
{
element->SizeChanged() -= OnElementSizeChanged;
element->SizeChanged() += OnElementSizeChanged;
UpdatePos(element);
}
}
Re: Unit health bars with NoesisGUI
Coming back to this years later - I ended up using it regularly, ignoring the warnings it produced, but I'm trying to apply some downward pressure on my Noesis logs.1. I was unbinding first to make sure I only register once to the element's SizeChanged event.
2. In C++ a property changed function should be like this:Code: Select allstatic void OnPosChanged(Noesis::BaseComponent* obj, const Noesis::DependencyPropertyChangedEventArgs& e) { if (Noesis::FrameworkElement* element = Noesis::DynamicCast<Noesis::FrameworkElement*>(obj)) { element->SizeChanged() -= OnElementSizeChanged; element->SizeChanged() += OnElementSizeChanged; UpdatePos(element); } }
The '-=' operator triggers 'Event 'SizeChanged' has no handlers registered' the first time through, but there's no way to check if there are any existing usages of the UIElement::RoutedEvent_. Any advice for using the above function without causing the warnings? Can be quite spammy when spawning a lot of icons.
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Unit health bars with NoesisGUI
I verified that WPF does not raise any error or warning when removing a handler from an event that was not previously registered, so this is wrong in Noesis. Could you please report it in our bugtracker, we shouldn't show any error message in that case either.
In the meantime one simple workaround would be to filter out those error messages in the LogHandler if you have your own set.
Or define another attached property in the CenteredCanvas, a bool that indicates if SizeChanged event has been registered:
In the meantime one simple workaround would be to filter out those error messages in the LogHandler if you have your own set.
Or define another attached property in the CenteredCanvas, a bool that indicates if SizeChanged event has been registered:
Code: Select all
public static bool GetIsRegistered(FrameworkElement element) { return (bool)element.GetValue(IsRegisteredProperty); }
public static void SetIsRegistered(FrameworkElement element, bool value) { element.SetValue(IsRegisteredProperty, value); }
public static readonly DependencyProperty IsRegisteredProperty = DependencyProperty.RegisterAttached(
"IsRegistered", typeof(bool), typeof(CenteredCanvas), new PropertyMetadata(false));
private static void OnPosChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = d as FrameworkElement;
if (element != null)
{
if (!GetIsRegistered(element))
{
element.SizeChanged += OnElementSizeChanged;
SetIsRegistered(element, true);
}
UpdatePos(element);
}
}
Re: Unit health bars with NoesisGUI
Fixed, thanks!
Who is online
Users browsing this forum: Semrush [Bot] and 29 guests