- Home /
How would i change this string in update to a StringBuilder?
void Update()
{
gameTimer += Time.deltaTime * 18;
seconds = (ulong)(gameTimer % 60);
minutes = (ulong)(gameTimer / 60) % 60;
hours = (ulong)(gameTimer / 3600) % 24;
days = ((ulong)(gameTimer / 86400) % 7) + 1;
weeks = ((ulong)(gameTimer / 604800) % 4) + 1;
months = ((ulong)(gameTimer / 2419200) % 12) + 1;
years = ((ulong)(gameTimer / 29030400)) % 100 + 2038;
string timerString = string.Format ("{0:0} :{1:00} ", hours, minutes);
minutesOClock.text = timerString;
}
Answer by Bunny83 · Feb 11, 2019 at 09:33 PM
I'm not sure why you want to do that. A StringBuilder only has benefits when used with really large strings. If you want to avoid garbage generation, that's not possible since the final ToString call that you need when using the StringBuilder will also create a new string and therefore garbage.
Anyways, this is what it looks like when using a cached string builder:
StringBuilder sb = new StringBuilder();
void Update()
{
// [ ... ]
sb.Length = 0;
sb.Append(hours).Append(" :");
if (minutes < 10)
sb.Append("0");
sb.Append(minutes).Append(" ");
minutesOClock.text sb.ToString();
}
This should output the same as your string.Format version. Note that you could use AppendFormat for the minutes, however since the parameters of AppendFormat (just like string.Format) are object references, value types like int / ulong / etc would be boxed and create extra garbage.
Yes,I see now, i ended up going for (if seconds == 0) UpdateTimeString(). still gets called twice or trice,but thats better than 60p/s.
Answer by andrew-lukasik · Feb 14, 2019 at 09:29 PM
Use CacheIntString class. It's made just for cases like this! Think of it as specialized hash table for strings.
It's core idea is to make you think how to create all your strings once and never allocate mid-frame again (or rarely at least). First thing you need to do is to split your timer into separate (repeating) numerical pieces. Then define how every particular piece will display in CacheIntString constructor calls like below:
using System;
using UnityEngine;
using UnityEngine.UI;
public class TestCacheIntString : MonoBehaviour
{
public Text _displaySeconds;
public Text _displayMinutes;
public Text _displayHours;
public Text _displayDays;
public double _time = 0;
CacheIntString cacheSeconds = new CacheIntString(
(seconds)=>seconds%60 , //describe how seconds (key) are translated to useful value (hash)
(second)=>second.ToString("00") //you describe how string is built based on value (hash)
, 0 , 59 , 1 //initialization range and step, so cache will be warmed up and ready
);
CacheIntString cacheMinutes = new CacheIntString(
(seconds)=>seconds/60%60 , // this translates input seconds to minutes
(minute)=>minute.ToString("00") // this translates minute to string
, 0 , 60 , 60 //minutes needs a step of 60 seconds
);
CacheIntString cacheHours = new CacheIntString(
(seconds)=>seconds/(60*60)%24 , // this translates input seconds to hours
(hour)=>hour.ToString("00") , // this translates hour to string
0 , 24 , 60*60 //hours needs a step of 60*60 seconds
);
CacheIntString cacheDays = new CacheIntString(
(seconds)=>seconds/(60*60*24) , // this translates input seconds to days
(day)=>day.ToString() , // this translates day to string
0 , 31 , 60*60*24 //days needs a step of 60*60*24 seconds
);
void Update ()
{
_time += Time.deltaTime;
int seconds = Mathf.FloorToInt( (float)_time );
_displaySeconds.text = cacheSeconds[ seconds ];
_displayMinutes.text = cacheMinutes[ seconds ];
_displayHours.text = cacheHours[ seconds ];
_displayDays.text = cacheDays[ seconds ];
}
}
The only thing i see wrong with this method, not that anything is actually wrong with it is that setting up the text components in unity editor to look proper and scale correctly would be a pain in the butt to do. Still it's very function-able.
Edit:
just to be clear, though, if making a string to add all the variables to, and having one text component to display on actually draw a large number of memory over having 4 separate text components?
Strings are 2 bytes per character I think. So for 10-character long timer string it would be something like 1,2kB generated every second. Looks like not much but it will trigger GC collection sooner than later. And GC can cause annoying CPU spikes sometimes. While dropping 1 frame (repeatedly) for some games is not a problem at all but it is a big issue for things like mobile and mobile VR especially.
To write programs that are not constantly asking for more memory every second is a good goal to aim for I think
Your answer
Follow this Question
Related Questions
Do something when health is below X 2 Answers
Setting Text.text in UI efficiency? 1 Answer
How efficient is Update() vs Update via a Manager vs Coroutines for iOS Deployment? 2 Answers
Having lots of Update Functions 2 Answers
Efficiency of Game Loops 2 Answers