Tansform.position assign attempt for 'Enemy(Clone)' is not valid. Input position is {NaN, NaN, NaN} - unity3d

From my research I can see I am getting a vector issue (it looks like I divide by 0 but I don't do an divisions in my script).
I checked every script I have for positions and tried to catch it between trace statements. Couldnt do it. It looks like its in some update or fixed update but I have no clue where.
This error comes up SOMETIMES right as an enemy is killed.
I feel it has something to do with its position. Clicking on this error does nothing, but SOMETIMES it highlights the gameobject that got the bad transform passed to it.
Is there somewhere in your code I could get getting a NAN vector that I can look for?
Right as an enemy dies I will get a lot of these, and once its dead (destroyed) this problem goes away. It will play through a bit of animation, triggering the error each frame, and then when the enemy dies the error stops.
Clicking the error will only highlight the gameobject but will not open to a script where this occurs.
It comes up maybe 1 in 5 times whenever an enemy is killed...
Here is the enemy dying script (it doesn't set positions so the problem likely does not lie here)
public void killSelf()
{
_animator.SetBool("isAttacking",false);
_animator.SetBool("isDead",true);
StartCoroutine(processKillSelf());
}
IEnumerator processKillSelf()
{
yield return new WaitForSeconds(1f);//its a 30 frame animation
Destroy(gameObject);
}
Is there a way to FORCE unity3D to break on this error and let me look up the stack trace?
Not having the stack trace is the only reason I can't find where this is occurring.

I know this is late but I just had a similar problem and maybe it helps someone in the future.
As you already noticed this always happens if somwhere you try to devide a value by 0.
In my case I just couldn't find where I possibly divided by 0 that easy and unfortunately something like
try
{
transform.position = possiblyBrokenVector3;
}
catch
{
}
didn't catch it either so I couldn't set up proper brakepoints since it was called in Update.
What you always can do is actually checking the values for NaN. I directly made it an extension method for checking Vector3:
public static Vector3Extensions
{
public static bool ContainsNaN(this Vector3 vector)
{
return float.IsNaN(vector.x) || float.IsNaN(vector.y) || float.IsNaN(vector.z);
}
}
Now I can always check before I assign like
if(possiblyBrokenVector3.ContainsNaN)
{
Debug.LogWarning("Ignoring a NaN vector here!", this);
}
else
{
transform.position = possiblyBrokenVector3;
}
set a Brakepoint on the Debug line and finally step through the call history to see how the broken input value actually was produced.

Without seeing more of your code I can't say for sure but it sounds like reference is not being updated internally (ie. something is pointing to a vector that has already been destroyed.) I would poke around your animation code to see if that turns up anything.
For what its worth Object.Destroy has an optional second argument that is a delay timer. ( http://docs.unity3d.com/ScriptReference/Object.Destroy.html ) so you could re-write your code like this:
public void killSelf()
{
_animator.SetBool("isAttacking",false);
_animator.SetBool("isDead",true);
Destroy(gameObject, 1f);
}
and totally omit the processKillSelf() coroutine.

I found the problem:
It was not the character, but an HP bar on the character. The local scale of that HP bar was having one of the values set to 0 on death. Making sure the value is at least 0.1f fixed this problem:
float x = _hpBarMaxScale * health/healthmax>0?(_hpBarMaxScale * health/healthmax):0.1f;
hpBar.transform.localScale = new Vector3(x,hpBar.transform.localScale.y,hpBar.transform.localScale.z);

Related

Unity - How do I click on a specific object instead of just the whole screen in a different object?

I'm coding a top down game, with point and click movement. Currently you are able to click on the map, but you can also click outside the map to move there. I added colliders to the walls, but you still try and go outside. Code example:
if (Input.GetMouseButtonDown(1)) {'move'}
But what I want is something like this:
if (Input.GetMouseButtonDown(1) on MAP) //map is the object
So I want to be able to only click on the map, and if you click outside the map, it won't do anything. Thanks!
My script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 10f;
Vector2 lastClickedPos;
//Rect inRect = new Rect(82.80f, -83.20f, 164.90f, 163.29f);
bool moving;
private void Update()
{
if (Input.GetMouseButtonDown(1) && GameObject.CompareTag("clickedOn")){ // && inRect.Contains(Input.mousePosition)
lastClickedPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moving = true;
}
if (moving && (Vector2)transform.position != lastClickedPos)
{
float step = speed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, lastClickedPos, step);
}
else
{
moving = false;
}
}
}
I think you can resolve your issue using tags or layers. I'll just list how to setup tags since it has a lot less setup vs. doing layers.
First off you'll need to create a tag and since Unity has good documentation on stuff like this I'll just link it here: https://docs.unity3d.com/Manual/Tags.html
Once you created your tag and tagged the ground/environment/area you want to have be clickable with the tag then you just need to find the object you want to collided with and use CompareTag so to put that in an example here is what your if statement could look like:
if (Input.GetMouseButtonDown(1) && collidedObject.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
Just to note I've named the gameObject that was found to collidedObject but you can name it whatever you want.
Hopefully this helps, let me know if I need to clarify something, it has been awhile since I've done a stackoverflow answer so I may have left something out.
Edit: Alright so adding onto this, you'll need to also look into how to do raycasting to check what object you click on so you can determine if it's a spot you can move to.
I've just tested this in a project just to make sure I understand it (I've used raycast a lot but never really done point to click movement before).
Essentially I've broken down things into 3 statements, which you can add together into one if statement but it's more so I can explain everything in detail:
if (Input.GetMouseButtonDown(1))
What you use currently, we want to make sure we only do the next few checks when we click
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity))
So this here, is essentially drawing a line from a position, in this bit we are using the player camera which is just a regular camera reference and converting a point on screen into a ray which we then set the length to be Mathf.Infinity (this can be whatever float, I've just used this for an example) and then we output the hit object to hit which is a RaycastHit struct.
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
Now we finally check to see if the collider is not null (in case we hit the sky or something, which shouldn't happen for you in a top down game) and also that the object has right tag. Again you need to setup the tags which I've listed above and make sure you set the correct game objects in scene to have the correct tag. After which you should be able to move to the position (using the position you've setup and such is fine)
So it should look something like this:
if (Input.GetMouseButtonDown(1))
{
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out rayCastHit, Mathf.Infinity))
{
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
}
}
Make sure to also put a reference to the camera and RayCastHit objects (these are the playerCamera and rayCastHit variables in the above). For my example script that I created I made them global variables.

Moving something rotated on a custom pivot Unity

I've created an arm with a custom pivot in Unity which is essentially supposed to point wherever the mouse is pointing, regardless of the orientation of the player. Now, this arm looks weird when pointed to the side opposite the one it was drawn at, so I use SpriteRenderer.flipY = true to flip the sprite and make it look normal. I also have a weapon at the end of the arm, which is mostly fine as well. Now the problem is that I have a "FirePoint" at the end of the barrel of the weapon, and when the sprite gets flipped the position of it doesn't change, which affects particles and shooting position. Essentially, all that has to happen is that the Y position of the FirePoint needs to become negative, but Unity seems to think that I want the position change to be global, whereas I just want it to be local so that it can work with whatever rotation the arm is at. I've attempted this:
if (rotZ > 40 || rotZ < -40) {
rend.flipY = true;
firePoint.position = new Vector3(firePoint.position.x, firePoint.position.y * -1, firePoint.position.z);
} else {
rend.flipY = false;
firePoint.position = new Vector3(firePoint.position.x, firePoint.position.y * -1, firePoint.position.z);
}
But this works on a global basis rather than the local one that I need. Any help would be much appreciated, and I hope that I've provided enough information for this to reach a conclusive result. Please notify me should you need anything more. Thank you in advance, and have a nice day!
You can use RotateAround() to get desired behaviour instead of flipping stuff around. Here is sample code:
public class ExampleClass : MonoBehaviour
{
public Transform pivotTransform; // need to assign in inspector
void Update()
{
transform.RotateAround(pivotTransform.position, Vector3.up, 20 * Time.deltaTime);
}
}

Sound for collision only once

Ive got the following code to check for agent collision.
I want to fire a MIDI message only once when they start colliding.
Ive got this so far.
void draw(){
//Loop through people, and check collision, then play note, if intersecting
for(int i=0;i<people.size();i++){
Person p = people.get(i);
p.collide(people,collisions);
p.triggerMidi();
p.run();
}
}
public void collide(ArrayList<Person> people, ArrayList<Person> connections) {
for(Person other : people) {
if (other != this) {
if (this.collide(other)) {
this.isIntersecting=true;
//connections.add(other); // when a collision is found, add it to a list for later use.
}
}
}
}
void triggerMidi(){
if(!hasPlayed && this.isIntersecting==true){
MIDI.sendNoteOn(channel, agentNote, 127);
delay(200);
MIDI.sendNoteOff(channel,agentNote, 127);
hasPlayed=true;
}
}
This works to play the sound only once at the start of collision.
But how do I get it to play again at the start of another collision.
Obviously I have to set hasPlayed back to false.
But where?
When I set it to false in the collide loop, the sound play a million times.
Any ideas?
First off, you probably shouldn't have a call to delay() from your drawing thread. That will cause your sketch to become laggy and unresponsive. Instead, you might want to put your sound playing on a different thread.
Then, to answer your original question- do you know how long the note plays for? If so, just record the time that the note starts, and then use that time to check the elapsed time. The millis() function might come in handy for that. When the elapsed time is greater than the duration of the note, then you can set hasPlayed back to false.

A way to check a BufferedImage repaint()?

I'm working in Eclipse and I want to know if I can make an if statement that checks to see if the BufferedImage has been painted/drawn onto the frame. For some reason, it's not painting the correct image because clickable regions are appearing on that picture when they are not supposed to.
For example, when I click the region to go from 4>5 everything is good. When I click from 5 to go to 4 I end up at 6 because the 'regions' from 4 are appearing in 5 (The image should always be painted before the clickable regions are shown) before it's even being painted. I want to restrict this to check if the image has been painted onto the frame first.
I really don't want to use anything else besides what I have right now (so no new classes being implemented to do this task), I really just want a simple yet effective way to resolve this. Here is what I'm talking about:
...
MouseAdapter mouseHandler = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
repaint();
if(n==0)
{
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133
{
n = 4;
}
return;
}
if(n==5)
{
if(...)
{
n = 4;
}
return();
}
if(n==6)
{
if(...)
{
n = 5;
}
if(...)
{
n = 0;
}
if(...)
{
n = 6;
}
return();
}
}
...
I think you might need to give a little more information. The problem might lie in how you repaint, not whether it was painted.
If you are running another thread as your main program, you might instead want to send the mouse events synchronously to that so that the main thread can process the mouse click and then repaint.
Another solution might be to override the repaint method and paint the buffered images there, but you may have done that.
Also, a little off topic, I noticed that you used for loops to determine if the mouse was clicked in a specific area.
You could shorten the code:
for(int i=459; i<491; i++){
if(e.getX()==i){
for(int j=111; j<133; j++){
if(e.getY()==j){
//action taken
}
}
}
}
To:
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133{
//action taken
}
This would take up less space in your code and less time checking every pixel.
Back to your question.
I dont know of a function to tell if a buffered image has been painted. The ploblem that you are having though might of might not be in the code provided. Posting the rest of your code would be beneficial.
Okay I found the solution, I forgot to come back to this question and let you know. The problem was that the mouse was double clicking for some reason. You could almost say it was 'recursive'. I decided to move the mouseListener from inside the paintComponent to outside of it, and surely enough that fixed it.

Time Delay for a process in Unity 3D

I have to give the delay for the process to happen, which I am calling in the Update function.
I have tried CoUpdate workaround also. Here is my code:-
function Start()
{
StartCoroutine("CoStart");
}
function CoStart() : IEnumerator
{
while(true)
{
yield CoUpdate();
}
}
function CoUpdate()
{
//I have placed the code of the Update().
//And called the wait function wherever needed.
}
function wait()
{
checkOnce=1; //Whenever the character is moved.
yield WaitForSeconds(2); //Delay of 2 seconds.
}
I have to move an object when a third person controller(which is another object) moves out of a boundary. I have included "yield" in my code. But, the problem happening is: The object which was moving when I gave the code for in the Update(), is moving, but isn't stopping. And it is moving up and down. I don't know what is happening! Can someone help? Please, thanks.
I am not entirely clear what you are trying to accomplish, but I can show you how to set up a Time Delay for a coroutine. For this example lets work with a simple cool down, much like you set up in your example. Assuming you want to continuously do something every 2 seconds while your game is running a slight modification can be made to your code.
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(2);
}
}
You can also calculate a wait time using some other logic
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(CalculateWait());
}
}
function CalculateWait() : float
{
// use some logic here to determine the amount of time to wait for the
// next CoStart cycle to start
return someFloat;
}
If I have missed the mark entirely then please update the question with a more detail about what you are attempting to accomplish.
I am not 100% sure that I understand you question but if you want to start one object to move when the other is out of bound then just make a reference in the first object to the second object and when the first object is out of bounds (check for this in Update of the first object) call some public function StartMove on the second object.
I wouldn't suggest CoRoutines. It can sometimes crash your computer. Just define a variable and decrement it. Example:
private float seconds = 5;
then do anywhere you want to delay:
seconds -= 1 * Time.deltaTime;
if(seconds <= 0) {your code to run}
This will make a delay of 5 seconds. You can change 5 to any value to change the number of seconds. Also you can speed up the decrement by changing the value of 1. (This is mostly useful when you want to sync 2 delayed actions, by using the same variable)
Hope this helps. Happy coding :)