Dear stackoverflow community,
I have countdown timer for my double points power up and now I have problem because my code works fine in game,but when timer is active game is lagging , not too much but any lag is not good for my game because the player needs to play smoothly without any unoptimised component..
I have this code and I bet the game is lagging because the code is in update method ( I tried to put it in game manager script but then timer won't countdown so that is not solution )
This is the code ( Thanks to stackoverflow user #siusiulala , who wrote me the working code)
but seems like it needs to be in another method or something because Update method running performance when has countdown inside.
private void Update(){
if (isDoublePoints)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
Debug.Log("TIMER ISS " + powerUpTimer);
if (powerUpTimer <= 0)
{
// End of power up time
isDoublePoints = false;
}
}
}
public void OnPickPowerUp(float buffTime)
{
powerUpTimer += buffTime;
}
I hope someone will give solution to lagg because I saw a lot of games that has power up systems without any laggs inside...
Thank you stackoverflow, without you my game would not ever come to end :)
what trollingchar's answer says about the Debug.Log is correct.
To use the [SerializeField] might be considered as a dirty and lazy hack by some people. Because it has the side-effect that it is now serialized, that means the value is stored in the assets. It's not bad but if you are exact it shouldn't be done with fields that will be changed on runtime anyway.
Instead you can simply go to the Inspector, open the context menu and set it to Debug Mode
this makes the Inspector not use the Custom EditorScripts but instead reveal all private fields (of Serializable types).
For example for the Transform component
However way more efficient than using the Update method with a flag at all would be to rather use a Coroutines.
Coroutines can be started and run parallel (every frame right after) the Update method but the advantage: when a coroutine is finished - it is finished and doesn't continue checking the bool flag every frame.
So whenever you pickup a PowerUp instead of setting the flag to true rather use
StartCoroutine(PowerUpRoutine());
and implement a routine like
private IEnumerator PowerUpRoutine()
{
isDoublePoints = true;
while(powerUpTimer > 0)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
//Debug.Log("TIMER ISS " + powerUpTimer);
// yield in simple words makes Unity "pause"
// the execution here, render this frame and continue from here
// in the next frame
yield return null;
}
// End of power up time
isDoublePoints = false;
}
public void OnPickPowerUp(float buffTime)
{
powerUpTimer += buffTime;
// avoid concurrent routines
if(!isDoublePoints) StartCoroutine(PowerUpRoutine());
}
In order to display it in your game you can use a Text or TextMeshPro and set the text like e.g.
[SerializeField] private Text _text;
private IEnumerator PowerUpRoutine()
{
isDoublePoints = true;
while(powerUpTimer > 0)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
//Debug.Log("TIMER ISS " + powerUpTimer);
// set the text of the Text component to display the value
// for the $ symbol google for "c# string interpolation"
_text.text = $"TIMER IS {powerUpTimer:00.00}";
// yield in simple words makes Unity "pause"
// the execution here, render this frame and continue from here
// in the next frame
yield return null;
}
// End of power up time
isDoublePoints = false;
}
From my experience, Debug.Log() is a very expensive method. It will cause lag when called every frame. My IDE even highlights usage of Debug.Log() in Update() as warning because of that. Use this method only for debugging, and then remove.
If you want to be able to see the timer value, add [SerializeField] attribute to your field and it will show up in the inspector.
You can use the profiler by selecting Window-Analysis-Profiler, assuming you are using Unity 2018.x. It records how much time processing takes, and helps locating bottlenecks.
Related
Does this work? This is in the start method, using photon for networking. I am trying to wait till room time is initialised.
Wait:
if (!PhotonNetwork.CurrentRoom.CustomProperties.ContainsKey("StartTime") )
{
goto Wait;
}
else
{
goto Continue;
}
Continue:
startTime = double.Parse(PhotonNetwork.CurrentRoom.CustomProperties["StartTime"].ToString());
In general I would say avoid using goto at all!
In almost all cases I can think of any other solution in my eyes is cleaner and better to read and maintain than goto jumps. It is more a "relic" of former times. In the examples of goto might be the only use-case where it might make sense .. within a switch-case or to break out of a nested loop .. but even there you can find other (in my eyes better) solutions.
Your code basically equals writing
while(!PhotonNetwork.CurrentRoom.CustomProperties.ContainsKey("StartTime")) { }
startTime = double.Parse(PhotonNetwork.CurrentRoom.CustomProperties["StartTime"].ToString());
And latest now I hope you see the huge issue: You have a neverending while loop!
Inside the while the condition is never changed
And it can not be changed from the outside either since you run this in Start so the entire Unity main thread is blocked until that loop ends. I'm not 100% sure but afaik PhotonNetwork needs the Unity main thread to dispatch the received events -> your condition probably will never ever become true.
You should rather use a Coroutine. A Coroutine is like a small temporary Update method. It is not async but rather runs right after Update until the next yield statement and thereby still allows your Unity main thread to continue rendering and doesn't freeze your entire application.
// Yes, if you make Start return IEnumerator
// then Unity automatically runs it as a Coroutine!
private IEnumerator Start ()
{
// https://docs.unity3d.com/ScriptReference/WaitUntil.html
// This basically does what it says: Wait until a condition is true
// In a Coroutine the yield basically tells Unity
// "pause" this routine, render this frame and continue from here in the next frame
yield return new WaitUntil(() => PhotonNetwork.CurrentRoom.CustomProperties.ContainsKey("StartTime"));
startTime = double.Parse(PhotonNetwork.CurrentRoom.CustomProperties["StartTime"].ToString());
....
}
Even better than checking this every frame in a loop at all would actually be
check this once in start
only check it again once after the room properties have actually changed
So something like e.g.
bool isInitialzed;
private void Start ()
{
TryGetStartTime (PhotonNetwork.CurrentRoom.CustomProperties);
}
private void TryGetStartTime(Hashtable properties)
{
if(!properties.Contains("StartTime")) return;
startTime = double.Parse(properties["StartTime"].ToString());
isInitialzed = true;
}
public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
TryGetStartTime (propertiesThatChanged);
}
And rather make other methods wait until isInitialized is true.
Good day, I'm trying to create an enemy that damages the player because that's what enemies do. I have figured it out on how to do this the only problem is that the enemy kills the player almost instantly and is too overpowered... I was trying to put some cooldown before the enemy strikes again but unfortunately, an OnTriggerEnter2D seems to only accept 1 if statement.
void OnTriggerEnter2D(Collider2D col){
//What iam trying to achieve but this doesn't work
if(Time.time > nextDamageTime) {
if(col.CompareTag("Player")){
player.curHealth -= 1;
Debug.Log("Enemy damaged player!");
nextDamageTime = Time.time + cooldownTime;
}
}
}
First of all this is c#. There is nothing that can anyhow control how many if blocks you open within a method.
So, no there is no limit on conditions and if blocks within OnTriggerEnter2D!
I don't see a direct problem in your code actually except maybe: OnTrigggerEntet2D is called exactly once namely in the moment the trigger enters without exiting. So if this already instantly kills the player then reducing 1 might simply be a too high value.
With the timer nothing happens because as said it is called only once, so in the case the timer wasn't finished yet nothing happens until the enemy leaves and re-enters eventually.
You might want to rather use OnTriggerStay2D which is rather called every frame as long as a trigger collides.
float timer;
private void OnTriggerEnter2D (Collider other)
{
// reset the timer so the first hit happens immediately
timer = 0;
}
private void OnTriggerStays2D(Collider other)
{
if(!col.CompareTag("Player")) return;
timer -= Time.deltaTime;
if(timer > 0) return;
timer = cooldownTime;
player.curHealth -= 1;
}
Which now would reduce the players health by 1 every cooldownTime seconds.
In general especially for multiple enemies I personally would rather give the player the control over timer after it was last hit so the player itself has kind of an invincible time. Enemies can simply attack like they would do usually but the player itself decides whether the damage counts or not.
I am trying to run a block of code a certain amount of seconds after a 2D Collision. Currently, when the collision occurs it calls a function that loops over itself until a certain time is reached. The time is calculated using a time variable and adding Time.deltaTime each iteration.
This is the function
public void springBack(Collider2D collision, float timeCount) {
if (timeCount > springReloadTime * 10) {
Debug.Log("Springing Back!");
timeCount = 0f;
} else {
timeCount = timeCount + Time.deltaTime;
springBack(collision, timeCount);
}
}
The issue occurs when I run this. Everything happens in one frame right after the collision instead of after a few seconds. If I could call the springBack(collision, timeCount); function on the next frame, this would work. When I looked it up, some people suggested using yield return null;, but since this isn't a loop it gives me this error here:
The body of 'playerMovement.springBack(Collider2D, float)' cannot be an iterator block because 'void' is not an iterator interface type
Is there another way to force it to run on the next frame? I could call it from the update() loop, but I think I can have multiple instances of this function running without it inferring with each other. If you need the values of some of the public variables (like springReloadTime), please ask.
how can I display 3D text after certain time then hide it after certain time
My tries
public Text text_tap;
GameObject.Find("3dtext").active = true; // first try but it dosnt work
if (Time.time > 5) {
// second try but it I cant attach my 3d text to my script
text_tap.gameObject.SetActive(true);
}
I cant find any thing in 3D documentation
I don't know the exactly problem, but there you have some hints:
If you search in the scene for a GameObject that is deactivated it won't find it. The Gameobject MUST be active for the GameObject.Find() function to work. The easiest thing you can do is to keep the GameObject activated, and if the initial state is for it to stay hidden just hide it in the Awake().
Secondly, seems that you are trying to access a TextMesh object
but you reference in your code a Text object.
If you find a GameObject and request a Component that the GO does not contains, it returns null.
Finally The api to Activate/Deactivate a GameObject (GO) is
myGameobject.SetActive(true)
The one you are using (myGameobject.active = true) is deprecated
Try this example, it should work:
public YourMonoBehaviour : MonoBehaviour
{
public TextMesh text_tap;
float awakeTime;
void Awake()
{
// Remember to activate the GO 3dtext in the scene!
text_tap = GameObject.Find("3dtext").GetComponent<TextMesh>():
awakeTime = Time.time
}
void Update()
{
if ((Time.time - awakeTime) > 5)
{
// second try but it I cant attach my 3d text to my script
text_tap.gameObject.SetActive(true);
}
}
}
If you need to "do something after a delay" you're talking about Coroutines.
Checking Time.time will only check if the game has been running for x time, and using Thread.Sleep in Unity will cause it to delay since you're causing an Update or similar to lock and not return.
Instead, use
yield return WaitForSeconds(5);
text_tap.gameObject.SetActive(false);
As another warning, this code assumes that the target object is not the same gameObject as the one hosting this script, since coroutines do not execute on inactive objects. Similarly, disabling an ancestor (via the scene hierarchy, or transofrm.parent) of a gameObject disables the gameObject itself.
If this is the case, get the component that renders 3d text and disable it instead of the whole gameObject via the enabled field.
you can also use Invoke() to achieve. as explained above note that the text would have to be set by other means than Find cause if it is not active it will not find it.
void Start() //or any event
{
Invoke("ShowTextTap", 5f);//invoke after 5 seconds
}
void ShowTextTap()
{
text_tap.gameObject.SetActive(true);
//then remove it
Invoke("DisableTextTap", 5f);
}
void DisableTextTap()
{
text_tap.gameObject.SetActive(false);
}
I'm writing a game with some animations, and use those animations when the user clicks on a button. I would like to show the user the animation, and not "just" call a new level with Application.loadLevel. I thought I could use the Time.DeltaTime in the onMouseUp method and add it to a predefined 0f value, then check if it is bigger than (for example) 1f, but it just won't work as the onMouseUp method adds just "it's own time" as the delta time.
My script looks like this now:
public class ClickScriptAnim : MonoBehaviour {
public Sprite pressedBtn;
public Sprite btn;
public GameObject target;
public string message;
public Transform mesh;
private bool inAnim = true;
private Animator animator;
private float inGameTime = 0f;
// Use this for initialization
void Start () {
animator = mesh.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
}
void OnMouseDown() {
animator.SetBool("callAnim", true);
}
void OnMouseUp() {
animator.SetBool("callAnim", false);
animator.SetBool("callGoAway", true);
float animTime = Time.deltaTime;
Debug.Log(inGameTime.ToString());
// I would like to put here something to wait some seconds
target.SendMessage(message, SendMessageOptions.RequireReceiver);
}
}
}
Im not entirely sure what your trying to do by using Time.deltaTime in onMouseUp. This is just the time in seconds since the last frame was rendered, and should act the same no matter where you try to access it. Normally it is used in a function that is called every frame, not one-off events like onMouseUp.
Despite not being certain what you are trying to achieve, it sounds like you should be using Invoke:
http://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
Just put the code you wish to be delayed into a separate function, and then invoke that function with a delay in onMouseUp.
EDIT: To backup what some others have said here I would not use Thread.Sleep() in this instance.
You want to do this (and all waiting functions that do not appear to make the game "freeze") by blocking the Update loop by using a Coroutine.
Here is a sample of what you are probably looking for.
void OnMouseUp()
{
animator.SetBool("callAnim", false);
animator.SetBool("callGoAway", true);
//Removed the assignement of Time.deltaTime as it did nothing for you...
StartCoroutine(DelayedCoroutine());
}
IEnumerator DoSomethingAfterDelay()
{
yield return new WaitForSeconds(1f); // The parameter is the number of seconds to wait
target.SendMessage(message, SendMessageOptions.RequireReceiver);
}
Based on your example it is difficult to determine exactly what you want to accomplish but the above example is the "correct" way to do something after a delay in Unity 3D. If you wanted to delay your animation, simply place the calling code in the Coroutine as I did the SendMessage invocation.
The coroutine is launched on it's own special game loop that is somewhat concurrent to your game's Update loop. These are very useful for many different things and offer a type of "threading" (albeit not real threading).
NOTE:
Do NOT use Thread.Sleep() in Unity, it will literally freeze the game loop and could cause a crash if done at a bad time. Unity games run on a single thread that handles all of the lifecycle events (Awake(), Start(), Update(), etc...). Calling Thread.Sleep() will stop the execution of these events until it returns and is most likely NOT what you're looking for as it will appear that the game has frozen and cause a bad user experience.