I've been learning C# over the summer and now feel like making a small project out of what I've done so far. I've decided on a sort of text based adventure game.
The basic structure of the game will involve having a number of sectors(or rooms). Upon entry into a room, a description will be outputted and a number of actions and such you may take; the ability to examine, pick up, use stuff in that room; possibly a battle system, etc etc. A sector may be connected up to 4 other sectors.
Anyway, scribbling ideas on paper on how to design the code for this, I'm scratching my head over the structure of part of my code.
I've decided on a player class, and a 'level' class that represents a level/dungeon/area. This level class would consist of a number of interconnected 'sectors'. At any given time, the player would be present in one certain sector in the level.
So here's the confusion:
Logically, one would expect a method such as player.Move(Dir d)
Such a method should change the 'current sector' field in the level object. This means class Player would need to know about class Level. Hmmm.
And Level may have to manipulate the Player object (eg. player enters room, ambushed by something, loses something from inventory.) So now Level also needs to hold a reference to the Player object?
This doesn't feel nice; everything having to hold a reference to everything else.
At this point I remembered reading about delegates from the book I'm using. Though I know about function pointers from C++, the chapter on delegates was presented with examples with a sort of 'event based' programming viewpoint, with which I did not have much enlightenment about.
That gave me the idea to design the classes as follows:
Player:
class Player
{
//...
public delegate void Movement(Dir d); //enum Dir{NORTH, SOUTH, ...}
public event Movement PlayerMoved;
public void Move(Dir d)
{
PlayerMoved(d);
//Other code...
}
}
Level:
class Level
{
private Sector currSector;
private Player p;
//etc etc...
private void OnMove(Dir d)
{
switch (d)
{
case Dir.NORTH:
//change currSector
//other code
break;
//other cases
}
}
public Level(Player p)
{
p.PlayerMoved += OnMove;
currSector = START_SECTOR;
//other code
}
//etc...
}
Is this an alright way to do this?
If the delegate chapter was not presented the way it was, I would not have thought of using such 'events'. So what would be a good way to implement this without using callbacks?
I have a habit of making highly detailed posts... sorry v__v
What about a 'Game' class which would hold the majority of the information like a Player and a current room. For an operation such as moving the player, the Game class could move the player to a different room based on the room's level map.
The game class would manage all the interactions between the various components of the games.
Using events for something like this brings the danger that your events will get tangled. If you're not careful you'll end up with events firing each other off and overflowing your stack, which will lead to flags to turn events off under special circumstances, and a less understandable program.
UDPATE:
To make the code more manageable, you could model some of the interactions between the main classes as classes themselves, such as a Fight class. Use interfaces to enable your main classes to perform certain interactions. (Note that I have taken the liberty of inventing a few things you may not want in your game).
For example:
// Supports existance in a room.
interface IExistInRoom { Room GetCurrentRoom(); }
// Supports moving from one room to another.
interface IMoveable : IExistInRoom { void SetCurrentRoom(Room room); }
// Supports being involved in a fight.
interface IFightable
{
Int32 HitPoints { get; set; }
Int32 Skill { get; }
Int32 Luck { get; }
}
// Example class declarations.
class RoomFeature : IExistInRoom
class Player : IMoveable, IFightable
class Monster : IMoveable, IFightable
// I'd proably choose to have this method in Game, as it alters the
// games state over one turn only.
void Move(IMoveable m, Direction d)
{
// TODO: Check whether move is valid, if so perform move by
// setting the player's location.
}
// I'd choose to put a fight in its own class because it might
// last more than one turn, and may contain some complex logic
// and involve player input.
class Fight
{
public Fight(IFightable[] participants)
public void Fight()
{
// TODO: Logic to perform the fight between the participants.
}
}
In your question, you identified the fact that you'd have many classes which have to know about each other if you stuck something like a Move method on your Player class. This is because something like a move neither belongs to a player or to a room - the move affects both objects mutually. By modelling the 'interactions' between the main objects you can avoid many of those dependencies.
Sounds like a scenario I often use a Command class or Service class for. For example, I might create a MoveCommand class that performs the operations and coordinations on and between Levels and Persons.
This pattern has the advantage of further enforcing the Single Responsibility Principal (SRP). SRP says that a class should only have one reason to change. If the Person class is responsible for moving it will undoubtedly have more than one reason to change. By breaking the logic of a Move off into its own class, it is better encapsulated.
There are several ways to implement a Command class, each fitting different scenarios better. Command classes could have an Execute method that takes all necessary parameters:
public class MoveCommand {
public void Execute(Player currentPlayer, Level currentLevel) { ... }
}
public static void Main() {
var cmd = new MoveCommand();
cmd.Execute(player, currentLevel);
}
Or, sometimes I find it more straightforward, and flexible, to use properties on the command object, but it makes it easier for client code to misuse the class by forgetting to set properties - but the advantage is that you have the same function signature for Execute on all command classes, so you can make an interface for that method and work with abstract Commands:
public class MoveCommand {
public Player CurrentPlayer { get; set; }
public Level CurrentLevel { get; set; }
public void Execute() { ... }
}
public static void Main() {
var cmd = new MoveCommand();
cmd.CurrentPlayer = currentPlayer;
cmd.CurrentLevel = currentLevel;
cmd.Execute();
}
Lastly, you could provide the parameters as constructor arguments to the Command class, but I'll forgo that code.
In any event, I find using Commands or Services a very powerful way to handle operations, like Move.
For a text-based game, you're almost certainly going to have a CommandInterpretor (or similar) object, which evaluates the user's typed commands. With that level of abstraction, you don't have to implement every possible action on your Player object. Your interpreter might push some typed commands to your Player object ("show inventory"), some commands to the currently-occupied Sector object ("list exits"), some commands to the Level object ("move player North"), and some commands to specialty objects ("attack" might be pushed to a CombatManager object).
In that way, the Player object becomes more like the Character, and the CommandInterpretor is more respresentational of the actual human player sitting at the keyboard.
Avoid getting emotionally or intellectually mired in what the "right" way to do something is. Focus instead on doing. Don't put too much value on the code you've already written, because any or all of it may need to change to support things that you want to do.
IMO there's way too much energy being spent on patterns and cool techniques and all of that jazz. Just write simple code to do the thing you want to do.
The level "contains" everything within it. You can start there. The level shouldn't necessarily drive everything, but everything is in the level.
The player can move, but only within the confines of the level. Therefore, the player needs to query the level to see if a move direction is valid.
The level isn't taking items from the player, nor is the level dealing damage. Other objects in the level are doing these things. Those other objects should be searching for the player, or maybe told of the player's proximity, and then they can do what they want directly to the player.
It's ok for the level to "own" the player and for the player to have a reference to its level. This "makes sense" from an OO perspective; you stand on Planet Earth and can affect it, but it is dragging you around the universe while you're digging holes.
Do Simple Things. Any time something gets complicated, figure out how to make it simple. Simple code is easier to work with and is more resistant to bugs.
So firstly, is this an alright way to
do this?
Absolutely!
Secondly, if the delegate chapter was
not presented the way it was, I would
not have thought of using such
'events'. So what would be a good way
to implement this without using
callbacks?
I know a lot of other ways to implement this, but no any other good way without some kind of callback mechanism. IMHO it is the most natural way to create a decoupled implementation.
Related
I've been writing unity games for some time now using C#. After each game I became more and more experienced, my code changed, I started using best practices.
However, today I have a question: how to build the application architecture correctly?
I don't like that there are a lot of fields in my code that are mixed together with the main logic, I feel that this should not be the case. The solution I have come to so far is to make 2 classes, one contains all the information, and the second implements all the logic, but the class in which all the logic is located becomes dependent on the class with information.
Tell me, more experienced colleagues, what is the right thing to do?
To begin with, try to divide the class that contains logic into smaller parts so that every part only has one responsibility and does one particular thing. Then move these parts into other classes. Try to find a balance when splitting the logic class. Making new class for each method is one extreme, having one class with all the logic is another extreme, the solution is somewhere in the middle. When it's done move on to the next step.
The next step is to name these classes. It might seem easy, but it's really important. Some good examples of naming:
PlayerMover or PlayerMovement, the class which responsibility is to move the player in the chosen direction.
PlayerInput, the class which responsibility is to interact with input and translate it into the language that other components can understand. For example translating keyboard input into Vector3, so PlayerMovement doesn't have to worry about which key was pressed, it only knows where to move, so their responsibilities don't intercept more than necessary.
Tips on naming:
Classes represent entities, so they should be named as entities, it means that their names should be nouns
If giving a name to a class seems hard then the class is most likely formed wrong and has too many responsibilities or doesn't have a whole one
The next step is to separate different layers. Try to make logic independent of UI, so UI can be changed or removed without affecting logic layer. Continuing with the example of player subsystems, make PlayerMovement independent of type of input it uses with abstractions if needed, so it can be keyboard input or joystick input and PlayerMovement doesn't care which one. Also make PlayerInput independent of whether someone uses it or not, for example with properties or events. It will allow to create components once and then use in any project without rewriting everything.
Talking about dividing data from logic, it'll most likely result in having two very similar inheritance hierarchies, so it's better to store the data right where it's used, unlike it's a special case with settings file or big amounts of data.
These are basic tips on this topic and of course building a project architecture is much more complicated than that, but these are great things to start with.
You can continue with Solid (some principles are already mentioned here) and things like Zenject
I get the feeling like you might be looking to incorporate complicated design patterns into your code just because you can, not because it's solving any problems.
You could use interfaces in order to decouple your system classes from your data classes:
public interface IHealth
{
int Current { get; set; }
int Max { get; set; }
}
public class Health : IHealth
{
public int Current { get; set; }
public int Max { get; set; }
}
public class DamageCommand
{
public void Invoke(IHealth health, int amount)
{
health.Current -= amount;
}
}
However, before you go down this route, I'd recommend you first stop and ask yourself if this is offering you any actual tangible benefits to offset the increase in complexity?
Removing dependencies between concrete classes can often be useful for the concrete benefits this can offer, such as making the code more easily unit testable and making it easier to swap a class with another implementation later on. But when we are talking about just pure data objects, how often would you really run into a scenario where you want to swap out the implementation?
If you like keeping your data and systems separate, then I recommend looking into Unity's entity component system (ECS) for a good data-oriented architecture. Or if you want to build your own architecture, still consider using data-oriented design, as this can give a huge boost to performance.
I'm looking for a bit of direction on an issue I keep running into. I've recently challenged myself to code very granularly, so that small pieces of functionality can exist in a single component and operate independently of any other components.
The issue that I am running into is that some components use the same functions, functions that will likely never need to differ from each other. For example, I have a generic function that you can pass a string, and it executes an animation with that name. It seems silly to copy and paste this function into two separate components that need to trigger an animation, creating two versions of the function. The only other idea I have though is creating a separate modular piece that handles all animation. The only issue is, if I do that, now my modular components require the existence of this new component to fully function. Obviously I could make it "function" without it and throw debug warnings, but ultimately it makes objects more difficult to properly configure.
I am curious if anyone has any insight into these situations, as I am imagining there's got to be a technique to it.
Thanks
Questions like this are kind of the heart of object-oriented programming. I'd highly recommend the "Gang of Four" book on Design Patterns.
It is a whole textbook, but a few key approaches to your particular problem might be:
Favor composition over inheritance
Code to interfaces instead of classes, and
Use factories to instantiate and initialize objects.
So you could have an enum that defines the kind of animator you get:
public enum AnimationType
{
Knight,
Peasant
}
then you can have an interface that defines the animation states:
public interface IAnimator
{
void Idle();
void Walk();
void Attack();
}
With an interface it doesn't matter what the actual class is that's doing the work, all you care about is that it does the actions listed in the interface.
This means that you can now have anything inherit the interface, like:
public class AnimatorA : IAnimator
{
public AnimatorA(string filepath)
{
// load whatever you need to
}
public void Walk()
{
// do the walk animation
}
// also all the other things required by IAnimator
}
You can create as many or few animator classes as you want now, and they're all interchangeable because the class that needs it is only going to refer to it as an IAnimator.
Almost finally, now you can make a factory to get the particular things you want:
public static class IAnimatorFactory
{
private const string knightPath = "/path/to/knight";
private const string peasantPath = "/path/to/peasant";
public IAnimator GetIAnimator(AnimationType animationType)
{
switch(animationType)
{
case AnimationType.Knight:
return new AnimatorA(knightPath);
case AnimationType.Peasant:
return new AnimatorA(peasantPath);
}
}
}
and then finally you can have your class use the enum, which shows as a dropdown in the inspector, and a private IAnimator, and just get the AnimationType on Start():
public class YourActor
{
public AnimationType animationType;
private IAnimator animator;
void Start()
{
animator = IAnimatorFactory.GetIAnimator(animationType);
}
void Update()
{
if(walkCondition)
{
animator.Walk();
}
// etc.
}
}
The factory is a static class, so you don't need an instance of it. Want to change the animator type? Make a new class, modify when the factory should use that new class, and you don't touch any of the user classes. Want to change the thing the class is using? Change the enum and don't touch the factory or any of the animators.
This can add a fair bit of overhead, but paying the price for adding that infrastructure buys you easier maintenance down the road.
Lets say I'm making a game with a bunch of levels, whereby as you progress through levels, new types of enemies are introduced. If a developer has an enemy and wants to get the level an enemy can be found in, the most intuitive way I can think of allowing them to do this would be to store a list levels on the enemy that they are contained within. This however I feel like is a problem, because now I've coupled my enemy and my level together. My enemy now knows about the context in which it used. My research says that these kinds of relationships are unhealthy for maintaining software, but it seems intuitive to use from a level above.
Here's a very basic implementation:
public class Level
{
List<Enemy> typesOfEnemies;
public Level(List<Enemy> typesOfEnemies)
{
this.typesOfEnemies = new List<Enemy>(typesOfEnemies);
for (int i = 0; i < typesOfEnemies.Count; i++)
{
typesOfEnemies[i].AddLevelContainedWithin(this);
}
}
}
public class Enemy
{
public List<Level> levelsContainedWithin = new List<Level>();
public void AddLevelContainedWithin(Level level)
{
levelsContainedWithin.Add(level);
}
}
So there's probably a few problems associated with this code. The most obvious one that sticks out to me is that I can add a level to an enemy without updating my Level's types of enemies which puts them out of sync. I could resolve this by creating another class that does the work, ensuring the Level and Enemy stay in-sync, but now I've added a layer of code complexity. The developer now has another class they have to know about to work with.
My question is, how do I make this maintainable and intuitive to for a developer to use?
You can introduce another class that does the coupling:
class LevelManager
{
public List<Enemy> GetEnemies(Level l) { ... }
public List<Level> GetLevels(Enemy e) { ... }
}
Now all your data-objects have to know is the LevelManager.
Actually it´s not even neccessary to have this dependcy in neither your Enemy- nor your Level-class. If a developer wants to know in which levels an enemy can spawn, he may use the LevelManager instead.
When I trying to think over game architecture in Unity, I face the following problem: there are several ways to build relations between components, and I cannot understand which of them is the most optimal.
For example we have GameplayObject component with following parametres
public class GameplayObject : MonoBehaviour
{
// every gameplay object has chanse of appear on board
[SerializeField, Min(0)] int m_ChanseOfAppear = 0;
public int ChanseOfAppear => m_ChanseOfAppear;
//every gameplay object may be destroyed
public virtual void Destroy()
{
}
}
Destroy method can be executed after user input(click or drag is just a few types of input) or another gameplay object can execute destory method.
For example we have following TapBehaviour component
public class CustomDestroyer: MonoBehaviour, IPointerClickHandler
{
public event System.Action OnTap = delegate { };
IPointerClickHandler.OnPointerClick()
{
OnTap();
}
}
We have concrete GameplayObject (for exapmle CustomDestroyer) who should destroy objects in a concrete way. And now it need a dependency for our input component
[RequireComponent(typeof(TapBehaviour), typeof(BoxColiider2D))]
public class CustomDestroyer: GameplayObject
{
TapBehaviour m_TapBehaviour;
TapBehaviour TapBehaviour
{
get
{
if (m_TapBehaviour == null)
m_TapBehaviour = GetComponent<TapBehaviour>();
return m_TapBehaviour;
}
}
public override void Destroy()
{
}
}
But we may do it just in an opposite way: inherit CustomDestroyer from TapBehaviour and use GameplayObject like component.
So the main question is how you build architecture of your projects? When you use component's inheritance, when use just components? May be I'm losing some more preferable ways?
The more you explain to me, the more I will be grateful to you!
And sorry for bad english, just learning it.
Unity offers a great degree of freedom regarding architecture, its sometimes overwhelming when you can successfully do stuff in so many different ways that will all work. Regarding your question - in terms of future proofing I would lean towards using seperate components (aka single responsibility), loosely tied together using interfaces (like IDestroy instead of concrete types), the idea is to, whenever possible, avoid interdependency of components.
But for a great deal of cases inheritance with overrides will be fine, and refactoring is often painless (and there's many ways to automate editor tasks if you go too far down one of the alleys) so my advice would be: do not overcomplicate things, do it as simple as possible, but not simpler
From my experience with Unity, you should exactly know what do you want to do and possible future implementations, then find a way to do it and that doesn't limit your future implementations.
There's not ONE way to relate things in Unity, that's part of its ease of use. Everyone ends up doing things their way.
Try to find a solution that balances readability and efficiency, and stick to the newest components which are usually better overall.
Edit: Forgot to say that if you're a code guy try not to abuse the power of scripts since unity was designed for avoiding code. The UI has a lot of options that, in comparison with writing code, seem like magic.
So I'm having trouble with figuring out a way to implement spells in my game. The problem is that I want to add many spells that are different(like teleportation, telekinesis, fire control etc.). The first thing I tried was making a big class hierarchy like :
Spell -> Passive ->Speed
->Flying
-> Active ->Teleportation
Telekinesis
At the start it seemed good but when I started implementing a lot of spells it started to get messy.
I've searched for other solution and I found about the Entity-Component based system. But I don't think it'll be a good solution.
So do any of you know of any other approach to this problem?
What if you used something like the Strategy Design Pattern and you where to have an interface which defines an method such as ApplySpell() and maybe a Name property and the concrete spell implemented said interface?
That way, for each character, you could iterate over their assigned spells and use the Name property to get the name (maybe you want to list them through a UI or something like that), or maybe store them in a dictionary where the name of the spell is the key, and the actual spell implementation is the value.
What you could do then is that once that the user has selected the spell, all that you need to do is to call ApplySpell() and what the actual spell does is delegated to the class which represents the spell.
In this way, you would not need to worry which spell you need to invoke because everything is being done behind the scenes.
Inheritance is ok, but for properties you can use interfaces or base classes as properties:
class SpellBase
{
public string Name { get; protected set; } // all spells have to have name
public virtual void Animate() { ... } // abstract?
...
}
Then for teleportation
class TeleportationSpell: SpellBase, IEffect
{
... // set name, override Animate() and implement IAreaEffect (as OnSelfEffect() for teleport)
}
interface IEffect
{
public EffectBase Effect {get; set;}
...
}
class EffectBase { ... }
class OnSelfEffect: EffectBase { ... }
class OnTargetEffect: EffectBase { ... }
class OnSelfAndTargetEffect: EffectBase { ... }
Interfaces will make your hierarchy less branchy, but will required more code to implement (which is not really a problem, as you can move common code into methods and call them).
The Entity-Component approch is a good solution for your problem. :)
You should invest more time in understanding it.
You always have to make a decision between "is a" or "have a" relationship.
Where "is a" means inheritance and "have a" means composition.
The thing on EntityComponents is to put every game object attribute into a component class and then just put these components together. You could create every combination of properties without or less code changes (depending on the implementation).
With using that approch it's also easy to create a multiplayer game, because you have just a few places in your code to put the communication stuff.
The other side is you will have a lot of classes and everything is highly decoupled. In general that's a plus and what we want as OO developers.
But for a new developer or a developer with not that high skills, this could be horrible to read.
So i would advise you to choose the entity component approach, because your game will be easier to extend in the future.
Instead of creating multiple classes for each type of magic, just start casing them all in one Magic class and handle them from there as per a trigger?
switch(castID) {
default:
break;
case 1: //air strike
Spell AirStrike = new Spell('AirStrike');
break;
case 2:
...
}
And then have a class for Spell and handle each spell in there based on params sent