Czy warto uczyć się C++ w 2026 roku? Jest to główny język gier wideo na konsolach i PC. Nie działają dobrze w nim moduły i tylko GCC 16.2 je wspiera.
Wspiera tlyko w małych programach, gdzie jest 2-3 pliki ale gdy jest więcej to już się nie skompiluje. Moim zdaniem moduły w C++ są źle zaprojektowane.
Zwłaszcza że Rust jest mało popularnym i lubianym następcą i w jego skrzyniach pojawia się szkodliwe oprogramowanie.
https://safedep.io/arrayref-proc-macro1-rust-build-time-malware/
Nauka C++ w 2026
Wątek przeniesiony 2026-08-22 07:56 z Kariera przez pradoslaw.
- Rejestracja: dni
- Ostatnio: dni
- Postów: 8
- Rejestracja: dni
- Ostatnio: dni
- Lokalizacja: Silesia/Marki
- Postów: 5698
LibidoDominandi napisał(a):
Czy warto uczyć się C++ w 2026 roku?
Nie warto się uczyć w ogóle, ej aj wszystko zrobi lepiej
Jest to główny język gier wideo na konsolach i PC.
no to w takim razie warto jak chcesz robić gry wideo czy na pc
Nie działają dobrze w nim moduły i tylko GCC 16.2 je wspiera.
ale konkretnie 16.2 i żaden inny czy 16.2+? może nie jest to nikomu do niczego potrzebne. a może twórcy kompilatorów pracują za wolno i trzeba by ich z batem pogonić
Wspiera tlyko w małych programach, gdzie jest 2-3 pliki ale gdy jest więcej to już się nie skompiluje. Moim zdaniem moduły w C++ są źle zaprojektowane.
tu potrzebny jest ekspert jakiś od c++ niestety
Zwłaszcza że Rust
zwłaszcza sugeruje jakiś związek logiczny, tylko nie wiem czy do tych modułów w C++ czy do gier czy do czego
jest mało popularnym
to nie dobrze
i lubianym następcą
to dobrz jak jest lubianym, no chyba iż miało być "mało lubianym". ja tam lubię rust, ale to może dlatego że nigdy nic nie pisałem komercyjnego w c++
i w jego skrzyniach pojawia się szkodliwe oprogramowanie.
heh, no niestety wszędzie się pojawiają https://www.facebook.com/groups/egyptian.geeks/posts/8608815492491416/
tak więc nie do końca wiem czy to jest pytanie
- czy warto uczyć się C++ do pisania gier, pewnie tak
- warto się uczyć C++ do ogólnych programów, pewnie nie. C++ króluje w grach i embedded
- ukryty hejt na Rust bo ci się nudzi
- Rejestracja: dni
- Ostatnio: dni
- Postów: 8
Mam taki prosty program z modułami w C++. I on się skompiluje pod GCC 16, ale jak jest większy program z modułami to już nie da rady go skompilować. Po prostu kompilatory nie wspierają tego.
Burrito.cpp
module Burrito;
import std;
Burrito::Burrito()
{
std::println("i am a orange");
}
Burrito.h
#ifndef BURRITO_H
#define BURRITO_H
class BURRITO_H {
public:
Burrito();
protected:
private:
};
#endif // BURRITO_H
main.cpp
import std;
import Burrito;
int main()
{
Burrito bo;
}
g++-16 -std=c++26 -fmodules --compile-std-module ../Burrito.ixx ../Burrito.cpp
Do pisania gier też w niedalekiej przyszłości C++ UE5 może być zastąpiony meta językiem VERSE.
https://www.reddit.com/r/gamedev/comments/1u8xp4t/all_things_about_ue6_announced_yesterday/
Niektórzy programiści twierdzą że zwykłe C++ jest o wiele łatwiejszy w obsłudze niż wersja UE5 C++.
Także w grach nikt nie wiąże przyszłości z językiem Rust, wolą dodawać do swoich silników zupełnie nowe autorskie języki programowania.
- Rejestracja: dni
- Ostatnio: dni
- Lokalizacja: Kraków
- Postów: 66
Nie wiem do czego zmierzasz. Podany przykład działa na GCC 16.1.
burrito.cppm
export module Burrito;
import std;
export class Burrito
{
public:
Burrito();
};
burrito.cpp
module Burrito;
Burrito::Burrito()
{
std::println("i am an orange");
}
main.cpp
import std;
import Burrito;
int main()
{
Burrito bo;
}
g++ -std=c++26 -fmodules --compile-std-module -c -x c++ burrito.cppm
g++ -std=c++26 -fmodules -c burrito.cpp
g++ -std=c++26 -fmodules -c main.cpp
g++ burrito.o main.o -o burrito
$ ./burrito
i am an orange
g++ (GCC) 16.1.1 20260728
- Rejestracja: dni
- Ostatnio: dni
- Postów: 8
No zmierzam do tego tak jak napisałem, że gdy mam więcej plików moduły już nie działają.
Enemy.cpp
module Enemy;
import std;
void Enemy::createEnemy(int type)
{
if (type == 0) {
name = "Zombie";
maxHealth = 50;
damage = 5;
}
else if (type == 1) {
name = "Szkieletor";
maxHealth = 35;
damage = 8;
}
else if (type == 2) {
name = "Goblinek";
maxHealth = 30;
damage = 10;
}
else {
name = "Zombiak";
maxHealth = 50;
damage = 5;
}
currentHealth = maxHealth;
}
void Enemy::SetLevel(int enemyLevel)
{
level = enemyLevel;
maxHealth += (level - 1) * 10;
damage += (level - 1) * 2;
currentHealth = maxHealth;
}
Enemy.ixx
export module Enemy;
import std;
export class Enemy {
public:
std::string name;
int maxHealth = 0;
int currentHealth = 0;
int level = 1;
int damage = 0;
void createEnemy(int type);
void SetLevel(int enemyLevel);
};
Game.cpp
module Game;
import std;
import Enemy;
import Player;
import Print;
enum class GameState {
MainMenu,
CharacterSelect,
Playing,
Exit
};
static int GetEnemyCount()
{
int roll = std::rand() % 100;
if (roll < 50) return 1;
else if (roll < 75) return 2;
else if (roll < 90) return 3;
else return 4;
}
static int GetEnemyType() { return std::rand() % 4; }
static int GetEnemyLevel() { return 1 + std::rand() % 3; }
void RunGame()
{
Player player;
std::srand(std::time(0));
bool running = true;
GameState state = GameState::MainMenu;
while (running)
{
if (state == GameState::MainMenu)
{
Print::WriteColor("====UpiornePotyczki====\n\n", Color::Red);
Print::Write("1. Nowa Gra\n");
Print::Write("2. Wyjście\n");
int choice;
std::cin >> choice;
state = (choice == 1) ? GameState::CharacterSelect : GameState::Exit;
Print::Clear();
}
else if (state == GameState::CharacterSelect)
{
Print::WriteColor("Wybierz klasę:\n\n", Color::Yellow);
Print::Write("1. Wojownik\n2. Łotrzyk\n3. Mag\n");
int choice;
std::cin >> choice;
player.SetClass(choice);
player.DisplayPickedCharacter();
int tmp;
std::cin >> tmp;
Print::Clear();
state = GameState::Playing;
}
else if (state == GameState::Playing)
{
std::vector<Enemy> enemies;
int enemyCount = GetEnemyCount();
for (int i = 0; i < enemyCount; i++)
{
Enemy e;
e.createEnemy(GetEnemyType());
e.SetLevel(GetEnemyLevel());
enemies.push_back(e);
}
bool inCombat = true;
while (inCombat)
{
Print::Clear();
Print::WriteColor("Pojawiają się przeciwnicy!\n\n", Color::Red);
for (int i = 0; i < enemies.size(); i++)
{
Print::Write("Przeciwnik " + std::to_string(i + 1) + ": ");
Print::WriteColor(enemies[i].name, Color::Yellow);
Print::Write("\n");
}
int choice;
std::cin >> choice;
if (choice == 2)
inCombat = false;
}
state = GameState::Exit;
}
else
{
running = false;
}
}
Print::WriteColor("\nKoniec gry\n", Color::Magenta);
}
Game.ixx
export module Game;
import std;
import Enemy;
import Player;
import Print;
export void RunGame();
Player.ixx
export module Player;
import std;
import Print;
export class Player {
public:
std::string name;
std::string playerClass;
int strength = 0;
int intelligence = 0;
int vitality = 0;
int maxHealth = 0;
int currentHealth = 0;
int maxMana = 0;
int currentMana = 0;
int damage = 0;
void SetClass(int choice);
void CalculateStats();
void DisplayPickedCharacter();
};
Print.cpp
module Print;
import std;
void Print::Write(const std::string& text)
{
std::print("{}", text);
}
void Print::SetColor(Color color)
{
switch (color) {
case Color::Red: std::print("\033[31m"); break;
case Color::Green: std::print("\033[32m"); break;
case Color::Yellow: std::print("\033[33m"); break;
case Color::Cyan: std::print("\033[36m"); break;
case Color::Blue: std::print("\033[34m"); break;
case Color::Magenta: std::print("\033[35m"); break;
case Color::White: std::print("\033[37m"); break;
case Color::Black: std::print("\033[30m"); break;
default: std::print("\033[0m"); break;
}
}
void Print::WriteColor(const std::string& text, Color color)
{
SetColor(color);
std::print("{}", text);
SetColor(Color::Default);
}
void Print::Clear()
{
std::system("clear");
}
Print.ixx
export module Print;
import std;
export enum class Color {
Default, Red, Green, Yellow, Cyan, Blue, Magenta, White, Black
};
export class Print {
public:
static void Write(const std::string& text);
static void SetColor(Color color);
static void WriteColor(const std::string& text, Color color);
static void Clear();
};
main.cpp
import Game;
int main()
{
RunGame();
}
Makefile
CXX = g++-16
CXXFLAGS = -std=c++26 -fmodules -O2 -Wall
TARGET = game
# moduły
MODS = Print.ixx Enemy.ixx Player.ixx Game.ixx
MOD_OBJS = Print.o Enemy.o Player.o Game.o
# main
MAIN = main.o
all: $(TARGET)
# --- moduły ---
Print.o: Print.ixx
$(CXX) $(CXXFLAGS) -c Print.ixx -o Print.o
Enemy.o: Enemy.ixx
$(CXX) $(CXXFLAGS) -c Enemy.ixx -o Enemy.o
Player.o: Player.ixx
$(CXX) $(CXXFLAGS) -c Player.ixx -o Player.o
Game.o: Game.ixx
$(CXX) $(CXXFLAGS) -c Game.ixx -o Game.o
# --- main ---
main.o: main.cpp
$(CXX) $(CXXFLAGS) -c main.cpp -o main.o
# --- linkowanie ---
$(TARGET): $(MOD_OBJS) $(MAIN)
$(CXX) $(CXXFLAGS) $^ -o $(TARGET)
run: $(TARGET)
./$(TARGET)
clean:
rm -f *.o $(TARGET)
CMakeLists.txt
cmake_minimum_required(VERSION 3.28)
project(UpiornePotyczki LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_compile_options(-fmodules)
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
add_executable(game
main.cpp
)
target_sources(game PRIVATE
Game.ixx
Player.ixx
Enemy.ixx
Print.ixx
)
"cpp": "clear && cd $dir && mkdir -p bin && cd bin && g++-16 -std=c++26 -fmodules --compile-std-module ../Print.ixx ../Enemy.ixx ../Player.ixx ../Game.ixx ../Print.cpp ../Enemy.cpp ../Game.cpp ../main.cpp -o game && ./game"
Mam błąd przy poleceniu make
Bash (Shell)
Opcje:
rm -rf build
mkdir build
cd build
cmake -DCMAKE_CXX_COMPILER=g++-16 -DCMAKE_CXX_STANDARD=26 ..
make
make
[ 16%] Building CXX object CMakeFiles/game.dir/main.cpp.o
In module imported at /home/user/UpiornePotyczki/main.cpp:1:1:
Game: error: failed to read compiled module: Nie ma takiego pliku ani katalogu
Game: note: compiled module file is ‘gcm.cache/Game.gcm’
Game: note: imports must be built before being imported
Game: fatal error: returning to the gate for a mechanical issue
compilation terminated.
make[2]: *** [CMakeFiles/game.dir/build.make:79: CMakeFiles/game.dir/main.cpp.o] Błąd 1
make[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/game.dir/all] Błąd 2
make: *** [Makefile:91: all] Błąd 2
GCC musi wygenerować .gcm zanim skompiluje main.cpp ale Game.ixx NIE został jeszcze zbudowany jako .gcm
- Rejestracja: dni
- Ostatnio: dni
- Lokalizacja: Kraków
- Postów: 66
Ten kod też się kompiluje i uruchamia. Jest jeden problem. Funkcje klasy Player nie są zdefiniowane. 'Poprawiony' (tj. puste definicje) nie wydaję się działać poprawnie. W tej chwili nie mam ochoty na szukanie przyczyny.
Nie chciało mi się też sprawdzać Makefile i CMakeLists więc użyłem mojej ulubionej metody.
g++ -std=c++26 -fmodules --compile-std-module -c -x c++ enemy.cppm
g++ -std=c++26 -fmodules -c -x c++ print.cppm
g++ -std=c++26 -fmodules -c -x c++ player.cppm
g++ -std=c++26 -fmodules -c -x c++ game.cppm
g++ -std=c++26 -fmodules -c print.cpp
g++ -std=c++26 -fmodules -c enemy.cpp
g++ -std=c++26 -fmodules -c game.cpp
g++ -std=c++26 -fmodules -c main.cpp
g++ print.o player.o enemy.o game.o main.o -o itworks
- Rejestracja: dni
- Ostatnio: dni
- Postów: 8
Teraz działa! Wtedy miałem GCC 16.1 a teraz mam GCC 16.2. usunałem pliki Game, poprawiłem plik Makefile i dodałem Linkowanie.
Enemy.cpp
module Enemy;
import std;
void Enemy::createEnemy(int type)
{
if (type == 0) {
name = "Zombie";
maxHealth = 50;
damage = 5;
}
else if (type == 1) {
name = "Szkieletor";
maxHealth = 35;
damage = 8;
}
else if (type == 2) {
name = "Goblinek";
maxHealth = 30;
damage = 10;
}
else {
name = "Zombiak";
maxHealth = 50;
damage = 5;
}
currentHealth = maxHealth;
}
void Enemy::SetLevel(int enemyLevel)
{
level = enemyLevel;
maxHealth += (level - 1) * 10;
damage += (level - 1) * 2;
currentHealth = maxHealth;
}
Enemy.ixx
export module Enemy;
import std;
export class Enemy {
public:
std::string name;
int maxHealth = 0;
int currentHealth = 0;
int level = 1;
int damage = 0;
void createEnemy(int type);
void SetLevel(int enemyLevel);
};
Player.cpp
module Player;
import std;
import Print;
void Player::SetClass(int choice)
{
name = "Bohater";
if (choice == 1) {
playerClass = "Wojownik";
strength = 10;
intelligence = 2;
vitality = 8;
}
else if (choice == 2) {
playerClass = "Łotrzyk";
strength = 6;
intelligence = 5;
vitality = 6;
}
else if (choice == 3) {
playerClass = "Mag";
strength = 2;
intelligence = 10;
vitality = 5;
}
CalculateStats();
}
void Player::CalculateStats()
{
int baseHealth = 50;
int baseMana = 30;
// Obliczanie zdrowia
maxHealth = baseHealth + (vitality * 10);
currentHealth = maxHealth;
// Obliczanie many
maxMana = baseMana + (intelligence * 10);
currentMana = maxMana;
// Obliczanie obrażeń w zależności od klasy
if (playerClass == "Wojownik") {
damage = strength * 2;
}
else if (playerClass == "Łotrzyk") {
damage = strength * 2 + 2;
}
else if (playerClass == "Mag") {
damage = intelligence * 2;
}
else {
damage = 5;
}
}
void Player::DisplayPickedCharacter()
{
Print::Clear();
Print::Write("Wybrałeś ");
Print::WriteColor(playerClass, Color::Cyan);
Print::Write("!\n\n");
Print::Write("Siła: ");
Print::Write(std::to_string(strength) + "\n");
Print::Write("Inteligencja: ");
Print::Write(std::to_string(intelligence) + "\n");
Print::Write("Witalność: ");
Print::Write(std::to_string(vitality) + "\n");
Print::Write("Zdrowie: ");
Print::WriteColor(std::to_string(currentHealth), Color::Green);
Print::Write("\n");
Print::Write("Mana: ");
Print::WriteColor(std::to_string(currentMana), Color::Cyan);
Print::Write("\n");
Print::Write("\nNaciśnij 1, aby kontynuować: ");
}
Player.ixx
export module Player;
import std;
import Print;
export class Player {
public:
std::string name;
std::string playerClass;
int strength = 0;
int intelligence = 0;
int vitality = 0;
int maxHealth = 0;
int currentHealth = 0;
int maxMana = 0;
int currentMana = 0;
int damage = 0;
void SetClass(int choice);
void CalculateStats();
void DisplayPickedCharacter();
};
Print.cpp
module Print;
import std;
void Print::Write(const std::string& text)
{
std::print("{}", text);
}
void Print::SetColor(Color color)
{
switch (color) {
case Color::Red: std::print("\033[31m"); break;
case Color::Green: std::print("\033[32m"); break;
case Color::Yellow: std::print("\033[33m"); break;
case Color::Cyan: std::print("\033[36m"); break;
case Color::Blue: std::print("\033[34m"); break;
case Color::Magenta: std::print("\033[35m"); break;
case Color::White: std::print("\033[37m"); break;
case Color::Black: std::print("\033[30m"); break;
default: std::print("\033[0m"); break;
}
}
void Print::WriteColor(const std::string& text, Color color)
{
SetColor(color);
std::print("{}", text);
SetColor(Color::Default);
}
void Print::Clear()
{
std::system("clear");
}
Print.ixx
export module Print;
import std;
export enum class Color {
Default, Red, Green, Yellow, Cyan, Blue, Magenta, White, Black
};
export class Print {
public:
static void Write(const std::string& text);
static void SetColor(Color color);
static void WriteColor(const std::string& text, Color color);
static void Clear();
};
main.cpp
import std;
import Enemy;
import Player;
import Print;
// Stany gry
enum class GameState {
MainMenu,
CharacterSelect,
Playing,
Exit
};
// Losuje liczbę przeciwników
int GetEnemyCount()
{
int roll = std::rand() % 100;
if (roll < 50) {
return 1;
}
else if (roll < 75) {
return 2;
}
else if (roll < 90) {
return 3;
}
else {
return 4;
}
}
// Losuje typ przeciwnika
int GetEnemyType()
{
return std::rand() % 4;
}
// Losuje poziom przeciwnika
int GetEnemyLevel()
{
return 1 + std::rand() % 3;
}
Player player;
int main()
{
std::srand(std::time(0));
bool running = true;
GameState state = GameState::MainMenu;
while (running) {
if (state == GameState::MainMenu) {
Print::WriteColor("====UpiornePotyczki====\n\n", Color::Red);
Print::Write("1. Nowa Gra\n");
Print::Write("2. Wyjście\n");
Print::WriteColor("\nWybierz opcję: ", Color::Yellow);
int choice;
std::cin >> choice;
if (choice == 1) {
state = GameState::CharacterSelect;
}
if (choice == 2) {
state = GameState::Exit;
}
Print::Clear();
}
else if (state == GameState::CharacterSelect) {
Print::WriteColor("Wybierz swoją klasę:\n\n", Color::Yellow);
Print::Write("1. Wojownik\n");
Print::Write("2. Łotrzyk\n");
Print::Write("3. Mag\n");
Print::WriteColor("\nWybierz opcję: ", Color::Yellow);
int choice;
std::cin >> choice;
player.SetClass(choice);
player.DisplayPickedCharacter();
int continueChoice;
std::cin >> continueChoice;
Print::Clear();
state = GameState::Playing;
}
else if (state == GameState::Playing) {
std::vector<Enemy> enemies;
int enemyCount = GetEnemyCount();
// Tworzenie przeciwników
for (int i = 0; i < enemyCount; i++) {
Enemy enemy;
int type = GetEnemyType();
int level = GetEnemyLevel();
enemy.createEnemy(type);
enemy.SetLevel(level);
enemies.push_back(enemy);
}
bool inCombat = true;
// Pętla walki
while (inCombat) {
Print::Clear();
Print::WriteColor("Pojawiają się przeciwnicy!\n\n", Color::Red);
for (int i = 0; i < enemies.size(); i++) {
Print::Write("Przeciwnik " + std::to_string(i + 1) + ": ");
Print::WriteColor(enemies[i].name, Color::Yellow);
Print::Write(" Poziom. " + std::to_string(enemies[i].level));
Print::Write(" - Zdrowie: " + std::to_string(enemies[i].currentHealth) + "\n");
}
Print::Write("\nTwoje zdrowie: ");
Print::WriteColor(std::to_string(player.currentHealth), Color::Green);
Print::Write("\n");
Print::Write("\n1. Atak\n");
Print::Write("2. Ucieczka\n");
Print::WriteColor("\nWybierz opcję: ", Color::Yellow);
int choice;
std::cin >> choice;
if (choice == 1) {
Print::WriteColor("\nWybierz przeciwnika do ataku: ", Color::Yellow);
int targetChoice;
std::cin >> targetChoice;
int targetIndex = targetChoice - 1;
if (targetIndex >= 0 && targetIndex < enemies.size()) {
enemies[targetIndex].currentHealth -= player.damage;
Print::Write("\nUderzasz ");
Print::WriteColor(enemies[targetIndex].name, Color::Yellow);
Print::Write(" za " + std::to_string(player.damage) + " obrażeń!\n");
if (enemies[targetIndex].currentHealth <= 0) {
Print::WriteColor("\n" + enemies[targetIndex].name + " został pokonany!\n", Color::Green);
enemies.erase(enemies.begin() + targetIndex);
}
if (enemies.empty()) {
Print::WriteColor("\nWygrałeś walkę!\n", Color::Green);
inCombat = false;
}
else {
Print::Write("\nPrzeciwnicy atakują!\n");
for (int i = 0; i < enemies.size(); i++) {
player.currentHealth -= enemies[i].damage;
if (player.currentHealth < 0) {
player.currentHealth = 0;
inCombat = false;
state = GameState::Exit;
Print::Write("\nZostałeś pokonany\n");
break;
}
Print::WriteColor(enemies[i].name, Color::Red);
Print::Write(" uderza cię za " + std::to_string(enemies[i].damage) + " obrażeń!\n");
if (player.currentHealth <= 0) {
Print::WriteColor("\nZostałeś pokonany..\n", Color::Red);
inCombat = false;
state = GameState::Exit;
break;
}
}
}
}
else {
Print::Write("\nNieprawidłowy cel!\n");
}
}
else if (choice == 2) {
Print::WriteColor("\nUciekasz jak tchórz!\n", Color::Yellow);
inCombat = false;
}
else {
Print::Write("\nNieprawidłowy wybór!\n");
}
}
state = GameState::Exit;
}
else if (state == GameState::Exit) {
running = false;
}
}
Print::WriteColor("\n************************************\n", Color::Magenta);
Print::WriteColor("* Dzięki za grę w UpiornePotyczki! *\n", Color::Magenta);
Print::WriteColor("************************************\n", Color::Magenta);
}
Makefile
CXX = g++-16
CXXFLAGS = -std=c++26 -fmodules
TARGET = game
.PHONY: all clean run std-module
all: $(TARGET)
# =========================
# Standard library
# =========================
std-module:
$(CXX) $(CXXFLAGS) --compile-std-module -c
# =========================
# Print
# =========================
Print.ixx.gcm: Print.ixx std-module
$(CXX) $(CXXFLAGS) -c Print.ixx
Print.o: Print.cpp Print.ixx.gcm
$(CXX) $(CXXFLAGS) -c Print.cpp -o Print.o
# =========================
# Enemy
# =========================
Enemy.ixx.gcm: Enemy.ixx std-module
$(CXX) $(CXXFLAGS) -c Enemy.ixx
Enemy.o: Enemy.cpp Enemy.ixx.gcm
$(CXX) $(CXXFLAGS) -c Enemy.cpp -o Enemy.o
# =========================
# Player
# =========================
Player.ixx.gcm: Player.ixx Print.ixx.gcm std-module
$(CXX) $(CXXFLAGS) -c Player.ixx
Player.o: Player.cpp Player.ixx.gcm
$(CXX) $(CXXFLAGS) -c Player.cpp -o Player.o
# =========================
# main
# =========================
main.o: main.cpp
$(CXX) $(CXXFLAGS) -c main.cpp -o main.o
# =========================
# Linkowanie
# =========================
$(TARGET): Print.o Enemy.o Player.o main.o
$(CXX) $^ -o $@
# =========================
# Uruchamianie
# =========================
run: $(TARGET)
./$(TARGET)
# =========================
# Czyszczenie
# =========================
clean:
rm -rf *.o $(TARGET) gcm.cache
rm -f *.ixx.gcm
README.md
# Projekt C++ – Makefile i moduły C++26
Projekt jest przykładową aplikacją C++ wykorzystującą **moduły C++20/C++26 (`.ixx`)** oraz moduł standardowej biblioteki `std`. Do kompilacji wykorzystywany jest **GCC 16.2**.
## Wymagania
Do poprawnej kompilacji projektu wymagane są:
* **GCC 16.2** (`g++-16`)
* Obsługa standardu **C++26**
* Obsługa modułów C++
* Program **GNU Make**
* System **Linux**
Sprawdzenie wersji kompilatora:
```bash
g++-16 --version
Przykładowy wynik:
g++-16 (GCC) 16.2.0
Kompilacja
Aby skompilować projekt, wykonaj:
make
Makefile wykorzystuje:
CXX = g++-16
CXXFLAGS = -std=c++26 -fmodules
Podczas pierwszej kompilacji generowany jest również skompilowany moduł standardowej biblioteki C++.
GCC przechowuje skompilowane moduły w katalogu:
gcm.cache/
Po poprawnej kompilacji zostanie utworzony plik wykonywalny:
game
Uruchomienie programu
Aby skompilować projekt i uruchomić program:
make run
Można również uruchomić wcześniej skompilowany program bezpośrednio:
./game
Czyszczenie projektu
Aby usunąć pliki wygenerowane podczas kompilacji:
make clean
Polecenie usuwa:
- pliki obiektowe
*.o, - program wykonywalny
game, - katalog
gcm.cache, - wygenerowane pliki
*.ixx.gcm.
Struktura projektu
.
├── CMakeLists.txt
├── Makefile
├── Enemy.cpp
├── Enemy.ixx
├── Player.cpp
├── Player.ixx
├── Print.cpp
├── Print.ixx
└── main.cpp
Moduły projektu
| Plik | Opis |
|---|---|
Print.ixx |
Interfejs modułu odpowiedzialnego za funkcje związane z wyświetlaniem informacji. |
Print.cpp |
Implementacja modułu Print. |
Enemy.ixx |
Interfejs modułu reprezentującego przeciwników. |
Enemy.cpp |
Implementacja modułu Enemy. |
Player.ixx |
Interfejs modułu gracza. |
Player.cpp |
Implementacja modułu Player. |
main.cpp |
Główny punkt wejścia programu. |
Makefile |
Automatyzuje proces kompilacji, linkowania, uruchamiania i czyszczenia projektu. |
CMakeLists.txt |
Konfiguracja projektu dla systemu CMake. |
Moduły C++
Projekt wykorzystuje moduły zamiast tradycyjnych plików nagłówkowych .h/.hpp.
Przykładowy interfejs modułu znajduje się w pliku:
Player.ixx
Natomiast jego implementacja w:
Player.cpp
Moduły są kompilowane przez GCC przy użyciu:
-fmodules
oraz standardu:
-std=c++26
Moduł standardowej biblioteki
Makefile zawiera osobny cel:
std-module:
$(CXX) $(CXXFLAGS) --compile-std-module -c
Odpowiada on za przygotowanie modułu standardowej biblioteki, który może być następnie importowany w kodzie za pomocą:
import std;
Dzięki temu projekt korzysta z mechanizmu modułów dostępnego w GCC 16 zamiast klasycznych:
#include <iostream>
#include <vector>
#include <string>
Makefile
Najważniejsze ustawienia:
CXX = g++-16
CXXFLAGS = -std=c++26 -fmodules
TARGET = game
Dostępne polecenia
| Polecenie | Opis |
|---|---|
make |
Kompiluje cały projekt i tworzy game. |
make run |
Kompiluje projekt, jeśli jest to konieczne, i uruchamia game. |
make clean |
Usuwa pliki wynikowe oraz skompilowane moduły. |
make std-module |
Przygotowuje skompilowany moduł standardowej biblioteki. |
Kolejność kompilacji
Makefile zapewnia odpowiednią kolejność budowania modułów.
Najpierw przygotowywany jest moduł standardowej biblioteki:
std-module
Następnie kompilowane są moduły:
Print.ixx
Enemy.ixx
Player.ixx
Po nich kompilowane są odpowiadające im pliki implementacyjne:
Print.cpp
Enemy.cpp
Player.cpp
Na końcu kompilowany jest:
main.cpp
Wszystkie pliki obiektowe są następnie łączone w jeden program:
game
Schemat budowania:
std
│
├── Print.ixx ──> Print.o
│
├── Enemy.ixx ──> Enemy.o
│
└── Player.ixx ──> Player.o
│
main.cpp ─────────> main.o
│
▼
┌─────────┐
│ game │
└─────────┘
Uwagi dotyczące GCC 16.2
Projekt jest skonfigurowany konkretnie pod:
GCC 16.2
g++-16
Dlatego Makefile używa:
CXX = g++-16
Jeżeli system posiada inną wersję GCC lub kompilator jest dostępny pod inną nazwą, należy odpowiednio zmienić wartość CXX.
Przykładowo:
CXX = g++
lub:
CXX = g++-16
Dla tego projektu zalecane jest jednak używanie GCC 16.2, ponieważ obsługa modułów i import std; jest zależna od wersji kompilatora.
Szybki start
Po pobraniu projektu wystarczy wykonać:
make
a następnie:
./game
lub bezpośrednio:
make run
Aby wyczyścić wszystkie pliki wygenerowane podczas kompilacji:
make clean
- Rejestracja: dni
- Ostatnio: dni
- Postów: 8
Przepisałem też wersję na Rust trochę rozbudowaną z paskami postępu, aby sprawdzić czy kodu będzie duzo mniej niż w C++.
enemy.rs
pub struct Enemy {
pub name: String,
pub max_health: u32,
pub current_health: u32,
pub level: u8,
pub damage: u32,
}
impl Enemy {
pub fn new() -> Self {
Self {
name: String::new(),
max_health: 0,
current_health: 0,
level: 1,
damage: 0,
}
}
pub fn create_enemy(&mut self, enemy_type: u8) {
match enemy_type {
0 => {
self.name = "Zombie".to_string();
self.max_health = 30;
self.damage = 5;
}
1 => {
self.name = "Szkieletor".to_string();
self.max_health = 25;
self.damage = 6;
}
2 => {
self.name = "Goblin".to_string();
self.max_health = 20;
self.damage = 4;
}
_ => {
self.name = "Zombie".to_string();
self.max_health = 30;
self.damage = 5;
}
}
self.current_health = self.max_health;
}
pub fn set_level(&mut self, level: u8) {
self.level = level;
self.max_health += (level as u32 - 1) * 10;
self.current_health = self.max_health;
self.damage += (level as u32 - 1) * 2;
}
}
player.rs
use crate::print::{Color, Print};
pub struct Player {
pub name: String,
pub player_class: String,
pub strength: u32,
pub intelligence: u32,
pub vitality: u32,
pub max_health: u32,
pub current_health: u32,
pub damage: u32,
}
impl Player {
pub fn new() -> Self {
Self {
name: String::new(),
player_class: String::new(),
strength: 0,
intelligence: 0,
vitality: 0,
max_health: 0,
current_health: 0,
damage: 0,
}
}
pub fn set_class(&mut self, choice: u8) {
self.name = "Bohater".to_string();
match choice {
1 => {
self.player_class = "Wojownik".to_string();
self.strength = 10;
self.intelligence = 2;
self.vitality = 8;
}
2 => {
self.player_class = "Łotrzyk".to_string();
self.strength = 6;
self.intelligence = 5;
self.vitality = 6;
}
3 => {
self.player_class = "Mag".to_string();
self.strength = 2;
self.intelligence = 10;
self.vitality = 5;
}
_ => {
self.player_class = "Wojownik".to_string();
self.strength = 10;
self.intelligence = 2;
self.vitality = 8;
}
}
self.calculate_stats();
}
fn calculate_stats(&mut self) {
self.max_health = 50 + self.vitality * 10;
self.current_health = self.max_health;
self.damage = match self.player_class.as_str() {
"Wojownik" => self.strength * 2,
"Łotrzyk" => self.strength * 2 + 2,
"Mag" => self.intelligence * 2,
_ => 5,
};
}
pub fn display(&self) {
Print::clear();
Print::write("Wybrałeś ");
Print::write_color(&self.player_class, Color::Cyan);
Print::write("!\n\n");
Print::write(&format!("Siła: {}\n", self.strength));
Print::write(&format!("Inteligencja: {}\n", self.intelligence));
Print::write(&format!("Witalność: {}\n", self.vitality));
Print::write("Zdrowie: ");
Print::draw_hp_bar(self.current_health, self.max_health, 20, Color::Green);
}
}
print.rs
use crossterm::{
ExecutableCommand,
cursor::MoveTo,
style::Color as CtColor,
style::Stylize,
terminal::{Clear, ClearType},
};
use std::io::{Write, stdout};
#[derive(Clone, Copy)]
pub enum Color {
// Default,
Red,
Green,
Yellow,
Cyan,
// Blue,
Magenta,
// White,
// Black,
}
pub struct Print;
impl Print {
pub fn write(text: &str) {
print!("{}", text);
stdout().flush().unwrap();
}
pub fn write_color(text: &str, color: Color) {
let ct_color = match color {
Color::Red => CtColor::Red,
Color::Green => CtColor::Green,
Color::Yellow => CtColor::Yellow,
Color::Cyan => CtColor::Cyan,
// Color::Blue => CtColor::Blue,
Color::Magenta => CtColor::Magenta,
// Color::White => CtColor::White,
// Color::Black => CtColor::Black,
// Color::Default => CtColor::Reset,
};
print!("{}", text.with(ct_color));
stdout().flush().unwrap();
}
pub fn clear() {
// stdout().execute(Clear(ClearType::All)).unwrap();
let mut stdout = stdout();
stdout.execute(Clear(ClearType::All)).unwrap();
stdout.execute(MoveTo(0, 0)).unwrap();
stdout.flush().unwrap();
}
pub fn draw_hp_bar(current: u32, max: u32, width: usize, color: Color) {
let filled = (current * width as u32) / max;
let empty = width as u32 - filled;
let bar = format!(
"[{}{}] {}/{}",
"█".repeat(filled as usize),
" ".repeat(empty as usize),
current,
max
);
Print::write_color(&bar, color);
Print::write("\n");
}
}
main.rs
mod enemy;
mod player;
mod print;
use crate::enemy::Enemy;
use crate::player::Player;
use crate::print::{Color, Print};
use rand::Rng;
use std::io;
fn get_enemy_count() -> usize {
let mut rng = rand::rng();
let roll = rng.random_range(0..100);
match roll {
0..=49 => 1,
50..=74 => 2,
75..=89 => 3,
_ => 4,
}
}
fn get_enemy_type() -> u8 {
let mut rng = rand::rng();
rng.random_range(0..3)
}
fn get_enemy_level() -> u8 {
let mut rng = rand::rng();
rng.random_range(1..=3)
}
fn main() {
let mut player = Player::new();
loop {
Print::clear();
Print::write_color("==== UpiornePotyczki ====\n\n", Color::Red);
Print::write("1. Nowa Gra\n");
Print::write("2. Wyjście\n");
Print::write_color("\nWybierz opcję: ", Color::Yellow);
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let choice = input.trim().parse::<u8>().unwrap_or(0);
if choice == 2 {
break;
} else if choice == 1 {
// wybór klasy
Print::clear();
Print::write_color("Wybierz klasę:\n\n", Color::Yellow);
Print::write("1. Wojownik\n2. Łotrzyk\n3. Mag\n");
Print::write_color("\nWybierz opcję: ", Color::Yellow);
input.clear();
io::stdin().read_line(&mut input).unwrap();
let class_choice = input.trim().parse::<u8>().unwrap_or(1);
player.set_class(class_choice);
player.display();
// rozpoczęcie walki
let enemy_count = get_enemy_count();
let mut enemies: Vec<Enemy> = Vec::new();
for _ in 0..enemy_count {
let mut e = Enemy::new();
e.create_enemy(get_enemy_type());
e.set_level(get_enemy_level());
enemies.push(e);
}
let mut in_combat = true;
while in_combat {
Print::clear();
Print::write_color("Przeciwnicy:\n", Color::Red);
for (i, e) in enemies.iter().enumerate() {
Print::write(&format!("{}: {} lvl {} ", i + 1, e.name, e.level));
Print::draw_hp_bar(e.current_health, e.max_health, 20, Color::Green);
}
Print::write("\nTwoje HP: ");
Print::draw_hp_bar(player.current_health, player.max_health, 20, Color::Cyan);
Print::write("\n1. Atak\n2. Ucieczka\nWybierz: ");
input.clear();
io::stdin().read_line(&mut input).unwrap();
let combat_choice = input.trim().parse::<u8>().unwrap_or(1);
if combat_choice == 2 {
Print::write_color("\nUciekasz!\n", Color::Yellow);
Print::write("\nNaciśnij ENTER, aby kontynuować...");
let mut pause = String::new();
io::stdin().read_line(&mut pause).unwrap();
break;
} else if combat_choice == 1 {
Print::write_color("\nWybierz przeciwnika: ", Color::Yellow);
input.clear();
io::stdin().read_line(&mut input).unwrap();
let target_idx = input.trim().parse::<usize>().unwrap_or(1) - 1;
if target_idx < enemies.len() {
let dmg = player.damage;
let e = &mut enemies[target_idx];
e.current_health = e.current_health.saturating_sub(dmg);
Print::write(&format!("\nZadajesz {} obrażeń {}!\n", dmg, e.name));
if e.current_health == 0 {
Print::write_color(&format!("{} pokonany!\n", e.name), Color::Green);
enemies.remove(target_idx);
}
if enemies.is_empty() {
Print::write_color("\nWygrałeś walkę!\n", Color::Green);
Print::write("\nNaciśnij ENTER, aby kontynuować...");
let mut pause = String::new();
io::stdin().read_line(&mut pause).unwrap();
in_combat = false;
} else {
// atak przeciwników
for e in enemies.iter() {
player.current_health =
player.current_health.saturating_sub(e.damage);
Print::write_color(
&format!("{} zadaje {} obrażeń!\n", e.name, e.damage),
Color::Red,
);
if player.current_health == 0 {
Print::write_color("\nZostałeś pokonany!\n", Color::Red);
in_combat = false;
break;
}
}
}
}
}
}
}
}
Print::write_color("\nDzięki za grę!\n", Color::Magenta);
}
- Rejestracja: dni
- Ostatnio: dni
- Postów: 8
Skompilowałem też program pod Zig aby sprawdzić czy składnia będzie bardziej czytelna od Rust. Ale wyszedł mi jakiś bardziej rozwlekły kod.
Ale ja najsłabiej znam ten język. Skompilowałem grę na Zig 0.16, ale na 0.17 też powinno działać, uruchamiamy zig run.
W tych nowych językach programowania systemowego to jest dobre, że nie trzeba tworzyć dodatkowych plików .h, .ixx.
I nie trzeba wypisywać Makefile i CMakeLists.txt
enemy.zig
const std = @import("std");
pub const Enemy = struct {
name: []const u8,
max_health: u32,
current_health: u32,
level: u8,
damage: u32,
pub fn init() Enemy {
return Enemy{
.name = "",
.max_health = 0,
.current_health = 0,
.level = 1,
.damage = 0,
};
}
pub fn createEnemy(self: *Enemy, enemy_type: u8) void {
switch (enemy_type) {
0 => {
self.name = "Zombie";
self.max_health = 30;
self.damage = 5;
},
1 => {
self.name = "Szkieletor";
self.max_health = 25;
self.damage = 6;
},
2 => {
self.name = "Goblin";
self.max_health = 20;
self.damage = 4;
},
else => {
self.name = "Zombie";
self.max_health = 30;
self.damage = 5;
},
}
self.current_health = self.max_health;
}
pub fn setLevel(self: *Enemy, level: u8) void {
self.level = level;
self.max_health += (@as(u32, level) - 1) * 10;
self.current_health = self.max_health;
self.damage += (@as(u32, level) - 1) * 2;
}
};
player.zig
const std = @import("std");
const Print = @import("print.zig").Print;
const Color = @import("print.zig").Color;
pub const PlayerClass = enum {
Warrior,
Rogue,
Mage,
};
pub const Player = struct {
name: []const u8,
class: PlayerClass,
strength: u32,
intelligence: u32,
vitality: u32,
max_health: u32,
current_health: u32,
damage: u32,
pub fn init() Player {
return Player{
.name = "",
.class = .Warrior,
.strength = 0,
.intelligence = 0,
.vitality = 0,
.max_health = 0,
.current_health = 0,
.damage = 0,
};
}
pub fn setClass(
self: *Player,
choice: u8,
) void {
self.name = "Bohater";
switch (choice) {
1 => {
self.class = .Warrior;
self.strength = 10;
self.intelligence = 2;
self.vitality = 8;
},
2 => {
self.class = .Rogue;
self.strength = 6;
self.intelligence = 5;
self.vitality = 6;
},
3 => {
self.class = .Mage;
self.strength = 2;
self.intelligence = 10;
self.vitality = 5;
},
else => {
self.class = .Warrior;
self.strength = 10;
self.intelligence = 2;
self.vitality = 8;
},
}
self.calculateStats();
}
fn calculateStats(
self: *Player,
) void {
self.max_health =
50 + self.vitality * 10;
self.current_health =
self.max_health;
self.damage = switch (self.class) {
.Warrior => self.strength * 2,
.Rogue => self.strength * 2 + 2,
.Mage => self.intelligence * 2,
};
}
pub fn display(
self: Player,
) void {
Print.clear();
Print.write("Wybrałeś ");
const class_name = switch (self.class) {
.Warrior => "Wojownik",
.Rogue => "Łotrzyk",
.Mage => "Mag",
};
Print.writeColor(
class_name,
Color.Cyan,
);
Print.write("\n\n");
std.debug.print(
"Siła: {}\n",
.{self.strength},
);
std.debug.print(
"Inteligencja: {}\n",
.{self.intelligence},
);
std.debug.print(
"Witalność: {}\n",
.{self.vitality},
);
Print.write("Zdrowie: ");
Print.drawHpBar(
self.current_health,
self.max_health,
20,
Color.Green,
);
}
};
print.zig
const std = @import("std");
pub const Color = enum {
Default,
Red,
Green,
Yellow,
Cyan,
Blue,
Magenta,
White,
Black,
};
pub const Print = struct {
pub fn write(text: []const u8) void {
std.debug.print("{s}", .{text});
}
pub fn writeColor(text: []const u8, color: Color) void {
const code = switch (color) {
.Red => "\x1b[31m",
.Green => "\x1b[32m",
.Yellow => "\x1b[33m",
.Cyan => "\x1b[36m",
.Blue => "\x1b[34m",
.Magenta => "\x1b[35m",
.White => "\x1b[37m",
.Black => "\x1b[30m",
.Default => "\x1b[0m",
};
std.debug.print("{s}{s}\x1b[0m", .{
code,
text,
});
}
pub fn clear() void {
std.debug.print("\x1b[2J\x1b[H", .{});
}
pub fn drawHpBar(
current: u32,
max: u32,
width: usize,
color: Color,
) void {
const filled = (current * width) / max;
const empty = width - filled;
Print.writeColor(
"[",
color,
);
for (0..filled) |_| {
Print.write("█");
}
for (0..empty) |_| {
Print.write(" ");
}
std.debug.print("] {}/{}\n", .{
current,
max,
});
}
};
main.zig
const std = @import("std");
const Player = @import("player.zig").Player;
const Enemy = @import("enemy.zig").Enemy;
const Print = @import("print.zig").Print;
const Color = @import("print.zig").Color;
var random_counter: u32 = 0;
fn readNumber() u8 {
var buffer: [32]u8 = undefined;
const size = std.posix.read(
0,
&buffer,
) catch return 0;
var end: usize = 0;
while (end < size and buffer[end] != '\n') {
end += 1;
}
return std.fmt.parseInt(
u8,
buffer[0..end],
10,
) catch 0;
}
fn waitEnter() void {
var buffer: [8]u8 = undefined;
_ = std.posix.read(
0,
&buffer,
) catch {};
}
fn getEnemyType() u8 {
random_counter += 1;
return @intCast(
random_counter % 3,
);
}
fn getEnemyCount() usize {
random_counter += 1;
return switch (random_counter % 4) {
0 => 1,
1 => 2,
2 => 3,
else => 4,
};
}
fn getEnemyLevel() u8 {
random_counter += 1;
return @intCast(
(random_counter % 3) + 1,
);
}
pub fn main() !void {
var player = Player.init();
while (true) {
Print.clear();
Print.writeColor(
"==== UpiornePotyczki Zig ====\n\n",
Color.Red,
);
Print.write(
"1. Nowa Gra\n",
);
Print.write(
"2. Wyjście\n\n",
);
Print.writeColor(
"Wybierz: ",
Color.Yellow,
);
const menu = readNumber();
if (menu == 2) {
break;
}
if (menu != 1) {
continue;
}
Print.clear();
Print.writeColor(
"Wybierz klasę:\n\n",
Color.Yellow,
);
Print.write(
"1. Wojownik\n",
);
Print.write(
"2. Łotrzyk\n",
);
Print.write(
"3. Mag\n\n",
);
Print.writeColor(
"Wybierz: ",
Color.Yellow,
);
player.setClass(
readNumber(),
);
player.display();
waitEnter();
var enemies: [4]Enemy = undefined;
var enemy_count =
getEnemyCount();
var i: usize = 0;
while (i < enemy_count) {
enemies[i] =
Enemy.init();
enemies[i].createEnemy(
getEnemyType(),
);
enemies[i].setLevel(
getEnemyLevel(),
);
i += 1;
}
var fighting = true;
while (fighting) {
Print.clear();
Print.writeColor(
"=== WALKA ===\n\n",
Color.Red,
);
i = 0;
while (i < enemy_count) {
std.debug.print(
"{}. {s} lvl {}\n",
.{
i + 1,
enemies[i].name,
enemies[i].level,
},
);
Print.drawHpBar(
enemies[i].current_health,
enemies[i].max_health,
20,
Color.Green,
);
i += 1;
}
Print.write(
"\nTwoje HP:\n",
);
Print.drawHpBar(
player.current_health,
player.max_health,
20,
Color.Green,
);
Print.write(
"\n1. Atak\n",
);
Print.write(
"2. Ucieczka\n\n",
);
Print.writeColor(
"Wybierz: ",
Color.Yellow,
);
const action =
readNumber();
if (action == 2) {
Print.writeColor(
"\nUciekasz!\n",
Color.Yellow,
);
fighting = false;
continue;
}
if (action == 1) {
Print.write(
"\nWybierz przeciwnika: ",
);
const target =
readNumber();
if (target == 0 or target > enemy_count) {
continue;
}
const index =
target - 1;
enemies[index].current_health =
if (enemies[index].current_health > player.damage)
enemies[index].current_health - player.damage
else
0;
std.debug.print(
"\nZadajesz {} obrażeń {s}\n",
.{
player.damage,
enemies[index].name,
},
);
if (enemies[index].current_health == 0) {
Print.writeColor(
"Pokonany!\n",
Color.Green,
);
enemies[index] =
enemies[enemy_count - 1];
enemy_count -= 1;
}
if (enemy_count == 0) {
Print.writeColor(
"\nWygrałeś walkę!\n",
Color.Green,
);
fighting = false;
continue;
}
i = 0;
while (i < enemy_count) {
player.current_health =
if (player.current_health > enemies[i].damage)
player.current_health - enemies[i].damage
else
0;
std.debug.print(
"{s} zadaje {} obrażeń!\n",
.{
enemies[i].name,
enemies[i].damage,
},
);
if (player.current_health == 0) {
Print.writeColor(
"\nZostałeś pokonany!\n",
Color.Red,
);
fighting = false;
break;
}
i += 1;
}
waitEnter();
}
}
Print.write(
"\nENTER aby wrócić do menu...\n",
);
waitEnter();
}
Print.writeColor(
"\nDzięki za grę!\n",
Color.Magenta,
);
}