Разработка игр в Visual Studio
Разрабатывайте высокопроизводительные двух- и трехмерные игры с DirectX, которые запускаются на различных устройствах семейства Windows, включая настольные компьютеры, планшеты и телефоны. Visual Studio предлагает отличный набор инструментов для создания игр с DirectX, позволяющих писать код для шейдеров и разрабатывать ресурсы или отлаживать и профилировать графику, — и все это в рамках знакомой среды IDE Visual Studio.

Шаблоны проектов
Быстрое начало работы
Начните создавать игры DirectX в Visual Studio с помощью встроенных шаблонов проектов DirectX. При создании игр DirectX 12 или DirectX 11 для Windows или Windows Phone вы найдете шаблон, который вам подходит.
Отладка графики
Локальный или удаленный захват кадров
Устранение проблем отрисовки может быть непростой задачей. Visual Studio Graphics Diagnostics вместе с отдельным средством PIX on Windows предоставляет простой способ захвата и анализа кадров из игр DirectX 10, 11 или 12 локально или удаленно. Можно проверять каждое событие DirectX, объект графики, журнал пикселей и изучать графический конвейер для того, чтобы понять, что именно произошло во время кадра. Это средство также фиксирует стеки вызовов для каждого события графики, упрощая переход обратно в код приложения в Visual Studio.
Отладка кода шейдера
Тот же высококлассный отладчик теперь работает для отладки кода шейдеров
Отладка кода шейдера из захваченного кадра — отличный способ выявить источник проблем отрисовки. Просто установите точку останова в коде шейдера и нажмите клавишу F5 для его отладки. Можно проверить переменные и выражения в окне “Locals” и “Autos”. Если вы раньше использовали отладчик Visual Studio для других языков, эта задача не представит для вас никакой проблемы.
Увеличение частоты кадров
Поиск ресурсоемких вызовов прорисовки
Ищете способы увеличения частоты кадров для игры? Средство анализа кадров Visual Studio может пригодиться. Оно анализирует записанные кадры для поиска ресурсоемких вызовов прорисовки и выполняет с ними эксперименты для просмотра возможностей оптимизации производительности. Вся информация предоставляется в удобном отчете.
Анализ использования графического процессора
Сведения о том, как игра загружает ЦП и графический процессор
Использование функции Visual Studio GPU Usage или PIX в Windows для выяснения того, как выполняется игра на ЦП и графическом процессоре. Функция “Использование графического процессора” в реальном времени обеспечивает сбор данных, дополняющих анализ кадров, который выполняется над захваченными кадрами в автономном режиме. Отчет об использовании графического процессора ясно показывает, где находится узкое место – на ЦП или графическом процессоре.
Редактор кода шейдера
Цветовая разметка синтаксиса шейдера
Вне зависимости от того, размещается ли код шейдера в файлах HLSL или FX, редактор Visual Studio их распознает. Редактор шейдера предоставляет выделение синтаксиса и фигурные скобки автозаполнения, обеспечивая удобный способ для чтения и записи кода шейдера в Visual Studio. Можно также настроить редактор для использования избранных шрифтов и темы.
Компиляция кода шейдера
Файлы шейдера являются частью проекта
Как и положено, файлами шейдера можно управлять и создавать их как часть проектов Visual Studio. Просто задайте свойства файла шейдера, чтобы указать тип шейдера, его модель и параметры оптимизации. Visual Studio выполнит компиляцию шейдера.
Создание шейдеров
…в конструкторе шейдеров Visual Studio
Если вы не знаете HLSL и предпочитаете более наглядный способ создания шейдеров, конструктор шейдеров Visual Studio вам в этом поможет. Вместо того чтобы писать код шейдера в редакторе, добавьте и подключите узлы шейдера с помощью графического интерфейса. Вы можете применять различные текстуры, лампочки и даже добавлять и просматривать анимации в режиме реального времени. Создание шейдеров никогда не было таким простым.
Просмотр трехмерных моделей
…в средстве просмотра моделей Visual Studio
Не нужно выходить из интегрированной среды разработки, в которой вы уже работаете, просто для просмотра последних 3D-моделей, отправленных вам художником. Можно просмотреть 3D-модели OBJ и FBX в среде Visual Studio. Панорамирование, масштаб, изменение позиций камеры, просмотр объекты анимации на основе времени — в трехмерном мире невероятно много вещей для анализа. В средстве просмотра моделей можно также вносить простые изменения в модели.
Редактирование текстур
…в редакторе изображений Visual Studio
Редактор изображений Visual Studio не только прекрасно работает с файлами изображений, но также распознает файлы текстуры DirectDraw Surface (DDS). Помимо базового просмотра и функциональности рисования можно также переключать каналы RGBA, создавать MIP-карты и применять фильтры. Этот редактор изображений способен выполнить многие задачи по изменению текстуры.
Конвейер содержимого ресурсов
Управление конвейерами содержимого
Работа с ресурсами в различных форматах может быть невероятно сложной. Visual Studio предоставляет средства управления конвейерами содержимого для изображений, моделей и шейдеров. Просто задайте свойства файла для использования конвейера содержимого и настройте параметры. Visual Studio выполнит преобразования формата автоматически во время построения.
Ядро Unity объединяется в одну непревзойденную платформу, позволяющую создавать двухмерные и трехмерные игры и интерактивное содержимое. Создайте одну игру и опубликуйте ее на 21 платформе, включая все мобильные платформы, WebGL, настольные системы (Mac, ПК и Linux), Интернет или приставки. Используйте мощные кроссплатформенные инструменты, чтобы обеспечить прекрасное выполнение своего интерактивного содержимого на любом устройстве.

Отладка в Visual Studio
Высококлассная отладка для игр на основе Unity в Visual Studio
Visual Studio обеспечивает первоклассную отладку игрового ядра Unity. Быстро выявляйте проблемы, отлаживая игры на основе Unity в Visual Studio: задавайте точки останова и оценивайте переменные и сложные выражения. Вы можете выполнять отладку игры Unity, запущенной в Unity Editor или Unity Player, и даже отлаживать внешнюю управляемую библиотеку DLL в Unity Project.


Повышение производительности
Полнофункциональная среда IDE для Unity
Программируйте эффективнее, используя все возможности повышения производительности, предлагаемые Visual Studio, в том числе IntelliSense, рефакторинг и просмотр кода. Настройте среду программирования в полном соответствии со своими потребностями — выберите любимую тему, цвет, шрифты и остальные параметры. Кроме того, используйте Unity Project Explorer для перехода к сценариям Unity и их создания. Вам больше не надо переключаться между несколькими интегрированными средами разработки. Вы можете быстро создавать методы сценариев Unity в Visual Studio, используя мастера реализации поведений MonoBehaviour и быстрых поведений MonoBehaviour.
Unreal Engine 4 — это полный набор инструментов для разработки игр, созданный опытными специалистами в этой области для своих коллег. В Unreal Engine 4 есть все необходимое для создания двухмерных мобильных игр, консольных блокбастеров и игр в виртуальной реальности, чтобы вы могли приступить к их разработке, распространять и совершенствовать их, а также составить достойную конкуренцию другим компаниям в этой отрасли. Изменяйте, создавайте и отлаживайте игры на основе Unreal на языке C++ в Visual Studio, чтобы повысить продуктивность.

Программирование в Visual Studio
Unreal Engine легко интегрируется с Visual Studio, позволяя быстро вносить изменения в код проекта и увидеть результаты сразу после компиляции. Пишите код с многофункциональной поддержкой IntelliSense с динамическим списком ошибок и комментариев, чтобы ускорить рабочий процесс. Экономьте время, используя интеллектуальные предложения IntelliCode о типах пользовательского оборудования. При желании можно обучить модель IntelliCode локально с помощью базы кода команды и получать конкретные предложения, основанные на типах и шаблонах кодирования команды.
Отладка в Visual Studio
Visual Studio обеспечивает высококлассную отладку в рамках Unreal Engine. Вы можете легко отлаживать проекты Unreal с помощью расширенной поддержки с визуализаторами и исследовать распространенные типы Unreal, такие как FNames и динамические массивы. Вы можете также проверить производительность программы: просто обратите внимание на советы по производительности, которые отображаются в редакторе.
Cocos — это предназначенный для создания игр профессиональный пакет средств разработки с открытым исходным кодом, который упрощает процесс разработки. С помощью Cocos вы можете быстро создавать проекты, разрабатывать игры и анимационные клипы, а также пакетировать и публиковать игры для распространения. Создавайте игры Cocos, используя мощную среду IDE Visual Studio IDE, и выполняйте отладку с помощью первоклассного отладчика Visual Studio, чтобы значительно повысить эффективность своей работы.

Программирование в Visual Studio
Подсистема Cocos полностью интегрируется с Visual Studio и позволяет программировать эффективнее, используя все возможности повышения производительности, предлагаемые Visual Studio, в том числе IntelliSense, рефакторинг и просмотр кода. Компилируйте и запускайте проекты в Visual Studio, чтобы сразу просмотреть влияние изменений в коде.


Отладка в Visual Studio
Используйте мощный отладчик Visual Studio, чтобы быстрее находить ошибки. Отладчик Visual Studio интегрируется с рабочим процессом разработки игр на Cocos: задайте точки останова и начните вычислять переменные и выражения. Вы можете также проверить производительность программы: просто ознакомьтесь с советами по производительности, которые отображаются в редакторе.
Создание серверной инфраструктуры для игр
Azure предоставляет гибкие возможности выбора для серверной части игр в облаке. Для вычислений можно использовать такие предложения IaaS, как виртуальная машина, масштабируемые наборы виртуальных машин в Windows и Linux, или же предложения PaaS, например Service Fabric и Службу приложений. В качестве хранилища данных можно использовать управляемые службы баз данных, такие как База данных SQL Azure и Azure DocumentDB, а также MongoDB и другие варианты из Azure Marketplace.


Сохранение заинтересованности игроков
Внедрите многопользовательские режимы и списки лидеров, используя Azure Active Directory. Например, управляйте поставщиками удостоверений социальных сетей, таких как Facebook, Google и Майкрософт. Управляйте удержанием игроков, повышением вовлеченности пользователей и монетизацией на разных платформах с помощью Центров уведомлений Azure и Служб мультимедиа Azure.
Исследование больших данных для подробного анализа игр
Создайте многофункциональную платформу сквозного анализа игр в Azure с помощью средств из Cortana Intelligence Suite и решений для больших данных. Анализируйте поведение игроков на мобильных устройствах с помощью таких служб, как машинное обучение Azure и службы мобильного взаимодействия Azure, чтобы максимально увеличить частоту использования приложения, удержание пользователей и монетизацию.
Как писать на c++ в годот?
Недавно я решил перейти в gamedev и в роли движка выбрал Godot.До этого у меня был опыт работы с c++.Я не хотел бы изучать Gdscript или c#, а хотел бы продолжить работать на c++, но не знаю как.
- Вопрос задан 19 июн.
- 1772 просмотра
Комментировать
Решения вопроса 0
Ответы на вопрос 2
you don’t choose c++. It chooses you
C++ не основной язык для Godot, но есть вот такое расширение, чтобы писать на С++.
https://docs.godotengine.org/en/stable/tutorials/s.
Выберите правильную версию godot и следуйте инструкции. Она оочень подробная + есть несколько видео на известной площадке, где также рассказывают как использовать С++.
Ответ написан 19 июн.
Нравится 2 3 комментария
ChatGPT чтоль? Чего такой ответ странный? 😀

tukreb, не, вообще на чатжпт не похоже.
Да и ссылка легко гуглится в документации
tukreb, Я даже не знаю комплимент это или упрек. Вот вам для сравнения ответ от chatgpt.
Отлично, что вы решили попробовать Godot для своих игровых проектов! Хотя Godot поддерживает Gdscript и C# в качестве основных языков программирования, вы можете использовать и C++ для разработки в этом движке.
Важно отметить, что работа с C++ в Godot немного отличается от обычной разработки на этом языке. Вместо написания всего кода на C++, вам потребуется создать динамическую библиотеку (DLL) на C++, которую затем можно будет использовать в Godot. Взаимодействие с движком осуществляется через его API на C++.
Вот несколько шагов, которые вам понадобятся для использования C++ в Godot:
1. Установите Godot Engine: Перейдите на официальный сайт Godot (https://godotengine.org/) и загрузите последнюю версию движка. Установите его на свой компьютер.
2. Создайте проект: Запустите Godot и создайте новый проект. Выберите пустой шаблон или тот, который лучше всего соответствует вашим потребностям.
3. Настройте среду разработки: Если вы используете IDE, такое как Visual Studio или Visual Studio Code, настройте его для работы с Godot. На сайте Godot есть подробные инструкции по настройке различных IDE.
4. Создайте модуль на C++: В Godot создайте свой C++ модуль. Модуль представляет собой DLL, который будет содержать ваш пользовательский код на C++. Модуль может содержать классы, функции и другие элементы, которые вам понадобятся для разработки игры.
5. Взаимодействие с движком: Используйте API Godot на C++ для взаимодействия с движком. API предоставляет функции для работы с узлами, ресурсами, сигналами и другими элементами Godot.
6. Соберите и подключите модуль: Соберите вашу DLL и подключите ее к проекту Godot. Затем вы сможете использовать свой C++ код в скриптах и компонентах игры.
Обратите внимание, что использование C++ в Godot может быть сложным и требует знания языка C++, а также понимания API и архитектуры Godot. Будьте готовы к изучению документации и примеров кода для получения лучшего понимания процесса.
Также стоит отметить, что Gdscript и C# предоставляют более простой путь для начинающих
1. Установите необходимые инструменты: Убедитесь, что у Вас установлены C++ компилятор и среда разработки, такие как Visual Studio или GCC, в зависимости от Вашей операционной системы.
2. Скомпилируйте движок Godot: Перейдите на официальный сайт Godot и загрузите исходный код движка. Затем следуйте инструкциям по компиляции для Вашей операционной системы. В результате Вы получите исполняемый файл движка, который Вы сможете использовать для разработки.
3. Создайте новый проект: Откройте Godot и создайте новый проект. Выберите пустой шаблон или любой другой шаблон, который Вам нравится.
4. Создайте новый скрипт: В окне редактора Godot выберите «Скрипт» и выберите C++ как язык скрипта. Затем выберите место, где Вы хотите сохранить скрипт.
5. Начните писать код: Откройте созданный скрипт в выбранной Вами среде разработки и начните писать код на C++. Godot предоставляет API для работы с C++, и Вы можете использовать функции и классы из этого API для создания игровой логики.
6. Скомпилируйте и запустите проект: После написания кода скомпилируйте проект с помощью выбранной Вами среды разработки. Затем запустите полученный исполняемый файл и проверьте, что Ваш код работает внутри Godot.
Обратите внимание, что использование C++ в Godot может быть сложнее, чем использование Gdscript или C#, поэтому рекомендуется ознакомиться с документацией и руководствами по C++ в Godot, чтобы получить более подробную информацию о возможностях и ограничениях.
Введение в интегрированную среду разработки Visual Studio (C#)
Область применения:
Visual Studio Visual Studio для Mac
Visual Studio Code ![]()
Интегрированная среда разработки (IDE) — это многофункциональная программа, которая поддерживает многие аспекты разработки программного обеспечения. Интегрированная среда разработки Visual Studio — это стартовая площадка для написания, отладки и сборки кода, а также последующей публикации приложений. Помимо стандартного редактора и отладчика, которые есть в большинстве сред IDE, Visual Studio включает в себя компиляторы, средства автозавершения кода, графические конструкторы и многие другие функции для улучшения процесса разработки.

На рисунке выше представлена среда Visual Studio с открытым проектом и подсказки по основным окнам и функциональных возможностях.
- Справа в верхнем углу окна Обозревателя решений вы можете просматривать файлы кода, перемещаться по ним и управлять ими. Обозреватель решений позволяет упорядочить код путем объединения файлов в решения и проекты.
- В центральном окне редактора, с которым вы, вероятно, будете работать дольше всего, отображается содержимое файла. В окне редактора вы можете вносить изменения в код или разрабатывать пользовательский интерфейс, например окно с кнопками или текстовые поля.
- Окно Изменения Git в нижнем углу справа позволяет отслеживать рабочие элементы и предоставлять общий доступ к коду, используя Git, GitHub или другие технологии управления версиями.
Выпуски
Служба Visual Studio доступна для Windows и Mac. Функции Visual Studio для Mac во многом аналогичны возможностям Visual Studio для Windows и оптимизированы для разработки кросс-платформенных и мобильных приложений. Эта статья посвящена версии Visual Studio для Windows.
Существует три выпуска Visual Studio: Community, Professional и Enterprise. Сведения о функциях, поддерживаемых в каждом выпуске, см. на странице Сравнение выпусков Visual Studio.
Популярные средства повышения производительности
Вот несколько популярных возможностей Visual Studio, которые повышают производительность при разработке программного обеспечения:
-
Волнистые линии и быстрые действия Волнистые линии обозначают ошибки или потенциальные проблемы кода прямо во время ввода. Эти визуальные подсказки помогают немедленно устранить проблемы, не дожидаясь появления ошибок во время сборки или выполнения. Если навести указатель мыши на волнистую линию, на экран будут выведены дополнительные сведения об ошибке. Также в поле слева может отображаться лампочка, указывающая на наличие сведений о быстрых действиях для устранения ошибки.



- Очистка кода Вы можете одним нажатием кнопки отформатировать код и применить к нему исправления, предложенные параметрами стиля кода, соглашениями в файле .editorconfig и (или) анализаторами Roslyn. Очистка кода, которая сейчас доступна только для кода C#, помогает устранять проблемы в коде перед переходом к его проверке.

- Очистка кода Вы можете одним нажатием кнопки отформатировать код и применить к нему исправления, предложенные параметрами стиля кода, соглашениями в файле .editorconfig и (или) анализаторами Roslyn. Очистка кода, которая сейчас доступна только для кода C#, помогает устранять проблемы в коде перед переходом к его проверке.
- Рефакторинг Рефакторинг включает в себя такие операции, как интеллектуальное переименование переменных, извлечение одной или нескольких строк кода в новый метод и изменение порядка параметров методов.





Дополнительные сведения и советы по повышению производительности см. в разделе Практическое руководство. Поиск в Visual Studio.

Дополнительные сведения и советы по повышению производительности см. в разделе Практическое руководство. Поиск в Visual Studio.









- Горячая перезагрузка Горячая перезагрузка позволяет изменять файлы кода приложения и сразу же применять изменения кода к работающему приложению.
Установите Visual Studio.
В рамках этого раздела вы создаете простой проект для тестирования некоторых возможностей Visual Studio. IntelliSense используется как вспомогательный инструмент для программирования и отладки приложения, который позволяет просмотреть значение переменной во время выполнения приложения или изменить цветовую тему.
Чтобы начать работу, скачайте и установите Visual Studio. Этот модульный установщик позволяет выбрать и установить рабочие нагрузки, которые являются группами функций, необходимыми для предпочитаемого языка программирования или платформы. Выполните следующие инструкции по созданию программы и в процессе установки выберите рабочую нагрузку Кроссплатформенная разработка .NET Core.

Чтобы начать работу, скачайте и установите Visual Studio. В модульном установщике вы можете выбрать и установить рабочие нагрузки, которые являются группами функций, необходимыми для предпочитаемого языка программирования или платформы. Чтобы выполнить следующую процедуру создания программы, обязательно выберите во время установки рабочую нагрузку Разработка классических приложений .NET.

При первом запуске Visual Studio можно выполнить вход с использованием учетной записи Майкрософт или рабочей учетной записи.
Создание программы
Давайте создадим простую программу.
- Откройте Visual Studio. Откроется начальное окно, где можно клонировать репозиторий, открыть недавно использованный проект или создать новый.
- Выберите Создать проект.
Откроется окно Создание проекта с отображением нескольких шаблонов проектов. Шаблон содержит основные файлы и параметры, которые требуются для определенного типа проекта. - Чтобы найти нужный шаблон, введите консоль .net core в поле поиска. Список доступных шаблонов автоматически отфильтруется по введенным словам. Вы можете дополнительно отфильтровать результаты шаблона, выбрав C# в раскрывающемся списке Все языки, Windows в списке Все платформы и Консоль в списке Все типы проектов. Выберите шаблон Консольное приложение и щелкните Далее.

- В окне Настройка нового проекта введите HelloWorld в поле Имя проекта, при необходимости измените расположение каталога для вашего проекта (по умолчанию используется путь C:\Users\\source\repos ) и щелкните Далее.

- В окне Дополнительные сведения убедитесь, что в раскрывающемся меню Целевая платформа указано .NET Core 3.1, а затем щелкните Создать.
Visual Studio создаст проект. Это простейший вариант приложения «Hello World», в котором вызывается метод Console.WriteLine() для вывода литеральной строки «Hello World!» в окне консоли (выходных данных программы). Должно отобразиться примерно следующее:
Код C# для вашего приложения отображается в окне редактора, который занимает большую часть пространства. Обратите внимание, что текст автоматически выделяется цветом для обозначения разных частей кода, таких как ключевые слова и типы. Кроме того, небольшие вертикальные штриховые линии кода указывают, какие фигурные скобки соответствуют друг другу, а номера строк помогут вам найти нужный код позже. Чтобы свернуть или развернуть блоки кода, используйте небольшие рамки со знаком минус. Эта функция структурирования кода позволяет скрыть ненужный код на экране. Файлы вашего проекта перечислены в окне обозревателя решений, которое находится справа.
Есть и другие доступные меню и окна инструментов, но об этом позже. - Теперь запустите приложение. Это можно сделать, выбрав Запуск без отладки в меню Отладка в строке меню. Можно также нажать клавиши CTRL+F5.

- Чтобы закрыть окно консоли, нажмите любую клавишу.
- Давайте добавим новый год в это приложение. Перед строкой Console.WriteLine(«Hello World!»); добавьте следующий код C#:
Console.WriteLine("\nWhat is your name?"); var name = Console.ReadLine();
Console.WriteLine($"\nHello !");

- Запустите среду Visual Studio. Откроется начальное окно, где можно клонировать репозиторий, открыть недавно использованный проект или создать новый.
- Выберите Создать проект.
Откроется окно Создание проекта с отображением нескольких шаблонов проектов. Шаблон содержит основные файлы и параметры, которые требуются для определенного типа проекта. - Чтобы найти шаблон, попробуйте ввести или ввести ключевые слова в поле поиска. Список доступных шаблонов будет фильтроваться по введенным ключевым словам. Вы можете дополнительно отфильтровать результаты шаблона, выбрав C# в раскрывающемся списке Все языки, Windows в списке Все платформы и Консоль в списке Все типы проектов. Выберите шаблон Консольное приложение и нажмите кнопку Далее.

- В поле Имя проекта окна Настроить новый проект введите HelloWorld. При необходимости измените расположение каталога проекта в расположении по умолчанию C:\Users\\source\repos, а затем нажмите кнопку Далее.

- Убедитесь, что в окне Дополнительные сведения в раскрывающемся меню Целевая платформа указано .NET 6.0, а затем щелкните Создать.
Visual Studio создаст проект. Это простейший вариант приложения Hello World, в котором вызывается метод Console.WriteLine() для вывода строки Hello World! в окне консоли. Файлы проекта отображаются справа в окне интегрированной среды разработки Visual Studio в окне с названием Обозреватель решений. В окне Обозреватель решений выберите файл Program.cs. Код C# для вашего приложения открывается в центральном окне редактора, который занимает большую часть пространства.
Код автоматически выделяется цветом для обозначения таких элементов, как ключевые слова и типы. Найти код можно по номерам строк. Небольшие вертикальные пунктирные линии в коде указывают, какие фигурные скобки соответствуют друг другу. Чтобы свернуть или развернуть блоки кода, используйте небольшие рамки со знаками минус и плюс соответственно. Эта функция структурирования кода позволяет скрыть ненужный код на экране.
Также доступно множество других меню и окон инструментов. - Запустите приложение, выбрав в главном меню Visual Studio пункты Отладка>Запуск без отладки. Можно также нажать клавиши CTRL+F5.

- Для закрытия окна консоли нажмите любую клавишу.
- Давайте добавим новый год в это приложение. Перед строкой Console.WriteLine(«Hello World!»); добавьте следующий код C#:
Console.WriteLine("\nWhat is your name?"); var name = Console.ReadLine();
Console.WriteLine($"\nHello !");

Использование рефакторинга и IntelliSense
Рассмотрим несколько примеров того, как рефакторинг и IntelliSense помогают повысить эффективность кода.
Сначала переименуйте переменную name :
- Дважды щелкните переменную name и введите для нее новое имя: username. Вокруг переменной появится прямоугольник, а в поле появится значок лампочки.
- Выберите значок лампочки для отображения доступных быстрых действий. Выберите Переименовать name в username.




int dayOfYear = now.DayOfYear; Console.Write("Day of year: "); Console.WriteLine(dayOfYear);
Совет Код Console.Write отличается от Console.WriteLine тем, что не добавляет знак завершения строки после ее вывода. Это означает, что следующий фрагмент текста, отправляемый на вывод, будет выводиться в той же строке. Можно навести указатель мыши на каждый из этих методов в коде, чтобы просмотреть его описание.




Отладка кода
При написании кода его следует регулярно запускать и проверять на предмет ошибок. Система отладки Visual Studio позволяет просматривать код с шагом в одну инструкцию, проверяя значения переменных. Вы можете задать точки останова, которые позволяют приостановить выполнение кода в определенной строке и увидеть, как изменяется значение переменной при выполнении кода.
Зададим точку останова, чтобы во время выполнения программы отобразилось значение переменной username .
-
Установите точку останова в строке с кодом Console.WriteLine($»\nHello !»); , щелкнув крайнее поле слева (область навигации) в этой строке. Кроме того, вы можете выбрать строку кода и нажать клавишу F9. В области навигации появится красный кружок, и эта строка будет выделена.




После запуска приложения можно применить изменения кода к работающему приложению, нажав кнопку Горячая перезагрузка.

Дополнительные сведения об отладке в Visual Studio см. в статье Знакомство с отладчиком Visual Studio.
Настройка Visual Studio
Вы можете настроить пользовательский интерфейс Visual Studio, в том числе изменить цветовую тему, установленную по умолчанию. Изменение цветовой темы

- В строке меню выберите Сервис>Параметры, чтобы открыть диалоговое окно Параметры.
- Откройте страницу параметров Окружение >Общие, измените значение Цветовая тема на Темная и щелкните ОК. Цветовая тема для всей интегрированной среды разработки изменится на тему Темная.

- В строке меню выберите Сервис>Параметры, чтобы открыть диалоговое окно Параметры.
- На странице параметров Среда >Общие измените значение параметра Цветовая тема на Синий или Светлый. Затем нажмите кнопку ОК. Цветовая тема для всей интегрированной среды разработки соответствующим образом изменится. На следующем снимке экрана показана синяя цветовая тема:
Дополнительные сведения о других способах персонализации интегрированной среды разработки см. в разделе Персонализация Visual Studio.
Выбор параметров среды
Вы можете настроить Visual Studio для использования параметров среды, предназначенных для разработчиков на C#.
- В строке меню выберите Сервис>Импорт и экспорт параметров.
- В мастере импорта и экспорта параметров выберите Сбросить все параметры, а затем нажмите кнопку Далее.
- На странице Сохранить текущие параметры выберите, следует ли сохранить текущие параметры перед сбросом. Если вы не изменяли параметры, выберите Нет, только сбросить параметры, перезаписав мои текущие значения. Затем выберите Далее.
- На странице Выбор набора параметров, используемого по умолчанию выберите Visual C#, а затем нажмите кнопку Готово.
- На странице Сброс завершен нажмите Закрыть.
Дополнительные сведения о других способах персонализации интегрированной среды разработки см. в разделе Персонализация Visual Studio.
Следующие шаги
Узнайте больше о Visual Studio, прочитав одну из следующих вводных статей:
Как написать игру на C++
Эта статья — нарезка приемов реализации базовых элементов игр. Вы можете скомпоновать их между собой и получить нужный результат. В ходе написания статьи удалось сделать несколько удачных находок, которых не было в других подобных руководствах. По крайней мере в тех, которые удалось найти в интернете.
Оглавление
- Мотивация
- Зачем так делать?
- Почему C++?
- Что нужно знать, чтобы понять статью?
- Windows API
- conio.h + getch
- Windows API
- std::chrono
- Перестановка курсора
- Вывод готовых фрагментов текста
- Настройка буферизации stdio.h
- Измерение производительности
Мотивация
Зачем так делать?
Чтобы научиться программировать. А точнее — чтобы научиться переносить человеческие мысли в код. Обычные задания по программированию плохо тренируют этот навык потому что они скучные. Признаюсь честно, я намеренно готовил скучные задания для своих студентов. Нудные задания на превращение двух чисел в третье неизбежны, чтобы освоить базовые концепции. Такие задания помогут вам спуститься по горной реке, заполненной острыми камнями синтаксиса и водоворотами новых концепций. Проблема в том, что простые понятные задания однажды бросят вас в бескрайнем океане возможностей. Тогда вы обнаружите, что можете плыть куда угодно. Однако никто не подскажет вам верное направление. Ни одна платформа с автоматической проверкой решений не сможет проверить творческую работу и поставить оценку. Это может сделать либо живой преподаватель, либо живой напарник.
В статье ничего не будет про создание конкретной игры. Потому что конкретная игра — конкретные правила. А правила вы должны придумать сами. Дайте волю своему воображению, вспомните свою любимый жанр и сделайте хорошую попытку. Единственное ограничение — мы не будем рисовать красивую картинку. Потому что красивая картинка займет слишком много сил и будет только отвлекать нас от упоения кодом. Скажу по секрету, как только вы сделаете свою игру с помощью символов в черном окошечке, то сможете воплотить ваш замысел на любой другой платформе. Интересная игра будет интересна даже в консоли.
Почему C++?
Главная причина в том, что я сейчас веду индивидуальные занятия по C++ у одного талантливого студента. Его успехи вдохновили меня, а его вопросы показали о чем вообще нужно написать. C++ до сих пор рекомендуют как «язык для обучения». Это вселяет надежду, что статья будет полезна многим. Может быть когда-нибудь я напишу такую же статью и для других языков или для Linux, но не рассчитывайте на это. Если вы напишете сами подобный сборник советов для другого языка, то сообщите мне личным сообщением. Я добавлю ссылку на ваш труд.
Что нужно знать, чтобы понять статью?
Если вы понимаете концепцию циклов, массивов и функций, то вам должно быть достаточно. Предупреждаю сразу, в статье будут магические конструкции, которые я не буду объяснять. У меня нет цели сделать всеобъемлющий курс по C++. Цель — писать код и радоваться тому, как оно почти магически заработает. Когда закончите с основной целью или когда встретитесь с непреодолимыми проблемами, тогда и углубляйтесь.
Обработка действий игрока в реальном времени
Проходя стандартную пачку учебных задач вы скорее всего использовали std::cin или scanf. Программа ждала от пользователя каких-то данных и нажатия кнопки Enter. Вся осмысленная работа происходила уже после того, как исходные данные получены и проверены. Но в настоящих играх специальную кнопку нужно нажимать только в пошаговых стратегиях. Остальные игры реагируют сразу при нажатии нужных кнопок. Поэтому главный прием для написания игры в реальном времени — моментальная обработка нажатия клавиш. Программа не должна ждать нажатия кнопки Enter. Игра должна работать и параллельно обрабатывать нажатие на клавиатуру. В профессиональном сообществе говорят, что когда нужно нажимать кнопку Enter, то это блокирующий ввод. Соответственно, когда программа работает и при этом готова обрабатывать команды — неблокирующий ввод.
Windows API
Когда я погуглил «non-blocking console input», то SO услужливо выдал мне вот такой вопрос https://stackoverflow.com/questions/6171132/non-blocking-console-input-c К сожалению, у него нет того ответа, который нужен нам. Вариант с фоновым потоком я считаю слишком сложным для того, чтобы кодить для своего удовольствия. Вариант с дополнительной нестандартной библиотекой — тоже. Вся остальная выдача не сильно отличалась по смыслу.
Существует ли такая возможность вообще? Наверняка да. Например в SDL.dll мы делаем графическое приложение и основной способ реагировать на клавиатуру с мышью — обрабатывать системные события. Вот есть целая глава руководства https://www.willusher.io/sdl2%20tutorials/2013/08/20/lesson-4-handling-events При этом тащить всю библиотеку SDL мне совсем не хотелось. Но раз нестандартная библиотека может обрабатывать события, значит любой сможет. Таким образом, чтобы программа работала и при этом моментально обрабатывала нажатия на клавиши, нужно отказаться от scanf и std::cin. Нам нужно пойти глубже — на уровень событий.
Поиск по ключевым словам «handle C++ event» выдало целую кучу бесполезной информации по рисованию окон в каком-то из фреймворков windows. Я все еще намерен писать простую игру в консоли, а не оконное приложение, поэтому игнорирую результаты.
Следующая попытка была «cpp event loop with console input» и мне попалась статья https://docs.microsoft.com/en-us/windows/console/reading-input-buffer-events из которой я и взял решение. Считаю это удачной находкой, потому что в процессе написания этой статьи пытался вспомнить свои запросы и даже слегка видоизмененный «event loop c++ with console application» уже не давал нужной информации. Пришлось смотреть историю поиска. Для будущих поколений я добавил сюда явный текст своих запросов, в надежде что будущие поиски будут более результативными.
Подстава в том, что даже эта статья на самом деле показывает пример блокирующего чтения «The function [ReadConsoleInput] does not return until at least one record is available to be read.» Поэтому я посмотрел на соседние статьи и нашел обзор низкоуровневых функций чтения https://docs.microsoft.com/en-us/windows/console/low-level-console-input-functions
В ней описана функция PeekConsoleInput, которая «If no records are available, the function returns immediately.», но которая при этом «Reads without removing the pending input records in an input buffer.». То есть если ее вызвать несколько раз подряд, то она получит информацию об одних и тех же событиях. К счастью там же еще описана функция FlushConsoleInputBuffer, которая удаляет все непрочитанные накопленные события. Сочетание этих двух функций позволит добиться нужного эффекта.
Я видоизменил код из найденной статьи https://docs.microsoft.com/en-us/windows/console/reading-input-buffer-events а именно:
- Удалил обработку событий мыши. Если понадобится, спишите ее сами из оригинала.
- Удалил колдовство над режимом консоли (функции GetConsoleMode/SetConsoleMode). Для обработки клавиатуры подходит и стандартный режим. Это позволило упростить обработку ошибок в функции ErrorExit.
- Добавил сквозной счетчик итераций внешнего цикла. Чтобы было видно работу программы при отсутствии событий.
Получилось вот так:
Я знаю, что публиковать код картинкой — ужасно. Признаюсь, что это намеренно. Ленивые и недостаточно целеустремленные не смогут просто скопировать решение.
При запуске будет повторяться одна и та же фраза. Например «iteration 468811 total 106 current 2» где значение после iteration — счетчик, который увеличивается при каждой итерации цикла. Даже если мы ничего не будем нажимать. значение после total — количество событий, которые мы обработали значение после current — количество событий, которые нужно обработать в этой итерации цикла
Допустим мы увидели как обрабатывать события клавиатуры не останавливая работу программы. Что можно с этим сделать?
Чтобы собрать больше информации, я поставил точку останова в функции обработки события и понажимал разные кнопки.
Анализ результатов в отладчике
Например вот так выглядит событие при нажатии «ж».
Вот так — нажатие шифта
Вот так — нажатие «ж» с удерживаемым шифтом. Пришлось переставить точку остановки на «отпускание» клавиши, чтобы программа не реагировала на зажатый шифт. Обратите внимание, что dwControlKeyState отличается от «ж» без шифта.
Вот так — символ точка с запятой на английской раскладке. Это та же кнопка, что и «ж».
Вот так — символ точка с запятой на русской раскладке. Это уже другая кнопка — с цифрой «4».
Вот так — доллар. Он тоже на цифре «4», но на английской раскладке.
Обратите внимание, что во всех вариантах нажатия на кнопку «ж», «Ж», «;» в поле wVirtualKeyCode находится одно и то же число. Если использовать это поле, то управление не будет зависеть от раскладки и даже от зажатого шифта и капслока.
Еще важный момент, что wVirtualKeyCode для точки с запятой «;» на разных клавишах разный, но uChar.UnicodeChar у них одинаковый.
Итого с помощью Windows API мы можем:
- Различать нажатые клавиши по коду клавиши независимо от раскладки
- Различать нажатые клавиши по коду из Юникода независимо от их расположения на клавиатуре.
- Обрабатывать нажатия на shift, ctrl, alt.
- Понимать, нажат ли сейчас shift, ctrl, alt.
- Отличать нажатие клавиши и ее отпускание (буду благодарен, если подберете слово получше).
- Прикрутить обработку событий мыши
Если мы можем отличать нажатые кнопки, то можем и по-разному на них реагировать. Например менять координаты персонажа при нажатии на стрелки. Буду использовать обычную для программ координатную сетку. Ноль в ней находится слева сверху. Ось X увеличивается вправо, а ось Y — вниз. Для обозначения координат персонажа объявил две переменные: x и y с начальным значением 10.
В обработчик нажатия клавиш добавил ветвление. В нем проверяется код клавиши и увеличивается соответствующая переменная.
Для проверки запустил и нажал вниз 1 раз, вправо 2 раза, вверх 3 раза, влево 4 раза. Получился вывод как на картинке. Чтобы сделать скриншот, пришлось подключить отладчик.
conio.h + getch
Мой студент параллельно мне нашел способ обрабатывать нажатия на кнопки с помощью комбинации функций kbhit и getch из conio.h. С помощью getch можно получить код нажатого символа, но эта функция ждет следующего нажатия. Чтобы программа при этом продолжала работать, нужно сначала вызвать kbhit. Эта функция вернет true, если нажата хотя бы одна клавиша, но при следующего нажатия ждать не будет. Если ничего не нажато, то kbhit возвращает false и программа работает дальше.
К сожалению, я сходу не разобрался, в какой кодировке этот код для кириллических символов. Я реализовал прототип с помощью conio и поэкспериментировал с нажатием клавиш. Эта библиотека игнорирует нажатие shift, ctrl, alt, caps lock. Точка с запятой «;» на клавише с «ж» вернет код «59», на клавише с «4» тоже получился код 59. У символов в разных регистрах, например «ж» и «Ж» будут разные коды. У меня получились 166 и 134 соответственно. Интересно, что стандартное преобразование «(char)key» превратило эти коды в совершенно другие символы.
Получается, что conio проще чем winapi по написанию, но беднее по возможностям. Какой именно способ применить для вашей игры — выбирать только вам.
Запуск периодических событий по таймеру
Как вы можете заметить, если просто отпустить программу в свободный полет, то она будет очень часто обращаться к списку событий. На моем компьютере получается несколько десятков раз за каждую миллисекунду. Само по себе это хорошо, т.к. позволяет оперативно реагировать на все действия игрока. Однако если мир будет меняться с такой же скоростью, то игрок никак не сможет поспеть за ним. Есть несколько способов контролировать период обновления мира. Я выбрал вариант без подключения дополнительных библиотек. Основная идея в том, чтобы постоянно смотреть на время, а обновление мира запускать каждые несколько секунд. Раз уж я докопал до ручной реализации event-loop, то изобрести велосипед с реализацией задержки в рамках выбранной архитектуры не составит труда.
Конкретная реализация нагуглилась с первого запроса «windows.h time milliseconds». https://stackoverflow.com/questions/17008026/windows-how-to-get-the-current-time-in-milliseconds-in-c Для очистки совести я еще попробовал поискать «cpp thread sleep» и «cpp sleep». Первый вариант получился слишком сложный, а второй недостаточно точный.
Поэтому я предлагаю свой вариант. Цель — вписать ожидание в существующий цикл обработки событий. Для этого в начале программы я получаю текущее время. Если бы я работал с обычными часами, то это было бы 17:32:44. Затем я вычисляю время следующего события. Допустим оно должно произойти через 40 минут. Для этого к текущему времени прибавляю длительность ожидания. В моем примере событие произойдет в 18:12:44. Время второго события получается к времени первого события прибавляю длительность ожидания. 18:52:44 Врем третьего события 19:32:44 и так далее.
Существует несколько способов получить текущее время:
- С помощью GetSystemTime из windows.h
- С помощью std::chrono
Сразу говорю, второй вариант получается проще. Но я сначала пошел первым путем, а потом было жалко удалять это из черновика. Плохой пример тоже пример, поэтому смотрите.
Получение текущего времени Windows API
Функция GetSystemTime находится в библиотеке . Она записывает структуру с несколькими полями. Каждое поле отвечает за свою компоненту времени: год, месяц, день и так далее включая миллисекунды. Называются они довольно очевидно, так что я думаю вы догадаетесь или воспользуетесь переводчиком. В примере ниже структура со временем лежит в переменной tempTime. В отладчике она выглядит примерно так:
Обратите внимание, что все компоненты времени меняются от 0 до значения не превышающего следующую компоненту. Ну то есть часы от 0 до 23, минуты от 0 до 59. Миллисекунды от 0 до 999. Если сравнивать «в лоб», то возникнет проблема когда последнее обновление было в конце прошлой секунды, а текущее время — в текущей. У этого варианта есть два решения:
- Конвертировать полученную структуру в что-то вроде unix timestamp — одно число, начиная с «начала эпохи».
- Сделать хитрое сравнение.
Первый вариант может решить проблему только если я воспроизведу вычисление настоящего unix-timestamp, а мне лень. Например там будет сложность с количеством дней в месяце. Поэтому я сделаю менее точную версию, где «начало эпохи» будет в начале текущего месяца.
Код получается вот таким
При запуске получается примерно такой вывод:
Теперь добавляю дополнительную переменную для хранения времени предыдущего обновления (prevTime) и периода между обновлениями (delay).
Каждую итерацию цикла текущее время сравнивается со временем последнего обновления. Если разница больше задержки, то выполняется обновление. Дополнительно реализована функция для предупреждения перехода через ноль.
При запуске получается примерно такой вывод.
Таким образом команда вывода слова «tick» будет выполняться раз в 300 миллисекунд. После нее нужно помещать логику будущей игры.
Получение текущего времени std::chrono
Реализация с помощью std::chrono получилась значительно проще. Но в ней используется непривычное для обычных людей запись времени. Там нет количества часов с начала дня, минут с начала часа и секунд с начала минуты. С помощью std::chrono можно получить количество миллисекунд с «начала эпохи». То есть с 1 января 1970 года. Это получается огромное целое число. Например 1610827417491. В отличие от «количества миллисекунд с начала секунды», это число всегда увеличивается. Поэтому не возникнет необходимости беспокоиться о переполнении часов.
В этом варианте я также использовал другой способ сравнения времени. На одно арифметическое действие в цикле меньше. Мелочь, а приятно.
Константа PRIu64 нужна для форматированного вывода значения типа uint64_t с помощью printf. Для её использования нужно подключить inttypes.h Хотелось бы, конечно, не подключать лишних библиотек, но времени на обход именно этой библиотеки у меня не было.
Вывод получился вот таким
Что с этим можно сделать? Навскидку мне в голову пришло:
- Симуляция гравитации в платформере. Каждые Х миллисекунд персонаж должен падать на 1 символ.
- Полет пуль. Каждые Х миллисекунд передвинь пулю по направлению выстрела.
- Движение змейки. Каждые Х миллисекунд передвинь сегменты змейки на один символ от головы.
Что самое важное, у разных событий может быть разный период обновления. Вот прототип, где каждые 300 миллисекунд у персонажа уменьшается координата Х на 1, а каждые 225 миллисекунд — координата Y. При этом игрок может на WASD влиять на эти координаты гораздо чаще.

На картинке ниже вывод, который получается в такой программе время. Обратите внимание на чередование фраз «Each 225» и «Each 300».

Генератор случайных чисел
Если немного копнуть в алгоритм генераторов случайных чисел, то можно обнаружить, что они не такие уж случайные. Поэтому используют более точный термин Генератор псевдо-случайных чисел или ГПСЧ.
По запросу «с++ rand» и «cpp rand» можно найти довольно много материалов. Например обширную статью на русском https://en.cppreference.com/w/cpp/numeric/random/rand и чуть более сухую на английском https://en.cppreference.com/w/cpp/numeric/random/rand
Вкратце перескажу основные тезисы:
- Функция rand возвращает случайное число от 0 до RAND_MAX. Значение RAND_MAX зависит от библиотеки, но должно быть не менее 32767.
- Если просто вызывать функцию rand, то при разных запусках, последовательность случайных чисел будет одинаковой.
- Чтобы последовательность была каждый раз разной, нужно применить функцию srand.
Я от себя добавлю, что аргумент функции srand еще называют зерном (или «сидом» от слова seed). Вы подобное видели во многих играх при создании мира.
Примеры начальных значений для ГПСЧ

Если зерно мира заполнять одним и тем же значением, то получатся одинаковые последовательности случайных значений. В играх получатся одинаковые миры. Вот пример использования ГСПЧ с постоянным значением зерна.

При запуске у меня появляется фраза «first is 440 second is 19053». Независимо от количества запусков получаются одни и те же числа. У вас могут получиться другие числа, но от запуска к запуску они должны быть одинаковы.
Чтобы последовательность менялась от запуска к запуску нужно передавать такое зерно, которое будет всегда разное. Что у нас не повторяется при запуске одной и той же программы раз за разом?
Ответ — время запуска. Простейший способ задания зерна случайности — взять текущее время. Обычно во всяких примерах предлагают подключить библиотеку ctime и вызывать функцию std::time(nullptr). Этот вариант не устраивает меня потому что библиотека ctime больше ни для чего не используется. Зато при реализации периодических событий была подключена библиотека chrono. В примере ниже зерно мира задается текущим временем.
Я варварски сконвертировал uion64_t в int, но этого оказалось достаточно.
Следующая проблема в том, что числа получаются огромные. Повторюсь, что rand возвращает число от 0 до RAND_MAX, который у меня равен 0x7fff. В десятичной системе счисления диапазон случайных чисел получается от 0 до 32767.
Классический способ уменьшить этот диапазон — применить операцию «остаток от деления». Выражение выглядит как «std::rand() % boundary», где boundary — новое ограничение диапазона.
Вы когда-нибудь задумывались, а почему остаток от деления нам действительно помогает? Не получится ли такого, что какое-то число будет встречаться чаще других?
Строгое математическое доказательство будет чрезмерным для этой статьи. Поэтому постараюсь объяснить «на пальцах».
Посмотрите на два столбца чисел. Левый столбец — просто числа по порядку от 0 до 14. Правый столбец — остаток от деления числа из левого столбца на 4.
Как вы можете заметить, диапазон от 0 до 14 разделился на несколько отрезков от 0 до 3. Числа в правом столбце возрастают так же равномерно, как и в левом. Точно такая же закономерность прослеживается и на диапазоне от 0 до 32767. Функция rand() может вернуть каждое число «из левого столбца» с равной вероятностью. После нахождения остатка от деления мы получим соответствующее число из правого столбца.
Обратите внимание, что последний диапазон «от 0 до 3» в правом столбце не успел закончиться. Если числа в левом диапазоне будут выбираться с равной вероятностью, то числа 0, 1, 2 из правого столбца будут появляться чаще, чем число 3. То же самое будет и на полном масштабе, если 32767 не будет делиться нацело на выбранное вами ограничение. Впрочем, исходный диапазон рандома достаточно велик, чтобы мы не заметили этот небольшой недостаток. В документации на cppreference в формуле выбора числа есть попытка компенсировать его делением на «((RAND_MAX + 1u)/6)».
Как уже говорилось выше, рандом возвращает числа от 0 до ограничения. Проблема в том, что при написании игры нужно использовать числа не от 0, а например от 10 до 20. В реализации std::rand приходится упражняться со сложением и вычитанием. К счастью, я нашел ссылку на статью с описанием синтаксиса, который был добавлен в стандарт C++ от 2011 года. https://en.cppreference.com/w/cpp/numeric/random/uniformintdistribution В нем есть красивый способ описать рандом в нужном диапазоне. Немножко оформления и можно просто получать случайные числа в нужном диапазоне.
#include #include int main() < std::random_device rd;//Источник зерна для рандома std::mt19937 gen(rd());//Вихрь Мерсенна (Mersenne Twister) std::uniform_int_distribution<>oneToSix(1, 6);//функция распределения от 1 до 6 std::uniform_int_distribution<> twentyToForty(20, 40);//функция распределения от 20 до 40 //Два числа в диапазоне от 1 до 6 int first = oneToSix(gen); int second = oneToSix(gen); printf("%d %d ", first, second); //два числа в диапазоне от 20 до 40 int third = twentyToForty(gen); int fourth = twentyToForty(gen); printf("%d %d", third, fourth); return 0; >Подробнее о Вихре Мерсенна можно почитать на Википедии https://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%85%D1%80%D1%8C_%D0%9C%D0%B5%D1%80%D1%81%D0%B5%D0%BD%D0%BD%D0%B0
Меня такая находка очень порадовала. Пора переходить в туториалах для новичков на стандарт C++ хотя бы десятилетней давности. Этот фрагмент кода я специально для вас оформил текстом, который можно скопировать.
Изменение размера консоли
По умолчанию у меня в консоли помещается 80 символов в высоту и 50 в ширину. Это примерно треть моего экрана, поэтому захотелось увеличить количество символов в строке консоли хотя бы до 200.
Изменение размера окна консоли тоже оказалось задачкой с подвохом. Первый запрос был тривиальным «c++ change size of console window». Первый ответ на него подробно объяснял как сделать это с помощью настроек окна консоли на уровне операционной системы. То есть не из самой игры, а со стороны пользователя. Прикладывать эту инструкцию к игре я посчитал неправильным. Нужен способ сделать это из самой программы. Второй и последующие ответы описывали изменение размера окна консоли с помощью функции MoveWindow. Фактическое количество текста при этом не менялось. Если окно становилось слишком маленьким, то появлялись полосы прокрутки.
Следующая попытка была «c++ set console size». Два первых ответа вели на известные советы с функцией MoveWindow. Зато дальше пошли ссылки на документацию. А именно — на функцию SetConsoleScreenBufferSize. Судя по описанию, она меняет не размер видимого окна, а внутренний размер буфера. В качестве аргументов она принимает поток вывода и структуру с желаемыми размерами буфера.
На тот момент я не знал точно, какие размеры стандартные и что я могу туда поставить. Поэтому указал 20 на 20. Для проверки размеров окна я также вывел прямоугольник из цифр от 0 до 9 шириной 20 на 20. Получился вот такой код:
Вывод получился вот таким
Поскольку это работа на уровне WinAPI, в результате получился код ошибки. Я в основном работаю с java стеком и обычно вижу стектрейсы и тексты исключений. Несмотря на это, принцип решения проблемы не изменился. Для расшифровки кода ошибки нужно воспользоваться официальной документацией. Она легко ищется запросом «getlasterror error codes». Кодов ошибок описано около девяти тысяч на нескольких страницах. Для моего случая подойдет первая страница https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes—0-499-
Ошибка гласит ERROR-INVALID-PARAMETER 87 (0x57) The parameter is incorrect.
Маловато объяснений. Тогда я проверил как другие пишут этот код. Запрос «SetConsoleScreenBufferSize incorrect argument» привел меня вот на этот вопрос на SO https://stackoverflow.com/questions/12900713/reducing-console-size
В ключевых аспектах код ответа был похож на мой. Но в нем содержалось важное дополнение «If you call SetConsoleScreenBufferSize with illegal value in COORDS (e.g. too little height/width) then you get an error, usually 87 ‘invalid argument’.»
Потом я посмотрел в документацию к функции SetConsoleScreenBufferSize https://docs.microsoft.com/en-us/windows/console/setconsolescreenbuffersize и увидел что на размеры буфера наложены ограничения. Получается, что я передал слишком маленькие значения. У меня не было необходимости перебирать значения для получения точных минимальных размеров. В конце концов цель — увеличить размеры буфера, а не уменьшить. Поэтому показалось логичным отталкиваться от текущих размеров окна. Раз у нас есть функция SetЧтототам, значит должна быть и функция GetЧтототам. GetConsoleScreenBufferInfo действительно нашлась https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo С помощью неё и отладчика MSVS я выяснил, что размеры буфера на моей машине по умолчанию 80 на 50. Ширину я увеличил примерно в три раза, а высоту в полтора. При инициализации структуры size значением X = 200 и Y = 80 в высоту появились полосы прокрутки. Здесь и пригодилась функция MoveWindow.
Исходный код был видоизменен вот так:
Вывод при этом получился таким
Ускорение вывода текста
Запустив программу для заполнения большого экрана символами, я обнаружил, что текст пишется в консоль очень медленно. Заполнение символами экрана 200 на 80 заметно человеческому глазу. За одну секунду получится обновить экран лишь 1-2 раза. Это вряд ли связано с производительностью компьютера. Интуиция подсказывает, что это искусственное ограничение. При решении этой проблемы у меня было два направления поиска:
- Как быстро написать много текста?
- Как обновить только тот фрагмент экрана, который действительно менялся?
Сначала я поискал «cpp console reduce delay between screen updates». Практически все ссылки вели на советы по добавлению паузы, что мне совершенно не интересно. Только один ответ в выдаче говорил что-либо об ускорении вывода https://stackoverflow.com/questions/26376094/c-writing-to-console-without-delays. В нем предлагается подготовить большой буфер в памяти и вывести его одной командой.
Затем я поискал «windows.h write lot of text without animation» и нашел вот такой вопрос с очень любопытным ответом. https://stackoverflow.com/questions/34842526/update-console-without-flickering-c
Автор вопроса и автор ответа разговаривают в контексте создания консольной игры. Вместо полной очистки и полного заполнения экрана в ответе предлагается:
- Переставить курсор и писать новый текст поверх старого без предварительной очистки экрана.
- Переписывать фрагменты, а не отдельные буквы.
- Переписывать отдельные буквы на экране.
- Использовать два буфера и писать на экран только разницу.
Обратите внимание, в ответе на вопрос есть еще пример настройки цвета текста в консоли. У меня, к сожалению, не хватило времени воспроизвести этот прием.
Перестановка курсора
Простой перенос курсора в верхний левый угол экрана значительно улучшил ситуацию на windows 7. Я все еще видел процесс заполнения экрана, но текст на экране не исчезал два раза в секунду. У меня пропадали некоторые линии. У моего студента была windows 10 и без дополнительных ухищрений было видно только мигание самого курсора в разных частях экрана. Пропадания линий замечено не было.
За основу был взят код из главы «Изменение размера консоли».
Для своего удобства я реализовал функцию заполнения структуры нужными координатами.
Для перестановки курсора достаточно вызвать функцию SetConsoleCursorPosition и передать ей в качестве аргументов поток вывода и структуру с координатами желаемой позиции курсора.
Как я уже говорил, это улучшило ситуацию, но проблему полностью не решило. Поэтому я решил раскопать поглубже.
Вывод готовых фрагментов текста
Следующая попытка — писать символы не по одному, а строчками. Для этого нужно подготовить массив символов в памяти, который я далее буду писать как целую строку. Длина этого массива совпадает с шириной окна консоли. Во внутреннем цикле массив будет заполняться, а во внешнем — выводиться на экран. Ранее я выводил цифры с помощью форматированного вывода. Теперь нужно писать именно цифры. Для простой конвертации числа в символ нужно знать коды этих символов. У цифры «0» код символа 48, у цифры «9» — 57. То есть к числу от 0 до 9 включительно достаточно просто прибавить 48. Напомню, что последний символ в буфере обязательно должен быть «\0», чтобы не ловить баги, связанные с выводом нежелательных символов. За основу я взял код из раздела про изменение размера консоли.
В примерах еще часто использовался std::cout.flush(), который тащит за собой подключение iostream. Мне не хотелось использовать дополнительную библиотеку. Наверняка аналог есть и в stdio, который уже подключен. Мой запрос для поиска был «stdio flush output». Две ссылки на Stackoverflow указывают на fflush
- https://stackoverflow.com/questions/12450066/flushing-buffers-in-c/12450125
- https://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strin
Функция fflush вызывается с аргументом stdout. Я минут 10 искал как правильно заполнить переменную stdout, а оказалось она просто доступна из глобальной области видимости.
Настройка буферизации stdio.h
Подозреваю, что в stdio.h буферизация вывода уже реализована, поэтому мой код скорее всего оказался велосипедом. На момент оформления этой статьи я уже забыл как искал информацию. Главное — для настройки буфера с помощью stdio.h нужно воспользоваться функцией setvbuf. Она принимает stdout, буфер, какие-то флаги и число — размер буфера.
Измерение производительности
Тут я осознал, что не в состоянии сравнить производительность разных вариантов «на глаз». Поэтому вкрутил измерение времени до и после обновления экрана. Напишу тут порядок цифр, потому что значения с точностью до микросекунд не существенны для сравнения. Код практически полностью списал из ответа к этому вопросу https://stackoverflow.com/a/21856299
Само измерение — тривиально. Получаю текущее время до и после отрисовки экрана. Затем вычитаю и вывожу на экран. Есть способы лучше, но для моей задачи этого оказалось достаточно. Код ниже — развитие варианта с настройкой буфера с помощью stdio.h Код варианта с велосипедным буфером принципиально не отличался, поэтому не публикую его.
Измерил количество микросекунд без явного указания буфера. Три запуска, три числа: 8930000 8880000 9220000.
с размером буфера 1/16 от 740000 до 750000 микросекунд
с размером буфера 1/8 от 40000 до 39000 микросекунд
с размером буфера 1/4 от 18000 до 19000 микросекунд
с размером буфера 1/2 от 12000 до 13000 микросекунд
с размером буфера равным ширине консоли от 90000 до 10000.
Во всех этих случаях наблюдается мигание строк на экране размером с буфер. То есть при буфере 1/4, мигают фрагменты в четверть строки. С буфером равным ширине консоли получается очень большой разброс, причем без промежуточных значений. либо за 10000, либо за 90000. При этом мигает так, как будто буфер половина. redraw настройка буфера библиотеки.PNG
Велосипедный вариант с заполнением массива и выводом его на экран получился такой же, как с размером буфера равным ширине консоли. Были цифры 90000 и 10000 без промежуточных значений. При этом для создания буфера меньшего размера пришлось бы значительно усложнить реализацию. redraw велосипедный буфер.PNG
Чтобы применить технику двойной буферизации, нужно чтобы на экране что-нибудь менялось. При этом меняться должен не весь экран, а только некоторые части.
Заключение
К сожалению, новогодние праздники кончились и у меня сильно сократилось время на написание статьи. Вряд ли я когда либо еще плотно займусь этой темой. Поэтому я остановлюсь на достигнутом, чтобы не закопаться в перфекционизме.
В качестве бонуса — ссылка на подробный разбор вывода русского текста в консоль. https://ru.stackoverflow.com/questions/459154/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-%D1%8F%D0%B7%D1%8B%D0%BA-%D0%B2-%D0%BA%D0%BE%D0%BD%D1%81%D0%BE%D0%BB%D0%B8
P.S. Если вы нашли опечатки или ошибки в тексте, пожалуйста, сообщите мне. Это можно сделать выделив часть текста и нажав «Ctrl / ⌘ + Enter», если у вас есть Ctrl / ⌘, либо через личные сообщения. Если же оба варианта недоступны, напишите об ошибках в комментариях. Спасибо!