I'm creating a 2d Turret Defense game in C# and I have a question about the management of the updates of the turrets.
I want to create the menu over the turret sprite with several options for the upgrade.
My idea is to load the menu from the resources as sprite, assign it as child to the turret gameObject and then manage the comunication between the button to the turret to start the upgrade process.
There is a way to assign a method/function to a resource freshly loaded?
For example (in turret class):
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
myMenuButton.OnMouseDown(){
// do something with the current turret's class
};
Or I have to save the gameObject sprite in resource folder with inside a script that will manage the OnMouseDown method searching the parent script of the turret and use the correct method?
For example (in menu sprite class):
void OnMouseDown(){
GameObject parent = gameObject.transform.parent.gameObject;
turretScript parentScript = (turretScript) go.GetComponent(typeof(turretScript));
parentScript.doUpgrade();
}
Thanks for any help!
I don't know about Sprite itself (documentation may have information about it, did you already research there?)
But I don't see why you cant use Buttons instead of Sprite to handle this "menu behaviour" you wants to do. Button class have an onClick event handler that you can use to assign listners at runtime like
myMenuButton.onClick.AddListener(() => {
//handle click here
});
you can also set methods by lambda expression, just google about it and you'll find a lot of resources to learn how to use it ;)
first create a class which already implements the OnMouseDown and a list of function pointers and attach this class to every single item you are going to use it on beforehand.
public class MouseDownScript : MonoBehaviour
{
public List<Action> Actions = new List<Action>();
void OnMouseDown(){
foreach(Action action in Actions)
action();
}
}
Then you have the ability to:
{
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
// Cache the script
MouseDownScript mouseScript = myMenuButton.GetComponent<MouseDownScript>();
// Add anonymous actions with a lambda as such:
mouseScript.Actions.Add(() => /*Any action here*/);
// Add existing functions as such:
mouseScript.Actions.Add(MouseDownAction));
}
public void MouseDownAction() {}
I don't know Unity but you might be able to inherit from Sprite, add a property to it of type Action and assign that after you create it with Resources.Load
class Program
{
static void Main(string[] args)
{
var s = new Sprite();
s.Action = dostuff;
// outputs Hello
s.Action.Invoke();
Console.ReadLine();
}
public static void dostuff() { Console.WriteLine("Hello"); }
public class Sprite
{
public Action Action { get; set; }
}
}
You can add new class to Sprite when create Sprite
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
myMenuButton.AddComponent<ClassForSprite>();
In ClassForSprite
public class MouseDownScript : MonoBehaviour
{
void onAction1(){
//Code process for action 1
}
void onAction2(){
//Code process for action 2
}
}
I think you'll want to use delegates.
Basically you define a method signature e.g.
public delegate void ClickHandler();
then you define an instance of that signature:
public ClickHandler OnClick;
you can assign a method to OnClick on runtime and execute.
in turret:
public void ExecuteOnTurrent()
{
// do stuff in turret
}
public void LoadSprite()
{
Sprite myMenuButton = Resources.Load <Sprite> ("sprite_menu_to_load");
myMenuButton.OnClick= ExecuteOnTurrent;
}
in sprite
public delegate void ClickHandler();
public ClickHandler OnClick;
public void Update()
{
//- detect click or other action
if(click and OnClick!= null)
{
OnClick();
}
}
Related
I'm creating a unity project where when the user presses a certain button a stat modifier will increase, how would I get this to work using C#? this is the current code but I feel like this is entirely in the wrong direction
public class logicBook
{
public void Equip(Character c)
{
c.logic.AddModifier(10);
}
public int logic;
logic = 0;
}
I'm pretty new to C# and Unity so help would be really apricated!
I believe by button you meant key so if you want to implement that you need first to have your class inherit from Monobehaviour so that you can use your script as a component of a gameobject.
then you should have an Update function (it is a function that gets called every frame) and rihgt there you can check the input
public class logicBook : MonoBehaviour{
public void Equip(Character c){
// do some cool stuff here
}
void Update(){
if(Input.GetKeyDown(/* Key that triggers this */){
Equip(/* Argument here */);
}
}
If you want to have a button in game that triggers this function create a canvas UI in your scene and create a button object as a child of your canvas and then add the function to the button onClick component.
I am currently working on a game, and while designing a Main Menu for the game, I need to be able to access different GameObjects and enable/disable them for the user to access different parts of the menu. The different parts are held under their respective parent GameObjects, for example, all the GameObjects related to the "Options Menu" will be under a single empty GameObject called "Options".
Here is the code pertaining to a script that holds the references to all the different parent objects and the methods to enable them:
public class MenuSwitch : MonoBehaviour
{
public GameObject Main;
public GameObject Options;
public GameObject About;
public GameObject Load;
public void MainMenu()
{
Main.SetActive(true);
Options.SetActive(false);
About.SetActive(false);
Load.SetActive(false);
}
public void OptionsMenu()
{
Main.SetActive(false);
Options.SetActive(true);
About.SetActive(false);
Load.SetActive(false);
}
However. the problem is the navigation of the Menu has to be done by the player by typing 'commands'. For example, they would type '/options' to go to the options menu. This functionality will remain throughout the game, so I made another script to handle player commands from the in-game console. Now how do I call the methods from MenuSwitch, because making a MenuSwitch object inside my console script does not seem to work, it gives an error: "Object reference not set to an instance of an object".
Here is the script handling the commands specified in the main menu:
// Commands
string[] MainMenuCommands = { "./start", "./load", "./about", "./options", "./end" };
private void Update()
{
if (Input.GetKeyDown(KeyCode.Return))
{
// Reset the console
playerInput = console.text;
console.text = "";
// Main Menu Input
if (CheckCommand(playerInput) == true)
{
RunCommand(playerInput, scene);
}
}
}
void RunCommand(string _command, int _scene)
{
// Main Menu commands
if (_scene == 0)
{
if (_command == MainMenuCommands[0]) { StartCoroutine(MM_Start()); }
else if (_command == MainMenuCommands[1]) { MM_Load(); }
else if (_command == MainMenuCommands[2]) { MM_About(); }
else if (_command == MainMenuCommands[3]) { MM_Options(); }
else if (_command == MainMenuCommands[4]) { MM_End(); }
}
}
The MM methods are empty currently, that is where the enabling/disabling will take place. How do I enable or disable the GameObjects inside the MenuSwitch script? The objects are referenced in that script via the Unity inspector.
You could create an empty object and call it MenuManager, attach the MenuScript to it. Then in a script you want to call MenuSwitch functions from declare a class member like so:
public MenuSwitch menuManager;
Drag and drop the MenuManager empty gameObject in the inspector and then you can easily use all the public functions from MenuSwitch script by just calling them from the menuManager variable like so:
menuManager.MainMenu();
menuManager.OptionsMenu();
I hope it was helpful :)
Edit: It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!
I have an issue with the class below called ProjectileWeapon. It is based on an abstract class called Weapon, and that class inherits MonoBehaviour.
Weapon has two abstract functions called BeginCycle and EndCycle which are implemented in the ProjectileWeapon class. Those functions set a variable called "firing".
The problem is, "firing" doesn't ever seem to be set despite the functions being called correctly. I know the functions are called because I can see the prints in the console.
Also, when I use that variable in the update function, it doesn't do anything because the variable never changes.
The OnGUI function is working and is displaying text on screen, however the "firing" variable is never updated.
Am I misunderstanding how to use inheritance?
This class is on the weapon prefab which is then instantiated during the equipping function in the game
public class ProjectileWeapon : Weapon
{
private bool firing;
private float firingTimer;
void Start()
{
print("ProjectileWeapon start");
}
void OnGUI()
{
GUI.Label(new Rect(0,100,100,100), "ProjectileWeapon firing: " + firing);
}
void Update()
{
// this function is called but "firing" is not updated
}
public override void BeginCycle()
{
print("projectile begin cycle");
firing = true;
}
public override void EndCycle()
{
print("projectile end cycle");
firing = false;
}
}
Here's the base class:
public abstract class Weapon : MonoBehaviour
{
public abstract void BeginCycle();
public abstract void EndCycle();
}
EDIT: Here is the code that calls the above
This component is added to the player game object
public class WeaponHandler : MonoBehaviour
{
public bool FireInput { get; set; } // set to true when user holds the mouse button down, and false when let go
public Weapon WeaponBehaviour; // this is the script that does the weapon functionalility. Any subclass of Weapon can be put here e.g. ProjectileWeapon, MeleeWeapon
private bool isFiring = false;
void OnGUI()
{
GUI.Label(new Rect(0,0,100,100), "fire input:" + FireInput + ", isFiring:" + isFiring);
}
void Update()
{
// fire weapon
if (WeaponBehaviour && FireInput && !isFiring)
{
ActivateWeapon();
}
else if (!FireInput && isFiring)
{
DeActivateWeapon();
}
}
private void ActivateWeapon()
{
print("activate weapon");
isFiring = true;
WeaponBehaviour.BeginCycle();
}
private void DeActivateWeapon()
{
print("deactivate weapon");
isFiring = false;
WeaponBehaviour.EndCycle();
}
}
Based on the code you've provided, there are three possible scenarios:
You're not calling the functions. However, if the print functions are called, then you must be calling them.
You're calling both functions, which sets the variable to true, and then to false.
You're overriding the variable with a local variable with the same name. Visual Studio will warn you if that's the case.
It's hard to tell without knowing where the variables are supposed to be called. If you upload the rest of the code, I'm sure the answer will be clear.
It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!
I am trying to tranfer from one scene to another and trigger a function once this happens.
So when i press my play game button on my main menu page it loads a function to begin building the world. I have got the function to build a world but once ive attatched it to a button it has stopped working.
So far I believe this is down to me not fully understanding the method of calling a function from another class for it to run normally.
I begin to define my GameObject as:
private static GameOjbect Play;
This doesnt allow me to assign a GameObject to it within the unity editor. Therefore, i went down the method of using:
GameObject Play = GameObject.Find("PlayScreen");
My GameObeject is active in the heirarchy when this function begins but the program still does not function correctly. To test where the program is encountering an issue I used:
Debug.Log(Play);
Which i believed would just output "PlayScreen" to the debug log as this is the gameobject I am searching for, but this only returns "Null" and my program does not progress any further which is creating a wall.
Below is my main menu code:
public class MainMenu : MonoBehaviour
{
public GameObject PlayScene;
public GameObject SettingsScreen;
public void PlayGame()
{
SceneManager.LoadScene("InGame");
Debug.Log("Loading startup...");
WorldBuilder.Awake();
}
}
Below is my WorldBuilding function:
public class WorldBuilder:MonoBehaviour
{
public static GameObject Play;
public static void Awake()
{
Debug.Log("Finding Scene...");
GameObject Play = GameObject.Find("PlayScreen");
Debug.Log(Play);
}
}
How come my program is not finding the GameObject?
I am still new to C# so any sort of help is appreciated. Thankyou.
Don't make the Awake function static. If you do Unity won't call it.
Also, you are creating a local variable when you do GameObject Play = GameObject.Find("PlayScreen");. If you want to keep it in a static variable, you shouldn't do that. See below:
public class WorldBuilder : MonoBehaviour {
public static GameObject Play;
public static void Awake()
{
Debug.Log("Finding Scene...");
WorldBuilder.Play = GameObject.Find("PlayScreen");
Debug.Log(Play);
}
}
Also, remove the call in PlayGame:
public void PlayGame()
{
SceneManager.LoadScene("InGame");
Debug.Log("Loading startup...");
}
So, I have this crazy idea to have enums pointing to gameobjects.
Here's what I want to do:
/* These enums would hold gameobjects instead of ints */
enum exampleEnum{
AddPanel,
ListPanel
}
public class GUIManager : MonoBehaviour {
void Start()
{
EnablePanel(exampleEnum.AddPanel);
}
void EnablePanel(GameObject panel)
{
panel.setActive(true);
}
}
Is there any way to make this work? Or a workaround?
This might be possible with something other than an enum but I don't know of it if there is and I'm looking through the web for a such a solution.
This would satisfy your requirement, works for any amount of enum values or panels.
// Add this to each of your panels, the enum field can be integrated into your other behaviours as well
public class EnumPanel : MonoBehaviour
{
// configurable from the Editor, the unity way.
public ExampleEnum Type;
}
// Assign all your panles in the editor (or use FindObjectsByType<EnumPanel> in Start())
public class GUIManager : MonoBehaviour
{
// configurable from the Editor, the unity way.
public EnumPanel[] Panels;
void Start()
{
// Optionally find it at runtime, if assigning it via the editor is too much maintenance.
// Panels = FindObjectsByType<EnumPanel>();
EnablePanel(ExampleEnum.AddPanel);
}
void EnablePanel(ExampleEnum panelType)
{
foreach(var panel in Panels)
{
if(panel.Type == panelType)
EnablePanel(panel.gameObject);
}
}
void EnablePanel(GameObject panel)
{
panel.setActive(true);
}
}
I don't know why the answer from: #Paradox Forge was wrong but maybe this will help you.
System.Collections.Generic.Dictionary
I don't have a lot of time to explain the dictionary class but this is how you can use it.
This will cost some performance but has really nice readability
public class GUIManager : MonoBehaviour {
public enum exampleEnum{
AddPanel,
ListPanel
}
//For readability you can also add "using System.Collections.Generic;" on the top of your script
private System.Collections.Generic.Dictionary<exampleEnum,GameObject> exampleDictionary = new System.Collections.Generic.Dictionary<exampleEnum, GameObject>();
private GameObject SomeGameObject;
private GameObject SomeOtherGameObject;
void Start()
{
//You have to add all the enums and objects you want to use inside your GUIManager.
exampleDictionary.Add (exampleEnum.AddPanel, SomeGameObject); //Add panel will be linked to SomeGameObject
exampleDictionary.Add (exampleEnum.ListPanel, SomeOtherGameObject); //List Panel will be linked to SomeOtherGameObject
EnablePanel(exampleEnum.AddPanel);
}
void EnablePanel(exampleEnum examplePanel)
{
if (!exampleDictionary.ContainsKey (examplePanel)) //If the given panel does not exist inside the dictionary
return; //Leave the method
GameObject panelToEnable = exampleDictionary [examplePanel]; //Will return the GameObject linked to given panel
panelToEnable.SetActive(true); //Enable the gameobject
}
}
If you want to know more about the Dictionary class go to: Dictionary