I have a character in my scene that moves with a character controller (using that Wow camera script). There’s an elevator that he walks onto in my scene, triggering a collider that moves the elevator up using transform.position. Of course, when the elevator moves up, it moves through the character controller, and my character falls through it.
How can I keep this from happening? I know one way would be to make the player a child of the lift, but that seems inelegant. I’m wondering if there’s another way to do it.
I would try adding a kinematic rigidbody and using Translate instead of transform.position. I found that if you tell an object to move into a space occupied by another object, it passes right through the colliders.
This is a code i have been looking for for a long time too, I also want to use a character controller style walker(Not rigidbody)walker. Such a common design item would be cool if it were a editor script setup.
How about a terrain collider on the elevator/platform :idea:
Im trying the trigger to parent style now. But after wrestling with the 2d platform scripts its seem to need the platform controller to work and after trying to create a workable script it just wouldnt work.
lol
Im Totally :shock: At how many post topics i read through for clues to a (CharacterController)platform script.
But i think im getting close.
var other: GameObject;
function OnTriggerEnter(other:Collider){
yield WaitForSeconds(0.1);
//other.gameObject.transform.parent=gameObject.transform;
other.gameObject.transform.parent=gameObject.transform;
}
function OnTriggerExit(other : Collider) {
other.gameObject.transform.parent = null;
other.gameObject.transform.parent = null;
}
I just raised a trigger up a little on the platform, rigidbody to no grav and kinematic, i have the platform mover script moving my platform not a animation(i think any platform/mover script works) and this script on the platform as well. and i tested it a good amount of times(think once it didnt unparent) but worked 99% of the time.
So could someone test this and add some bells and whistles to gain a ultimate platform script for all
I double checked the OnTriggerExit code just to show ths platform who’s in charge. lol 8)
In that platformer tutorial, the platform does NOT make the player a child. Also, it DOES use transform.position to move. Somehow the player moves with the platform, not through it, but I can’t duplicate the results.
Is anyone else having any progress without making the player a child?
This works for me for riding a merry go round. Tag your objects appopriately and insert it in place of mine. JS script below…
var myCharacterController : CharacterController;
private var activePlatform : Transform;
private var hit : ControllerColliderHit;
private var activeGlobalPlatformPoint : Vector3;
private var activeLocalPlatformPoint : Vector3;
private var activeGlobalPlatformRotation : Quaternion;
private var activeLocalPlatformRotation : Quaternion;
var tagName : String;
private var moveDirection = Vector3.zero;
private var grounded : boolean = false;
function FixedUpdate () {
if (tagName != "MovingSurface")
{
activePlatform = null;
lastPlatformVelocity = Vector3.zero;
}
if (tagName == "MovingSurface")
{
if (grounded) {
// We are grounded on a Moving Surface, so recalculate move direction directly from axes
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);// Move the controller
var controller : CharacterController = GetComponent(CharacterController);
var flags = controller.Move(moveDirection * Time.deltaTime);
grounded = (flags CollisionFlags.CollidedBelow) != 0;
//Keep moving (?) by getting the spped var from the FPSWalker CC script or whatever you are using
//moveDirection *= FPSWalker.speed;
}
var calculatedMovement = moveDirection * Time.deltaTime;
// Moving platform support
if (activePlatform != null) {
var newGlobalPlatformPoint = activePlatform.TransformPoint(activeLocalPlatformPoint);
var moveDistance = (newGlobalPlatformPoint - activeGlobalPlatformPoint);
transform.position = transform.position + moveDistance;
lastPlatformVelocity = (newGlobalPlatformPoint - activeGlobalPlatformPoint) / Time.deltaTime;
// If you want to support moving platform rotation as well:
var newGlobalPlatformRotation = activePlatform.rotation * activeLocalPlatformRotation;
var rotationDiff = newGlobalPlatformRotation * Quaternion.Inverse(activeGlobalPlatformRotation);
transform.rotation = rotationDiff * transform.rotation;
}
else
{
lastPlatformVelocity = Vector3.zero;
}
//Controller Move takes place here
collisionFlags = myCharacterController.Move (calculatedMovement);
// Moving platforms support
if (activePlatform != null) {
activeGlobalPlatformPoint = transform.position;
activeLocalPlatformPoint = activePlatform.InverseTransformPoint (transform.position);
// If you want to support moving platform rotation as well:
activeGlobalPlatformRotation = transform.rotation;
activeLocalPlatformRotation = Quaternion.Inverse(activePlatform.rotation) * transform.rotation;
}
}
}
function OnControllerColliderHit (hit : ControllerColliderHit) {
// 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.5) {
activePlatform = hit.collider.transform;
tagName = hit.collider.tag;
}
}
Yes, indeed, there is another method. It has to do with detecting when the character collider hits another object through OnColliderHit. This is the initial function in the character controller in the tutorial:
function OnControllerColliderHit (hit : ControllerColliderHit)
{
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;
}
}
Then you modify your character’s movement to match the lift’s movement.
It seems like there would be a performance hit, since OnControllerColliderHit is triggered every frame. A bit much for the few times my character uses a lift.
My player is being successfully parented to the lift trigger. The lift moves up, my player goes with it for a second, then falls through to the lift. Stays with that for a second, then falls through that also, to his death.
:shock:
C# Script:
using UnityEngine;
using System.Collections;
public class SingleLiftScript : MonoBehaviour {
// Variables:
public float liftSpeed = 0.01f;
public bool isMoving = false;
public int _direction = 1; // 1 is up, -1 is down
// Update:
void Update () {
if(isMoving)
{
Vector3 pos = transform.position;
float yNow = transform.position.y;
pos.y = yNow + Time.time * liftSpeed * _direction;
transform.position = pos;
}
}
// Functions:
void OnTriggerEnter (Collider other)
{
Debug.Log("collider tag: "+other.tag+", collider name: "+other.name);
if(other.tag == "Player")
{
print("player ENTERED the lift.");
// yield WaitForSeconds(0.1);
other.gameObject.transform.parent=gameObject.transform;
Debug.Log(other.tag+"'s parent is now "+other.gameObject.transform.parent.tag);
isMoving = true;
// liftScript.GetMoving ();
}
else
if(other.tag == "Trigger")
{
isMoving = false;
print("lift trigger is stopping movement of lift");
if(other.name == "End")
{
print("trigger is changing direction");
ChangeDirection();
}
}
}
void OnTriggerExit(Collider other) {
if(other.tag == "Player")
print("player Exited the lift.");
{
other.gameObject.transform.parent = other.gameObject.transform;
Debug.Log(other.tag+"'s parent is now "+other.gameObject.transform.parent.tag);
}
}
public void ChangeDirection()
{
_direction *= -1;
}
}
Yeah, it seems you’ll need to provide some code to properly handle gravity on a standard character controller or it will fall thru.
The 2d Platformer code simply checks to see if it’s under a platform with a collider check, then adds the platform as a variable for the scripts. after which it changes the characters position based on the amount the platform has moved each frame.
Parenting won’t work…at least in my case as it broke other scripts. I tried for days…That script does. Although it doesn’t take on the rotation of the merry go round…but the CC sticks in place and faces wherever it should usually do via MouseLook and LookAt etc…
I got part of that script off of Unity Answers… The part that makes your head hurt makes mine hurt too I modfied it to stick to not just translating but rotating platform but still have the CC work the same. You would love my 800+ line GUI script :twisted: It’'s a brain stomper too… I should just pick easy crap! No one is better at kicking my own butt than me!
This is surprisingly tricky, most physics engines do not handle it well.
The first thing to do is make the elevator kinematic. Than somehow, maybe using a trigger, detect when the character is on top of the elevator. When this occurs you want to be able to feed the displacement of the elevator as input into the character controller. Its not quite that simple because if you are also applying gravity to the character when the elevator is going down it will tunnel through. This usually works fairly well, as long as the elevator is not going all that fast (at least slower than jumping).
that freezes my character unless I include this line:
if(grounded || onLift) {
Here’s the Character Controller’s Update() function, if you want to see it in context:
function Update () {
if(!isTalking){ // <---- !isTalking
//
// Only allow movement and jumps while ----------------- GROUNDED -------------
if(grounded || onLift) {
moveDirection = new Vector3((Input.GetMouseButton(1) ? Input.GetAxis("Horizontal") : 0),0,Input.GetAxis("Vertical"));
// if moving forward and to the side at the same time, compensate for distance
// TODO: may be better way to do this?
if(Input.GetMouseButton(1) Input.GetAxis("Horizontal") Input.GetAxis("Vertical")) {
moveDirection *= .7;
}
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= isWalking ? walkSpeed : runSpeed;
moveStatus = "idle";
if(moveDirection != Vector3.zero) {
moveStatus = isWalking ? "walking" : "running";
if (isWalking){
anim.Walk();
} else {
anim.Run();
}
} else {
anim.Idle();
}
// Jump!
if(Input.GetButton("Jump"))
{
anim.Jump();
moveDirection.y = jumpSpeed;
}
} // END "IS GROUNDED"
} // END "!TALKING"
// Allow turning at anytime. Keep the character facing in the same direction as the Camera if the right mouse button is down.
if(Input.GetMouseButton(1)) {
transform.rotation = Quaternion.Euler(0,Camera.main.transform.eulerAngles.y,0);
} else {
transform.Rotate(0,Input.GetAxis("Horizontal") * rotateSpeed * Time.deltaTime, 0);
}
// Toggle walking/running with the T key
if(Input.GetKeyDown("t"))
isWalking = !isWalking;
//Apply gravity:
if(!onLift)
moveDirection.y -= gravity * Time.deltaTime;
//Move controller
var controller:CharacterController = GetComponent(CharacterController);
var flags = controller.Move(moveDirection * Time.deltaTime);
grounded = (flags CollisionFlags.Below) != 0;
};
static function ClampAngle (angle : float, min : float, max : float) {
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp (angle, min, max);
}
I modded the 2d platform tutorial, setup a trigger on the platform, parented it to that platform. Added a script to parent anything that entered it, and unparent anything that exited.
Afterwards i commented out the area of the character controller that handled platforms. Worked fine what what i tried, cept lerpz would do a rocket thrust animation thing while that platform was moving up.
Everything seemed fine and didn’t require any rewriting of the character controller script, plus other objects like the crates would fall on the platform and work as expected.
Because the objects are parented, rotations will work, so you could have spinning platforms if you wanted such a thing.
I tried making a script and I pasted Vimalakirti’s code in. When I tried attaching it to the controller I got an error message that said “NewScript has not finished compilation yet.” When I tried running the game without it attached it bugged out.