So I have a bunch of functions that I use OVER AND OVER again all over the place in random areas. And also some getters and setters.
For example just stuff like this:
public GameObject GameObjectCache
{
get
{
if (gameObjectCache == null)
gameObjectCache = this.gameObject;
return gameObjectCache;
}
}
private GameObject gameObjectCache;
public Transform TransformCache
{
get
{
if (transformCache == null)
transformCache = this.GetComponent<Transform>();
return transformCache;
}
}
private Transform transformCache;
This is for Unity if you can't tell.
What I would really like to do is take those functions and put them somewhere else. Then in my classes just do like
[TransformCache]
Some kind of one line tag and it would place the function, from elsewhere, inline into my class.
I know there is some complex way to do this with Mono.Cecil, which if anyone has a simple tutorial on that I'd love the link.
But is there an easier way than that? I know C and Objective-C, and even CG code have functions to do this. Is there anyway to do this in C# with ease?
I don't know if this will help you or not, but how about wrapping some of these common things you want into a wrapper class, then just add the class to your other game object. Something like
public class MyWrapper
{
private GameObject parentGameObj;
public MyWrapper(GameObject srcObj)
{
parentGameObj = srcObj;
}
public GameObject GameObjectCache
{
get
{
if (gameObjectCache == null)
gameObjectCache = parentGameObj.gameObject;
return gameObjectCache;
}
}
private GameObject gameObjectCache;
public Transform TransformCache
{
get
{
if (transformCache == null)
transformCache = parentGameObj.GetComponent<Transform>();
return transformCache;
}
}
private Transform transformCache;
}
Then, in your classes that you will be using it
public class YourOtherClass : GameObject
{
MyWrapper mywrapper;
public Start()
{
// instantiate the wrapper object with the game object as the basis
myWrapper = new MyWrapper(this);
// then you can get the game and transform cache objects via
GameObject cache1 = myWrapper.GameObjectCache;
Transform tcache1 = myWrapper.TransformCache;
}
}
Sorry... in C++ you can derive from multiple classes which in essence COULD allow something like that. The only other thing I can think of is looking into using Generics if your functions use repeated similar types via GetComponent() calls.
Related
iam pretty new in C# and Unity and facing currently following issue in my game project.
Ive a class to instantiate for various shops each shop makes a different profit has a different name and coasts different power recourses. So my idea is to have a class to make those shops and on constructor call e.g shop creation it should change variables of another script where the balance, power, and so on is calculated here the example code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Shops
{
public int shopID;
public string shopName;
public float shopPower;
public double shopCoast;
public string shopType;
public double shopProfit;
public Shops(int id, string name, float power, double coast, string type, double profit)
{
this.shopID = id;
this.shopName = name;
this.shopPower = power;
this.shopCoast = coast;
this.shopType = type;
this.shopProfit = profit;
Debug.Log("Shop created");
addProfit(profit);
subPower(power);
subCoast(coast);
}
public void addProfit(double profit)
{
}
public void subPower(float power)
{
}
public void subCoast(double coast)
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Calculations : MonoBehaviour
{
public double money;
public float power;
public double profitPerHour, coastPerHour, totalPerHour;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Is it correct or lets say common the way ive written it and that the constructor calls a few methods on call? And how can i accsess the variable double money from the Calculations : Monobeheivor script without the script being attached to a game object? Is there a way without attaching it to a gameobject or not? My idea is that the calculation script does all the calculations needed and iam just grabing the values for my UI from this script. Or wont the update() and start function work?
And one more Question
How can i store all created classobjects in a list or array so that i can print it later in a table where its sorted for example from lowest to highest profit. What is better to use an array or a list?
I hope i made my question clear and thank you in advance.
Like #Ben Rubin said in his comment, it is not a good idea to do another things than initialization of variables in the constructor. But you could do a method eg. Init() which will calls the methods on other classes.
If you have a calculation instance for each shop, then maybe you should merge the 2 classes in only one.
However, if your calculation is global for all the shops, you could make it singleton, without having to make it a MonoBehaviour. And inside you register your shops in a list where you can then access later inside the Calculations class.
public class Shop
{
...
public Shop(...)
{
// initialize only your variables
}
public void Init()
{
Calculations.Instance.RegisterShop(this, true);
// Do your calls to other classes here
}
public void Clear()
{
Calculations.Instance.RegisterShop(this, false);
}
...
}
public class Calculations
{
private static Calculations m_instance = null;
public static Calculations Instance { // singleton
get {
if(m_instance == null)
m_instance = new Calculations();
return m_instance;
}
}
private List<Shop> shopList = new List<Shop>();
public RegisterShop(Shop shop, bool register)
{
bool isRegistered = shopList.Contains(shop);
if(register && !isRegistered)
shopList.Add(shop);
else if(!register && isRegistered)
shopList.Remove(shop);
}
...
}
And for exemple in a script ShopSeller, attached to a GameObject in your scene (I don't know if you do something like that)
public class ShopSeller : MonoBehaviour
{
private Shop shop = null;
public void Start()
{
shop = new Shop(...);
shop.Init();
}
public void OnDestroy()
{
shop?.Clear();
}
...
}
Note that singleton needs to be used wisely, it is not the answer for all difficulties you encounter. It is the only way I can think of for you to not make a MonoBehaviour and attach it to a GameObject to use it.
For your bonus question, you could refer to this chart if you want to choose an appropriate container for what you want to do.
It uses the C++ container types, but it remain the same with some translations to C# :
vector = List
list = LinkedList
map = Dictionary
set = HashSet
deque = no equivalent in C#
The only things it lacks is the basic array, it is useful when you know exactly the length, and you know it will never grow or shrink during runtime.
I'm making a card game where I assign random effects to cards, so I need to load the effect's code at runtime with just the class name.
I don't know if my abstract class and child are done properly, and I also don't exactly know how to get the class needed from a path.
I know Resouces.Load won't work but I'll leave it there to convey what I wanna do more easily.
public class GameManager : MonoBehaviour
{
public Effect effect;
...
effect = Resources.Load<Effect>("Card/Effects/" + c.cardActual.effect1);
if (effect.Execution())
{
StartCoroutine(TargetAndCastSpell(c,p));
}
This is the code for my abstract class
public abstract class Effect : MonoBehaviour
{
public string targetType;
public List<int> availableTargets;
public int effectTier;
public PlayerHolder playerTarget;
public CardPhysicalInstance minionTarget;
public PlayerHolder caster;
public void EffectX(PlayerHolder PlayerTarget, CardPhysicalInstance MinionTarget)
{
}
public bool Execution()
{
return false;
}
}
And lastly the child I want to load in runtime
class Spark : Effect
{
string targetType = "any";
//Deal 1 damage to any target
public bool Execution ()
{
bool canTarget = false;
caster = GameManager.singleton.currentPlayer;
availableTargets = SpellHelper.AvailableTargets();
if (targetType == "any") //Placeholder check
{
canTarget = true;
caster.playerState = GameManager.PlayerState.targeting;
}
return canTarget;
}
...
Any help is deeply appreciated, thanks and sorry about my clear lack of understanding of abstract classes.
Based on comments, I think Overriding is the Droid you are looking for. With Polymorphy there is two ways different Implementations can be resolved.
hiding is possibly by default. However, it is also pretty much useless. It is one of those things we thought we need and now everyone adds it to their OOP language. But aside from not using hiding when I wanted to overwrite, I have never had any use for it.
Overriding is the important thing. However, overriding has to be allowed for a function in the base class that first added it.
In Effect:
//Stil work how it does, but now can be overridden
public virtual bool Execution()
{
return false;
}
In Spark:
//We are actually overriding - not just hiding - Effect.Execution() here
public override bool Execution ()
{
bool canTarget = false;
caster = GameManager.singleton.currentPlayer;
availableTargets = SpellHelper.AvailableTargets();
if (targetType == "any") //Placeholder check
{
canTarget = true;
caster.playerState = GameManager.PlayerState.targeting;
}
return canTarget;
}
You can assign a Spark to a Effect variable, call Execution() and Polymorphy will deal with calling the version of Spark.
Add anotehr Effect sub-class? As long as it also overrides Execution() it works the same.
The Effect version could be empty/turned abstract. Or be kept as a default version for all subclasses.
With hiding you would have to cast it back to Spark to get access to it's variant of the Method. Wich is just extra work with no apparent advantage.
This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 3 years ago.
I am making a game that involves the score of the player. This score is always displayed on the screen. However when i change scenes the score resets back to 0. How can i make it so that the score gets saved across all of my scenes?
I have looked on the internet for hours but i was not able to fix it that way. The 2 main things that i came across were this:
Static variables
DontDestroyOnLoad()
While i am sure that these would fix my issue, i have no idea how to implement them in my code. Do i make a new script? Do i put them in an existing script? And if so, which one? How can call upon variables if they are static?
Thanks in advance for any help!
If you're just trying to be able to access simple data across screens, static variables seem like the most straightforward solution. It's fine if you've never used them before, they're very easy to use once you get used to them. :)
The first thing to do is create a class that will hold your static variables (yes, this means writing a new script and putting it in your Scripts folder). It's possible to add new static variables on a class you're already using, but if you're just learning I would recommend keeping things like that separated so it's more organized.
public class GameValues {
public static int score { get; set; }
// You can add other static variables here.
}
Notice I didn't inherit from MonoBehaviour. This is fine because we don't need to add this script to a GameObject, it's just going to be used to store our data. Now if we need to change or read the score, we can do it like this.
public class Example : MonoBehaviour {
public int scoreLimit = 100;
private void Update () {
/*
* When accessing static members of a class, no object reference is needed.
* Instead you access it as though it were a member of the class.
*/
int currentScore = GameValues.score;
if (Input.GetKeyDown (KeyCode.Space) && (currentScore < scoreLimit)) {
GameValues.score += 1;
}
}
}
Obviously this is a rather silly example, but you get the idea. If you'd like to do some further reading, here is the official Microsoft documentation on the static keyword.
Happy coding!
You have a couple of options.
1) PlayerPrefs
Unity has an in-built save procedure called PlayerPrefs that will store data between scenes and game restarts.
//Store Score Value
PlayerPrefs.SetInt("Score", Score);
// Retrieve Score Value if there is one
if (PlayerPrefs.HasKey("Score"))
{
Score = PlayerPrefs.GetInt("Score");
}
PlayerPrefs Documentation
2) Unity Singleton
The Unity Singleton differs from a normal programming languages slightly in its use of DontDestroyOnLoad to keep the GameObject holding the Singleton alive between scenes.
public class UnitySingleton : MonoBehaviour
{
public static UnitySingleton Instance { get; private set; }
public int Score { get; set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
}
else
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
}
Usage
//Store Score
UnitySingleton.Instance.Score = value;
//Retrieve Score if set otherwise (it will) return 0
Score = UnitySingleton.Instance.Score;
Alright here's a minimal example
Save data object:
using UnityEngine;
public class SaveDataHere : MonoBehaviour {
public string myData;
public static string staticData = "Static data is still here";
// Use this for initialization
void Start () {
DontDestroyOnLoad(this);
myData = "I didn't get destroyed haha";
}
}
In the new scene:
using UnityEngine;
public class InNewScene : MonoBehaviour {
// Use this for initialization
void Start () {
var saveData = FindObjectOfType<SaveDataHere>();
Debug.Log("instance data is " + saveData.myData);
Debug.Log("static data is " + SaveDataHere.staticData);
}
}
Basically I have one script set up which during play prints to the console.
//Method to loop find question to be asked and print it as well as loop through answerText and print the appropriate answer options/
public void AskNewQuestion ()
{
//prints Person: + apropriate question based on questionUpTo.
Debug.Log ("Person:" + questionsText [questionUpTo]);
//Loops through answersText and compares it to questionUpTo to find applicable answers.
for (int i = 0; i < answerText[questionUpTo].Count; i++)
{
//Prints applicable answers to console.
Debug.Log(i+1 + ":" + answerText[questionUpTo][i]);
}
}
Now I need this to print not to the console, but through another script which handles a canvas, and the canvas is where I need the text to print to.
public class Textboxmanager : MonoBehaviour {
public GameObject textBox;
public Text theText;
public TextAsset textFile;
public string[] textLines;
public int currentLine;
public int endAtLine;
public bool isActive;
// Use this for initialization
void Start ()
{
if(textFile != null)
{
textLines = (textFile.text.Split('\n'));
}
if(endAtLine == 0)
{
endAtLine = textLines.Length - 1;
}
if(isActive)
{
EnableTextBox();
}
else
{
DisableTextBox();
}
}
void Update()
{
if(!isActive)
{
return;
}
theText.text = textLines[currentLine];
if(Input.GetKeyDown(KeyCode.Return))
{
currentLine += 1;
}
if(currentLine > endAtLine)
{
DisableTextBox();
}
}
public void EnableTextBox()
{
textBox.SetActive(true);
}
public void DisableTextBox()
{
textBox.SetActive(false);
}
}
There are many ways in which you can accomplish this. First of all, i notice that the script from which you are trying to print to is a manager of sorts. In that case there is a high level concept called Singletons.
Here is a link on how to implement Singletons in C-Sharp:
https://unity3d.com/learn/tutorials/projects/2d-roguelike-tutorial/writing-game-manager
Watch the unity video on them and make sure this is the right approach to your problem.
If you don't want to go down that complex path and would rather avoid the headache then i will try and explain how to reference another script and all of its functions, variables and other data without singletons.
Class1 {
public Class2 class2Reference;
private void printTextFromClass2() {
class2Reference.printAnswerText();
}
}
Class2 {
public void printAnswerText() {
Debug.Log("String");
}
}
In this example you can see that i have two classes, these may be in different scripts and on completely different gameObjects within Unity. We created a variable with the datatype as the class name we are trying to access. In this case the datatype is Class2. We then use it as a variable any time that we need to access that script.
The only thing that you must do is give it a script to reference. Much like a string is null until we give it a value, our variable 'private Class2 class2Reference' is null until we give it a value.
This is the easy part. Save and go back to unity. You should see your variable in the inspector with "none" written next to it. Drag and drop your second script into that box and you should be ready to rumble.
Hope this helps you out buddy.
Instantiate the AskNewQuestion class inside the TextboxManager class.
puclic TextboxManager : Monobehavior {
AskNewQuestion askNewQuestion = new AskNewQuestion();
}
Make a function inside AskNewQuestion that returns the array string. Then call it on the TextboxManager class to get the array string. Like this: textLines = askNewQuestion.questions;.
Edit: You can use a { get; set; } method with this. First initialize on where you store the string array then apply it on the { get; set; } method.
string[] questions;
public string[] Questions {
get {
return quesions;
}
}
I have my main program which contains an array called setOfBalls[i] which stores ellipses. The ellipses have three properties:
setOfBalls[i].velocity;
setOfBalls[i].direction;
setOfBalls[i].mass;
Then I have a collision class and need to be able to access the arrays with direction, velocity and mass in the collision class called Collisions.cs.
I need to be able to detect a two body collision out of my three balls but I do not know how to get the array from main program to my collision class?
public Boolean twoBodyCollision()
{
}
Consider adding parameters to your method and send the array as an argument; e.g.
public boolean HasCollidingObjects(Ball[] listOfBalls) { // ... }
Here I assume you're using a Ball class, but it's only an example. Use your class instead.
Also, you should use C# naming conventions instead of Java's. You can check the MSDN pages for information on what these are.
To pass an object into a class, you can use a constructor:
private string[] setofBalls;
public ClassName(string[] setBalls)
{
setofBalls = setBalls;
}
public void DoSomething()
{
foreach (string ball in setofBalls) { ...... }
}
More on Object Oriented Programming with C#: https://msdn.microsoft.com/en-us/library/dd460654.aspx
You can create an instance of the class you have your method in then call if from your "main program"
public void MethodInMainProgram()
{
Collision collision = new Collision();
collision.TwoBodyCollision(setOfBalls);
}
public class Collision
{
public void TwoBodyCollision(Ellipse[] ellipses)
{
//logic that detects collisions
}
}
more info on creating instances here