Главная Учебники - Разные Лекции (разные) - часть 22
Министерство образования и науки Российской Федерации Федеральное агентство по образованию Государственное образовательное учреждение высшего профессионального образования «РОСТОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ» МЕТОДИЧЕСКИЕ УКАЗАНИЯ к курсу программирования для студентов физического факультета Сравнительное объектно-ориентированное проектирование 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();
|