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

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...

Related

Passing Gameobject throught class in Unity

im trying to make a card game. There is something I can't understand:
I have a class named HandManagement that has a list of GameObject and a method AddToHand that add to a list (the hand) a gameobject passed through variable (bringing in hand)
I have a Tile GameObject on the screen with a script that, when collides with mousedown:
the variable hand of HandManagement type is called with method AddToHand to add that gameobject to hand (AddToHand(this.gameobject)
When I try unity tells me that the object doesnt exist..
Why?
Thank you
Tile Class:
{
m_ObjectCollider.isTrigger = true;
tileInHand.PickTile (tileName);
HandManagement Class:
public GameObject tile;
public List<GameObject> hand = new List<GameObject>();
public void PickTile(string pickedTile)
{
Debug.Log ("picked");
tile = Instantiate (tile);
tile.transform.SetParent (transform, false); //i put it in a grid of hand card
hand.Add (tile); //I add it to the list of card in hand
}}
first of all, change the string to a GameObject, then Instantiate it
public void PickTile(GameObject pickedTile)//change it from string to gameobject
{
Debug.Log ("picked");
tile = Instantiate (pickedTile);//instantiate the picked tile.
tile.transform.SetParent (transform, false);
hand.Add (tile);
}}
now tile holds the GameObject "pickedTile" so it will be added to the grid and to the hand.
Next, you need to change the referenced object from a string to a GameObject
tileInHand.PickTile(tileGameObject);
The Instantiate function copies the object tile into tile, this is empty to begin with. Try instantiating a prefab gameobject into the tile, or make sure gameobject tile is never empty (eg. by setting it in the Start()).
https://docs.unity3d.com/ScriptReference/Object.Instantiate.html

Is there a way to specify where a game object spawns in the hierarchy in unity?

I have set up a random spawner that creates new game objects but they are being created outside of my canvas and therefore can't be seen when play the game. Is there any way to fix this? The objects 'neg thoughts' are UI Buttons and are being created outside of the canvas even though I need them to appear on screen so they can be used.
I did see a similar question but the suggestions didn't work for my problem.
this is very frustrating for me and any help would be awesome!
You can simply pass the parent into Instantiate
parent Parent that will be assigned to the new object
var newObj = Instantiate(prefab, parentTransform);
or with additional transforms
var newObj = Instantiate (prefab, position, rotation, parentTransform);
Or as others already said you can still do it afterwards at any time either by simply assigning a new transform.parent
newObj.transform.parent = parentTransform;
or using transform.SetParent
newObj.transform.SetParent(parentTransform, worldPositionStays);
The advantage of the later is that you have an optional parameter worldPositionStays
worldPositionStays If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.
assigning a new
transform.parent = parentTransform;`
will always act the same way as
transform.SetParent(parentTransform);
since the default value for worldPositionStays (so if you don't explicitly pass it) is true.
So for the specific case of UI in a Canvas you could use
public TheSpawnComponent : MonoBehaviour
{
// Via the Inspector drag&drop any object here that is inside the canvas
// or the canvas itself
[SerializeField] private GameObject parentInCanvas;
[SerializeField] private Button buttonPrefab;
public void DoInstantiate()
{
var newButton = Instantiate (buttonPrefab, parentInCanvas);
//Todo Position and callback
}
}
Or if the spawner script is attached to an object inside the canvas anyway you could also spawn as child of this one directly using
var newButton = Instantiate(buttonPrefab, transform);
You can specify the transforms parent after you have created the game object.
spawnedObject.transform.parent = canvas.transform;
You have to use below code
public GameObject Prefab; // Object to Create
public Transform ParentOfObj;// Must Be inside the canvas or canvas it self
void Start()
{
GameObject g = Instantiate(Prefab) as GameObject;
g.transform.SetParent(ParentOfObj);
g.transform.localScale = Vector3.one;
}

Why the IK controller is not exist in the ThirdPersonController Animator component in the Inspector?

I'm trying to follow the instructions in the unity documents how to use Inverse Kinematics in this page:
Inverse Kinematics
But i don't have the IK Animator Controller when i select Controller for the Animator.
I tried adding the script now. But the hand the right hand is folded to the other side. It's not like it's holding the flash light: The script is attached to the ThirdPersonController. And i dragged to the script in the Inspector to the rightHandObj the EthanRightHand and to the lookObj i dragged the Flashlight.
But the hand seems to be wrong way.
This is the script i'm using now the IKControl:
using UnityEngine;
using System;
using System.Collections;
[RequireComponent(typeof(Animator))]
public class IKControl : MonoBehaviour
{
protected Animator animator;
public bool ikActive = false;
public Transform rightHandObj = null;
public Transform lookObj = null;
void Start()
{
animator = GetComponent<Animator>();
}
//a callback for calculating IK
void OnAnimatorIK()
{
if (animator)
{
//if the IK is active, set the position and rotation directly to the goal.
if (ikActive)
{
// Set the look target position, if one has been assigned
if (lookObj != null)
{
animator.SetLookAtWeight(1);
animator.SetLookAtPosition(lookObj.position);
}
// Set the right hand target position and rotation, if one has been assigned
if (rightHandObj != null)
{
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandObj.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandObj.rotation);
}
}
//if the IK is not active, set the position and rotation of the hand and head back to the original position
else
{
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 0);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 0);
animator.SetLookAtWeight(0);
}
}
}
}
Add an empty game object to the flashlight and target that instead of the flashlight object itself. Hit play and then fiddle with the placement of the empty object until it is where you want it. Then just turn the flashlight into a prefab, stop play mode and make sure the flashlight in the scene matches the prefab (you can just use revert to do that if needed).
Now it would be doing exactly what you want every time. You could even have multiple prefabs with the empty object positioned differently to allow characters with larger or smaller hands to hold it convincingly.

Unity: GameObject always at center regardless of position changes

I am working on a 2D game and have created a game object using C# script as below. I also set my camera to orthogonal and have adjusted my sprite based on the width of the screen. Regardless of the position I set, the object is always at the center of the screen. How can I solve this?
using UnityEngine;
using System.Collections;
public class TestingPositions : MonoBehaviour {
GameObject hero;
Sprite heroSprite;
Vector3 heroPosition;
// Use this for initialization
void Start () {
hero = new GameObject ();
Instantiate (hero, heroPosition, Quaternion.identity);
Camera camera = GameObject.FindGameObjectWithTag ("MainCamera").GetComponent<Camera> ();
heroPosition = camera.ScreenToWorldPoint(new Vector3(Screen.width/4, Screen.height/4, camera.nearClipPlane));
heroSprite = Resources.Load <Sprite> ("Sprites/heroImage");
SpriteRenderer renderer = hero.AddComponent<SpriteRenderer>(); renderer.sprite = heroSprite;
}
}
when you use Instantiate you have to use it on
an existing model.
Instantiate means "duplicate this model" or "copy this model", or "make a new one, using this model as an example".
What you are doing, is creating a brand new empty "hero" game object - and then "instantiating" it. That is meaningless and does nothing.
What you must do whenever you want to use "Instantiate" is this:
public GameObject modelPerson;
Note that the name must be "modelSomething".
first put that in your code. LOOK at the Inspector. MAKE your actual model hero (or whatever it is)
Sit it somewhere off camera where it is not seen.
Now, drag that thing to the "modelPerson" slot in the Inspector.
If you are not familiar with the basics of using Inspector-dragging in Unity, review basic Unity tutorials https://unity3d.com/learn/tutorials/topics/scripting
Next in your code, perhaps in Start, try this
GameObject newHero = Instantiate( modelPerson );
newHero.transform.position = .. whatever you want
newHero.transform.rotation = .. whatever you want
newHero.name = "Dynamically created";
newHero.transform.parent = .. whatever you want
once you understand these basics, there is very much more to learn about Instantiate. You can ask that in separate questions. Good luck.
Your need to save the reference to your gameObject that is created with Instantiate, because Instantiate makes a copy not modifies the original.
To modify a gameobjects position after instantiation, you need to use gameobject.transform.position = newPosition; To modify it before instantiation, you would need to do the "heroPosition" line before using heroPosition in Instantiate.
So like this:
using UnityEngine;
using System.Collections;
public class TestingPositions : MonoBehaviour
{
GameObject hero;
SpriteRenderer heroSprite;
// Use this for initialization
void Start()
{
Camera camera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
//Save the reference to the instantiated object into a variable
//Since you are creating an object from scratch, you don't even need Instantiate, which means copy - not create.
hero = new GameObject();
//Set its position
hero.transform.position = camera.ScreenToWorldPoint(new Vector3(Screen.width / 4, Screen.height / 4, camera.nearClipPlane));
//Set its rotation
hero.transform.rotation = Quaternion.identity;
//Add sprite renderer, save the reference
heroSprite = hero.AddComponent<SpriteRenderer>();
//Assign the sprite
heroSprite.sprite = Resources.Load<Sprite>("Sprites/heroImage");
}
}

Drag and Drop between 2 gameObjects

I have 2 Spheres in my scene. I want to be able to drag and drop my mouse from one sphere to the other, and have an indicator (a straight line for example) while dragging. After releasing the mouse button, I want to store in the first sphere the other sphere (as GameObject).
I need this in UnityScript, but I can accept C# ideas.
So far I have thought about onMouseDown event on the first Sphere, and then onMouseEnter to the other sphere, I'll store the data in some global variable (which I donno how to do yet) and in case of onMouseExit I'll just put the global variable as null.
Then onMouseUp on the first Sphere I'll store the global variable in the pressed object (the sphere).
Any tips and tricks on how to do it?
Assumptions/Setup
You're working in a 2D worldspace; the logic for dragging the object for this part of the solution would need changing.
Setting parent after click drag, referred to setting the object you didn't click to be the child of the parent you did click on.
The camera should be an orthographic camera; using a perspective camera will cause the dragging to not align with where you think it should be.
In order to make the dragging work, I created a quad that was used as the 'background' to the 2D scene, and put that on a new layer called 'background'. Then setup the layermask field to only use the background layer.
Create and setup a material for the line renderer, (I'm using a Particles/Additive shader for the above example), and for the parameters of the line renderer I'm using Start Width: 0.75, End Width: 0, and make sure Use World Space is ticked.
Explanation
Firstly setup the otherSphere field to be pointing to the other sphere in the scene. OnMouseDown and OnMouseUp toggle the mouseDown bool, that is used to determine if the line renderer should be updated. These are also used to turn on/off the line renderer, and set/remove the parent transform of the two spheres.
To get the position to be dragged to, I'm getting a ray based off of the mouse position on screen, using the method ScreenPointToRay. If you wanted to create add smoothing to this drag functionality, you should enable the if (...) else statement at the bottom, and set the lerpTime to a value (0.25 worked well for me).
Note; when you release the mouse, the parent is immediately set, as such both objects end up getting dragged, but the child will return to it's former position anyway.
[RequireComponent(typeof(LineRenderer))]
public class ConnectedSphere : MonoBehaviour
{
[SerializeField]
private Transform m_otherSphere;
[SerializeField]
private float m_lerpTime;
[SerializeField]
private LayerMask m_layerMask;
private LineRenderer m_lineRenderer;
private bool m_mouseDown;
private Vector3 m_position;
private RaycastHit m_hit;
public void Start()
{
m_lineRenderer = GetComponent<LineRenderer>();
m_lineRenderer.enabled = false;
m_position = transform.position;
}
public void OnMouseDown()
{
//Un-parent objects
transform.parent = null;
m_otherSphere.parent = null;
m_mouseDown = true;
m_lineRenderer.enabled = true;
}
public void OnMouseUp()
{
//Parent other object
m_otherSphere.parent = transform;
m_mouseDown = false;
m_lineRenderer.enabled = false;
}
public void Update()
{
//Update line renderer and target position whilst mouse down
if (m_mouseDown)
{
//Set line renderer
m_lineRenderer.SetPosition(0, transform.position);
m_lineRenderer.SetPosition(1, m_otherSphere.position);
//Get mouse world position
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out m_hit, m_layerMask))
{
m_position.x = m_hit.point.x;
m_position.y = m_hit.point.y;
}
}
//Set position (2D world space)
//if (m_lerpTime == 0f)
transform.position = m_position;
//else
//transform.position = Vector3.Lerp(transform.position, m_position, Time.deltaTime / m_lerpTime);
}
}
Hope this helped someone.