Unity3D Game with State Machine, Coroutines and Commands - c#

I'm having some issues with my game architecture and the way to handle coroutines. I am on a small tile game where the player has to click on some tiles. Those tiles contains different kind of ennemies, objects or are just empty. The feedback actions are differents depending on which entities are clicked. Basically, the player moves on an empty tile, attacks ennemies then get some loot, etc.
So it's turn based and the player has action points to use before giving the hand to the ennemies. Then they move, attack the player regarding their own behavior.
The problems I struggle with are the tweens and coroutines stuff. I would like to wait all the animations linked to an action before playing the next one.
What I have now is a 'master class' handling states and player inputs. Each states are based on the same interface with generic actions like: Init(), Play(), End().
Firstly I started by chaining IEnumerator with all inheritances but I found it a bit messy so I changed my mind.
Secondly I refactored all the actions in the form of ICommand (Hit, Move, Die, Whatever...). I get an IEnumerator from the command when calling Resolve(). I add those command to a queue with some infos like delays and so on. When all the actions are queued, my animation manager triggers all the animation with starting some coroutine StartCoroutine(myCommand.Resolve()).
But I'm still not sure of what I am doing or if I am just putting too much complexity. And I don't know how to simply handle using a weapon.
internal class PlayerTurnDungeonState : IDungeonState
{
DungeonApplication app;
public PlayerTurnDungeonState(DungeonApplication app)
{
this.app = app;
}
public void Init()
{
app.player.RestoreMana();
}
public void Play(Player player, Entity target)
{
if (player.isReady)
{
player.isReady = false;
IItem item = player.GetActiveItem();
if (target is Opponent)
{
if (player.CanUse(item) && player.CanTarget(item, target))
{
app.animationManager.AddAnimation(
player.UseItem(item, target) //returns UseItemCommand
);
}
}
else
{
(...)
}
}
}
public void PlayerCheck(Player player, Entity entity)
{
if (player.life.value == 0)
app.GameOver();
else if (player.mana.value == 0)
app.EndOfTurn();
}
public IDungeonState GetNextTurn()
{
return new OpponentTurnState(app);
}
public void End()
{
}
}
public class UseItemCommand : ICommand
{
private IItem item;
private Entity target;
private Entity caster;
public UseItemCommand(Entity caster, IItem item, Entity target)
{
this.item = item;
this.target = target;
this.caster = caster;
}
public IEnumerator Resolve()
{
yield return caster.ConsumeAP(item.apCost);
yield return item.Use(caster, target);
}
}
class Weapon : Item
{
(...)
public override IEnumerator Use(Entity caster, Entity target)
{
yield return base.Use(caster, target);
yield return target.Hit(this.power.value);
}
}
public abstract class Item : IItem
{
(...)
public virtual IEnumerator Use(Entity caster, Entity target)
{
this.durability.value--;
}
}
Example of a flow: During the PlayerTurnState, when I click on a tile occupied by a monster, it sends the input to the master class. If the player is ready to do some action, I call state.Play(player, monster) the player checks if the weapon can be used then hit the poor monster. I have to handle a cast animation, an attack animation, a hit animation then sometimes the monster death animation etc. and maybe with some objects I don't need any IEnumerator..
So I don't know how to handle properly all that stuff >_<' and in that case I don't know where and how add my PlayerCheck() from the PlayerTurnState.
My target is to have flexibles class to manage and decline many objects with differents stats or behavior.
What could be the best way to manage that? What I am doing wrong there?

Related

Data Persistence between Scenes in Unity creates multiple instances, regardless of conditional destroying of extra Game Objects

I am creating a game where the first scene will have a generated map saved as a 2D array, then the next scene takes care of all the combat. Once that's done, user needs to go back to the first scene and see the same map. I have followed Unity's tutorial on Data persistence, and as you can see in the code below I am checking twice if an Instance is not null and destroying the object if its not.
The problem is, that every time I go back from combat scene to the map scene, it creates another instance of WorldMapManager, and generates another map on top of the existing one.
Where am I going wrong to stop creation of unnecessary extra copies of WorldMapManager object?
public class WorldMapManager : MonoBehaviour
{
public static WorldMapManager Instance { get; private set; }
public static int _mapSizeX = 4;
public static int _mapSizeY = 4;
public static int _playerScore;
public static int _playerCells;
public static int _enemyScore;
public static int _enemyCells;
public static GameObject[,] _map;
public static GameObject _startingPoint;
public static Vector2Int _playerBase;
// Awake is called before Start
void Awake()
{
if (WorldMapManager.Instance != null)
{
Destroy(gameObject);
}
else
{
DontDestroyOnLoad(gameObject);
}
SceneManager.sceneLoaded += OnLevelFinishedLoading;
}
// Initialize the map
public void InitMap()
{
// Map Generation happens here
}
// OnSceneLoaded is called when a scene is loaded
void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
if (scene.name == "WorldMap")
{
if (WorldMapManager.Instance != null)
{
Destroy(this);
} else
{
InitMap();
}
}
}
}
I have been stuck on this for several days trying different approaches, but I'm all out of ideas, so thank you very much for any help.
By the looks of your code it seems you never ever set the WorldMapManager.Instance. So it will always be null and always go on to DontDestroyOnLoad(gameObject);.
So add the assignment to Instance too.
Instance = this;
DontDestroyOnLoad(gameObject);
For the same reason in void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode) the InitMap() will be executed. No need to set anything to Instance here ofcourse.

Is it possible to set a method of a class from another script in Unity C#?

I'm working on a weapon system that for my fps game. The player class contains it's own methods that might need to change when a weapon is used. The problem is I want to define the methods that will be replaced with player class' methods on the weapon class.
For example I have a shooting coroutine which uses private members of Player class, I want to change the corouitine using delegates but a coroutine defined in the Weapon class won't be able to access those members.
I know that I can define the coroutine that will come with given Weapon at the Player class and change it according to the attached item, but for clarity of the code, I want to define the shooting corouitines on the Weapon classes. Is there any approach to overcome this issue? Thanks in advance.
If I understand you correctly, something like this should work?
public interface IWeapon
{
void Shoot();
}
// -------------------------------------
public class WaterGun : IWeapon
{
void Shoot()
{
// Shoot water?
}
}
// -------------------------------------
public class LaserPistol : IWeapon
{
void Shoot()
{
// Shoot laser?
}
}
// -------------------------------------
public class Player {
IWeapon weapon;
void Start()
{
this.weapon = new WaterGun();
// later
this.weapon = new LaserPistol();
}
IEnumerator Shoot()
{
// player shoot logic here then weapon-specific logic ->
this.weapon.Shoot();
}
}
But if you simply want to keep a function in a variable, there are ways to do that too, for example:
Action shootFunction;
var waterGun = new WaterGun();
shootFunction = waterGun.Shoot; // assigning a reference to the function without executing the method
shootFunction(); // calls waterGun's Shoot() method
I think you need to detail what you have & what you want to achieve in order to get a good answer here.
It looks like your question boils down to:
I want to define the shooting corouitines(sic) on the Weapon classes
Yeah, we can do that. Let's start by looking at the bits we'd need. In this scenario, it makes sense that we use an interface:
public interface IWeapon
{
bool weaponFiring {get;}
IEnumerator StartWeaponFire ( Player player );
}
Let's look at a sample weapon:
public class WaterPistol : IWeapon
{
public bool weaponFiring { get; private set; }
public IEnumerator StartWeaponFire ( Player player )
{
weaponFiring = true;
Debug.Log ( "Squirt!" );
// Do your weapon logic/animation/cooldown here ..
yield return new WaitForSeconds ( 0.5f );
// We can acccess the 'player' data because we've sent a reference as an argument.
player.currentHealth -= 1;
Debug.Log ( "..." );
weaponFiring = false;
}
}
Now, to run the StartWeaponFire is just as easy as if the coroutine were actually on the player, but it's on the IWeapon instead.
public class Player : MonoBehaviour
{
// An example of data on this player class.
public float currentHealth { get; set; }
// A reference to the current weapon. Has the coroutine we want to start.
public IWeapon currentWeapon { get; set; }
// This can be used to manually stop a coroutine if needed.
private Coroutine _weaponCoroutine;
private void Update ( )
{
if ( Input.GetMouseButton ( 0 )
&& currentWeapon != null
&& !currentWeapon.weaponFiring )
{
_weaponCoroutine = StartCoroutine ( currentWeapon.StartWeaponFire ( this ) );
}
}
}
Notice we're starting a coroutine which has been defined on the currentWeapon, and we're sending through a reference to the Player class. The other method, the 'coroutine' in this case, can then call the public fields, properties and methods of the Player instance.
This is great way to enable an item to define a "coroutine" but allow a specified object to run that coroutine code. This scenario would allow you to have multiple 'players' be able to run the same coroutine, and you don't need to clutter your `Player' class with code for each individual weapon you might include in the game.

How to assign multiple functions to a button at different times?

I want a button to do multiple things but not at the same time.
For e.g. in the game, when the player comes near the door and press the button, i want it to open the door. And when player comes near a weapon and press the SAME button, i want the player to pick up the weapon.
P.S. I'm making a game for mobile.
You could for example use an enum and set a value/type on it.
You can set a value when you are near a door, for example, and overwrite it when you are near a weapon.
As soon as the button is pressed, you simply check the value of the enum and execute an action based on the value.
enum Actions
{
OpenDoor,
NearWeapon
}
You actually do not need different callbacks for this.
I would have one single callback react differently to whatever you are close to.
This is pretty much the same as here. Slightly different use case but the principle is similar.
There is basically two main option
Either the target objects implement the logic => use a common interface/base class
Your player implements the logic => use whatever to differ between the object types
Then your player could check what it "is close to" - I will just assume physics again but how exactly you check what you are "close to" is up to you - and interact with that target object.
Interface
You could simply have a shared interface like e.g.
public interface IInteractable
{
void Interact(/*pass in whatever arguments you need e.g.*/Player player);
}
and then have your classes implement it and the logic e.g.
public class Door : MonoBehaviour, IInteractable
{
public void Interact(Player player)
{
// whatever
}
}
and
public class Weapon : MonoBehaviour, IInteractable
{
public void Interact(Player player)
{
// whatever
}
}
and then assuming you use physics (triggers) you could do e.g.
public class Player : Monobehaviour
{
[SerialzieField] private Button button;
// we will store multiple close objects so we can smoothly transition between them in case
// we are close to multiple ones - up to you of course
private readonly List<IInteractable> currentActives = new();
private void Awake()
{
i(!button) button = GetComponent<Buton>();
// you attach a snigle callback
button.onClick.AddListener(Interact);
}
private void OnTriggerEner(Collider other)
{
// does this object have an IInteractable
var interactable = other.GetComponentInParent<IInteractable>();
if(interactable == null) return;
currentCloses.Add(interactable);
}
private void OnTriggerExit(Collider other)
{
// was the object an IInteractable?
var interactable = other.GetComponentInParent<IInteractable>(true);
if(interactable == null) return;
currentCloses.Remove(interactable);
}
private void Interact()
{
// some Linq magic to pick the closest item - again up to you
var currentClosest = currentCloses.Select(item => (MonoBehaviour)item).OrderBy(item => (item.transform.position, transform.position).sqrMagnitude).FirstOrDefault();
if(currentClosest == null) return;
// now let that object handle the interaction
// this class doesn't need to know what exactly this means at all
currentClosest.Interact(this);
}
}
This is of course a little bit inflexible and you always need to have a common shared interface and pass along all needed parameters. It can also get quite dirty if e.g. the weapon implements its own "pick up" - doesn't feel quite right.
Different Types
You could simply have different types (components) on your target objects, lets say e.g.
// have a common shared base class
public abstract class Interactable : MonoBeaviour
{
}
and then have derived types
public class Door : Interactable
{
// they can either implement their own logic
public void Open()
{
// whatever
}
}
and e.g.
public class Weapon : Interactable
{
// this one is purely used to differ between the type
// we rather expect whoever uses this will implement the logic instead
}
and then pretty similar as above
public class InteractionController : Monobehaviour
{
[SerialzieField] private Button button;
private readonly List<Interactable> currentActive = new();
private void Awake()
{
i(!button) button = GetComponent<Buton>();
button.onClick.AddListener(Interact);
}
private void OnTriggerEner(Collider other)
{
var interactable = other.GetComponentInParent<Interactable>();
if(!interactable) return;
currentCloses.Add(interactable);
}
private void OnTriggerExit(Collider other)
{
var interactable = other.GetComponentInParent<Interactable>();
if(!interactable) return;
currentCloses.Remove(interactable);
}
private void Interact()
{
var currentClosest = currentCloses.OrderBy(item => (item.transform.position, transform.position).sqrMagnitude).FirstOrDefault();
if(currentClosest == null) return;
// just that this one you check which actual type this component has
// and deal with it accordingly, completely customizable
switch(currentActive)
{
case Door door:
door.Open();
break;
case Weapon weapon:
PickupWeapon(weapon);
break;
default:
Debug.LogError($"Interaction with {currentActive.GetType().AssemblyQualifiedName} is not implemented!");
break;
}
}
// Just as example that the target doesn't even need to implement the method itself
private void PickupWeapon(Weapon weapon)
{
// whatever
}
}

XNA - how to use the same key for different options?

So I have this code: Variables:
enum gameState
{
gameLoading,
mainMenu,
gameOptions,
levelSelect,
gamePlaying,
gameOver
}
In the Update() method:
if (CurrentGameState == gameState.gameLoading)
{
if (Keyboard.GetState().IsKeyDown(Keys.E))
{
graphics.ToggleFullScreen(); //?
}
graphics.ApplyChanges();
}
...
//gameState.gameLoading logic
if (Keyboard.GetState().IsKeyDown(Keys.Enter))
CurrentGameState = gameState.mainMenu;
So what I want is to have Enter pressed in gameState.gameLoading and both resolution is fullscreen and the gameState is equal to mainMenu. When in gameState.mainMenu the resolution can't be changed from fullscreen to windowed by pressing enter. How to achieve this? Maybe using list?
I think you should have different classes for any gameState if they need to have different behaviours. This will ensure you that each Update affect only its own gameState.
Since you've already decided what the states of your game are, why don't use the State Machine design pattern to control your input handling behaviour?
This pattern's agenda is to delegate the work from the actual object to its states. What you do is create a class with an Update() method for each state like pinckerman suggested, and enter all the input handling + state transition logic there. You can find an example here: http://sourcemaking.com/design_patterns/state.
When I use it, I detach the context from it's states and transitions completly by using an abstract state class and inherit from it. This makes it easier to change the state machine if needed.
Here is a quick example:
public class Context
{
private GameState state;
public void Update()
{
state.Update();
}
public void ChangeState(GameState nextState)
{
state = nextState;
}
}
public abstract class GameState
{
protected Context context;
public virtual void Update()
{
// some basic implementation if you want.
}
}
public class GameLoadingState : GameState
{
public override void Update()
{
// Handle key presses.
context.ChaneState(new MainMenuState(context));
}
}
public class MainMenuState : GameState
{
public override void Update()
{
// Handle key presses in some other way.
// Change state if needed.
}
}
Again, if you don't like the implementation of the passive context, you can change it to be more active.
Hope this helps!

Return multiple types from single method in C#

I am working on a basic game and 2D engine in Xna/C# and I am trying to simplify a few things in it. I have a base class of Entity2D from the engine and two classes specific to the game that inherit from it: Tower and Enemy. In my engine, rather than having two separate lists, one for Towers and one for Enemies I would like to combine them together into a single generic list. I then I have the problem of when I need to return a Tower from the list or an Enemy from the list. I know that I can use typecasting from the engine object:
class Entity2D {...} //engine object
class Tower : Entity2D {...} //game specific
class Enemy : Entity2D {...} //game specific
//In engine:
public Entity2D GetEntity(int index) { ...return objects[index];}
//Somewhere in the game
{
Enemy e = GetEntity(0) as Enemy;
if(e != null)
//Enemy returned
Tower t = GetEntity(0) as Tower;
if(t != null)
//Tower returned
}
Of course this seems really inefficient.
I have also looked into the is keyword a bit, and it seems that works like so:
Entity2D entity = GetEntity(0);
if(entity is Tower)
{
Tower t = (Tower)entity;
t.DoTowerThings();
}
Still that results in returning a base object and using even more memory to create a second object and typecast into it.
What would really be nice is if there is a way to do something like this:
//In engine:
public T GetEntity(int index)
{
if(T == Tower) //or however this would work: (T is Tower), (T as Tower), etc
return objects[index] as Tower;
else if(T == Enemy)
return objects[index] as Enemy;
else return null;
}
Enemy e = GetEntity(0);
But then that breaks the engine portion of having the engine and game be seperate
I am looking for the clearest as well as most memory efficient way to go about this while still having Entity2D be engine based and avoid having Tower or Enemy in the engine at all.
Any suggestions would be welcome!
Thanks!
Nearly there!
//In engine:
public T GetEntity<T>(int index) where T : Entity2D
{
return objects[index] as T;
}
//Somewhere else:
Enemy e = GetEntity<Enemy>(0);
Note: if objects[index] is NOT a T, it will return null instead.
However, if it was my game, I would just keep separate lists for each type of object.
If you have to disambiguate the return value, then the structure of the code is bad, the objects are not logically similar and you would be better off having two lists with a separate accessor for each type.
However, if the types are very similar and you only need to disambiguate between them rarely, then do this:-
abstract class GameEntity : Entity2D
{
abstract public void TowerOnlyFunction (args); // need to look up exact syntax, but you get the idea
abstract public void EnemyOnlyFunction (args);
void CommonFunctions (args);
}
class Tower : GameEntity
{
public void TowerOnlyFunction (args)
{
// code
}
public void EnemyOnlyFunction (args)
{
// empty
}
}
class Enemy : GameEntity
{
public void TowerOnlyFunction (args)
{
// empty
}
public void EnemyOnlyFunction (args)
{
// code
}
}
//Somewhere in the game
void DoSomethingWithTower ()
{
GameEntity e = GetEntity(0);
e.TowerOnlyFunction ();
}
You can do this, (full code on Github)
public T GetEntity<T>(int index) where T : Entity2D
{
return list.ElementAt(index) as T;
}
If the element is not the expected type it will return null.
Tower t = GetEntity<Tower>(0);

Categories