📜 ⬆️ ⬇️

Simple GUI for XNA

Good day. This article will not open to you the new facets of programming, it will not tell you about a cool way to solve a problem, nothing like that. Just another old bike, rusty, but on the go, and go to him for a very long time ...



so

When I started writing the first "serious" game on XNA, there was a problem with the lack of a standard GUI on this engine. As I study, I have little experience, it was decided to write my own interface system, instead of using ready-made tools. The implementation was taken from the well-known HGE engine in the past. There was nothing revolutionary there: the Gui class, the GuiObject class, various buttons, lists, etc. are inherited from the latter.
')
Base code
class Gui { public GuiObject elements[]; public Gui() { elements = new GuiObject[6]; } } class GuiObject { public Rectangle rect; //    , ,  .. public bool lpressed; //     public bool rpressed; public bool lclick;//      public bool rclick; public GameState drawstate; //    ,    public bool darktransparency; //    ,    public bool lighttransparency; public string text; public bool undercursor; public GuiObject(Rectangle rec, bool dtr, bool ltr, GameState st, UpdateFunction f,DrawFunction f2, string text = "") { rect = rec; lpressed = false; rpressed = false; enable = true; lclick = false; rclick = false; darktransparency = dtr; lighttransparency = ltr; drawstate = st; this.text = text; updateFunction = f; drawFunction = f2; } } public enum GameState { Any, MainMenu, Game } 


So, the base was ready. The next problem is event handling. Shortly before writing this code, at the university we were told about delegates. Being able to call functions unknown to you is quite a good ability. It was decided to dwell on them. In other matters, delegates are used to create buttons in Windows Forms C # applications. The following code has been added to GuiObject.

Code with delegates
  public delegate void UpdateFunction(ref GuiObject me); public delegate void DrawFunction(Texture2D line, Texture2D darkbackground, Texture2D lightbackground, ref GuiObject me); public DrawFunction drawFunction; public UpdateFunction updateFunction; /*           . , ,   */ /* line, darkbackground, lightbackground -   ,   */ 


Now it was necessary to make the handler itself. Handling deals with the Gui class. It enumerates all the elements, and if the element’s drawstate matches the passed state argument, processing continues. I'll show you now.

Treatment
 //Gui.cs public void Update(MouseState mstate,GameState state,GameTime gameTime) { for (int i = 0; i < elements.Length; i++) { if (elements[i].drawstate == state&&elements[i].enable) { elements[i].Update(mstate); elements[i].updateFunction(ref elements[i]); } } } /*   foreach  for,         */ /*   ?      ( click  pressed),          ( ).*/ // GuiObject.cs public void Update(MouseState state) { lclick = false; rclick = false; if (rect.Contains(new Point(state.X, state.Y))) { if (state.LeftButton == ButtonState.Pressed) if (!lpressed) { lclick = true; lpressed = true; } if (lpressed && state.LeftButton == ButtonState.Released) lpressed = false; if (state.RightButton == ButtonState.Pressed) if (!rpressed) { rclick = true; rpressed = true; } if (rpressed && state.RightButton == ButtonState.Released) rpressed = false; undercursor = true; } else undercursor = false; } 


With the processing figured out, there is only drawing. Remember there is an Any item in GameState? If you want the button to be always ... and in other matters, see.

Drawing
 //Gui.cs public void Draw(Texture2D line, Texture2D darkbackground, Texture2D lightbackground, GameState state) { for (int i = 0; i < elements.Length; i++) { if ((elements[i].drawstate == GameState.Any || elements[i].drawstate == state)&&elements[i].enable) elements[i].drawFunction(line, darkbackground, lightbackground, ref elements[i]); } } 


Here is the main part of the code. Now you just need to create a button, create a handler and a painter for it, and send it through the compiler into an infinite loop of execution. In the game (at least for me) quite often you need to draw the same elements - the background, the stroke and the text inside. Therefore, the draftsman for them can be universal, but the processing will have to be described separately for each element.

A terrible example of using the code from the developed game
 // void Init() state = GameState.MainMenu; gui = new Gui(); gui.elements[0] = new GuiObject(new Rectangle(0, 0, width-205, height), false, false, GameState.Game, Main, MapGuiDraw); gui.elements[1] = new GuiObject(new Rectangle(width - 205, 0, 205, height), false, false, GameState.Game, RightPanel, RightPanelDraw); gui.elements[2] = new GuiObject(new Rectangle(width - 205, 0, 205, 39), false, false, GameState.Game, GameMenuButton, GameMenuButtonDraw); gui.elements[3] = new GuiObject(new Rectangle((width - 150) / 2, height / 2, 150, 30), false, false, GameState.MainMenu, StartGameButton, StandartButtonDraw, "Start game"); gui.elements[4] = new GuiObject(new Rectangle((width - 150) / 2, height / 2 + 50, 150, 30), false, false, GameState.StartGameMenu, GenerateButton, StandartButtonDraw, "Generate"); //Draw Functions Example void StandartGuiDraw( Texture2D line, Texture2D darkbackground, Texture2D lightbackground, ref GuiObject me) { if (me.darktransparency) DrawTexturedRect( darkbackground, me.rect); if (me.lighttransparency) DrawTexturedRect( lightbackground, me.rect); DrawOutLine(line, me.rect); if (me.text != "") { Vector2 size = font.MeasureString(me.text); spriteBatch.DrawString(font, me.text, new Vector2((int)(me.rect.X + me.rect.Width / 2 - size.X / 2), (int)(me.rect.Y + me.rect.Height / 2 - size.Y / 2)), Color.White); } } 


Picture Example
Townsman game (in development)
Menu:

Card Generator Menu:

Game screen:

Game Ancient Empires
Menu:

Map Editor Menu:

Map Editor:


Work features

The main disadvantage of this approach is that when adding a new element to Gui, you need to climb into the class and change the size of the array. Dares using lists.

In general, this is all I wanted to talk about. Thanks for attention.

About games
On the materials: my games, Townsman is still being written, I will finish it soon, and I will let you know about it.
Ancient Empires can be found on ex.ua or in Google with a bunch of small games.

PS Please write what you did not like. Minuses without description are not accepted. :)

Source: https://habr.com/ru/post/180613/


All Articles