Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Простенькое GUI для XNA

Anna | 18.06.2014 | нет комментариев
Доброго времени суток. Эта статья не откроет Вам новые грани программирования, она не расскажет о изумительном методе решения задачи, ничего такого. Легко ещё один ветхий велосипед, ржавый, но на ходу, и ехать ему ещё дюже долго…

Выходит

Когда я начал писал первую «серьёзную» игру на XNA стала задача с отсутствием стандартного GUI на этом движке. Так как я учусь, навыка у меня немножко, было решено писать свою систему интерфейса, взамен применения теснее готовых инструментов. За основу было взято реализацию с знаменитого в прошлом движка HGE. Ничего революционного там не было: класс Gui, класс GuiObject, от последнего наследуются различные кнопочки, списочки и т.д.

Базовый код

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
}

Выходит, база была готова. Дальнейшая задача — обработка событий. Недавно до написания этого кода, в универе нам рассказывали про делегаты. Уметь вызывать незнакомые тебе функции абсолютно недурная способность. Было решено остановиться именно на них. В прочем именно делегаты и применяются для создания кнопочек в Windows Forms приложениях на С#. В GuiObject добавился дальнейший код.

Код с делегатами

        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 - это текстуры окантовки, и два фона*/

Сейчас необходимо было сделать сам обработчик. Обработкой занимается класс Gui. Он перебирает все элементы, и если drawstate элемента совпадал с переданным доводом-состоянием, обработка продолжается. Теперь покажу.

Обработка

//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;
}

С обработкой разобрались, осталось лишь отрисовка. Помните, в GameState есть пункт Any? Если необходимо, Дабы кнопочка была неизменно,… а в прочем глядите.

Отрисовка

//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]);
            }
        }

Вот и готова основная часть кода. Сейчас необходимо лишь сделать кнопочку, сделать для неё обработчик и рисовальщик, и отправить через компилятор в безмерный цикл выполнения. В игре (по крайней мере, у меня) достаточно Зачастую необходимо рисовать идентичные элементы — фон, обводка и текст внутри. Следственно рисовальщик для них может быть универсальным, а вот обработку придётся описывать отдельно для всякого элемента.

Страшный пример применения кода из разрабатываемой игры

// 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);
            }
        }

Пример работы в картинках

Игра Townsman (в разработке)
Меню:

Меню генератора карт:

Игровой экран:

Игра Ancient Empires
Меню:

Меню редактора карт:

Редактор карт:
Особенности работы

Основной недочет такого подхода — при добавлении нового элемента в Gui необходимо лезть в класс и менять размер массива. Решается применением списков.

В всеобщем, это всё о чем я хотел рассказать. Спасибо за внимание.

Об играх

Об материалах: игры мои, Townsman ещё пишутся, скоро доделаю, и дам об этом знать.
Ancient Empires дозволено обнаружить на ex.ua либо в гугле со связкой small games.

P.S. Пожалуйста пишите что Вам не понравилось. Минусы без изложения не принимаются. :)

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB