Review
You have a handful of difficulty levels, so you can manage this with a couple of booleans:
bool Increased100500 = false;
bool Increased5001500 = false;
bool Increased15003000 = false;
Your Increase
method is a black-box to us. Maybe it's linearly changing some difficulty parameters, or maybe it sets custom values to each of these parameters.
As the game gets bigger, gets more parameters that are sensitive to the difficulty level, gets more difficulty levels and gets such features as skipping levels, decreasing score and levels, it will be hard to maintain the code.
Proposed Changes
To allow the game to get more complex, I would opt to use a state machine pattern to handle difficulty levels. All actions concerning changing game parameters that influence the difficulty are handled by each specific state. This allows for an object-oriented design, extensibility, and drastically decreasing the amount of boolean flags in the game.
State
Each state has actions on Enter
and Exit
. A Difficulty level state also handles OnScoreUpdated
events.
abstract class DifficultyLevelState
{
public virtual void Enter(Game game) { }
public virtual void Exit(Game game) { }
protected virtual DifficultyLevelState EasyLevel() => new EasyState();
protected virtual DifficultyLevelState NormalLevel() => new NormalState();
protected virtual DifficultyLevelState HardLevel() => new HardState();
protected virtual DifficultyLevelState ExpertLevel() => new ExpertState();
public void OnScoreUpdated(Game game)
{
var score = game.Score;
if (score < 100)
{
game.UpdateDifficultyLevel(EasyLevel());
}
else if (score < 500)
{
game.UpdateDifficultyLevel(NormalLevel());
}
else if (score < 1500)
{
game.UpdateDifficultyLevel(HardLevel());
}
else //if (score < 3000)
{
game.UpdateDifficultyLevel(ExpertLevel());
}
}
}
Each specific state implements a self-transition to avoid unnecessary state changes and actions on Enter
to change the game's difficulty level. I haven't found a use for Exit
, but I'll keep the method because the pattern provides it, and you might find a use case for it. Implement EasyState
, NormalState
, HardState
and ExpertState
.
class EasyState : DifficultyLevelState
{
protected override DifficultyLevelState EasyLevel() => this;
public override void Enter(Game game)
{
// Change game difficulty parameters:
// game.SpeedFactor = 1;
// game.CollisionFactor = 0.75;
}
}
State Machine
Your Game
probably provides a score
somehow and has an entrypoint to Start
the game:
class Game
{
private int score;
bool Increased100500 = false;
bool Increased5001500 = false;
bool Increased15003000 = false;
public void Start()
{
Score = 0;
}
public int Score
{
get => score;
protected set
{
if (score != value)
{
score = value;
IncreaseScore(); // OP method
}
}
}
}
Let's remove the IncreaseScore
, the boolean flags and implement the state machine.
class Game
{
private int score;
private DifficultyLevelState difficultyLevel;
public void Start()
{
Score = 0;
UpdateDifficultyLevel(new EasyState());
}
public int Score
{
get => score;
protected set
{
if (score != value)
{
score = value;
OnScoreUpdated();
}
}
}
private void OnScoreUpdated()
{
difficultyLevel.OnScoreUpdated(this);
}
internal void UpdateDifficultyLevel(DifficultyLevelState state)
{
if (difficultyLevel != state)
{
difficultyLevel?.Exit(this);
difficultyLevel = state;
difficultyLevel?.Enter(this);
}
}
}
Now, any time the score gets updated, the game asks the current difficulty state to handle the score. The current state might decide to keep the state as is, or to change the state to another difficulty level based on the current score. Whenever the difficulty state changes, its Enter
actions change the difficulty parameters of the game. This pattern also allows to skip states (big score leaps) and decrease difficulty levels.
avoid answering questions in comments
. \$\endgroup\$