{$APPTYPE CONSOLE}
uses
SysUtils;
begin
{ TODO -oUser -cConsole Main :
Insert code here }
end.
Ну, или иного содержания в зависимости от версии программы, можно начинать "кодинг". Я опишу программирование трехмерного объекта - куба, ведь для любой красивой игры нужна 3d-графика, но начинать что-либо всегда приходится с нуля, а мы и начнем с самых азов трехмерного программирования...
Шаг 3.
Здесь будет написан сам код, а пояснения к каждой строчке будут ограничены двумя галочками "//" или скобками {} (пояснение не является частью кода и в финальной стадии ничему не служит).
program window;
uses
Windows,
Messages, //Библиотека, отвечающая за прорисовку окна и объектов на нем
OpenGL; //Библиотека OpenGL
const
WND_TITLE = 'http://www.mipstudio.ru/'; // заголовок окна
FPS_TIMER = 1; // Таймер, чтобы считать FPS
FPS_INTERVAL = 100; // Отсчет fps каждые 100 миллисекунд
RAIN_TIMER = 2;
type TGLCoord = Record // Координаты
X, Y, Z : glFloat;
end;
var
h_Wnd : HWND; // Глобальный дескриптор окна
h_DC : HDC; // Глобальный контекст устройства
h_RC : HGLRC; // OpenGL контекст прорисовки
keys : Array[0..255] of Boolean; // массив клавиш
FPSCount : Integer = 0; // Счетчик для FPS
ElapsedTime : Integer; // Время, затраченное на прорисовку кадра
ElapsedTime2 : Integer;
xVert, yVert : glFloat; // Переменные, отвечающие за поворот объекта на оси
{$R *.RES}
////////////////////////////////////////
{ Процедура по заданию координат объекта }
////////////////////////////////////////
procedure DrawBox;
begin
glBegin(GL_QUADS);
//Низ куба
glTexCoord2f(0, 0); glVertex3f(-1, -1, 1);
//На примере двух верхних строк разберем все остальные,
//первая половина строки - это координаты текстуры, а
//вторая - это координаты вершины по x, y, z, как вы
//понимаете, в кубе четыре вершины, значит, и строчек
//с координатами вершин тоже должно быть четыре.
glcolor3f(1, 1, 1); // А эта строка отвечает за цветовое оформление
//стороны куба, соответственно Red, Green, Blue в разных
//сочетаниях и оттенках, чтобы стороны не сливались.
glTexCoord2f(0, 1); glVertex3f( 1, -1, 1);
glTexCoord2f(1, 1); glVertex3f( 1, -1, -1);
glTexCoord2f(1, 0); glVertex3f(-1, -1, -1);
//Верх куба
glcolor3f(1, 0, 0);
glTexCoord2f(0, 0); glVertex3f(-1, 1, 1);
glTexCoord2f(0, 1); glVertex3f( 1, 1, 1);
glTexCoord2f(1, 1); glVertex3f( 1, 1, -1);
glTexCoord2f(1, 0); glVertex3f(-1, 1, -1);
//Передняя грань
glcolor3f(0, 1, 0);
glTexCoord2f(0, 0); glVertex3f(-1, 1, -1);
glTexCoord2f(0, 1); glVertex3f( 1, 1, -1);
glTexCoord2f(1, 1); glVertex3f( 1,-1, -1);
glTexCoord2f(1, 0); glVertex3f(-1,-1, -1);
//Задняя грань
glcolor3f(0, 0, 1);
glTexCoord2f(0, 0); glVertex3f(-1, 1, 1);
glTexCoord2f(0, 1); glVertex3f( 1, 1, 1);
glTexCoord2f(1, 1); glVertex3f( 1,-1, 1);
glTexCoord2f(1, 0); glVertex3f(-1,-1, 1);
//Правый бок
glcolor3f(1, 1, 0);
glTexCoord2f(0, 0); glVertex3f(-1, 1, 1);
glTexCoord2f(0, 1); glVertex3f(-1, 1,-1);
glTexCoord2f(1, 1); glVertex3f(-1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(-1,-1, 1);
//Левый бок
glcolor3f(0, 1, 1);
glTexCoord2f(0, 0); glVertex3f(1, 1, 1);
glTexCoord2f(0, 1); glVertex3f(1, 1,-1);
glTexCoord2f(1, 1); glVertex3f(1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(1,-1, 1);
glEnd;
end;
///////////////////////////////////////
{ Функция для прорисовки текущей сцены}
///////////////////////////////////////
procedure glDraw();
begin
glClear(GL_COLOR_BUFFER_BIT or GL_ DEPTH_BUFFER_BIT); // Очистка экрана и буфера глубины
glLoadIdentity(); // Сбрасываем установки вида
glTranslatef(0.0,0.0,-5); // Расположение объекта в пространстве
glRotatef(xVert, 1, 0, 0); // Угол поворота объекта
glRotatef(yVert, 0, 1, 0);
DrawBox; // Вызов координат объекта и прорисовка по ним
end;
/////////////////////////
{ Инициализация OpenGL}
////////////////////////
procedure glInit();
var I, J : Integer;
begin
glClearColor(0.0, 0.0, 0.0, 0.0); // Черный фон
glShadeModel(GL_SMOOTH);
glClearDepth(1.0); // Установка буфера глубины
glEnable(GL_DEPTH_TEST); // Включаем буфер глубины
xVert :=0; // Задаем начальный-стартовый поворот объекта
yVert :=30;
end;
///////////////////////////
{ Изменение размеров окна}
///////////////////////////
procedure glResizeWnd(Width, Height : Integer);
begin
if (Height = 0) then // чтобы не было деления на ноль
Height := 1;
glViewport(0, 0, Width, Height); // Устанавливаем окно вывода для OpenGL
glMatrixMode(GL_PROJECTION); // Изменяем режим матрицы
glLoadIdentity(); // Сбрасываем установки вида
gluPerspective(45.0, Width/Height, 1.0, 100.0); // Установка перспективы
glMatrixMode(GL_MODELVIEW); // Возвращаем режим матрицы к modelview
glLoadIdentity(); // Сбрасываем установки вида
end;
////////////////////////////////////////
{ Обработка нажатия клавиш клавиатуры }
///////////////////////////////////////
procedure ProcessKeys;
begin
if (keys[VK_UP]) then xVert := xVert - 0.2; // При нажатии на соответствующие
if (keys[VK_DOWN]) then xVert := xVert + 0.2; // кнопки стрелок объект будет
if (keys[VK_RIGHT]) then yVert := yVert - 0.2; // поворачиваться.
if (keys[VK_LEFT]) then yVert := yVert + 0.2;
end;
////////////////////////////////////////
{ Оконная функция для обработки сообщений}
////////////////////////////////////////
function WndProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
case (Msg) of
WM_KEYDOWN: // Устанавливаем параметр нажатия клавиши
//(wparam) в значение true, чтобы мы могли проверить нажатие клавиши
begin
keys[wParam] := True;
Result := 0;
end;
WM_KEYUP: // Устанавливаем параметр нажатия клавиши
//(wparam) в значение false, чтобы мы могли проверить нажатие клавиши
begin
keys[wParam] := False;
Result := 0;
end;
WM_SIZE: // Изменяем размер окна с новыми width и height
begin
glResizeWnd(LOWORD(lParam),HIWORD( lParam));
Result := 0;
end;
WM_TIMER : // Обработка события таймер
begin
if wParam = FPS_TIMER then
begin
FPSCount :=Round(FPSCount * 1000/FPS_INTERVAL);
FPSCount := 0;
Result := 0;
end
end;
else
Result := DefWindowProc(hWnd, Msg, wParam, lParam); // По умолчанию возвращаем //ничего не случилось
end;
end;
////////////////////////////////////////
{ Корректно уничтожает окно, созданное нами}
////////////////////////////////////////
procedure glKillWnd(Fullscreen : Boolean);
begin
// Делаем текущий контекст устройства нетекущим и освобождаем контекст
// устройства, которое использовалось как контекст вывода
if (not wglMakeCurrent(h_DC, 0)) then
MessageBox(0, 'Ошибка', 'Error', MB_OK or MB_ICONERROR);
// Пробуем удалить контекст вывода
if (not wglDeleteContext(h_RC)) then
begin
MessageBox(0, 'Ошибка', 'Error', MB_OK or MB_ICONERROR);
h_RC := 0;
end;
// Пробуем удалить контекст устройства
if ((h_DC = 1) and (ReleaseDC(h_Wnd, h_DC) <> 0)) then
begin
MessageBox(0, 'Ошибка', 'Error', MB_OK or MB_ICONERROR);
h_DC := 0;
end;
// Пробуем уничтожить окно
if ((h_Wnd <> 0) and (not Destroy Window (h_Wnd))) then
begin
MessageBox(0, 'Не могу уничтожить окно', 'Error', MB_OK or MB_ICONERROR);
h_Wnd := 0;
end;
// Пробуем удалить регистрацию класса окна
if (not UnRegisterClass('OpenGL', hInstance)) then
begin
MessageBox(0, 'Не могу удалить регистрацию класса окна!', 'Error', MB_OK or MB_ICONERROR);
hInstance := 0;
end;
end;
// Функции, описанные выше, служат для оповещения о соответствующих ошибках.
// Они очень важны и нельзя о них забывать!
////////////////////////////////////////
{ Создает окно и сопоставляет ему OpenGL rendering context }
////////////////////////////////////////
function glCreateWnd(Width, Height : Integer; Fullscreen : Boolean; PixelDepth : Integer) : Boolean;
var
wndClass : TWndClass; // Класс Окно
dwStyle : DWORD; // Стиль окна
dwExStyle : DWORD; // Расширенные стили окна
dmScreenSettings : DEVMODE; // Установки экрана (fullscreen, и т.д...)
PixelFormat : GLuint; // Установки для OpenGL визуализации
h_Instance : HINST; // Текущий экземпляр
pfd : TPIXELFORMATDESCRIPTOR; // Установки для OpenGL окна
begin
h_Instance := GetModuleHandle(nil);
//Получаем экземпляр для нашего окна
ZeroMemory(@wndClass, SizeOf(wndClass)); // Очищаем структуру класса окна
with wndClass do // Устанавливаем класс Окно
begin
style := CS_HREDRAW or // Перерисовка окна в случае изменения его ширины
CS_VREDRAW or // Перерисовка окна в случае изменения его высоты
CS_OWNDC; // Уникальный device context для окна
lpfnWndProc := @WndProc; // Оконная функция для обработки сообщений
hInstance := h_Instance;
hCursor := LoadCursor(0, IDC_ARROW);
lpszClassName := 'OpenGL';
end;
if (RegisterClass(wndClass) = 0) then // Пробуем зарегистрировать новый оконный класс begin
MessageBox(0, 'Невозможно зарегистрировать класс окна!', 'Error', MB_OK or MB_ICONERROR);
Result := False;
Exit
end;
// Если задано, переходим в полноэкранный режим
if Fullscreen then
begin
ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
with dmScreenSettings do begin // Устанавливаем параметры экрана
dmSize := SizeOf(dmScreenSettings);
dmPelsWidth := Width; // Ширина экрана
dmPelsHeight := Height; // Высота экрана
dmBitsPerPel := PixelDepth; // Глубина цвета
dmFields := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL;
end;
// Пробуем переключиться в полноэкранный режим
if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN) = DISP_CHANGE_FAILED) then
begin
MessageBox(0, 'Невозможно переключиться на полный экран', 'Error', MB_OK or MB_ICONERROR);
Fullscreen := False;
end;
end;
// Если мы в полноэкранном режиме, то
if (Fullscreen) then
begin
dwStyle := WS_POPUP or // Создаем popup окно
WS_CLIPCHILDREN
or WS_CLIPSIBLINGS;
dwExStyle := WS_EX_APPWINDOW; // Максимальный приоритет окна
ShowCursor(False); // Убираем курсор с экрана (чтобы не мешал)
end
else
begin
dwStyle := WS_OVERLAPPEDWINDOW or
// Создаем overlapping окно
WS_CLIPCHILDREN or
WS_CLIPSIBLINGS;
:= WS_EX_APPWINDOW or // Максимальный приоритет окна
WS_EX_WINDOWEDGE; // Граница с выступающим контуром
end;
// Непосредственно создаем окно
h_Wnd := CreateWindowEx(dwExStyle, // расширенные стили окна
'OpenGL', // Имя класса
WND_TITLE, // Название окна (заголовок)
0, 0, // Позиция окна
Width, Height, // Размер окна
0, // Дескриптор родительского окна
0, // Дескриптор меню
h_Instance, // Дескриптор копии приложения
nil); // Ничего не передаем в WM_CREATE
if h_Wnd = 0 then
begin
glKillWnd(Fullscreen); // Отменяем все установки, сделанные нами
MessageBox(0, 'Уничтожение окна невозможно', 'Error', MB_OK or MB_ICONERROR);
Result := False;
Exit;
end;
// Пробуем получить контекст устройства
h_DC := GetDC(h_Wnd);
if (h_DC = 0) then
begin
glKillWnd(Fullscreen);
MessageBox(0, 'Ошибка при обращении к устройству', 'Error', MB_OK or MB_ICONERROR);
Result := False;
Exit;
end;
// Установки для OpenGL
with pfd do
begin
nSize := SizeOf(TPIXELFORMATDESCRIPTOR); // Размер этого дескриптора формата точек
nVersion := 1; // Версия этой структуры данных
dwFlags := PFD_DRAW_TO_WINDOW // Буфер, поддерживающий вывод в окно
or PFD_SUPPORT_OPENGL // Буфер, поддерживающий OpenGL рисование
or PFD_DOUBLEBUFFER; // Поддержка двойной буферизации
iPixelType := PFD_TYPE_RGBA; // Формат цвета - RGBA
cColorBits := PixelDepth; // OpenGL глубина цвета
cDepthBits := 16; // Глубина цвета буфера глубины
end;
// Пробуем найти формат точек, поддерживаемый устройством, что лучше, чем присваивать свой //формат
PixelFormat := ChoosePixelFormat(h_DC, @pfd);
if (PixelFormat = 0) then
begin
glKillWnd(Fullscreen);
MessageBox(0, 'Невозможно найти соответствующий формат пиксела', 'Error', MB_OK or MB_ICONERROR);
Result := False;
Exit;
end;
// Устанавливаем определенный устройством формат точек (содержится в PixelFormat)
if (not SetPixelFormat(h_DC, PixelFormat, @pfd)) then
begin
glKillWnd(Fullscreen);
MessageBox(0, 'Невозможно установить соответствующий формат пиксела', 'Error', MB_OK or MB_ICONERROR);
Result := False;
Exit;
end;
// Создаем контекст прорисовки OpenGL (rendering context)
h_RC := wglCreateContext(h_DC);
if (h_RC = 0) then
begin
glKillWnd(Fullscreen);
MessageBox(0, 'Ошибка', 'Error', MB_OK or MB_ICONERROR);
Result := False;
Exit;
end;
if (not wglMakeCurrent(h_DC, h_RC)) then
begin
glKillWnd(Fullscreen);
MessageBox(0, 'Не могу активировать контекст OpenGL', 'Error', MB_OK or MB_ICONERROR);
Result := False;
Exit;
end;
// Инициализируем таймер для подсчета FPS
SetTimer(h_Wnd, FPS_TIMER, FPS_INTERVAL, nil);
// Устанавливаем окно так, чтобы быть уверенными, что у него наибольший приоритет
ShowWindow(h_Wnd, SW_SHOW);
SetForegroundWindow(h_Wnd);
SetFocus(h_Wnd);
// Пробуем изменить размер, чтобы убедиться, что все работает нормально
glResizeWnd(Width, Height);
glInit();
Result := True;
end;
////////////////////////////////////////
{ Главный цикл обработки сообщений приложения }
////////////////////////////////////////
function WinMain(hInstance : HINST; hPrevInstance : HINST;
lpCmdLine : PChar; nCmdShow : Integer) : Integer; stdcall;
var
msg : TMsg;
finished : Boolean;
DemoStart, LastTime : DWord;
begin
finished := False;
// Выполняем инициализацию приложения (создаем окно):
if not glCreateWnd(800, 600, true, 32) then //Объявление разрешения и количество бит
begin
Result := 0;
Exit;
end;
// Главный цикл:
while not finished do
begin
if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then // Проверяем, было ли сообщение для этого окна
begin
if (msg.message = WM_QUIT) then // Выходим, если получили WM_QUIT
finished := True
else
begin // Иначе транслируем и диспетчируем полученное сообщение
TranslateMessage(msg);
DispatchMessage(msg);
end;
end
else
begin
Inc(FPSCount); // Увеличиваем счетчик FPS
LastTime :=ElapsedTime;
ElapsedTime :=GetTickCount() - DemoStart; // Считаем затраченное время
ElapsedTime :=(LastTime + ElapsedTime) DIV 2; // Усредняем с предыдущим временем для гладкости вывода счетчика
glDraw(); // Рисуем сцену
SwapBuffers(h_DC); // Показываем сцену
if (keys[VK_ESCAPE]) then // Если пользователь нажал ESC, то выходим
finished := True
else
ProcessKeys; // Проверяем нажатие любых других клавиш
end;
end;
glKillWnd(FALSE);
Result := msg.wParam;
end;
begin
WinMain( hInstance, hPrevInst, CmdLine, CmdShow );
end.
// Все, проект готов и отлично функционирует!!! Шаг 4.Как видите, не такой уж сложный и большой код, а по меркам серьезного проекта - вообще смешно, но если представить, что только что, своими руками, не рисуя ни в каком 3D графическом пакете, вы создали куб, даже более, вы создали полностью самостоятельную, полноэкранную программу, рассчитывающую в реальном времени координаты куба с использованием библиотеки OpenGl. Не правда ли, после таких слов повышается самооценка? В дальнейшем, возможно с моей помощью или своими стараниями, вы можете дополнить этот проект текстурами, загружаемыми из любых форматов рисунков или, изменить объекты на сцене, в общем, что вашей душе угодно.