Зеленое счастье и никаких наркотиков!

воскресенье, октября 31, 2010

Один урок в неделю: Информационные структуры часть вторая

Всем привет! По прежнему болею, ужасно себя чувствую, но больше стыдно что этот урок только через неделю от намеченного времени, по крайней мере, один урок в неделю писать успеваю :). Еще в середине недели было туговато со временем, много его ушло на исправление ошибок в MQN, после вывешивания на FGL, а я еще не записал трейлер, хотя пора бы уже. Собственно по MQN, наверно стоит сказать что первый bid уже произошел, и он примерно на том уровне который ожидал. Но не настолько хорош чтобы его принять :). Еще до вывешивания игры на FGL, познакомился с товарищем flazm, которого попросил помочь с продажей игры, так как я в этом деле полный нуб, собственно он и выложил ее. Попутно мы поговорили о дальнейшем сотрудничестве и будущее кажется ярче чем было неделю назад. Еще очень хотелось поехать на FlashGAMM но денег нет, и это очень печально. Ну что же, давайте все таки начнем наш урок:
Вторая часть информационных структур. В этом уроке мы поговорим о практическом применении структур. Напишем панель инструментов для интерфейса рисовалки. Давайте обратимся к нашему рисунку. И подумаем над чем нам предстоит работать.
Давайте, подумаем, как пользователь будет управляться с панелью:
При наведении на панель, иконка ближняя к курсору будет увеличиваться в размерах (в будущем мы сделай более приятный эффект). Если нажать проблем, когда мышка будет находится над панелькой, откроется настройка панели. Она будет выглядеть как сетка всех возможных инструментов. Пользователь просто перетаскивает необходимый ему инструмент на панель. Также можно перемещать уже установленные на панельке инструменты на новые позиции, и удалить простым сбросом их с панели.
А теперь попробуем представить это все в виде структур:
Начнем сверху вниз. На самом верху находится наша цель. Ниже первая структура, это сама Панель. В ней будут храниться ID структур инструмента находящиеся на панели, по ним будем искать что нам нужно нарисовать, как реагировать на клик мыши и прочее-прочее. Ниже, изображена структура Инструменты, собственно в ней будут перечислены все структуры существующих у нас инструментов. Структура Инструмент - показывает, какие данные и в каком порядке мы будем хранить в  структуре Инструменты(обратите внимание на Ы). Пока что это "Имя(строка)" и "ID спрайта из системы GML(целое)". Последнее, стоит объяснить подробнее, за этим хитрыми словами скрывается результат работы функции sprite_add, просто  решил, что это нужно отметить. Конечно здорово искать спрайт по названию, но лучше обращаться напрямую, так быстрей. Результаты работы функции sprite_add хранятся в структуре спрайты. Хотел показать на схеме, что это явно Map, здесь каждому из текстовых имен соответствует ID спрайта в системе, можно, вместо создания кучи переменных с именем спрайта, обращаться к спрайту через глобальный MAP - удобно - рекомендую. :). Сама структура создается очень просто, открывается определенная папка, в ней находятся все графические файлы, потом они загружаются функцией sprite_add, в качестве ключа используется имя без расширение.. Последние две структуры, тоже явные MAP, обе создаются автоматически, нужно только положить в папку Language - файл с расширением ini с определенным содержанием. Этим мы займемся потом, но стоит запомнить об этом функционале.
Теперь, когда у нас есть все необходимые структуры перед глазами, и вроде бы больше ни о чем мы не забыли, приступаем к программированию.
Открываем последний сохраненный нами файл проекта, и тут же его сохраняем под новым именем. Затем, создаем в папке рядом с проектом папку Graphics, укладываем туда еще одну папку со спрайтами-иконками наших инструментов, называем соответствующе - Instruments. В папку Graphics кинем подложку под панельку panel.png. В проект внесем некоторые подготовительные изменения строки, которые отвечали за начальные значения Высоты, Ширины и Цвета, заменим на статические - пока что. Получится что-то вроде этого:
width = 320;
height = 240;
surface = surface_create(width,height);
 
surface_set_target(surface);
 draw_clear(c_white);
surface_reset_target();
Еще, нам потребуется немножко дополнительных функций для отладки: 

out(argument0,...,argument15); - имеется 16 аргументов. Это отладочная функция, просто выводит на экран стандартное окошко сообщения со всеми аргументами переданными в функцию.
var i,s;
s="Debug:"
for(i=0;i<16;i+=1)
  if (string(argument[i])!="0") 
    s+= "#" +string(i)+': '+string(argument[i]);
 
show_message(s);
outList(ds_list); - имеется один аргумент - список, который нужно вывести на экран.
var i,size,str,list;
list = argument0;
if (list<0) exit;
size = ds_list_size(list);
str = 'List:#'
for (i=0;i<size;i+=1)
 str += ds_list_find_value(list,i)+'#';
show_message(str);
Сохранимся, и начнем уже изменения собственно кода. Первым делом объявим все нужные нам структуры. Открываем событие create объекта objMain и дописываем:
globalvar SPRITES,LANGUAGE;
SPRITES = ds_map_create();
LANGUAGE = ds_map_create();
panel = ds_list_create();
instruments = ds_list_create();
Смотрим на нашу схему, считаем, и выходит что все верно, мы добавили все четыре структуры :). Теперь, нужно загрузить данные. Начнем с LANGUAGE, на самом деле, у нас пока нет файла с языком, и мы будем заниматься его загрузкой когда нибудь потом, когда нам будет известно о полном содержании текста в нашей программе, а сейчас мы сделаем функцию заглушку, чтобы в дальнейшем ее заменить на настоящий код. Функция будет называться loadLanguage, аргументом будет выступать название языка, взятого из файла настройки, которого, к слову у нас тоже пока нет.
loadLanguage(langauge); - language - строка, название файла языка без расширения.

var l;
l = LANGUAGE;
ds_map_clear(l); //А вдруг мы новый язык загружаем?
ds_map_add(l,"Pencil","Pencil");
ds_map_add(l,"Brush","Brush");
ds_map_add(l,"Save","Save");
ds_map_add(l,"Zoom","Zoom");
Ну что же язык мы загрузили, теперь начнем загружать спрайты. С ними посложней, требуется скопировать одну уже готовую функцию, а потом написать еще одну, собственно загрузку спрайтов.
scanFiles(Path,Mask) - аргументы пояснены в комментарии. Автор функции я.
/*
 0 - Путь к папке например working_directory+'/background/'
 1 - Маска файлов например *.txt
 Обязательно полный путь указывайте! Иначе скрипт будет работать некоректно!
 Функция возвращает ID созданого списка, после того как его используйте, обязательно удалите.
 ВНЕЗАПНО!!! В списке будут содержаться только имена файлов!!!
 ВНЕЗАПНО!!! Возвращаемое значение может быть -1, если ничего не будет найдено!
*/
var list,file;
list = ds_list_create();
file = file_find_first(argument0+argument1,fa_directory)  // файл
if file!=''
{
if !directory_exists(argument0+file) ds_list_add(list,file);
while (file!='')
 {
  file = file_find_next();
  if file!=''
  if !directory_exists(argument0+file) 
   ds_list_add(list,file);
 }
}
file_find_close();
 
if ds_list_size(list)!=0 return list;
ds_list_destroy(list);
return -1;
А теперь пишем функцию загрузки спрайтов. Стоит сказать, что у нас пока что две папки со спрайтами, и мы не знаем заранее размера спрайтов в папке Graphics. Короче здесь тоже не доделка.

loadSprites(Path) - путь к папке со спрайтами.

var path,lst,size,i;
path = argument0;
if (!directory_exists(path))
{
    out("Папка спрайтов не существует!");
    return -1;
}
 
lst = scanFiles(working_directory+'/'+path+'/Instruments/',"*.png");
size = ds_list_size(lst); // Запоминаем размер списка
for(i=0;i<size;i+=1)
{
    ds_map_add(SPRITES,
        string_replace(ds_list_find_value(lst,i),".png",""),
            sprite_add(working_directory+'/'+path+'/Instruments/'+ds_list_find_value(lst,i),1,false,true,64,64));
}
ds_list_destroy(lst); // Удаляем уже ненужный нам список
// Загружаем спрайты отдельно (времянка).
ds_map_add(SPRITES,"Panel",sprite_add(working_directory+'/'+path+'/panel.png',1,false,true,0,0));
Пришла пора наших инструментов. В будущем, я предполагаю сделать из них что-нибудь более мощное, а пока, это будет предопределенные инструменты и функцию-заглушку их загрузки и инициализации мы напишем сейчас.
loadInstruments(instruments) - аргумент список, в который мы загрузим наши инструменты
var ins,I;
ins = argument0; // Вдруг нужно будет загрузить в другой список?
//Или из другого объекта?
I = ds_list_create();
  ds_list_add(I,ds_map_find_value(LANGUAGE,"Pencil"));
  ds_list_add(I,ds_map_find_value(SPRITES,"pencil"));
ds_list_add(ins,I);
I = ds_list_create();
  ds_list_add(I,ds_map_find_value(LANGUAGE,"Brush"));
  ds_list_add(I,ds_map_find_value(SPRITES,"brush"));
ds_list_add(ins,I);
I = ds_list_create();
  ds_list_add(I,ds_map_find_value(LANGUAGE,"Save"));
  ds_list_add(I,ds_map_find_value(SPRITES,"save"));
ds_list_add(ins,I);
I = ds_list_create();
  ds_list_add(I,ds_map_find_value(LANGUAGE,"Zoom"));
  ds_list_add(I,ds_map_find_value(SPRITES,"zoom"));
ds_list_add(ins,I);
Собственно так у нас создаются инструменты, одновременно загружаются названия из текущего языка. Теперь нужно внести все эти функции в objMain:Create, и добавить инструменты на панель, в будущем это будет делаться в настройках, но их у нас пока нет :). Вот что получится:
loadLanguage("English");
loadSprites("Graphics");
loadInstruments(instruments);
panelSprite = ds_map_find_value(SPRITES,"panel"); // Спрайт панели
// Добавляем на панель наши инструменты
ds_list_add(panel,ds_list_find_value(instruments,2));
ds_list_add(panel,ds_list_find_value(instruments,0));
ds_list_add(panel,ds_list_find_value(instruments,1));
На этом этапе нас уже можно поздравить, мы написали половину нашей программы. Остался вывод на экран. Меняем параметр depth на любой большой отрицательный, и создаем событие objMain:Draw, открываем его и думаем что будем делать. Очевидно, что нам следует отрисовать здесь нашу панельку, и спрайты наших кнопочек.
var i,size,scale,sprw,X,Y; 
size = ds_list_size(panel);
sprw = sprite_get_width(panelSprite);
scale = size*32/sprw;
X = room_width / 2 - scale * sprw / 2 - sprw;
Y = 16;
draw_sprite(panelSprite,0,X,Y);
draw_sprite_ext(panelSprite,1,X+sprw,Y,scale,1,0,-1,1);
draw_sprite(panelSprite,2,X+sprw+scale*sprw,Y);
 
X += sprw + 16;
Y += sprw + 16;
draw_set_halign(fa_center);
for(i=0;i<size;i+=1)
{
    tmp = ds_list_find_value(panel,i);
    draw_sprite_ext(ds_list_find_value(tmp,1),0,X+i*32,Y,0.25,0.25,0,c_white,1);
    draw_text(X+i*32,Y+24+12*(i mod 2),ds_list_find_value(tmp,0));
}
draw_set_halign(fa_left);

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

2 комментария: