Иногда нечем занятся, поэтому решил написать серию туториалов на тему "написания своего "скелета" для ......". Постараюсь обьяснить все от А до Я(хотя это зависит от настроения и желания).
Сразу огаворюсь: Описание будет направлено на юзера которы хоть немного знаком с матчастью(т.е. тот которому не придется подробно объяснять чем переменная int отличается от переменной bool, и какие значения они могут принимать.)
После каждого "тутора" будет прикреплен исходный код проделанного(полнаценный проект). Писаться будет все на одном проекте и с каждым последующем уроком выкладываться будет дополненный предыдущий проект. Таким образом в конце всей ленейки будет предаставлен полноценный "скелет" .
Пока не решил каким образом будет оформлены уроки. варианта два: - Все будет в одной теме, каждый урок в новом сообщении, либо второй вариант - каждый новый урок в новой теме.
Текст скорее всего будет с орф. ошибками и иправлять их скорее всего не буду(хотя постараюсь писать грамотно), поэтому по этому поводу прошу не поднимать шум и принять это как стиль написания автора)
Les #1 - SDK
ИСПОЛЬЗУЕТСЯ "Microsoft Visual Studio 2010"
Первым делом сделаем свою жизнь проще, а именно подключим DirectX Software Development Kit(SDK). Но делать это мы будем не указывая пути в настройках проекта, а используя для этого стандартные пути включаемых фаилов и библиотек Microsoft Visual Studio(MVS).
Узнаем где у нас установлена MVS. Если во время установки все осталось по дефолту, то искать нужно в папке "С:\Program Files\Microsoft Visual Studio 10.0" (это для 32-х битных систем, какой дефолтный путь на 64-х я не знаю).
Открыв папку с MVS видим среди всего прочего папку "VC", именно сюда нужно поместить фаилы SDK.
Скачиваем относительно своей системы: [Ссылки могут видеть только зарегистрированные пользователи. ] [Ссылки могут видеть только зарегистрированные пользователи. ]
Распаковываем архив в папку "VC", соглашаясь на слияние папок и замену фаилов.
С установкой SDK мы закончили. Теперь нет необходимости вручную указывать пути в настройках, тем самым экономим время в будующем и избигаем некоторых проблем)\
Les #2 - Создание проекта
Открываем MVS. Создаем новый проект:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Нажимаем "Далее". Настраиваем проект:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Нажимаем по проекту ПКМ -> Свойства и выставляем все согласно скрину:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Добавляем к проекту фаил .cpp: ПКМ по проекту - Добавить - Создать новый фаил - Выбираем "Фаил .cpp" - называем его любым именем(я назову Base.cpp).Это будет основной фаил проекта и "на него" будем лепить все остальное.
Теперь заполним минимум, а именно напишем точку иницализации\входа нашей dll.
Подключаем к проекту:
Код:
#include <Windows.h>
Справка
Цитата:
windows.h является Windows-конкретный файл заголовков для языка C программирования, который содержит заявления для всех функций в Windows API...
Пока из всех значений dwReason нам потребуется только "DLL_PROCESS_ATTACH" Справка
Цитата:
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
Les #3 - d3d9 hook & EndScene
Напишем функцию которая будет перехватывать оригинальный метод DirectX9 - EndScene(ES), выполнять необходимые нам действия и возвращала бы все "на круги своя"...
Цитата:
EndScene - метод который вызывается после окончания сцены.
Работать будем пока только с этим методом(впринципе его достаточно для наших действий)
Для описания хука нам потребуется несколько функций для работы с памятью. Завернем их все в класс cMemory.
Будем добавлять с помощью "Мастера классов"
Цитата:
ПКМ по проекту -> Добавить -> Класс
в открывшемся окне нажимаем "Далее" и заполняем согласно скрину:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Можно и вручную создать фаилы, но зачем нужны лишние движения?
После этого у вас появятся два фаила: cMemory.cpp и cMemory.h
Работаем с cMemory.h:
подключим "Windows.h"
Код самый примитивный) подробно разбирать его не вижу смысла.
Теперь на иницализацию нашей DLL запустим поток. Что бы не писать постоянно всю функцию CreateThread(вдруг еще гне нужно будет) опишем для нее небольшой макрос:
[Ссылки могут видеть только зарегистрированные пользователи. ]
ЗЫ:Будет дополнятся в зависимости от свободного времени и настроения
Добавлено через 2 минуты
Писать в одном сообщении все - сложно, поэтому туторы быдут разбиты на сообщения. На усмотрение администрации все останется в таком виде, либо соеденятся со стартовым сообщением.
Les #4 - Text
Для рисования(текста, боксов, бордеров и прочего) опишим еще один класс - cRender.как это сделать было описано выше.
Подключаем cRender.h к Base.cpp:
Код:
#include "cRender.h"
задаем указатель на класс
Код:
cRender Render;
Также для всего нам понадобится определения цветов. Создадим фаил Colors.h.
Для определения будем использовать структуру enum:
При потере контекста устройства будет произведена сброс и перегрузка шрифта, что в свою очередь позволит избежать черных экранов и потери текста при сворачивании\разворачивании проложения.
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
Добавлено через 2 часа 54 минуты
Les #5 - Box & Border
Работаем с cRender.h.
добавляем прототипы для двух функций:
Код:
void Draw_Box( int x, int y, int w, int h, D3DCOLOR Color, LPDIRECT3DDEVICE9 m_pD3Ddev);
void Draw_Border(int x, int y, int w, int h,int s, D3DCOLOR Color, LPDIRECT3DDEVICE9 m_pD3Ddev);
x,y - положение в пространстве
w,h - ширина и высота соответственно
s - ширина линии обводки
Color - цвет
m_pD3Ddev - указатель на устройство
переходим к сRender.cpp
Код:
void cRender::Draw_Box(int x, int y, int w, int h, D3DCOLOR Color,IDirect3DDevice9* mDevice)
{
D3DRECT rec;
rec.x1 = x;
rec.x2 = x + w;
rec.y1 = y;
rec.y2 = y + h;
mDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
mDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, D3DPT_TRIANGLESTRIP);
mDevice->Clear( 1, &rec, D3DCLEAR_TARGET, Color, 1, 1 );
}
Это самый простой вариант(прозрачность не поддерживается). Более сложный, с поддержкой прозрачности - по запросу пользователей.
[Ссылки могут видеть только зарегистрированные пользователи. ]
Теперь опишем функцию Draw_Border(обводка)
Код:
void cRender::Draw_Border(int x, int y, int w, int h,int s, D3DCOLOR Color,IDirect3DDevice9* mDevice)
{
Draw_Box(x, y, s, h,Color,mDevice);
Draw_Box(x,y+h, w, s,Color,mDevice);
Draw_Box(x, y, w, s,Color,mDevice);
Draw_Box(x+w,y, s,h+s,Color,mDevice);
}
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
Les #6 - Мышка и Кнопки
Для "обнаружения" курсора в нужной нам позиции опишем функцию:
Код:
BOOL cRender::IsInBox(int x,int y,int w,int h)
{
POINT MousePosition;
GetCursorPos(&MousePosition);
ScreenToClient(GetForegroundWindow(),&MousePosition);
return(MousePosition.x >= x && MousePosition.x <= x + w && MousePosition.y >= y && MousePosition.y <= y + h);
}
в класс cRender пишем прототип:
Код:
BOOL IsInBox(int x,int y,int w,int h);
Для работы с кнопками можно использовать метод GetAsyncKeyState(Байт_код), но с ней иногда бывают проблеммы(залипание, фантомное срабатывание иногда просто не действует), поэтому опишем свою функцию.
Прототип:
Код:
BOOL State_Key(int Key,DWORD dwTimeOut);
Key - байт код клавиши
dwTimeOut - время до возможности повторного действия в Мсек(не совсем точное определение)
[Ссылки могут видеть только зарегистрированные пользователи. ]
Les #7 - Блок меню + решение избавления от координат
Конфигурирование меню предлагаю вынести в отдельный зоголовочный фаил. Так будет проще ориентироваться в будущем.
Создаем заголовочный фаил Menu.h. К нему подключаем cRender
Именно в этом блоке мы и будем рисовать внешний вид.
Строить меню можно различными способами. Например самый распространенный - указывать вручную координаты для каждого элемента, но это в каком-то смысле очень трудно и накладно(хотя если развито пространственное мышление и дальномер то.....)В любом случаи я покажу как можно избавится от необходимости координировать каждый из элементов. Для этого будем использовать struct.
В класс cRender добавляем структуру с двумя переменными типи int и пишим для нее указатель.
Код:
struct stMenu
{
int x,
y;
};
stMenu pos_Menu;
pos_Menu - указатель на нашу структуру.
Теперь зделаем "иницализацию" координатного положения и возможность открывать\скрывать меню по нажатию указанной клавиши:
В cRender добавляем прототип для будущей функции:
20,20 - начальное положение
VK_END - клавиша открытия\скрытия меню(в данном случаи это клавиши END)
&Render.pos_Menu - указатель на структуру
pDevice - устройство
По дефолту у нас меню в скрытом положении. Исправить это можно иницализировав переменную Show , например в конструкторе класса:
Код:
cRender::cRender(void)
{
Show = true;
}
"Show = true;" - меню открыто сразу
"Show = false;" - меню закрыто
[Ссылки могут видеть только зарегистрированные пользователи. ]
Добавлено через 3 часа 17 минут
Les #8 - Рисуем кнопку для основного меню
Основное меню: Что это должно из себя представлять? Кнопки, строки или любые выдуманные элементы, при активации которых на экран выводилось бы соответствующее субменю. При этом, при нажатии на одну из кнопок, предедушее открытое субменю скрывалось.
Вариантов решения несколько: для каждого субменю завести переменную и при клике на кнопку сначало все переменные обнулять, а затем активировать переменную соответствующую нажатой кнопке. Второй вариант - использовать для этой цели массивы.
Именно массивы я буду использовать в туториале.
Рисовать я решил кнопку примерно следущего вида:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Основная идея следующая: Рисуем прямоугольник с обводкой и текстом. При наведении на кнопку корсором цвет текста и обводки меняется, а при активации меняется цвет текста.
Вернемся к cRender.cpp и заполним функцию Draw_Menu_But:
Код:
void cRender::Draw_Menu_But(stMenu *pos_Menu,char* text,IDirect3DDevice9* pDevice)
{
if(IsInBox(x,y,w,h))
{
//если наведен курсор
if (State_Key(VK_LBUTTON,30) )
if (Button_Mass[Button_Number]!= 1)
Button_Mass[Button_Number]= 1;
}
if (Button_Mass[Button_Number])
{
//Если активна
for ( int i = 0; i < 20; i++ )
if ( i != Button_Number )
Button_Mass[i] = 0;
}
Button_Number = Button_Number + 1;
if (Button_Max < Button_Number )
Button_Max = Button_Number;
}
Так выглядит основная рабочая часть кода.
Теперь займемся рисованием:Нам нужно завести две переменных типа D3DCOLOR для смены цвета при взаимодействии. Так же нарисовать прямоугольник, обводку и текст.
Код:
void cRender::Draw_Menu_But(stMenu *pos_Menu,char* text,IDirect3DDevice9* pDevice)
{
D3DCOLOR Bord_text = line_Color;
D3DCOLOR text_Activ = line_Color;
if(IsInBox(x,y,w,h))
{
//если наведен курсор
Bord_text = text_Activ =Lime;
if (State_Key(VK_LBUTTON,30) )
if (Button_Mass[Button_Number]!= 1)
Button_Mass[Button_Number]= 1;
}
Draw_GradientBox(x, y, w, h, DarkRed, BLACK,vertical , pDevice );
Draw_Border(x, y, w, h,1,Bord_text,pDevice);
if (Button_Mass[Button_Number])
{
//Если активна
text_Activ = YellowGreen;
for ( int i = 0; i < 20; i++ )
if ( i != Button_Number )
Button_Mass[i] = 0;
}
Draw_Text(x+(w/2),y+3,text_Activ, text,C_Text);
Button_Number = Button_Number + 1;
if (Button_Max < Button_Number )
Button_Max = Button_Number;
}
Теперь разберемся с положением в пространстве и размерами:
заводим четыре переменных типа int:
Код:
int x ,
y ,
h,
w ;
И задаем им значения:
Код:
int x = (*pos_Menu).x,
y = (*pos_Menu).y,
h = 22,
w = 101;
x = текущее положение\значение "х"(структура)
y = текущее положение\значение "y"(структура)
w = длинна кнопки
h = ширина кнопки
Указываем это в самом начале тела функции:
Код:
void cRender::Draw_Menu_But(stMenu *pos_Menu,char* text,IDirect3DDevice9* pDevice)
{
int x = (*pos_Menu).x,
y = (*pos_Menu).y,
h = 22,
w = 101;
......
......
......
......
}
При таком раскладе мы уже сможем нарисовать одну кнопку, но последующие будут накладываться друг на друга, так как у нас еще не задано "смещение". Для этого в конце теля функции добавим:
Код:
(*pos_Menu).y = y+24;
Итог:
Код:
void cRender::Draw_Menu_But(stMenu *pos_Menu,char* text,IDirect3DDevice9* pDevice)
{
int x = (*pos_Menu).x,
y = (*pos_Menu).y,
h = 22,
w = 101;
D3DCOLOR Bord_text = line_Color;
D3DCOLOR text_Activ = line_Color;
if(IsInBox(x,y,w,h))
{
Bord_text = text_Activ =Lime;
if (State_Key(VK_LBUTTON,30) )
if (Button_Mass[Button_Number]!= 1)
Button_Mass[Button_Number]= 1;
}
Draw_GradientBox(x, y, w, h, DarkRed, BLACK,vertical , pDevice );
Draw_Border(x, y, w, h,1,Bord_text,pDevice);
if (Button_Mass[Button_Number])
{
text_Activ = YellowGreen;
for ( int i = 0; i < 20; i++ )
if ( i != Button_Number )
Button_Mass[i] = 0;
}
Draw_Text(x+(w/2),y+3,text_Activ, text,C_Text);
Button_Number = Button_Number + 1;
if (Button_Max < Button_Number )
Button_Max = Button_Number;
(*pos_Menu).y = y+24;
}
Смысл: При вызове считываем координаты со структуры -> рисуем задуманое -> записываем в структуру новые координаты.
В результате мы получаем:
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
Les #9 - Рисуем checkBox
Цель: Нарисовать элемент который имел бы два положения - вкл\выкл.
Этот и последующие элементы будут всегда пренадлежать суб-меню. Поэтому что бы не замарачиваться с начальной координатой положения - допишем еще одну переменную в структуру stMenu:
void cRender::Draw_CheckBox(stMenu *pos_Menu,bool &Var,char *Text,IDirect3DDevice9 *pDevice)
{
int x = (*pos_Menu).x+115,
y = (*pos_Menu)._y,
w = 16,
h = 16;
Draw_Box( x, y, w, h, 0xff305C5F,pDevice);
Draw_Border( x, y, w, h,1, line_Color,pDevice);
if(IsInBox(x,y ,w,h))
{
if (State_Key(VK_LBUTTON,300) )
Var=!Var;
}
if(Var)
Draw_GradientBox(x+3, y+3, 11, 11, DarkRed, BLACK,vertical , pDevice );
Draw_Text(x+24,y+2,line_Color, Text,L_Text);
(*pos_Menu)._y = y+20;
}
Система думаю будет ясна из предыдущего тутора.
Единственное о чем можно сказать, так это о "x = (*pos_Menu).x+115".
(*pos_Menu).x - это начальное положение меню
+115 - это цифра определяет смещение от начала.Складывается она из длинны Draw_Menu_But() + небольшое дополнение, что бы был зазор между основным меню и суб-меню.
Так или иначе нам придется использовать переменные различного типа. Удобнее всего будет завести структуру и обращатся к переменным с помощью указателя на структуру.
Для этого создадим заголовочный фаил "Structure.h" с таким содержинием:
Код:
#pragma once
struct cFun
{
};
extern cFun Fun;
Цитата:
#pragma once — нестандартная, но широко распространенная препроцессорная директива, разработанная для контроля за тем, чтобы конкретный исходный файл при компиляции подключался строго один раз
В фаиле cRender.h подключим его
Код:
#include "Structure.h"
так же поступим и в фаиле Base.cpp и опишем указатель на структуру:
Код:
#include "Structure.h"
cFun Fun;
теперь указав внутри структуры необходимую нам переменную:
Код:
struct cFun
{
bool _Peremennay;
};
мы можем получить доступ к ней практически из любого места проекта используя указатель "Fun":
Код:
Fun._Peremennay
Добавлено через 20 минут
Завершение.
В конечном проекте я завел две переменных в классе cRender:
Код:
w_menu //ширина основного меню
w_sub_menu
w_menu - ширина основного меню
w_sub_menu - ширина суб-меню
Так же эти переменные влияют на: размеры кнопок, ширину фона, растояние между текстом и контролом.
Добавил задний фон и заголовок. Заголовок вынес в Init_PosMenu():
Render.Init_PosMenu(20,20,VK_END,"D3D9 Menu от А до Я",&Render.pos_Menu,pDevice);
Немного поправил элементы управления, добавив выравнивание по краям:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Так же в структуре stMenu завел две переменных:
Код:
int height_fon,
height_sub_fon;
Эти переменные отвечают за ширину фона меню. То есть, при дабавлении луюого из элементов управления задний фон будет подгонятся под размеры.
Так же все цвета вынес в константы, что бы можно было легко изменить цвет лубого из элементов меню.
Сконфигурировал меню по такому виду:
[Ссылки могут видеть только зарегистрированные пользователи. ]
В итоге получил:
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
________________
-Отложи на послезавтра то что можешь сделать сегодня, и тогда у тебя появятся два свободных дня!
Последний раз редактировалось крайслер; 08.12.2013 в 09:01.
Причина: Добавлено сообщение
Вот побольше бы таких статей. Одна из очень немногих статей за последнее время, которая действительно что-то стоит. Продолжайте в том же духе!
________________
Принимаются пожертвования любых размеров в фонд поддержки начинающих программистов
Кошельки: WMZ - Z276844220882; WMR - R231028582939; WMU - U394136909210; ЯД - 410011494605270.