Игровая консоль
Думаю, что некоторые, прочитав заголовок, могли подумать, что речь пойдет об игровых приставках, но это на самом деле не так. Сегодня я хочу рассказать о простом и удобном отладочном инструменте.
Уверен, большинство знают, что такое игровая консоль, так как не раз, наверное, использовали читы в разных играх, активировали какие-то скрытые настройки или возможности игры. Но для тех, кто не в курсе, я немного расскажу о том, что это такое и зачем это нужно в ваших играх.
Игровая консоль — это некое скрытое окно, в которое игры выводят сообщения об ошибках или предупреждения, так же в это окно пользователь может вводить какие-то команды, предусмотренные разработчиком и получать какой-то результат. Консоль есть практически в каждой уважающей себя игре и открывается она по умолчанию на клавишу "~" (тильда). Если вдруг в какой-то игре нет консоли, то это совсем не значит, что консоль отсуствует вообще. Разработчики могут сделать хитрые сочетания клавиш для ее открытия, либо вообще заблокировать её в релизной версии игры, чтобы игроки не могли заглянуть во внутренности игры :)
Зачем нужна консоль?
Любой Flash разработчик знает замечательный метод trace() который позволяет выводить пользовательские сообщения в отладочное окно output. Но метод trace работает только в отладочном Flash плеере, что накладывает некоторые ограничения на отладку и тестирование. Наличие встроенной консоли решают эту проблему и при разумном использование позволяет разработчику всегда знать о том, что происходит в игре не зависимо от того, работает ли он с отладочной версией программы или с релизом.
Но самая важная возможность консоли это даже не вывод каких-то сообщений, а возможность создавать команды, к которым можно привязывать выполнение определенных методов.
Ранее в уроках я немного писал про этапное программирование — это когда задача разбивается на несколько небольших задач и они поочередно выполняются. Например, когда я писал уроки про поиск пути, то прежде, чем использовать поиск пути юнитами в игре, мне нужно было написать и отладить класс (движок) поиска пути. Конечно, в случае с уроками вся отладка была простой, задаем объекту текущие координаты, потом задаем финальную точку прямо в коде, компилируем приложение и смотрим на результат поиска пути — зачем тут что-то мудрить с консолью, когда и так все можно протестировать, чуть-чуть изменив код!? Но когда игра становится все больше, когда компиляция занимает больше времени (если вы компилируете под Flash IDE), и когда до тестируемой фичи надо пробраться через, как минимум половину зловещего уровня, или вовсе протестировать какое-то необычное действие, а может и даже часть действия, которое еще не работает полноценно — то вот тут-то как раз на помощь и приходит консоль! :)
Для чего использовать?
Когда я заканчивал работу над Mining Truck 2 я в какой-то момент осознал, что мне очень не хватает возможности вводить команды, чтобы быстро что-то протестировать. Например, вызвать окно выдачи трофея или вызывать какие-то действия, чтобы настроить эффекты. И вот я гонял на машинке, добывал трофеи и выполнял еще кучу других безумных действий, чтобы только настроить какие-то эффекты. Но потом, когда меня уже начало тошнить от постоянного процесса прохождения игры и долгих компиляций, я догадался делать сочетания горячих клавиш для тестовых методов — это немного облегчило мои страдания. Но когда нужно было проверить разные вариации и выполнить тестовые методы с разными параметрами, то количество хоткеев быстро начало расти и тут я в полной мере осознал всю необходимость игровой консоли, и решил, что в следующем проекте обязательно сделаю.
На деле игровая консоль в Зомботроне меня выручала и спасала не раз. Например, когда я разрабатывал систему триггеров, первым делом все это работало через консоль, введя команду objects можно увидеть все объекты доступные для активации, и если вызвать команду action [obj name] — то указанный объект выполняет свои действия, таким образом я в первую очередь проверял работу отдельных объектов, прежде чем включать их в общую игровую цепочку. Так же в консоль выводятся всякие замечания в работе логики игры и в первую очередь это касается триггерной системы. Вот например, если нажать кнопку открытия двери и дверь не откроется, то открыв консоль, можно будет увидеть сообщение о том, какой объект вызвал другой объект, и почему другой объект не сработал — в таких случах причиной сбоя обычно становилось неправильное имя объекта, и тут легко можно было понять, где ошибка.
Без читов тоже не обошлось, правда режим бога отсутствует, но вот оружие и деньги можно заполучить используя консоль — ну не зарабатывать же мне каждый раз кучу денег, чтобы протестировать покупку какой-нибудь новой пушки!? Да и пушки первым делом в игру добавлялись как раз через консоль, и только потом уже появлялись в магазине :) То есть я пишу код для новой пушки, добавляю эффекты, настраиваю пули, компилирую игру, ввожу команду и мне, как мана небесная, на голову падает новый ствол, я его хватаю и тут же тестирую на мясе — безумно удобно, и самое главное никакого лишнего кода кроме одного тестового метода, сбрасывающего герою вещь: debugItemDrop(itemKind:uint) :)
Я тоже хочу, где взять!?
Ранее, когда я еще писал казуальные игры, я использовал в своих играх жалкое самописное подобие консоли для загрузки уровней из файлов или для быстрого перехода между уровнями, но это было давно и не правда. Поэтому сразу мне не приходило в голову о том, какая должна быть консоль и какими плюшками должна обладать, поэтому первым делом я решил погуглить и посмотреть наличие аналогов и при возможности вдохновиться ими. Если честно я особо ни на что и не рассчитывал, так как консоли во флеше не являются большой необходимостью, ведь есть метод trace()! Но к моему удивлению, я все же нашел фактически один единственный вариант, который меня устраивал как по функционалу, так и по оформлению, вот тут...
Правда, поковыряв исходники, я обнаружил, что с консолью тянутся еще какие-то необходимые ей классы. Как выяснилось позже для меня — это то самое интересное глобальное хранилище, о котором я рассказывал ранее здесь. Более того, эта консоль устроена таким образом, что позволяет менять содержимое глобального хранилища: удалять существующие записи, создавать новые и редактировать уже имеющиеся простым вводом новых данных. Но после дискуссии в комментариях к той записи о глобальном хранилище, я решил от этого самого хранилища отказаться в таком виде, в каком оно было предложено. Таким образом, взяв оригинальный класс консоли, я произвел небольшие модификации, выкинув из него глобальное хранилище и добавив пару новых важных для меня возможностей:
- Задавать описание/справку регистрируемой команды, чтобы при необходимости можно было увидеть очередность передаваемых параметров и их типы, а так же подсмотреть, что делает команда - очень удобно если у вас дырявая память и большой проект.
- Вывод всех зарегистрированных команд в консоли и подсказок для них;
- Возможность вывода разноцветных строк для повышения читабельности;
- Вынес настройки шрифта в константы для быстрой настройки внешнего вида;
- Сделал класс консоли синглтоном;
- И еще какие-то мелочи которые с наскоку не объяснить.
Как встроить консоль в свою игру?
Исходник оригинального класса консоли вместе с примером его использования вы можете скачать здесь. А я расскажу только об использовании своей модификации консоли.
Первым делом добавляем классы консоли Console.as и Repository.as в свой проект. Инициализируем консоль в главном классе приложения, например в классе App.as или Game.as:
import ru.antkarlov.utils.Console; public static var console:Console = Console.getInstance(); public static var trace:Function = console.trace;
Обратите внимание, что переменная, в которой находится указатель на консоль, является статической — это нужно для того, чтобы можно было обращаться к консоли, не имея указателя на созданный экземпляр класса App или Game. Так же, помимо указателя на саму консоль, мы записываем в переменную trace указатель на метод trace() что бы проще было к нему обратиться или в будущем без проблем подменить его на любой другой для вывода сообщений.
Когда консоль создана, мы можем выводить в нее любые сообщения из любого места программы таким образом:
App.trace("Hello console!");
Или с указанием цвета:
App.trace("It's a error!", Console.ERROR);
Так же в метод trace можно передавать не только стринговые значия, но и любые другие типы данных — в этом случае для них будет вызываться стандартный метод toString(). Например:
App.trace(_game);
Выведет тип класса:
[object Game]
А если в классе Game.as перекрыть стандартный метод toString(), например так:
public function toString():String
{
return "[Level: " + _currentLevel.toString() + ", Health: " + _heroHealth.toString()"]";
}
То можно выводить в консоль какие-то интересные или важные параметры класса Game или любого другого класса.
Но самая интересная возможность — это, конечно, регистрировать в консоли свои команды. Сделать это достаточно просто:
App.console.register(executeMethod, "exe");
private function executeMethod():void
{
// Здесь может быть какой-нибудь код, который якобы выполняет ваша команда.
trace("Method has been executed!", Console.RESULT);
}
Теперь если ввести в консоли команду "exe", то будет выполнен метод commandMethod(). Кроме простых команд, можно регистрировать и более сложные, которые могут передавать простые значения:
App.console.register(division, "div", "[a,b] - divides the value A to value B.");
private function division(arr:Array):void
{
// Параметры введены не верно
if (arr.length <= 1)
{
trace("Parameters are entered incorrectly.", Console.ERROR);
return;
}
// Проверка возможной ошибки деления на ноль
if (arr[0] == 0 || arr[1] == 0)
{
trace("Divide by zero error.", Console.ERROR);
}
else
{
trace("Result: " + String(arr[0] / arr[1]), Console.RESULT);
}
}
С точки зрения кода выглядит все просто и удобно, а вот так вот это работает в живом примере:
Введите команду -help, чтобы получить базовую справку. Команда -regs выведет все пользовательские команды с их описанием.
Правда здорово и удобно!? :) Предлагаю вам скачать пример консоли вместе с исходниками и изучить его более подробно:
Ссылка на исходники — CS4, *.zip, 52кб.
Думаю, врядли автор оригинальной консоли прочтет эти строки, но в любом случае я хочу выразить ему большую благодарность за оригинальную консоль, которая оказалась прекрасным инструментом в разработке, а так же за то, что спас меня от изобретения очередного велосипеда :)
Надеюсь, что и в ваших проектах консоль сбережет вам пару пучков нервных клеток! Счастливой вам отладки!
Написать комментарий
Индикаторы: Уроки, Разработка, Action Script 3
Постоянная ссылка
Следующая статья "Линукс на флеше с ноля".
П.С. постоянно проваливаю твой "Я не бот" тест
Отличная статья! Спасибо. =)
Отличная статья!
Вроде в monsterdebuger тоже самое можно делать? По крайней мере судя по тестовой игре :)
@sovik, как раз пару недель назад посмотрел и разобрался с MonsterDebugger — это конечно навороченный инструмент и возможностей у него по больше чем у простой консоли, мне понравилось :) Но MonsterDebugger это все же внешний инструмент для отладки и в релизную версию игры его уж точно нельзя пропускать, а консоль можно.
Ant.Karlov, у меня в одной социалке внедрен Monster, когда забываю его вычистить из кода. Пробовали консоль воткнуть, но ужасно неудобно с ней было. Просто такая плохая реализация у нас была.
Я пробовал юзать консоль (не эту), но нашел ее неудобной, по крайней мере для вывода, тк она затеняет кусок игрового поля. Потому для ввода комманд я юзаю менеджер читов, просто набирая с клавы комманды, а для вывода юзаю Arthropod.
@HamsterWarrior Arthropod - отличная вещь. Сам пользуюсь
Попробуйте http://code.google.com/p/flash-console/ мне очень нравится
Спасибо за статью и класс.
Только у меня возникли проблемы с запуском консоли? Прикрутил к коду, но консоль не появляется. У кого были такие проблемы поделитесь, что это может быть.
Кто подскажет как заставить FlashIDE в режиме дебага принимать события клавиш запущенной флешки, а не воспринимать их как горячие клавиши среды.
@Алексей, в первую очередь нужно убедится, что консоль инициализирована, то есть выполнен метод initialize(). По умолчанию консоль закрыта, чтобы её открыть нужно нажать "~". Либо использовать методы open() и close() для открытия и закрытия консоли.
@Алексей, чтобы клавиши обрабатывались корректно в режиме дебага во Flash IDE, следует передать фокус полю ввода консоли — для этого достаточно кликнуть в поле ввода мышкой.
Антон, спасибо большое. Не полез в класс Consol сразу и думал что вся инициализация там идет в конструкторе класса и достаточно создать экземпляр класса
public static var console:Console
Оказалось, что нужно еще отдельно вызвать метод инициализации и передать переменную stage
console.initialize(stage)
Разобрался, все работает, спасибо еще раз.
@Ant.Karlov - спасибо, очень полезно, будем использовать!
@icewind - спасибо за ссылку, очень полезная вещь.. но пока, на первый взгляд немного перегруженная.
Как тут уже выше советовали - http://code.google.com/p/flash-console/ - лучшее что я видел, и по сей день использую в повседневной разработке.
@focus, @icewind, это мега навороченная консоль :) Причем мне кажется, что больше половины фич которыми она обладает я, например, никогда не буду использовать в своих проектах. Задачи любой консоли — это уметь выводить сообщения (текст) и выполнять пользовательские команды, а все остальное это уже на ваше усмотрение ;)
C нетерпением ждем продолжения уроков по товердефенсу :)
http://code.google.com/p/flash-console/
от нее как раз отказались в пользу монстра, дико неудобная штука на мой взгляд. Оттуда вроде бы взял метод вывода любого объекта в виде строки.
@Ant.Karlov, @WeslomPo Да, обычно требуется лишь часть её функционала, однако это никак не сказывается на простоте и удобстве её использования, мне хватило пары часов, чтобы изучить все её возможности и понять, что тот велосипед, что у меня был до этого для таких целей, можно отправить на помойку. А сравнение с MD не совсем корректно, как выше уже правильно писали - монстр не пригоден для работы с уже релизнымполуреризным проектом и не обладает особенностями встроенной консоли, хоть он и бесспорно очень хорош. В любом случае - спорить смысла нет - у всех свои предпочтения, и всегда хорошо, когда кто-то создаёт что-то своё, новое. Выбор - это здорово!
Спасибо! очень не хватало!!!
И правда очень полезная статья!
А зачем нужен Repository? Почему не хранить функции вместе с коммандами и их описаниями в Array?
@Максим, если верить умным книжкам, то хранилище типа "ключ/объект" работает быстрее чем массивы, тем более если объектов много, а извлечь нужно только один. Только не помню уже в какой умной книжке я это прочитал, по AS3 или по Objective-C :)
Но если вам не нравится, то вы всегда можете переписать это под Array или под Vector. Правда в случае с массивами вам прийдется хранить в массиве объекты с такими параметрами как: ключ, указатель на метод, описание - со всеми вытекающими из этого заморочками.
Антон, Спасибо огромное за уроки, да и вообще за блог. А консоль то кстати в зомботроне работает. Теперь очень сложно не за читерить ;)
Сразу захотелось сделать свою консоль :)
P.S. Только почему-то div выводит NaN
А не. Это я протупил. Вводил с квадратными скобками :)
Hey! This is a very nice modification on top of my AS3 console, good job!
Will be using this myself!
не показывает текст, изменил на _sans в константе, продвижений нет.
проблема решена.
Приятная реализация (:
Давно хотел реализовать, теперь уж точно буду делать (:
@noname, если есть какая-то проблема и вы чуть позже сами находите на нее решение, то лучше всего будет, если вы сами напишите как решили сложившуюся проблему. Ваше решение может помочь другим кто столкнется с такой же проблемой.
В данном случае похоже на то что вы просто забыли переключить значение константы EMBED_FONTS с true на false.
Выше дело было не в эмбеде скорей всего, а в том, что FD почему-то не хватает фонт сразу, а если его в объявлениях добавить как private var bf:Font= new BraviaRegular(); (ну что-то вроде) - текст начинает отображаться правильно.
Кстати я добавил в консоль прокрутку роликом еще, иногда неудобно/лень до PgUp/PgDn тянуться :)
_stage.addEventListener(MouseEvent.MOUSE_WHEEL, wheelHandler);
private function wheelHandler(e:MouseEvent):void
{
if (e.delta < 0)
{
scrollDown();
}
else
{
scrollUp();
}
}
Здравствуйте
А вот у меня почему то консоль работать не хочет(
Запускал из примера.
При компиляции swf она просто появляется и спрятать ее нельзя, команду ввести в нее тоже нельзя
Если запустить swf из компа, консоль появляется прячется по тильде, но команды по прежнему ввести нельзя.
В чем может быть проблема?
@Smith, а в консоле отображаются шрифты? В примере по умолчанию используется не системный пиксельный шрифт, чтобы все работало правильно его следует установить в систему, либо использовать другой шрифт.
Привет, спасибо за консоль, первый раз сталкиваюсь с ними и сразу резанули 2 мелочи:
1. пишу команду:
move 123,123 - все ок
move 123, 123 - пробел перед запятой идет как 0, т.е. move 123,0
И зачем в русской раскладке консоль пишет аглицкими буквами (может это стандарт для них), а то я набираю свое любимое move 123, 123 и понять не могу куда запятая делась :) , а оказывается у меня RU активна.
2. При масштабировании (подслеповат я уже), поле _console чем-то перекрывается и букофки исчезают :(
@Cocodrilo,
1. В данном примере консоли используется стандартный метод split() для разделения строки на массив, но проблема даже не в этом, а в стандартном преобразовании String в Number перед делением двух значений. Похоже что String который начинается с пробела == 0. Решение простое, прежде чем начать делить, из команды следует удалить все символы кроме цифр и запятых. Но поскольку это код примера и он практически не имеет отношения непосредственно к самой консоли, я не стал усложнять пример лишними проверками на безопасность выполнения команд. Программист использующий консоль в своих проектах должен заботится о безопасности выполнения команд самостоятельно.
Про раскладку проблемы не понял, у меня в русской раскладке вообще ничего не вводится (нет русских символов в шрифте).
2. Посмотрите класс консоли, там есть маска размер которой можно отредактировать и получить консоль такого размера какого вам нравится.
@Ant.Karlov
>маска размер которой можно отредактировать
Только что-то не соображу где это обработать.
_stage.addEventListener(Event.RESIZE, resizeHandler);
хоть и прописано, но не срабатывает без
_stage.scaleMode = StageScaleMode.NO_SCALE,
которое может не совпадать с этой переменной в игре и при установке консоли в NO_SCALE приводить к изменению логики оторисовки игры
А по поводу обработки ошибок ввода - все же консоль обязана это делать, а не программист каждый раз подставлять костылики. ИМХО
@Cocodrilo, дело в том что при вводе команд полученные из строки консоли значения передаются в привязанный программистом метод в котором введеные значения как-то используются. И поскольку программист веденные значения может использовать любым образом: работать с ними как со строковым значениями или как с числовыми, или еще как-то — то базовую проверку для ввода всех значений делать уже не удобно.
К тому же как я уже заметил выше, в примере проблема кроется вовсе не в консоли, а в методе который производит расчеты. Методы привязываемые к консоли могут получать аргументы в виде массивов, или даже в виде нескольких аргументов. В примере значения которые будут делится задаются в виде одного аргумента через запятую. А можно было бы их задавать и в виде двух через пробел.
В любом случае консоль — это отладочный инструмент разработчика, а не игрушка для игроков, поэтому в большинстве случаев нет смысла возится с проверкой валидности введенных параметров.
Спасибо за столь замечательную идею и код - переделал и соптимайзил для себя -- теперь тащуусь)
Здравствуйте.
Не получается запустить консоль.
в начале класса пишу
public static var console:Console = Console.getInstance();
public static var trace:Function = console.trace;
далее в самом классе console.trace("test");
ни чего не происходит, нажате на тильду тоже не работает.
Если добавить console.initialize(stage); , то пишет ошибку TypeError: Error #1009: Не удается вызвать свойство или метод со ссылкой на объект "null".
at ru.antkarlov.utils::Console/initialize()
at main()
В чём здесь ошибка?
@Quies, ошибка скорее всего в том, что вы инициализируете консоль до того как основной класс программы был добавлен на сцену, то есть ссылка на stage равна null.
Попробуйте сделать инициализацию консоли таким образом:
if (stage != null)
{
initConsole();
}
else
{
addEventListener(Event.ADDED_TO_STAGE, initConsole);
}
private function initConsole(event:Event = null):void
{
console = Console.getInstance();
}
Судя по вашему примеру вы инициализируете консоль вовсе в объявлении переменных класса, что естественно не сработает из-за нулевого указателя на stage.
Спасибо большое, разобрался :)
Не там инициализировал,.
private function initConsole(event:Event = null):void
{
var console:Console = Console.getInstance();
console.initialize(stage);
}
ещё, не добавил console.initialize(stage); , в конце.
Консоль по нажатию тильды открылась, но возникли проблемы с отображением текста.
Поменял строчки первые в классе консоли и заработало.
private static const FONT_NAME:String = "Arial";
private static const EMBED_FONT:Boolean = false;
private static const FONT_SIZE:int = 12;
Мне кажется, вы недостаточно расписали процесс подключение консоли и её настройку, из за этого есть вопросы в комментариях.
Хотя, в целом очень рад что наткнулся на вашу статью, не пришлось изобретать велосипед :)
Попытался вставить сию вещицу на свою игрушку,но когда я ее запускаю,то появляется просто черное окошко,никакие нажатия не регестрируются,я подумал,что дело в размере флешки(он у меня нестандартный),но нет,когда я запускаю просто твой пример(cntr + enter) без изменений,то все тоже самое(появляется просто оформленное черное окошко,на ввод и нажатие клавиш не реагирует.
*Андрей,
у меня тоже окошко было черным, но выше постом пишут
Поменял строчки первые в классе консоли и заработало.
private static const FONT_NAME:String = "Arial";
private static const EMBED_FONT:Boolean = false;
private static const FONT_SIZE:int = 12;
Так вот у меня тоже заработало :) читайте коменты.
Вы можете использовать следующие тэги для форматирования текста в комментариях: [b]толстый[/b], [i]наклонный[/i], [u]подчеркнутый[/u], [link]ссылка[/link], [link=адрес ссылки]текст ссылка[/link]
Ух ты! Классно. И очень удобно. Правда я ещё не делал таких глобальных игр, чтобы использовать консоль. Делаю всё по-старинке, как ты раньше :)
15 Августа 2011
— 01:55
#