I'm making game with Unity and I'm using SceneManager.LoadScene for loading from main scene to play scene. Everything is fine, but it takes too long time. So, game move from main scene to play scene but there is a slider between two scenes.
This is my code:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class Load : MonoBehaviour
{
public Slider LoadSlider;
public Text percentSlider;
void Start ()
{
InvokeRepeating ("AdLoadPercent", 0.01f, 0.4f);
}
public void AdLoadPercent()
{
LoadSlider.value += Random.Range(0.6f,0.9f);
percentSlider.text=Mathf.RoundToInt(LoadSlider.value*100).ToString() + " %";
if (LoadSlider.value >= 1f)
{
SceneManager.LoadScene ("Scena1");
}
}
}
Why it take so long when my slider is equal 1?
" long " means that I have to wait more than 15 seconds.
Thanks and kind regards
Is it the same without using the slider?
I haven't used InvokeRepeating, never even heard about it in fact, so there's a chance that there's something going on with this function.
Place LoadScene() line in the Start() function and see if this helps. If it switches scenes instantly (or almost, like in < 1.25s) it's a problem with your repeating function. For things like that I recommend using an Update() function or an IENumerator
Example #1:
bool loadingStarted = false;
void Start()
{
loadingStarted = true;
}
void Update()
{
if(loadingStarted)
{
progressbar.value += Time.deltaTime*0.25f;
//.. and so on ...
}
}
Example #2:
void Start()
{
StartCoroutine(Countdown());
}
IENumerator Countdown()
{
while(progressBar.value < 1f)
{
//Do your incrementation here...
if(progressBar.value >= 1f) break;
return yield new WaitForEndOfFrame(); //or WaitForSeconds(0.05);
}
}
Related
VIDEO < when ever I click my replay button and when the transition is about to start it loads the game before the transition even ends you can see the game being loaded and causes this snappy effect that I'm not sure why is there anyway i could fix this?
my code even though I have a yield that waits for the aimation to finish it doesn't wait at all it only delays when the animation will show and plays the game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
using UnityEngine.SceneManagement;
public class replayscript : MonoBehaviour
{
GameObject replaybutton;
public Animator transitionERR;
public float transitiontime = 19f;
GameObject replay;
public float timeRR = 0f;
// Start is called before the first frame update
// Update is called once per frame
void Start()
{
replay.SetActive(false);
}
void Update()
{
if (CrossPlatformInputManager.GetButton("replaybutton")){
Debug.Log("candice");
StartCoroutine(LoadLevel(1));
replay.SetActive(true);
}
}
IEnumerator LoadLevel(int levelIndex)
{
//play animation
transitionERR.SetTrigger("start");
//wait for the animaton
yield return new WaitForSeconds(1.5f);
//load the scene
SceneManager.LoadScene(1);
}
}
You could try adding the transitionERR.SetTrigger("start"); before using the StartCoroutine(LoadLevel(1));
You could make an event for the animation end and then put your load scene code in there.
Something like:
//
public UnityAnimationEvent OnAnimationComplete;
public AnimationClip clip;
//
AnimationEvent animationEndEvent = new AnimationEvent();
animationEndEvent.time = clip.length;
animationEndEvent.functionName = "AnimationCompleteHandler";
animationEndEvent.stringParameter = clip.name;
clip.AddEvent(animationEndEvent);
//
public void AnimationCompleteHandler(string name)
{
OnAnimationComplete?.Invoke(ChangeSceneMethodName);
}
Also if you plan on using a coroutine to get a animation duration you could use WaitForSeconds(anim.GetCurrentAnimatorStateInfo(0).length+anim.GetCurrentAnimatorStateInfo(0).normalizedTime);
I've just started exploring unity & c# working together, and accidently I faced a next problem:
I have a fish. Fish was supposed to go from left to right, then change its direction and go from right to left. I haven't finished that moving part yet, but I was going to do it with timer. So timer is active, fish starts to move, timer stops, changing direction, timer resets, fish starts to move etc.
I want to flip my sprite, so it will face correct direction. It doesn't work from Elapsed function and I don't understand why. If you have any ideas, please share
using System.Timers;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FishBehavior : MonoBehaviour
{
public float moveSpeed;
private int direction; //for left -1, for right 1
private Timer timer;
private SpriteRenderer img;
private Rigidbody2D rb;
void Start()
{
img = GetComponent<SpriteRenderer>();
rb = GetComponent<Rigidbody2D>();
direction = 1;
timer = new Timer();
timer.Elapsed += TimerElapsed;
ChangeTimerOptions();
}
private void Move()
{
//moving
}
private void ChangeDirection()
{
direction *= -1;
img.flipX = !img.flipX; //Doesn't work!
//stop movement
}
private void ChangeTimerOptions()
{
System.Random rand = new System.Random();
timer.Interval = rand.Next(3000, 8000);
timer.Enabled = true;
Move();
}
private void TimerElapsed(object source, ElapsedEventArgs e)
{
ChangeDirection();
ChangeTimerOptions();
}
}
The issue is that the callback for TimerElapsed is on a different thread than that of Unity. Meaning any sort of calls you make inside of it to methods that would normally work in Unity will not. I would recommend instead using a Coroutine or an Invoke.
To briefly explain what both are, a Coroutine is a special method that completes small amounts of work over multiple frames. The method can also be suspended for certain amounts of time to wait to perform code down the line. An Invoke or InvokeRepeating is a call to perform a method after a certain amount of time or to continually make a call to a method after a set start time and set amount of time.
If you are also eventually incorporating a movement and randomizing your wait time, I would consider using a Coroutine over an Invoke so you can handle all movement/flip logic from the same main call.
public float moveSpeed;
private int direction; //for left -1, for right 1
private Timer timer;
private SpriteRenderer img;
private Rigidbody2D rb;
// store the coroutine in case a duplicate call occurs somehow
private Coroutine MoveAndFlip = null;
void Start()
{
img = GetComponent<SpriteRenderer>();
rb = GetComponent<Rigidbody2D>();
direction = 1;
// detect if any coroutine is already running
CleanUpMoveAndFlipCoroutine();
MoveAndFlip = StartCoroutine(MoveAndFlipAsset());
}
private void Move()
{
//moving
}
private void ChangeDirection()
{
direction *= -1;
img.flipX = !img.flipX;
//stop movement
}
private IEnumerator MoveAndFlipAsset()
{
// instead of milliseconds, use seconds
float randomTimeInterval = Random.Range(3.0f, 8.0f);
while(true)
{
// can do movement here - depending on how you would like to apply motion it would change how
// it is implemented such as directly changing velocity, using a Vector3.Lerp, AddForce, etc.
// wait the time to flip
yield return new WaitForSeconds(randomTimeInterval);
// now flip
ChangeDirection();
}
}
private void CleanUpMoveAndFlipCoroutine()
{
if (MoveAndFlip != null)
StopCoroutine(MoveAndFlip);
}
If you would like an Invoke example to the above implementation, here is how you could approach it.
void Start()
{
img = GetComponent<SpriteRenderer>();
rb = GetComponent<Rigidbody2D>();
direction = 1;
Invoke("ChangeDirection", RandomTimeToFlip());
}
private float RandomTimeToFlip()
{
return Random.Range(3.0f, 8.0f); ;
}
private void ChangeDirection()
{
direction *= -1;
img.flipX = !img.flipX;
//stop movement
// start our method again after a set amount of time
Invoke("ChangeDirection", RandomTimeToFlip());
}
Edit: Apparently, you are able to use the TimerElapsed by changing the Timer's SynchronizingObject reference, but I am not exactly sure what to assign it to. I would still recommend using one of the two methods I described above though.
I am having a problem with my game. I have a script ( that is listed below ) that is supposed to show a text for some time and then make it disappear. This script is being done through a game object with a trigger collider. This script is working fine with the first game object, but then it is not working for the next game instance where I've used it ( as seen in the video listed below ). May anybody tell me what's wrong in this instance and help me to fix this? Thank you!
Video link:https://streamable.com/zwjism
Script used for the first text trigger ( the attack text ):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class CollisionText : MonoBehaviour
{
public TextMeshProUGUI enemyWarning;
public float duration = 4f;
[SerializeField] private Animator anim;
void Start()
{
enemyWarning.enabled = false;
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
StartCoroutine ( showText(other) );
}
}
IEnumerator showText(Collider2D player)
{
enemyWarning.enabled = true; // displays text
attackDemonstration();
yield return new WaitForSeconds(duration); // waits an amount of time x
enemyWarning.enabled = false; // makes the text invisible
Destroy(gameObject); // destroys the trigger after the x amount of time
}
void attackDemonstration()
{
for(int i=1; i<=3; i++)
{
anim.SetTrigger("Attack");
}
}
}
Script used for the second text trigger that doesn't disappear ( the test text ):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class test : MonoBehaviour
{
public TextMeshProUGUI enemyWarning;
public float duration = 4f;
[SerializeField] private Animator anim;
void Start()
{
enemyWarning.enabled = false;
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
StartCoroutine(showText(other));
}
}
IEnumerator showText(Collider2D player)
{
enemyWarning.enabled = true; // displays text
yield return new WaitForSeconds(duration); // waits an amount of time x
enemyWarning.enabled = false; // makes the text invisible
Destroy(gameObject); // destroys the trigger after the x amount of time
}
}
Try activating before calling the StartCoroutine function. Sometimes we cannot make changes in IEnumerator due to some situations that I do not know the reason of. Maybe there is someone who knows better.
or
If you do something like this while you are defining it, you can try it too.
tmpScore = gameObject.AddComponent <TextMeshPro> ();
I hope it helps.
or
The duration value sounds too low and may change quickly. You can try printing something to the console with the debug.log command.
This question already has answers here:
destroy a specific gameobject after an amount of time in unity
(3 answers)
Closed 2 years ago.
The problem is in that way it will create more and more objects if automatic or if using the mouse.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScifiEffects : MonoBehaviour
{
public GameObject spawnEffect;
public bool automaticFire = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (automaticFire == false)
{
if (Input.GetMouseButtonDown(0))
{
GameObject effect = Instantiate(spawnEffect, transform.position, Quaternion.FromToRotation(Vector3.up, spawnEffect.transform.forward)) as GameObject;
}
}
else
{
GameObject effect = Instantiate(spawnEffect, transform.position, Quaternion.FromToRotation(Vector3.up, spawnEffect.transform.forward)) as GameObject;
}
}
}
If I add :
Destroy(effect);
Either in the mouse or if automatic the gameobject will be destroy at once and the effect will not happen. I could use a coroutine but then I will have to call the StartCoroutine inside the Update either if automatic or not and it will start many coroutines.
Destroy takes an optional second parameter
t The optional amount of time to delay before destroying the object.
So you can e.g. simply do
if (Input.GetMouseButtonDown(0))
{
GameObject effect = Instantiate(spawnEffect, transform.position, Quaternion.FromToRotation(Vector3.up, spawnEffect.transform.forward)) as GameObject;
Destroy(effect, 3.5f);
}
To destroy the effect object after 3.5 seconds
Note however that you probably should not Instantiate an object every frame in your else case. Rather you could define a certain interval like e.g.
// adjust in the Inspector
public float maxSpawnPerSecond = 2f;
...
private float timer;
void Update()
{
if (!automaticFire)
{
if (Input.GetMouseButtonDown(0))
{
Spawn();
}
}
else
{
if(maxSpawnPerSecond > 0)
{
timer -= Time.deltaTime;
if(timer <= 0)
{
Spawn();
timer = 1 / maxSpawnPerSecond;
}
}
}
}
private void Spawn()
{
GameObject effect = Instantiate(spawnEffect, transform.position, Quaternion.FromToRotation(Vector3.up, spawnEffect.transform.forward)) as GameObject;
Destroy(effect, 3.5f);
}
There are no downsides to starting a bunch of coroutines other than regular coding issues like infinite loops.
Also coroutines has exactly what you need with a method called WaitForSeconds() that only works in coroutines. As far as I know
void Update()
{
if(true) // Change if statement so you don't create an infinite loop and crash your program
{
StartCoroutine("SpecialEffects");
}
}
IEnumerator SpecialEffects()
{
// Special Effects
yield return new WaitForSeconds(seconds);
// Code that destroys something
}
You can add Thread.Sleep(miliseconds); to wait before it happens. You could also make a Task which sleeps for a while and then does the delete, make sure to await it.
I'm creating a game in unity 2D and in this game a GameObject called Moving_Truck is required to smoothly move into the scene from that left side. as this will be required later, I tried to make the method run from an other code on another object, the object is called scene control and the script is called opening scene.
the problem is when I push the space button the Moving_Truck game object does not move. I am fairly new to C# and have tried a few solutions such as Vector2.MoveTowards and Vector2.Lerp. I have also modified my code multiple times trying to get this to work. here is the most recent version of the codes:
CharacterBase
using UnityEngine;
using System.Collections;
public class CharacterBase : MonoBehaviour {
private float SprSize, HalfSprSize, Distance;
public int run = 1;
public void CharMove(int Dir, string Char, float speed, string BackgroundName)
{
var CharOBJ = GameObject.Find(Char);
var BGOBJ = GameObject.Find(BackgroundName);
SprSize = CharOBJ.GetComponent<Renderer>().bounds.size.x;
HalfSprSize = SprSize / 2;
Vector2 EndPos = new Vector2(BGOBJ.transform.position.x, CharOBJ.transform.position.y);
Debug.Log(EndPos);
CharOBJ.transform.position = Vector2.MoveTowards(CharOBJ.transform.position, EndPos, speed * Time.deltaTime);
}
}
OpeningScene
using UnityEngine;
using System.Collections;
public class OpeningScene : CharacterBase {
int Advance = 0, Run = 0;
void Start ()
{
}
void FixedUpdate()
{
if (Input.GetKeyUp("space"))
{
Run = 1;
Debug.Log("Space Pressed");
}
if (Run == 1)
{
Run = 0;
Advance += 1;
switch (Advance)
{
case 1:
CharMove(-1, "Moving_Truck", 0.05f, "House_Front");
break;
case 2:
CharMove(1, "Moving_Truck", 0.05f, "House_Front");
break;
}
}
}
}
This is driving me nuts, I've been trying to fix it for about an hour or Two now, can someone please help, also sorry for the long question, just comment if you need more info. also please ignore the Dir Argument for now.
Thanks.
Unity's Input.GetKeyUp only returns true on the frame when you release the spacebar. Because of this, CharMove will only be called that one frame you press the spacebar, and then only move 0.05f * timeDelta, which is probably going to be less than a pixel.
Also, this is unrelated, but you don't want to call GameObject.Find(string) every time you move the character. Instead, call it once in the Start() method and then store the result to a field.
Have you tried
GetComponent<Rigidbody2D> ().velocity = new Vector2 (moveSpeed, GetComponent<Rigidbody2D> ().velocity.y);