void __fastcall TSprite::EndPaint()
{
for (int i=Z+1;i<spriteList->Count;i++)
if (sprite->mask)
{
if (sprite->visible) sprite->Paint();
sprite->mask=false;
}
}
// Директива компилятору #undef отказывается от обозначения sprite
#undef sprite
// Директива компилятору #define в данном случае вводит имя canvas
#define canvas spriteList->Canvas
// Изображает спрайт на канве
void __fastcall TSprite::Paint()
{
canvas->CopyMode=cmSrcCopy;
image->Canvas->CopyRect(Rect(0,0,image->Width,image->Height),
canvas, SpriteRect);
PaintPicture();
}
// Убирает изображение спрайта с канвы, восстанавливая фон
void __fastcall TSprite::Restore()
{
canvas->CopyMode=cmSrcCopy;
canvas->CopyRect(SpriteRect, image->Canvas,
Rect(0,0,image->Width,image->Height));
}
// Директива компилятору #undef отказывается от обозначения canvas
#undef canvas
// Возвращает прямоугольник спрайта
TRect __fastcall TSprite::GetSpriteRect()
{
return Rect(location,Point(location.x+size.cx,location.y+size.cy));
}
// Определяет факт пересечения прямоугольников спрайтов,
// находящихся в слоях First и Second
bool __fastcall TSprite::Intersect(int const First,int const Second)
{
TRect rect;
return IntersectRect(rect,
((TSprite*)(spriteList->Items[First]))->SpriteRect,
((TSprite*)(spriteList->Items[Second]))->SpriteRect);
}
// Реализация методов класса спрайт со следом TtracedSprite
// Констуктор класса вызывает конструктор предка и инициализирует поле center
__fastcall TTracedSprite::TTracedSprite(TRect const rect):TSprite(rect)
{
center = CenterPoint(SpriteRect);
}
// Деструктор освобождает массив точек следа и вызывает деструктор предка
__fastcall TTracedSprite::~TTracedSprite()
{
trPoints.Length=0;
}
// Устанавливает цвет следа и, одновременно, делает след цветным
void __fastcall TTracedSprite::SetTraceColor(TColor const value)
{
traceColor=value;
traceColored=true;
}
// Перемещает спрайт на вектор drift
bool __fastcall TTracedSprite::Move(TSize const drift)
{
if (Visible && Traced) PutTrace();
bool result=TSprite::Move(drift); // Так вызывается метод наследника
if (result) center =CenterPoint(SpriteRect);
return result;
}
#define sprite ((TTracedSprite*)(SpriteList->Items[i]))
#define sprList ((TTracedSpriteList*)SpriteList)
// Помещает пиксел следа на канву
void __fastcall TTracedSprite::PutTrace()
{
for (int i=SpriteList->Count-1;i>=0;i--)
if (sprite->Visible && PtInRect(sprite->SpriteRect,Center))
sprite->Restore();
// Знак ! означает оператор логического отрицания в C
if (!sprList->TraceMap[Center.x-sprList->ClientRect.Left]
[Center.y-sprList->ClientRect.Top])
{
SpriteList->Canvas->Pixels[Center.x][Center.y]=traceColored?traceColor:
// Знак ^ означает оператор логической симметрической разности.
(TColor)(0xffffff ^ SpriteList->Canvas->Pixels[Center.x][Center.y]);
sprList->TraceMap[Center.x-sprList->ClientRect.Left]
[Center.y-sprList->ClientRect.Top]=true;
trPoints.Length++;
trPoints[trPoints.High].x=Center.x;
trPoints[trPoints.High].y=Center.y;
}
for (int i=0;i<SpriteList->Count;i++)
if (sprite->Visible && PtInRect(sprite->SpriteRect,Center))
sprite->Paint();
}
#undef sprite
#undef sprList
// Реализация методов класса эллиптического спрайта TEllipseSprite
// Констуктор вызывает конструктор предка и инициализирует поле color
__fastcall TEllipseSprite::TEllipseSprite(TRect const rect):
TTracedSprite(rect)
{
color=DefaultColor;
}
// Устанавливает цвет спрайта, меняя его изображение на экране
void __fastcall TEllipseSprite::SetColor(const TColor value)
{
if (color!=value)
{
bool VisState=Visible;
Visible=false;
color=value;
if (VisState) Visible=true;
}
}
#define canvas SpriteList->Canvas
// Создает изображение эллиптического спрайта на канве
void __fastcall TEllipseSprite::PaintPicture()
{
canvas->Brush->Style=bsSolid;
canvas->Brush->Color=color;
canvas->Pen->Color=color;
canvas->Ellipse(SpriteRect);
};
#undef canvas
Предлагается создать оконное приложение, тестирующее описанные классы спрайтов, в среде C++ Builder.
C#
В языке C# компилируемый модуль является отдельным файлом и содержит в себе сразу и описание, и реализацию методов класса. Хэдеры отсутствуют. Последовательность описания членов класса не имеет значения. Более того, такой модуль легко скомпилировать в форме отдельного исполняемого модуля с расширением .dll (dynamic link library). В отличие от exe-файла динамически загружаемая библиотека не имеет точки входа и не может выполняться независимо от вызывающего приложения.
В языке C# все типы являются классами – наследниками одного общего для всех класса Object. Это относится даже к простым типам int, double и т.д. Такие типы являются типами-значениями. К типам-значениям относится также перечислимый тип enum. Объекты типов-значений передаются целиком со всеми своими полями. Обычно это небольшие по объему структуры (struct). Другие типы классов передаются по ссылке (указателю, или адресу) и называются ссылочными типами. К ним относятся многие библиотечные и пользовательские классы (class).
В C# cуществует специфический тип классов, обозначаемый служебным словом delegate. Тип delegate позволяет описывать указатели на любой метод класса, которые, в частности, могут служить обработчиками событий.
В нашей реализации спрайтов код всех классов помещается в отдельный компилируемый модуль, который компилируется в отдельный исполняемый модуль типа библиотеки – модуль с расширением .dll.
Весь код в C# разбит на пространства имен (namespace). Часто отдельный компилируемый модуль относится к одному пространству имен, которое указывается в заголовке модуля (в нашем случае это namespace spritesdll). Но это не правило.
В общем случае
· один исполняемый модуль (.dll или .exe) может собираться из нескольких компилируемых модулей, образуя «сборку» (assembly);
· один компилируемый модуль может состоять из нескольких пространств имен;
· одно пространство имен может охватывать несколько компилируемых модулей;
· описание одного класса может охватывать несколько компилируемых модулей, но при этом каждый отдельный класс может принадлежать только одному пространству имен.
Далее весь комментарий находится в тексте.
/* В начале модуля обычно находится список используемых пространств имен.
Каждое из имен в списке предваряется служебным словом using.
Если имя пространства имен (например, в нашем случае, имя System.Collections)
присутствует в списке, то в коде модуля имя любого идентификатора из пространства
имен System.Collections (в нашем случае имя типа ArrayList) может быть записано
сокращенно (ArrayList) – без указания имени пространства имен
(т.е., не в виде System.Collections.ArrayList).*/
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace spritesdll
{
// Следующий ниже и далее в тексте комментарий, выделенный тройным слэшом ///,
// используется средой для поддержки справочной системы, описывающей элементы кода
/// <summary>
/// Поддерживает список спрайтов, используя объект типа ArrayList.
/// </summary>
/// <remarks>
/// Спрайт - плоский графический объект, занимающий прямоугольную область экрана.
/// Каждый спрайт списка принадлежит как-бы отдельной
/// изображающей плоскости экрана - z-слою.
/// Каждый спрайт списка имеет свое значение z - индекс спрайта в списке.
/// Ось z направлена перпендикулярно экрану по направлению к наблюдателю.
/// Единственным параметром конструктора класса SpriteList является
/// объект типа Control.
/// Объект Control ограничивает область перемещения спрайтов списка и
/// создает объект класса Graphics для изображения спрайта.
/// При перерисовке объекта Control или его уничтожении список очищается.
/// Каждый спрайт списка создается методом Add, параметрами которого являются
/// тип класса спрайта и занимаемый спрайтом прямоугольник.
/// Метод Add возвращает экземпляр созданного спрайта.
/// Прямоугольник спрайта должен полностью принадлежать прямоугольнику
/// объекта Control.
/// Списку могут принадлежать спрайты разного типа -
/// наследники абстрактного класса Sprite.
/// Метод RemoveSpriteAt удаляет из списка спрайт, принадлежащий конкретному слою.
/// Метод Clear удаляет все спрайты из списка.
/// Метод MoveSprite перемещает спрайт из одного слоя в другой.
/// Элементы списка доступны через индексы с нулевой базой.
/// </remarks>
public class SpriteList
{
/// <summary>
/// Хранит ссылку на объект типа Graphics для изображения спрайтов.
/// </summary>
Graphics canvas;
/// <summary>
/// Возвращает ссылку на объект типа Graphics для изображения спрайтов.
/// </summary>
// Так описываются свойства в C#. Модификатор доступа internal ограничивает
// доступ к члену класса тем исполняемым модулем, в котором этот член описан.
internal Graphics Canvas { get { return canvas; } }
/// <summary>
/// Хранит ссылку на Control, с которым связан список спрайтов.
/// </summary>
Control parent;
/// <summary>
/// Возвращает ссылку на Control, ограничивающий спрайты списка.
/// </summary>
internal Control Parent { get { return parent; } }
/// <summary>
/// Хранит ссылку на клиентский прямоугольник объекта Control.
/// </summary>
Rectangle clientRect;
/// <summary>
/// Возвращает ссылку на клиентский прямоугольник объекта Control.
/// </summary>
public Rectangle ClientRect { get { return clientRect; } }
/// <summary>
/// Хранит ссылку на список ссылок на спрайты.
/// </summary>
// ArrayList – стандартный класс, описанный в одной из библиотек .net.
ArrayList list = new ArrayList();
/// <summary>
/// Возвращает ссылку на список ссылок на спрайты.
/// </summary>
internal ArrayList List { get { return list; } }
/// <summary>
/// Возвращает спрайт - элемент списка из данного слоя.
/// </summary>
/// <param name="z">
/// Слой-индекс спрайта в списке.
/// </param>
/// <returns>
/// Спрайт из слоя z.
/// </returns>
// Так описывается свойство, индексирующее объекты класса – так называемый индексатор