Adam's Lair Forum

game development and casual madness
It is currently 2020/01/28, 18:16

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2, 3  Next
Author Message
PostPosted: 2015/01/27, 12:01 
Novice Member
Novice Member

Joined: 2015/01/27, 11:47
Posts: 11
Location: Germany
Role: Hobbyist
Hello.

Ive just discovered Duality and I am amazed. I am just an inch and some hours away from switching to Duality (I am currently working with Unity2D).

I have just one large question left:

What is the recommended (if any) approach to handle GUI? In my former project this has always been a large hassle. How do the Duality-Pros^^ handle their GUI?


Ive found one possibility (FrozenCore), is this recommended? Is there an efficent way of building a GUI in Vanilla Duality? (I am basically C# / Unity Programmer/Designer/Hobbyist, I dont have much experience building APIs from scratch)

Thanks for your help!


Top
 Profile  
 
PostPosted: 2015/01/27, 12:18 
Forum Addict
Forum Addict
User avatar

Joined: 2013/09/19, 14:31
Posts: 883
Location: Italy
Role: Hobbyist
Welcome to the forums!

As the owner (developer? code monkey? :mrgreen: ) of FrozenCore I want to be clear about what it does: it does something, but definitely not everything you could expect from a GUI system such as, let's say, WinForms.

It provides some Widgets that you can place around and skin, and some basic-level functionalities.
It might be a little over-sensitive to sudden changes in the editor (sometimes you have to click "play" to see the effects of your changes, because some values are recalculated only when necessary and need a new frame to tick).
It might not have everything you need, behave differently from what you expect, and basically not work with all possible combination (it's something I am doing based mostly on MY requirements.. until I don't find something wrong, I might not know it's there :D)
It might crash :mrgreen: (for what I did until now, it doesn't.. but see the previous point)

On the other hand, I am always happy to have people check it out and give me feedback on what is not working and what could be added or modified.


In the end, I guess it all depends on what do you need from a GUI: it is just to present data to the player (in which case you could even roll out your custom Components), or do you need some interaction (maybe you could check out my library then, even if only to get an idea about how I did it)?

Feel free to ask if you need more help!
Good luck :mrgreen:

_________________
Come on Duality's Discord channel. We have cookies! :mrgreen:


Top
 Profile  
 
PostPosted: 2015/01/27, 12:27 
Novice Member
Novice Member

Joined: 2015/01/27, 11:47
Posts: 11
Location: Germany
Role: Hobbyist
Hmm... thanks for the quick answer.

I will begin with recreating the tutorial project and work through your breakout tutorial (by the way thanks for that, too).

So in my learning phase I wont need much GUI functionality.
Later though... i'll always need a combination of the following:

- simple ingame menus (start, maybe profile/save slot, settings, quit)
- some kind of HUD (Radar, crosshair, info boxes)
- some kind of workshop / lab windows, where you can drag/drop sprites into slots (probably the most complex)

Id try to recreate GUI functionality with ingame-collider-boxes and sprites (I am used to that), but I dont think thats recommended?

Thanks again!


Top
 Profile  
 
PostPosted: 2015/01/27, 12:39 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
Hello openend and welcome :)

great to hear you're considering Duality! Right now, UI is really an open field in Duality and there is no definite solution. It depends on what exactly you need, and which way of working you prefer.

You have already mentioned FrozenCore and its UI module - this is definitely one option, and SirePi can tell you far more about this than I could. (Edit: Turns out, he was faster than me. :mrgreen: See his response!)

A different, more code-centric approach would be to instead implement a single Component that handles rendering and updating of all UI by itself. For small things like a simple HUD, this is often the easiest / fastest option. If you need an example on how to draw stuff directly on the screen, you can take a look at the HUD code from the example project.

Here's a minimal setup you could use to experiment a little:
Code:
[Serializable]
public class SomeInterfaceExample : Component, ICmpRenderer, ICmpUpdatable
{
   [NonSerialized]
   private CanvasBuffer buffer = null;

   float ICmpRenderer.BoundRadius
   {
      get { return float.MaxValue; }
   }

   void ICmpUpdatable.Update()
   {
      // Update your UI stuff here
   }
   
   bool ICmpRenderer.IsVisible(IDrawDevice device)
   {
      // Only render when in screen overlay mode and the visibility mask is non-empty.
      return
         (device.VisibilityMask & VisibilityFlag.AllGroups) != VisibilityFlag.None &&
         (device.VisibilityMask & VisibilityFlag.ScreenOverlay) != VisibilityFlag.None;
   }
   void ICmpRenderer.Draw(IDrawDevice device)
   {
      // Create a buffer to cache and re-use vertices. Not required, but will boost performance.
      if (this.buffer == null) this.buffer = new CanvasBuffer();

      // Create a Canvas to auto-generate vertices from high-level drawing commands.
      Canvas canvas = new Canvas(device, this.buffer);
      canvas.State.SetMaterial(new BatchInfo(DrawTechnique.Alpha, ColorRgba.White));
      
      // Draw a circle at the mouse position
      canvas.FillCircle(DualityApp.Mouse.X, DualityApp.Mouse.Y, 2.0f);
   }
}


For a simple HUD and simple menus I'm mostly using something like the above.

Edit2:
openend wrote:
Id try to recreate GUI functionality with ingame-collider-boxes and sprites (I am used to that), but I dont think thats recommended?

Nope. Wouldn't recommend that - RigidBodies are for ingame physics, not for UI stuff. :) You can pull something together like this, but other options will likely be easier to maintain.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
PostPosted: 2015/01/27, 12:54 
Novice Member
Novice Member

Joined: 2015/01/27, 11:47
Posts: 11
Location: Germany
Role: Hobbyist
Nice.

I'll just check out the dual stick shooter example afterwards.
My first (own) project is actually a space-shooter, so it should be quite useful.

Thanks for your help!


Top
 Profile  
 
PostPosted: 2015/01/27, 13:01 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
openend wrote:
Nice.

I'll just check out the dual stick shooter example afterwards.
My first (own) project is actually a space-shooter, so it should be quite useful.


A note on this: You don't actually need to get it from the source code repo of Duality. Open the package Manager using File / Manage Packages..., select the online view and install the example. All the content and source code will be added to your project automatically.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
PostPosted: 2015/01/27, 13:52 
Novice Member
Novice Member

Joined: 2015/01/27, 11:47
Posts: 11
Location: Germany
Role: Hobbyist
Thanks.

Just another quick question:
I may be wrong but the HUD in the example project seems to be non-interactive. Is there a simple way to check for click events (or more or less construct buttons/drag-drog functionality) within the engine?


Top
 Profile  
 
PostPosted: 2015/01/27, 14:13 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
Yup, this is correct - the sample HUD isn't interactive at all :) That's why the above sample excerpt also implements ICmpUpdatable in order to receive per-frame updates:

Code:
void ICmpUpdatable.Update()
{
   // Update your UI stuff here
}


You can use this to check for user input and mouse interaction, for which you can check using DualityApp.Mouse and DualityApp.Keyboard. They have a lot of useful properties, like checking for button presses and clicks, as well as mouse position and speed.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
PostPosted: 2015/01/27, 15:18 
Novice Member
Novice Member

Joined: 2015/01/27, 11:47
Posts: 11
Location: Germany
Role: Hobbyist
Ok thanks, thats even better. I guess there is an API documentation somewhere :)

Ill just wait now, until I can install the engine (~5 hours). Thanks for the help so far.


Top
 Profile  
 
PostPosted: 2015/01/28, 20:30 
Member
Member

Joined: 2013/05/20, 17:19
Posts: 77
Role: Professional
We made a component for our game, cunningly called Menu, that lets you design all of your menus in world space. We found it much easier to work that way than to build every menu in screen space, as you can't click and drag screen space objects in the editor, or you couldn't way back when. That may have changed since?

Drop one of these components in your scene and then add child game objects to it. When the menu is made active at runtime, it automatically switches itself and all its children to the screen space visibility group, and rescales everything to whatever resolution the game is running in, so you can design your menu once for any resolution and not have to worry about it again:)

Here's the code:

Code:
[Serializable]
   [RequiredComponent(typeof(Transform))]
   [EditorHintCategory("GUI")]
   public class Menu : Component, ICmpInitializable, ICmpUpdatable
   {
      public const float DesignResolutionX = 1920;
      public const float DesignResolutionY = 1080;

      public VisibilityFlag VisibilityGroup { get; set; }
      public bool Enabled { get; set; }
      public Menu PreviousMenu { get; set; }

      public Menu()
      {
         VisibilityGroup = VisibilityFlag.Group30 | VisibilityFlag.AllFlags;
      }

      public void OnInit(InitContext context)
      {
         if (DualityApp.ExecContext != DualityApp.ExecutionContext.Game)
            return;

         if(context != InitContext.Activate)
            return;

         SetChildrensVisibilityGroup();

         UpdateResolution();

         Scene.GameObjectParentChanged += OnGameObjectParentChanged;
      }

      public void OnUpdate()
      {
         UpdateResolution();
      }

      public void OnShutdown(ShutdownContext context)
      {
         if (context == ShutdownContext.Deactivate)
            Scene.GameObjectParentChanged -= OnGameObjectParentChanged;
      }

      public void ShowMenu()
      {
         GameObj.Active = true;
         UpdateResolution();
      }

      private void UpdateResolution()
      {
         var scale = CalculateScale();
         if (GameObj.Transform.Scale != scale)
            GameObj.Transform.Scale = scale;

         var offset = (DualityApp.TargetResolution.Y - (scale * DesignResolutionY)) / 2;
         if (GameObj.Transform.Pos.Y != offset)
            GameObj.Transform.Pos = new Vector3(0, offset, GameObj.Transform.Pos.Z);
      }

      private static float CalculateScale()
      {
         return DualityApp.TargetResolution.X / DesignResolutionX;
      }

      private void SetChildrensVisibilityGroup()
      {
         foreach (var renderer in GameObj.GetComponentsDeep<Renderer>())
         {
            renderer.VisibilityGroup = VisibilityGroup;
         }
      }

      private void OnGameObjectParentChanged(object sender, GameObjectParentChangedEventArgs e)
      {
         var parent = e.NewParent;

         while (parent != null)
         {
            if (parent == GameObj)
            {
               SetChildrensVisibilityGroup();
               if (e.Object.Transform != null && e.Object.Parent != null && e.Object.Parent.Transform != null)
                  e.Object.Transform.Scale *= e.Object.Parent.Transform.Scale;

               break;
            }

            parent = parent.Parent;
         }
      }
   }


We also have a handy cam view layer that will show you the title safe area and various aspect ratios. You'll need to put this code in an editor plugin:

Code:
public class MenuCamViewLayer : CamViewLayer
   {
      private BatchInfo _16_9Material;
      private BatchInfo _16_10Material;
      private BatchInfo _4_3Material;
      private BatchInfo _titleSafeMaterial;

      public override string LayerName
      {
         get { return "Menu view layer"; }
      }

      public override string LayerDesc
      {
         get { return "Displays outlines for what will be visible on screens with different aspect ratios as well as the title safe area"; }
      }

      public MenuCamViewLayer()
      {
         _16_9Material = new BatchInfo(DrawTechnique.Mask, new ColorRgba(255, 255, 0));
         _16_10Material = new BatchInfo(DrawTechnique.Mask, new ColorRgba(0, 255, 0));
         _4_3Material = new BatchInfo(DrawTechnique.Mask, new ColorRgba(0, 0, 255));
         _titleSafeMaterial = new BatchInfo(DrawTechnique.Mask, new ColorRgba(255, 0, 0));
      }

      protected override void OnCollectDrawcalls(Canvas canvas)
      {
         DrawMenuRegions(canvas);
      }

      private void DrawMenuRegions(Canvas canvas)
      {
         var menus = Scene.Current.FindComponents<Menu>();
         
         foreach (var menu in menus)
         {
            var transform = menu.GameObj.Transform;
            var x = transform.Pos.X;
            var y = transform.Pos.Y;
            var width = Menu.DesignResolutionX * transform.Scale;
            var height = Menu.DesignResolutionY * transform.Scale;

            DrawScreenSpaceRepresentation(canvas, x, y, width, height, 1, 9f / 9f, _16_9Material, "16:9");
            DrawScreenSpaceRepresentation(canvas, x, y, width, height, 1, 10f / 9f, _16_10Material, "16:10");
            DrawScreenSpaceRepresentation(canvas, x, y, width, height, 1, 12f / 9f, _4_3Material, "4:3");
            DrawScreenSpaceRepresentation(canvas, x, y, width, height, 0.8f, 0.8f, _titleSafeMaterial, "Title Safe Region");
         }
      }

      private void DrawScreenSpaceRepresentation(Canvas canvas, float x, float y, float width, float height, float scaleX, float scaleY, BatchInfo colour, string text)
      {
         var scaledHeight = height * scaleY;
         var adjustedY = y + (height - scaledHeight) / 2;

         var scaledWidth = width * scaleX;
         var adjustedX = x + (width - scaledWidth) / 2;

         canvas.State.SetMaterial(colour);
         canvas.DrawRect(adjustedX, adjustedY, scaledWidth, scaledHeight);
         canvas.DrawText(text, adjustedX, adjustedY);
      }

   }


We actually do use rigid bodies for our clickable areas, but only because there was a convenient ready made editor in the rigid body editor. It works ok, and I barely even cringe at all when I look at it now:) If we were doing that again we'd probably make a dedicated editor for drawing menu shape clickable areas.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2, 3  Next

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group