- Home /
Creating Extending/Retracting Piston. Extends OK, won't retract
I'm trying to create a rigidbody game object of a piston that will extend at a certain speed up to a certain position when the player contacts it, and then retract back to its starting position after the player is no longer contacting it. I created an empty game object, and as children I created a cube (with its colliders turned off so as to not interfere with the cylinder), and then a cylinder. I added a rigid body to the cylinder. Here's my code for the script attached to the cylinder.
using UnityEngine;
using System.Collections;
public class Piston : MonoBehaviour {
public float speed;
private bool pistonExtend;
// Use this for initialization
void Start () {
pistonExtend = false;
}
// Update is called once per frame
void Update () {
Debug.Log ("pistonExtend = " + pistonExtend);
//Debug.Log ("velocity = " + rigidbody.velocity);
if (pistonExtend == true & transform.position.y < 0.5f) {
rigidbody.MovePosition(rigidbody.position+Vector3.up * speed * Time.deltaTime);
//Debug.Break();
}
if(pistonExtend == false & transform.position.y > 0.0f)
{
Debug.Log("I should be retracting");
//rigidbody.MovePosition(rigidbody.position+Vector3.down * speed * Time.deltaTime);
rigidbody.MovePosition(Vector3.zero);
}
}
void OnCollisionEnter(Collision other){
if (other.gameObject.tag == "Player") {
pistonExtend=true;
}
}
void OnCollisionExit(Collision other){
if (other.gameObject.tag == "Player") {
pistonExtend=false;
}
}
}
It works halfway. When the player contacts the cylinder, it extends, but extends past the point where the code was to have it stop extending. And then when it retracts, it snaps back but not to the zero point in the code. It usually snaps back to a point in the y0.5-0.8 range, even though its move position should be to vector3.zero.
When I step through 1 frame at a time after extension, the piston sits at its weirdly offset retracted position steadily. However, if I let it run full speed, it bounces between its offset retracted position and another position lower (but not zero).
As far as I can tell, my code logic should be correct. I've tried many other methods to try and get this to work, but none get as close as this. I've tried update, fixedupdate, and lateupdate. All produce identical behavior. What am I doing wrong?
I tested your code a bit. I added this to the top of the Update() callback:
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.Space)) {
pistonExtend = !pistonExtend;
}
And I uncommented line 25 and commented out line 26. I placed this script on a cube with a Rigidbody. It worked fine when I hit the space key...both extending and contracting. Next I made the cube kinematic. I then created a "Player" object, and tested the collisions. Again it worked fine. It is only when I turned off 'is$$anonymous$$inematic' that I had issues. So my best guesses:
You did not make the object kinematic and therefore the physics between the two objects is playing a role.
There is some other script impacting the positioning of your piston.
It seems there's something wrong with the parent/child relationships going on. Since my cylinder is a child of a placehoder parent object, all transforms should be relative to that parent, correct? This doesn't seem to be the case here. I have the parent object at position y=-0.5, and when I use your debug spacebar to move the cylinder, it moves up twice as far as it should (to world co-ordinates y=0.5, but relative to the parent this is 1.0) and then only retracts back half as far as it should (world co-ordinates 0.0, relative to parent this is 0.5).
Have I messed something up with my parent/child relationship? Is transform.position always relative to world coordinates and not parent co-ordinates? I'll try and make a video of the behavior I'm observing and post the link here.
A transform has ways of reading and setting both the local and the world position. 'transform.position' is the world position of that object, and 'rigidbody.$$anonymous$$ovePosition()' set the world position of that object. If you want to check the local position of the object, you need to use localPosition when reading from the transforms. For example:
if (pistonExtend == true & transform.localPosition.y < 0.5f) {
Well, there's my problem. Now my new problem is that the cylinder does not snap exactly back into its original position. Depending on the value of my speed variable, the cylinder can snap as high as 1.5 (relative), and when it snaps back, it can be as low as -0.5. This is a problem because when it snaps too low, the collider is now below where the player is able to touch it, and thus won't be able to actuate the cylinder again. Any way to get it to snap to exactly the positions I need it to?
You did change line 22 to also use transform.localPosition? Beyond that, you should only see a very small amount out of range. For example, given your values of 0.5 for top and 0.0 for bottom, your local position should be between 0.501 and -0.01. That is, your code is not guaranteed to land precisely at .5 or 0.0. First get your local position working correctly so you only have a very slight amount above and below your target values. Then if you need a precise position, use $$anonymous$$athf.Clamp() to restrict the values to the 0.0 to 0.5 range.
Answer by Crono141 · Aug 10, 2014 at 05:38 PM
OK. Thanks so much to robertbu for helping me work this out. My final implementation is a little bit different than what I was initially going for. The point of the piston object was to propel the player using physics when the play made contact. This was very difficult to fine tune between bounciness of the player physics material and the rate the piston was extending. I ultimately decided to just have the piston directly change the players velocity instead of using the rigidbody collision to propel the player. Here's the code now.
using UnityEngine;
using System.Collections;
public class Piston : MonoBehaviour {
public float speedExtend; // speed piston extends
public float speedRetract; // speed piston retracts
public float timeExtended; // time in seconds piston should remain extended before retracting
public Vector3 jumpspeed = new Vector3 (0, 10, 0); // player's vertical speed when piston is triggered
private bool pistonExtend; // boolean to tell script to extend piston
private float timeLeft; // variable to hole amount of time left before piston retracts
private bool extended; // boolean to denote if piston is in an extended state
private Quaternion orientation; //get worldspace orientation to make piston object work in any orientation
// Use this for initialization
void Start () {
pistonExtend = false;
extended = false; //piston starts retracted
timeLeft = timeExtended; //set time remaining to total time
}
// Update is called once per frame
void FixedUpdate () {
orientation = transform.rotation; //set orientation quat during update so it can move in the world and always work
if (pistonExtend == true) { //do this when we want to extend
if(transform.localPosition.y <= 0.5f){ // extend until we reach this position
rigidbody.MovePosition(rigidbody.position+orientation*Vector3.up * speedExtend * Time.deltaTime);
extended = true;
}
timeLeft = timeLeft-Time.deltaTime; // start counting down
if(timeLeft<0f){
pistonExtend=false; //when time runs out, set the retract flag
}
}
if(pistonExtend == false & transform.localPosition.y >= 0.1f) //retract back to start position
{
rigidbody.MovePosition(rigidbody.position+orientation*Vector3.down * speedRetract * Time.deltaTime);
timeLeft = timeExtended; //reset extended time
extended = false;
}
}
void OnTriggerEnter(Collider other){
if (other.gameObject.tag == "Player" && extended == false) {
pistonExtend=true;
other.rigidbody.velocity = orientation * jumpspeed; //set player velocity according to orientation
}
}
}
Changing from "update" to "FixedUpdate" solved my weird overtravel issues. Thanks again.
Your answer
Follow this Question
Related Questions
Rotate my player around my locked target. 1 Answer
Trouble with implementing Vector2.ClampMagnitude in the Rigidbody2D.MovePosition function 1 Answer
How do I implement both Rigidbody.MovePosition and Rigidbody.MoveRotation simultaneously? 1 Answer