Сообщений 2 Оценка 207 [+1/-0] Оценить |
Нет, мы не будем разбирать произведение А.С.Пушкина “Пиковая дама”. В данной статье я расскажу вам, как написать свою карточную игру на Visual Basic .NET с использованием библиотеки CARDS.DLL, входящей в состав Windows. Лично я впервые узнал о возможности использовании готовой библиотеки в своих программах из книги Брюса Мак-Кинни "Крепкий орешек Visual Basic". Я очень обрадовался, когда узнал, что нет необходимости самостоятельно рисовать колоду карт. Всю работу за нас сделали программисты из Microsoft (за что им отдельное спасибо!). Тех, кто по-прежнему программирует на Visual Basic 6.0, я отсылаю на свой сайт http://rusproject.narod.ru/games.htm, где вы можете найти подборку игр с использованием этой библиотеки. Но на дворе уже 2005 год и программисты вовсю изучают новую платформу .NET Framework. Давайте рассмотрим процесс создания карточной игры с учетом реалий сегодняшнего дня.
Прежде чему приступить к программированию игры, надо сначала выяснить, о чем собственно, идет речь. Итак, в состав Windows, начиная с Windows 95 (А может и раньше), традиционно входит набор карточных игр. Если внимательно приглядеться, можно заметить, что все игры имеют одинаковый интерфейс. Объясняется это очень просто – игры обращаются к одной и той же библиотеке. В Windows XP данная библиотека находится в файле CARDS.DLL. Впрочем, надо заметить, что название файла тоже менялось в разных версиях Windows, пройдя путь от 16-битной версии к 32-битной. Я думаю, самое время запустить какую-нибудь карточную игру, чтобы посмотреть, как вообще выглядят карты. Для наших целей лучше всего подойдет игры Косынка, которая имеет в своих настройках пункт Рубашка (меню Игры | Рубашка…).
Используя эту опцию, можно посмотреть не только на рабочую сторону карты, но и на обратную. Если ваше знакомство с Windows состоялось достаточно давно, то вы можете обратить внимание, что внешний вид карт (точнее, обратной их стороны) в Windows XP немного отличается от рубашек в играх на Windows 9x. Для особо любознательных сообщу, что в старых Windows некоторые виды рубашек даже содержали анимационные эффекты в виде летающих летучих мышей или мигающих приборов у робота.
Итак, библиотека CARDS.DLL содержит пять функций для работы с картами. Вот как выглядят объявления четырех функций:
Declare Function cdtInit Lib"cards.dll" (_ ByRef width AsInteger, _ ByRef height AsInteger) AsBooleanDeclareFunction cdtDraw Lib"cards.dll" ( _ ByVal hdc As IntPtr, _ ByVal x AsInteger, ByVal y AsInteger, _ ByVal card AsInteger, _ ByVal mode AsInteger) AsBooleanDeclareFunction cdtDrawExt Lib"cards.dll" (ByVal hdc As IntPtr, _ ByVal x AsInteger, ByVal y AsInteger, ByVal dx AsInteger, _ ByVal dy AsInteger, ByVal card AsInteger, _ ByVal suit AsInteger, ByVal color AsLong) AsBooleanDeclareSub cdtTerm Lib"cards.dll" () |
Объявление пятой функции cdtAnimate() я приводить не буду, так как в Windows XP она работать не будет. Если вам все-таки хочется поработать и с этой функцией, то обратить к поисковым службам. Да поможет вам Goggle!
Разберем объявленные функции поподробнее.
Функция cdtInit инициализирует библиотеку Cards.dll. Как видите, функция имеет два аргумента, в которых содержится информация о ширине и высоте карты в пикселях. Размер стандартной карты по умолчанию – 71 на 97 пикселей. Впрочем, можно не обращать внимания на эти величины при вызове функции, так как они не влияют на выводимый размер. При успешной инициализации возвращается TRUE, в противном случае возвращается FALSE.
Функция cdtTerm выполняет обратную задачу и очищает ресурсы. Данную функцию удобнее всего вызывать при закрытии программы.
Функция cdtDraw выводит на экран заданную карту. Функция имеет следующие параметры:
Карты можно рисовать на экране различными способами. Вот перечень доступных режимов.
Обратите внимание, что нельзя получить доступ к изображениям карт напрямую, сначала вам необходимо получить доступ к графическому контексту устройства.
Функция cdtDrawExt аналогична предыдущей функции cdtDraw, но имеет два дополнительных параметра, dx и dy, позволяющих определять размер выводимой карты.
В библиотеке содержится 68 картинок. Из них 52 выводят игровую картинку карты и 16 выводят обратную сторону (рубашку). Все карты хранятся в библиотеке в определенном порядке. Названия масти и ее перевод на русском дан в листинге ниже. Первой картой по порядку является (индекс = 0) туз треф (Ace of Clubs), далее туз бубен (Ace of Diamonds) и так далее. Так и идут все 52 карты от Clubs, Diamonds, Hearts до Spades и обратно к Clubs, начиная с туза и заканчивая королем. Эти карты можно представить с помощью двух перечислений для масти и номинала карты.
'Масти карт Public Enum eSUIT CLUBS = 0 ' трефы DIAMOND = 1 ' бубны HEARTS = 2 ' черви SPADES = 3 ' пикиEndEnum'Номинал картыPublicEnum eFace Ace = 0 'туз Two = 1 ' двойка Three = 2 ' тройка Four = 3 ' четверка Five = 4 ' пятерка Six = 5 ' шестерка Seven = 6 ' семерка Eight = 7 ' восьмерка Nine = 8 ' девятка Ten = 9 ' десятка Jack = 10 ' валет Queen = 11 ' дама King = 12 ' корольEndEnum |
Чтобы получить номер нужной карты, надо знать волшебную формулу:
card = eFace * 4 + eSuit |
Например, нужно получить номер пиковой дамы. Подставляем в формулу нужные значения:
card = eFace.Queen * 4 + eSUIT.DIAMOND |
Например, пиковой даме соответствует номер 47 (11 * 4 + 3).
Шестнадцать рисунков рубашек карт находятся в промежутке от 53 до 68. Опять используем перечисление для удобства:
' Картинки для рубашки Public Enum eBACK CROSSHATCH = 53 ' Сетка SKY = 54 ' Небо MINERAL = 55 ' Минерал FISH = 56 ' Рыба FROG = 57 ' Лягушка FLOWER = 58 ' Цветок ISLAND = 59 ' Остров с пальмами SQUARES = 60 ' Квадраты MAGENTA = 61 ' Фиолетовый узор SANDDUNES = 62 ' Песчаные дюны SPACE = 63 ' Астронавт LINES = 64 ' Линии CARS = 65 ' Машинки UNUSED = 66 ' Неиспользуемая карта THE_X = 67 ' Символ X THE_O = 68 ' Символ 0EndEnum |
Теперь можно приступить к написанию программы Для удобства расположим код рисования карт в отдельном классе cards, который будет базовым классом для примера. В этом классе разместим все функции, константы и перечисления. Так как при работе с карточной библиотекой необходима ее инициализация, то сделаем это в конструкторе класса:
Public Sub New() cdtInit(width, height) EndSub |
Наконец-то мы можем приступать к выводу карты на экран. Давайте оформим этот код в виде функции DrawCard:
Public Function DrawCard(ByVal hdc As IntPtr, ByVal x AsInteger, _ ByVal y AsInteger, ByVal card AsInteger, ByVal mode AsInteger) AsBooleanReturn cdtDraw(hdc, x, y, card, mode, &HFFFFFF) EndFunction |
При вызове данной функции понадобятся дескриптор контекста устройства, координаты выводимой карты, номер карты и режим вывода. Предположим, что нужно вывести пиковую даму. Вычисляем ее номер и вызываем функцию:
Private Sub Form1_Paint(ByVal sender AsObject, ByVal e _ As System.Windows.Forms.PaintEventArgs) HandlesMyBase.Paint hdc = e.Graphics.GetHdc() Try mycard.DrawCard(hdc, 35, 10, 47, 0) Finally If Not hdc Is Nothing Then e.Graphics.ReleaseHdc(hdc) End Try EndSub |
Таким образом, с помощью данной функции можно вывести любую карту из колоды. Процесс подключения созданного класса к основной форме приложения я опущу в целях экономии места. Вы можете все сами посмотреть в исходном примере.
Теперь, когда мы разобрались с открытыми картами, перейдем к выводу рубашек карт. Опять пишем функцию-обертку DrawBackCard:
Public Function DrawBackCard(ByVal hdc As IntPtr, ByVal x AsInteger, _ ByVal y AsInteger, ByVal back As eBACK) AsBooleanReturn cdtDraw(hdc, x, y, back, mdFaceDown, &HFFFFFF) EndFunction |
Эта функция почти аналогична предыдущей, только вместо картинки и режима вывода карты используется одно из значений перечисления eBack. Вот как выглядит вывод двух разных рубашек с изображением машинок и лягушки (хотя, конечно, в настоящей карточной игре используется рубашка одного вида)
Public Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Dim g As Graphics = CreateGraphics() hdc = g.GetHdc() ' Выводим рубашки с изображением машинок и лягушки mycard.DrawBackCard(hdc, 120, 10, cards.eBACK.CARS) mycard.DrawBackCard(hdc, 200, 10, cards.eBACK.FROG) g.ReleaseHdc(hdc) EndSub |
Останавливаться на функциях DrawInvertedCard, DrawEmptyCard и DrawBigCard нет смысла, так как они весьма похожи на две предыдущие. Хочу только заметить, что функция DrawBigCard на самом деле может рисовать не только большие, но и маленькие карты, и она использует карточную функцию cdtDrawExt.
Можно выводить карты не только в стандартном виде, но и развернуть их на любой градус. Вот как будет выглядеть функция для вращения карты на 90 градусов
Public Sub DrawRotatedCard(ByVal g As Graphics, ByVal x AsInteger, _ ByVal y AsInteger, ByVal card AsInteger, ByVal mode AsInteger) Dim offScreenBitmap As Bitmap = New Bitmap(71, 97) Dim offScreenGraphics As Graphics = g.FromImage(offScreenBitmap) ' Получим дескриптор контекста устройстваDim hdc As IntPtr = offScreenGraphics.GetHdc() cdtDraw(hdc, 0, 0, card, mode, &HFFFFFF) ' Освобождаем ресурсы offScreenGraphics.ReleaseHdc(hdc) ' Массив из вершин (верхний левый угол, верхний правый угол, нижний левый угол)Dim aPoints() As Point = {New Point(x, y + 71), _ New Point(x, y), _ New Point(x + 97, y + 71)} g.DrawImage(offScreenBitmap, aPoints) EndSub |
Прежде всего создается новый графический объект, на котором будем рисовать. Затем создаем на его основе новый объект Graphics и получаем дескриптор контекста устройства, на котором рисуем карту как обычно, затем удаляем дескриптор контекста устройства для освобождения ресурсов. Теперь приступаем к вращению карты. Массив aPoints содержит три вершины прямоугольника. Три структуры Point содержат верхнюю левую, верхнюю правую и нижнюю левую углы прямоугольника. Массив передается массиву DrawImage(), где картинка, представленная параметрами (offScreenBitmap), деформируется и сдвигается, чтобы соответствовать параллелограмму, заданному параметром aPoints. Таким образом можно нарисовать любой поворот карты, просто надо вычислить нужное значение координат вершин карты.
Вот и подошел к концу рассказ о том, как использовать карточную библиотеку в своих программах. На самом деле, созданный проект выглядит немного неуклюже. Многие методы используют одни и те параметры, не очень удобен вызов конкретной карты и так далее. Улучшение кода (или основательная его переделка) будет вашим домашним заданием. Потрудитесь написать игру сами, чтоб получить удовольствие от собственной работы. Удачного вам программирования!
Сообщений 2 Оценка 207 [+1/-0] Оценить |