Полезные функции и процедуры: часть 1 - Delphi, Pascal, ObjectPascal - Программирование
Навигация по сайту
Сайт:

Дополнительно:

Файловый архив:

Каталог статей:

Форум:


Категории раздела
Delphi, Pascal, ObjectPascal [18]
Программирование на Delphi, Pascal, ObjectPascal
C, C++, C# [7]
Программирование на C, C++, C#
ПХП (PHP) [6]
Все что связано с программированием на PHP.
DirectX [0]
Программирование с использованием графического API DirectX
OpenGL [0]
Программирование с использованием графического API OpenGL
Работа с базами данных (БД) [0]
Работа с базами данных MySQL и т.д. Разработка, теории, алгоритмы.
Сетевое программирование [0]
Сетевое программирование, организация сетей.
Программирование игр [0]
Все что связано с программированием игр, организацией их разработки.
Работа с мультимедиа данными [0]
Загрузка, обработка, воспроизведение и все что связано со звуком и видео.
Работа с устройсвами ввода и вывода [0]
Программирование устройств ввода и вывода. Работа с геймпадом, рулем и многим другим.
Программирование HTML 5 игр [0]
Программирование HTML 5 игр, html верстка, JS (JavaScript)
Остальное [0]
Все остальное, что не попадает ни под одну категорию.

Мини-Опрос
Какие языки программирования вы знаете?
Всего ответов: 773

Партнеры сайта
....

 Главная » Статьи » Программирование » Delphi, Pascal, ObjectPascal » Полезные функции и процедуры: часть 1

Полезные функции и процедуры: часть 1

00:31
В этой и следующих статьях я расскажу о некоторых полезностях. Профи они ни к чему, но новичкам могут пригодиться. Здесь рассмотрены многие разделы программирования: математика, графика, сети и т.д. Сразу оговорюсь, что написано всё на Delphi. И еще: то, в чем я не уверен на 99%, я приводить не буду. Скорее всего, будут только уже использовавшиеся участки кода. Надеюсь, что кому-нибудь это пригодится.

Глава 1. Математические функции и процедуры.

Математика - сложная, но требуемая в программировании вещь. Тот, кто плохо знает математику, вряд ли сможет написать даже простой шутер - нужны поворот за мышью, стрельба, движение... Это все требует тригонометрических расчетов и многого другого. В общем, смотрите.

1. Константы.

В некоторых функциях нельзя обойтись без этих констант. Хотя можно использовать их альтернативы, например, функцию DegToRad, их использование в чем-то быстрее (не нужно дополнительное деление, к примеру).

const
PiDiv2 = Pi / 180;
Константа PiDiv2 используется для перевода градусов в радианы и обратно. Для первого умножайте градусы на это число, для второго - делите.

2. Поворот точки.

Самая простая вещь. На эту тему говорилось много, но вдруг кто не знает.

function RotateX(Angle, X, Y: Real; Xc: Real = 0; Yc: Real = 0): Real;
var
Radians: Real;
begin
Radians := Angle * PiD2;
RotateX := (X - Xc) * Cos(Radians) - (Y - Yc) * Sin(Radians) + Xc;
end;

function RotateY(Angle, X, Y: Real; Xc: Real = 0; Yc: Real = 0): Real;
var
Radians: Real;
begin
Radians := Angle * PiD2;
RotateY := (Y - Yc) * Cos(Radians) + (X - Xc) * Sin(Radians) + Yc;
end;

function RotatePoint(Angle: Real; Point: TPoint; Xc: Real = 0; Yc: Real = 0): TPoint;
begin
RotatePoint.X := Round(RotateX(Angle, Point.X, Point.Y, Xc, Yc));
RotatePoint.Y := Round(RotateY(Angle, Point.X, Point.Y, Xc, Yc));
end;
Здесь X и Y или Point - координаты точки, Xc и Yc - координаты центра, вокруг которого точку нужно повернуть на угол Angle. При повороте не забывайте, что в качестве параметров должны быть координаты не повернутой точки (а то бывает, что повернут по оси X и при повороте по Y используют эту координату, а потом удивляются - почему глючит).

3. Угол между точками.

Это немного сложнее, но тоже легко - на уровне тригонометрии девятого класса. В основном используется для поворота игрока за курсором мыши и т.п.

function GetAngle(X, Y: Real): Real;
begin
GetAngle := -(ArcSin(X / Sqrt((X * X + Y * Y))))) / PiDiv2;
end;
Следует отметить, что точка должна находиться относительно в центре координат. Таким образом, для получения угла между точками нужно подставить значения (X - X0), (Y - Y0).

4. Траектория.

Иногда требуется найти позицию тела, движущегося по определенной траектории, в момент времени. Для этого существует специальная функция:

function GetTrajecPos(X, Y, Speed, Angle, Time: Real): TPoint;
begin
GetTrajecPos.X := Round(X + Speed * Cos(Angle) * Time);
GetTrajecPos.Y := Round(Y + Speed * Sin(Angle) * Time + Sqr(Pi * Time) / 2);
end;
Что такое X, Y, Speed, Angle и Time - это, я думаю, и так понятно. Как можно заметить, тут не указана масса тела, однако использовать формулу можно и без нее - уменьшать скорость движения тела обратно пропорционально массе.

5. Псевдослучайные числа.

Случайных чисел в природе не существует - так учит нас математика. Генераторы псевдослучайных чисел существуют практически во всех языках программирования. Но иногда целесообразнее пользоваться своим собственным генератором, нежели стандартным. Для рассказа о том, как его написать, приведу одну мессагу из эхи FIDO7 RU.HACKER за далекий 1996 год:

- Area: RU.HACKER ------------------------------------------------------------
Msg#: 232 Date: 11 Feb 96 18:27:00
From: Vladimir Gorpenko Read: No Replied: No
To: Tolya Zherdev Mark:
Subj: random number
------------------------------------------------------------------------------
Здpавствуй, Tolya!

08 Feb 96 10:13 Tolya Zherdev писал Porfiriev Sergey следующее:

TZ> Для pавномеpного pаспpеделения:
TZ> X(I+1)=F{11X(I)+Pi}, пpи X(0)=0.5 дает около 8000 не повтоpяющихся чисел.

8000 - это что-то очень плохо. Hа 16-pазpядных целых должны получаться все
65536 неповтоpяющихся чисел.

А что, наpод Кнута совсем читать не хочет?

Вот pецепт (из "выводов" к pазделу о генеpатоpах псевдослучайных чисел):

"наилучший" и "пpостейший" датчик случайных чисел получается по фоpмуле

X(I+1)=(a*X(I)+c) mod m

Умножение должно быть точным (без окpугления и тому подобного).

Пpи выбоpе X(0),a,c и m необходимо соблюдать некотоpые пpавила и
использовать случайные числа осмысленно, pуководствуясь следующими пpинципами.

i) Число X(0) может быть пpоизвольным.

ii) Число m должно быть велико. Удобно выбиpать его pавным pазмеpу слова
вычислительной машины (Кнут явно имеет в виду 2**k, где k - pазpядность слова
или pазpядность слова за вычетом знакового pазpяда - пpим. Го), поскольку пpи
этом эффективно вычисляется (a*X+c) mod m.

iii) Если m пpедставляет собой степень двойки (т.е. если используется машина,
pаботающая в двоичной системе счисления), выбеpите a таким, чтобы a mod 8 = 5.
Пpи таком выбоpе величины a, пpи условии, что c выбиpается описанным ниже
способом, гаpантиpуется, что датчик случайных чисел даст все m возможных
pазличных значений X, пpежде чем они начнут повтоpяться и, кpоме того,
гаpантиpуется высокая "мощность".

iv) Множитель a должен пpевосходить величину Sqrt(m), желательно, чтобы он был
больше m/100, но меньше m-Sqrt(m). Последовательность pазpядов в двоичном
пpедставлении a не должна иметь пpостого, pегуляpного вида. Высказанных
сообpажений обычно бывает достаточно, но если датчик случайных чисел
используется интенсивно, множитель a, кpоме того, следует выбиpать так, чтобы
удовлетвоpялся "спектpальный тест".

v) Постоянная c должна быть pавна нечетному числу. Желательно выбиpать c таким
обpазом, чтобы отношение c/m было бы пpиблизительно pавно величине
0.2113248654051871.

vi) Менее значимые pазpяды X не очень хоpоши, лучше pассматpивать X как дpобь
X/m в интеpвале между 0 и 1.

За обоснованием, pазъяснением непонятных слов, методами пpовеpки датчиков -
пожалуйста, к Кнуту.

TZ> Hу а начальную инициализацию фоpмулы (Hапp. X(0) или I0) делаешь в
TZ> зависимости от текущего вpемени.

Это точно, только хоpошо бы сpазу кpутануть генеpатоp хоть pазок, чтобы
pазбpос пеpвых значений не так кучковался пpи последовательных запусках.

С уважением,
Vladimir

-!- GoldED/386 2.42.G0614
! Origin: Огоpошенный сyдьбою, ты все же не отчаивайся! (fidonet 2:5020/157.6)
Сначала кажется, что числа для подобного генератора найти сложно, однако можно удовлетворить практически все эти условия:

m = 65536;
a = 35789;
c = 13849;

Так получаем формулу: X(I+1) = (35789 * X(I) + 13849) mod 65536.

Таким образом, можно написать свою процедуру Randomize и свою функцию Random:

var
OldX: Integer;

procedure Randomize;
begin
OldX := GetTickCount;
end;

function Random(I: Integer): Integer;
var
X: Integer;
begin
X := (35789 * OldX + 13849) mod 65536;
OldX := X;
Result := X mod I;
end;
Естественно, что такая функция Random будет поддерживать только 16-битные числа, однако на практике этого в 90% случаев достаточно. Для поддержки больших чисел нужно будет подбирать новые значения a и c - m тогда должно соответствовать количеству байт под число.

Глава 2. Графические функции и процедуры.

Графика - тоже серьезная вещь. Например, новичок в API не сможет загрузить битмап из файла, если не будет знать его размеры. От "мерцания" он тоже вряд ли сможет избавиться.

1. Поверхности.

О создании поверхностей можно почитать в статье "Поверхности: как это делается". Использование поверхностей избавляет от "мерцания".

2. Загрузка битмапа.

Рисунки в формате BMP используются практически везде. Формат нетрудный, поддерживается Windows, легко сжимается и прочая, и прочая. Но стандартные функции Windows позволяют загрузить его из файла, только если будет известен размер. Для нормальной загрузки битмапа из файла может послужить следующая функция:

function LoadBitmapFromFile(FileName: String): HBITMAP;
var
// Файл для загрузки заголовка битмапа.
F: File;
// Заголовочные структуры.
BitmapFileHeader : TBITMAPFILEHEADER;
BitmapInfoHeader : TBITMAPINFOHEADER;
// Счетчик чтения файла. Применяется для проверки.
ReadCount : DWORD;
begin
LoadBitmapFromFile := 0;
// Открытие файла и установка на начало.
AssignFile(F, FileName);
Reset(F, 1);
// Чтение заголовка TBitmapFileHeader.
BlockRead(F, BitmapFileHeader, SizeOf(TBitmapFileHeader), ReadCount);
if (ReadCount <> SizeOf(TBitmapFileHeader)) then
begin //Ошибка чтения.
CloseFile(F);
Exit;
end;
// Чтение заголовка TBitmapInfoHeader.
BlockRead(F, BitmapInfoHeader, SizeOf(TBitmapInfoHeader), ReadCount);
if (ReadCount <> SizeOf(TBitmapInfoHeader)) then
begin //Ошибка чтения.
CloseFile(F);
Exit;
end;
CloseFile(F);
with BitmapInfoHeader do
begin
Result := LoadImage(HInstance, PChar(FileName), IMAGE_BITMAP,
biWidth, biHeight, LR_LOADFROMFILE);
SetBitmapDimensionEx(Result, biWidth, biHeight, nil);
end;
end;
При возникновении ошибки функция вернет 0. Я частично взял эту функцию из статьи Мироводина Дмитрия "Низкоуровневая загрузка растра", которую можно найти на DelphiGFX. Правда, здесь больше преимуществ: меньше размер, один и тот же код для разных типов и пр.

3. Отображение рисунка частями (pattern drawing).

Часто требуется отобразить не весь рисунок, а только какую-либо его часть. Примеров куча: графические редакторы, игры и т. п.

Для следующего примера потребуется переменная Brush типа HBrush, инициализированная с помощью функции CreatePatternBrush(Handle), где Handle - это описатель битмапа. Удаляется эта кисть с помощью DeleteObject(Brush). Можно это делать прямо в процедуре, но тогда сильно снизится скорость. Также перед этим должна быть вызвана функция SetBitmapDimensionEx для того, чтобы процедура могла получить размер рисунка. Эту функцию (SetBitmapDimensionEx) достаточно вызвать только один раз, при изменении размеров рисунка.

procedure DrawPattern(DC: HDC; X, Y: Integer; PatWidth, PatHeight: Integer;
Brush: HBRUSH; Bitmap: HBITMAP; Pattern: Integer = -1);
var
BmpSize: SIZE;
OldObj: HGDIOBJ;
OldOrg: TPoint;
begin
OldObj := SelectObject(DC, Brush);
//Получить размер рисунка.
GetBitmapDimensionEx(Bitmap, BmpSize);
if (Pattern = -1) then
begin //Отобразить весь рисунок.
SetBrushOrgEx(DC, X, Y, @OldOrg);
PatBlt(DC, X, Y, BmpSize.cX, BmpSize.cY, PATCOPY);
end
else
begin //Отобразить только часть рисунка.
SetBrushOrgEx(DC, X - (Pattern mod (BmpSize.cX div PatWidth)) * PatWidth,
Y - (Pattern div (BmpSize.cX div PatWidth)) * PatHeight, @OldOrg);
PatBlt(DC, X, Y, PatWidth, PatHeight, PATCOPY);
end;
SetBrushOrgEx(DC, OldOrg.X, OldOrg.Y, nil);
SelectObject(DC, OldObj);
end;
Теперь пояснения. Windows использует BrushOrg, чтобы рисовать с определенной точки. Так частенько поступают графические редакторы при предпросмотре смещения рисунка. А мы используем это, чтобы отобразить только часть рисунка.

4. Отображение рисунка с поворотом.

Нижеследующий пример дает изображение похуже, чем те же OpenGL или DirectDraw, но зато не требует дополнительных библиотек. Тут есть и еще один большой недостаток: данный код работоспособен только под Windows семейства NT. Если его немного переделать, то можно отображать рисунок частями. Те, кто прочел первую главу, наверняка заметят некоторое сходство - тут использовались те же формулы.

procedure DrawRotated(DC: HDC; Bitmap: HBITMAP; X, Y: Integer; Angle: Real);
var
TempDC: HDC;
BmpSize: SIZE;
Radians: Real;
Points: array[0..2] of TPoint;
Xc, Yc: Integer;
begin
TempDC := CreateCompatibleDC(DC);
SelectObject(TempDC, Bitmap);
GetBitmapDimensionEx(Bitmap, BmpSize);
Radians := Angle * Pi / 180;

with BmpSize do
begin
//Находим центр.
Xc := X + (cX div 2);
Yc := Y + (cY div 2);

//Считаем координаты.
Points[0].X := Round((X - Xc) * Cos(Radians) - (Y - Yc) * Sin(Radians) + Xc);
Points[0].Y := Round((Y - Yc) * Cos(Radians) + (X - Xc) * Sin(Radians) + Yc);

Points[1].X := Round((X + cX - Xc) * Cos(Radians) - (Y - Yc) * Sin(Radians) + Xc);
Points[1].Y := Round((Y - Yc) * Cos(Radians) + (X + cX - Xc) * Sin(Radians) + Yc);

Points[2].X := Round((X - Xc) * Cos(Radians) - (Y + cY - Yc) * Sin(Radians) + Xc);
Points[2].Y := Round((Y + cY - Yc) * cos(Radians) + (X - Xc) * Sin(Radians) + Yc);

//Выводим.
PlgBlt(DC, Points, TempDC, 0, 0, cX, cY, 0, 0, 0);
DeleteDC(TempDC);
end;
end;
И опять-таки можно сильно оптимизировать этот процесс: TempDC используется лишь для копирования с нее изображения. Каждый раз создавать и удалять контекст устройства - долго. Лучше сразу после загрузки изображения создавать DC и выбирать Bitmap для него, а когда он уже больше не нужен - удалять. Таким же образом можно оптимизировать и предыдущую процедуру:

procedure DrawPattern(DC, TempDC: HDC; X, Y, PatWidth, PatHeight: Integer;
Bitmap: HBITMAP; Pattern: Integer = -1);
var
BmpSize: SIZE;
begin
//Получить размер рисунка.
GetBitmapDimensionEx(Bitmap, BmpSize);
with BmpSize do
if (Pattern = -1) then
//Отобразить весь рисунок.
BitBlt(DC, X, Y, cX, cY, TempDC, 0, 0, SRCCOPY)
else
//Отобразить только часть рисунка.
BitBlt(DC, X, Y, PatWidth, PatHeight, TempDC,
(Pattern mod (cX div PatWidth)) * PatWidth,
(Pattern div (cX div PatWidth)) * PatHeight, SRCCOPY);
end;
Так получится гораздо быстрее.


Категория: Delphi, Pascal, ObjectPascal | Просмотров: 2022 | Добавил: ДядяВолк (11.08.2010) | Рейтинг: 0.0/0
Теги: функции, процедуры, полезные
Источник: http://quadrathell.cn.ua/ | Автор: OSokin |
HTML ссылка на материал:
BB ссылка на материал:
Похожие материалы :
Возможно вам будет интересно:
Экономика в ммо (2)
Создаем искусственный интеллект (1)
Создание многопользовательской(online) игры на Game Maker. (часть 2) (0)
Создаем платформер. Scirra Construct (4)
3D Rad - делаем компас. (0)
Обмен информацией по TCP/IP-протоколу (Delphi) (0)
Что с чем едят - 3d Rad (0)
24 совета по программированию в Delphi (Дельфи) часть 2 (0)
Глобальные объекты в Дельфи. Их свойства и методы. (0)
Функции D3D в Game Maker (2)
Создание наземного врага в платформере (2)
С чего начать создание игры для начинающих? (13)
Инветарь на Game Maker (0)
Знакомство с Движком! (0)
Моя первая страница на PHP (0)
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Мы в социальных сетях

Поиск
Поиск по всему сайту:
Поиск по разделу:

Панель пользователя
Здравствуйте, Гость


Ник:
Пароль:
Запомнить :

Ваш IP: 54.205.150.215

Случайные конструкторы

Случайные движки

Случайные статьи

Статистика
Онлайн всего: 6
Гостей: 6
Пользователей: 0

На сайте были:
proto1ype

При полном или частичном копировании материалов сайта ссылка на Make-Games.ru обязательна. Make-Games.ru © 2008 - 2016 Хостинг от uWeb
Топ Разработка игр Рейтинг@Mail.ru