I have a variable that I need to check frequently when my game is running. To simplify this, the check is: if (score >= achievement1) {do something}.
It seemed overkill to put this check in the Update() function which is called every frame;
So instead, I call the InvokeRepeating function in the Start (); function:
InvokeRepeating ("checkscoreforachievement", 0f, 2f);
Is this smart or is there a better way doing this? End result should be that within a few seconds after achieving a certain score, the achievement is triggered.
The reason I'm asking is that there are a few more things I need to do regularly when my game is running, so I'll end up with quite a few of these processes. Wondering if that isn't too much of a resource drain. Can't find good documentation on this subject.
No, InvokeRepeating is not better in this case.
It's not better because you are calling that function every 2 seconds which means that when score >= achievement1 evaluates to true, it will take about 2 seconds to detect that. Also, InvokeRepeating uses reflection which is slow. There is no advantage of using it here. Using the Update function like you are currently doing it is totally fine.
A much more better solution than using the Update function would be to check if score >= achievement1 only when score is changed with auto property.
public int achievement1 = 0;
private float _score;
public float score
{
get
{
return _score;
}
set
{
if (_score != value)
{
_score = value;
if (_score >= achievement1)
{
//Do something
}
}
}
}
This will only do the check when the score variable is set or changed instead of every frame but the Update code is fine.
Some other users have pointed this out similar things already, but essentially when the score is changed, call a function of your own to check. Don't invoke repeating or use update because honestly, it's just unnecessary to do it so infrequently or so frequently respectively.
Literally set it that when your score is incremented or decremented (changed in any way) you call the function to check.
You could also create an Array of achievements, and check the index before you even call the function to display/update the achievement:
if(arrayname[i] != "Unlocked"){
//Call function;
}
Related
I am trying to create a situation where I can take "hold 'E' to interact" type of input. My first thoughts are, start a timer after the button has been held down. After the timer reaches 700 ms, take the action. But the problem, is I don't know how to write that in c# since I am new in this language. Any other approach is also appreciated.
I am adding pseudocode here
//pseudocode
//inside the update function
if (e is pressed)
{
start timer;
if (timer.time == 700)
{
Debug.Log("E is pressed for quite some time");
}
}
What I would do is use a coroutine.
When the user presses down the button, the coroutine would start and if the user stops pressing the button the coroutine would stop.
To do this, your code could look something like:
using System.Collections;
[SerializeField] int time;
void Update ()
{
if (Input.GetKeyDown(KeyCode.E)) StartCoroutine(Action());
if (Input.GetKeyUp(KeyCode.E)) StopCoroutine(Action());
}
IEnumerator Action ()
{
yield return new WaitForSeconds(time);
// do stuff
}
Thanks to derHugo for spotting an error in my example code - its now been edited.
Of course there are other ways to go about what you want to do, but I'm thinking a coroutine is your best option because it will then be super easy to add some feedback to the user when they are holding down, in your case, 'E'. For example, you could fill an image to create an effect similar to this:
And, well, even if you don't want to do that - coroutines are still a good option.
Hope this helps!
I have a seperate script of time which I use to show time in my scene. It contains hour and minute and seconds variable.I want to do some specified work e.g., code execution on specified time and currently i am doing something like this. in Update. I am running a function which check continously check time variable in order to run an animation.
void Update()
{
checkTrainArriveTime();
}
void checkTrainArriveTime()
{
if (timeManager.GetComponent<Scale2>().hour == trainArriveTimeHour && timeManager.GetComponent<Scale2>().min == trainArriveTimeMin
&& isTrainArriveConditionExecute)
{
isTrainArriveConditionExecute = false;
PlayAnimationClip("Start");
}
else if (timeManager.GetComponent<Scale2>().min != trainArriveTimeMin)
{
isTrainArriveConditionExecute = true;
}
}
As Time will match this function will play the animation. Now I have 50 script attached to 50 different game Object. It is
working fine but It definitely not the right way to use Update Event. In my code, It is necessary to check time on every frame and
extra load on update. Is there any efficient way to do this Job?.
I can see your struggle. You are right, it is definitely not the best way forward.
The best option I can see here would be creating Animation Manager which is a singleton instance (there is only one instance allowed per application).
I would suggest moving your animation triggering logic to an Update method of AnimationManager.
Once you have done that. You will be able to access its instance calling AnimationManager.getInstance() method.
Next step is creating internal registry that would be nothing else than just a list of your registered game objects that you want to trigger animation for.
I don't know what exactly is your timeManager but I can imagine it is probably an instance of TimeManager controller that you drag and drop onto your public timeManager property. Consider turning it into singleton as well or at least moving assignment of timeManager.GetComponent<Scale2>() into Awake() method.
It is important to not to call GetComponent() method from inside of Update()', as it has an impact on performance.GetComponent` is quite expensive to call.
Hope it helps.
I have a jumpscare in my game. I am using Unity 3D.
My first function is
public void ScareMe(Vector3 pos) {
//it does some necessary irrelevant
//stuff and then it invokes another function
Invoke ("Smile",.2f);
}
In my other function I want to make an object appear and then disappear in 0.2 ms.
I use
IEnumerator Smile() {
Object.SetActive(true);
yield return new WaitForSeconds(0.2f);
Object.SetActive(false);
}
But for some reason my function Smile is never invoked as long as it returns something other then void.
Is there any way to use something like yield but go around it so I don't have to return anything?
I was thinking a while loop like a coroutine? But I am not sure how to go about it.
If I remember correctly, Unity's Invoke(func) is only willing to start a coroutine in Javascript, where the difference between a void and a coroutine is less strict.
What I would do is use StartCoroutine(Smile()); and start Smile with another yield return new WaitForSeconds(0.2f);, or better yet have Smile take a float smileDelay parameter and use that for your WaitForSeconds.
I have an assignment for my computer graphics class, were we have to alter a game code. The class itself is entirely about theory, so we pretty much have to figure out the code for ourselves and I've never programmed in C#, so sorry for any ignorance on my part.
Whenever my player sprite is hit by a box, I want the lives variable to decrement by 1. However, whenever it gets hit it just keeps going down. I understand why this is happening, but I don't know how to stop it.
This is the code that checks wether the two objects have collided.
if (b.BoundBox.Intersects(player.BoundSphere)){
player.hit = true;
}
In the update function for the player I have:
if(hit){
lives -= 1
}
How can I make it decrements only once, instead of constantly?
This is happening because it will constantly be detecting that it is colliding, as long as part of one of the objects is inside the other it will say that it is colliding.
What you can do to work around this is when the objects collide, set a boolean value that says the objects have collided and start a timer for however many seconds. While the boolean value is set to true, when the objects collide it will not decrement the lives but if it is false it will decrement. Once the timer is up, set the value to false so they can keep colliding. This gives the object time to leave the 'innards' of the other object.
Edit:
This is a very simple fix and might not work in your situation (as i don't know the rules of the game). A more complete solution would be to push the objects apart just enough that they are no longer colliding or predict one update tick ahead where each object will be and prevent them from ever actually colliding.
You don't need a timer for this problem, just declare another bool that keeps track if you've updated lives.
if (b.BoundBox.Intersects(player.BoundSphere) && !player.hit)
{
player.hit = true;
player.updated = false;
}
In your player's update:
if (hit && !updated)
{
lives -= 1
updated = true;
}
However, if you want to use a counter simply declare a:
TimeSpan timer = new TimeSpan(0, 0, x); //where x is how many seconds you need
and then in the Update method:
timer -= gameTime.ElapsedGameTime;
if (timer <= TimeSpan.Zero)
{
// do what you want
}
I am trying to build an upgrade system for a project in unity with C# and was wondering what might be the best way to modify a variable, by an array of custom objects (size may vary) that have information of how much should the variable be changed.
some ideas pop to mind, but here are my concerns:
The first one is by using a property:
public float SomeStat{
get{
float modStat = 0;
foreach(StatChanger Stat in StatChangers){
modStat += Stat.valueChange;
}
return modStat;
}
}
this will solve my problem very cleanly, but my concern is that when the property is accessed, every frame for example, there will be a performance drop for a larger amount of items in the list
The second one is to call a function that updates the variable and stores it in another variable to be used every frame without a performance drop:
public float someStat = 10;
public float someStatModifed;
void UpdateStat(){
someStatModifed = someStat;
foreach(StatChanger Stat in StatChangers){
someStatModifed += Stat.valueChange;
}
}
this works but I may forget to call it, and it becomes more messy when having more than one Variable that needs Changing.
should I use a combination of those two or is there better ways of doing it?
I can't say what the performance would be, relative to anything else, but you can use LINQ:
someStatModifed += StatChangers.Sum(stat => stat.valueChange);
Also, don't worry too much about optimizing it until there's a problem. Do try to break it, but don't fix it unless it's noticeable even with large data sets.