- Home /
Same Code, Different Variables (Optimization)
I have an optimization question :
I have lines of code, repeating the same function over, and over, and over again, but each time it has a different variable. The problem for example I have some attributes I need, and I need to make sure that if it's value is at 0, it won't go negative. So I write my code like this :
void Update () {
if(energy <= 0){
energy = 0;
}
}
This code works, so when my energy becomes 0, it doesn't go into the negative. But then I'm not just counting how much energy the player has, I'm counting health, ammo, power, mana, etc.... So then I have to write the code like this :
void Update () {
if(energy <= 0){
energy = 0;
}
if(health <= 0){
health = 0;
}
if(power <= 0){
power = 0;
}
if(mana <= 0){
mana = 0;
}
}
Suddenly, I have more lines of code then I need and it checks for each of them every frame, making the script do more work. What I could make it do is check for it only under certain circumstances, not every frame, but even then, the code is longer and probably gives performance a hit. Is there another way of writing something like this? Here's my code for my specific situation :
void Update () {
if(health <= 0){
health = 0;
}
if(energy <= 0){
energy = 0;
}
if(speed <= 0){
speed = 0;
}
if(mana <= 0){
mana = 0;
}
if(power <= 0){
power = 0;
}
if(agility == maxAgility){
canAddAgility = false;
}
else{
canAddAgility = true;
}
if(toughness == maxToughness){
canAddToughness = false;
}
else{
canAddToughness = true;
}
if(stamina == maxStamina){
canAddStamina = false;
}
else{
canAddStamina = true;
}
if(supernaturalFocus == maxSupernaturalFocus){
canAddSupernaturalFocus = false;
}
else{
canAddSupernaturalFocus = true;
}
if(strength == maxStrength){
canAddStrength = false;
}
else{
canAddStrength = true;
}
if(agility == minAgility){
canSubtractAgility = false;
}
else{
canSubtractAgility = true;
}
if(toughness == minToughness){
canSubtractToughness = false;
}
else{
canSubtractToughness = true;
}
if(stamina == minStamina){
canSubtractStamina = false;
}
else{
canSubtractStamina = true;
}
if(supernaturalFocus == minSupernaturalFocus){
canSubtractSupernaturalFocus = false;
}
else{
canSubtractSupernaturalFocus = true;
}
if(strength == minStrength){
canSubtractStrength = false;
}
else{
canSubtractStrength = true;
}
}
And for the GUI (same script) :
if(GUI.Button(new Rect(350, 60, 30, 25), "+") && canAddAgility && pointsLeft > 0){
agility ++;
pointsLeft --;
StatusModifier();
}
if(GUI.Button(new Rect(350, 90, 30, 25), "+") && canAddToughness && pointsLeft > 0){
toughness ++;
pointsLeft --;
StatusModifier();
}
if(GUI.Button(new Rect(350, 120, 30, 25), "+") && canAddStamina && pointsLeft > 0){
stamina ++;
pointsLeft --;
StatusModifier();
}
if(GUI.Button(new Rect(350, 150, 30, 25), "+") && canAddSupernaturalFocus && pointsLeft > 0){
supernaturalFocus ++;
pointsLeft --;
StatusModifier();
}
if(GUI.Button(new Rect(350, 180, 30, 25), "+") && canAddStrength && pointsLeft > 0){
strength ++;
pointsLeft --;
StatusModifier();
}
if(GUI.Button(new Rect(380, 60, 30, 25), "-") && canSubtractAgility){
agility --;
pointsLeft ++;
StatusModifier();
}
if(GUI.Button(new Rect(380, 90, 30, 25), "-") && canSubtractToughness){
toughness --;
pointsLeft ++;
StatusModifier();
}
if(GUI.Button(new Rect(380, 120, 30, 25), "-") && canSubtractStamina){
stamina --;
pointsLeft ++;
StatusModifier();
}
if(GUI.Button(new Rect(380, 150, 30, 25), "-") && canSubtractSupernaturalFocus){
supernaturalFocus --;
pointsLeft ++;
StatusModifier();
}
if(GUI.Button(new Rect(380, 180, 30, 25), "-") && canSubtractStrength){
strength --;
pointsLeft ++;
StatusModifier();
}
As you can see, the code is long, but it's just doing basically the same thing over and over again, making the script long and giving it a performance hit. There must be another way to write this. Thanks in Advance.
If this is a start screen, or pops up whenever you gain a level, the speed won't matter. Some buttons just sitting there won't stutter, even if you somehow drop down to 10FPS. Just wrap all the overly long ifs in your OnGui with if(showGainGUI==true)
.
This is a Character Generator, where the Character has points to spend on attributes that will affect things like speed, health, energy, etc... The GUI function allows them to modify it using buttons and visually see what they're doing.
Answer by Bunny83 · Nov 12, 2011 at 02:37 AM
The best and easiest way is to use properties, that's the real advantage of properties:
private float m_Health;
public float Health { get {return m_Health;} set { m_Health = Mathf.Max(0,value); } }
That way it get only checked when you change the value. It's also a good place to check for the player death or to play a hitsound or something like that.
Another advantage is if you have weird behaviour of your health var, you can put a Debug.Log in the setter to see when, how often and to what value it gets set.
edit
There are some disadvantages of course. The biggest one is that you can't show this variable in the inspector since it's not a variable. You could turn the private m_Health into a public var but make sure you don't set this variable from one of your scripts since it would bypass the check ...
I'm not exactly sure what properties are or how to replicate them in other instances. Things like maxHealth and such have to be used and assigned in the Inspector. Also, things like health have checks for a separate static variable that has the same value. Like this for example :
public float health;
public static float healthDisplay;
void Update () {
healthDisplay = health;
}
That way I can have a float that I can edit in the Inspector and a separate static float that other scripts can access. So things like health and maxHealth have to be variables. As for properties, I couldn't find anything on them in the Script Reference or the $$anonymous$$anual.
Properties consists just of a setter and a getter method but the property itself can be used like a variable.
e.g: if you use this assignment:
Health = 100.0f;
behind the scenes the "set" method of this property get called. Inside the set method you have a special parameter available: "value". It hold the assigned value. In this case 100.0f;
This would would call the "get" method to get the current value, increment it and call the "set" method to assign in back:
Health++;
What you do in the setter or getter is up to you. I just clamped it like you wanted so it doesn't get below 0.0 and then assigning it to the "true" variable. Getter and setter are one of the very basics in OOP encapsulation.
Nearly every so called "variable" of all Unity objects are properties:
.transform
.gameObject
.position
.parent
All those "variables" just wrap get and set methods.
btw. to keep a value between an upper and lower value you can use `V = $$anonymous$$athf.Clamp(V, $$anonymous$$Value, maxValue);`
That makes sense. Thanks for that. But I still have this repeating problem for the GUI code and the "canSubtract" and "canAdd". Is there a way to solve those?
You could replace all those if's with this code:
canAddAgility = agility < maxAgility; canAddToughness = toughness < maxToughness; canAddSta$$anonymous$$a = sta$$anonymous$$a < maxSta$$anonymous$$a; canAddSupernaturalFocus = supernaturalFocus < maxSupernaturalFocus; canAddStrength = strength < maxStrength; canSubtractAgility = agility > $$anonymous$$Agility; canSubtractToughness = toughness > $$anonymous$$Toughness; canSubtractSta$$anonymous$$a = sta$$anonymous$$a > $$anonymous$$Sta$$anonymous$$a; canSubtractSupernaturalFocus = supernaturalFocus > $$anonymous$$SupernaturalFocus; canSubtractStrength = strength > $$anonymous$$Strength;Anything you test in if, while etc. clauses actually returns a bool value and can directly be assigned to a variable.
But if you don't need canAddAnything or canSubtractWhatever for other purposes, you can do the comparison inside the OnGUI code directly:
if(GUI.Button(new Rect(350, 60, 30, 25), "+") && agility < maxAgility && pointsLeft > 0){ agility ++; pointsLeft --; Status$$anonymous$$odifier(); }
$$anonymous$$aybe you could reduce your code even more passing the variable by reference to these specific functions:
void AddButton(Rect rect, ref int variable, int max){ if (GUI.Button(rect, "+") && variable < max && pointsLeft > 0){ variable++; pointsLeft--; Status$$anonymous$$odifier(); } }
void SubButton(Rect rect, ref int variable, int $$anonymous$$){ if (GUI.Button(rect, "-") && variable > $$anonymous$$){ variable--; pointsLeft++; Status$$anonymous$$odifier(); } } Call the functions above for each variable in OnGUI:
void OnGUI(){ AddButton(new Rect(350, 60, 30, 25), ref agility, maxAgility); SubButton(new Rect(380, 60, 30, 25), ref agility, $$anonymous$$Agility); AddButton(new Rect(...), ref sta$$anonymous$$a, maxSta$$anonymous$$a); SubButton(new Rect(...), ref sta$$anonymous$$a, $$anonymous$$Sta$$anonymous$$a); ... }