GUI элементы на GMS 2.3+

Ранее были разработаны некоторые элементы пользовательского интерфейса ListBox, RadioBox, Button, CheckBox, Slider. Все это было создано еще на старом Гамаке.
Сейчас GameMkaer "вырос", добавились новые функционалы, и пришла пора модернизировать свои GUI в более современный вид.
   



          

 


  

   

Для начала создадим родительский элемент/конструктор который будет базисом для всех джочерних элементов и наследовать его.

Так же все события GUI элементов будут транслироваться в EventManager.

====================================================================

Slider Слайдер.




Создаём объект с параметрами:


Скрипт конструктор слайдера:

/// @description SLIDER
//Site:   MidaDev.ru
//Author: Dimusikus

function GUI_Slider() : GUIElement() constructor 
{	
	#region Constructor
	my_name_is	= other.my_name_is;
	value		= other.value;
	old_value	= value;
	minimum		= (min(other.minimum, other.maximum));//floor
	maximum		= (max(other.minimum, other.maximum));//floor
	interval	= (clamp(other.interval, minimum, maximum));//floor	
	tip_x		= 0;	
	//подписка на модификацию данных от внешнего источника	
	EVENTGUI.subscribe("set_" + my_name_is, function(_data){ set(_data, false); trace("GUI_Slider(", my_name_is,") Получил событие=", "set_", my_name_is, ", данные=", _data);})
	EVENTGUI.subscribe("set_random_" + my_name_is, function(){ set_random();})
	EVENTGUI.subscribe("set_random_all_sliders", function(){ set_random();})	
	#endregion	
	//*******************************************************************
	static math_tip_x = function(_val){tip_x = remap(_val, minimum, maximum, _xo, _xo + width);};
	//*******************************************************************
	static set = function(_value, _flag=true) 
	{	if is_undefined(_value) then show_message("GUISLIDER _value=undefined");
		if !is_undefined(_value)
		{	value = clamp(_value,minimum, maximum);// / interval) * interval;		
			value = (floor(value / interval) * interval)			
			math_tip_x(value); //расчёт позиции бегунка слайдер
			if (old_value != value) and _flag==true then EVENTGUI.publish(my_name_is, value);
			old_value = value;
		}else value = -1;
	}
	//*******************************************************************
	static set_random = function(){ randomize(); set( random_range(minimum, maximum) );};
	//*******************************************************************
	static remap = function(val, min1, max1, min2, max2) {	return min2 + (max2 - min2) * ((val - min1) / (max1 - min1));	}
	//*******************************************************************	
	static get = function(){return value;}
	//*******************************************************************	
	static listen = function(){	if mouse_grab then set(remap(clamp(mouse_x, _xo, _xo + width), _xo, _xo + width, minimum, maximum)); 	}
	//*******************************************************************	
	static draw = function() 
	{	var b;		
		draw_focus();
		
		if other.slider_type=="BAR"		then 
		{	b=0;// рисуем корпус слайдера корпус
			draw_set_color(color_border);
			draw_rectangle(_xo, _yo  , _xo + width, _yo + height , true); 
			b=1;
			draw_set_color(color_fill); //рисуем саму полоска
			if other.slider_draw_button then    draw_button(_xo+b, _yo+b,  tip_x-b, _yo + height-b, true);
			else							 draw_rectangle(_xo+b, _yo+b,  tip_x-b, _yo + height-b, false); 
		}
		
		if other.slider_type=="BEGUNOK"	then 
		{	
			b=height/2;			
			draw_set_color(color_border); // рисуем линию на которой бегает бегунок
			if other.slider_draw_button then    draw_button(_xo, _yo + b -1 , _xo + width , _yo + b + 1, true);
			else							 draw_rectangle(_xo, _yo + b -1 , _xo + width , _yo + b + 1, false); 
			
			draw_set_color(color_fill); //рисуем только бегунок			
			if other.slider_draw_button then	     draw_button(tip_x - height/3 , _yo,  tip_x + height/3 , _yo + height , true);
			else								  draw_rectangle(tip_x - height/3 , _yo,  tip_x + height/3 , _yo + height , false);
		}
		
		//рисуем текст
		draw_set_hv(fa_left, fa_middle);
		draw_set_color(color_text);
		draw_set_font(font);
		draw_text(_xo + width + padding, _yo + (height * 0.5), string(my_name_is) + ": " + string(get()));		
	}
	//*******************************************************************	
	//set(value);	
	math_tip_x(value); //сразу иниции позицию бегунка
}
/// @description Create Slider
slider = new GUI_Slider();

/// @description Step Event Slider
slider.step();

/// @description Draw Event Slider
slider.draw();




====================================================================

SliderHSV Слайдер.


Для проекта ParticleDesigner был создан слайдер выбора цвета в формате HSV.
Добавляем изображение к объекту слайдера:

В наследники ставим объект слайдера обычного.
Но только меняем содержимое события Draw:

draw_self();
slider.draw();

Итоговый объект на экране у нас получится как:



====================================================================


CheckBox.


/// @description CHECK BOX
//Site:   MidaDev.ru
//Author: Dimusikus

function GUI_CheckBox() : GUIElement() constructor 
{	
	#region Constructor
	my_name_is	= other.my_name_is;
	value		= other.value;
	old_value	= value; //!value 
	
	EVENTGUI.subscribe("set_" + my_name_is, function(_data){ set(_data); trace("GUI_CheckBox(", my_name_is,") Получил событие=", "set_", my_name_is, ", данные=", _data);})
	EVENTGUI.subscribe("set_random_" + my_name_is, function(){ set_random();})
	EVENTGUI.subscribe("set_random_all_sliders", function(){ set_random();})	
	#endregion	
	//*******************************************************************
	static set = function(_value, _flag=true) 
	{	if is_undefined(_value) then show_message("GUICHECKBOX _value=undefined");
		value = _value;
		if (old_value != value) and (_flag==true) then	EVENTGUI.publish(my_name_is, value);
		old_value = value;
	}
	//*******************************************************************
	static set_random = function(){ randomize(); set( choose(true,false) );};	
	//*******************************************************************
	static get = function(){return value;}
	//*******************************************************************
	//static listen = function()	{	}	
	//*******************************************************************
	static click = function(){	set_focus();	set(!get());	}	
	//*******************************************************************
	static draw = function() 
	{	draw_focus();		
		draw_set_color(color_border);
		draw_rectangle(x, y, x + height, y + height, false); 
		
		if value then draw_set_color(color_fill);		
		else draw_set_color(color_backgr);
		draw_rectangle(x+2, y+2, x + height-2, y + height-2, false); 
		
		draw_set_color(color_text);
		draw_set_hv(fa_left, fa_middle);
		draw_set_font(font);
		draw_text(x + height + padding, y + (height * 0.5), my_name_is);
	}	
	//*******************************************************************
	//инициализируем значение
	//set(value);	
	//*******************************************************************
}


Объект содержит всего три штатных события со скриптами:

/// @description Create event CheckBox
checkbox = new GUI_CheckBox();

/// @description Step Event CheckBox
checkbox.step();

/// @description DrawEvent CheckBox
checkbox.draw();


Свойства обекта:



Итог:



 ====================================================================


RadioCheckBox.




Конструктор:

/// @description CHECK BOX
//Site:   MidaDev.ru
//Author: Dimusikus

function GUI_RadioBotton() : GUIElement() constructor 
{	
	#region Constructor
	my_name_is	= other.my_name_is;
	value		= other.value;
	
	EVENTGUI.subscribe("set_" + my_name_is, function(_data){ set(_data); trace("GUI_RadioBotton(", my_name_is,") Получил событие=", "set_", my_name_is, ", данные=", _data);})	
	EVENTGUI.subscribe(other.radiobox_group, function(_d)	{if _d != _id then { value = false; }})	
	#endregion	
	//*******************************************************************
	static set = function(_value=true, _flag=true)
	{	value = _value;
		if _flag==true then EVENTGUI.publish(my_name_is, value);
		EVENTGUI.publish(other.radiobox_group, other.id);		
	}
	//*******************************************************************
	static get = function(){return value;}
	//*******************************************************************
	//static listen = function()	{	}	
	//*******************************************************************
	static click = function() 	{	set_focus();		set(true);	}	
	//*******************************************************************
	static draw = function() 
	{	draw_focus();		
		draw_set_color(color_border);
		draw_circle(x ,y , w2, false);
		draw_set_color(color_backgr);
		draw_circle(x ,y , w2-2, false);		
		
		if value then draw_set_color(color_fill);
		else draw_set_color(color_backgr);	
		
		draw_circle(x,y , w2-4, false);		
		
		draw_set_color(color_text);
		draw_set_hv(fa_left, fa_middle);
		draw_set_font(font);
		draw_text(x + h2 + padding, y , my_name_is);
	}	
	//*******************************************************************
	//инициализируем значение
	//set(value);
	//инициализируем групповую отметку сообщением	
	EVENTGUI.publish(other.radiobox_group, other.id);
	//*******************************************************************
}




Переменная radiobox_group отвечает за группу в которой учавствует checkbox.

Объект check_box содержит следующий код:

/// @description Create Event RadioBox
radiobotton = new GUI_RadioBotton();

/// @description StepEvent RadioBox
radiobotton.step();

/// @description	DrawEvent RadioBox
radiobotton.draw();


====================================================================


DropDown.




Конструктор:

/// @description Drop Down
//Site:   MidaDev.ru
//Author: Dimusikus

function GUI_DropDown() : GUIElement() constructor 
{	
	//*******************************************************************
	#region Constructor
	x			= other.x;
	y			= other.y;
	my_name_is	= other.my_name_is;
	options		= other.options;	//массив пунктов меню
	value		= other.index;		//индекс выделенного пункта меню
	#endregion
	//*******************************************************************
	static set = function(_value, _flag=true) 
	{	if is_undefined(_value) then {show_message("DROPDOWN set index = undefined"); _value=-1; return;};
		value =_value;
		if _flag==true then EVENTGUI.publish(my_name_is, [options[value], value]);
	}
	//*******************************************************************
	static listen = function() 
	{	//ждём клика мышкой
		if (!mouse_check_button_pressed(mb_left)) return;		
		//тогда высчитываем куда ана кликнула
		for(var i = array_length(options)-1; i>=0; i--) 
		{	if (!point_in_rectangle(mouse_x, mouse_y,	x, y + (height * (i+1)), x + width, y + (height * (i+2)))) then continue;			
			set(i); //поймали клик на элементе i
			return;
		}		
	}//если мы тут, то кликнули вникуда
	//*******************************************************************
	static draw = function() 
	{	var kuku="v";		
		draw_set_hv(fa_left, fa_middle);
		draw_set_font(font);
		
		if (get_focus()) 
		{	kuku= "^";			
			draw_focus();			
			
			draw_set_color(color_text);
			for(var i = array_length(options)-1; i>=0; i--) 
			{	draw_set_color(color_backgr);
				if (point_in_rectangle(mouse_x, mouse_y, x, y + (height * (i+1)), x + width, y + (height * (i+2)))) or i==value then draw_set_color(color_fill);
				draw_rectangle                          (x, y + (height * (i+1)), x + width, y + (height * (i+2)), false);
				draw_set_color(color_text);
				draw_text(x + padding, y + (height * (i+1.5)), options[i]);
			}
		}
		//draw_text(x + width - (padding * 1)-5, y + (height * 0.5), kuku);
		draw_set_hv(fa_right, fa_middle);
		draw_text(x + width , y + h2, kuku);
				
		//основная часть элемента
		draw_set_color(color_border);
		draw_rectangle(x, y, x + width, y + height-2, true);		
		draw_set_color(color_text);
		draw_set_hv(fa_left, fa_middle);
		draw_text(x + padding, y + h2, options[get()]);					
	}	
	//*******************************************************************
	//set(value);
	//*******************************************************************
}

 

 

 скрипты объекта:

/// @description Create Event DropDown
checkbox = new GUI_DropDown();

/// @description StepEvent DropDown
checkbox.step();

/// @description Draw Event DropDown
checkbox.draw();

 

 

====================================================================


ListBox.

/// @description GUI_ListBox
//Site:   MidaDev.ru
//Author: Dimusikus

function GUI_ListBox() : GUIElement() constructor 
{	
	//*******************************************************************
	#region Constructor	
	my_name_is	= other.my_name_is;
	//items		= other.items;	//массив пунктов меню
	value		= other.index;		//индекс выделенного пункта меню
	old_depth	= other.depth;		//стоковая глубина элемента	
	
	//for (var i = 0; i < 4; i++)   other.items[i] = string(random(100));
	lowerPos = 0;
	max_column_display = floor(height / char_height);
	GUI_Elements.add_element(my_name_is, self);
	#endregion
	static set_focus = function() {focus = true; other.depth -= 10;}
	//*******************************************************************
	static remove_focus = function() {focus = false; other.depth = old_depth;}
	//*******************************************************************
	static add_item = function(_item){	array_push(other.items, _item);	}
	//*******************************************************************
	EVENTGUI.subscribe("add_item_" + my_name_is, function(_item){ add_item(_item); trace("GUI_ListBox(", my_name_is,") Получил событие=", "add_item_", my_name_is, ", данные=", _item);})
	//*******************************************************************
	static set = function(_value, _flag=true) 
	{	if is_undefined(_value) then {show_message("ListBox set index = undefined"); _value=-1; return;};
		value =_value;
		if _flag==true then
		{ EVENTGUI.publish(my_name_is, [other.items[value], value]);
			if callback!=pointer_null then callback([other.items[value], value]);}
	}
	//*******************************************************************
	static listen = function() 
	{	
		if (get_focus()) 
		{	//перемещение списка вниз(к началу)
			if (mouse_wheel_up()) then{ lowerPos = max(lowerPos-1, 0)};
			else //перемещение списка вверх(к концу)
			if (mouse_wheel_down()) then{ if lowerPos < (array_length(other.items) - max_column_display) then	lowerPos = lowerPos+1;};
		}		
		//ждём клика мышкой
		if (!mouse_check_button_pressed(mb_left)) return;
		
		//очистка состояния кнопок мыши, что бы други элементы ГУИ под курсором не схавали это событие
		//можно создать событие приоритета элементов, где он отключает реакции других элементов на мыщку
		mouse_clear(mb_any); 
		
		//тогда высчитываем куда ана кликнула
		for(var i = 0; i< max_column_display; i++) 
		{	var pos = lowerPos + i;				
			if pos>=array_length(other.items) then break;
			if (!point_in_rectangle(mouse_x, mouse_y,	x, y + (char_height * (i+0)), x + width, y + (char_height * (i+1)))) then continue;			
			set(pos); //поймали клик на элементе pos
			return;
		}
	}//если мы тут, то кликнули вникуда
	//*******************************************************************
	static draw = function() 
	{	//var kuku="v";		
		draw_set_hv(fa_left, fa_middle);
		draw_set_font(font);
		draw_focus();
		
		//if (get_focus()) 
		{			
			
			oldd = other.depth;
			other.depth-=10;
			draw_set_color(color_text);			
			
			for(var i = 0; i< max_column_display; i++) 
			{	
				var pos = lowerPos + i;				
				if pos>=array_length(other.items) then break;
				
				draw_set_color(color_backgr);
				//if (point_in_rectangle(mouse_x, mouse_y, x, y + (char_height * (i+1)), x + width, y + (char_height * (i+2)))) or pos==get() then draw_set_color(color_fill);
				if pos==get() then draw_set_color(color_fill);
				//draw_rectangle                          (x, y + (char_height * (i+1)), x + width, y + (char_height * (i+2)), false);
				draw_rectangle                          (x, y + (char_height * (i+0)), x + width, y + (char_height * (i+1)), false);
				
				draw_set_color(color_text);
				
				draw_text(x + padding, y + (char_height * (i+0.5)), other.items[pos]);
			}
			other.depth=oldd;
		}	
		
	}	
	//*******************************************************************
	//set(value);
	//*******************************************************************
}

 

 

====================================================================

Slider vertical.

/// @description SLIDER
//Site:   MidaDev.ru
//Author: Dimusikus

function GUI_SliderV() : GUIElement() constructor 
{	
	#region Constructor
	my_name_is	= other.my_name_is;
	value		= other.value;
	old_value	= value;
	minimum		= (min(other.minimum, other.maximum));//floor
	maximum		= (max(other.minimum, other.maximum));//floor
	interval	= (clamp(other.interval, minimum, maximum));//floor	
	tip_y		= 0;//other.x;	
	GUI_Elements.add_element(my_name_is, self);
	//подписка на модификацию данных от внешнего источника	
	EVENTGUI.subscribe("set_" + my_name_is, function(_data){ set(_data, false); trace("GUI_Slider(", my_name_is,") Получил событие=", "set_", my_name_is, ", данные=", _data);})
	EVENTGUI.subscribe("set_random_" + my_name_is, function(){ set_random();})
	EVENTGUI.subscribe("set_random_all_sliders", function(){ set_random();})	
	#endregion	
	//*******************************************************************
	static math_tip_y = function(_val)
	{	tip_y = remap(_val, minimum, maximum, _yo + height, _yo );
	};
	//*******************************************************************
	static set = function(_value, _flag=true) 
	{	if is_undefined(_value) then show_message("GUISLIDER _value=undefined");
		if !is_undefined(_value)
		{	value = clamp(_value,minimum, maximum);// / interval) * interval;		
			value = (floor(value / interval) * interval)			
			math_tip_y(value); //расчёт позиции бегунка слайдер
			if (old_value != value) and _flag==true then EVENTGUI.publish(my_name_is, value);
			old_value = value;
			if callback!=pointer_null then callback(value);
		}else value = -1;
	}
	//*******************************************************************
	static set_random = function(){ randomize(); set( random_range(minimum, maximum) );};
	//*******************************************************************
	static remap = function(val, min1, max1, min2, max2) {	return min2 + (max2 - min2) * ((val - min1) / (max1 - min1));	}
	//*******************************************************************	
	static get = function(){return value;}
	//*******************************************************************	
	static listen = function(){	if mouse_grab then set(remap(clamp(mouse_y, _yo, _yo + height), _yo + height, _yo , minimum, maximum)); 	}
	//*******************************************************************	
	static draw = function() 
	{	var b;		
		draw_focus();
		
		if other.slider_type=="BAR"		then 
		{	b=0;// рисуем корпус слайдера корпус
			draw_set_color(color_border);
			draw_rectangle(_xo, _yo  , _xo + width, _yo + height , true); 
			b=1;
			draw_set_color(color_fill); //рисуем саму полоска
			if other.slider_draw_button then    draw_button(_xo - b + width-1,  tip_y, _xo+b+1, _yo-b+height, true);			
			else							 draw_rectangle(_xo - b + width,  tip_y, _xo+b, _yo-b+height,  false); 
		}
		
		if other.slider_type=="BEGUNOK"	then 
		{	
			//b=height/2;			
			b=width/2;
			draw_set_color(color_border); // рисуем линию на которой бегает бегунок
			if other.slider_draw_button then    draw_button(_xo + b - 1, _yo , _xo +b + 1, _yo + height, true);
			else							 draw_rectangle(_xo + b - 1, _yo , _xo +b + 1, _yo + height, false);
			
			draw_set_color(color_fill); //рисуем только бегунок
			if tip_y < _yo then	tip_y=_yo;//исправляет, глюк, бегунок при старте экрана находится где хрен знает где но не на слайдере почемуто
	
			if other.slider_draw_button then	     draw_button(_xo, tip_y - width/3 , _xo + width,  tip_y + width/3 , true);
			else								  draw_rectangle(_xo, tip_y - width/3 , _xo + width,  tip_y + width/3 , false);
			
		}
		
		//рисуем текст
		draw_set_hv(fa_center, fa_middle);
		draw_set_color(color_text);
		draw_set_font(font);
		draw_text(_xo + width, _yo + (height * 1) + padding+5, string(my_name_is) + ": " + string(get()));		
	}
	//*******************************************************************	
	//set(value);	
	math_tip_y(value); //сразу иниции позицию бегунка
}

 

====================================================================

CircularSlider Круговой Слайдер.
 

 

/// @description
//Site:   MidaDev.ru
//Author: Dimusikus
function GUI_KnobCircular() : GUIElement() constructor 
{
	#region Constructor
	my_name_is	= other.my_name_is;
	angle		= other.image_angle + other.angle;	//градусы 0-360
	pointinrect = false;							//детект мыши по окружности а не по прямоугольнику
	GUI_Elements.add_element(my_name_is, self);
	#endregion	
	//*******************************************************************
	//значение other.angle_speed должно быть > 5 иначе регулятор двигаеться не будет.
	//прилипание к целым значения градусов
	static prilipalka = function(a)
	{
		var f = min(other.angle_speed, 5);
		if a>360-f then return 360;
		if a<f then return 0;
		
		if a>(45-f) and a<(45+f) then return 45;
		if a>(90-f) and a<(90+f) then return 90;
		
		if a>(135-f) and a<(135+f) then return 135;
		if a>(180-f) and a<(180+f) then return 180;
		
		if a>(225-f) and a<(225+f) then return 225;
		if a>(270-f) and a<(270+f) then return 270;
		
		if a>(315-f) and a<(315+f) then return 315;
		
		return a;
	}
	//*******************************************************************
	static set = function(a)
	{	a = clamp(a, 0,360);
		a = prilipalka(a);		
		other.image_angle = a + other.offset_degree;				
		if a != angle then
		{	angle = a;			
			EVENTGUI.publish(my_name_is, a);			
			if callback!=pointer_null then callback(value);
		}
	}
	//*******************************************************************
	static listen = function() 
	{ 	if mouse_grab then
		{	var targetDirection = point_direction(x, y, mouse_x, mouse_y);			
			var angleDifference = angle_difference(angle+ other.offset_degree, targetDirection);      
			var angleIncrement = other.angle_speed; //скорость перемещения угла к конечному результату
			var a  = angle - (min(abs(angleDifference), angleIncrement) * sign(angleDifference));						
			set(a);
		};
	}
	//*******************************************************************
	static draw = function() 
	{	
		with(other){draw_self();};
		draw_set_hv();
		draw_set_color(color_text);
		draw_text(x,y+height/2 + padding+4,my_name_is +  string(floor(angle)))
		draw_focus();		
	};
	//*******************************************************************
	set(angle);
}

 

====================================================================

VolumSlider Круговой Слайдер по типу регулятора громкости.

 /// @description
//Site:   MidaDev.ru
//Author: Dimusikus
function GUI_KnobVolume() : GUIElement() constructor 
{
	#region Constructor
	my_name_is	= other.my_name_is;
	
	angle		= other.image_angle + volume_to_angle(other.volume);	//градусы 0-360
	
	pointinrect = false;							//детект мыши по окружности а не по прямоугольнику
	GUI_Elements.add_element(my_name_is, self);
	#endregion	
	//*******************************************************************
	///Map(val, min1, max1, min2, max2)       
	static remap = function(val, min1,max1, min2, max2)
	{
		return min2 + (max2 - min2) * ((val - min1) / (max1 - min1));    
	}
	//*******************************************************************
	//angle to volume
	static angle_to_volume = function(_angle)
	{		
		return remap(_angle, other.angle_min , other.angle_max, 1, 0);
	}
	//*******************************************************************
	//volume to angle
	static volume_to_angle = function(_vol)
	{			
		return remap(_vol, 0, 1, other.angle_min, other.angle_max);
	}
	//*******************************************************************
	static set = function(a)
	{	a = clamp(a, other.angle_min, other.angle_max);
		
		other.image_angle = a + other.offset_degree;				
		if a != angle then
		{	angle = a;			
			other.volume = angle_to_volume(angle);			
			EVENTGUI.publish(my_name_is, other.volume);			
			if callback!=pointer_null then callback(other.volume);
		}
	}
	//*******************************************************************
	static listen = function() 
	{ 	if mouse_grab then
		{	var targetDirection = point_direction(x, y, mouse_x, mouse_y);			
			var angleDifference = angle_difference(angle+ other.offset_degree, targetDirection);      
			var angleIncrement = other.angle_speed; //скорость перемещения угла к конечному результату
			var a  = angle - (min(abs(angleDifference), angleIncrement) * sign(angleDifference));						
			set(a);
		};
	}
	//*******************************************************************
	static draw = function() 
	{	
		with(other)
		{
			draw_sprite_ext(sprite_index, 0, x, y, image_xscale, image_yscale, 0, image_blend, image_alpha);
			draw_sprite_ext(sprite_index, 1, x, y, image_xscale, image_yscale, image_angle, image_blend, image_alpha);
			//draw_self();
		};
		draw_set_hv();
		draw_set_color(color_text);
		draw_text(x,y+height/2 + padding+4,my_name_is +  string(other.volume))
		draw_focus();		
	};
	//*******************************************************************
	set(angle);
}