Announcement

Collapse
No announcement yet.

slate tutorials from Wraiyth

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    slate tutorials from Wraiyth

    Not mine, but they helped me a ton and I would hate to have them go to waste. All credit goes to Wraiyth for these.
    Start off by creating two files in your Private directory. Slate widgets are generally prefixed with S, so we'll call is SMyGameSlateHUDWidget. Create a header and cpp file with that name (SMyGameSlateHUDWidget.cpp and SMyGameSlateHUDWidget.h)

    Header code:

    [CODE]
    #pragma once

    #include "Slate.h"

    class SMyGameSlateHUDWidget : public SCompoundWidget
    {
    SLATE_BEGIN_ARGS(SMyGameSlateHUDWidget)
    : _OwnerHUD()
    {}

    SLATE_ARGUMENT(TWeakObjectPtr<AShooterHUD>, OwnerHUD)

    SLATE_END_ARGS()

    /** Needed for every widget */
    void Construct(const FArguments& InArgs);

    /** returns a string of information to display */
    FString GetSomeString() const;

    private:
    /** Pointer to our parent HUD */
    TWeakObjectPtr<class AShooterHUD> OwnerHUD;
    };
    [\CODE]
    There's a few important parts here:

    Inheriting from SCompoundWidget allows us to create our own Widget that contains other multiple widgets.
    All Slate widgets need to include arguments sections. This is where you can initialize properties for this widget through Slate's syntax.
    Every Slate widget contains a Construction function called Construct.

    CPP file:
    [Code]
    #include "ShooterGame.h"
    #include "SMyGameSlateHUDWidget.h"

    void SMyGameSlateHUDWidget::Construct(const FArguments& InArgs)
    {
    OwnerHUD = InArgs._OwnerHUD;

    ChildSlot
    .VAlign(VAlign_Fill)
    .HAlign(HAlign_Fill)
    [
    SNew(SOverlay)
    +SOverlay::Slot()
    .VAlign(VAlign_Top)
    .HAlign(HAlign_Center)
    [
    SNew(STextBlock)
    .ShadowColorAndOpacity(FLinearColor::Black)
    .ColorAndOpacity(FLinearColor::White)
    .ShadowOffset(FIntPoint(-1,1))
    .Font(FSlateStyle::GetFontStyle("NormalFont"))
    .Text(this, &SMyGameSlateHUDWidget::GetSomeString)
    ]
    ];
    }

    FString SMyGameSlateHUDWidget::GetSomeString() const
    {
    return FString(TEXT("Hello!"));
    }[\Code]
    This part is pretty straightforward! We add a new SOverlay to this widget and inside that overlay we create an STextBlock with a variety of properties. Note the bind to GetSomeString for the text for the Widget. If we wanted that text to be conditional or changing (say a Round Timer) then we can perform functionality inside the GetSomeString function to return the text.
    SNew will create a new Widget, and then all prpoerties of that widget are accessed and set afterwards.

    Thats the entire widget itself! The next step is to actually add it in-game.
    We do this inside the HUD.

    Inside your HUD's header file, add a TSharedPtr of your Widget class:
    Code:
        /** HUD menu widget */
        TSharedPtr<class SMyGameSlateHUDWidget> MyHUDMenuWidget;
    Finally, in DrawHUD(), we need to conditionally construct the Widget and add it to the Viewport
    Code:
        if ( !MyHUDMenuWidget.IsValid() )
        {
            SAssignNew(MyHUDMenuWidget,SMyGameSlateHUDWidget)
                .OwnerHUD(this);
    
            if ( MyHUDMenuWidget.IsValid() )
            {
                GEngine->GameViewport->AddViewportWidgetContent(
                    SNew(SWeakWidget)
                    .PossiblyNullContent(MyHUDMenuWidget.ToSharedRef())
                    );
            }
        }
    The important part here is SAssignNew. As opposed to SNew which creates a widget, SAssignNew creates a widget and lets you assign it to a variable for later use. In this case the later use is adding it in AddViewportWidgetContent.

    Fire up your game and you should have the word "Hello" printed in the top middle of the screen! Note that Slate doesn't work in Play-In-Editor - you need to launch the game in a separate window to see the results.

    Next tutorial I'll probably cover Slate Styles and adding some additional controls to a basic HUD widget. Menus will come later

    Slate Styles:

    Start off by creating two files in your Private directory - MyGameStyle.cpp and MyGameStyle.h

    MyGameStyle.h:
    Code:
    #pragma once
    
    #include "Slate.h"
    
    class MyGameStyle
    {
    public:
        /** overrides current slate style */
        static void ApplyStyle();
    
        /** reloads textures used by slate renderer */
        static void ReloadTextures();
    };
    MyGameStyle.cpp:

    Code:
    #include "ShooterGame.h"
    #include "MyGameStyle.h"
    
    void MyGameStyle::ApplyStyle()
    {
        FSlateStyle& Style = FSlateStyle::GetInstance();
    
        //Fonts
        Style.Set("MyGame.HUDFont", FSlateFontInfo(TEXT("Roboto-Black"), 36));
    
    
    }
    
    void MyGameStyle::ReloadTextures()
    {
        FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
    }
    The important part is the ApplyStyle function. This is where you set your styles for the various Slate controls. In this case we're setting a Style called MyGame.HUDFont and setting it to use Roboto-Black font with a font size of 36. This function is where you'd add other styles. For instance, you could set a color as such:
    Style.Set("MyGame.SomeFontColor", FLinearColor(0.6f,1.0f,0.6f));

    Or an image like such:
    Style.Set("ShooterGame.MainMenu.BackgroundImage", new FSlateImageBrush(TEXT("texture://Game/UI/MainMenu/Background.Background"), ScreenSize));

    To use this style, you first need to call ApplyStyle in the context that you are going to use it. In this case, its in the Construct function of our widget.
    MyGameStyle::ApplyStyle();

    You can then replace the Font in the STextBlock widget to reference your style:
    .Font(FSlateStyle::GetFontStyle("MyGame.HUDFont"))

    Done! Fire up the game and your text up the top will be larger.
    Last edited by iLLo; 03-19-2014, 02:55 PM.

    #2
    Sorry for the dumb question, but where can I download/find info about Slate ?

    Comment


      #3
      The documentation will likely be updated in due time, but it appears to have been left off as of now.
      About Me | JavaScript and UE4 | Exodus | Code Notes

      Comment


        #4
        Looks like you have to open the plugin manager and enable Slate before to start to use it.
        My projects : Jurassic-life MESA Unreal Engine 4 Developers France Steam Group Unreal Engine 4 French tutorials

        Comment


          #5
          Thanks for bringing this post over. Sometime in the next few days, I'll move this to the Tutorials section of the Wiki
          Check out my blog & website at http://www.wraiyth.com - for game development musings.
          Or feel free to follow me on Twitter - https://twitter.com/wraiyth

          Technical Director at Pub Games
          Check out our Unreal Engine games & video tutorials!
          http://www.pubgames.net.au

          Comment


            #6
            Originally posted by Ad3ViLl View Post
            Sorry for the dumb question, but where can I download/find info about Slate ?
            I'm asking myself the same question! Anyone?

            [EDIT] Nevermind. I found it: https://docs.unrealengine.com/latest...ure/index.html
            Last edited by EvilCleric; 03-20-2014, 07:22 AM.
            "I have harnessed the shadows that stride from world to world to sow death and madness."

            Comment


              #7
              The best way to learn Slate right now is by example. To find examples, you can use the widget reflector. In the editor, go to Window -> Developer Tools -> Widget Reflector. From a running game, hit ~ to bring up the console and type WidgetReflector. Now you can press the "Pick Widget" button and point at any piece of UI to inspect it. Press ESC to stop picking. Notice that there is a file name and line number listed for every Widget. You can now look at the code that created most widgets.

              For the simplest example of writing UI, take a look at the SlateViewer program. It has a TestSuite with many examples of straight-forward Slate code that is generally unobstructed by the complexities of various use cases.

              Comment


                #8
                I haven't started with Slate yet, but I just wanted to say, if this tutorial is worthy, add it to the wiki https://wiki.unrealengine.com/Category:Tutorials

                Comment


                  #9
                  I had to change a few parameters, I got everything to build, but I'm getting linker errors. Hmm, guess I'll have to take a look at the pre-built strategy game.

                  Code:
                  //MyUIWidget.h
                  
                  #pragma once
                  #include "MyHUD.h"
                  #include "Slate.h"
                  
                  
                  
                  
                  class SMyUIWidget : public SCompoundWidget
                  {
                  
                  SLATE_BEGIN_ARGS(SMyUIWidget)
                  : _OwnerHUD()
                  {}
                  
                  SLATE_ARGUMENT(TWeakObjectPtr<AMyHUD>, OwnerHUD)
                  
                  SLATE_END_ARGS()
                  
                  /** Needed for every widget */
                  void Construct(const FArguments& InArgs);
                  
                  /** returns a string of information to display */
                  FString GetSomeString() const;
                  
                  private:
                  /** Pointer to our parent HUD */
                  TWeakObjectPtr<class AMyHUD> OwnerHUD;
                  };
                  Code:
                  //MyUIWidget.cpp
                  
                  #include "MyProject.h"
                  #include "MyUIWidget.h"
                  
                  void SMyUIWidget::Construct(const FArguments& InArgs)
                  {
                  OwnerHUD = InArgs._OwnerHUD;
                  
                  ChildSlot
                  .VAlign(VAlign_Fill)
                  .HAlign(HAlign_Fill)
                  [
                  SNew(SOverlay)
                  +SOverlay::Slot()
                  .VAlign(VAlign_Top)
                  .HAlign(HAlign_Center)
                  [
                  SNew(STextBlock)
                  .ShadowColorAndOpacity(FLinearColor::Black)
                  .ColorAndOpacity(FLinearColor::White)
                  .ShadowOffset(FIntPoint(-1,1))
                  .Font(FSlateFontInfo("Veranda", 16))
                  .Text(FText::FromString("asdf"))
                  ]
                  ];
                  }
                  
                  FString SMyUIWidget::GetSomeString() const
                  {
                  return FString(TEXT("Hello!"));
                  }
                  Last edited by User-658380556; 03-22-2014, 11:01 AM.

                  Comment


                    #10
                    I've gotten a little further, but still getting errors and now I have no idea how to fix them. The documentation is no help, it needs to provide complete examples of working code (aka idioms) in a start-to-finish manner, not random broken function calls with ambiguous explanations. As it is, the only way to understand the documentation would be to learn Slate, at which point you would want a reference manual not a "getting started" guide. Anyway, here's my code, I modified the tutorial above to get it down to a single linker error. Once I get it to build/work I'll spend the time going through it line by line itt.

                    Code:
                    1>  MyActor.cpp
                    1>C:\Users\Trae\Documents\Unreal Projects\MyProject\Source\MyProject\MyUIWidget.cpp(8): error C2220: warning treated as error - no 'object' file generated
                    1>C:\Users\Trae\Documents\Unreal Projects\MyProject\Source\MyProject\MyUIWidget.cpp(8): warning C4273: 'SMyUIWidget::Construct' : inconsistent dll linkage
                    1>          c:\users\trae\documents\unreal projects\myproject\source\myproject\MyUIWidget.h(21) : see previous definition of 'Construct'
                    1>  -------- End Detailed Actions Stats -----------------------------------------------------------
                    Code:
                    //MyUIWidget.h
                    
                    
                    
                    #include "MyHUD.h"
                    #include "Core.h"
                    #include "PrimitiveTypes.h"
                    #include "DrawElements.h"
                    #include "Events.h"
                    
                    
                    class SLATE_API SMyUIWidget :
                    	public SCompoundWidget
                    {
                    public:
                    	// Needed for every widget
                    	void Construct(
                    		const TAttribute<FString> & InToolTipText,
                    		const TSharedPtr<SToolTip> & InToolTip,
                    		const TAttribute< TOptional<EMouseCursor::Type> > & InCursor,
                    		const TAttribute<bool> & InEnabledState,
                    		const TAttribute<EVisibility> & InVisibility,
                    		const FName& InTag);
                    
                    	SLATE_BEGIN_ARGS(SMyUIWidget)
                    		: _OwnerHUD()
                    		{}
                    
                    	SLATE_ARGUMENT(TWeakObjectPtr<AMyHUD>, OwnerHUD)
                    
                    	SLATE_END_ARGS()
                    
                    private:
                    	// Pointer to our parent HUD
                    	TWeakObjectPtr<class AMyHUD> OwnerHUD;
                    };
                    Code:
                    //MyUIWidget.cpp
                    
                    #include "MyProject.h"
                    #include "MyUIWidget.h"
                    
                    
                    void SMyUIWidget::Construct(
                    	const TAttribute<FString> & InToolTipText,
                    	const TSharedPtr<SToolTip> & InToolTip,
                    	const TAttribute< TOptional<EMouseCursor::Type> > & InCursor,
                    	const TAttribute<bool> & InEnabledState,
                    	const TAttribute<EVisibility> & InVisibility,
                    	const FName& InTag)
                    {
                    
                    	/*
                    OwnerHUD = InArgs._OwnerHUD;
                    
                    ChildSlot
                    .VAlign(VAlign_Fill)
                    .HAlign(HAlign_Fill)
                    [
                    SNew(SOverlay)
                    +SOverlay::Slot()
                    .VAlign(VAlign_Top)
                    .HAlign(HAlign_Center)
                    [
                    SNew(STextBlock)
                    .ShadowColorAndOpacity(FLinearColor::Black)
                    .ColorAndOpacity(FLinearColor::White)
                    .ShadowOffset(FIntPoint(-1,1))
                    .Font(FSlateFontInfo("Veranda", 16))
                    .Text(FText::FromString("asdf"))
                    ]
                    ];
                    */
                    }
                    Here is the code for the previous definition.
                    Code:
                    	void Construct(
                    		const TAttribute<FString> & InToolTipText ,
                    		const TSharedPtr<SToolTip> & InToolTip ,
                    		const TAttribute< TOptional<EMouseCursor::Type> > & InCursor ,
                    		const TAttribute<bool> & InEnabledState ,
                    		const TAttribute<EVisibility> & InVisibility,
                    		const FName& InTag );
                    Last edited by User-658380556; 03-23-2014, 09:42 AM.

                    Comment


                      #11
                      PROGRESS

                      I've gotten a basic "skeleton widget" to compile. I don't think it will do anything, but here it is. Maybe from here the documentation will be more useful.

                      Code:
                      //MyUIWidget.h
                      
                      #pragma once
                      
                      #include "MyHUD.h"
                      #include "Slate.h"
                      
                      //class declare
                      class SMyUIWidget : public SCompoundWidget
                      {
                      public:
                      	SLATE_BEGIN_ARGS(SMyUIWidget)
                      	{}
                      
                      	SLATE_ARGUMENT(TWeakObjectPtr<AMyHUD>, OwnerHUD)
                      
                      	SLATE_END_ARGS()
                      
                      	/** needed for every widget */
                      	void Construct(const FArguments& InArgs);
                      
                      private:
                      	/** Pointer to our parent HUD */
                      	TWeakObjectPtr<class AMyHUD> OwnerHUD;
                      };
                      Code:
                      //MyUIWidget.cpp
                      
                      #include "MyProject.h"
                      #include "MyUIWidget.h"
                      
                      
                      void SMyUIWidget::Construct(const FArguments& InArgs)
                      {
                      	OwnerHUD = InArgs._OwnerHUD;
                      }

                      Comment


                        #12
                        This is some of the nastiest design I've ever worked with, it even makes oldschool Win32 programming seem easy/fun.

                        Just to fill in the color of an SNew argument, I've gone through about 6 classes/struts that do nothing but wrap functoin call and do half a dozen implicit conversions just to get a data structure Slate can work with. It's simply disgusting.

                        The only conclusion I can come to is that we're supposed to be designing 4-6 classes for each widget, because half a dozen half a dozen line function-arguments (a function that takes two arguments, which are two function calls, that take 2-3 arguments each, that take 2-3 function calls each and so forth..) aren't how good C++ is written.

                        For example,
                        Code:
                        .ColorAndOpacity(this, FLinearColor(FColor(255, 213, 160, GetCurrentOpacity() * 255))
                        is bad
                        Last edited by User-658380556; 03-23-2014, 01:57 PM.

                        Comment


                          #13
                          Originally posted by Bleakwise View Post
                          This is some of the nastiest design I've ever worked with, it even makes oldschool Win32 programming seem easy/fun.

                          Just to fill in the color of an SNew argument, I've gone through about 6 classes/struts that do nothing but wrap functoin call and do half a dozen implicit conversions just to get a data structure Slate can work with. It's simply disgusting.

                          The only conclusion I can come to is that we're supposed to be designing 4-6 classes for each widget, because half a dozen half a dozen line function-arguments (a function that takes two arguments, which are two function calls, that take 2-3 arguments each, that take 2-3 function calls each and so forth..) aren't how good C++ is written.

                          For example,
                          Code:
                          .ColorAndOpacity(this, FLinearColor(FColor(255, 213, 160, GetCurrentOpacity() * 255))
                          is bad

                          what exactly would you like to do? Do you want to have a color parameter in your widget? What kind of widget would you like to make? Specify your question to make it easier to help you.
                          Last edited by szyszek; 03-23-2014, 05:29 PM.

                          Comment


                            #14
                            Originally posted by szyszek View Post
                            what exactly would you like to do? Do you want to have a color parameter in your widget? What kind of widget would you like to make? Specify your question to make it easier to help you.
                            I was just trying to get some widget class to build so I could start tinkering with it. I the way I do these things is just start off with the most basic build I can and then start adding things to it / changing things around until I get a feel for the library then I start actually putting in work towards a goal.

                            I've made some more progress, but I'm having trouble with PDB files at the moment, my entire build environment just died on me and I'm not sure why. I think it has something to do with the way I built the engine, it's weird but instead of having the build platforms I had about 12 different build options, 4 debug, 4 debug engine, 4 debug editor, 4 developer, 4 developer engine, 4 developer game, and then 4 debug-development options etc etc....

                            Anyway, I had a working build of "Hello, Slate World!" right before things went nuts. I'm planning on making a tutorial on the wiki when I return my environment to some level of sanity, it will cover how to get "Hello, Slate World!" to build from a freshly made project and cover all the required classes, buildfile modifications, a widget class, a custom HUD class, and how to use that custom HUD in your game.
                            Last edited by User-658380556; 03-23-2014, 06:39 PM.

                            Comment


                              #15
                              Trying to get a grip on Slate as well, but I've run into a weird compiler error that says it can't find the <WidgetClassName>.generated.h file ... The examples above doesn't contain that include so at first I assumed it doesn't need it - but if I don't have that include, the compiler complains about it not being there...

                              Not sure where to go from here - I was wondering if you guys could confirm or deny that it indeed belongs in the header and maybe why I'd get this compiler error?

                              Thanks in advance!

                              Comment

                              Working...
                              X