W świecie programowania gier komputerowych, gdzie terminy są krótkie, a wymagania zmieniają się z dnia na dzień, często kusimy się na „eleganckie”, „zaawansowane” rozwiązania. Szablony w C++, systemy zdarzeń w Unity, wzorce projektowe na każdym kroku… A potem? Kod staje się nieczytelny, debugowanie trwa wieki, a nowy członek zespołu nie wie, od czego zacząć.
Tu z pomocą przychodzi zasada KISS (Keep It Simple, Stupid), co po polsku oznacza mniej więcej „Trzymaj to prosto, głupcze!”. To jedna z najstarszych i najważniejszych reguł inżynierii oprogramowania. Nie chodzi o pisanie byle jak, ale o świadome unikanie niepotrzebnej komplikacji.
Główna myśl zasady KISS brzmi:
Najprostsze rozwiązanie, które działa – jest najlepsze.
W programowaniu gier prostota to nie lenistwo – to mądrość. Prosty kod oznacza szybsze prototypy, mniej błędów, łatwiejszą współpracę i mniejsze ryzyko, że coś się zepsuje podczas optymalizacji.
Rozważmy prosty system zadawania dodatkowych obrażeń, gdy wróg jest osłabiony (np. spowolniony).
Czasem programiści tworzą całą architekturę: interfejsy, strategie, fabryki…
// IDamageStrategy.h
class IDamageStrategy {
public:
virtual int calculate(Enemy& enemy, Weapon& weapon) = 0;
virtual ~IDamageStrategy() = default;
};
// BonusDamageStrategy.cpp
class BonusDamageStrategy : public IDamageStrategy {
public:
int calculate(Enemy& enemy, Weapon& weapon) override {
int base = weapon.damage;
if (enemy.isSlowed) base = static_cast(base * 1.5f);
enemy.health -= base;
return base;
}
};
// DamageSystem.cpp
class DamageSystem {
std::unique_ptr strategy;
public:
DamageSystem(std::unique_ptr s) : strategy(std::move(s)) {}
void apply(Enemy& enemy, Weapon& weapon) {
int dmg = strategy->calculate(enemy, weapon);
std::cout << "Zadano " << dmg << " dodatkowych obrażeń!\n";
}
};
Problem? Dla jednego typu bonusu mamy co najmniej 3 pliki, ponad 50 linii kodu, wstrzykiwanie zależności i wirtualne wywołania. Debugowanie to koszmar, a wydajność cierpi na overheadzie.
// Damage.cpp
int applyBonusDamage(Enemy& enemy, Weapon& weapon) {
int baseDamage = weapon.damage;
if (enemy.isSlowed) {
baseDamage = static_cast(baseDamage * 1.5f);
}
enemy.health -= baseDamage;
std::cout << "Zadano " << baseDamage << " dodatkowych obrażeń!\n";
return baseDamage;
}
Jedna funkcja, 10 linii, zero overheadu. Czytelna jak książka. Łatwa do testów jednostkowych. Działa w C++, C#, Pythonie – wszędzie.
Wrogowie chodzą po ustalonej trasie.
FSM z węzłami, przejściami, warunkami… Dla prostego patrolu – niepotrzebne.
// Enemy.cpp
std::vector patrolPoints;
int currentPointIndex = 0;
void updatePatrol(float deltaTime) {
Vector2 target = patrolPoints[currentPointIndex];
moveTowards(target, deltaTime);
if (atPosition(target)) {
currentPointIndex = (currentPointIndex + 1) % patrolPoints.size();
}
}
Zero stanów, zero komplikacji. Dodanie punktu? Wrzuć do wektora. Gotowe.
| Aspekt | Bez KISS | Z KISS |
|---|---|---|
| Czytelność kodu | Niska – warstwy abstrakcji | Wysoka – kod mówi, co robi |
| Czas tworzenia | Długi – projektowanie architektury | Krótki – pisz i testuj od razu |
| Łatwość debugowania | Trudna – wiele warstw | Łatwa – krok po kroku |
| Skalowalność | Tak, ale za wcześnie | Tak, gdy naprawdę potrzeba |
| Ryzyko błędów | Wysokie – skomplikowane interakcje | Niskie – mniej kodu, mniej błędów |
Zasada KISS nie jest dogmatem. Są sytuacje, gdy komplikacja jest uzasadniona:
Zasada KISS to nie lenistwo – to mądrość doświadczonego programisty. W grach czas to pieniądz, a prototyp musi działać na wczoraj. Prosty kod oznacza szybsze iteracje, mniej błędów, łatwiejszą współpracę i mniejsze ryzyko, że coś się zepsuje podczas intensywnej pracy nad programem.
Pisz tak, jak by czytał to programista po 12 godzinach pracy – jeśli zrozumie w 10 sekund, masz KISS. Stosuj tę zasadę w C++, C#, Unity, Unreal, Godot – wszędzie. Twój kod (i zespół) Ci za to podziękują!