Интересный практический проект, собственная игра на C++ для портативной ретроконсоли!
В этой статье:
• Как организовать структуру небольшого игрового проекта
• Как устроить главный игровой цикл и переключение состояний
• Как разделить стартовый экран, игровой процесс и завершение игры
• Как собрать клон Google Dino и запустить его на реальном устройстве
Хороший способ посмотреть, как обычные классы, состояния и игровой цикл превращаются в законченную работающую игру.
📣 C++ Ready | #статья
В этой статье:
• Как организовать структуру небольшого игрового проекта
• Как устроить главный игровой цикл и переключение состояний
• Как разделить стартовый экран, игровой процесс и завершение игры
• Как собрать клон Google Dino и запустить его на реальном устройстве
Хороший способ посмотреть, как обычные классы, состояния и игровой цикл превращаются в законченную работающую игру.
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍6🔥3
Как безопасно удалять элементы из контейнера в C++20?
Частая ошибка удалять элементы прямо внутри range-for:
Проблема в том, что обход и изменение контейнера легко приводят к пропуску элементов, инвалидированным итераторам или слишком шумному коду.
В C++20 для этого есть std::erase_if:
После этого в vector останутся только нужные значения:
То же самое удобно работает и с ассоциативными контейнерами:
Здесь удалятся записи, где значение равно нулю, а код останется коротким и читаемым.
📣 C++ Ready | #совет
Частая ошибка удалять элементы прямо внутри range-for:
for (int x : values) {
if (x < 0) {
// удалить x?
}
}Проблема в том, что обход и изменение контейнера легко приводят к пропуску элементов, инвалидированным итераторам или слишком шумному коду.
В C++20 для этого есть std::erase_if:
std::vector<int> values{5, -1, 7, -3};
std::erase_if(values, [](int x) {
return x < 0;
});После этого в vector останутся только нужные значения:
// values: 5, 7
То же самое удобно работает и с ассоциативными контейнерами:
std::map<std::string, int> scores{
{"ann", 10},
{"bob", 0},
{"kim", 7}
};
std::erase_if(scores, [](const auto& item) {
return item.second == 0;
});Здесь удалятся записи, где значение равно нулю, а код останется коротким и читаемым.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥6👍3
Очень интересная статья о том, как автор пытался воссоздать Minecraft на C++ и Vulkan за одну неделю!
В этой статье:
• Как устроена идея voxel-мира и генерации чанков
• Почему Vulkan требует аккуратной работы с рендерингом и ресурсами
• Как маленький прототип постепенно превращается в собственный игровой движок
📣 C++ Ready | #статья
В этой статье:
• Как устроена идея voxel-мира и генерации чанков
• Почему Vulkan требует аккуратной работы с рендерингом и ресурсами
• Как маленький прототип постепенно превращается в собственный игровой движок
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥7👍5
Напоминалка по алгоритмам стандартной библиотеки C++!
Например, std::find_if помогает найти элемент по условию, std::sort сортирует диапазон, а std::transform удобно применять для преобразования значений без ручного цикла.
На картинке шпаргалка по STL-алгоритмам: поиск, сравнение диапазонов, сортировка, partition, rotate, copy, transform, remove, unique и числовые алгоритмы из <numeric>.
Сохрани, чтобы не потерять!
📣 C++ Ready | #ресурс
Например, std::find_if помогает найти элемент по условию, std::sort сортирует диапазон, а std::transform удобно применять для преобразования значений без ручного цикла.
На картинке шпаргалка по STL-алгоритмам: поиск, сравнение диапазонов, сортировка, partition, rotate, copy, transform, remove, unique и числовые алгоритмы из <numeric>.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤6👍4
Собираем маленький scope guard!
Иногда нужно выполнить действие при выходе из функции: закрыть временный режим, вернуть флаг обратно, удалить временный файл или откатить локальное состояние.
Писать cleanup руками в каждом return неудобно:
Если return станет больше, легко забыть один из вызовов disable().
Сделаем небольшой scope guard:
В деструкторе он выполнит переданную функцию:
Запретим копирование, чтобы cleanup не вызвался дважды:
Поля класса:
Теперь очистка привязана к области видимости:
disable вызовется и при обычном завершении, и при раннем return.
Тот же подход удобен для временных файлов:
И для восстановления старого значения:
Scope guard это простой способ сделать cleanup надёжным. Код меньше зависит от того, сколько return появится внутри функции.
📣 C++ Ready | #практика
Иногда нужно выполнить действие при выходе из функции: закрыть временный режим, вернуть флаг обратно, удалить временный файл или откатить локальное состояние.
Писать cleanup руками в каждом return неудобно:
void process() {
enable();
if (!check_input()) {
disable();
return;
}
run_job();
disable();
}Если return станет больше, легко забыть один из вызовов disable().
Сделаем небольшой scope guard:
#include <functional>
#include <utility>
class ScopeExit {
public:
explicit ScopeExit(std::function<void()> fn)
: fn_(std::move(fn)) {}
В деструкторе он выполнит переданную функцию:
~ScopeExit() {
if (fn_) {
fn_();
}
}Запретим копирование, чтобы cleanup не вызвался дважды:
ScopeExit(const ScopeExit&) = delete;
ScopeExit& operator=(const ScopeExit&) = delete;
Поля класса:
private:
std::function<void()> fn_;
};
Теперь очистка привязана к области видимости:
void process() {
enable();
ScopeExit guard([] {
disable();
});
if (!check_input()) {
return;
}
run_job();
}disable вызовется и при обычном завершении, и при раннем return.
Тот же подход удобен для временных файлов:
ScopeExit cleanup([&] {
std::filesystem::remove(path);
});И для восстановления старого значения:
bool old = config.debug;
config.debug = true;
ScopeExit restore([&] {
config.debug = old;
});
Scope guard это простой способ сделать cleanup надёжным. Код меньше зависит от того, сколько return появится внутри функции.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Интересная статья о том, как устроить свою SDL-подобную библиотеку на C++!
В этой статье:
• Как создать окно, renderer и базовый цикл обработки событий
• Как разделять платформозависимый код и общие интерфейсы
• Почему в графических библиотеках важны абстракции, совместимость и управление ресурсами
📣 C++ Ready | #статья
В этой статье:
• Как создать окно, renderer и базовый цикл обработки событий
• Как разделять платформозависимый код и общие интерфейсы
• Почему в графических библиотеках важны абстракции, совместимость и управление ресурсами
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥5👍3
Шпаргалка по special member functions в C++!
Например, если в классе объявить деструктор, компилятор уже не будет автоматически объявлять move constructor и move assignment. А если объявить move constructor, copy-операции могут стать deleted.
На картинке таблица правил генерации специальных методов класса: default constructor, destructor, copy constructor, copy assignment, move constructor и move assignment в зависимости от того, что уже объявил разработчик.
Сохрани, чтобы не потерять!
📣 C++ Ready | #ресурс
Например, если в классе объявить деструктор, компилятор уже не будет автоматически объявлять move constructor и move assignment. А если объявить move constructor, copy-операции могут стать deleted.
На картинке таблица правил генерации специальных методов класса: default constructor, destructor, copy constructor, copy assignment, move constructor и move assignment в зависимости от того, что уже объявил разработчик.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤5👍5
Собираем типобезопасные флаги через enum class!
Иногда нужно хранить набор прав или режимов: чтение, запись, выполнение, логирование, debug-режимы. Часто для этого используют обычные int-флаги, но с ними легко случайно смешать разные значения.
Начнём с enum class:
Сам по себе enum class не умеет складываться через
Теперь можно собрать набор прав:
Чтобы проверять наличие флага, сделаем маленькую функцию:
Использование получается читаемым:
Можно добавить и удаление флага:
Например, забрать право записи:
Такой подход удобен для прав доступа, настроек движка, режимов логирования, feature flags и любых случаев, где несколько булевых значений логически относятся к одному набору.
📣 C++ Ready | #практика
Иногда нужно хранить набор прав или режимов: чтение, запись, выполнение, логирование, debug-режимы. Часто для этого используют обычные int-флаги, но с ними легко случайно смешать разные значения.
Начнём с enum class:
enum class Permission : unsigned {
Read = 1 << 0,
Write = 1 << 1,
Exec = 1 << 2
};Сам по себе enum class не умеет складываться через
|, поэтому добавим оператор:Permission operator|(Permission a, Permission b) {
return static_cast<Permission>(
static_cast<unsigned>(a) | static_cast<unsigned>(b)
);
}Теперь можно собрать набор прав:
auto user = Permission::Read | Permission::Write;
Чтобы проверять наличие флага, сделаем маленькую функцию:
bool has(Permission value, Permission flag) {
return (
static_cast<unsigned>(value) &
static_cast<unsigned>(flag)
) != 0;
}Использование получается читаемым:
if (has(user, Permission::Write)) {
std::cout << "Можно изменять данные\n";
}Можно добавить и удаление флага:
Permission remove(Permission value, Permission flag) {
return static_cast<Permission>(
static_cast<unsigned>(value) & ~static_cast<unsigned>(flag)
);
}Например, забрать право записи:
user = remove(user, Permission::Write);
Такой подход удобен для прав доступа, настроек движка, режимов логирования, feature flags и любых случаев, где несколько булевых значений логически относятся к одному набору.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍7🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Этот репозиторий помогает писать более безопасный, понятный и удобный для поддержки код. Здесь объясняется, как правильно использовать инструменты разработки, оформлять проекты, избегать распространённых ошибок и учитывать производительность, переносимость и многопоточность.
Оставляю ссылочку: GitHub
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍10🔥4
Интересная статья о том, как начать делать игру на C++ с помощью SFML!
В этой статье:
• Как создать базовое окно и подключить SFML к проекту
• Как отрисовать персонажа и подготовить первые игровые объекты
• С чего начинается простая 2D-игра на C++ без тяжёлого движка
📣 C++ Ready | #статья
В этой статье:
• Как создать базовое окно и подключить SFML к проекту
• Как отрисовать персонажа и подготовить первые игровые объекты
• С чего начинается простая 2D-игра на C++ без тяжёлого движка
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥7❤3😁1
Когда в C++ помогает std::exchange?
Иногда нужно забрать старое значение переменной и сразу записать новое. Часто это делают в две строки:
Работает, но намерение размазано, сначала читаем, потом отдельно меняем.
В C++ есть std::exchange:
Он возвращает старое значение, а в переменную кладёт новое.
Это удобно для флагов:
Теперь сразу понятно, мы выключили running и сохранили, был ли он включён раньше.
Ещё пример, забрать накопленный буфер:
Метод возвращает старый buffer, а внутри объекта оставляет пустую строку.
📣 C++ Ready | #совет
Иногда нужно забрать старое значение переменной и сразу записать новое. Часто это делают в две строки:
auto old = state;
state = State::Closed;
Работает, но намерение размазано, сначала читаем, потом отдельно меняем.
В C++ есть std::exchange:
auto old = std::exchange(state, State::Closed);
Он возвращает старое значение, а в переменную кладёт новое.
Это удобно для флагов:
bool wasRunning = std::exchange(running, false);
Теперь сразу понятно, мы выключили running и сохранили, был ли он включён раньше.
Ещё пример, забрать накопленный буфер:
std::string takeBuffer() {
return std::exchange(buffer, {});
}Метод возвращает старый buffer, а внутри объекта оставляет пустую строку.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Этот репозиторий представляет собой подробное руководство по современным практикам разработки на C++. В нём собраны рекомендации по организации проектов, стилю кода, оптимизации производительности, тестированию, работе с инструментами анализа и другим аспектам профессиональной разработки.
Оставляю ссылочку: GitHub
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍8🔥5
Шпаргалка по random distributions в C++!
Например, std::uniform_int_distribution подходит для случайных целых чисел в диапазоне, а std::normal_distribution для значений с нормальным распределением.
На картинке распределения из <random>: uniform, bernoulli, binomial, geometric, normal, lognormal, poisson, exponential, gamma, weibull и другие варианты с формулами и графиками.
Сохрани, чтобы не потерять!
📣 C++ Ready | #ресурс
Например, std::uniform_int_distribution подходит для случайных целых чисел в диапазоне, а std::normal_distribution для значений с нормальным распределением.
На картинке распределения из <random>: uniform, bernoulli, binomial, geometric, normal, lognormal, poisson, exponential, gamma, weibull и другие варианты с формулами и графиками.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤11🔥5🤝1
Собираем проверку расширений файлов на C++!
Иногда нужно быстро отфильтровать файлы: взять только
Подключим нужные заголовки:
Функция будет принимать путь и расширение:
Обойдём папку рекурсивно:
Проверим расширение:
Теперь можно найти все JSON-файлы:
Если папки нет,
std::filesystem позволяет писать маленькие файловые утилиты без платформенного кода. Такой фильтр легко расширить под поиск логов, конфигов, бэкапов или временных файлов.
📣 C++ Ready | #практика
Иногда нужно быстро отфильтровать файлы: взять только
.log, .json, .txt или изображения. Сделаем небольшую функцию на std::filesystem, которая проходит по папке и возвращает файлы с нужным расширением.Подключим нужные заголовки:
#include <filesystem>
#include <string>
#include <vector>
namespace fs = std::filesystem;
Функция будет принимать путь и расширение:
std::vector<fs::path> find_files(
const fs::path& root,
std::string_view extension
) {
std::vector<fs::path> result;
Обойдём папку рекурсивно:
for (const auto& entry : fs::recursive_directory_iterator(root)) {
if (!entry.is_regular_file()) {
continue;
}Проверим расширение:
if (entry.path().extension() == extension) {
result.push_back(entry.path());
}
}
return result;
}Теперь можно найти все JSON-файлы:
auto files = find_files("configs", ".json");
for (const auto& file : files) {
std::cout << file << '\n';
}Если папки нет,
recursive_directory_iterator может бросить исключение. Для утилит это нормально обработать явно:try {
auto files = find_files("configs", ".json");
} catch (const fs::filesystem_error& error) {
std::cerr << error.what() << '\n';
}std::filesystem позволяет писать маленькие файловые утилиты без платформенного кода. Такой фильтр легко расширить под поиск логов, конфигов, бэкапов или временных файлов.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍5🔥2
Разберём std::filesystem: 7 операций для работы с файлами и папками в C++!
std::filesystem помогает писать файловые утилиты без платформенного кода: собирать пути, проверять файлы, создавать папки, обходить директории и аккуратно чистить временные данные. Эта шпора пригодится для CLI-инструментов, логов, конфигов, сборочных скриптов и backend-задач на C++.
📣 C++ Ready | #шпора
std::filesystem помогает писать файловые утилиты без платформенного кода: собирать пути, проверять файлы, создавать папки, обходить директории и аккуратно чистить временные данные. Эта шпора пригодится для CLI-инструментов, логов, конфигов, сборочных скриптов и backend-задач на C++.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤2👍2