I’m developing a 3D platformer game and one of the key aspects of the game is moving platforms. These moving platforms work by using a Vector3.Lerp between a start position and an end position then move back and forth between these two positions. At some points throughout my level, the moving platforms have to be angled to fit in but my main problem is that when the player is parented to these platforms, the camera rotation changes and when I jump off, it changes back and completely jumps off the platform at a whole new angle.
Basically when the player jumps on the platform, he becomes parented to it, this is what I want as I want the player to move with the platform but I don’t want it to take the rotation. The player is a first person controller with the camera parented to the player object as a whole.
I’ve tried several different things, such as making a bigger empty game object around the platform which isn’t rotated and parent the player to that but no luck, I’ve also tried to set a lock position for rotation upon trigger enter then after parenting, change the player rotation back to the initial rotation but also no luck.
From what I’ve read by google searching all I could find was quickly unparent, change rotation and re-parent but this won’t work in this scenario.
So what’s the best way to approach this, is there something easy I’m missing here?
If you don’t understand anything, feel free to ask me to re-iterate. Thanks for reading, any help would be appreciated.
There’s a difference between rotation and localRotation, and even though you can use either with root objects, their difference becomes imperative in parenting. I can’t help you more without seeing your code, but using localRotation and rotation properly will help you solve the problem.
Thanks for advice, so I think this similar to what I was trying earlier but it didn’t work. My approach was to set a Quaternion to the player’s current localRotation upon triggering (landing on the platform) then parent the object, then reset the localRotation to this earlier Quaternion? This didn’t work, the same logic applies when leaving the platform. Is this what you meant, or am I off a little here?
void OnTriggerEnter(Collider other){
//Check if the object we have collided with is the player
if(other.gameObject.tag == "Player"){
Quaternion plyrRot = other.transform.localRotation;
//Make the player a child of the platform
//so the movement is relative
other.transform.parent = transform;
other.transform.localRotation = plyrRot;
Debug.Log("Parented");
}
}
I can think of a couple of ways for you to solve this issue, but neither of them is elegant, because your solution approach is not elegant in itself.
Parenting an object to another essentially casts an Inverse Transform Point from the parent’s position, rotation and scale relative to the child. In your case you only parent them to make use of the position, essentially ignoring the rotation and the scale. So instead of parenting and only use 30% of parenting’s options, you should just emulate in script what parenting essentially does in regards to position. Applying a relative position is simpler and more elegant than enforcing a global rotation and scale (I know scale isn’t a factor for you yet, but it is for Unity, and you might notice scales slightly offsetting when parenting occurs. Even if you don’t keep track of scale, Unity does anyway).
So, here’s a pseudo-parenting solution which only works in regards of position :
public Transform myChild;
private Vector3 offset;
void OnTriggerEnter(Collider other) {
if(other.gameObject.tag == "Player") {
myChild = other.gameObject.transform;
offset = transform.InverseTransformPoint(myChild.position);
}
}
void OnTriggerExit(Collider other) {
if(other.gameObject.tag == "Player") {
myChild = null;
offset = Vector3.zero;
}
}
void Update ( ) {
if(myChild) {
//Comment the following line if the player is unable to move while on the platform
offset = transform.InverseTransformPoint(myChild.position);
//This line makes the object behave as if it is a child
myChild.position = transfom.TransformPoint(offset);
}
}
To “unparent” the object just make myChild null. Otherwise it automatically unparents itself when it exits the collider.
Also, for performance reasons you might want to change Update function into OnCollisionStay, so the code doesn’t execute while there’s no collision. I’m just demonstrating the principle here. Also, as it states in the comments I added in Update, you might want to comment out the offset reassignment if the player is unable to move while on the platform. Otherwise leave it as it is. Attach the script on the platform with the Trigger. Note that I haven’t tested it so it might have a couple of bugs/typos. Should work in principle though.
Hey there, thanks for the advice. I have noticed some scaling but nothing noticeable. At several points throughout the game, I normally have triggers to “tidy up” such as re-setting the scale and hiding objects that are no longer visible a good bit away. I’ve read your code a few times, I’ve never actually heard of InverseTransformPoint so I had to look it up, I think I grasp the concept of this now.
A few things though, I tried your code and it didn’t work. The player no longer moves with the moving platform, I’ll attach my code, you’ll see that I’ve set up some Debug.Log statements to try and find what’s going on but everything seems right, like where the player co-ordinates should be etc… but the player just doesn’t move with the platform.
void OnTriggerEnter(Collider other){
//Check if the object we have collided with is the player
if(other.gameObject.tag == "Player"){
//Make the player a child of the platform
//so the movement is relative
myChild = other.gameObject.transform;
offset = transform.InverseTransformPoint(myChild.position);
//other.transform.parent = transform;
//Debug.Log("Parented " + offset);
}
}
void OnTriggerStay(Collider other){
//Check if myChild exists
if(myChild) {
//Comment the following line if the player is unable to move while on the platform
//Debug.Log("Here " + offset);
offset = transform.InverseTransformPoint(myChild.position);
//This line makes the object behave as if it is a child
myChild.position = transform.TransformPoint(offset);
Debug.Log(" "+myChild.position);
}
}
void OnTriggerExit(Collider other){
if(other.gameObject.tag == "Player"){
//Remove the player as a child
//other.transform.parent = null;
myChild = null;
offset = Vector3.zero;
//Reset the rotation
Debug.Log("Unparented");
}
}
Thanks for your help thus far, this just might be something obvious I’m missing or something else going wrong in the code somewhere.
I tested the code in a custom scene and it works. Make sure your player has the proper tags, that you use rigidbody on the objects you want to detect collisions, that your colliders are triggers, and that you comment out the offset line if the player is unable to move while on the platform. If he is able to move, you need to make sure to recalculate his offset after the move line in your character controller and after the platform moves (you can move the offset recalculation to a LateUpdate method just to be sure). Otherwise the offset will reset any movements forced by the platform.
PS. Also make sure the player switches to a kinematic object. This motion is not physics based and physics are just going to mess with the player object. Turn kinematic off once player gets unparented.