Replicating Unity3D Awake() & Update() methods without Unity - c#

Recently I've made the switch from Unity to a bright & shiny OpenGl/Freeglut solution using Mono in Xamarin Studio. I figured this was a wise move because I wasn't learning what I wanted to learn with Unity's handholding. Apparently this wasn't the wisest of moves but I am committed now. There are two things I miss, very much: Awake() & Update().
It was magic. Any object I wanted could extend monobehaviour & I could run some code every frame. But as a neophyte csharper, I'm not entirely sure how to replicate this outside of unity.
Basically I have my main program; the trimmed down version is this:
using System;
using Tao.FreeGlut;
using OpenGL;
namespace Project
{
class Program
{
private static int width = 1280, height = 720;
public static MyPlayerObj player = new MyPlayerObj();
public static MyMouseListenerObj mouseE = new MyMouseListenerObj();
public static MyKeyboardListenerObj keyboardE = new MyKeyboardListenerObj();
public static MyEnemyObj e1 = new MyEnemyObj();
public static MyEnemyObj e2 = new MyEnemyObj();
public static MyEnemyObj e3 = new MyEnemyObj();
public static void Main(string[] args)
{
Glut.glutInit();
Glut.glutInitDisplayMode(Glut.GLUT_DOUBLE | Glut.GLUT_DEPTH);
Glut.glutInitWindowSize(width, height);
Glut.glutCreateWindow("Project");
Glut.glutIdleFunc(Update);
Glut.glutDisplayFunc(DisplayFunc);
Awake();
Glut.glutMainLoop();
}
public static void Awake()
{
// Run all Awake Methods
}
private static void DisplayFunc()
{
}
public static void Update()
{
Gl.Viewport(0, 0, width, height);
Gl.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// Run All Update Methods
// Then run all LateUpdate methods
Glut.glutSwapBuffers();
}
}
}
In this example, I have some objects such as player, keyboard & mouse listeners, and a bunch of enemies. Now, what I want to do is be able to call each objects Awake method if it has one, or ignore it if it doesn't. This might be something like (pseudocode, forgive me):
public static void Update()
{
interfacedObjects = assembly().findAllObjectsUsingInterface(IUpdateable);
foreach (var item in interfacedObjects)
{
if(item.hasMethod('Update')
item.call('Update');
}
}
Essentially, what I'm trying to do is keep my objects chained to the parent "Monobehaviour". I want to keep my class structure as simple as possible.
public class MyPlayerObj : MyMonoBehavior
{
public void Update()
{
// Do stuff
}
}
public class MyMonoBehavior : IUpdateable
{
public virtual void Update()
{
}
}
Does anyone have any suggestions for me? I know its possible, but I just don't know what this design pattern is called, nor how to execute it.
I should mention that I would prefer to keep this solution within the native codebase, in the interest of learning.

Interesting question.
I don't know how Unity does this behind the scenes, but would love to find out. I think the simplest way for you to implement this is to look at the Observer Design Pattern. The basic idea that you are looking for is to have a MonoBehavior interface or abstract class that everyone inherits from. Then you would have a class that handles the MonoBehaviors by running a loop on every MonoBehavior that exists.

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()
{
// ...
}
}

Trying to change BattleState globally

Hi I'm a completely new to coding and am trying to create a card game. I've watched some tutorials and tried to take things into my own hands but cant seem to figure out something. I currently have a BattleState set up;
public enum BattleState { START, PLAYERMAINPHASE, PLAYERBATTLEPHASE, PLAYERENCORESTEP, ENEMYTURN, WON, LOST }
and would like it so when i change the BattleState with a script, it changes it for every other script that references this BattleState. Sorry for the bad wording. Coding is rough :/
You can use interfaces, create an interface such as IBattleStateChanger and have a method on it
interface IBattleStateChanger{
void ChangeBattleState(YourClass.BattleState state);
}
Then on every script you want the value to change implement this interface as
ClassExample : IBattleStateChanger {}
This will then force you to create a method in the script to change the state
After that, whenever you want to change the value globally on the scripts where you implemented this interface, you can do a foreach loop finding each type of this interface such as
BattleState newState = BattleState.START;
foreach (var obj in FindObjectsOfType<IBattleStateChanger>){
obj.SetBattleState(newState);
}
You could use a static event and attach listeners/callbacks to it like e.g.
public enum BattleState
{
START, PLAYERMAINPHASE, PLAYERBATTLEPHASE, PLAYERENCORESTEP, ENEMYTURN, WON, LOST
}
public static class BattleStateMgr
{
private static BattleState _state;
public static BattleState State => _state;
public static event System.Action<BattleState> OnStateChange;
public static ChangeState(BattleState s)
{
_state = s;
OnStateChange?.Invoke(_state);
}
}
public class OtherScript : MonoBehaviour
{
private void Awake()
{
BattleStateMgr.OnStateChagne += OnBattleStateChange;
}
private void OnDestroy()
{
BattleStateMgr.OnStateChagne -= OnBattleStateChange;
}
private void OnBattleStateChange(BatlleState newState)
{
Debug.Log($"Changed Battle State to{newState}", this);
}
}
I believe you are confused about the scope of your variable. Each script you place an instance of the enum Battlestate, is a local version of that enum. If you want the reference to be global, you will need to have a central point where all scripts can grab this reference.
public class BattleManager : MonoBehaviour
{
private BattleState battleState;
// setter / getters
public BattleState GetBattleState(){return battleState; }
public void SetBattleState(BattleState state){ battleState = state; }
}
You are going to want to make a single script that holds the only reference to your enum Battlestate, then have your other scripts reference the variable.
public class OtherScript : MonoBehaviour
{
// assign this reference in the inspector
[SerializeField] private BattleManager bm = null;
private void YourFunction()
{
if(bm.GetBattleState() == BattleState.randomStateHere)
{
// run logic here
}
}
}
There are a number of ways to go about doing this, but the easiest would most likely be by declaring the variable static.
public class BattleManager : MonoBehaviour
{
private static BattleState battleState;
// setter / getters
public static BattleState GetBattleState(){return battleState; }
public static void SetBattleState(BattleState state){ battleState = state; }
}
public class OtherScript : MonoBehaviour
{
private void YourFunction()
{
if(BattleManager.GetBattleState() == BattleState.randomStateHere)
{
// run logic here
}
}
}
I do not know how many scripts you need to access this variable, but if it is only a handful, I would instead assign references to the script that holds the enum to each of the scripts that need it. I would avoid simply using static as it is the easy approach but creates what is called a code smell. The reason for this is OOP (object-oriented programming) by design should generally not have mutable global variables.
If you have a single instance of an object that manages all of your battle activity and a lot of scripts need to access it, you can look into the Singleton pattern. As you are new to programming, I would not implement this pattern until you understand the time and place to properly use it. You can also completely avoid using it by properly assigning the references you need in the inspector or by using a Object.FindObjectOfType in either Start or Awake.

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.

Get Component to the Script vs Instance of the Script itself

In order to get variable(s), function(s) in another class, I have known 2 ways of doing this. First, is to use Get Component to the Script that we want to get the variable(s), function(s) into. Second, is to use Instance of the Script itself.
So I have made the following code:
First case: Get Component to the Script itself
public class Manager : MonoBehaviour
{
private AnotherManager _anotherManager;
private void Awake()
{
_anotherManager = GameObject.Find("Managers").GetComponent<AnotherManager>();
}
private void Start()
{
_anotherManager.myIntVariable = 10;
_anotherManager.MyFunction();
}
}
public class AnotherManager : MonoBehaviour
{
public int myIntVariable;
public void MyFunction()
{
}
}
Second case: Use Instance of the Script itself
public class Manager : MonoBehaviour
{
private void Start()
{
AnotherManager.instance.myIntVariable = 10;
AnotherManager.instance.MyFunction();
}
}
public class AnotherManager : MonoBehaviour
{
public static AnotherManager instance;
public int myIntVariable;
private void Awake()
{
instance = this;
}
public void MyFunction()
{
}
}
My question is: Is there any difference between those cases? In terms of good practice of coding for programmer or performance or it is just a matter of programmer's perspective or whatever else?
Thanks
The second example is the what is known as the Singleton Pattern and should be used very sparingly.
I try to never use the first approach either where you find the gameobject and hope it exists.
You can expose a field for the Unity Inspector so that you can wire it up the same as you can expose any other variable
public AnotherManager AnotherManager;
Alternatively, if you hate using public all over the place like that, like me, you can also indicate to Unity that you wish to expose this variable in the inspector with the SerializeField attribute
[SerializeField]
private AnotherManager anotherManager;
With both of these methods, you can then drag an an object that has the AnotherManager component attached into the field in the inspector.
If instantiated objects need access to this, you will need to wire it up when it is instantiated.
If you need help attaching it in unity I can attach some screenshots.

How to convert this C++ code into C# code? What syntax am I forgetting?

Google is flooded with Unity3D & XNA resources. It is quite difficult to search for anything dealing with C# without the results being overwhelmingly Unity or XNA based. To even find good tutorials, I had to find a book on amazon and then google the author with -XNA -Unity, lol.
My C++ code is organized nicely with each separate major class being its own file, in an organized directory.
Game.cpp
Init.cpp
Render.cpp
Update.cpp
Game.cpp, being main(), where my game runs and calls Init, Render, and Update.
class Game
{
public:
Game();
private:
void Init();
void Update(sf::Time deltaTime);
void Render();
sf::RenderWindow mWindow;
};
int main()
{
Init();
while (mWindow.isOpen())
{
Update(TimePerFrame);
Render();
}
}
And of course, Init.cpp
#include "Game.hpp"
void Game::Init()
{
mWindow.setVerticalSyncEnabled(true);
}
Now, I have the C# bindings for SFML and switched from Code::Blocks to VS2012. I've been working with C++ for so long that I am having some trouble with C#. I went through some tutorials to refresh myself with the syntax, but there is something I'm still not remembering. Obviously, this is a dumb question. However, google is not very much help due to how overwhelmingly swarmed C# is with Unity & XNA results, and I just can't remember how to do this right.
I want to have the same separate files,
Game.cs
Init.cs
Update.cs
Render.cs
However, I can't even get it to function in a single class.
Game.cs
class Game
{
public void Main()
{
// Create the main window
RenderWindow mWindow = new RenderWindow(new VideoMode(1200, 1200), "SevenBlack");
mWindow.Closed += new EventHandler(OnClose);
Color windowColor = new Color(0, 0, 0);
//Initialize Game
Init();
while (mWindow.IsOpen())
{
Update(TimePerFrame);
}
Render();
}
}
private void Init()
{
mWindow.setVerticalSyncEnabled(true);
}
private void Update(long dt)
{
}
private void Render()
{
mWindow.Clear(windowColor);
mWindow.Display();
}
}
I get "The name 'mWindow' does not exist in the current context.
private void Game::Init()
{
mWindow.setVerticalSyncEnabled(true);
}
Does not work either, since this is C#.
mWindow is not declared as a class level variable, so its only scope is in main. Do this to allow the variable to be seen to the entire class.
class Game
{
private RenderWindow mWindow;
public void Main()
{
// Create the main window
mWindow = new RenderWindow(new VideoMode(1200, 1200), "SevenBlack");
...

Categories