Unity 2D - Why Rect.Contains does Not work in this example? - unity3d

In a 2D space:
I have an sprite and a script component is assigned to it
I've this statement in the Start method:
"onScreenRect = rt.rect;"
It will Not detect mouseOver as it could be expected!
Now if this statement is moved to Update method, it will.
Please explain why.
public class EvtPub : MonoBehaviour
{
RectTransform rt;
Rect onScreenRect;
void Start()
{
rt = GetComponent<RectTransform>();
// having next statement here, the code will Not work!
onScreenRect = rt.rect;
}
void Update()
{
// having next statement here, the code Will work!
// onScreenRect = rt.rect;
onScreenRect.Set(onScreenRect.x + transform.position.x,
onScreenRect.y + transform.position.y, onScreenRect.width, onScreenRect.height);
if (onScreenRect.Contains(Input.mousePosition))
{
Debug.Log("Mouse over detected!");
}
}
}

well, Rect in Unity is a struct,it's a value type. which means the onScreenRect will be a value copy of the rt.Rect,not the reference. when you put the statement in Start(), the value will be copied only one time and will not be updated. :)sry about my poor English and hope helpful.

Related

TrailRenderer start point on click

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;
}

How do I change GameObject properties in scripts Unity?

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

GameObject Follow cursor yet also follows enemies?

I'm making a simple character that follows the player's cursor. What I also want is for when the game object "enemy" appears the character then goes to that location to alert the player. Once the enemy is gone the character continues to follow the cursor like normal. Is there a reason why my script won't work. How else can I paraphrase it?
public class FollowCursor : MonoBehaviour
{
void Update ()
{
//transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
if (gameObject.FindWithTag == "Enemy")
{
GameObject.FindWithTag("Enemy").transform.position
}
if (gameObject.FindWithTag != "Enemy")
{
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
}
}
You are not using FindWithTag correctly, as it is a method that takes a string as parameter you need to use it like this:
GameObject.FindwithTag("Something") as stated in the Unity scripting API
Now to apply this to your code you would need to do the following to set your players position based on wether or not an enemy is found (assuming this script is on your actual player object):
if(GameObject.FindWithTag("Enemy"))
{
//If an enemy is found with the tag "Enemy", set the position of the object this script is attatched to to be the same as that of the found GameObject.
transform.position = GameObject.FindWithTag("Enemy").transform.position;
}
else
{
//when no enemy with the tag "Enemy" is found, set this GameObject its position to the the same as that of the cursor
transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
However this code will just snap your player instantly to the position of the found Enemy. If this is not the desired behaviour you could use a function like Vector3.MoveTowards instead to make the player move to it gradually.
This code also has room for optimisation as searching for a GameObject every update frame is not the ideal solution. But for now it should work.
I'm going to code coding all the function for you, I'm not pretty sure about the beavihour of your code, I understand a gameobject will be attached to the mouse position, so not really following....
Vector3 targetPosition;
public float step = 0.01f;
void Update()
{
//if there is any enemy "near"/close
//targetPosition = enemy.position;
//else
//targetPosition = MouseInput;
transform.position = Vector3.MoveTowards(transform.position, targetPosition , step);
}
For the f you can use a SphereCast and from the enemies returned get the closest one.

GetComponent<Rigidbody>().AddExplosionForce in Unity Doesn't Work

I'm making a little game in Unity, with Stickybombs. I made it so that when you press Right Click, the stickybomb blows up, destroys itself, and instantiates a particle effect for explosions. Here is the following code:
void Update () {
if(Input.GetButtonDown("Fire2")) {
GetComponent<Rigidbody>().isKinematic = false;
GetComponent<Rigidbody>().AddExplosionForce(explosionForce, transform.position, explosionRadius, 3.0f);
Destroy(gameObject);
Instantiate (Explosion, transform.position, new Quaternion(0, 0, 0, 0));
}
}
However, the line GetComponent().AddExplosionForce(explosionForce, transform.position, explosionRadius, 3.0f); doesn't do anything. Am I doing something wrong?
Here is the variables. Thanks in advance :D
public GameObject Explosion;
private float explosionForce = 300;
private float explosionRadius = 15f;
My previous comment, converted into an answer:
Don't know if it can help, but you destroy the object as soon as AddExplosionForce is invoked. This way, you don't have the time to simulate the explosion effect, because the object is immediately destroied. Have you tried by removing the Destroy() call and see what happpens?
Side note: I suggest to call the Destroy() function at the end, and using a delay, as an example.

Setting Active a 3rd person character controller through script

I have applied a 3rd person character controller to an avatar, and everything seems fine.
Once I disable the avatar game object, and use the following code to re-activate it at run time:
public GameObject avatar;
void Start()
{
avatar.SetActive(true);
}
I get the following error message:
NullReferenceException: Object reference not set to an instance of an object
UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter.CheckGroundStatus () (at Assets/Standard Assets/Characters/ThirdPersonCharacter/Scripts/ThirdPersonCharacter.cs:217)
UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter.Move (Vector3 move, Boolean crouch, Boolean jump) (at Assets/Standard Assets/Characters/ThirdPersonCharacter/Scripts/ThirdPersonCharacter.cs:56)
UnityStandardAssets.Characters.ThirdPerson.ThirdPersonUserControl.FixedUpdate () (at Assets/Standard Assets/Characters/ThirdPersonCharacter/Scripts/ThirdPersonUserControl.cs:73)
When I click on it, the main error instance seems to be in the following block of code, in the line with apply root motion:
void CheckGroundStatus()
{
RaycastHit hitInfo;
#if UNITY_EDITOR
// Helper to visualise the ground check ray in the scene view.
Debug.DrawLine( transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance) );
#endif
// 0.1f is a small offset to start the ray from inside the character.
// It is also good to note that the transform position in the sample assets is at the base of the character.
if ( Physics.Raycast( transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance ) )
{
m_GroundNormal = hitInfo.normal;
m_IsGrounded = true;
m_Animator.applyRootMotion = true;
}
else
{
m_IsGrounded = false;
m_GroundNormal = Vector3.up;
m_Animator.applyRootMotion = false;
}
}
Could someone please help me understand why?
It looks like you don't have assigned Animator from Unity Editor but from code (function Start) but this code didn't run because it wasn't enabled at beginning of scene.
Other problem might be that you removed Animator from Game Object where you have your ThirdPersonCharacter script.
You just have to use GetComponent:
GetComponent<Animator>().applyRootMotion = true;
Don't use a variable.