I have a problem to display a text at run time in my game for a second , so I want to know if its possible to add or remove a GUI label to the seen at run time ?
Do you mean something like this?
void OnGUI() {
if (textShouldBeShown) {
GUI.Label(new Rect(10f, 10f, 100f, 50f), "MyText");
}
}
GUI components get drawn on every frame. the OnGUI() function is just like the Update() function except that unlike the Update() function GUI components can be called in it.
You can think of OnGUI as a loop. It will call GUI components declared inside of it in order, then do it all over again every frame. So if you hook into this loop and block some components from being called at runtime, the very next frame those componants will not be rendered.
Here is a set of functions that can allow you to do this is a specified time:
private bool guiIsOn = true;
private void TurnOffGUIInSeconds(int seconds)
{
StartCoroutine(_TurnOffGUIInSeconds(seconds));
}
private IEnumerator _TurnOffGUIInSeconds(int seconds)
{
yield return new WaitForSeconds(seconds);
guiIsOn = false;
}
void OnGUI()
{
if(guiIsOn)
{
GUI.Label(new Rect(5,5,5,5), "Label text");
}
}
Related
I followed Brackeys tutorial on how to create a Fruit Ninja Replica (youtube).
When creating the blade, though, the behaviour I got wasn't exactly the same.
Expected behaviour
Actual behaviour
The difference is that in the Actual behaviour, the trail starts where it stopped the last time it was shown. The code responsible for this is exactly the same as the video:
public class BladeController : MonoBehaviour
{
bool isCutting = false;
public GameObject bladeTrailPrefab;
GameObject currentBlade;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0)) {
StartCutting();
} else if (Input.GetMouseButtonUp(0)) {
StopCutting();
}
if (isCutting) {
UpdateCut();
}
}
void UpdateCut()
{
GetComponent<Rigidbody2D>().position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
void StartCutting()
{
isCutting = true;
this.currentBlade = Instantiate(bladeTrailPrefab, transform);
}
void StopCutting()
{
isCutting = false;
Destroy(currentBlade, 1f);
}
}
After understanding the code, I thought the problem was that I instantiated the bladeTrail before actually moving the Blade to the new position, but tried moving the Instantiate method to UpdateCut after changing the position and only if this.currentBlade == null.
I've search a lot about this, and even found some other posts with the same problem but no answer.
It seams the Instantiate is using the last mouse position to instiantiate the prefab.
Maybe use:
Instantiate(bladeTrailPrefab, Camera.main.ScreenToWorldPoint(Input.mousePosition), Quaternion.identity)
I ran into this problem following the tutorial as well.
It's a few years later but for those of you who hit this page, I found a solution for me that while isn't fantastic, it's better than having the streaks shown in the post.
Before instantiating the trail vfx, make sure to wait until fixed update is called after you set the position of the parent transform.
I did this with a Coroutine like so:
private IEnumerator StartCutting()
{
// When we begin cutting, move the blade object to the input position
m_isCutting = true;
m_previousPosition = m_camera.ScreenToWorldPoint(Input.mousePosition);
m_rigidBody.position = m_previousPosition;
// Then for positions to be updated so that the vfx doesn't get confused
yield return m_waitForFixedUpdate; // new WaitForFixedUpdate(); <-- cache this
// Instantiate the trail at this new position
m_currentTrail = Instantiate(m_trailPrefab, m_rigidBody.transform);
m_collider.enabled = false;
}
I'm trying to save/load my game. In the load method, everytime I change the properties of a GameObject, those changes are applied and then get reverted shortly after. Here is my code:
public void Load()
{
SceneManager.LoadScene(sceneID);
List<GameObject> rootObjects = new List<GameObject>();
Scene scene = SceneManager.GetActiveScene();
scene.GetRootGameObjects(rootObjects);
int ncube = 0, npick = 0;
for (int i = 0; i < rootObjects.Count; ++i)
{
GameObject obj = rootObjects[i];
if (obj.CompareTag("Player"))
{
obj.transform.position = player.position;
obj.transform.rotation = player.rotation;
obj.transform.localScale = player.localScale;
}
else if (obj.CompareTag("Cube"))
{
obj.transform.position = cube[ncube].position;
obj.transform.rotation = cube[ncube].rotation;
obj.transform.localScale = cube[ncube].localScale;
++ncube;
}
else if (obj.CompareTag("Pickup"))
obj.SetActive(pickup[npick++]);
else if (obj.CompareTag("Door"))
obj.SetActive(door);
else if (obj.CompareTag("GreenWall"))
obj.SetActive(greenWall);
}
}
Those changes are applied to the GameObject, however they get aborted right away. How can I resolve this?
The script contains these lines of code is not a component of the GameObject.
Edit 1: Complete code updated.
Problem is I think that
Scene scene = SceneManager.GetActiveScene();
scene.GetRootGameObjects(rootObjects);
gets the objects from the scene before the Scene is fully loaded so they are reset.
From the Docu
When using SceneManager.LoadScene, the loading does not happen immediately, it completes in the next frame. This semi-asynchronous behavior can cause frame stuttering and can be confusing because load does not complete immediately.
I guess you rather should use SceneManager.sceneLoaded and do your stuff there like
public void Load()
{
SceneManager.LoadScene(sceneID);
}
And maybe in an extra component within the scene:
void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// your stuff here
}
void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
though we don't know/see where player, cube[ncube] etc come from ...
for transparting values between Scenes you should get into using ScriptableObjects
The problem might be that SceneManager.LoadScene completes in the next frame. See the documentation: https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadScene.html
It says:
When using SceneManager.LoadScene, the loading does not happen
immediately, it completes in the next frame. This semi-asynchronous
behavior can cause frame stuttering and can be confusing because load
does not complete immediately.
You change the values within the same frame, thus they are overwritten when loading the scene finishes. Cou could use an event to prevent that behaviour: https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager-sceneLoaded.html
Is it possible to delay the effects of a script that is attached to a game object?
I have 2 characters in a scene, both receiving live motion capture data, so they are both animated identically. I have rotated one by 180 degrees and placed it in front of the other one, to create the impression that they are copying/mirroring each other. Each has several scripts attached to them, of course.
Now, I would like to be able to delay one character a bit (e.g. 1 sec) to make it look more realistic, as if one character is observing the other character and then copying its exact movements.
I am trying to achieve this by buffering the animation data (network data) that reaches the second character. Below, is the code snippet that animates the 1st character immediately:
void Update()
{
Vector3[] latestPositions;
Quaternion[] latestOrientations;
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latestOrientations))
{
updateMvnActor(currentPose, latestPositions, latestOrientations);
updateModel(currentPose, targetModel);
}
}
And below, is the code snippet that is supposed to animate the 2nd character but in fact there is no movement at all:
private Queue<Vector3[]> posQueue;
private Queue<Quaternion[]> rotQueue;
void Start()
{
posQueue = new Queue<Vector3[]>();
rotQueue = new Queue<Quaternion[]>();
}
void Update()
{
Vector3[] latestPositions;
Quaternion[] latestOrientations;
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latestOrientations))
{
posQueue.Enqueue(latestPositions);
rotQueue.Enqueue(latestOrientations);
if ((posQueue.Count > 10) && (rotQueue.Count > 10))
{
Vector3[] delayedPos = posQueue.Peek();
Quaternion[] delayedRot = rotQueue.Peek();
updateMvnActor(currentPose, delayedPos, delayedRot);
updateModel(currentPose, targetModel);
}
}
}
Isn't peek supposed to provide the 1st element in the queue, which here would be the one in the 11th place? Or is it because queue is not suitable for heavy animation updating?
EDIT: SOLVED
Added int delayedFrames = 30; to class variables (to replace number 10) and used Dequeue instead of Peek.
EDIT: Answer to #JTech's question isn't complete in this answer, look at the comments underneath his/her question for the final solution
I personally dislike WaitForSeconds, it has slowed my game way down once.
Try something like:
public List<Vector3[]> PreviousPositions;
public List<Quaternion[]> PreviousRotations;
private Vector3[] latestPositions;
private Quaternion[] latestOrientations;
void Start(){
PreviousPositions = new List<Vector3[]>();
PreviousRotations = new List<Quaternion[]>();
}
void Update(){
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latetOrientations))
{
updateModel(currentPose, targetModel);
//Add the position and rotationstuff to the 'buffer' lists here
Invoke("DoStuff", 1);
}
}
void DoStuff(){
updateMvnActor(currentPose, PreviousPositions[0] , PreviousRotations[0]);
PreviousPositions.RemoveAt(0);
PreviousRotations.RemoveAt(0);
}
OnApplicationQuit(){
CancelInvoke();
}
I don't think you can delay the Update method.
What you can use is WaitForSeconds, or you can test if 1 sec passed with an if statement based on Time.
Then execute the code to move the second character.
EDIT
I think a simple timer will do the job but you will need to take in account the 1 sec time when getting the latest positions/orientations.
public float delay = 1.0f;
private float timer = 0;
void Update()
{
Vector3[] latestPositions;
Quaternion[] latestOrientations;
timer += Time.deltaTime;
if (timer >= delay)
{
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latestOrientations))
{
updateMvnActor(currentPose, latestPositions, latestOrientations);
updateModel(currentPose, targetModel);
}
timer = 0;
}
}
Otherwise you can use InvokeRepeating with a delay as a parameter to repeat a method every X seconds.
To take only the actions made before the delay, you can register the time at the moment when it's done, 'remove' the actions made during the delay and take the actions made before it.
Or register the number of actions made by the first character during the delay and remove the same number of actions - the last ones - to your update on the second character. (seems easier/more simple)
Like this, I assume the second character will not do the last actions made during the delay, but will do it after.
So I've got this script here that moves the player by -1.25 when you click it but I want it to be continuously adding -1.25 until you release the button. Right now it only moves once when you click the button. My code:
var character : GameObject;
function OnMouseDown () {
character.GetComponent(Animator).enabled = true;
BlahBlah ();
}
function OnMouseUp () {
character.GetComponent(Animator).enabled = false;
}
function BlahBlah () {
character.transform.position.x = character.transform.position.x + -1.25;
}
Does anyone have any ideas? Thanks!
You're simply forgetting to work in the Update()
var character : GameObject;
function OnMouseDown ()
{
character.GetComponent(Animator).enabled = true;
}
function OnMouseUp ()
{
character.GetComponent(Animator).enabled = false;
}
function BlahBlah ()
{
// I added time.deltaTime, since you'll be updating the value every "frame",
// and deltaTime refers to how much time passed from the last frame
// this makes your movement indipendent from the frame rate
character.transform.position.x = character.transform.position.x - (1.25* time.deltaTime);
}
// The standard Unity3D Update method runs every frame
void Update()
{
if (character.GetComponent(Animator).enabled)
{
BlahBlah ();
}
}
What I did here is using Unity logic. Almost everything works in the Update() function, which gets called every frame. Note that frame rate can vary based on machine / complexity of scene, so make sure to use always Time.deltaTime when adding something related.
Another note for you: modifying the position directly won't make your object react to collisions ( so you'll be moving through objects, but you will still "trigger" the collision ). So, if you want to manage collision, remember to use physics!
you can use Input.GetMouseButton because it registers every frame so when you hold down the mouse it gets it but because it checks every frame while the mouse is down so your object will move so fast so you may want to add a timer that if the time is reached it moves so it moves a bit slower so we check if a specified amount of time that we set in timeLeft is passed and the mouse is held down then we move our object
float timeLeft=0.5f;
void Update(){
timeLeft -= Time.deltaTime;
if (Input.GetMouseButton(0)){
if(timeLeft < 0)
{
BlahBlah ();
}
}
}
void BlahBlah () {
timeLeft=0.5f;
character.transform.position.x = character.transform.position.x + -1.25;
}
As I remember onMouseDown fires every frame if you're holding the button, so you'll need to do the movement within that function, let me check one sec.
I have two cs files, Main.cs and Menu.cs. On OnGUI event which is in Main.cs file I call method from Menu.cs.
private void OnGUI()
{
Menu menu=new Menu();
menu.Create_Menu();
}
And in Menu.cs.
public void Create_Menu ()
{
StartCoroutine(LoadCar());
}
private IEnumerator LoadCar()
{
//Load Object
Download download;
download=new Download();
GameObject go = null;
yield return StartCoroutine(LoadAsset("http://aleko-pc/3dobjects?key=1017&objecttype=1","car13",(x)=>{go = x;}));
}
I get error NullReferenceException
UnityEngine.MonoBehaviour.StartCoroutine (IEnumerator routine)
If I copy private IEnumerator LoadCar() method in Main.cs class, and call from OnGUI it works.
Maybe I do not understant working area of Coroutines, Can any body help me?
First of all the OnGUI method is called every frame and I don't think you want to download the assets every frame.
Second, you need to make sure Menu is derived from MonoBehviour and added to the view hierarchy.
A better approach would be to add Menu as a component to a GameObject (maybe the same that has the Main script attached) and call Create_Menu on the Start method of Menu.