I'm writing a 2D Platformer and I'm trying to get the player to stay on a moving platform. I've done searching and tinkering for a day or two now, and I'm not having any luck. Basically, I've been told to try to keep the character moving with the platform when they are touching. Firstly, if I use anything related to OnTriggerEnter(), the player goes right through the platform. If I do OnCollisionEnter() (with a CharacterController on the player and a BoxCollider on the platform), nothing happens at all. These are two things I've found suggested most. The other is parenting the player with the platform, but this apparently causes "problems" (frequently stated, never explained). So, how can I get the player to stay on the moving platform? Here is the code for the moving platform: Code (csharp): public class MovingPlatform : MonoBehaviour { private float useSpeed; public float directionSpeed = 9.0f; float origY; public float distance = 10.0f; // Use this for initialization void Start () { origY = transform.position.y; useSpeed = -directionSpeed; } // Update is called once per frame void Update () { if(origY - transform.position.y > distance) { useSpeed = directionSpeed; //flip direction } else if(origY - transform.position.y < -distance) { useSpeed = -directionSpeed; //flip direction } transform.Translate(0,useSpeed*Time.deltaTime,0); } AND here is the code for the Player's movement (in Update): Code (csharp): CharacterController controller = GetComponent<CharacterController>(); float rotation = Input.GetAxis("Horizontal"); if(controller.isGrounded) { moveDirection.Set(rotation, 0, 0); //moveDirection = new Vector3(rotation, 0, 0); moveDirection = transform.TransformDirection(moveDirection); //running code if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //check if shift is held { running = true; } else { running = false; } moveDirection *= running ? runningSpeed : walkingSpeed; //set speed //jump code if(Input.GetButtonDown("Jump")) { //moveDirection.y = jumpHeight; jump (); } } moveDirection.y -= gravity * Time.deltaTime; controller.Move(moveDirection * Time.deltaTime);
Make the player a child of the platform when its moving, when the play wants to move or jump, stop the player from being its child. This should make it work dandy.
Or if the above idea doesn't work out you could make a ghost platform that is a child of the normal platform to handle movement. That way the normal platform will stop you from falling, and the ghost platform can handle the player movement with OnTriggerEnter(). Assuming that randomly falling through the platform is the only obstacle down that path... lol
http://answers.unity3d.com/questions/8207/charactercontroller-falls-through-or-slips-off-mov.html This is the solution i just implented into a platforming game, works brilliantly
Here is the complete character movement code I used that contains support for moving platforms. It is not perfect but it works pretty well. Code (CSharp): using UnityEngine; using System.Collections; using System.Collections.Generic; public class PlayerCharacter : MonoBehaviour { public float speed = 1.0f; public string axisName = "Horizontal"; private Animator anim; public string jumpButton = "Fire1"; public float jumpPower = 10.0f; public float minJumpDelay = 0.5f; public Transform[] groundChecks; private float jumpTime = 0.0f; private Transform currentPlatform = null; private Vector3 lastPlatformPosition = Vector3.zero; private Vector3 currentPlatformDelta = Vector3.zero; public float groundRadius = 0.2f; public LayerMask whatIsGround; bool doubleJump = false; // Use this for initialization void Start () { anim = gameObject.GetComponent<Animator>(); } // Update is called once per frame void Update () { //Left and right movement anim.SetFloat("Speed", Input.GetAxis(axisName)); /*if(Input.GetAxis(axisName) < -0.1) { Vector3 theScale = transform.localScale; theScale.x *= -1; transform.localScale = theScale; } else if(Input.GetAxis(axisName) > 0.1) { Vector3 theScale = transform.localScale; theScale.x *= 1; transform.localScale = theScale; }*/ transform.position += transform.right*Input.GetAxis(axisName)*speed*Time.deltaTime; //Jump logic bool grounded = false; foreach(Transform groundCheck in groundChecks) { grounded |= Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround); } anim.SetBool("Grounded", grounded); if(jumpTime > 0) { jumpTime -= Time.deltaTime; } if(Input.GetButtonDown(jumpButton) && anim.GetBool("Grounded")) { anim.SetBool("Jump",true); rigidbody2D.AddForce(transform.up*jumpPower); jumpTime = minJumpDelay; } if(anim.GetBool("Grounded") && jumpTime <= 0) { anim.SetBool("Jump",false); } //if (anim.GetBool("Grounded")) { //Moving platform logic //Check what platform we are on List<Transform> platforms = new List<Transform> (); bool onSamePlatform = false; foreach (Transform groundCheck in groundChecks) { RaycastHit2D hit = Physics2D.Linecast (transform.position, groundCheck.position, 1 << LayerMask.NameToLayer ("Ground")); if (hit.transform != null) { platforms.Add (hit.transform); if (currentPlatform == hit.transform) { onSamePlatform = true; } } } if (!onSamePlatform) { foreach (Transform platform in platforms) { currentPlatform = platform; lastPlatformPosition = currentPlatform.position; } } if (currentPlatform != null) { //Determine how far platform has moved currentPlatformDelta = currentPlatform.position - lastPlatformPosition; lastPlatformPosition = currentPlatform.position; } //} } void LateUpdate() { if (anim.GetBool ("Grounded")) { if (currentPlatform != null) { //Move with the platform transform.position += currentPlatformDelta; } } } }
Hi all, and thanks KholdStare2399 for getting me thinking along the right lines. I found my character was sliding about a lot on the platform, because the platforms position was being transformed and the character was moving with velocity and it seemed that this was resulting in them moving either too fast or slowly when stood still. I ended up having to give the platform a Rigidbody2D (isKinematic = true) and move it using velocity in order to make the player move smoothly with it. The character can also climb on moving vertical platforms. Lots of code from fixed update removed for clarity but hopefully this will be enough to get the idea of what i'm doing? Anyway, please do let me know if this makes no sense for any reason, and I hope it helps someone. Code (CSharp): void FixedUpdate () { grounded = Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround); stucktowall = Physics2D.OverlapCircle (wallCheck.position, groundRadius, whatIsGround); moveH = Input.GetAxis ("Horizontal"); /* lots of code removed that was basically cribbed from the getting started tutorials about jumping and such */ if (grounded) { // if we're on the ground, get the platform direction from the floor... platformDirection = GetCurrentPlatformDirection (groundCheck); theBody.velocity = newVector2 ((moveH * maxSpeed) + platformDirection.x, theBody.velocity.y) ; } else if (stucktowall) { // get it from the wall we're meant to be stuck to platformDirection = GetCurrentPlatformDirection (wallCheck); // we slide down the wall we're stuck to slower than a fall... theBody.velocity = newVector2 (0, ( platformDirection.y + theBody.velocity.y) / 2); } else { // basically the physics will be dealing with it, but again, // code not shown for other stuff not related to the platform moving. } /* lots of code removed */ } Vector2 GetCurrentPlatformDirection(Transform checkWith) { RaycastHit2D hit = Physics2D.Linecast (transform.position, checkWith.position, 1 << LayerMask.NameToLayer ("Ground")); if (hit.transform != null) { Rigidbody2D cp = hit.transform.GetComponent<Rigidbody2D> (); if(cp!=null) { return cp.velocity; } } return new Vector2(0,0); } With apologies for formatting.
Hi, we solved this issue using a SliderJoint2D and reducing the character gravity scale to zero while he is on the platform. For the full explanation: http://spacelizardstudio.com/devblog/index.php/2016/03/02/unity-a-guide-to-moving-platforms/
[SerializeField] private Vector3 speedVar; private void OnCollisionEnter(Collision collision) { if(collision.GameObject.tag == "Player"){ platformMoving = true; collision.collider.transform.SetParent(transform); } private void OnCollisionExit(Collision collision) { platformMoving = false; collision.collider.transform.SetParent(null); } private void FixedUpdate() { if(platformMoving){ transform.position += (speedVar * Time.deltaTime); } } Try that and see if it works for you. That fixedupdate is the bread and butter! I thought I'd hate learning the serialized field but it comes in extremely handy especially with fixedupdate's calculation functionality.