How to move an object relative to another moving object?

Hi, I am making a sign that moves up and down above a player’s head while the player is in a zone that pops the sign over their head. My problem is that every time I try to get the sign to move in its local space, it keeps returning to the same position that I have specified in my Update() function - which is an offset from the player’s current position. What should I change/use in my code to make this happen?


Ok, I finally got it figured out. It feels like there has to be some better way to do this with Unity, but this is the only working way I figured out to get a sign bouncing over the player’s head as they moved around inside a zone.

This is the full source in case anyone else wants to figure out how to do something similar.

using UnityEngine;
using System.Collections;

public class TriggerForest : MonoBehaviour {

	//private variables
	private bool playerInBounds;
	private bool bCreated; 
	private GameObject signObj; 							//displays the text
	private GameObject floatTextObj; 						//displays instructions to interact
	private static float initDuration = 0.8f; 				//controls duration that sign moves in a direction until switching
	private static float duration = initDuration;
	private static float speed = 0.5f; 						//controls speed that sign moves
	private Vector3 move = new Vector3(0.0f, 0.0f, 0.5f); 	//controls direction that sign moves in
	private bool bDone = false;

	//public variables
	public GameObject player;
	public GameObject playerPosition; 						//always center on the player
	public GameObject forestSign;
	public GameObject floatText;
	public Vector3 signOffset = new Vector3(0.0f, 0.5f, 3.0f); 		//set the default offset for displaying sign above player
	public Vector3 fTextOffset = new Vector3(0.0f, 0.5f, -1.5f);

	// Use this for initialization
	void Start () {

	}

	//check if player is insider trigger
	void OnTriggerEnter(Collider other) {
		if (other.gameObject.tag == "Player") {
			playerInBounds = true;

			//Debug.Log(playerInBounds); //test
			Debug.Log ("Entering forest.");
		}
	}

	void OnTriggerExit(Collider other) {
		if (other.gameObject.tag == "Player") {
			playerInBounds = false;

			//Destroy (other.gameObject); //test
			Debug.Log ("Leaving forest.");
		}
	}

	// Update is called once per frame
	void Update () {

		//alternative way to reference player
		//var player = GameObject.FindWithTag("Player");

		if (playerInBounds) {

			//check if user pressed the key to enter level (using e for now)
			//GetKeyDown occurs only once per press, compared to GetKey which continues to occur as long as button is pressed
			if(Input.GetKeyDown(KeyCode.E)) {

				//reference documentation for the line below:	http://docs.unity3d.com/ScriptReference/Application.LoadLevel.html

				//switch the scene to forest level
				Application.LoadLevel("testSceneLoad");
			}

			//ensures we do not continue making objects; also init run once things for sign here
			if(!bCreated) {

				//create objects from prefabs
				signObj = (GameObject) Instantiate(forestSign, transform.position, transform.rotation);
				floatTextObj = (GameObject) Instantiate(floatText, transform.position, transform.rotation);

				//set the intial position of sign
				signObj.transform.position = player.transform.position + signOffset;

				//parent the sign to the player's position; this updates its position automatically
				signObj.transform.parent = playerPosition.transform;

				bCreated = true;
			}

			/* THE SIGN */
			//update position of sign

			//moves sign down
			if(!bDone) {
				if(duration > 0.0f) {
					
					signObj.transform.Translate(move * speed * Time.deltaTime, Space.Self);
					
					duration -= Time.deltaTime;
					if(duration <= 0.001f) {
						duration = initDuration;
						bDone = true;
					}
				}
			}

			//moves sign up
			if(bDone) {
				if(duration > 0.0f) {
					
					signObj.transform.Translate(-move * speed * Time.deltaTime, Space.Self);
					
					duration -= Time.deltaTime;
					if(duration <= 0.001f) {
						duration = initDuration;
						bDone = false;
					}
				}
			}

			//update rotation of sign
			var newRot = transform.rotation.eulerAngles;
			newRot.x = 0.0f; newRot.y = 180.0f; newRot.z = 0.0f;
			signObj.transform.rotation = Quaternion.Euler(newRot); //this sits the sign upright


			/* THE FLOATING TEXT */
			//update position of floating text
			floatTextObj.transform.position = player.transform.position + fTextOffset;

			//update rotation of floating text
			var newRot2 = transform.rotation.eulerAngles;
			newRot2.x = 90.0f; newRot2.y = 0.0f; newRot2.z = 0.0f;
			floatTextObj.transform.rotation = Quaternion.Euler(newRot2); //this sits the text upright
		}
		else {
			Destroy(signObj);
			Destroy (floatTextObj);
			bCreated = false;
		}
	}
}

Is there a reason your not just parenting it to your player?

2 Answers

2

So for moving an object relative to another object, one of the best ways of doing that would be to parent it to the object you want it to move relative to. Then to animate or to move it around relative to that object you would adjust the localPosition of the child object. I modified a bit of your code to reflect some of these ideas. (Note: I haven’t tested any of this in Unity.)

if(!bCreated) { 
    signObj = (GameObject) Instantiate(forestSign, transform.position, transform.rotation);
    signObj.transform.parent = player.transform;                    // Parent the signObj to the player
    signObj.transform.localPosition = Vector3.up * textOffset;      // Set the local position of the signObj
    bCreated = true;
}
 
// Updates the signObj's position
signObj.transform.localPosition = Vector3.up * textOffset;
 
//update rotation of sign
var newRot = transform.rotation.eulerAngles;
newRot.x = 0.0f; newRot.y = 180.0f; newRot.z = 0.0f;
signObj.transform.rotation = Quaternion.Euler(newRot); //this sits the sign upright
 
// Update the textOffset to move the signObj up and down
textOffset += moveSpeed * moveDirection * Time.deltaTime;

// Restrict the textOffset between the maxHeight and minHeight
// and reverse the direction it moves
if(textOffset > maxHeight) {
    textOffset = maxHeight;
    moveDirection *= -1;
} else if (textOffset < minHeight) {
    textOffset = minHeight;
    moveDirection *= -1;
}

Using “signObj.transform.localPosition = Vector3.up * textOffset;” after I have parented the object to the player centers the sign on the player and its position doesn’t change from the player’s current position. I nearly have it working now though.

So I parented the sign to the player and then I move it in local space which almost does exactly what I am looking to make it do. However, the sign rotates whenever the player rotates, instead of keeping the same position overhead. Is there a way to parent an object to just the position of another object?

I think you could do this by using a dummy object. Assign both the sign & the player to the dummy object. Set transforms on the dummy object, but rotation to the player.