- Home /
Horizontal platform passes through CharacterController
Using the code from the Unity 2D Platform tutorial I am able to get a vertically moving platform working no problem but when I change the platform to move horizontally the following issue is encountered:
When standing still the platform passes through the CharacterController
The character controller is not 'pushed' forwards or backwards by the platform
This exact same problem happens in the tutorial example itself if you move the platforms to go in a Horizontal pattern instead of the vertical 'elevator' pattern.
The desired behavior is that the CharacerController be pushed by the horizontal platform in either direction.
FACTS:
Unity 3.4
The platform isKinematic = true
The platform has a rigidBody
The platform has a box collider
CODE: The player controls (attached to PlayerObject)
using UnityEngine;
using System.Collections;
public class CPlayerControls : MonoBehaviour
{
//Public
public float mSpeed = 3.0f;
public float mJumpSpeed = 8.0f;
public float mGravity = 20.0f;
//Private
private Vector3 mMoveDirection = Vector3.zero;
private CharacterController mController;
////////////BEGIN Moving platform support
private Transform activePlatform;
private Vector3 activeLocalPlatformPoint;
private Vector3 activeGlobalPlatformPoint;
private Vector3 lastPlatformVelocity;
/////////END Moving platform support
// Use this for initialization
void Start ()
{
this.mController = this.GetComponent<CharacterController>();
}
// Update is called once per frame
void Update ()
{
// Character is on the ground
if(this.mController.isGrounded)
{
this.mMoveDirection = new Vector3(Input.GetAxis("Horizontal"), 0.0f, 0.0f);
this.mMoveDirection = this.transform.TransformDirection(this.mMoveDirection);
this.mMoveDirection *= this.mSpeed;
// Player Jump
if(Input.GetButton("Jump"))
{
this.mMoveDirection.y = this.mJumpSpeed;
}
//Player Super Jump
if(Input.GetButtonDown("Jump") && Input.GetButton("Fire1"))
{
this.mMoveDirection.y = this.mJumpSpeed + 5.0f;
}
}
// Character is NOT on the ground
if(!this.mController.isGrounded)
{
this.mMoveDirection.x = Input.GetAxis("Horizontal");
this.mMoveDirection.x *= this.mSpeed;
}
/////////BEGIN MOVING PLATFORM SUPPORT
if (activePlatform != null)
{
Vector3 newGlobalPlatformPoint = activePlatform.TransformPoint(activeLocalPlatformPoint);
Vector3 moveDistance = (newGlobalPlatformPoint - activeGlobalPlatformPoint);
if (moveDistance != Vector3.zero)
this.mController.Move(moveDistance);
lastPlatformVelocity = (newGlobalPlatformPoint - activeGlobalPlatformPoint) / Time.deltaTime;
}
else
{
lastPlatformVelocity = Vector3.zero;
}
activePlatform = null;
///////END MOVING PLATFORM SUPPORT
//Moving player
this.mMoveDirection.y -= this.mGravity * Time.deltaTime;
this.mController.Move(this.mMoveDirection * Time.deltaTime);
////////BEGIN Moving platform support
if (activePlatform != null)
{
activeGlobalPlatformPoint = transform.position;
activeLocalPlatformPoint = activePlatform.InverseTransformPoint (transform.position);
}
///////END Moving platform support
}
void FixedUpdate ()
{
// Make sure we are absolutely always in the 2D plane.
Vector3 pos = new Vector3(this.transform.position.x, this.transform.position.y, 0.0f);
this.transform.position = pos;
}
void OnControllerColliderHit (ControllerColliderHit hit)
{
print(hit.moveDirection);
if (hit.moveDirection.y > 0.01)
return;
// Make sure we are really standing on a straight platform
// Not on the underside of one and not falling down from it either!
if (hit.moveDirection.y < -0.9 && hit.normal.y > 0.9)
{
activePlatform = hit.collider.transform;
}
}
}
CODE: The platform controller (attached to PlatformObject)
using UnityEngine;
using System.Collections;
public class CMovingPlatformController : MonoBehaviour
{
public GameObject targetA;
public GameObject targetB;
float speed = 0.05f;
void FixedUpdate ()
{
float weight = Mathf.Cos(Time.time * speed * 2 * Mathf.PI) * 0.5f + 0.5f;
this.transform.position = targetA.transform.position * weight + targetB.transform.position * (1-weight);
}
}
Any help is greatly appreciated!
Answer by iPedro · Oct 11, 2011 at 07:38 PM
Figured it out, it's not the most elegant solution and in all honesty I still expect there to be a better one but for anyone that falls into a similar snake pit here is my solution:
in the platform controller script I added the following
void OnCollisionStay(Collision collisionInfo)
{
CPlayerControls playerControls = collisionInfo.gameObject.GetComponent<CPlayerControls>();
foreach (ContactPoint contact in collisionInfo.contacts)
{
//Debug.DrawRay(contact.point, -contact.normal * 10, Color.white);
float fDir = contact.normal.x;
if(fDir < 0.0f)
fDir += 0.6f;
else
fDir -= 0.6f;
playerControls.HorizontalPlatformCollision(-fDir);
}
}
In the character controller script I added the following method:
public void HorizontalPlatformCollision(float pDirection)
{
this.mMoveDirection = new Vector3(pDirection, 0.0f, 0.0f);
this.mMoveDirection = this.transform.TransformDirection(this.mMoveDirection); // Transforms direction from local space to world space
this.mMoveDirection *= this.mSpeed;
//Moving player
this.mMoveDirection.y -= this.mGravity * Time.deltaTime;
this.mController.Move(this.mMoveDirection * Time.deltaTime);
}
This results in achieving my desired behavior.
Answer by jtbentley · Oct 10, 2011 at 03:52 AM
When moving physics objects, they must be moved within the FixedUpdate loop, because it is synchronous with the physics timestep. Moving in the update means that you might be exceeding tolerances etc inbetween the physics iterations.
Here's a citation from the unity manual
FixedUpdate should be used instead of Update when dealing with Rigidbody. For example when adding a force to a rigidbody, you have to apply the force every fixed frame inside FixedUpdate instead of every frame inside Update.
Also, you should never directly edit the transform of a rigid body, you should only edit its location through rigidbody functions...
Rigidbody.MovePosition
For kinematic rigidbodies it applies friction based on the motion of the rigidbody. This lets you simulate moving platforms with rigidbodies sitting on top of the elevator. If you want other rigidbodies to interact with the kinematic rigidbody you need to move it in the FixedUpdate function.
So just to re-iterate:
I am using a CharacterController to move my player object and calling CharacterController.$$anonymous$$ove as described in the documentation.
I am not directly setting the transform except in fixed update where I make sure it always stays in the 2D world by setting the z position to 0. This was taken directly from the Unity 2D platformer (Lerpz) tutorial so I assume it's ok in this context.
$$anonymous$$y question still is how to achieve the following:
"The desired behavior is that the CharacerController be pushed by the horizontal platform in either direction."
With these clarifications is your advise still the same?
Thanks.
2D world by setting the z position to 0. no longer need todo this, the Rigidbody has axis and rotation locks... maybe these will help, i doubt it.. but that the way todo those now.
Answer by jtbentley · Oct 10, 2011 at 11:21 PM
Have you tried increasing the Skin Width?
Thanks for sticking with me :) The character is a 2D sprite made with Sprite$$anonymous$$anager 2 so there is no skin width as far as I can tell.
I think the solution is to apply a force to the character when a collision occurs with the moving platform, but I'm not exactly sure how i go about doing that yet....