I have several game objects; the idea is that each time you click on any of them a pop up window with a button shows up. Depending on the object you clicked, the pop up's button performs a different function.
This is the code I'm currently using:
Script PopUpCaller- attached to each of the game objects:
public GameObject popUp;
string rewriteThis;
void Start()
{
rewriteThis = gameObject.name; // Each game object's name = desired method's name. E.g. if the method's name is ActionA() then the game object's name would be ActionA.
}
public void TaskOnClick()
{
popUp.SetActive(true);
popUp.transform.position = Input.mousePosition;
PopUp.instance.rewriteMethodName = rewriteThis;
}
Script PopUp - attached to the pop up window
public static PopUp instance;
public string rewriteMethodName;
private void Awake()
{
instance = this;
}
void Start()
{
gameObject.SetActive(false);
}
public void DoThings()
{
GameObject.Find("MapManager").SendMessage("rewriteMethodName");
}
Script Manager containing the methods for the game objects.
public void ActionA() //named like the corresponding game object
{
//stuff to be done
}
public void ActionB() //named like the corresponding game object
{
//other stuff to be done
}
When I run the code as it is I receive the "SendMessage rewriteMethodName has no receiver!".
Now when I change "rewriteMethodName" in the PopUp script to "ActionA" or "ActionB" everything starts working, which suggests that the Manager script doesn't recognize "rewriteMethodName" even if it returns the string "ActionA" or "ActionB".
I also tried doing the same via MethodInfo and making Manager public static but it didn't work out either:
MethodInfo method =Manager.instanceManager.GetType().GetMethod("rewriteMethodName", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
method.Invoke(Manager.instanceMapManager, null);
Any suggestions how to fix it?
Thank you!
rewriteMethodName holds the method name, so should be passed in SendMessage instead of the string “rewriteMethodName”. To close out the question:
public void DoThings()
{
GameObject.Find("MapManager").SendMessage(rewriteMethodName);
}
Related
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.
I'm making a game in Unity3D with C# for mobile devices and can't figure out how to check which scene was loaded before the current scene. I need to check this to change the spawn point from the player gameobject. First I added a simple script to my buttons (loadnextscene and loadprevscene)
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Application.LoadLevel(sceneNumber);
}
}
A second scripts handles the touch input from the user and changes the movement of the player object.
So, for example: If the player clicks on the "load previous scene" button in the second Level to switch to the first level again, I want to set the spawn point of the player object on the right half on the screen and not on the left side like when the game was started the first time.
I tried it with Singleton and PlayerPrefs, but it did not work out.
You need to save the scene number to some variable before LoadScene, then check it after the scene loaded.
The only problem is that this variable will be destroyed after the new scene is loaded. So, to prevent it, you can use DontDestroyOnLoad. Here is what you do:
First, create a new empty game object, and attach the following script to it:
using UnityEngine;
using System.Collections;
public class Indestructable : MonoBehaviour {
public static Indestructable instance = null;
// For sake of example, assume -1 indicates first scene
public int prevScene = -1;
void Awake() {
// If we don't have an instance set - set it now
if(!instance )
instance = this;
// Otherwise, its a double, we dont need it - destroy
else {
Destroy(this.gameObject) ;
return;
}
DontDestroyOnLoad(this.gameObject) ;
}
}
And now, before you load, save the scene number in the Indestructable object:
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Indestructable.instance.prevScene = Application.loadedLevel;
Application.LoadLevel(sceneNumber);
}
}
And last, in your scene Start() check Indestructable.instance.prevScene and do your magic accordingly.
More info here:
http://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
*I did not compile the code, so there may be some errors, but this is the general idea.
Why did the PlayerPrefs approach did not work?
I think its the easiest way to solve your problem.
public class FirstLevel : MonoBehaviour {
public void Start() {
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
}
And then in the second scene simply read the saved PlayerPrefs
public class SecondLevel : MonoBehaviour {
string PrevScene;
public void Start() {
PrevScene = PlayerPrefs.GetString("SceneNumber");
// if there will be a third scene, etc.
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
public void GoToPrevScene() {
SceneManager.LoadScene(PrevScene);
}
}
You can solve this problem with a single static member variable in the SwitchScene class. No need for the singleton pattern or DontDestroyOnLoad.
public class SwitchScene : MonoBehaviour
{
public int sceneNumber;
private static int previousScene;
private int oldPreviousScene;
void Start()
{
oldPreviousScene = previousScene;
previousScene = sceneNumber;
}
public void HandleLoadPrevButtonClick()
{
SceneManager.LoadScene(oldPreviousScene);
}
}
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.
I've been tinkering with a game engine and part of it has been building a class for the UI elements. My goal is to make it really simple to add buttons to the UI with just a line that will include the button location and what method it calls when someone presses that button. I just can't figure out how to pass in the target method that will trigger when the button is pressed. I can get methods to subscribe to delegate events, just not when they're wrapped up in a list of button objects I've created.
I've simplified the code down to just what I'm trying to accomplish here. The main sticking point is I'm not sure what to put in as the object type for the method parameters for addButton() in order to be able to pass in another method that can subscribe to delegate events. If I try Void or Object, I get conversion errors.
public Class UserButton
{
public delegate void triggerEvent();
public triggerEvent ButtonPress; //This should fire off the event when this particular button is pressed.
public UserButton(Point buttonlocation, Point buttonsize, string buttontext, Texture2d Texture)
{
//Store all this data
}
}
public Class UserInterface //This class is for the buttons that appear on screen. Each button should have a location and a method that it calls when it's "pressed"
{
List<UserButton> ButtonList = new List<UserButton>(); //List of all the buttons that have been created.
//Add a button to the list.
public void addButton(Point location, Point buttonsize, Texture2d texture, Method triggeredEvent) //Compile fails here because I can't figure out what type of object a method should be.
{
UserButton button = new UserButton(Point location, Point buttonsize, Texture2d texture);
button.ButtonPress += triggeredEvent; //The target method should subscribe to the triggered events.
ButtonList.Add(button);
}
public void checkPress(Point mousePos) //Class level method to sort through all the buttons that have been created and see which one was pressed.
{
foreach (UserButton _button in ButtonList)
{
if (_button.ButtonBounds.Contains(mousePos))
{
_button.ButtonPress(); //Call the event that should trigger the subscribed method.
}
}
}
}
public class Game1 : Game
{
//Main methods
//Load
protected override void LoadContent()
{
UI = new UserInterface(); //Create the UI object
UI.addButton(new Point(32,32), Texture,toggleRun()); //Pass in the method this button calls in here.
}
private void toggleRun() //The button should call this method to start and stop the game engine.
{
if (running)
{
running = false;
} else {
running = true;
}
protected override void Update(GameTime gameTime)
{
if (MouseClick) //simplified mouse check event
{
UI.checkPress(mousePos); //Pass the current mouse position to see if we clicked on a button.
}
}
}
Your "triggeredEvent" parameter should be the same delegate type "triggerEvent":
public void addButton(Point location, Point buttonsize, Texture2d texture, triggerEvent triggeredEvent)
To pass a method as the parameter, use the method name without invoking it (this will only work if the method "toggleRun" does not have any overloaded methods, and matches the signature of the "triggerEvent" delegate):
UI.addButton(new Point(32,32), Texture, toggleRun);
So I have this problem where I have a variable called StarBounds in my main game class file. The file is now called MainGameClass.cs with namespace StarCatcher.
I have made a class to detect if the mouse is hovered over the StarBounds variable and then clicks. On click I would like to edit the StarBounds variable from this other class called GameFunctions.cs.
I am able to do something like...
MainGameClass mgc = new MainGameClass();
The when the hover and click event is triggered I can type in without errors:
mgc.StarBounds = new rectangle(0,0,0,0);
But in the actual game it does not change. And also I sometimes get errors when doing the "mgc.StarBounds = new rectangle(0,0,0,0);" saying it doesn't have an object reference.
I think this is most likely just a scope issue. The exception is caused because mgc is null. Make sure that GameFunctions has not declared a local copy of MainGameClass and is referencing a pre-existing instance. Otherwise, use a static variable for StarBounds as shown in the example. Example,
public class MainGameClass {
public static Rectangle StarBounds;
public void HandleInput () {
// if GameFunctions.ClickedWithinStarBounds(mouse)
// GameFunctions.OnClickStarBounds()
}
}
public class GameFunctions {
public static void ClickedWithinStarBounds(MouseState mouse) {
// create a rectangle around the mouse (ie. cursor) for the detection area
// return left mouse button is down or pressed && IsWithinStarBounds
}
public static bool IsWithingStarBounds(Rectangle area) {
return (MainGameClass.StarBounds.Intersects(area);
}
public static void OnClickStarBounds() {
MainGameClass.StarBounds = Rectangle.Empty;
}
}