How to assign multiple functions to a button at different times? - c#

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
}
}

Related

Unity c#: Code-structure or how to access a method/variable from another class/script?

I have two classes: Menu_Buttons, in which there are definitions for methods executed on clicking different buttons in the menu, and PauseMenu, which defines what happens when the Menu key is pressed during the game.
Menu_Buttons:
public class Menu_Buttons : MonoBehaviour
{
public void Menu_NewGameClick()
{
SceneManager.LoadScene(1);
}
public void Menu_ContinueClick()
{
Debug.Log("This will continue the game from the last save");
}
public void Menu_LoadGameClick()
{
SceneManager.LoadScene(1);
Debug.Log("Another menu will show to choose which save to load");
}
public void Menu_SaveGameClick()
{
SaveItem();
Debug.Log("This will save the game");
}
public void Menu_OptionsClick()
{
Debug.Log("This will show the game options");
}
public void Menu_QuitClick()
{
Application.Quit();
Debug.Log("The Game should quit now");
}
}
PauseMenu:
public class PauseMenu : MonoBehaviour
{
//private bool isPauseMenuOpened = false;
public GameObject pauseMenu;
void Update()
{
if (Input.GetKeyDown(KeyCode.B))
{
if (pauseMenu.activeSelf) { ClosePauseMenu(); }
else { OpenPauseMenu(); }
}
}
public void OpenPauseMenu()
{
pauseMenu.SetActive(true);
Cursor.visible = true;
Cursor.lockState = CursorLockMode.Confined;
//isPauseMenuOpened = true;
Time.timeScale = 0f;
}
public void ClosePauseMenu()
{
pauseMenu.SetActive(false);
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
//isPauseMenuOpened = false;
Time.timeScale = 1f;
}
}
I wanted to add another method called Menu_ResumeClick, which would resume the game from the Pause Menu. Of course, I could just create this method in the PauseMenu script and then everything is fine. It looks like this:
public void Menu_ResumeClick()
{
ClosePauseMenu();
}
But since I would like to keep things organised, I thought it would be better to put this method in the Menu_Buttons script along with all the other similar methods. So I tried this:
public void Menu_ResumeClick()
{
PauseMenu.ClosePauseMenu();
}
And then problems begin... I get an error: an object reference is required for the non-static field method or property. Then if I change any of those classes to static, I get errors saying: cannot declare instance members in a static class. Not to mention that static classes canot inherit from MonoBehaviour. Maybe I would be able to solve those problems somehow, but the thing is I don't want to change the whole code just because I would rather have a method in another class. It's just for keeping things organised, nothing more.
I have to admit that I'm a bit frustrated by how these things work. I can easily put the Menu_ResumeClick() method in the PauseMenu class, but in the future it may be difficult to keep track of things if I have various methods scattered around different scripts. The most reasonable solution is to put every menu button in the Menu_Buttons class and then access them from there, but it poses problems that I described. Actually this is not the first time when I'm having problems with accessing methods or variables from other classes - there always seem to be some difficulties. It seems the best way to write code would be to just have a single class for the whole game because then I would be able to access absolutely anything easily - but again the problem would be with keeping things organised.
So, the question is: can I easily use methods (or variables) from other classes, without changing the whole code for this purpose? In other words can I somehow just call a method from another class like this: className.MethodName(); or set a variable from another class like this: className.varName = 2; without making everything static, etc.?
And a bonus question: If it's not possible, then how should I structure my code? Should I try to squeeze as many things as possible into a single class to be able to access them easily, should I make classes static, whenever it's possible, etc.?
In PauseMenu, you can add a field for the Menu_Buttons, which Unity can serialize, so you can pull the GameObject (what contains the Menu_Buttons) in the inspector, and you can call its public methods (and access its public members) from PauseMenu.
public Menu_Buttons MenuButtons;
// or (I much more like this version, keeping things as encapsulated as possible)
[SerializeField] private Menu_Buttons _menuButtons;
private void Resume() => _menuButtons.Menu_ResumeClick();
Edit based on comments:
Both script can have references to each other. As both logically related, I wouldn't separate them, because with the references, we couple them anyway.
Example:
public class Menu_Buttons : MonoBehaviour
{
[SerializeField] private PauseMenu _pauseMenu;
public void Menu_ResumeClick() => _pauseMenu.ClosePauseMenu();
// ...
}
public class PauseMenu : MonoBehaviour
{
[SerializeField] private Menu_Buttons _menuButtons;
// ...
public void ClosePauseMenu()
{
// ...
}
}

Unity3D Game with State Machine, Coroutines and Commands

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?

How should you initialize constant data that needs parameters from another part of the code?

I don't think I explained my question very well in the title, so I'll do my best to do it here.
I have an abstract class called Song, a class that extends it MidiSongand then I have a SongCreator interface and a MidiSongCreatorclass that implements it. I would like to have a way to store lots of SongCreators so I can call their Create method but the problem is, since the SongCreators will each be a MidiSongCreator I am wondering how I should initialize each MidiSongCreator since it takes a MIDIPlayer and other things to help initialize it which doesn't have a static reference to it. Should I create a static class that holds lots of SongCreators? Should I not make the SongList class static?
What is looks like:
public abstract class Song{
public IList<Playable> notes { get; private set; }
public SongPlayData Start(){
// calls onStartEvent
return CreateSongData();
}
protected abstract SongPlayData CreateSongData();
public bool Update(SongPlayData songData, float songTime,List<SongPlayer> players) { // note that the players list is a list of people who are playing this game (this is a rhythm game) (doesn't have anything to do with MIDIPlayer
}
public void End(){
//calls end event
}
}
public class MidiSong : Song { // this is the class that needs the MIDIPlayer parameter
public MIDIPlayer midiPlayer;
protected MidiSong(MIDIPlayer player){
this.midiPlayer = player;
}
protected override SongPlayData CreateSongData() {
return new MidiSongData(midiPlayer);
}
}
public interface SongCreator<out T> where T : Song {
T Create();
}
public class MidiSongCreator : SongCreator<MidiSong>, IListenerObject { // this is the class that I need to store lots of instances of. the midiPlayer will probably be the same every time
private MIDIPlayer player;
public MidiSongCreator(MIDIPlayer player) {
this.player = player;
Init();
}
private void Init() {
player.midiListener.listener = this;
//
}
private void Clear() { // resets all the data so we can create another Song if we need to (even without entering stuff in)
if(player.midiListener.listener == this) {
player.midiListener.listener = null;
}
}
public MidiSong Create() {
MidiSong r = new MidiSong(player);
// I'm still going to implement calls to other methods from midiPlayer
Clear();
return r;
}
public void OnLoad(MidiFile file) {
// does stuff to load midi file (deals with individual events)
}
}
public class MasterSong : MonoBehaviour { // this should initialize last btw (It's in the Script Execution Order)
public MIDIPlayer midiPlayer;
public Song song;
public SongPlayData playData;
// Use this for initialization
void Start() {
// this is where I'd like to reference a SongCreator and call it's create method and Start the song
//for instance:
song = SongList.SONG_NAME.Create();
playData = song.Start();
}
void Update() {
}
}
It's a RhythmGame made with unity, but I didn't add the unity tag because I feel that this is more of a C#/design thing.
Also note, that I have my classes much more organized that just one file with all these.
I'm looking for ways to improve on the design that I have.
This is a design problem, domain design!
I suggest don't write code yet. Create a class diagram, with pen and paper, don't need to use tools in the beginning.
Try to determine entities - classes, interfaces etc - and the relationship among them. Just use boxes and arrow, don't need to put details yet. With boxes and arrows, you will be able to have a picture of your domain much more clearly. Keep refining and changing it, still at this high level, without details, until you are satisfied.
Then, step by step, refine it by adding details/properties such attributes and methods. This may cause to change the diagram from the first step.
I intentionally did not refer to specifics of you questions like the classes and interfaces you mentioned. Since, there is not enough information to comment on that. Secondly, and more importantly, you should start at high level of design and once that is done, then do coding based on your design.

Change a variable's accessibility during runtime

I wish to know if there is room for changing a variable's accessibility during runtime in Unity C#? The reason I want this is to hide unnecessary public variables when my script uses another state. I tried some dynamic variables, but I get errors that it can't be found.
I have that springDistance but I want to use it only when the state of the trapType is springOut.
Some code:
public class SpringTrap : MonoBehaviour
{
private Transform objectToMove;
// Use this for initialization
public enum TypeOfTrap
{
springOut
}
[Tooltip("Set the type of trap here to use.")]
public TypeOfTrap trapType = TypeOfTrap.springOut;
public float springDistance;
void Start ()
{
objectToMove = transform.FindChild("ObjectsToAnimate");
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
//if the player has entered the danger zone
switch (trapType)
{
case TypeOfTrap.springOut:
break;
}
}
}
IEnumerator SpringOut(float springDistance)
{
float deltaDist = 0.0f;
while(deltaDist < springDistance)
{
objectToMove.position +=
}
}
}
Manipulation of the inspector is done with custom editor scripts. There you can display things, e.g. based on conditions.
Here is an example that does a very similar thing to the requested (displaying different things based on an enum field):
http://answers.unity3d.com/questions/417837/change-inspector-variables-depending-on-enum.html
An easier way to do what you are trying to achieve is to create an abstract trap class and then subclass different types of traps.
The abstract trap class handles collisions and then calls the abstract method Trigger.
public abstract class Trap : MonoBehaviour {
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
Trigger(other.transform);
}
protected abstract void Trigger (Transform victim);
}
A spring trap can then be easily created by extending Trap and overriding the Trigger method.
public class SpringTrap : Trap {
public float SpringDistance;
protected override void Trigger (Transform victim) {
victim.position += Vector3.up * SpringDistance;
}
}
Now you don't have to worry about hiding irrelevant properties based on trap type because the only properties that will appear in the inspector are those you add to that variation of trap.

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!

Categories