Баланс игры
Я полагаю, что одна из отличительных черт, выделяющих C&C, это возникающее чувство сбалансированности. По моему мнению Command & Conquer одна из наиболее сбалансированных игр. Вы не можете выиграть с помощью одного типа подразделений или единственного типа стратегии. Кажется, почти все в игре имеет свою Немезиду.
Вы должны бороться за сбалансированность, программируя любые типы стратегических игр. Если вы допустите несбалансированность, игроки быстро найдут ее и воспользуются ею. Это ясно видно в таких играх, как Total Annihilation от Cavedog Entertainment. В Total Annihilation выигрывает тот, кто первым построит большие воздушные силы. Самолеты передвигаются слишком быстро, и нет способа для их эффективного уничтожения.
Разрабатывая свою игру убедитесь, что вы выполнили моделирование сражений между разными подразделениями. Это поможет выявить их сильные и слабые стороны, а также обнаружить критическую несбалансированность.
Будущее стратегий реального времени
Теперь, когда вы увидели прошлое стратегий реального времени, я покажу вам будущее. Конечно, я не гадалка, но попытаюсь сделать это.
Command & Conquer от Westwood
Прошло более десяти лет с момента выхода игры Utopia, когда компания Westwood выпустила быстро ставшую популярной стратегию реального времени Command & Conquer. C&C, как известно не была первой современной стратегией реального времени, но она действительно определила черты современных игр этого жанра. Ниже вы можете видеть графическую заставку игры:
Рис. 1.5. Титульный экран игры Command & Conquer. ©2002 Electronic Arts, All Rights Reserved.
Воспоминания возвращаются— моя армия готова, БМП загружены, воздушные силы заправлены и готовы к вылету, ядерное оружие готово к использованию. Я отдаю приказ «вперед» отвлекающему подразделению БМП. Оно стремительно атакует защитные системы врага. Пока враг занят отвлекающим маневром БМП, я отправляю флот геликоптеров атаковать строительный центр, сердце действий врага. Тем временем мой 11-й танковый дивизион медленно продвигается, разрушая внешнюю стену вражеской базы. В это время подразделение геликоптеров прорвало передовую линию ПВО и атаковало нервный центр. Я даю зеленый свет БМП с инженерами, они направляются к вражеской базе. Мои геликоптеры были разрушены, но нанесли серьезный урон строительному центру — он уже дымится. Пришло время для ядерной атаки. Код введен и ядерные заряды отправляются в атмосферу, нацеленные на строительный центр. После попадания нервный центр врага прекращает существование. Он не выдержал комбинацию из атаки геликоптеров и атомного оружия. К этому времени танки глубоко вклинились на территорию вражеской базы. Инженеры прибыли к кратеру, находящемуся на месте бывшегго строительного центра и выгрузились из БМП. Они размещают заряды на оставшихся оборонительных сооружениях. Заряды взрываются и разрушают цели. После этого танки идут вперед и разрушают то, что осталось от базы. Подождите, часы действительно показывают пять утра?
Я не могу сосчитать, сколько часов потратил на игру C&C в моем первом офисе. Мой деловой партнер и я приглашали в офис пару друзей и играли до раннего утра. Мой первый офис располагался в крошечном помещении размерами десять на десять футов. Компьютеры Pentium-133 (последняя модель в то время) создавали столько тепла, что температура в помещении достигала 89 градусов по Фаренгейту. Ничто из этого нас не устрашало; мы набивались в офис и играли. Обычно собиралось более четырех человек, и шла игра на выбывание.
Command H.Q.
Еще одна из моих любимых старых стратегических игр называется CommandH.Q. Ее разработала Ozark Softscape, — небольшая домашняя команда разработчиков из Арканзаса. Наш мир настолько тесен,что один из разработчиков из Ozark, Марк Ботнер, теперь работает со мной (привет, Марк!).
В Command H.Q. вы сражаетесь один на один с другим игроком или против компьютера. Игра очень похожа на Empire, где вы завоевываете города, создаете воинские подразделения и совершаете набеги на вражескую территорию. Особенность игры заключается в том, что вы можете выбирать временной период, в который происходит игра. Если вы выберете прошедшие года, например 1918, то будете играть со старой технологией. Если вы выберете будущее, скажем 2023 год, в игре будут доступны войска будущего. Кроме того, игра поддерживает многопользовательский режим с соединением компьютеров через модем или через последовательный кабель.
В Command H.Q. есть несколько зацепок для привлечения внимания игрока. Во-первых — это управление ресурсами. Это одна из первых игр, в которые я играл, где в общую картину добавлена экономика. Контроль над месторождением нефти имеет такую же стратегическую ценность, как контроль над важным аванпостом. Второй приманкой является многопользовательский режим. Искуственный интеллект игры достаточно слаб, но многопользовательский режим компенсирует это недостаток, позволяя играть с живым соперником.
Empire
Около 1987 года компания с названием Interstel выпустила игру Empire. Empire — это двухмерная походовая стратегическая игра в которой вам надо завоевывать города, чтобы установить господство над миром. Каждый раз, когда вы завоевываете город, у вас появляется возможность создавать в нем новые армии. Вы можете выбрать, какую армию создавать из списка в котором перечислены восемь различных воинских подразделений. Одни соединения создаются дольше, чем другие, кроме того некоторые подразделения доступны только в городах, расположенных рядом с океаном. Например, авианосцы доступны только в тех городах, где есть гавань.
Empire — удивительно захватывающая игра, которая удерживает вас на долгие часы. Я не уверен, почему она настолько захватывающая. Может быть потому что чем больше городов вы завоевываете, тем больше подразделений можете создать? Или возможно потому что карты очень большие и требуют много времени на исследования? Ключевой момент, который следует всегда помнить, заключается в том, что игра может быть простой и при этом доставлять массу удовольствия. К этой цели должны стремиться все программисты игр.
Рис. 1.14. Классическая игра Empire. ©2003 Killer Bee Software, All Rights Reserved.
Если вас заинтересовала игра Empire, проверьте сайт Killer Bee Software. Марк Кинкэд из Killer Bee Software приобрел права на серию Empire и планирует выпустить восстановленную игру Empire и обновленную версию.
Игровое поле
На рис.1.1 показано представление игрового поля игры Utopia. Мир разделен на два отдельных острова. На игровом поле существуют несколько видов элементов: блоки, представляющие землю, здания, океан, корабли, рыбу и погоду.
Рис. 1.1. Игровое поле программы Utopia
Чем была бы игра без игрового поля? Тем же самым, чем будет приборная панель автомобиля без ветрового стекла! Игровое поле используется чтобы показать игру в действии. На нем отображается территория, здания и подразделения. Прочитав это вы можете подумать «Ха! Я так и знал!». Но прежде чем делать поспешные выводы, подумайте обо всех играх прошлых лет, которые обходились без игрового поля. Текстовые приключенческие игры, MUD и игры через BBS в основном обходились без игрового поля. Я думаю, должен вызывать интерес тот факт, что многие игры прошлого вообще обходились без графического вывода. Помните об этом разрабатывая свои игры. Роскошная графика и спецэффекты не являются необходимыми для того чтобы игра была интересной и захватывающей.
Индикаторы ресурсов
В игре Command & Conquer есть единственный индикатор ресурсов, расположенный в верхней части экрана и отображающий количество имеющегося у игрока тибериума. Планируя свою собственную стратегическую игру, подумайте о размещении индикаторов. Вы не должны загромождать текстом весь интерфейс— это очень важный аспект проектирования интерфейса.
Интерфейс
В C&C есть несколько элементов, перенесенных из более ранней игры Westwood, Dune. На рис.1.7 можно заметить общие принципы организации интерфейса.
Рис. 1.7. Интерфейс Command & Conquer. ©2002 Electronic Arts, All Rights Reserved.
Как видно на рисунке, интерфейс состоит из следующих основных элементов: экран радара, индикаторы ресурсов, объекты для постройки, уровень энергии и игровая область.
История
Предпосылка для игры Command & Conquer заключается в том, что вы контролируете команду Глобальной Оборонной Инициативы Объединенных Наций, или GDI. GDI воюет со злым Братством NOD, лидера которого зовут Кейн. Вы можете видеть его фотографию на рис. 1.6. Главная идея состоит в том, что обе группировки находятся на планете, где сражаются за тибериум. Тибериум — это минерал, который делает возможным существование окружающего мира. Он используется в игре для постройки любого военного подразделения или здания. Без него вы проиграете. Раз тибериум это минерал, его необходимо собирать. Поэтому общая стратегия в игре выглядит так: собрать столько тибериума, сколько возможно, построить армию и уничтожить другого игрока (или игроков).
Рис. 1.6. Кейн, злой коммандир (из Tiberian Sun). ©2002 Electronic Arts, All Rights Reserved.
В конце каждой главы будет
В конце каждой главы будет приводиться подборка советов и секретов, представленных мною на протяжении главы. Итак, вот что мы узнали в главе 1:
Простой набор основных правил может сделать вашу игру интересной. Очень сложные системы игры не являются необходимыми. Главное — заинтересовать игрока.
Иногда простая идея может следать игру гораздо интереснее.
Планируя свою стратегию реального времени, подумайте о размещении выводимых данных. Не следует загромождать весь интерфейс текстом — это важный аспект разработки интерфейса.
Постарайтесь, чтобы игра не выглядела похожей на электронную таблицу. Для этого вы должны использовать графические представления числовых значений.
Чтобы игра была интересной и захватывающей, роскошная графика и спецэффекты не обязательны.
Разрабатывая свою игру убедитесь, что вы выполнили моделирование сражений между разными подразделениями. Это поможет выявить их сильные и слабые стороны, а также обнаружить критическую несбалансированность.
Игра с сотнями несбалансированных подразделений гораздо хуже, чем игра с десятком идеально сбалансированных подразделений.
netlib.narod.ru | < Назад | Оглавление | Далее > |
Экран радара
Экран радара показывает вам территорию, стоения и подразделения на игровой карте. Сразу после запуска игры экран радара неактивен. Чтобы он включился, игроку необходимо построить несколько зданий. Тот факт, что вы начинаете игру без этой технологии, добавляет интересный штрих к игровому процессу. Для рисования радарной карты игровой движок должен только уметь представлять каждый блок карты в виде одного пикселя. Сначала отображается территория, затем здания и потом подразделения. Каждому игроку назначается отдельный цвет, чтобы их можно было отличать друг от друга.
Большинство современных стратегий реального времени используют тот или иной вид радарного экрана. Игры, подобные Age of Empires от Ensemble Studios также используют экран радара для показа ресурсов, людей, территории и других, интересных игроку элементов. Игра Age of Empires идет в средневековом окружении, так что в ней не может быть настоящего радара, однако показывается та же самая информация. Возможно, какой-то фермер создал твердотопливный ракетный двигатель из свиного навоза, чтобы запустить спутник-шпион. Выбирайте объяснение сами.
Корабли
В игре вы можете покупать корабли двух типов: рыболовецкие и боевые. Когда вы строите рыболовецкое судно, оно появляется на ближайшем свободном квадрате вашей базы.
Рыблолвецкими судами можно управлять, так что вы можете отправить их в плавание по океану. Когда вы помещаете рыболовецкий корабль на квадрат с косяком рыбы, он начинает производить золото. Кроме того, рыболовецкое судно автоматически кормит 500 человек. Эти корабли абсолютно необходимы игроку.
Боевые корабли помогают защищаться от пиратов и от боевого флота вашего противника. Кроме того, вы можете нападать на рыболовецкие суда противника и отправлять их на дно моря.
Объекты для постройки
В середине правой части интерфейса расположены кнопки, представляющие здания и подразделения, которые может создать игрок. В начале игры игрок может создать всего несколько видов объектов. По ходу игры игрок открывает новые объекты для строительства и они появляются в рассматриваемом списке. Это хороший способ представить игроку все объекты, которые он может построить. Вместо того, чтобы блуждать по множеству меню, игрок прокручивает список доступных зданий и подразделений, после чего щелкает по требуемому объекту. На рис. 1.8 представлено изображение разведывательного байка.
Рис. 1.8. Разведывательный байк GDI. ©2002 Electronic Arts, All Rights Reserved.
Океан
Значительная часть игрового поля занятя океаном. Океан — свободная зона, и никто не может владеть отдельным квадратом океана. Вы не можете строить в океане здания, но вы можете строить рыболовные или боевые корабли, которые будут плавать в нем.
Первые популярные стратегии реального времени
Развитие большинства жанров видеоигр определялось появлением какой-либо популярной игры. Castle Wolfenstein и Doom сделали популярными трехмерные «стрелялки» с видом от первого лица. Игра SimCity принесла популярность экономическим симуляторам. Civilization определила развитие пошаговых стратегических игр. Путь стратегий реального времени не столь ясен, поскольку развитие жанра определили несколько игроков.
Первые стратегические игры
В отличие от стратегий реального времени обычные стратегические игры развивались в течение гораздо более длительного времени. Я не могу утверждать, что знаю какие игры были первыми, а здесь я представлю несколько привлекших мое внмание.
Первые стратегии реального времени
Также, как история древних веков содержит много загадок, прошлое стратегических игр реального времени не является полностью ясным. Многие люди утверждают, что первая стратегия реального времени— это Dune от Westwood, но я вспоминаю намного более ранние примеры игр этого жанра.
Populous от Bullfrog
Через несколько лет после дебюта игры Utopia компания Bullfrog выпустила игру Populous. Это не была типичная стратегия реального времени, поскольку вы не могли непосредственно создавать военные соединения. Вместо этого ваши здания «выводили» большее количество жителей. Большее население увеличивало вашу власть.
Приманка
Первая игра Populous (сейчас игр с таким названием существует по меньшей мере три) была первой популярной игрой, позволяющей игроку побыть в роли «бога». Такая власть позволяет игроку испытать гораздо больше, чем простое строительство империи. В результате строительства большего числа городов вы получаете большую цивилизацию, но, кроме того, вы получаете и магические силы. По мере возрастания этих сил вы можете использовать все более мощные заклинания. Некоторые заклинания представляют собой простые атаки, такие как удар молнии. Другие заклинания гораздо мощнее, например, вулканы. Нет ничего приятнее, чем создать вулкан посреди города ващего противника.
Как вы теперь знаете, Utopia
Как вы теперь знаете, Utopia имеет весьма небольшой набор руководящих правил. Игровой процесс достаточно прямолинеен, поскольку ваша основная задача — произвести наибольшее количество золота. В конце игры побеждает тот игрок, у которого больше золота. Очень интересно, что в игре с таким небольшим количеством правил и параметров существует множество возможных стратегий.
Я описал эту игру так подробно, чтобы показать, что очень простая (по сегодняшним меркам) игра может быть и интересной и многогранной. Когда вы будете разрабатывать и программировать игры, помните, что простой набор базовых правил может сделать вашу игру более привлекательной для игрока. Нет необходимости в очень сложных игровых системах. Главное — чтобы игрок получал удовольствие.
Рыба, погода и пираты
Игрок имеет значительный контроль над миром, но есть вещи неподвластные ему. Косяки рыбы и пираты беспорядочно бороздят океан. Рыба полезна для получения золота, а пираты могут потопить ваш рыболовный флот.
Рис. 1.4. Трусливые пираты бороздят океан игры Utopia |
Есть несколько погодных явлений: ливни, тропические штормы и ураганы. Ливни полезны, поскольку когда они проходят над полями, то приносят золото. Тропические штормы тоже приносят золото аналогичным образом, но они могут уничтожить поля, потопить корабли и, иногда, даже разрушить здания. Ураганы хуже всего— они уничтожают все на своем пути. Опасайтесь ураганов — если вы не будете действовать быстро, они могут уничтожить весь ваш рыбацкий флот.
Широкомасштабные многопользовательские игры
Я предсказываю, что в стратегические игры реального времени будущего будут играть одновременно много пользователей. В настоящее время уже идет работа над несколькими широкомасштабными многопользовательскими играми, но готов держать пари, что в ближайшие годы таких проектов станет гораздо больше. Я, со своей стороны, хотел бы увидеть военную игру в которой могут одновременно участвовать тысячи игроков. Конечно, есть несколько игр, утверждающих, что предоставляют подобную возможность, но, по моему мнению, ни одна из них не достигает полностью обещанного результата.
Представьте, какое удовольствие доставит командование небольшим отрядом солдат в подобной игре. Каждое принимаемое вами решение может оказать влияние на исход сражения и войны в целом. Должно быть смоделировано все— от снабжения до командования. Варианты безграничны — хватало бы мощности процессора и полосы пропускания.
StarCraft от Blizzard
Другой популярной игрой, выпущенной Blizzard Entertainment является StarCraft. StarCraft чем-то похожа на Warcraft: Orcs & Humans, но ее действие разворачивается в будущем. В игре имеются три различные расы, каждая со своими сильными и слабыми сторонами. Эта уникальная особенность выделяет StarCraft среди современников. До StarCraft, большинство стратегий реального времени предлагали противостояние примерно одинаковых рас или сторон. StarCraft впервые предложил настолько различных соперников. На рис. 1.13 показана атака расы, называемой зерги на людей.
Рис. 1.13. Атака зергов на людей в игре StarCraft. ©2002 Blizzard Entertainment, All Rights Reserved.
netlib.narod.ru | < Назад | Оглавление | Далее > |
Строительство ландшафта
Другой уникальной особенностью игры Populous является возможность изменять ландшафт территории, на которой проживают ваши люди. Инструменты в игре позволяют вам создавать равнины или возвышенности. Создавая большие пространства ровной земли вы обеспечиваете свое население пригодным для строительства ландшафтом. Если вы оставляете свою землю неровной, ваши люди вынуждены жить в небольших домах. Если же вы создадите широкие равнины, они смогут строить замки и большие здания. Проектируя свою игру помните об этой уникальной возможности. Иногда даже самые простые идеи делают игру гораздо более увлекательной.
Как я упоминал, было выпущено несколько вариантов игры Populous. Чтобы получить самые последние новости, относящиеся к этой игре, посетите сайт http://www.populous.net.
netlib.narod.ru | < Назад | Оглавление | Далее > |
Технология
Как и в большинстве современных стратегий реального времени, в Command& Conquer присутствуют две воюющие стороны. Каждая из них обладает собственными сильными и слабыми сторонами. В данном случае сильные и слабые стороны выражаются в форме подразделений и оружия.
У зловещего Братства NOD есть тяжелые и медленные подразделения, обладающие большой огневой мощью. Также у него есть исключительно сильная оборона и специальные средства для атак. Одним из таких специальных средсв являются ядерные ракеты. Когда игрок, управляющий NOD, построит Храм NOD, он получает возможность достаточно часто запускать ядерные ракеты. Этот зловещий храм изображен на рис. 1.9.
Рис. 1.9. Храм NOD. ©2002 Electronic Arts, All Rights Reserved.
После запуска ядерных ракет храм должен быть перезаряжен. Это сохраняет баланс игры, поскольку игрок NOD не может просто сидеть на месте и запускать ракету за ракетой. Ядерная ракета особенно разрушительна в точке взрыва. Большинство строений в игре не могут противостоять такому нападению и разрушаются. На рис. 1.10 показаны опустошения, производимые подобным нападением.
Рис. 1.10. Атака ядерных ракет на игрока GDI. ©2002 Electronic Arts, All Rights Reserved.
Как я упоминал ранее, NOD обладает также замечательной обороной. Одно из таких оборонительных сооружений называется Рука NOD. Это башня с лазером, который может разрушить большинство врагов за один выстрел. Обратная сторона такой защиты — большое потребление энергии и слабая защищенность против большого количества врагов. Вы можете увидеть как действует это оружие на рис. 1.11.
Рис. 1.11. Рука NOD в действии. ©2002 Electronic Arts, All Rights Reserved.
Узнав об огневой мощи, доступной NOD, вы можете спросить, что может противопоставить этому GDI. Оказывается GDI обладает исключительно быстрыми подразделениями и мощными воздушными силами. Из специального вооружения у GDI есть ионная пушка, посылающая мощный пучок энергии в заданную цель. Обычно она уничтожает почти все, во что попадает.
Воздушные силы GDI действительно великолепны. Летяшие в строю атакующие геликоптеры практически неуничтожимы. Одна из используемых мною в игре стратегий состояла в создании флота геликоптеров Orca и отправке этого роя для уничтожения врага. Если вы построите достаточное количество геликоптеров, они сокрушат систему ПВО. Геликоптер Orca изображен на рис. 1.12.
Рис. 1.12. Геликоптер Orca ВВС GDI. ©2002 Electronic Arts, All Rights Reserved.
The Seven Cities of Gold
Если у вас был компьютер Commodore 64 или Atari 800, возможно вы играли в игру The Seven Cities of Gold. Это еще одна замечательная игра, созданная Ozark Softscape. Она была выпущена в 1987 году и имела оглушительный успех. В этой игре вы занимались исследованием Нового Света в роли Христофора Колумба или испанского конквистадора.
Игра очень интересная и захватывающая, поскольку вы исследуете Новый Свет постепенно. Вы начинаете с приобретения снаряжения и найма людей, которые будут помогать вам в путешествии. Как только вы подготовитесь, можно поднять паруса и плыть в Новый Cвет. Это путешествие интересно само по себе — если вы не найдете Новый Свет то умрете от голода! Если вы все-таки нашли Новый Свет, вам следует высадиться, основать миссию и наладить отношения с аборигенами. Здесь мы встречаемся еще с одной особенностью игры: вы можете встечать аборигенов мирно, либо можете обыскивать их деревни в поисках золота. Если вы пришли с миром, весть о вашей честности распространится, и другие деревни будут встречать вас тем же. Если вы убиваете аборигенов, весть о вашем предательстве распространится еще быстрее, и будущие встречи с аборигенами скорее всего закончатся кровопролитем.
В игре существует множество уровней стратегии, и я даже не буду касаться их. Все, что я хочу сказать — игра доставляла массу удовольствия и может служить хорошей моделью для будущих игр. Я рекомендую вам изучить эту игру и запомнить уроки, которые вы сможете извлечь.
Умные компьютеры
Посмотрим правде в глаза: управляемые компьютером противники исключительно тупы. Сегодня не существует стратегии реального времени, которая могла бы бросить настоящий вызов игроку не прибегая к мошенничеству. Чтобы увидеть жульничающий искуственный интеллект в действии, достаточно сыграть в Empire Earth. Игра дает управляемым компьютером противникам больше ресурсов и, я подозреваю, что цикл производства у них тоже короче. Конечно, сначала некоторые игры кажутся трудными, но сколько длится это ощущение? Совсем недолго. В будущем появятся достаточно мощные процессоры, позволяющие создать феноменальные алгоритмы искусственного интеллекта. Проблема, стоящая перед программистами сегодня, заключается в том, что алгоритмы искусственного интеллекта требуют значительных объемов вычислений. После формирования каждую секунду десятков кадров с различными спецэффектами, на долю искуственного интеллекта остается не так много времени процессора. Возможно, когда-нибудь нашим кремниевым врагам будет предоставлено достаточно тактов.
netlib.narod.ru | < Назад | Оглавление | Далее > |
Уровень энергии
Хотя тибериум— это самый важный материал в Command & Conquer, без энергии ваши постройки не смогут функционировать. Индикатор энергии в нижнем правом углу интерфейса отображает объем используемой энергии в сравнении с доступной. Когда игрок строит электростанции цвет полосы становится более зеленым, а сама она растет. Когда строятся другие здания цвет полосы становится более красным. Такой стиль индикации дает игроку визуальное представление энергоснабжения в сравнении с потребностями. Это весьма изобретательно, поскольку чтобы представить текущую ситуацию игроку не надо счтиывать различные числа. Здесь мы пришли к еще одному ключевому пункту: постарайтесь, чтобы ваша игра не выглядела похожей на электронную таблицу. Для этого вам придется использовать графические представления числовых значений.
Utopia от Intellivision
В далекой, далекой местности, в двух милях вниз по грунтовой дороге мимо старой рыжей собаки, слева от большого дуба домашняя игровая система Intellivision населяла несколько гостинных в Соединенных Штатах.
(Отсюда начинается ретроспектива.) Жаркий летний полдень, я и мой брат Эдди участвуем в решающем сражении. Этому моменту предшествовали несколько недель планирования, маневров и пропаганды. Мой боевой флот вторгся в его водное пространство и разрушил рыболовные суда. Его люди голодают. Единственная надежда Эдди — сохранить свои посевы до следующего ливня. Но увы, я я подрядил армию симпатизирующих моим целям мятежников, чтобы они уничтожали поля. Бим, бам, бом! Ход закончен. Черт, теперь придется ждать следующего хода, чтобы установить мое марионеточное правительство на его острове.
Я провел много дней играя в Utopia с любой жертвой, — я имею в виду противника, — которую я мог уговорить сыграть. Utopia была весьма оригинальной игрой, в которой игрок действовал в режиме реального времени, а ходы использовались чтобы сообщать о произошедшем и подсчитать счет. Вы могли играть в однопользовательском режиме, но настоящее удовольствие было в игре с другим человеком. Utopia пердставляла собой смесь SimCity и Command & Conquer. Вы должны увеличивать благосостояние своих людей и в то же время уменьшать благосостояние вашего противника.
Warcraft: Orcs & Humans
Почти в то же время, когда вышла игра C&C, другая компания выпустила не менее захватывающую стратегию реального времени. Этой игрой, конечно, была Warcraft от Blizzard Entertainment. Находясь на другом конце спектра, Warcraft является игрой в стиле фэнтези, проходящей в средневековом окружении, в то время как C&C— это футуристическая военная игра.
В Warcraft вы можете играть либо за орков, либо за людей. Игра разворачивается вокруг битвы за землю Азерот. Доступно несколько сценариев, я полагаю по 12 для каждой стороны. Игра в однопользовательском режиме очень интересна, но гораздо больше удовольствия я получил от выбивания соплей из моих друзей в многопользовательском режиме. Warcraft является еще одной игрой, которая отняла множество часов моего времени. Мы могли играть до рассвета и дальше.
Как и Command & Conquer, Warcraft — хорошо сбалансированная игра. Играете ли вы за орков или за людей, у вас есть равные шансы на победу в игре с грамотным противником. Позднее была выпущена игра Warcraft II: Tides of Darkness, но я не думаю, что она была также хорошо сбалансирована. Может быть это объясняется количеством доступных подразделений. Следует помнить, что больше не всегда значит лучше. Игра с сотнями несбалансированных подразделений гораздо хуже, чем игра с десятком идеально сбалансированных произведений.
Warlords
Давным давно я играл в игру с названием Warlords на моем компьютере Amiga. Несколько друзей собирались у меня дома и приступали к игре. Эти встречи обычно продолжались по 12 часов и более, поскольку ходы делали все по очереди. В конечном итоге один из нас побеждал, и все получали массу удовольствия.
Warlords— это походовая стратегическая игра выпущенная Strategic Studies Group, в которой вы командуете фэнтезийными отрядами и ведете их в бой с противником. Усыпающие местность замки позволяют вам строить армии, необходимые для завоеваний. Уловка игры состоит в том, что замки создают различные армии. Некоторые замки производят драконов, другие — эльфов. Кроме того, вы можете получить героев, что дает значительное преимущество. Герои могут путешествовать по стране и находить специальные боевые артефакты в заброшенных храмах и руинах. Герои очень важны для игры, поскольку их преимущества влияют и на армии, путешествующие с ними.
За прошедшие годы SSG выпустила несколько новых версий Warlords, включая стратегическую игру в реальном времени с названием Warlords: Battlecry, у которой есть даже вторая версия. Привлекательность игры Warlords вызвана мощной системой искусственного интеллекта. Искусственный интеллект в серии игр всегда был превосходен иявляется достойным соперником в однопользовательском режиме игры.
X-COM: UFO Defense
Ни один список классических стратегических игр не может считаться полным бехз упоминания об X-COM. X-COM — это очень интересная игра в которой вы защищаете Землю от вторжения инопланетян. Главная часть игры представляет собой изометрический вид на действия боевой группы. Вы командуете группой бойцов, сражающихся против инопланетян в нескольких миссиях. Интерес игре придает развитие технологии. Вы начинаете игру с простейшим оборудованием, производимым земной промышленностью, а затем модернизируете его, изучая технологии инопланетян. С каждым сражением ваши солдаты становятся лучше и лучше, и в конце концов сравниваются с инопланетянами. Нет ничего более приятного, чем запустить управляемую ракету за угол, вниз по лестнице, а затем направо через дверной проем, чтобы поразить врага.
X-COM объединяет управление ресурсами и тактическую стратегию, предоставляя вам контроль над обороной всего мира. Вы должны перехватывать вражеские истребители, строить оборонные базы и уничтожать врагов на земле. Все это дает полезный опыт.
Единственным недостатком X-COM являются последовавшие продолжения. Первоначальная игра дает массу удовольствия, но продолжения не столь хороши.На этом язавершаю обзор первых стратегических игр. я мог бы продолжать еще и еще, но думаю, что у вас теперь есть достаточно примеров. Зачем я привел этот список игр? Чтобы дать вам идей для ваших собственных стратегических игр. Едва ли можно найти лучший способ начать разрабатывать игру, чем оглянуться назад и посмотреть, что люди создали до вас. История — великий учитель.
netlib.narod.ru | < Назад | Оглавление | Далее > |
Здания
Есть всего несколько типов зданий, которые можно выбрать, но каждый из них имеет важное значение для благосостояния цивилизации. Есть форты, фабрики, поля, школы, больницы, жилые дома и мятежники.
Вы можете спросить— как мятежники могут быть зданием? Для вас это может казаться странным, но в игре мятежные солдаты могут появляться и занимать квадраты земли. Земля, которую они занимают, не может быть использована для других целей, пока мятежники не будут уничтожены. Чтобы предотвратить захват вашей собственности мятежниками используются форты. Мятежные солдаты не могут находиться на смежных с фортом квадратах. Таким образом, если каждое из ваших зданий примыкает к форту, мятежники не смогут атаковать. Кроме того, форты защищают от пиратов. Представление форта показано на рис. 1.2.
Рис. 1.2. Форт в игре Utopia |
Фабрики нужны чтобы приносить доход. Ваш итоговый счет зависит от количества накопленного золота, поэтому фабрики жизненно необходимы для победы. Каждая фабрика за один ход производит небольшое количество золота. Отрицательная черта фабрик — загрязнение окружающей среды, которое увеличивает смертность населения. Кроме того, на фабрики влияет благосостояние вашего населения. С его ростом увеличивается и количество производимого фабриками золота. Сделайте население счастливым — и вы будете богаты! Представление фабрики вы можете увидеть на рис. 1.3.
Рис. 1.3. Фабрика в игре Utopia |
Поля очень просты — они обеспечивают людей пищей. Урожай с одного поля может прокормить 500 человек. Недостаток полей заключается в том, что они существуют ограниченное время. Вы должны создавать их заново каждые несколько ходов. Положительная черта заключается в том, что в случае ливня поля производят золото. Правильным образом размещенные поля могут превратиться в золотые рудники.
Школы увеличивают благосостояние населения. Побочным эффектом увеличения благосостояния является увеличение производительности фабрик. Образованное население — производительное население.
Больницы увеличивают численность населения. Кроме того, они значительно повышают благосостояние. Фактически, больница — одно из лучших зданий для постройки.
Жилые дома необходимы, чтобы вашему населению было где жить. Один квадрат жилых домов предоставляет кров для 500 человек.
Мятежные солдаты не являются зданием, но они могут занимать территорию вашего противника. Вы можете покупать мятежников и они автоматически появятся на собственности вашего оппонента. Следует иметь в виду, что мятежники не могут вторгаться в области, защищенные фортами.
Земля
Блоки, представляющие землю образуют два острова. На каждом квадрате земли может быть построено одно здание или ферма. Здания образуют инфраструктуру вашей колонии, а фермы обеспечивают продовольствием. Конечно, ничто не дается бесплатно, и вы можете строить только те объекты, на которые у вас хватает денег. Размещение объектов является ключевым фактором игры, поэтому к управлению землей следует отнестись тщательно.
Архитектура программ Windows
Если вы до Windows работали с другой операционной системой, такой как UNIX, Linux или DOS, новый стиль программирования может показаться несколько необычным. Сначала придется отказаться от функции main(), которая будет заменена функцией с именем WinMain(). Так же как в других операционных системах требовалось наличие функции main(), программам работающим в Windows необходима функция WinMain().
Добавление исходных файлов в проект
Раскройте меню Project и выберите пункт Project | Add To Project | New. В результате на экран будет выведено диалоговое окно, изображенное на рис.2.7.
Рис. 2.7. Диалоговое окно для выбора типа добавляемого к проекту файла |
Фактически это то же самое диалоговое окно, что и изображенное на рис. 2.3, просто в нем выбрана другая вкладка. Вместо того, чтобы выбирать тип создаваемого проекта, сейчас мы будем выбирать тип добавляемого файла. Больше всего нас должны интересовать типы C/C++ Header File и C++ Source File. Перед тем, как продолжить, выберите тип C++ Source File.
Мы уже на финишной прямой, но еще не закончили. Перед тем, как щелкнуть по кнопке OK, вы должны задать имя создаваемого файла. Введите в качестве имени файла CreateWindow и щелкните по кнопке OK.
ПРИМЕЧАНИЕ
СОВЕТ
Теперь, когда файл создан и добавлен к проекту, вы можете видеть его в окне рабочего пространства. Взгляните на рис. 2.8.
Рис. 2.8. Рабочее пространство с добавленным файлом
Я раскрыл папку Source Files, чтобы показать только что созданный файл CreateWindow.cpp. Когда вы дважды щелкаете по любому файлу, представленному в окне рабочего пространства, он загружается и выводится в области редактирования. На рис. 2.8 загружен файл CreateWindow.cpp. В данный момент он пуст и ждет, что вы добавите в него какой-нибудь текст.
Файлы ресурсов
Если вы умеете программировать, но являетесь новичком в программировании для Windows, вам может быть непонятно назначение папки Resource Files.
В программировании для Windows ресурсами называются элементы, используемые для расширения или добавляемые к приложению. Примерами ресурсов могут служить значки, растровые изображения, звуковые файлы и текстовые строки. Прелесть ресурсов в том, что они компилируются в вашу программу. Вам не надо беспокоиться об их установке на компьютер пользователя, ведь они являются частью исполняемого файла.
СОВЕТ
Я не хочу потеряться среди связанных с ресурсами тем, так что давайте отправимся дальше. В вашем проекте пока нет файлов, поэтому в папках ничего не перечислено. Давайте исправим это, добавив в проект файл с исходным кодом на С++.
Функция CreateWindowEx()
Для создания окна используется функция CreateWindowEx(). Ее можно применять для создания дочерних, всплывающих или перекрывающихся окон. При создании окна вы указываете используемый класс, имя приложения и некоторые другие параметры. Прототип функции выглядит следующим образом:
HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );
Первый параметр имеет тип DWORD и называется dwExStyle. Он похож на определяющий стиль член структуры WNDCLASSEX, но задает дополнительные стили окна. Список доступных дополнительных стилей приведен в таблице2.6.
Таблица 2.6. Дополнительные стили окна | |
Стиль | Описание |
WS_EX_ACCEPTFILES | Окно может получать файлы с использованием механизма перетаскивания. |
WS_EX_APPWINDOW | Заставляет окно перемешаться на верхний уровень панели задач, когда оно видимо. |
WS_EX_CLIENTEDGE | Окно обрамлено рамкой, чтобы клиентская область выглядела углубленной. |
WS_EX_CONTROLPARENT | Позволяет переключаться между дочерними окнами с помощью клавиши TAB. |
WS_EX_DLGMODALFRAME | Окно будет обрамлено двойной рамкой. |
WS_EX_LEFT | Окно выровнено по левой границе. Это значение по умолчанию. |
WS_EX_LEFTSCROLLBAR | Для некоторых языков полосу прокрутки следует располагать слева от текста. В таких случаях и применяется этот стиль. |
WS_EX_LTRREADING | Отображаемый в окне текст читается слева направо. Это значение по умолчанию. |
WS_EX_NOPARENTNOTIFY | Подавляет сообщение WM_PARENTNOTIFY отправляемое дочерним окном родительскому при создании или уничтожении. Применяется для дочерних окон. |
WS_EX_OVERLAPPEDWINDOW | Комбинация флагов WS_EX_CLIENTEDGE и WS_EX_WINDOWEDGE. |
WS_EX_PALETTEWINDOW | Комбинация флагов WS_EX_WINDOWEDGE, WS_EX_TOOLWINDOW и WS_EX_TOPMOST. |
WS_EX_RIGHT | Содержимое окна выравнивается по правой границе. Это необходимо для некоторых языков. |
WS_EX_RIGHTSCROLLBAR | Полоса прокрутки располагается справа от клиентской части окна. Это значение по умолчанию. |
WS_EX_RTLREADING | В некоторых языках текст читают справа налево. Для таких языков использование данного стиля позволит системе отображать символы в окне справа налево. |
WS_EX_STATICEDGE | Создает окно, предназначенное для элементов, которые не будут получать входные данные от пользователя. |
WS_EX_TOOLWINDOW | Создает окно, выглядящее как панель инструментов. |
WS_EX_TOPMOST | Окно будет оставаться самым верхним на рабочем столе, независимо от того, активно оно или нет. |
WS_EX_WINDOWEDGE | У окна будет рамка с рельефной гранью. |
В рассматриваемом примере я указываю в первом параметре дополнительный стиль WS_EX_OVERLAPPEDWINDOW. Благодаря использованию этого стиля, окно программы будет выглядеть подобно большинству приложений Windows.
Второй параметр представляет собой завершающуюся нулевым символом строку, содержащую имя класса для создаваемого окна. Все, что необходимо сделать — указать то же имя, которое вы использовали при инициализации члена lpszClassName структуры WNDCLASSEX. В моем примере я использую строку «Window Class».
Третий параметр — это еще одна завершающаяся нулевым символом строка. Вместо того, чтобы определять имя класса, она задает текст, который будет выведен в заголовке окна. Вы можете назвать свою программу как пожелаете, а я свою назвал «Create Window Example».
Четвертый параметр, dwStyle, позволяет задать различные комбинации стилей для вашего окна. Доступные стили перечислены в таблице 2.7.
Таблица 2.7. Cтили окна | |
Стиль | Описание |
WS_BORDER | Окно обрамлено тонкой рамкой. |
WS_CAPTION | У окна есть заголовок. |
WS_CHILD | Окно является дочерним. Этот стиль нельзя использовать вместе со стилем WS_POPUP. |
WS_CHILDWINDOW | То же самое, что и WS_CHILD. |
WS_CLIPCHILDREN | Предотвращает рисование в тех областях окна, которые заняты дочерними окнами. |
WS_CLIPSIBLINGS | Используется только для дочерних окон. Если дочернее окно, для которого был установлен этот стиль перекрывается другими дочерними окнами, то при обновлении содержимого окна будет перерисовываться только та часть, которая не закрыта другими окнами. |
WS_CLIPSIBLINGS | Окно заблокировано и не может принимать данные от пользователя. |
WS_DLGFRAME | Стиль для диалоговых окон. |
WS_GROUP | Указывает, что данное окно является первым в группе окон. Используется для упорядочивания окон. Первое окно, у которого установлен стиль WS_GROUP начинает группу, после чего все последующие окна будут добавляться в ту же группу, пока снова не встретится окно с установленным стилем WS_GROUP (оно начнет следующую группу). |
WS_HSCROLL | Окно с горизонтальной полосой прокрутки. |
WS_ICONIC | После создания окно отображается в свернутом виде. |
WS_MAXIMIZE | После создания окно отображается развернутым на весь экран. |
WS_MAXIMIZEBOX | В заголовке окна есть кнопка для развертывания на весь экран. |
WS_MINIMIZE | То же самое, что и стиль WS_ICONIC. |
WS_MINIMIZEBOX | В заголовке окна есть кнопка для свертывания окна. |
WS_OVERLAPPED | Перекрывающееся окно с заголовком и рамкой. |
WS_OVERLAPPEDWINDOW | Окно с комбинацией стилей WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX. |
WS_POPUP | Стиль для всплывающих окон. |
WS_POPUPWINDOW | Всплывающее окно с комбинацией стилей WS_BORDER, WS_POPUP и WS_SYSMENU. |
WS_SIZEBOX | Окно может изменять размер. |
WS_SYSMENU | У окна есть системное меню, вызываемое при щелчке правой кнопкой мыши по заголовку. Чтобы этот стиль работал правильно, он должен использоваться совместно со стилем WS_CAPTION. |
WS_TABSTOP | Окно может стать активным при нажатии на клавишу TAB. |
WS_THICKFRAME | То же самое, что и стиль WS_SIZEBOX. |
WS_TILED | То же самое, что и стиль WS_OVERLAPPED. |
WS_VISIBLE | После создания окно будет видимым. |
WS_VSCROLL | Окно с вертикальной полосой прокрутки. |
В программе CreateWindow в этом параметре я указываю стиль WS_OVERLAPPEDWINDOW. Это придает окну вид, похожий на большинство других приложений Windows.
Пятый параметр функции CreateWindowEx() — это целое число, задающее позицию окна на экране по горизонтали. Если вы создаете дочернее окно, координата отсчитывается относительно координаты по горизонтали родительского окна. Поскольку в примере нет дочерних окон, используемое значение координаты, 0, приведет к размещению окна вплотную к левой границе экрана.
Шестой параметро задает позицию окна на экране по вертикали. Подобно позиции по горизонтали, позиция по вертикали представляет координату на экране только в том случае, если окно не является дочерним. Для дочернего окна это значение задает смещение относительно позиции по вертикали родительского окна. Чтобы лучше понять эту концепцию взгляните на рис. 2.11.
Рис. 2.11. Координаты окна определяют его позицию в зависимости от того, является окно дочерним или нет |
Седьмой параметр создающей окно функции задает ширину окна в пикселах. В нашем примере ширина окна равна 320 пикселам.
Восьмой параметр задает высоту окна в пикселах. В примере высота окна равна 200 пикселам.
Девятый параметр функции указывает родителя данного окна. Он полезен, когда создается приложение с несколькими окнами. Поскольку в рассматриваемом примере есть только одно главное окно, данному параметру присваивается значение NULL чтобы указать, что у окна нет родителя.
Десятый параметр используется для задания дескриптора связанного с окном меню. В примере нет никаких меню, поэтому данному параметру присваивается значение NULL.
Одиннадцатый параметр используется для задания дескриптора экземпляра модуля. В этом параметре вы передаете дескриптор экземпляра, полученный функцией WinMain() в одном из ее параметров.
Последний параметр используется для указания дополнительных данных создаваемого окна. Мне очень редко приходилось использовать этот параметр, поэтому в большинстве приложений я присваиваю ему значение NULL.
Ничего себе! Вы проделали почти всю работу, необходимую для отображения окна на экране. На рис. 2.12 показан ход выполнения программы до настоящего момента.
Рис. 2.12. Ход выполнения кода приложения до вызова функции CreateWindowEx() |
Функция обработки сообщений
Вы вероятно задаетесь вопросом, где обрабатываются сообщения. Когда функция DispatchMessage() отправляет сообщения, они направляются функции обработки сообщений, указанной в классе окна. В рассматриваемом примере все сообщения отправляются написанной мной функции fnMessageProcessor().
Взгляните на код функции обработки сообщений, и вы увидите, что она представляет собой одну инструкцию switch средних размеров. Это вызвано тем, что единственная задача данной функции — перебрать все типы сообщений, которые интересуют нас и проверить, соответствует ли полученное сообщение одному из них.
Мой пример проверяет сообщения WM_CREATE, WM_DESTROY и WM_PAINT. Дополнительная работа производится только для сообщения WM_DESTROY.
Когда приложение получает сообщение WM_DESTROY, оно вызывает функцию PostQuitMessage(), которая завершает работу программы Windows. Это происходит, когда вы завершаете работу, щелкнув по кнопке закрытия окна.
Если полученное сообщение не соответствует ни одному из проверяемых программой, оно возвращается функции WinMain() с помощью функции DefWindowProc(). Это стандартная практика, поэтому я не рекомендую пытаться изменить ее.
Рассматриваемый пример проверяет лишь несколько типов сообщений, но примеры, встечающиеся дальше в этой книге проверяют гораздо больше сообщений. Помните об этом, когда будете просматривать код последующих примеров — вы можете столкнуться с абсолютно незнакомыми типами сообщений.
Функция RegisterClassEx()
Сейчас мы достигли момента, когда класс окна должен быть зарегистрирован. Ниже приведен фрагмент кода, выполняющий это действие.
if(RegisterClassEx(&wndclass) == 0) { // Сбой программы, выход exit(1); }
Вызов функции RegisterClassEx() необходим, чтобы потом мы смогли создать окно. Эта функция регистрирует класс в системе Windows. Если класс не зарегистрирован, то вы не сможете использовать его для создания окна.
Функция очень проста. Вот ее прототип:
ATOM RegisterClassEx( CONST WNDCLASSEX *lpwcx );
Первый и единственный необходимый для нее параметр является указателем на структуру данных WNDCLASSEX. Здесь все просто, — в нашем примере достаточно только передать функции значение &wndclass.
Не следует слишком волноваться из-за того, что функция возвращает значение типа ATOM. Важно только проверить равно возвращаемое функцией значение NULL или нет. Если возвращаемое функцией RegisterClassEx() значение не равно нулю, ее выполнение завершилось успешно.
Итак, класс окна зарегистрирован, и программа переходит к действительному созданию окна.
Функция ShowWindow()
Верите или нет, но факт создания окна не приводит к его отображению. (Эти парни из Microsoft иногда такие странные!) Чтобы окно было действительно выведено на экран вы должны вызвать функцию ShowWindow(). К счастью, она достаточно прямолинейна. Вот ее прототип:
BOOL ShowWindow( HWND hWnd, int nCmdShow );
Первый параметр задает дескриптор отображаемого окна. Это действительно просто, поскольку дескриптор уже подготовлен функцией, создавшей окно. Взгляните на пример, чтобы увидеть как я передаю дескриптор, возвращенный функцией CreateWindow().
Второй параметр — это целое число, определяющее как будет отображаться окно. Вот и пришло время для еще одной таблицы. Значения, которые можно использовать в этом параметре перечислены в таблице 2.8.
Таблица 2.8. Значения для функции ShowWindow() | |
Значение | Описание |
SW_HIDE | Окно скрыто и затем окно активируется. |
SW_MAXIMIZE | Окно развернуто на весь экран. |
SW_MINIMIZE | Окно свернуто и затем окно активируется. |
SW_RESTORE | Восстанавливает окно из свернутого или развернутого состояния. Это полезно, если требуется вернуть окну его первоначальные размеры и местоположение. |
SW_SHOW | Активирует окно и отображает его. |
SW_SHOWMAXIMIZED | Окно активируется и отображается развернутым на весь экран. |
SW_SHOWMINIMIZED | Окно активируется и отображается свернутым. |
SW_SHOWNA | Окно отображается в его текущем состоянии. Не оказывает никакого влияния если окно в данный момент активно. |
SW_SHOWNORMAL | Отображает окно в его нормальном состоянии. Это значение используется, когда функция ShowWindow() вызывается в первый раз. |
Хотя есть много значений, которые вы можете указать во втором параметре, простейший способ заключается в передаче значения целочисленной переменной iCmdShow, которая является одним из параметров функции WinMain(). Именно так я и поступаю в коде примера.
Итак, ваша программа полностью завершила свою основную задачу — отображение окна. Однако, она пока не может принимать никаких входных данных. Здесь в игру вступает код цикла обработки сообщений.
Функция WinMain()
Следуя далее мы столкнемся с функцией WinMain(). Как я говорил ранее, она является неотъемлимой частью всех программ Windows, а также является первой функцией, вызываемой во время выполнения программы.
На рис. 2.1 под функцией WinMain() располагается блок условия с текстом «Событие для обработки?». Он изображает цикл обработки сообщений, являющийся стандартной частью большинства приложений для Windows. Обычно после того как вы инициализировали программу Windows, чтобы она отображала окно и необходимые кнопки, начинается ожидание событий. В большинстве случаев в коде программы присутствует тот или иной вид цикла, проверяющего наличие событий в очереди. На рисунке это изображено пунктирной линией, ведущей к очереди событий. Цикл обработки событий проверяет очередь, пока не обнаружит наличие в ней какой-либо информации.
Как только в очереди найдено событие, оно извлекается и передается для дальнейшей обработки обработчику сообщений.
Как создать проект
Лучший способ обучения— практика, так что давайте создадим новый проект, для чего щелкнем по меню File и выберем пункт New. Вам будет предоставлено множество вариантов, изображенных на рис. 2.3.
Рис. 2.3. Диалоговое окно создания нового проекта в Visual C++ |
Диалоговое окно, которое вы видите, используется для указания типа создаваемого проекта. Как видно Visual C++ используется для создания всех типов программ, включая элементы управления ActiveX, COM-объекты, надстройки DevStudio и конечно же приложения Windows. Нас интересует тип приложений, который называется Win32 Application. Этот тип используется при создании программ для Windows. Выделите в списке пункт Win32 Application чтобы выбрать этот тип.
Перед тем, как двинуться дальше, вы должны сообщить Visual C++ имя вашего проекта и где он будет храниться. Эта информация вводится в текстовые поля Project name и Location. Для нашего примера в качестве имени проекта укажите CreateWindow.
СОВЕТ
После того, как вы введете требуемую информацию, щелкните по кнопке OK. Вам будет предложено диалоговое окно, показанное на рис. 2.4.
Рис. 2.4. Диалоговое окно выбора типа Windows-приложения |
Диалоговое окно, представленное на рис. 2.4, предлагает три варианта:
An empty project — пустой проект;
A simple Win32 application — простое приложение Win32;
A typical "Hello World!" application — типичное приложение «Hello World».
Первый вариант, пустой проект, используется наиболее часто. Его выбор приводит к созданию пустого проекта в который не включены никакие объекты. Фактически создается чистый холст для последующей работы.
Второй вариант, простое приложение Win32, используется реже, поскольку все, что он делает — это создает программу, которая ничего не делает. В этом случае для вас создаются исходные файлы, но они делают не слишком много.
Третий вариант, типичное приложение «Hello World!», делает больше, чем два предыдущих. Он создает законченую программу для Windows, которая выводит текст «Hello World!». Я не использую этот вариант слишком часто, поскольку создаваемый код на мой вкус выглядит слишком загроможденным.
Выберите первый переключатель и щелкните по кнопке Finish, чтобы открыть диалоговое окно, изображенное на рис. 2.5.
Рис. 2.5. Диалоговое окно подтверждения выбранных параметров |
Компиляция и выполнение кода
Итак, ваша программа целиком введена и вы готовы идти дальше. Чтобы скомпилировать программу в Visual C++ 6.0 вам необходимо сделать активным окно с компилируемой программой и нажать клавишу F7. Если все закончится успешнно, то в окне с выходными данными компилятора вы увидите следующий текст:
---------Configuration: CreateWindow - Win32 Debug------------ Compiling... CreateWindow.cpp Linking... CreateWindow.exe - 0 error(s), 0 warning(s)
Если во время компиляции будут обнаружены какие-либо ошибки, заново проверьте код и убедитесь, что все набрано правильно. Когда будут исправлены все ошибки, вы сможете запустить программу, нажав комбинацию клавиш Ctrl+F5. В результате начнет выполняться программа активного проекта. На рис. 2.13 показано, как должна выглядеть работающая программа рассматриваемого примера.
Рис. 2.13. Результат выполнения программы CreateWindow
netlib.narod.ru | < Назад | Оглавление | Далее > |
Давайте продолжим. Запустите вашу копию
Давайте продолжим. Запустите вашу копию Visual C++ 6.0. Вы должны увидеть экран, аналогичный представленному на рис. 2.2.
Рис. 2.2. Интерфейс Visual C++ 6.0
В интерфейсе, представленном на рис. 2.2, нет ничего особенного. Основные элементы управления сосредоточены в верхнем меню: File, Edit, View, Insert и т.д. Все, что вы делаете в Visual C++, должно быть связано с проектом. Проект — это верхний уровень иерархии в среде разработки.
Обработчик сообщений
Когда в своей программе вы создаете окно, то должны указать функцию, которая будет обрабатывать сообщения, поступающие от этого окна. Эта функция является действительным обработчиком сообщений. Итак мы пришли к следующему: обработчик сообщений — это обычная функция, получающая сообщения, направляемые вашей программе.
Обычно обработчик сообщений представляет собой одну гигантскую инструкцию switch, проверяющую каждый имеющий отношение к вашей программе тип сообщений, и затем выполняющую необходимые действия. Существует множество типов сообщений, которые никогда не будут докучать вам. Большинство моих игр проверяет только несколько ключевых типов сообщений.
СОВЕТ
netlib.narod.ru | < Назад | Оглавление | Далее > |
Очередь событий
Вторая ключевая область называется очередь событий (event queue). С этого момента проявляются отличия программирования в Windows от программирования в других операционных системах. События, о которых я говорил ранее, помещаются в очередь событий. Почти всегда, когда программа Windows получает входные данные или уведомление от пользователя или операционной системы, происходит генерация события, которое сохраняется в очереди. Программы используют систему очередей, чтобы сообщения никогда не терялись. Кроме того, сообщение может храниться в очереди так долго, как требуется программе.
Если вы знакомы с программированием в операционных системах, отличающихся от Windows, то знаете что если программа занята, а пользователь в это время нажимает клавишу, данные о нажатии могут быть потеряны. Это происходит потому, что программа была занята чем-то другим и пропустила данное событие. В Windows сообщение клавиатуры посылается в очередь событий для хранения. Там оно находится в безопасности, пока не будет затребовано программой. Если вы подумаете об этом, то оцените все преимущества— больше не надо беспокоиться о потерянных данных.
События, помещенные в очередь обрабатываются по принципу «первым пришел — первым ушел (FIFO)». Это означает, что первое помещенное в очередь событие будет первым вытолкнуто из очереди, когда программа запросит следующее событие. В этом нет ничего революционного — всего лишь стандартная очередь.
На рис. 2.1 в очереди событий содержится три сообщения: WM_KEYDOWN, WM_KEYUP и WM_SIZE. Для разработчика они представляют три возможных действия пользователя. Сначала пользователь нажал клавишу и затем отпустил ее. После этого пользователь изменил размер окна.
Пишем первую программу для Windows
На этом я заканчиваю обсуждение теории, лежащей в основе программирования для Windows. Пришло время написать вашу первую программу для Windows. Во всех, рассматриваемых в этой книге примерах, я использую компилятор Microsoft VisualC++ 6.0. Если у вас еще нет этой программы, я настоятельно рекомендую пойти и приобрести ее.
СОВЕТ
Погружаемся и сталкиваемся с кодом
Теперь, когда у вас есть чистый исходный файл, призывающий вас, настало время написать действительный код для Windows. Не волнуйтесь слишком много, я проведу вас через каждый шаг. Взгляните на приведенный ниже листинг. В нем полностью приведена рабочая программа для Windows.
// Стандартный включаемый файл Windows #include <windows.h>
// Прототип функции обратного вызова для обработки сообщений LRESULT CALLBACK fnMessageProcessor (HWND, UINT, WPARAM, LPARAM);
// Функция вызывается автоматически, когда программа запускается int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hWnd; MSG msg; WNDCLASSEX wndclass;
// Настройка класса окна wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = fnMessageProcessor; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "Window Class"; // Имя класса wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Регистрация класса окна if(RegisterClassEx(&wndclass) == 0) { // Сбой программы, выход exit(1); }
// Создание окна hWnd = CreateWindowEx( WS_EX_OVERLAPPEDWINDOW, "Window Class", // Имя класса "Create Window Example", // Текст заголовка WS_OVERLAPPEDWINDOW, 0, 0, 320, 200, NULL, NULL, hInstance, NULL);
// Отображение окна ShowWindow(hWnd, iCmdShow);
// Обработка сообщений, пока программа не будет прервана while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
return (msg.wParam); }
// Функция обратного вызова для обработки сообщений // (НЕОБХОДИМА ВСЕМ ПРОГРАММАМ ДЛЯ WINDOWS) LRESULT CALLBACK fnMessageProcessor (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { switch(iMsg) { // Вызывается, когда впервые создается окно case WM_CREATE: return(0); // Вызывается, когда окно обновляется case WM_PAINT: return(0); // Вызывается, когда пользователь закрывает окно case WM_DESTROY: PostQuitMessage(0); return(0); default: return DefWindowProc(hWnd, iMsg, wParam, lParam); } }
Получение сообщений функцией GetMessage()
Чтобы приложения Windows знали когда их окна закрываются, перемещаются или изменяют размеры, они должны принимать сообщения Windows. В общем случае ваши приложения для Windows должны всегда иметь цикл сообщений. Вспомните сведения о событиях и цикле сообщений, которые я приводил в начале главы. В примере я использую простейший метод проверки наличия сообщений и обработки любых обнаруженных сообщений.
Чтобы проверить наличие ожидающих обработки сообщений вызывается функция GetMessage(). Вот ее прототип:
BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax );
В первом параметре функция ожидает указатель на объект MSG. Структура данных MSG содержит всю информацию о любом обнаруженном сообщении.
Второй параметр указывает для какого окна проводится проверка наличия сообщений. Он необходим потому, что ваша программа может управлять несколькими окнами. В рассматриваемом примере есть только одно окно, поэтому я просто передаю дескриптор окна, созданного функцией CreateWindow().
Третий параметр задает нижнюю границу кодов получаемых сообщений. Мы хотим получать все сообщения, поэтому присваиваем данному параметру значение0.
Четвертый параметр позволяет задать верхнюю границу кодов получаемых сообщений. Поскольку мы хотим получать все сообщения, значение этого параметра также равно 0.
Помещение сообщений в очередь функцией DispatchMessage()
Подобно функции TranslateMessage(), функция DispatchMessage() требует единственный параметр. Цель этой функции— отправить прошедшее трансляцию сообщение в очередь сообщений программы. После вызова этой функции сообщение попадает в функцию обработки сообщений.
Последняя строка кода функции WinMain() возвращает значение wParam последнего сообщения Windows извлеченного функцией получения сообщений. Как говорил поросенок Порки, «Вот и все, ребята!».
Рабочее пространство
Итак, вот ваш проект во всей славе! Теперь, после того как проект создан, настало время подробнее поговорить о нем. Взгляните на рис. 2.6.
Рис. 2.6. Пустое пространство проекта для вашей новой программы
Взгляните на рабочее пространство — область в левой части, содержащую имя вашего проекта со знаком «+» рядом с ним. Перейдите на вкладку FileView и разверните дерево, щелкнув по знаку «+». Вы увидите три подпапки в дереве файлов проекта:
Source Files — исходные файлы;
Header Files — заголовочные файлы;
Resource Files — файлы ресурсов.
Надеюсь, вы знаете как программировать на С или С++ и для чего предназначены исходные и заголовочные файлы. Если нет, я рекомендую вам приобрести книгу по программированию на С++ и изучить ее перед тем, как продолжать чтение. Без этих знаний вам придется очень трудно.
ПРИМЕЧАНИЕ
Работа, управляемая событиями
Следующее отличие программирования в Windows заключается в том, что программы для Windows управляются событиями. Это значит, что программа, вместо того, чтобы бежать за информацией, может бездельничать и ждать сообщений, поступающих ей через очередь сообщений. Все сообщения получает и обрабатывает обработчик сообщений (message handler). Элементы, обрабатываемые в обработчике сообщений обычно называются событиями (events).
Вы можете задаться вопросом, какие
Вы можете задаться вопросом, какие виды событий обрабатываются. Существуют сотни, если не тысячи возможных событий, и некоторые из них описаны в таблице2.1.
Таблица 2.1. События Windows | |
Событие | Описание |
WM_KEYDOWN | Это событие генерируется каждый раз, когда нажимается клавиша на клавиатуре. Ввод с клавиатуры жизненно необходим для большинства игр, поэтому данное сообщение очень важно. |
WM_KEYUP | Это событие генерируется когда нажатая клавиша будет отпущена. Вам необходимо знать не только когда клавишу нажали, но и когда ее отпустили. Поэтому данное событие также важно. |
WM_LBUTTONDOWN | Это событие генерируется когда пользователь нажимает левую кнопку мыши, если указатель мыши находится в пределах окна. |
WM_LBUTTONUP | Это событие генерируется когда пользователь отпускает левую кнопку мыши, если указатель мыши находится в пределах окна. |
WM_SETFOCUS | Это событие генерируется когда приложение получает фокус клавиатуры. Например, событие WM_SETFOCUS генерируется, когда вы щелкаете мышью по неактивному окну. |
WM_SIZE | Это событие сообщает окну, что его размеры были изменены. Оно важно в ситуациях, когда для подгонки интерфейса к новому размеру окна требуется изменение расположения элементов. |
СОВЕТ
Структура данных WNDCLASSEX
Если в прошлом вы уже занимались программированием для Windows, то могли использовать структуру WNDCLASS. Между ней и объектом WNDCLASSEX есть только пара отличий. Во-первых добавлен апраметр, задающий размер структуры в байтах. Во-вторых, добавлен параметр задающий дескриптор маленького значка для окна.
Ниже приведен прототип структуры WNDCLASSEX:
typedef struct _WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX;
Нравится ли вам встеча со сложной структурой, назначение членов которой надо запомнить? Нет! Хорошо хоть Microsoft позаботилась, чтобы имена этих переменных было просто читать. Первая переменная типа UINT называется cbSize. Она используется для указания размера структуры данных. Обычно для инициализации этого члена структуры используется выражение sizeof(WNDCLASSEX). Инициализацию этого члена данных вы можете увидеть взглянув на приведенный выше листинг.
ПРИМЕЧАНИЕ
Второй элемент структуры также имеет тип UINT и называется style. Как указывает имя, член style используется для задания стиля создаваемого окна. Удобство данного члена данных в том, что вы можете указывать комбинации одних стилей с другими, используя несколько флагов, объединеных поразрядной операцией ИЛИ (|). Доступные стили перечислены в таблице 2.2.
Таблица 2.2. Стили окон | |
Значение | Действие |
CS_BYTEALIGNCLIENT | Выравнивает клиентскую область окна (область с содержимым) по границе байта в направлении оси X. Оказывает влияние на позицию окна по горизонтали и на его ширину. Лично я никогда не использовал этот стиль. |
CS_BYTEALIGNWINDOW | Выравнивает окно по границе байта в направлении оси X. Оказывает влияние на границу окна по горизонтали и на его ширину. Этот стиль я также не использую. |
CS_CLASSDC | Размещает в структуре класса единый контекст устройства для использования всеми окнами. Я пока не рассказывал о контекстах устройств, но сделаю это позже, так что нет причин для волнения. В Windows можно создать несколько классов окон одного и того же типа в разных потоках. Так как у вас может быть несколько копий класса, несколько потоков могут попытаться использовать контекст устройства одновременно. Это вызовет блокировку всех потоков, кроме одного, пока этот поток не завершит работу с контекстом. |
CS_DBLCLKS | Очень простой стиль. Когда он указан, Windows будет посылать вашему окну сообщение о двойном щелчке каждый раз, когда пользователь выполняет двойной щелчок кнопкой мыши в пределах области окна. Это может показаться глупым, но многие приложения запоминают время каждого щелчка кнопки мыши, чтобы определить был ли выполнен двойной щелчок. Я, чтобы добиться того же, просто использую данный стиль. |
CS_GLOBALCLASS | Этот стиль разрешает создание глобального класса окна. Чтобы получить дополнительную информацию, обратитесь к документации, поставляемой с Microsoft Visual C++. При разработке игр нет необходимости использовать данный стиль. |
CS_HREDRAW | Этот стиль заставляет перерисовывать все окно в случае изменения его ширины. |
CS_NOCLOSE | Запрещает выполнение закрытия окна через системное меню. |
CS_OWNDC | Выделяет уникальный контекст устройства для каждого окна, созданного с использованием класса. |
CS_PARENTDC | Устанавливается, когда дочернее окно использует ту же самую область отсечения, что и родительское. Это позволяет дочернему окну рисовать на родительском. Это не означает, что потомок использует контекст устройства родителя. В действительности потомок получает свой собственный контекст устройства из системного пула. Единственное, что делает этот флаг — увеличение производительности приложения. |
CS_SAVEBITS | Сохраняет в памяти растровое изображение находящейся под окном области экрана. В дальнейшем при перемещении окна скрытая область копируется на экран. Это предотвращает отправку сообщения WM_PAINT всем окнам, которые были скрыты данным. |
CS_VREDRAW | Заставляет перерисовывать все содержимое окна в случае изменения высоты окна. |
Третий член структуры имеет тип WNDPROC, является указателем на функцию и называется lpfnWndProc. Помещаемый сюда указатель должен указывать на функцию обработки сообщений Windows, которую окно использует чтобы принимать сообщения. Это очень важно, и функция, на которую ссылаются здесь должна полностью соответствовать прототипу, приведенному в моем коде. Взгляните на рис. 2.9, чтобы увидеть взаимоотношения между классом и обработчиком сообщений.
Рис. 2.9. Взаимоотношения между классом окна и обработчиком сообщений
Четвертый член структуры относится к типу int и называется cbClsExtra. Это целое число задает количество байт, которые будут выделены сразу за структурой данных класса окна. Я не имею ни малейшего представления, для чего это может быть нужно, поскольку во всех примерах, которые я видел, это значение устанавливалось равным 0. Я рекомендую вам поступать точно так же. Если желаете, можете просто игнорировать этот член данных, поскольку система сама по умолчанию присваивает ему нулевое значение.
Пятый элемент также относится к типу int и называется cbWndExtra. Это целое число задает количество байтов, которые будут выделены сразу за экземпляром окна. Работа с ним аналогична обращению с предыдущим членом структуры, и система также по умолчанию присваивает ему нулевое значение.
Шестой член структуры имеет тип HANDLE и называется hInstance. Дескриптор, который вы задаете здесь, является дескриптором экземпляра к которому относится окнонная процедура класса. В большинстве случаев можно задать значение дескриптора hInstance, получаемого функцией WinMain() в одном из параметров.
Седьмой параметр называется hIcon и имеет тип HICON. Тип HICON это ни что иное, как замаскированный тип HANDLE, поэтому он не должен смущать вас. Данный дескриптор указывает на класс значка, используемого окном. Класс значка в действительности является ресурсом значка. Не присваивайте этой переменной значение NULL, поскольку если сделать так, то программа будет перерисовывать изображение значка, при каждом свертывании окна.
Если вы еще раз взглянете на код, то увидите, что для инициализации седьмого параметра я использую вызов функции LoadIcon().
Функция LoadIcon() загружает ресурс значка из исполняемой программы. Хотя ресурсы компилируются внутрь вашей программы для Windows, вам все равно необходимо загружать их, поэтому и требуется вызов данной функции. Вот как выглядит ее прототип:
HICON LoadIcon( HINSTANCE hInstance, LPCTSTR lpIconName );
К счастью, у этой функции всего два прарметра — HINSTANCE и LPCTSTR. Первый параметр с именем hInstance, содержит дескриптор экземпляра модуля чей исполняемый файл содержит значок, который вы желаете использовать. В моих примерах программ я присваиваю этому параметру значение NULL. Делая так вы разрешите использование встроенных значов Windows, которые часто также называют стандартными значками.
Второй параметр является указателем на строку, содержащую имя загружаемого значка. Взглянув на разбираемый пример, вы увидите, что для инициализации данного параметра я использую константу IDI_APPLICATION. Ее значение соответствует значку, используемому по умолчанию для приложений, который вы могли видеть во многих программах для Windows. Остальные стандартные значения перечислены в таблице 2.3.
Таблица 2.3. Константы для стандартных значков | |
Значение | Описание |
IDI_APPLICATION | Значок, используемый по умолчанию для приложений. Он применяется и в рассматриваемом примере. В большинстве случаев вы можете использовать это значение, если только не хотите, чтобы у вашего приложения был нестандартный значок. |
IDI_ASTERISK | Данное значение создает для вашей программы значок в виде небольшого овала с буквой «i» внутри. |
IDI_ERROR | Красный круг с крестом внутри. |
IDI_EXCLAMATION | Желтый треугольник с восклицательным знаком внутри. |
IDI_HAND | Я понятия не имею, для чего нужны эти дубли, но этот значок выглядит точно так же, как IDI_ERROR. |
IDI_INFORMATION | Еще один дубль. Это значение задает небольшой овальный значок, аналогичный задаваемому значением IDI_ASTERISK. |
IDI_QUESTION | Использование этого значения даст вашему приложению значок с вопросительным знаком. |
IDI_WARNING | О, опять дублирование. На этот раз значок выглядит так же как для значения IDI_EXCLAMATION. |
IDI_WINLOGO | Если использовать эту константу, значок вашего приложения будет выглядеть как небольшой аккуратный логотип Windows. |
Вот и все, что можно сказать о функции LoadIcon(). Теперь вернемся к разбираемой программе. Ох, подождите, прежде чем продолжить, взгляните на рис. 2.10, где изображены некоторые стандартные значки.
Рис. 2.10. Некоторые стандартные значки Windows. ©2002 Microsoft, All Rights Reserved.
Восьмой член структуры данных WNDCLASSEX очень похож на седьмой, за исключением того, что он задает используемый окном курсор. Его тип HCURSOR, а имя — hCursor. Тип HCURSOR это еще один замаскированный дескриптор. Обычно здесь указывается значение дескриптора класса курсора, который будет использован в вашей программе для ее собственного курсора. Но вместо того, чтобы создавать нестандартный курсор, я воспользуюсь функцией LoadCursor().
Функция LoadCursor() похожа на функцию LoadIcon() за исключением того, что она загружает ресурсы курсора, а не ресурсы значка. Вот ее прототип:
HCURSOR LoadCursor( HINSTANCE hInstance, LPCTSTR lpCursorName );
Первый параметр называется hInstance, и содержит дескриптор экземпляра модуля, чей исполняемый файл содержит курсор, который вы собираетесь использовать. В своем примере я присваиваю данному параметру значение NULL. Это позволяет использовать встроенные курсоры Windows. Их также часто называют стандартными курсорами. Не чувствуете ли вы, что уже читали что-то похожее?
Второй параметр — это указатель на строку, содержащую имя загружаемого курсора. Как можно видеть, в рассматриваемом примере я присваиваю этому параметру значение IDC_ARROW. Оно соответствует стандартному курсору Windows, который вы могли видеть во многих программах. Оставшиеся стандартные значения перечислены в таблице 2.4.
Таблица 2.4. Константы для стандартных курсоров | |
Значение | Описание |
IDC_APPSTRING | Это курсор в форме стандартной стрелки с присоединенными к ней песочными часами. Обычно, данный курсор устанавливается, когда ваша программа занята. |
IDC_ARROW | Стандартный курсор Windows. |
IDC_CROSS | Создает курсов, выглядящий как перекрестье прицела. |
IDC_HELP | Этот курсор выглядит как стандартная стрелка с присоединенным к ней вопросительным знаком. Его хорошо использовать, когда пользователю предоставляется возможность задать вопрос. |
IDC_IBEAM | Курсор в форме буквы «I». Обычно используется в режиме ввода и редактирования текста. |
IDC_NO | Курсор в виде перечеркнутого круга. Его можно использовать, когда пользователь наводит курсор на область, которая не реагирует на щелчки кнопок мыши. |
IDC_SIZEALL | Курсор с перекрещенными стрелками. Применяется, когда пользователь изменяет размер окна или графического элемента. |
IDC_SIZENESW | Еще один курсор для изменения размера. В отличие от предыдущего курсора, у которого стрелки направлены во все четыре стороны, здесь стрелки направлены только на северо-восток и юго-запад. |
IDC_SIZENS | То же, что и предыдущий курсор, но стрелки направлены на север и на юг. |
IDC_SIZENWSE | То же, что и предыдущие два курсора, но стрелки направлены на северо-запад и юго-восток. |
IDC_SIZEWE | Еще один курсор со стрелками. В данном случае они направлены на запад и на восток. |
IDC_UPARROW | Курсор в виде стрелки, направленной вверх. |
IDC_WAIT | Курсор в виде песочных часов. Я рекомендую использовать этот курсор в тех случаях, когда ваша программа занята и пользователь может только ждать пока она не закончит работу. |
После того, как вы освоились с относящейся к курсорам частью структуры данных окна, пришло время поговорить о цвете фона. Чтобы задать цвет фона вашего окна, вы просто указываете желаемый цвет в члене данных hbrBackground. Это девятый элемент структуры, и он имеет тип HBRUSH. Возможно, вы подумали, что тип HBRUSH это всего лишь дескриптор класса кисти. Но этот член данных более гибкий и позволяет задать как дескриптор кисти, которая будет использоваться, так и значение используемого цвета. Существует одно требование — задавая цвет, вы должны использовать одно из следующих значений:
COLOR_ACTIVECAPTION
COLOR_APPWORKSPACE
COLOR_BACKGROUND
COLOR_BTNFACE
COLOR_BTNSHADOW
COLOR_BTNTEXT
COLOR_CAPTIONTEXT
COLOR_GRAYTEXT
COLOR_HIGHLIGHT
COLOR_HIGHLIGHTTEXT
COLOR_INACTIVEBORDER
COLOR_INACTIVECAPTION
COLOR_MENU
COLOR_MENUTEXT
COLOR_SCROLLBAR
COLOR_WINDOW
COLOR_WINDOWFRAME
COLOR_WINDOWTEXT
В рассматриваемом примере я не использую этот метод. Но если вы захотите установить для фона один из стандартных цветов, просто замените задающую фоновый цвет строку кода примера на ту, которая приведена ниже:
wndclass.hbrBackground = (HBRUSH)COLOR_GRAYTEXT;
В результате выполнения этого кода фон окна будет окрашен в серый цвет. Вы можете заменить COLOR_GRAYTEXT на любое из перечисленных выше значений. В моем коде вместо этого используется другой метод, включающий вызов функции GetStockObject().
Функция GetStockObject() часто используется для того, чтобы получить дескриптор одной из встроенных кистей, шрифтов, палитр или перьев. Дело в том, что в Windows есть несколько предопределенных типов, которые могут быть использованы в ваших приложениях. Давайте взглянем на прототип функции:
HGDIOBJ GetStockObject( int fnObject );
Если вам нравятся функции у которых только один параметр, то вот одна из них. Ее единственный параметр представляет собой целое число идентифицирующее предопределенный объект системы, который вы хотите использовать. Если вы укажете значение, соответствующее существующему объекту, то возвращенное функцией значение не будет равно нулю.
Моей программе посчастливилось использовать встроенный объект WHITE_BRUSH. Он окрашивает фон окна в белый цвет. В таблице 2.5 приведены еще несколько значений, которые можно использовать в качестве аргумента функции GetStockObject().
Таблица 2.5. Предопределенные объекты Windows | |
Значение | Описание |
BLACK_BRUSH | Соответствует названию. Объект предоставляет кисть, рисующую черным цветом. |
DKGRAY_BRUSH | Темно-серая кисть. |
GRAY_BRUSH | Серая кисть. |
HOLLOW_BRUSH | Возможно вы смотрели фильм «Человек-невидимка»? (Если не смотрели, не волнуйтесь — все равно фильм плохой.) Эта кисть невидима для пользователя, так же как и человек-невидимка. Это означает, что при использовании данной кисти не появляется никаких цветов. Аналогичным образом действует кисть NULL_BRUSH. |
LTGRAY_BRUSH | Светло-серая кисть. |
NULL_BRUSH | То же самое, что и HOLLOW_BRUSH. |
WHITE_BRUSH | Белая кисть. Именно она используется в моем примере. |
BLACK_PEN | Черное перо. Перья не влияют на цвет фона. Вы должны использовать их для задания цвета текста. |
WHITE_PEN | Белое перо. |
ANSI_FIXED_FONT | Объект устанавливает моноширинный системный шрифт. |
ANSI_VAR_FONT | Объект устанавливает системный шрифт с переменной шириной символов. |
DEFAULT_GUI_FONT | Устанавливается заданный по умолчанию системный шрифт. |
DEFAULT_PALETTE | Объект установленной по умолчанию палитры. |
Одиннадцатый член структуры данных также является строкой, которая должна завершаться нулевым символом. Его имя — lpszClassName. Как сказано в имени, эта строка используется для задания имени класса окна. Имя класса является уникальным идентификатором типа класса. Поэтому очень важно, чтобы вы не использовали заданное здесь имя для других классов вашей программы.
Двенадцатый, и последний, член структуры WNDCLASSEX — это переменная с именем hIconSm. Она аналогична члену данных hIcon, за исключением того, что здесь задается используемый программой маленький значок. В моем примере для этого применяется уже знакомая вам функция LoadIcon().
Ну что? Мы завершили изучение структуры данных WNDCLASSEX! Теперь пришло время зарегистрировать ее.
Структура программы
Если назначение обработчика событий все еще кажется вам немного странным, не волнуйтесь. Я пока не показал как он вписывается в общую картину. Рис. 2.1 поможет прояснить некоторые вещи.
Рис. 2.1. Структура обработки сообщений Windows
На рис. 2.1 есть несколько областей, представляющих для нас интерес. Прежде всего обратите внимание на изображение окна программы. Под ним нарисована клавиатура, представляющая входные данные, которые поступают от пользователя.
Трансляция сообщений функцией TranslateMessage()
Перед тем, как отправить сообщение в вашу очередь сообщений, вы должны транслировать его в символьные данные. Это делает функция TranslateMessage(). Для нее требуется единственный параметр — указатель на транслируемое сообщение. В примере я передаю функции адрес переменной, содержащей сообщение.
После того, как сообщение транслировано в символьные данные, вы можете поместить его в очередь сообщений с помощью функции DispatchMessage().
Включение файлов и прототипы функций
В нескольких первых строках кода указываются включаемые заголовочные файлы и приводятся прототипы функций. В программировании для Windows необходим только один заголовочный файл с именем windows.h.
Для нашего примера требуется единственный прототип функции для обработчика сообщений. Любая программа для Windows должна содержать функцию, которая будет вызываться для обработки сообщений и должна соответствовать следующему прототипу:
LRESULT CALLBACK fnMessageProcessor (HWND, UINT, WPARAM, LPARAM);
Имя функции может отличаться, но параметры должны оставаться неприкосновенными. Это вызвано тем, что Windows автоматически вызываеет данную функцию и не сможет работать правильно, если вы измените параметры.
Внутреннее устройство функции WinMain()
Первый элемент, упоминаемый в функции WinMain() - это объект, используемый для создания окна. Объект является структурой типа WNDCLASSEX и очень важен для создания окна вашей программы.