Учебное пособие: Методические указания к курсу программирования для студентов физического факультета Сравнительное объектно-ориентированное проектирование

Название: Методические указания к курсу программирования для студентов физического факультета Сравнительное объектно-ориентированное проектирование
Раздел: Остальные рефераты
Тип: учебное пособие Скачать документ бесплатно, без SMS в архиве

Министерство образования и науки Российской Федерации

Федеральное агентство по образованию

Государственное образовательное учреждение

высшего профессионального образования

«РОСТОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»

МЕТОДИЧЕСКИЕ УКАЗАНИЯ

к курсу программирования

для студентов физического факультета

Сравнительное объектно-ориентированное проектирование

Delphi vs C++ vs C#

Часть 2

Ростов-на-Дону

2006

Методические указания разработаны кандидатом физико-математических наук, доцентом кафедры теоретической и вычислительной физики Г.В. Фоминым.

Ответственный редактор доктор физ.-мат. наук, профессор В.П. Саченко

Компьютерный набор и верстка Г.В. Фомин

Печатается в соответствии с решением кафедры теоретической и вычислительной физики физического факультета РГУ, протокол №1 от 17 января 2006 г.


Сравнительное объектно-ориентированное проектирование

Delphi vs C++ vs C#

Часть 2

Содержание настоящего пособия является продолжением его 1-ой части «Сравнительное объектно-ориентированное проектирование Delphi vs C++ vs C#».

Как и в первой части, здесь публикуются прокомментированные коды нескольких классов, написанных на трех языках. Слушателю предлагается создать приложения, тестирующие эти классы, предварительно разобравшись в структуре самих классов и их возможностях.

Спрайты

Это классы, реализующие алгоритм воспроизведения коллекции графических объектов, упорядоченных в третьем измерении (так называемое z -упорядочение). Каждый спрайт занимает свой «слой» в измерении, перпендикулярном экрану (z -направление), как отдельное окно. Однако в отличие от окна спрайт принадлежит коллекции, связанной лишь с одним окном.

Delphi

В Delphi код приложения разбивается на отдельные модули. Каждый модуль состоит из интерфейсной секции , секции реализации и, возможно, секции инициализации. В интерфейсной секции размещаются описания типов, переменных, заголовков процедур и функций, доступных тем частям приложения, которые ссылаются на данный модуль. В секции реализации размещается код, реализующий объявленные в интерфейсе методы классов, процедуры и функции, а также локальные типы, переменные, процедуры и функции, доступные только коду самого модуля.

Интерфейсная секция классов спрайтов

unit uSprite;

{В модуле описаны классы TSpriteList, TSprite и их наследники,

предназначенные для Z-упорядочения графических изображений

на любой канве (например канве объекта типа TPaintBox).

Конструктор класса TSpriteList имеет один параметр -

канву, на которой производится отрисовка.

Конструктор класса TSprite имеет два параметра, определяющие

прямоугольник спрайта и список, которому спрайт принадлежит.}

interface

//Модули VCL, в которых описаны используемые в интерфейсе типы

uses Controls,Graphics,Classes,Types;

type

// Предварительное объявление класса TSprite

TSprite=class;

// Тип переменных, содержащих ссылки на классы типа TSprite

TSpriteClass=class of TSprite;

// Список спрайтов

TSpriteList=class

private

// Поля

// Хранит канву ("контекст устройства"), используемую для отображения спрайтов списка

FCanvas:Controls.TControlCanvas;

// Хранит режим отображения графического объекта при его копировании на канву

FCanvasCopyMode:Graphics.TCopyMode;

// Хранит прямоугольник, ограничивающий область отображения спрайтов списка

FClientRect:Types.TRect;

// Хранит список указателей на спрайты

FList:Classes.TList;

// Хранит текущее число спрайтов в списке

FCount:integer;

// Метод

// Возвращает спрайт списка под номером aZ

function GetSprite(aZ:integer):TSprite;

public

// Свойства

// Возвращает спрайт из списка как элемент массива

property Sprites[aZ:integer]:TSprite read GetSprite;default;

// Возвращает текущее число спрайтов в списке

property Count:integer read FCount;

// Возвращает ссылку на список указателей спрайтов

property List:Classes.TList read FList;

// Возвращает ссылку на канву, с которой связаны спрайты списка

property Canvas:Controls.TControlCanvas read FCanvas;

// Возвращает прямоугольник, ограничивающий область изображения спрайтов списка

property ClientRect:Types.TRect read FClientRect;

// Конструктор

// Создает и инициализирует экземпляр списка спрайтов, связанного с данной канвой

constructor Create(const aCanvas:Controls.TControlCanvas);

// Методы

// Реализует действия перед освобождением объекта

procedure BeforeDestruction;override;

// Создает и добавляет в список объект класса aSpriteClass,

// занимающего прямоугольник SpriteRect

function AddSprite(const aSpriteClass:TSpriteClass;

const SpriteRect:Types.TRect):TSprite;

// Перемещает спрайт внутри списка в z-направлении (с одного слоя в другой)

procedure MoveSprite(const fromZ,toZ:integer);

// Удаляет спрайт с индексом aZ (слой) из списка

procedure DeleteSprite(const aZ:integer);virtual;

// Очищает список от указателей на спрайты

procedure Clear;virtual;

end;

// Тип обработчика события, наступающего перед смещением спрайта

OnMoveEvent=function(Sender:TSprite;var NewLocation:Types.TPoint):

Boolean of object;

// Абстрактный класс спрайта регулирует изображение и перемещение спрайта.

// Изображению спрайта на канве предшествует сохранение в памяти фона,

// который перекрывается изображением.

// Требуемый участок фона сохраняется в объекте типа TBitmap.

// Изображение спрайта исчезает в момент восстановления фона –

// обратного копирования на канву сохраненного участка.

TSprite=class(TObject)

private

// Поля

// Хранит состояние видимости спрайта

FVisible: boolean;

// Хранит номер слоя, занимаемого спрайтом

FZ: integer;

// Хранит маску - наличие пересечений с одним из выше лежащих спрайтов

FMask: boolean;

// Хранит ссылку на список, которому принадлежит спрайт

FSpriteList: TSpriteList;

// Хранит Bitmap, содержащий фон спрайта

FImage: Graphics.TBitmap;

// Хранит координаты левого верхнего угла спрайта

FLocation: Types.TPoint;

// Хранит размеры спрайта

FSize: Types.TSize;

// Хранит ссылку на обработчик смещения спрайта

FOnMove: OnMoveEvent;

// Методы

// Готовит спрайт к изображению

procedure BeginPaint;

// Завершает процесс изображения спрайта

procedure EndPaint;

// Устанавливает маску для спрайта из слоя aZ

procedure SetMask(const aZ:integer);

// Определяет факт перекрытия спрайтов из слоев First и Second

function Intersect(const First,Second:integer):boolean;

// Устанавливает состояние видимости спрайта

procedure SetVisible(const aVisible: Boolean);

// Возвращает прямоугольник спрайта

function GetSpriteRect:Types.TRect;

// Конструктор

// Создает и инициализирует спрайт, принадлежащий списку Sprites

// с прямоугольником SpriteRect

constructor Create(const SpriteRect: Types.TRect;const Sprites: TSpriteList);

protected

// Методы

// Восстанавливает изображение фона спрайта

procedure Restore;virtual;

// Изображает спрайт

procedure Paint;virtual;

// Формирует реальное изображение спрайта (в этом классе метод абстрактный)

procedure PaintPicture;virtual;abstract;

public

// Свойства

// Возвращает слой спрайта

property Z:integer read FZ;

// Устанавливает и возвращает обработчик при перемещении спрайта

property OnMove:OnMoveEvent read FOnMove write FOnMove;

// Устанавливает и возвращает состояние видимости спрайта

property Visible:Boolean read FVisible write SetVisible;

// Возвращает положение левого верхнего угла спрайта

property Location:Types.TPoint read FLocation;

// Возвращает размеры спрайта

property SpriteSize:Types.TSize read FSize;

// Возвращает прямоугольник спрайта

property SpriteRect:Types.TRect read GetSpriteRect;

// Возвращает ссылку на список, которому спрайт принадлежит

property SpriteList:TSpriteList read FSpriteList;

// Методы

// Выполняет инициализирующие действия сразу после создания спрайта

procedure AfterConstruction;override;

// Выполняет действия непосредственно перед освобождением спрайта

procedure BeforeDestruction;override;

// Перемещает спрайт на вектор drift

function Move(const drift: Types.TSize): boolean;virtual;

// Перемещает спрайт в новое положение NewLocation

function MoveTo(const NewLocation: Types.TPoint): boolean;virtual;

end;

// Тип массива, хранящего карту следов (пикселей) спрайтов на канве

TTraceMap=Array of array of Boolean;

// Список спрайтов, оставляющих след на канве

TTracedSpriteList=class(TSpriteList)

private

// Поле

// Хранит карту следов на канве

FTraceMap:TTraceMap;

public

//Возвращает карту следов на канве

property TraceMap:TTraceMap read FTraceMap;

// Методы

// Выполняет инициализирующие действия сразу после создания списка

procedure AfterConstruction;override;

// Выполняет действия непосредственно перед освобождением списка

procedure BeforeDestruction;override;

// Удаляет спрайт с индексом aZ (слой) из списка

procedure DeleteSprite(const aZ:integer);override;

// Очищает список от указателей на спрайты

procedure Clear;override;

end;

// Тип массива точек следа спрайта

TTracePoints=array of Types.TPoint;

// Класс, спрайты которого оставляют след перемещения

// по канве списка типа TTracedSpriteList

TTracedSprite=class(TSprite)

private

// Поля

// Хранит указание, оставляет ли спрайт след

FTraced:Boolean;

// Хранит точки со следом

FTracePoints:TTracePoints;

// Хранит указание, имеет ли след определенный цвет

FTraceColored:Boolean;

// Хранит цвет следа

FTraceColor:Graphics.TColor;

// Хранит центр спрайта

FCenter:Types.TPoint;

// Метод

// Устанавливает цвет спрайта

procedure SetTraceColor(const aTraceColor:Graphics.TColor);

public

// Свойства

// Возвращает и устанавливает указание на наличия следа

property Traced:Boolean read FTraced write FTraced;

// Возвращает и устанавливает указатель на точки следа

property TracePoints:TTracePoints read FTracePoints;

// Возвращает и устанавливает указание, имеет ли след определенный цвет

property TraceColored:Boolean read FTraceColored write FTraceColored;

// Возвращает и устанавливает цвет следа

property TraceColor:Graphics.TColor read FTraceColor write SetTraceColor;

// Возвращает центр спрайта

property Center:Types.TPoint read FCenter;

// Методы

// Выполняет инициализирующие действия сразу после создания спрайта

procedure AfterConstruction;override;

// Выполняет действия непосредственно перед освобождением спрайта

procedure BeforeDestruction;override;

// Перемещает спрайт на вектор drift

function Move(const drift:Types.TSize):boolean;override;

// Воспроизводит след

procedure PutTrace;

end;

const DefaultColor=$ffffff;//Цвет эллипса по умолчанию

type

// Класс, изображающий спрайт в форме сплошного эллипса

TEllipseSprite=class(TTracedSprite)

private

// Поле

// Хранит цвет эллипса

FColor:Graphics.TColor;

protected

// Методы

// Изображает эллипс

procedure PaintPicture;override;

// Устанавливает цвет эллипса

procedure SetColor(const aColor:Graphics.TColor);

public

// Свойство

// Возвращает и устанавливает цвет эллипса

property Color:Graphics.TColor read FColor write SetColor;

// Метод

// Выполняет инициализирующие действия сразу после создания спрайта

procedure AfterConstruction;override;

end;

Вспомним правила описания в Delphi в контексте приведенного выше интерфейса модуля uSprite. С этой целью рассмотрим фрагмент начала модуля

uses Controls,Graphics,Classes,Types;

type

// Предварительное объявление класса TSprite

TSprite=class;

// Тип переменных, содержащих ссылки на классы типа TSprite

TSpriteClass=class of TSprite;

// Список спрайтов

TSpriteList=class

// Описание членов класса

end;

· Директива uses означает, что в коде настоящего модуля используются типы, переменные, процедуры, функции или константы (короче – имена), описанные в интерфейсах модулей Controls, Graphics, Classes, Types. Все перечисленные модули принадлежат в данном случае библиотеке среды Delphi.

· Служебное слово type означает, что ниже следует описание типов . Тип – это формат переменных. Существуют стандартные типы такие как , , и другие. Их формат задан средой. Другие типы, которые оказываются необходимыми в конкретном приложении или модуле, требуют специального описания.

· Краткое описание TSprite=class; типа TSprite означает, что класс TSprite будет описан ниже, но упоминание о нем необходимо уже здесь. Дело в том, что описанный ниже класс TSpriteList использует в своем описании TSprite. В то же время полное описание класса TSprite в свою очередь содержит ссылку на класс TSpriteList. Эта взаимозависимость описаний двух классов не позволяет предпочесть в порядке описания один класс другому. Выход – дать краткое (пустое) описание одного из классов перед полным описанием другого.

· Тип TSpriteClass=class of TSprite описывает переменные, которые содержат в себе ссылки на таблицы виртуальных методов класса TSprite и его наследников. Такие переменные могут быть использованы, например, при создании экземпляра объекта, о котором во время программирования известно лишь то, что он принадлежит к семейству спрайтов, то есть является наследником класса TSprite. Так одним из параметров метода AddSprite(const aSpriteClass: TSpriteClass; const SpriteRect: Types.TRect) класса TSpriteList является переменная типа TSpriteClass, указывающая, экземпляр какого класса спрайтов следует добавить в список.

Строка TSpriteList=class открывает описание класса , которое содержит в себе поля , свойства и методы класса TSpriteList вплоть до служебного слова end, завершающего перечисление членов класса . Все поля объекта инициализируются при явном вызове конструктора в коде приложения. По умолчанию, если в теле конструктора не указаны другие значения, все поля будут инициализированы нулями.

Каждый член класса TSpriteList имеет определенный уровень доступа . Так в описании класса TSpriteList имеется две секции, выделенные модификаторами доступа private и public.

Рассмотрим фрагмент кода, описывающий класс TSpriteList:

TSpriteList=class

private

// Поля

// Хранит канву ("контекст устройства"),используемую для отображения спрайтов списка

FCanvas:Controls.TControlCanvas;

// Метод

// Возвращает спрайт списка под номером aZ

function GetSprite(aZ:integer):TSprite;

public

// Свойства

// Возвращает ссылку на канву, с которой связаны спрайты списка

property Canvas:Controls.TControlCanvas read FCanvas;

// Возвращает спрайт из списка как элемент массива

property Sprites[aZ:integer]:TSprite read GetSprite;default;

// Конструктор

// Создает и инициализирует экземпляр списка спрайтов, связанного с данной канвой

constructor Create(const aCanvas:Controls.TControlCanvas);

end;

В Delphi модификатор доступа private применяется к членам класса, которые доступны лишь тому же модулю , в котором описан сам класс, но недоступны другим модулям программы. Обычно поля класса имеют уровень доступа private. Члены класса с уровнем доступа public доступны любой части программы . Свойства класса обычно имеют уровень доступа public. Так поле FCanvas (идентификаторы полей в Delphi принято начинать буквой F от field – поле) имеет уровень доступа private, но свойство Canvas открыто для доступа. Через свойство Canvas можно прочесть поле FCanvas, но нельзя изменить его значение. Так свойства могут регулировать доступ к полям.

Что касается методов, то их разделение по уровням доступа зависит от логики класса. Так, метод GetSprite(aZ:integer):TSprite класса TSpriteList «спрятан» от внешнего доступа под модификатором private. Его роль ограничивается обеспечением доступного свойства Sprites[aZ:integer] возвращаемым значением – спрайтом с индексом aZ из списка. Другие методы класса TSpriteList имеют открытый доступ. Среди них конструктор класса Create, создающий экземпляр объекта и инициализирующий его поля. Параметром конструктора является объект типа TControlCanvas из библиотечного модуля Controls. Объекты этого типа предоставляют спрайтам область изображения - прямоугольник с известными границами в окне приложения и инструменты изображения – кисть и карандаш с цветовой палитрой.

Модификатор const, указанный в описании параметра конструктора и многих других методов, не является обязательным. Он указывает лишь на то, что метод обязуется внутри не изменять значения параметра, передаваемого ему с этим модификатором.

Модификатор default в свойстве Sprites указывает на то, что доступ к объектам класса TSpriteList может осуществляться через свойство Sprites как к элементам массива – в индексном виде.

В коде настоящего модуля имена, описанные в других модулях, специально записаны в расширенном формате с тем, чтобы явно указать их принадлежность. Например, имя типа TControlCanvas, описанного в модуле Controls, записано в расширенном виде Controls.TControlCanvas. Вообще говоря, расширенное имя можно сократить, убрав имя модуля, если отсутствует конфликт имен.

Метод

procedure BeforeDestruction; override;

имеет модификатор override. Это означает, что метод BeforeDestruction является виртуальным и унаследован от предка класса TSpriteList, где он описан как виртуальный (virtual). Предком класса TSpriteList является класс TObject.

Другие методы

procedure DeleteSprite(const aZ:integer); virtual;

procedure Clear; virtual;

описаны как виртуальные в самом классе TSpriteList. У наследника TTracedSpriteList, эти же методы преобретают модификатор override.

Рассмотрим еще один фрагмент кода, относящийся к описанию Tsprite и следующий за описанием класса TSpriteList.

// Тип обработчика события, наступающего перед смещением спрайта

OnMoveEvent=function(Sender:TSprite;var NewLocation:Types.TPoint):Boolean of object;

// Абстрактный класс спрайта, регулирующий изображение и перемещение спрайта

TSprite=class(TObject)

private

// Конструктор

// Создает и инициализирует спрайт, принадлежащий списку Sprites

// с прямоугольником SpriteRect

constructor Create(const SpriteRect:Types.TRect;const Sprites:TSpriteList);

protected

// Формирует реальное изображение спрайта (в этом классе метод абстрактный)

procedure PaintPicture;virtual;abstract;

public

end;

Здесь

· Тип функции OnMoveEvent, описанный с модификатором of object, означает, что это тип метода класса , а не просто тип какой-то отдельной функции. Разница в том, что метод класса обязательно имеет один скрытый параметр Self - экземпляр класса, который его вызывает. У обычных процедур и функций такого параметра нет. Обработчики событий в Delphi обычно имеют тип метода . Тогда в них можно подставить ссылку на метод либо формы приложения, либо другого класса, использующего объявленное событие в своих целях.

· В заголовке описания класса TSprite в скобках указан предок TObject, хотя такое указание отсутствует в описании класса TSpriteList. В Delphi отсутствие предка по умолчанию означает, что предком является класс TObject. Так что в описании класса TSprite ссылку на TObject можно также опустить.

· Конструктор класса TSprite помещен в раздел private. Это делает невозможным создание экземпляров отдельных спрайтов из кода, написанного вне модуля uSprite. Логика классов TSprite и TSpriteList предполагает, что созданием спрайтов занимается только метод Add класса TSpriteList, который только и вызывает конструктор экземпляров класса TSprite.

· В описании класса TSprite присутствуют методы с уровнем доступа protected. Эти методы и вообще члены класса с доступом protected доступны любому предку класса TSprite, даже если они описаны в других модулях, но не доступны коду других классов, описанных в других модулях.

· Среди методов класса TSprite, защищенных модификатором protected есть абстрактный метод procedure PaintPicture; virtual; abstract. Он отмечен модификатором abstract. Абстрактный метод PaintPicture не имеет реализации в классе TSprite. Его реализация будет предложена наследниками. Наличие абстрактного метода делает сам класс TSprite абстрактным в том смысле, что его экземпляры не могут быть созданы.

После описания класса TSprite описаны один тип динамического массива

// Тип массива, хранящего карту следов (пикселей) спрайтов на канве

TTraceMap=Array of array of Boolean;

Тип TTraceMap описывает двумерный массив логических значений.

Динамичность массива в том, что его размер не фиксируется как постоянная величина в процессе разработки класса (design time), а определяется лишь в ходе счета (run time). Конкретные переменные, например, размеры области изображения спрайтов, приобретают реальные значения при создании экземпляра класса TTracedSpriteList=class(TSpriteList). Это происходит в методе AfterConstruction класса TTracedSpriteList, выполняющемся сразу вслед за созданием экземпляра объекта этого класса.

За описанием класса TTracedSpriteList и перед описанием класса TtracedSprite есть описание другого типа динамического массива

// Тип массива точек следа спрайта

TTracePoints=array of Types.TPoint;

Это уже одномерный массив точек - записей типа TPoint, описанных в стандартном модуле Types.

Вслед за этим описан класс

TTracedSprite=class(TSprite)

наследник класса TSprite.

Обратите внимание, что класс TTracedSprite, как и его предок TSprite, является абстрактным классом, так как не реализует абстрактный метод PaintPicture.

Вслед за описанием класса TTracedSprite расположен текст

const DefaultColor=$ffffff; //Цвет эллипса по умолчанию

type

// Класс, изображающий спрайт в форме сплошного эллипса

TEllipseSprite=class(TTracedSprite)

Здесь

· Служебное слово const указывает на то, что DefaultColor является постоянной величиной. Значение DefaultColor записано в 16-ной системе счисления, которая удобна при записи цветов. (В данном случае $ffffff означает максимальное число, содержащееся в трех байтах; в десятичной системе это число равно 224 – 1 = 1677215.) Дело в том, что информация о цвете в Delphi представляется четырехбайтовым целым числом. Старший байт используется для системных цветов, а в трех младших байтах находятся стандартные цвета – в младшем красный, в среднем зеленый и в старшем байте - синий. Другими словами чисто зеленый цвет, к примеру, отвечает числу $ff00. В 16-ричной записи видна структура байтов. Каждому байту отводится по две 16-ричные цифры. В данном случае число $ffffff означает, что все составляющие цвета входят одинаково и с полной интенсивностью – это белый цвет.

· Вслед за описанием постоянной идет описание класса TEllipseSprite, поэтому набирается служебное слово type, действие которого было отменено const.

· Класс TEllipseSprite является наследником класса TTracedSprite. В классе TEllipseSprite уже реализован абстрактный метод PaintPicture, поэтому можно создавать его экземпляры – сплошные эллипсовидые спрайты заданного цвета.

Секция реализации

В этой секции модуля находится код методов пяти классов, описанных выше

implementation uses SysUtils;

//Определяет, находится ли прямоугольник source внутри прямоугольника dest

function Contains(const source,dest:Types.TRect):Boolean;

begin

with dest do

Result:=(source.Left>=Left) and (source.Top>=Top)

and (source.Right<=Right) and (source.Bottom<=Bottom);

end {Contains};

//Реализация методов класса TSpriteList

constructor TSpriteList.Create(const aCanvas:Controls.TControlCanvas);

begin

inherited Create;

if Assigned(aCanvas) then FCanvas:=aCanvas else

raise SysUtils.Exception.Create('Конструктору класса TSpriteList не передана канва!');

FClientRect:=FCanvas.Control.ClientRect;

FCanvasCopyMode:=FCanvas.CopyMode;

FList:=Classes.TList.Create;

end {TSpriteList.Create};

procedure TSpriteList.BeforeDestruction;

begin

Clear;

FCanvas.CopyMode:=FCanvasCopyMode;

FList.Free;

FCount:=0;

inherited

end {TSpriteList.BeforeDestruction};

function TSpriteList.GetSprite(aZ:integer):TSprite;

begin

Result:=TSprite(FList[aZ]);

end {GetSprite};

function TSpriteList.AddSprite(const aSpriteClass:TSpriteClass;

const SpriteRect:Types.TRect):TSprite;

var aSprite:TSprite;

begin

Result:=nil;

if Assigned(aSpriteClass) and (SpriteRect.Right- SpriteRect.Left>0) and

(SpriteRect.Bottom-SpriteRect.Top>0) and Contains(SpriteRect,ClientRect) then

begin

aSprite:=aSpriteClass.Create(SpriteRect,Self);

aSprite.FZ:=FList.Add(aSprite);

FCount:=FList.Count;

Result:=aSprite;

end

end {AddSprite};

procedure TSpriteList.MoveSprite(const fromZ,toZ:integer);

var i,minZ:integer;

begin

if (fromZ<>toZ) and (fromZ>-1) and (fromZ<FCount) and

(toZ>-1) and (toZ<FCount) then

begin

if fromZ<toZ then minZ:=fromZ else minZ:=toZ;

for i:=FCount-1 downto minZ do

if Self[i].FVisible then Self[i].Restore;

FList.Move(fromZ,toZ);

for i:=minZ to FCount-1 do

begin

Self[i].FZ:=i;

if Self[i].FVisible then Self[i].Paint

end

end

end {MoveSprite};

procedure TSpriteList.DeleteSprite(const aZ:integer);

var i:integer;

begin

if (aZ>-1) and (aZ<FCount) then

begin

for i:= FCount-1 downto aZ do

with Self[i] do

if Visible then Restore;

Self[aZ].Free;

FList[aZ]:=nil;

FList.Delete(aZ);

FCount:=FList.Count;

for i:= aZ to FCount-1 do

with Self[i] do

begin

Dec(FZ);

if Visible then Paint;

end

end

end {TSpriteList.DeleteSprite};

procedure TSpriteList.Clear;

var i:integer;

begin

if Assigned(FList) then

for i:= FCount - 1 downto 0 do DeleteSprite(i);

end {TSpriteList.Clear};

//Реализация методов класса TSprite

constructor TSprite.Create(const SpriteRect:Types.TRect;const Sprites:TSpriteList);

begin

inherited Create;

FZ:=-1;

FSpriteList:=Sprites;

FLocation:=SpriteRect.TopLeft;

with FSize,SpriteRect do

begin

cx:=Right-Left;cy:=Bottom-Top

end;

end {TSprite.Create};

procedure TSprite.AfterConstruction;

begin

inherited;

FImage:=Graphics.TBitmap.Create;

FImage.Height:=FSize.cy;

FImage.Width:=FSize.cx;

end {TSprite.AfterConstruction};

procedure TSprite.BeforeDestruction;

begin

FImage.Free;

inherited

end {TSprite.BeforeDestruction};

procedure TSprite.SetVisible(const aVisible:Boolean);

begin

if aVisible<>FVisible then

begin

if aVisible then

begin

BeginPaint;

Paint;

EndPaint;

end else

begin

BeginPaint;

Restore;

EndPaint;

end;

FVisible:=aVisible

end

end {SetVisible};

function TSprite.Move(const drift:Types.TSize):boolean;

var NewPos:Types.TPoint;VisState:Boolean;

begin

Result:=true ;

NewPos:=Types.Point(FLocation.X+drift.cx,FLocation.Y+drift.cy);

if Assigned(FOnMove) then Result:=FOnMove(Self,NewPos);

Result:=Result and Contains(

Types.Rect(NewPos.X,NewPos.Y,NewPos.X+FSize.cx,NewPos.Y+FSize.cy),

FSpriteList.FClientRect);

if Result then

begin

VisState:=FVisible;

Visible:=false ;

FLocation:=NewPos;

Visible:=VisState

end

end {TSprite.Move};

function TSprite.MoveTo(const NewLocation:Types.TPoint):boolean;

begin

Result:=Move(Types.TSize(

Types.Point(NewLocation.X-FLocation.X,NewLocation.Y-FLocation.Y)))

end {MoveTo};

procedure TSprite.BeginPaint;

var i:integer;

begin

SetMask(FZ);

for i:=FSpriteList.FCount-1 downto FZ+1 do

with FSpriteList[i] do

if FMask and FVisible then Restore;

end {BeginPaint};

procedure TSprite.SetMask(const aZ:integer);

var i:integer;

begin

for i:=aZ+1 to FSpriteList.FCount-1 do

begin

with FSpriteList[i] do

FMask:= Intersect(aZ,i) or FMask;

if FMask then SetMask(i)

end

end {SetMask};

procedure TSprite.EndPaint;

var i:integer;

begin

for i:=FZ+1 to FSpriteList.FCount-1 do

with FSpriteList[i] do

if FMask then

begin

if FVisible then Paint;

FMask:=false

end

end {EndPaint};

procedure TSprite.Paint;

begin

with FSpriteList do

begin

FCanvas.CopyMode:=cmSrcCopy;

with FImage do

Canvas.CopyRect(Types.Rect(0,0,Width,Height),FCanvas,SpriteRect);

end;

PaintPicture

end {Paint};

procedure TSprite.Restore;

begin

with FSpriteList.FCanvas do

begin

CopyMode:= cmSrcCopy;

with FImage do CopyRect(SpriteRect,Canvas,Types.Rect(0,0,Width,Height));

end

end {Restore};

function TSprite.GetSpriteRect:Types.TRect;

begin

with FLocation,FSize do Result:=Types.Rect(X, Y, X+cx,Y+cy)

end {GetSpriteRect};

function TSprite.Intersect(const First,Second:integer):boolean;

var rect:Types.TRect;

begin

with FSpriteList[First] do

Result:=IntersectRect(rect,SpriteRect,FSpriteList[Second].SpriteRect);

end {Intersect};

//Реализация методов класса TTracedSpriteList

procedure TTracedSpriteList.AfterConstruction;

begin

inherited;

with ClientRect do SetLength(FTraceMap,Right-Left+1,Bottom-Top+1);

end {TTracedSpriteList.AfterConstruction};

procedure TTracedSpriteList.BeforeDestruction;

begin

inherited;

FTraceMap:=nil;

end {TTracedSpriteList.BeforeDestruction};

procedure TTracedSpriteList.DeleteSprite(const aZ:integer);

begin

if (aZ > -1) and (aZ < Count) then

begin

TTracedSprite(Self[aZ]).FTracePoints:=nil;

inherited DeleteSprite(aZ);

end

end {TTracedSpriteList.DeleteSprite};

procedure TTracedSpriteList.Clear;

var i,j:integer;

begin

for i:= Low(FTraceMap) to High(FTraceMap) do

for j:= Low(FTraceMap[i]) to High(FTraceMap[i]) do

FTraceMap[i,j]:= false ;

inherited Clear;

end {TTracedSpriteList.Clear};

//Реализация методов класса TTracedSprite

procedure TTracedSprite.AfterConstruction;

begin

inherited;

FCenter:=Types.CenterPoint(SpriteRect);

end {TTracedSprite.AfterConstruction};

procedure TTracedSprite.BeforeDestruction;

begin

FTracePoints:=nil;

inherited

end {TTracedSprite.BeforeDestruction};

procedure TTracedSprite.SetTraceColor(const aTraceColor:Graphics.TColor);

begin

FTraceColor:=aTraceColor;

FTraceColored:=true

end {SetTraceColor};

function TTracedSprite.Move(const drift:Types.TSize):Boolean;

begin

if FVisible and FTraced then PutTrace;

Result:=inherited Move(drift);

if Result then

FCenter:=Types.CenterPoint(SpriteRect)

end {TTracedSprite.Move};

procedure TTracedSprite.PutTrace;

var i:integer;

begin

with FCenter do

begin

for i:=FSpriteList.FCount-1 downto 0 do

begin

with FSpriteList[i] do

if FVisible and Types.PtInRect(SpriteRect,Self.FCenter)

then Restore;

end;

with TTracedSpriteList(FSpriteList),FClientRect do

if not TraceMap[x-Left,y-Top] then

begin

with FCanvas do

if FTraceColored then Pixels[x,y]:=FTraceColor else

Pixels[x,y]:=$ffffff xor Pixels[x,y];

TraceMap[x-Left,y-Top]:=true ;

SetLength(FTracePoints,High(FTracePoints)+2);

FTracePoints[High(FTracePoints)].X:=x;FTracePoints[High(FTracePoints)].Y:=y;

end;

for i:=0 to FSpriteList.FCount-1 do

begin

with FSpriteList[i] do

if FVisible and Types.PtInRect(SpriteRect,Self.FCenter)

then Paint;

end

end

end {PutTrace};

//Реализация методов класса TEllipseSprite

procedure TEllipseSprite.AfterConstruction;

begin

inherited;

FColor:=DefaultColor;

end {TEllipseSprite.AfterConstruction};

procedure TEllipseSprite.SetColor(const aColor: Graphics.TColor);

var VisState:Boolean;

begin

if FColor<>aColor then

begin

VisState:=FVisible;

Visible:=false ;

FColor:=aColor;

if VisState then Visible:=true

end

end {SetColor};

procedure TEllipseSprite.PaintPicture;

begin

with FSpriteList.FCanvas do

begin

Brush.Style:=bsSolid;

Brush.Color:=Color;

Pen.Color:=color;

Ellipse(SpriteRect);

end;

end {PaintPicture};

end {uSprite}.

Следует отметить, что в Delphi в разделе реализации можно указывать лишь имена методов, не повторяя список параметров и тип функции. Например, вместо строки кода

function TTracedSprite.Move(const drift: Types.TSize): Boolean;

можно было бы записать ее краткий вариант

function TTracedSprite.Move;

Здесь мы этим не пользовались, чтобы не затруднять чтение кода.

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

C++

Теперь рассмотрим версию тех же классов спрайтов, написанную на языке C++ в среде C++ Builder (6-ая версия) фирмы Borland.

Структура программного модуля в C++ несколько отличается от структуры модуля, написанного на Object Pascal в Delphi. В некотором смысле интерфейсной секции дельфийского модуля соответствует отдельный физический файл программного модуля на C++, именуемый «хэдер», или файл заголовков. Хэдер имеет расширение .h. Хэдер все же отличается от дельфийской секции interface тем, что в него можно помещать содержательную часть кода, а не только заголовки. Смотрите, к примеру, функцию Contains, описанную в хэдере.

Другой файл, имеющий расширение .cpp и то же имя, что хэдер, содержит реализацию кода, как в секции реализации дельфийского модуля. Оба файла образуют пару, соответствующую одному программному модулю типа unit в Delphi.

Хэдер

В начале рассмотрим подробнее содержание хэдера классов спрайтов модуля uSprite.

#ifndef uSpriteH

#define uSpriteH

//---------------------------------------------------------------------------

/*Модуль, в котором описаны классы TSpriteList и TSprite

для Z-упорядочения графических изображений

на любой канве (например, канве объекта типа TPaintBox).

Конструктор класса TSpriteList имеет один параметр - канву,

на которой производится отрисовка.

Конструктор класса TSprite имеет также один параметр - прямоугольник спрайта.

Объекты типа TSprite помещаются в список

методом AddSprite класса TSpriteList*/

class TSprite;

//TSpriteList

class TSpriteList

{

private :

// Поля

int count;

TControlCanvas* canvas;

TRect clientRect;

TList* list;

TCopyMode canvasCopyMode;

// Метод

TSprite* __fastcall GetItems(int );

public :

// Свойства

__property int Count={read=count};

__property TControlCanvas* Canvas={read=canvas};

__property TRect ClientRect={read=clientRect};

__property TList* List={read=list};

__property TSprite* Items[int Index]={read=GetItems};

// Конструктор

__fastcall TSpriteList(TControlCanvas* const );

// Деструктор

__fastcall virtual ~TSpriteList();

// Методы

TSprite* __fastcall AddSprite(TSprite* const );

void __fastcall MoveSprite(int const , int const );

void __fastcall virtual DeleteSprite(int const );

void __fastcall virtual Clear();

};

// Тип массива следов спрайтов на канве

typedef DynamicArray< DynamicArray < bool > > TTraceMap;

//TTracedSpriteList

class TTracedSpriteList:public TSpriteList

{

private :

// Поле

TTraceMap traceMap;

public :

// Свойство

__property TTraceMap TraceMap = {read=traceMap};

// Конструктор

__fastcall TTracedSpriteList(TControlCanvas* const );

// Деструктор

__fastcall ~TTracedSpriteList();

// Методы

void __fastcall virtual DeleteSprite(int const );

void __fastcall virtual Clear();

};

typedef bool __fastcall (__closure *OnMoveEvent)(TSprite* ,TPoint&);

//TSprite

class TSprite:public TObject

{

// Класс TSpriteList, объявленный friend , получает доступ

// к private и protected членам класса TSprite

friend class TSpriteList;

private :

// Поля

bool visible;

int z;

TSpriteList* spriteList;

OnMoveEvent onMove;

TSize size;

TPoint location;

Graphics::TBitmap* image;

bool mask;

// Методы

void __fastcall SetVisible(bool const );

TRect __fastcall GetSpriteRect();

void __fastcall BeginPaint();

void __fastcall EndPaint();

void __fastcall SetMask(int const );

bool __fastcall Intersect(int const ,int const );

protected :

// Методы

void __fastcall virtual PaintPicture()=0;

void __fastcall virtual Restore();

void __fastcall virtual Paint();

public :

// Свойства

__property bool Visible={read=visible,write=SetVisible};

__property int Z={read=z};

__property TSpriteList* SpriteList={read=spriteList};

__property OnMoveEvent OnMove={read=onMove,write=onMove};

__property TSize Size={read=size};

__property TPoint Location={read=location};

__property TRect SpriteRect={read=GetSpriteRect};

// Конструктор

__fastcall TSprite(TRect const );

// Деструктор

__fastcall virtual ~TSprite();

// Методы

bool __fastcall virtual Move(TSize const );

bool __fastcall virtual MoveTo(TPoint const );

};

// Тип динамического массива точек со следами спрайта

typedef DynamicArray <TPoint> TTracePoints;

//TTracedSprite

class TTracedSprite:public TSprite

{

private :

// Поля

TTracePoints trPoints;

bool traced;

bool traceColored;

TColor traceColor;

TPoint center;

// Метод

void __fastcall SetTraceColor(TColor const );

public :

// Свойство

__property TTracePoints TrPoints={read=trPoints};

__property bool Traced={read=traced,write=traced};

__property TColor TraceColor={read=traceColor,write=SetTraceColor};

__property bool TraceColored={read=traceColored,write=traceColored};

__property TPoint Center={read=center};

// Конструктор

__fastcall TTracedSprite(TRect const );

// Деструктор

__fastcall ~TTracedSprite();

// Методы

bool __fastcall virtual Move(TSize const );

void __fastcall PutTrace();

};

const TColor DefaultColor=0xffffff;

//TEllipseSprite

class TEllipseSprite:public TTracedSprite

{

private :

// Поле

TColor color;

protected :

// Методы

void __fastcall virtual PaintPicture();

void __fastcall SetColor(TColor const );

public :

// Свойство

__property TColor Color={read=color, write=SetColor};

// Конструктор

__fastcall TEllipseSprite(TRect const );

};

bool Contains(TRect const source,TRect const dest)

{

return source.Left>=dest.Left && source.Top>=dest.Top &&

source.Right<=dest.Right && source.Bottom<=dest.Bottom;

}

#endif

Весь код хэдера заключен «в скобки» защитного блокиратора вида

#ifndef uSpriteH

#define uSpriteH

#endif

Это директивы компилятору , которые переводятся так

#ifndef uSpriteH – если не определен символ uSpriteH

#define uSpriteH – определи символ uSpriteH

#endif – заверши область действия директивы «если».

Таким образом, если перед началом компиляции модуля символ uSpriteH определен , то все, что находится дальше вплоть до директивы #endif , то есть все операторы модуля, компилироваться не будут . Символ uSpriteH определяется при первой компиляции, когда он еще не определен, поэтому все повторные компиляции модуля блокируются .

Рассмотрим отдельные фрагменты кода.

class TSprite;

//TSpriteList

class TSpriteList

{

private :

// Поля

int count;

TControlCanvas* canvas;

void __fastcall SetVisible(bool const );

TRect __fastcall GetSpriteRect();

__property int Count = {read=count};

__property TSprite* Items[int Index]={read=GetItems};

// Конструктор

__fastcall TSpriteList(TControlCanvas* const );

// Деструктор

__fastcall virtual ~TSpriteList();

TSprite* __fastcall AddSprite(TSprite* const );

}

Здесь

· В описании типов и переменных на языке C в начале указывается идентификатор типа или тип, а затем имя типа или переменной: class TSpriteList или int count.

· Описание членов класса заключается в фигурные скобки. Эти скобки в C играют также роль ограничителей begin, end в Delphi.

· В описании TControlCanvas* canvas; стоит звездочка *. Это описание в языке С означает, что поле canvas является ссылкой на объект класса TControlCanvas, т.е. просто целым числом, содержащим адрес объекта в памяти. Если звездочку опустить, то canvas будет описана как объект типа TControlCanvas «по значению », т.е. содержать в себе все поля объекта типа TControlCanvas. В языке C описание объекта по значению приводит к тому, что в месте описания происходит создание реального экземпляра объекта – вызывается его «конструктор по умолчанию» и все поля инициализируются.

· В языке C нет процедур, как в Delphi, - только функции. Те функции, которые не возвращают значений, имеют тип void . Они являются аналогами процедур в Delphi.

· В C++ Builder в описании всех методов классов участвует модификатор __fastcall . Его смысл - обеспечить компиляцию в наиболее быстрый способ вызова метода при выполнении кода.

· В языке C даже, если функция не имеет параметров, в ее описании должны стоять скобки как в GetSpriteRect().

· В отличие от Delphi транслятор с языка C различает прописные и строчные буквы. Поэтому принято давать одинаковые имена полям и соответствующим свойствам, но начинать имена полей со строчной буквы, а свойств – с прописной буквы. Сравните, к примеру, описания поля count и свойства Count.

· Обратите внимание на синтаксис описания свойств в C++ Builder.

· Конструктор в C++ отличается от других методов тем, что его имя совпадает с именем класса и что он не возвращает никакой тип, даже void .

· Имя деструктора также совпадает с именем класса, но перед именем дается знак отрицания ~. Как и констуктор, деструктор не возвращает какой-либо тип. Кроме того, деструктор не должен иметь параметров. Деструктор часто объявляется виртуальным. В этом случае деструкторы всех наследников автоматически становятся виртуальными.

· В C++ модификатор virtual у виртуальных методов не заменяется у наследников на override , а остается virtual .

· В реализации на C++ у метода AddSprite есть только один параметр – ссылка на объект класса TSprite. Поэтому при обращении к методу AddSprite объект спрайта должен быть уже создан. В C++ нет возможности вызвать конструктор объекта, тип класса которого является переменной, как это делается в Delphi.

· При описании заголовков метода в хэдере языка C можно не указывать явно идентификаторы параметров – достаточно только типы. Так, в заголовке метода AddSprite указан только тип единственного параметра TSprite* const . Модификатор const играет ту же роль, что и в Delphi – параметр, объявленный как const , - не меняет своего значения внутри функции.

Прокомментируем другой фрагмент кода.

// Тип массива следов спрайтов на канве

typedef DynamicArray< DynamicArray < bool > > TTraceMap;

//TTracedSpriteList

class TTracedSpriteList:public TSpriteList

{

};

typedef bool __fastcall (__closure *OnMoveEvent)(TSprite* ,TPoint&);

//TSprite

class TSprite:public TObject

{

// Класс TSpriteList, объявленный friend , получает доступ

// к private и protected членам класса TSprite

friend class TSpriteList;

protected :

// Методы

void __fastcall virtual PaintPicture()=0;

};

Здесь

· Служебное слово typedef указывает на описание типа (подобно type в Delphi).

· Типом динамического массива, названного TTraceMap, является выражение DynamicArray< DynamicArray < bool > >. Оно имеет смысл двумерного массива («массива массивов») переменных логического типа. Имя DynamicArray является именем стандартного шаблона (template), находящегося в библиотеке C++Builder. Это параметризованные , или полиморфные (generic) функции. В Delphi нет аналогов шаблонам. Аргументом шаблона является тип. В данном случае аргументом внутреннего шаблона DynamicArray является тип bool , а аргументом внешнего – сам возвращаемый тип внутреннего шаблона DynamicArray< bool >.

· Класс TTracedSpriteList является наследником класса TSpriteList. В заголовке описания класса TTracedSpriteList присутствует ссылка на наследник TSpriteList с модификатором public . Модификатор public в данном контексте означает, что все члены, наследуемые от TSpriteList, сохраняют свою, заданную предком, доступность и в наследнике (public остается public и т.д.). Если бы модификатором был protected , то все наследуемые члены класса, объявленные в предке с модификаторами public и protected , приобрели бы в наследнике модификатор protected .

· В описании

typedef bool __fastcall (__closure *OnMoveEvent)(TSprite* ,TPoint&); именем описываемого типа является OnMoveEvent. Сам тип является методом класса с двумя параметрами типа TSprite* и TPoint&, который возвращает тип bool . То, что OnMoveEvent именно метод класса, а не просто функция, отмечено модификатором __closure . Тип TPoint является стандартным и описан в библиотеке C++Builder. Знак & служит для описания «параметра по ссылке» – аналог служебного слова var в Delphi.

· Модификаторы доступа к членам класса в C имеют слегка иной смысл, нежели в Delphi. Все члены с модификатором private доступны только методам этого же класса вне зависимости от того, в каком модуле класс описан. Члены класса с модификатором protected – только методам своего класса и классов-наследников. В Delphi члены с модификаторами private и protected доступны всему коду того модуля, в котором описан класс. Однако в C++ существует способ сделать доступными защищенные (private и protected ) члены класса другому классу. Для этого класс, методам которого разрешается доступ к защищенным членам, описывается как friend . Примером является декларация из описываемого кода friend class TSpriteList. Она говорит, что классу TSpriteList разрешается доступ ко всем без исключения членам класса TSprite.

· Обратите внимание на синтаксис описания абстрактного метода в C++ void __fastcall virtual PaintPicture()=0;

Реализация классов спрайтов

Ниже приведен полный код реализации классов спрайтов, описанных в хэдере. Комментарий к коду приводится непосредственно в тексте кода.

#include <vcl.h> //Модуль, несущий определения библиотеки VCL

/*Директива #pragma hdrstop означает окончание списка хэдеров,

компилируемых предварительно для использования в нескольких

файлах-исходниках одного проекта. В данном случае в этом списке

есть только файл vcl.h.

Директива #pragma hdrstop автоматически добавляется средой.*/

#pragma hdrstop

#include "uSprite.h" //хэдер нашего исходника

/*Директива #pragma package(smart_init) служит для «разумной»

последовательности в инициализации модулей при формировании

кода проекта. Она также автоматически добавляется средой

при создании нового модуля.*/

#pragma package(smart_init)

/*Далее располагается собственно авторский код.

Любой метод класса должен иметь в заголовке

имя класса, отделенного от имени самого метода

двойным двоеточием. В Delphi это была точка.*/

// Здесь реализуются методы класса TSpriteList.

// Конструктор инициализирует поля класса

__fastcall TSpriteList::TSpriteList(TControlCanvas* const canvas)

{

if (canvas) //Условие оператора if всегда пишется в скобках.

/* Проверку наличия не нулевого указателя можно проводить,

используя просто сам указатель, как в коде.

Это равносильно записи условия в виде (canvas!=NULL) –

указатель canvas не равен NULL*/

{

// служебное слово this в C имеет смысл self в Delphi – указатель на вызывающий объект

// вызов члена объекта, если объект задан своим указателем, происходит оператором ->

// оператор присвоения в С имеет вид =, а для сравнения используется двойной знак ==

this ->canvas=canvas;

clientRect=canvas->Control->ClientRect;

canvasCopyMode=canvas->CopyMode;

list=new TList(); // Так создается экземпляр объекта. Здесь TList() – конструктор.

} else

/*Служебное слово throw используется для создания исключительной ситуации.

После этого нормальный ход программы прерывается.

Управление передается на ближайший блок catch .*/

throw Exception("Канва не задана!");

}

// Деструктор очищает список от спрайтов, восстанавливает свойства канвы

// и убирает сам экземпляр списка list

__fastcall TSpriteList::~TSpriteList()

{

Clear();

canvas->CopyMode=canvasCopyMode;

delete list; // Так вызывается деструктор объекта.

}

// Возвращает элемент списка спрайтов, отвечающий слою aZ,

// как указатель на объект типа TSprite

TSprite* __fastcall TSpriteList::GetItems(int aZ)

{

// служебное слово return вызывает выход из метода и возвращение значения функции

// выражение (TSprite*) означает преобразование типа указателя, полученного после

// вызова свойства list->Items[aZ], в указатель на TSprite

return (TSprite*)list->Items[aZ];

}

// Добавляет в список объект типа TSprite и возвращает указатель на добавленный объект

TSprite* __fastcall TSpriteList::AddSprite(TSprite* const sprite)

{

// двойной знак && есть операция логического умножения

if (sprite && Contains(sprite->SpriteRect,ClientRect))

{

sprite->spriteList=this ;

sprite->z =list->Add(sprite);

count=list->Count;

return sprite;

} else return NULL;

}

// Перемещает спрайт с одной плоскости в другую (в смысле z-упорядочения)

void __fastcall TSpriteList::MoveSprite(int const fromZ, int const toZ)

{

if (fromZ != toZ && fromZ > -1 && fromZ < count &&

toZ > -1 && toZ < count)

{

//В языке C локальные переменные (как minZ здесь)

// могут быть описаны в любой точке кода

// Выражение вида a = b?c:d называется условным выражением.

// В нем переменной a присваивается значение c, если выполняется условие b,

// и значение d, если оно не выполняется

// int minZ = fromZ < toZ ? fromZ : toZ;

// В операторе цикла значение i в начале инициализируется,

// затем проверяется условие окончания цикла,

// выполняется оператор внутри цикла (если условие соблюдено),

// затем меняется значение параметра i.

// В данном случае оператор i-- означает уменьшение i на 1.

for (int i = count - 1; i >= minZ; i--)

if (Items[i]->Visible) Items[i]->Restore();

list->Move(fromZ,toZ);

for (int i = minZ; i < count; i++)

{

Items[i]->z = i;

if (Items[i]->Visible) Items[i]->Paint();

}

}

}

// Освобождает экземпляр объекта типа TSprite,

// находящийся в списке под номером aZ, и убирает указатель из списка

void __fastcall TSpriteList::DeleteSprite(int const aZ)

{

if (aZ<count && aZ>-1)

{

for (int i= count-1;i>=aZ;i--)

if (Items[i]->Visible) Items[i]->Restore();

delete Items[aZ];

list->Items[aZ]=NULL;

list->Delete(aZ);

count=list->Count;

for (int i=aZ;i<count;i++)

{

Items[i]->z--;

if (Items[i]->Visible) Items[i]->Paint();

}

}

}

// Очищает список от всех спрайтов

void __fastcall TSpriteList::Clear()

{

if (list && count > 0)

for (int i = count - 1; i > -1; i--) DeleteSprite(i);

};

// Реализация методов класса списка спрайтов со следом TTracedSpriteList

// Конструктор вызывает конструктор предка и инициализирует поле traceMap

// После имени конструктора через двоеточие вызывается конструктор предка TSpriteList.

__fastcall TTracedSpriteList::TTracedSpriteList(TControlCanvas* const canvas):

TSpriteList(canvas) // Вызов конструктора предка

{

traceMap.Length=ClientRect.Right-ClientRect.Left+1;

for (int i=0;i<=traceMap.High;i++)

traceMap[i].Length=ClientRect.Bottom-ClientRect.Top+1;

}

// Деструктор вызывает очистку списка от спрайтов и вызывает деструктор предка

__fastcall TTracedSpriteList::~TTracedSpriteList()

{

Clear();

}

// Удаляет спрайт слоя aZ из списка и удаляет сам спрайт

void __fastcall TTracedSpriteList::DeleteSprite(int const aZ)

{

((TTracedSprite*)Items[aZ])->TrPoints.Length=0;

TSpriteList::DeleteSprite(aZ); // Вызывается метод предка

}

// Очищает следы спрайтов и вызывает унаследованный метод очистки

void __fastcall TTracedSpriteList::Clear()

{

for (int i=traceMap.Low;i<= traceMap.High;i++)

for (int j=traceMap[i].Low;j<traceMap[i].High;j++)

traceMap[i][j]=false ;

TSpriteList::Clear(); // Вызывается метод предка

}

// Реализация методов класса спрайт TSprite

// Конструктор инициализирует поля класса

__fastcall TSprite::TSprite(TRect const rect)

{

location=Point(rect.Left,rect.Top);

size.cx=rect.Width(); size.cy=rect.Height();

image=new Graphics::TBitmap();

image->Height=rect.Height();

image->Width =rect.Width();

z=-1;

}

// Деструктор уничтожает поле image

__fastcall TSprite::~TSprite()

{

delete image;

}

// Устанавливает новое значение поля visible и изображает или убирает спрайт с экрана

void __fastcall TSprite::SetVisible(bool const value)

{

if (value!=visible)

{

if (value)

{

BeginPaint();

Paint();

EndPaint();

} else

{

BeginPaint();

Restore();

EndPaint();

}

visible=value;

}

}

// Директива компилятору #define в данном случае вводит имя sprite

// для выражения ((TSprite*)(spriteList->Items[i])).

// Это укорачивает имя кода последующих методов

#define sprite ((TSprite*)(spriteList->Items[i]))

// Перемещает спрайт на вектор drift в плоскости изображения

bool __fastcall TSprite::Move(TSize const drift)

{

TPoint newPos=Point(location.x+drift.cx,location.y+drift.cy);

bool result=true ;

// В этом месте вызывается обработчик события onMove, если он задан

if (onMove) result=onMove(this ,newPos);

// Здесь используется то, что оператор присвоения в C возвращает присвоенное значение

// Переменная result приобретает новое значение и одновременно возвращает его как

// условие оператора if

if (result=result &&

Contains(Rect(newPos.x,newPos.y,newPos.x+size.cx,newPos.y+size.cy),

spriteList->ClientRect))

{

bool VisState=visible;

Visible=false ;

location=newPos;

Visible=VisState;

}

return result;

}

// Перемещает спрайт в точку newPos

bool __fastcall TSprite::MoveTo(TPoint const newPos)

{

TSize s;

s.cx=newPos.x-location.x;s.cy=newPos.y-location.y;

return Move(s);

}

// Готовит изображение спрайта

void __fastcall TSprite::BeginPaint()

{

SetMask(Z);

for (int i=spriteList->Count-1;i>=Z+1;i--)

if (sprite->mask && sprite->visible) sprite->Restore();

}

// Устанавливает маску для спрайта с индексом anID (слой)

void __fastcall TSprite::SetMask(int const anID)

{

for (int i=anID+1;i<spriteList->Count;i++)

{

sprite->mask= sprite->Intersect(anID,i) || sprite->mask;

if (mask) SetMask(i);

}

}

// Завершает изображение спрайта

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>

// Так описывается свойство, индексирующее объекты класса – так называемый индексатор

public Sprite this [int z] { get { return (Sprite)list[z]; } }

/// <summary>

/// Хранит текущее число спрайтов в списке.

/// </summary>

int count;

/// <summary>

/// Возвращает число спрайтов в списке.

/// </summary>

public int Count { get { return count; } }

/// <summary>

/// Инициализирует новый экземпляр объекта класса типа SpriteList.

/// </summary>

/// <param name="control">

/// Объект типа Control, на прямоугольнике которого предполагается размещать

/// спрайты - элементы списка SpriteList.

/// </param>

/// <remarks>

/// Конструктор списка создает объект типа Graphics для изображения спрайтов

/// и добавляет к событиям перерисовки и уничтожения объекта Control

/// вызов метода Clear

/// </remarks>

public SpriteList(Control control)

{

if (control == null ) throw (

new ArgumentNullException("Аргумент конструктора SpriteList не определен!"));

parent = control;

canvas = parent.CreateGraphics();

clientRect = parent.ClientRectangle;

parent.HandleDestroyed += delegate { Clear(); };

parent.Invalidated += delegate { Clear(); };

}

/// <summary>

/// Возвращает перечислитель, позволяющий перемещаться по списку.

/// </summary>

/// <returns>

/// Ссылка на объект типа IEnumerator для списка SpriteList.

/// </returns>

/// <remarks>

/// Функция GetEnumerator позволяет использовать оператор foreach

/// для членов списка (спрайтов).

/// </remarks>

public IEnumerator GetEnumerator() { return list.GetEnumerator(); }

/// <summary>

/// Очищает список и освобождает объект типа Graphics,

/// используемый для изображения спрайтов.

/// </summary>

~SpriteList()

{

Clear();

if (canvas != null ) canvas.Dispose();

}

/// <summary>

/// Создает новый экземпляр спрайта и добавляет его к списку.

/// </summary>

/// <param name="SpriteType">

/// Имя класса добавляемого спрайта.

/// </param>

/// <param name="SpriteRect">

/// Прямоугольник спрайта.

/// </param>

/// <returns>

/// Созданный и добавленный в список спрайт.

/// </returns>

/// <remarks>

/// Метод Add возвращает null , если прямоугольник спрайта не

/// вписывается в прямоугольник объекта Control.

/// </remarks>

public Sprite AddSprite(Type SpriteType, Rectangle SpriteRect)

{

if (SpriteType != null && SpriteRect != null

&& SpriteRect.Height > 0 && SpriteRect.Width > 0 &&

clientRect.Contains(SpriteRect))

{

Sprite sprite;

try

{

sprite = (Sprite)Activator.CreateInstance(SpriteType,

new object [2] { SpriteRect, this });

}

catch (Exception e)

{

throw (e is System.Reflection.TargetInvocationException ?

e.InnerException : e);

}

sprite.Z = list.Add(sprite);

count = list.Count;

return sprite;

}

return null ;

}

/// <summary>

/// Меняет z-слой положения спрайта.

/// </summary>

/// <param name="fromZ">

/// Исходный слой.

/// </param>

/// <param name="toZ">

/// Конечный слой.

/// </param>

public void MoveSprite(int fromZ, int toZ)

{

if (fromZ != toZ &&

fromZ > -1 && fromZ < count &&

toZ > -1 && toZ < count)

{

Sprite tempSprite;

int minZ = fromZ < toZ ? fromZ : toZ;

for (int i = count - 1; i >= minZ; i--)

if (this [i].Visible) this [i].Restore();

tempSprite = this [fromZ];

list.RemoveAt(fromZ);

list.Insert(toZ, tempSprite);

for (int i = minZ; i < count; i++)

{

this [i].Z = i;

if (this [i].Visible) this [i].Paint();

}

}

}

/// <summary>

/// Удаляет спрайт заданного слоя из списка.

/// </summary>

/// <param name="z">

/// Слой удаляемого спрайта.

/// </param>

public virtual void RemoveSpriteAt(int z)

{

if (z > -1 && z < count)

{

for (int i = count - 1; i >= z; i--)

if (this [i].Visible) this [i].Restore();

list.RemoveAt(z);

count = list.Count;

for (int i = z; i < count; i++)

{

this [i].Z--;

if (this [i].Visible) this [i].Paint();

}

}

}

/// <summary>

/// Очищает список от спрайтов.

/// </summary>

public virtual void Clear()

{

if (list != null && count > 0)

for (int i = count - 1; i > -1; i--) RemoveSpriteAt(i);

}

}

/// <summary>

/// Тип делегата, предназначенного для обработки события,

/// наступающего в методе Move перед перемещением спрайта.

/// </summary>

/// <param name="sender">

/// Экземпляр наследника класса Sprite, вызывающий обработчик.

/// <param name="newLocation">

/// Новое положение левой верхней вершины спрайта,

/// которое может быть изменено обработчиком.

/// </param>

/// <returns>

/// true , если перемещение в новое положение разрешено, и false в противном случае.

/// </returns>

public delegate bool BeforeMoveEventHandler(Sprite sender, ref Point newLocation);

/// <summary>

/// Абстрактный класс спрайтов.

/// </summary>

/// <remarks>

/// Спрайт - это графический объект, ограниченный прямоугольной областью.

/// Объекты наследников класса Sprite создаются методом AddSprite класса SpriteList.

/// Изображения спрайтов могут независимо перемещаться на экране,

/// как бы занимая каждый свой слой (z-упорядочение).

/// Для перемещения спрайтов служат методы Move и MoveTo.

/// Свойство Visible определяет присутствие спрайта на экране.

/// </remarks>

public abstract class Sprite : Object

{

/// <summary>

/// Инициализирует экземпляр объекта класса Sprite.

/// Вызывается в методе AddSprite класса SpriteList.

/// </summary>

/// <param name="SpriteRect">

/// Прямоугольник спрайта.

/// <param name="sprites">

/// Список спрайтов, которому принадлежит создаваемый экземпляр.

/// </param>

/// <remarks>

/// Конструктор инициализирует поля объекта.

/// </remarks>

internal Sprite(Rectangle SpriteRect, SpriteList sprites)

{

spriteSize = SpriteRect.Size;

location = SpriteRect.Location;

image = new Bitmap(spriteSize.Width, spriteSize.Height);

bmpCanvas = Graphics.FromImage(image);

this .sprites = sprites;

}

/// <summary>

/// Деструктор. Освобождает объект image.

/// </summary>

~Sprite()

{

if (image != null ) image.Dispose();

}

/// <summary>

/// Хранит текущий индекс-слой спрайта.

/// </summary>

int z = -1;

/// <summary>

/// Возвращает и устанавливает значение индекса-слоя спрайта.

/// </summary>

public int Z { get { return z; } internal set { z = value ; } }

/// <summary>

/// Хранит текущее значение маски, используемой при определении фона спрайта.

/// </summary>

bool mask;

/// <summary>

/// Устанавливает маску спрайта.

/// </summary>

/// <param name="layer">

/// Индекс (слой) спрайта.

/// </param>

void SetMask(int layer)

{

for (int i = layer + 1; i < sprites.Count; i++)

{

sprites[i].mask = sprites[i].Intersect(layer, i) || sprites[i].mask;

if (mask) SetMask(i);

}

}

/// <summary>

/// Хранит ссылку на объект класса Bitmap,

/// временно хранящего фон спрайта.

/// </summary>

Bitmap image;

/// <summary>

/// Хранит ссылку на объект класса Graphics на Bitmap, содержащий фон спрайта.

/// </summary>

Graphics bmpCanvas;

/// <summary>

/// Хранит ссылку на список типа SpriteList, которому принадлежит спрайт.

/// </summary>

SpriteList sprites;

/// <summary>

/// Устанавливает и возвращает ссылку на SpriteList, которому принадлежит спрайт.

/// </summary>

public SpriteList Sprites

{

internal set { sprites = value ; }

get { return sprites; }

}

/// <summary>

/// Хранит текущее состояние видимости спрайта на экране.

/// </summary>

bool visible;

/// <summary>

/// Устанавливает и возвращает состояние видимости спрайта на экране.

/// </summary>

public bool Visible

{

set

{

if (value != visible)

{

BeginPaint();

if (value ) Paint(); else Restore();

EndPaint();

visible = value ;

}

}

get { return visible; }

}

/// <summary>

/// Полиморфный метод установки значений полей класса.

/// </summary>

/// <typeparam name="T">

/// Тип устанавливаемого поля.

/// </typeparam>

/// <param name="outValue">

/// Результирующее значение поля.

/// </param>

/// <param name="inValue">

/// Устанавливаемое значение поле.

/// </param>

/// <remarks>

/// Метод Set убирает спрайт с экрана на время изменения его поля типа T.

/// </remarks>

protected void Set<T>(ref T outValue, T inValue)

{

if (!outValue.Equals(inValue))

{

bool VisState = visible;

Visible = false ;

outValue = inValue;

Visible = VisState;

}

}

/// <summary>

/// Хранит положение верхнего левого угла спрайта.

/// </summary>

Point location;

/// <summary>

/// Устанавливает и возвращает положение верхнего левого угла спрайта.

/// </summary>

public Point Location { get { return location; } }

/// <summary>

/// Хранит размер спрайта.

/// </summary>

Size spriteSize;

/// <summary>

/// Возвращает размер спрайта.

/// </summary>

public Size SpriteSize { get { return spriteSize; } }

/// <summary>

/// Возвращает прямоугольник спрайта

/// </summary>

public Rectangle SpriteRect { get { return new Rectangle(location, spriteSize); } }

/// <summary>

/// Хранит обработчик движения спрайта.

/// </summary>

BeforeMoveEventHandler onBeforeMove;

/// <summary>

/// Устанавливает и возвращает обработчик движения спрайта.

/// </summary>

public BeforeMoveEventHandler OnBeforeMove

{

set { onBeforeMove = value ; }

get { return onBeforeMove; }

}

/// <summary>

/// Готовит изображение спрайта.

/// </summary>

void BeginPaint()

{

SetMask(z);

for (int i = sprites.Count - 1; i >= z + 1; i--)

if (sprites[i].mask && sprites[i].Visible) sprites[i].Restore();

}

/// <summary>

/// Завершает изображение спрайта.

/// </summary>

void EndPaint()

{

for (int i = z + 1; i < sprites.Count; i++)

if (sprites[i].mask)

{

if (sprites[i].Visible) sprites[i].Paint();

sprites[i].mask = false ;

}

}

/// <summary>

/// Определяет факт пересечения прямоугольников двух спрайтов.

/// </summary>

/// <param name="First">

/// Индекс (слой) первого спрайта.

/// </param>

/// <param name="Second">

/// Индекс (слой) второго спрайта.

/// </param>

/// <returns>

/// true , если спрайты пересекаются, и false в противном случае.

/// </returns>

bool Intersect(int First, int Second)

{

return sprites[First].SpriteRect.IntersectsWith

(sprites[Second].SpriteRect);

}

/// <summary>

/// Создает конкретное изображение спрайта.

/// </summary>

/// <remarks>

/// Метод PaintPicture является абстрактным в этом классе и должен быть

/// перекрыт наследниками, формирующими изображение с помощью этого метода.

/// </remarks>

protected abstract void PaintPicture();

/// <summary>

/// Убирает спрайт с экрана.

/// </summary>

protected internal virtual void Restore()

{

sprites.Canvas.DrawImage(image, location);

}

/// <summary>

/// Помещает спрайт на экран.

/// </summary>

protected internal virtual void Paint()

{

bmpCanvas.CopyFromScreen(sprites.Parent.RectangleToScreen

(SpriteRect).Location, new Point(), image.Size);

PaintPicture();

}

/// <summary>

/// Смещает положение спрайта на плоскости XY.

/// </summary>

/// <param name="drift">

/// Вектор смещения.

/// </param>

/// <returns>

/// true , если смещение произошло, и false , если нет.

/// </returns>

public virtual bool Move(Size drift)

{

Point newPos = location + drift;

bool result = true ;

if (onBeforeMove != null )

result = onBeforeMove(this , ref newPos);

if (result = result &&

sprites.ClientRect.Contains(new Rectangle(newPos, spriteSize)))

Set<Point>(ref location, newPos);

return result;

}

/// <summary>

/// Перемещает сайт в новое положение на плоскости XY.

/// </summary>

/// <param name="newLocation">

/// Новое положение левого верхнего угла спрайта.

/// </param>

/// <returns>

/// true , если перемещение произошло, false , если нет.

/// </returns>

public virtual bool MoveTo(Point newLocation)

{

return Move((Size)newLocation - (Size)location);

}

}

/// <summary>

/// Собирает и хранит информацию о следах спрайтов, формирующих список.

/// </summary>

/// <remarks>

/// Объекты класса TracedSpriteList в добавление к свойствам своего предка

/// SpriteList создают и поддерживают битовый массив, хранящий информацию о

/// каждом пикселе клиентской области. Если пиксел является следом спрайта,

/// то соответствующий элемент массива имеет значение true , если нет, то false .

/// Класс TracedSpriteList перекрывает методы RemoveSpriteAt и Clear, уничтожая

/// информацию о следе удаляемого спрайта.

/// </remarks>

public class TracedSpriteList : SpriteList

{

/// <summary>

/// Хранит двумерный битовый массив, отображающий состояние пикселей

/// прямоугольника объекта Control - принадлежит ли пиксел следу спрайта, или фону.

/// </summary>

BitArray[] traceMap;

/// <summary>

/// Возвращает ссылку на битовый массив состояния следов спрайтов.

/// </summary>

internal BitArray[] TraceMap { get { return traceMap; } }

/// <summary>

/// Инициализирует экземпляр объекта класса TracedSpriteList.

/// </summary>

/// <param name="control">

/// Объект, на котором изображаются спрайты.

/// </param>

public TracedSpriteList(Control control) : base (control)

{

traceMap = new BitArray[ClientRect.Width];

for (int i = 0; i < traceMap.Length; i++)

traceMap[i] = new BitArray(ClientRect.Height);

}

/// <summary>

/// Убирает спрайт из списка.

/// </summary>

/// <param name="z">

/// Индекс-слой устраняемого спрайта.

/// </param>

public override void RemoveSpriteAt(int z)

{

if (z > -1 && z < Count)

{

((TracedSprite)this [z]).TracePoints.Clear();

base .RemoveSpriteAt(z);

}

}

/// <summary>

/// Очищает список от спрайтов.

/// </summary>

public override void Clear()

{

for (int i = 0; i < traceMap.Length; i++)

for (int j = 0; j < traceMap[i].Count; j++)

traceMap[i][j] = false ;

base .Clear();

}

}

/// <summary>

/// Спрайт, оставляющий след.

/// </summary>

/// <remarks>

/// Класс TracedSprite как и его предок является абстрактным.

/// Наследники класса TracedSprite получают возможность оставлять

/// след на клиентской области в форме отдельного пикселя на месте

/// положения своего центра в момент, предшествующий смене положения.

/// Порождать объекты класса TracedSprite должен метод Add, вызванный классом

/// TracedSpriteList. В противном случае будет сгенерирована

/// исключительная ситуация типа ArgumentException.

/// </remarks>

public abstract class TracedSprite : Sprite

{

/// <summary>

/// Хранит true , если спрайт оставляет след, и false , если нет.

/// </summary>

bool traced;

/// <summary>

/// Устанавливает и возвращает значение поля traced.

/// </summary>

public bool Traced { set { traced = value ; } get { return traced; } }

/// <summary>

/// Хранит true , если пиксели следа

/// имеют специальный цвет, и false , если нет.

/// </summary>

/// <remarks>

/// Если пиксели следа не имеют специального цвета, то их цвет определяется

/// как дополнительный до белого от цвета пикселя фона.

/// </remarks>

bool traceColored;

/// <summary>

/// Устанавливает и возвращает значение поля traceColored.

/// </summary>

public bool TraceColored

{

set { traceColored = value ; }

get { return traceColored; }

}

/// <summary>

/// Хранит цвет следа.

/// </summary>

Color traceColor = Color.White;

/// <summary>

/// Устанавливает и возвращает цвет следа.

/// </summary>

public Color TraceColor

{

set { traceColored = true ; traceColor = value ; }

get { return traceColor; }

}

/// <summary>

/// Хранит координаты точек следа.

/// </summary>

ArrayList tracePoints = new ArrayList();

/// <summary>

/// Возвращает ссылку на список координат точек следа.

/// </summary>

public ArrayList TracePoints { get { return tracePoints; } }

/// <summary>

/// Хранит относительное положение центра спрайта.

/// </summary>

Size centerOffset;

/// <summary>

/// Хранит абсолютное положение центра спрайта.

/// </summary>

Point center;

/// <summary>

/// Возвращает положение центра спрайта.

/// </summary>

public Point Center { get { return center; } }

/// <summary>

/// Инициализирует экземпляр объекта класса TracedSprite.

/// </summary>

/// <param name="SpriteRect">

/// Прямоугольник спрайта.

/// <param name="sprites">

/// Список спрайтов, которому принадлежит создаваемый экземпляр.

/// </param>

internal TracedSprite(Rectangle SpriteRect, SpriteList sprites) : base (SpriteRect, sprites)

{

if (!(Sprites is TracedSpriteList))

throw (new ArgumentException("Спрайт со следом может быть" +

" только членом списка - наследника TracedSpriteList!"));

centerOffset = new Size(SpriteSize.Width / 2, SpriteSize.Height / 2);

center = Location + centerOffset;

}

/// <summary>

/// Перемещает спрайт на плоскости XY.

/// </summary>

/// <param name="drift">

/// Вектор смещения.

/// </param>

/// <returns>

/// true , если перемещение произошло, и false , если нет.

/// </returns>

public override bool Move(Size drift)

{

if (Visible && Traced) PutTrace();

bool result = base .Move(drift);

if (result) center = Location + centerOffset;

return result;

}

/// <summary>

/// Изображает след спрайта.

/// </summary>

/// <remarks>

/// След спрайта изображается в виде пикселя измененного цвета в точке,

/// где находился центр спрайта на момент его перемещения.

/// </remarks>

public void PutTrace()

{

for (int i = Sprites.Count - 1; i >= 0; i--)

if (Sprites[i].Visible &&

Sprites[i].SpriteRect.Contains(center))

Sprites[i].Restore();

if (!((TracedSpriteList)Sprites).TraceMap[center.X - Sprites.ClientRect.Left]

[center.Y - Sprites.ClientRect.Top])

{

if (!traceColored)

using (Bitmap bitmap = new Bitmap(1, 1))

using (Graphics graphics = Graphics.FromImage(bitmap))

{

graphics.CopyFromScreen(Sprites.Parent.RectangleToScreen(

new Rectangle(center.X, center.Y, 1, 1)).Location,

new Point(), bitmap.Size);

Color clr = bitmap.GetPixel(0, 0);

using (Brush brush = new SolidBrush(

Color.FromArgb(0xff ^ clr.R, 0xff ^ clr.G, 0xff ^ clr.B)))

Sprites.Canvas.FillRectangle(brush, center.X, center.Y, 1, 1);

}

else

using (Brush brush = new SolidBrush(traceColor))

Sprites.Canvas.FillRectangle(brush, center.X, center.Y, 1, 1);

((TracedSpriteList)Sprites).TraceMap[center.X - Sprites.ClientRect.Left]

[center.Y - Sprites.ClientRect.Top] = true ;

tracePoints.Add(new Point(center.X, center.Y));

}

foreach (TracedSprite sprite in Sprites)

if (sprite.Visible && sprite.SpriteRect.Contains(center))

sprite.Paint();

}

/// <summary>

/// Очищает коллекцию точек следа спрайта.

/// </summary>

~TracedSprite()

{

if (TracePoints != null && TracePoints.Count > 0) TracePoints.Clear();

}

}

/// <summary>

/// Спрайт в форме заполненного эллипса, заданного цвета и градиента.

/// </summary>

public class FillEllipseSprite : TracedSprite

{

/// <summary>

/// Хранит цвет спрайта.

/// </summary>

Color color = System.Drawing.Color.Gray;

/// <summary>

/// Возвращает и устанавливает цвет спрайта.

/// </summary>

public Color Color

{

set { Set<Color>(ref color, value ); }

get { return color; }

}

/// <summary>

/// Хранит указание на то, является ли заполнение эллипса градиентным.

/// </summary>

bool isGradient = true ;

/// <summary>

/// Устанавливает и возвращает поле isGradient.

/// </summary>

public bool IsGradient

{

set { Set<bool >(ref isGradient, value ); }

get { return isGradient; }

}

/// <summary>

/// Хранит цвета границы градиентного заполнения.

/// </summary>

Color[] colors = { Color.FromArgb(0, 0, 0) };

/// <summary>

/// Устанавливает и возвращает цвета границы градиентного заполнения.

/// </summary>

public Color[] Colors

{

set { Set<Color[]>(ref colors, value ); }

get { return colors; }

}

/// <summary>

/// Инициализирует экземпляр объекта класса FillEllipseSprite.

/// </summary>

/// <param name="SpriteRect">

/// Прямоугольник эллипса.

/// <param name="sprites">

/// Список спрайтов, которому принадлежит создаваемый экземпляр.

/// </param>

public FillEllipseSprite(Rectangle SpriteRect, SpriteList sprites)

: base (SpriteRect, sprites) { }

/// <summary>

/// Изображает спрайт в форме заполненного эллипса.

/// </summary>

protected override void PaintPicture()

{

if (!isGradient)

using (Brush brush = new SolidBrush(color))

Sprites.Canvas.FillEllipse(brush, SpriteRect);

else

using (GraphicsPath path = new GraphicsPath())

{

path.AddEllipse(SpriteRect);

using (PathGradientBrush pthGrBrush = new PathGradientBrush(path))

{

pthGrBrush.CenterColor = color;

pthGrBrush.SurroundColors = colors;

Sprites.Canvas.FillEllipse(pthGrBrush, SpriteRect);

}

}

}

}

}

Предлагается в среде MS Visual Studio 2005 составить проект, тестирующий описанные классы спрайтов.