Unity 3D - Cannot set the parent of my collided GameObject - unity3d

This bug is doing my head in and I can't figure it out!
I have an empty gun, and when I move it near the magazine it has a collide trigger that grabs the magazine, scales it and inserts it into the gun. This bit works fine. However in the code i also set the magazine parent to the gun and set the gravity to false and isKinematic to true - these bits do not happen. Therefore the magazine scales to the gun and then floats off into the distance and when I look in Unity it is not set to be a child of the gun and gravity and kinematic are both unchecked even though I clearly set these below. How is the magazine being scaled and positioned correctly but the parent and rigidbody edits are not being made?
Here is the code:
//THIS CLASS IS A CHILD OF THE GUN
public class GunBody : MonoBehaviour {
void OnTriggerEnter(Collider collider)
{
Debug.LogError("collision with well");
//check if the collision was with the magazine
if (collider.gameObject.name == "Magazine 1")
{
//reload the gun if it was
addClip(collider.gameObject);
}
}
public void addClip(GameObject magazine)
{
magazine.transform.parent = transform.parent; //DOES NOT WORK
magazine.transform.position = transform.parent.position;
magazine.transform.rotation = transform.parent.rotation;
magazine.transform.localRotation = Quaternion.Euler(-89.96101f, 0f, 0f);
magazine.transform.localScale = new Vector3(14f, 20f, 20f);
magazine.transform.localPosition = new Vector3(0f, -0.8215461f, 1.64772f);
magazine.GetComponent<Rigidbody>().useGravity = false; //DOES NOT WORK
magazine.GetComponent<Rigidbody>().isKinematic = true; //DOES NOT WORK
}
Hopefully someone can spot what is going wrong?
Thanks

when you do magazine.transform.parent = transform.parent; you set your script's parent as a parent of magazine. I don't know how your code is organized, but are you sure you want to set magazine as a child of GunBody's object parent? And also maybe try using magazine.transform.SetParent(this.transform).

This scale and positioning problems come from not having (1,1,1) scale of gun. Try to put a gun and a magazine to another empty object.

You can do
magazine.transform.SetParent(transform.parent, false);
So it will be set according to position and orientation of Parent. No need to write First 3 lines of code to set Parent Position and Rotation.

Related

Is there a way to make an instantiated text follow a game object?

I'm currently experiencing a coder's block right now as I'm trying to instantiate text on a game object, specifically a Zombie prefab. I've gotten down the enemy's position for the text to be spawned but can't seem to make it follow the zombie's movement as it walks towards the player.
This is my 'Word Spawner' script.
public GameObject wordpb; // my word prefab
public Transform canvas; // connected to a canvas ui that has world camera set
public EnemyOne enemy;
public DisplayWord Spawn()
{
Vector2 targetPos = new Vector2(enemy.transform.position.x, enemy.transform.position.y);
GameObject wordobj = Instantiate(wordpb, targetPos, Quaternion.identity, canvas);
DisplayWord displayWord = wordobj.GetComponent<DisplayWord>();
return displayWord;
}
and this is where the DisplayWord class is derived from.
public Text text;
public void SetWord(string word)
{
text.text = word;
}
public void ThrowLetter()
{
text.text = text.text.Remove(0, 1);
text.color = Color.red;
}
public void ThrowWord()
{
Destroy(gameObject);
}
My best guess is that I should be implementing a void Update method in which I use transform.Translate? Or should I put a placeholder that acts as a child class to my Zombie prefab and attach the DisplayWord script there? Please help a poor soul out.
How does the text relate to the Zombie game object? Knowing what the text is for in relation to the zombie might inform the best way to make it follow the game object.
If the text is something like a worldspace nametag, instead of translating in Update you could make the text a child of the game object it needs to follow. It might not be the most elegant solution but if you give each text its own worldspace canvas you could assign the text as a child of the zombie, or, if you know you'll always have text following a zombie, you could just add a worldspace canvas and text to the zombie prefab...
To instantiate as a child you'd need to rework your word prefab to be its own worldspace canvas with your text as its child.
You can assign a parent when you instantiate:
wordobj = Instantiate(wordpb, enemy);
Or set the parent after instantiation:
wordobj.transform.SetParent(enemy.gameObject.transform, true);
The second parameter in SetParent is 'worldPositionStays', and controls whether the child object keeps its world position (true) or whether it's transform is evaluated relative to its new parent's transform. You could make it work either way: if you leave it 'true' you don't need to change the rest of your code, but I think if you make it false you don't need to get the enemy GameObject's position... The same is true for Instantiate when a parent is assigned but no transform position/rotation. So I think you could skip the step of finding the enemy's position and rewrite your Spawn code to:
public GameObject wordpb; // my word prefab
public EnemyOne enemy;
public DisplayWord Spawn()
{
GameObject wordobj = Instantiate(wordpb, enemy, false);
DisplayWord displayWord = wordobj.GetComponent<DisplayWord>();
return displayWord;
}
You'd need to assign the newly instantiated canvas's worldspace camera to make this work...

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.

Black traces on screen

I am building a simple pizza slicing game in Unity. I have a Sprite with a collider attached as the pizza, and whenever I drag the mouse over the collider, it gets an enter point and an exit point and draws a transparent line between them, pretty much like this:
The problem is, whenever I move the pizza, the line leaves some sort of trace behind it, even if I disable the line renderer or the whole pizza game object. The only solution I have found for this, is to resize the game window, and everything goes back to normal.
Also, if I play my game in maximized mode, the screen fills with black lines, which is very odd. As before, if I resize my game window everything goes back to normal. Here is a screenshot:
Does anyone have an idea on how to solve this problem? Or, at least, go around it by resizing the game window somehow from the script, during play mode? I already tried resizing the camera and changing the resolution, but it does not change anything.
This is how I draw the lines:
void OnTriggerEnter2D (Collider2D other)
{
enterPoint = LineDrawer.instance.mousePos;
enterPoint.z = 0;
points.Add (enterPoint);
}
void OnTriggerExit2D(Collider2D other)
{
exitPoint = LineDrawer.instance.mousePos;
exitPoint.z = 0;
points.Add (exitPoint);
GameObject obj = new GameObject ();
GameObject instance = Instantiate (obj, transform.position, Quaternion.identity) as GameObject;
Destroy (obj);
instance.transform.SetParent (transform);
instance.name = "Cut";
linerenderer = instance.AddComponent<LineRenderer> ();
linerenderer.material = new Material(Shader.Find("Diffuse"));
linerenderer.useWorldSpace = false;
linerenderer.SetWidth (0.15f, 0.15f);
linerenderer.SetVertexCount (2);
linerenderer.SetPosition (0, enterPoint);
linerenderer.SetPosition (1, exitPoint);
}
The problems that I can see here are:
You should not give Quaternion.identity when you create it. You are making it a child. It should have the same rotation with the parent, or else your local positions won't work:
GameObject instance = Instantiate (obj, transform.position, transform.rotation) as GameObject;
You are not using world space for the line. I bet your enterPoint and exitPoint are in world space. Try this:
linerenderer.SetPosition (0, linerenderer.transform.InverseTransformPoint(enterPoint));
linerenderer.SetPosition (1, linerenderer.transform.InverseTransformPoint(exitPoint));
Why are you doing all this in Trigger functions? There is no if, so these functions execute whenever the pizza hits anything.

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

Unity3D OffNavMesh jump issue

I have set up Unity navigation meshes (four planes), navigation agent (sphere) and set up automatic and manual off mesh links. It should now jump between meshes. It does jump between meshes, but it does that in straight lines.
In other words, when agent comes to an edge, instead of actually jumping up (like off mesh link is drawn) it just moves straight in line but a bit faster. I tried moving one plane higher than others, but sphere still was jumping in straight line.
Is it supposed to be like this? Is it possible to set up navigation to jump by some curve? Or should I try to implement that myself?
I came by this question, and had to dig through the Unity sample. I just hope to make it easier for people by extracting the important bits.
To apply your own animation/transition across a navmesh link, you need to tell Unity that you will handle all offmesh link traversal, then add code that regularly checks to see if the agent is on an offmesh link. Finally, when the transition is complete, you need to tell Unity you've moved the agent, and resume normal navmesh behaviour.
The way you handle link logic is up to you. You can just go in a straight line, have a spinning wormhole, whatever. For jump, unity traverses the link using animation progress as the lerp argument, this works pretty nicely. (if you're doing looping or more complex animations, this doesn't work so well)
The important unity bits are:
_navAgent.autoTraverseOffMeshLink = false; //in Start()
_navAgent.currentOffMeshLinkData; //the link data - this contains start and end points, etc
_navAgent.CompleteOffMeshLink(); //Tell unity we have traversed the link (do this when you've moved the transform to the end point)
_navAgent.Resume(); //Resume normal navmesh behaviour
Now a simple jump sample...
using UnityEngine;
[RequireComponent(typeof(NavMeshAgent))]
public class NavMeshAnimator : MonoBehaviour
{
private NavMeshAgent _navAgent;
private bool _traversingLink;
private OffMeshLinkData _currLink;
void Start()
{
// Cache the nav agent and tell unity we will handle link traversal
_navAgent = GetComponent<NavMeshAgent>();
_navAgent.autoTraverseOffMeshLink = false;
}
void Update()
{
//don't do anything if the navagent is disabled
if (!_navAgent.enabled) return;
if (_navAgent.isOnOffMeshLink)
{
if (!_traversingLink)
{
//This is done only once. The animation's progress will determine link traversal.
animation.CrossFade("Jump", 0.1f, PlayMode.StopAll);
//cache current link
_currLink = _navAgent.currentOffMeshLinkData;
//start traversing
_traversingLink = true;
}
//lerp from link start to link end in time to animation
var tlerp = animation["Jump"].normalizedTime;
//straight line from startlink to endlink
var newPos = Vector3.Lerp(_currLink.startPos, _currLink.endPos, tlerp);
//add the 'hop'
newPos.y += 2f * Mathf.Sin(Mathf.PI * tlerp);
//Update transform position
transform.position = newPos;
// when the animation is stopped, we've reached the other side. Don't use looping animations with this control setup
if (!animation.isPlaying)
{
//make sure the player is right on the end link
transform.position = _currLink.endPos;
//internal logic reset
_traversingLink = false;
//Tell unity we have traversed the link
_navAgent.CompleteOffMeshLink();
//Resume normal navmesh behaviour
_navAgent.Resume();
}
}
else
{
//...update walk/idle animations appropriately ...etc
Its recommended to solve your problems via animation. Just create a Jump animation for your object, and play it at the correct time.
The position is relative, so if you increase the Y-position in your animation it will look like the object is jumping.
This is also how the Unity sample is working, with the soldiers running around.
Not sure what version of unity you are using but you could also try this, I know it works just fine in 4:
string linkType = GetComponent<NavMeshAgent>().currentOffMeshLinkData.linkType.ToString();
if(linkType == "LinkTypeJumpAcross"){
Debug.Log ("Yeah im in the jump already ;)");
}
also just some extra bumf for you, its best to use a proxy and follow the a navAgent game object:
Something like:
AIMan = this.transform.position;
AI_Proxy.transform.position = AIMan;
And also be sure to use:
AI_Proxy.animation["ProxyJump"].blendMode = AnimationBlendMode.Additive;
If you are using the in built unity animation!
K, that's my good deed for this week.
Fix position in update()
if (agent.isOnOffMeshLink)
{
transform.position = new Vector3(transform.position.x, 0f, transform.position.z);
}