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

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

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


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


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


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


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


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


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


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


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


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 составить проект, тестирующий описанные классы спрайтов.

Сохранить в соц. сетях:
Обсуждение:
comments powered by Disqus

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

Слов:12806
Символов:128631
Размер:251.23 Кб.