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.
Related
So, I as trying to create an "Open world exploration" game in C# WinForms, And while coding the mining, (which works just fine), I encountered a problem with saving the number of broken blocks to the inventory (a label). Basically, for every block player breaks, it gets added to the inventory as inventoryWrite.Text = $"Grass: {grassHolder}, Rock: {rockHolder}";.
Now, the thing is, sometimes, even though I use the ++ operator, it adds up to 4 to the inventory. I'm citing the code below.
private void Remove(object sender, EventArgs e, PictureBox itm)
{
if (itm.BorderStyle == BorderStyle.FixedSingle)
{
if (itm.Tag.Equals("grass") && items.Contains(itm))
{
grassHolder++;
itm.Tag = "";
}
if (itm.Tag.Equals("rock") && items.Contains(itm))
{
rockHolder++;
itm.Tag = "";
}
if (itm.Tag.Equals("dio") && items.Contains(itm))
{
dioHolder++;
itm.Tag = "";
}
this.Controls.Remove(itm);
items.Remove(itm);
}
}
I update the inventory in a public loop, don't worry about that (interval is 1ms). But I don't think that's the problem, since I tried putting it in the Remove() function, and nothing seemed to change.
I've even double locked the if statement, but nothing! It still adds more than 1. Can anybody tell me how to solve this? Thank you a lot.
EDIT:
As a reply to Ronald's comment, the if statement is called ONLY when the block is selected. ONLY once when the method is called.
There are too many points to cover in a comment and so I've had to enter an answer.
In itself the ++ operator is not the issue and will always behave as it should, but as someone reviewing a small piece of code the following points crop up.
grassHolder, rockHolder, dioHolder appear to have
accessibility beyond this function and so could be altered
elsewhere.
Function void Remove(object sender, EventArgs e, PictureBox itm) appears to be an event handler and yet there is no locking mechanism to ensure that the externally accessible parameters are not changed or used elsewhere whilst the function code is executed. Specifically items which is appears to be a collection of sorts and is used both in logic to determine whether parameters in (1) are incremented, but also has its contents changed within the function.
From comments made it would appear that this logic is run in
response to user interaction, maybe by use of a mouse button or key
event. Is this base event de-bounced to ensure that multiple
triggers aren't handled?
Your statement "saving the number of broken blocks to the inventory (a label)." Implies that you are storing game data within the UI. This should be avoided as it ties game data directly to the UI implementation and therefore makes it difficult to alter the game, but also ties any handling of game data directly to the UI thread.
Recommended actions:
Ensure that the parameters in question are not accessed and altered
elsewhere causing the issue seen.
Utilize a lock(x) statement to ensure that items is not changed
whilst this function is being executed. More information here
De-bounce the mouse button or key click that triggers this function
to ensure that multiple events aren't triggered. This is performed
by placing a minimum time between event triggers. A minimum time
period of say 150ms would be a good starting point. This would equate to a reasonably quick, conscious user action, but be slower than multiple events triggered by partial/poor switch contact. Incidentally this is especially true on touch screen interfaces.
Consider controlling access to global parameters through use of
access functions. For example
int IncrementRockHolder(){ rockHolder++;} Although implementation may appear onerous, they can greatly help with debugging as call stack information is then available showing what code is calling the function and thus making the change.
Implement a game engine class to control access to game data and implement game logic. This
would allow you to unit test game functionality whilst also freeing
it from UI implementation and restrictions.
Is there a way to check what methods exactly are called on scene's start? And which objects called them? I want to achieve something similar to adding Debug.Log(); to every single method but without doing it. I'd like to know what's going on in a project when I press play.
A scene consists only of game objects (there's nothing else in Unity).
GameObjects may have 1 or more Components attached.
When you click Play ...
It looks like you already know that, the calls such as
void Start()
(if they exist in the script) are called - automatically, by Unity - when each Component on each GameObject is launched with the scene.
It's quite common that inside each Start, you just add something like Debug.Log("starting, script name, on .. " + transform.name) which will simply let you know that that component on that game object has started.
Maybe that's what you need.
Obviously you will have added many functions to your own scripts
void SomeFunction() {
Are you saying you want to know when those functions run?
If so the only way to do that is simply manually add a Debug line, inside each function.
(Regarding functions which you add yourself, example SomeFunction, they are not in any way called automatically. They are only called if you call them (say from Start or similar). You can be sure they are only called if you call them.
Ok so I have this coroutine :
IEnumerator ShowCharTraits()
{
while(!hasPlayerChosen)
{
yield return null;
traitPanel.SetActive(true);
}
hasPlayerChosen = false;
traitPanel.SetActive(false);
// Debug.Log("Got called! job done");
}
It's being called like this from the awake method in my GameManager:
players = GameObject.FindGameObjectsWithTag("Player");
foreach (GameObject g in players)
{
ui_Controller.StartShowCharTraits();
g.GetComponent<PlayerToken>().isTurn = false;
}
StartShowCharTraits() is a simple method that does this :
public void StartShowCharTraits()
{
StartCoroutine("ShowCharTraits");
}
Now, I have checked the tags, no null reference exception, actually no errors or warnings are being thrown. If i load the scene in the editor and then play it everything works fine. traitPanel.SetActive(true); get called and my panel shows up. However when I load my scene from another scene using SceneManager.LoadScene(1); the above mentioned line is never reached. Any ideas why this is happening ?
Say you want to have one central place that is "like a singleton" in a Unity project. Example,
SoundEffects
LapTimer
Scores
SocialMediaConnections
All you do is this.
make your script SoundEffects.cs
recall that every Unity project must have a "preload" scene. (it's impossible to not have one)
in the preload scene, have a empty game object called "holder". make sure it is marked "DontDestroyOnLoad"
attach SoundEffects.cs to that holder
you're done.
there's just nothing more to it.
you're finished.
it's "just that simple"
So, any particular script, which happens to be attached to any particular object, in any particular scene, may need to access "SoundEffects"...
To do so, simply do this in Awake:
SoundEffects soundEffects = Object.FindObjectOfType<SoundEffects>();
then just use soundEffects anywhere in that script, for example soundEffects.PlayBoom(13) etc etc.
So in Awake()
Laps laps = Object.FindObjectOfType<Laps>();
Social social = Object.FindObjectOfType<Social>();
AI ai = Object.FindObjectOfType<AI>();
or whatever.
Unity is simple. Incredibly simple. It's just that easy.
write your script, LapTimer.cs or whatever
put it on a holder object in your preload scene (where else could it be?)
there's no "3", that's all there is. It's just that ridiculously simple.
Note that...
In the early days of Unity, someone unfortunately mentioned "singletons" on a QA board somewhere. (You can't have "singletons" in an ECS system, it's meaningless.) At the time, this led to huge discussions about how you can make a "singleton-like thingy" in Unity, which is piling confusion on confusion. Unfortunately from that day to this, you get people learning Unity, who see "singleton" mentioned here and there on the net, and it leads to endless confusion.
Once again, note that the idea of "managers" is just impossibly simple in Unity - explained above. It's trivial. It just couldn't be easier.
Note, above I mention you might want syntactic candy to avoid the incredible chore of typing Laps laps = Object.FindObjectOfType<Laps>();
There are very unfortunately no macros in Unity/c# so you need another way.
All you do is use a "Grid" script http://answers.unity3d.com/answers/1124859/view.html which has been popular for years in Unity.
But honestly, it's so simple to use Scores scores = Object.FindObjectOfType<Scores>(); that I usually just do that nowadays.
Ok how do I explain this. I have a singleton that acts as a data holder. While developing the scene with the game manager I had attached the singleton to the gamemanger object that hold a bunch of scripts. Now when I made the main menu I ofc added my singleton to it so I can carry info to the game scene and because of this my whole game manger object was being deleted. This is the culprit from the DataHolder :
void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);//This right here.
DontDestroyOnLoad(gameObject);
}
So I changed that line to Destroy(gameObject.GetComponent(instance.GetType()));
I want to check if object is active and if it is I want to inactivate other objects. Here is my method. It is not the best one but I just want it to work. This method I have doesn't work right because it only runs once even though I have it in update(). Need some help here...
update: the problem is that when I choose the selection that selection becomes true and because another selection is already true this code is not working and right now I don't know how I can fix it.
public GameObject selectionTop, selectionBot, selectionSplash, selectionFloor;
private void Update() => Check();
public void Check()
{
if (selectionTop.activeSelf)
{
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionBot.activeSelf)
{
selectionTop.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionSplash.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionFloor.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
}
}
Update only gets called when the GameObject its script is on is activeInHierarchy and the script is enabled. My guess is that you have this script on one of the GameObjects that is being disabled and that is causing update not to get called.
So I would move this script to another game object that is not used one of the "selection" game objects so that update will always be called.
Use a variable to store what the current selection is.
Then when a new selection is made, hide the current selection, then call check.
or -
Make a function that hides all the items. Call that each time. Then set the items you want active.
I'm not sure if this is a silly answer, I've never worked with the game sdk but just looking at the code...
the
if(...)
{
}
else if (...)
{
}
I've always seen it written this way, but maybe it's just me, you can check if your code is executed both using the debugger and if it is not, you can use the Debug.Writeline to let something be printed inside the routines on your debug screen to see which code is executed.
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
}