Character Falling Through World

Hello all,

As part of a script I’m writing, I require that the world (which consist of several terrains side by side) be reset once the player moves a certain distance from an origin point (x = 0, z = 0 most likely). Please don’t worry why I’m doing this, it’s not important to the problem at hand :smile:!

Basically a reset consist of moving the world back to some starting position, and also moving the player some amount so they don’t notice the move. Everything works fine for the most part, but the problem is sometimes the player goes through the terrain when a reset occurs, which is obviously no good!

Everything is reset/moved in the same frame, so I really don’t understand how the player can go through the terrain, but that is probably an issue for another place/time.

I am using a character controller and basic FPSWalker script (From the island demo I believe).

@script RequireComponent(CharacterController)
class FPSWalker extends MoveController
{
	var speed = 6.0;
	var jumpSpeed = 8.0;
	var gravity = 20.0;
	
	private var moveDirection = Vector3.zero;
	private var grounded : boolean = false;
	private var lastVelocity : Vector3;
	
	function FixedUpdate() {
		if (grounded) {
			// We are grounded, so recalculate movedirection directly from axes
			moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
			moveDirection = transform.TransformDirection(moveDirection);
			moveDirection *= speed;
			
			if (Input.GetButton ("Jump")) {
				moveDirection.y = jumpSpeed;
			}
		}
	
		// Apply gravity
		moveDirection.y -= gravity * Time.smoothDeltaTime;
			
		// Move the controller
		var controller : CharacterController = GetComponent(CharacterController);
		var flags = controller.Move(moveDirection * Time.deltaTime);
		grounded = (flags  CollisionFlags.CollidedBelow) != 0;
	}
	
	function FreezeMovement()
	{
		lastVelocity = moveDirection;
		GetComponent(CharacterController).Move(Vector3.zero);

	}
	
	function UnfreezeMovement()
	{
		moveDirection = lastVelocity;
		var flags = GetComponent(CharacterController).Move(moveDirection * Time.deltaTime);
		grounded = (flags  CollisionFlags.CollidedBelow) != 0;
	}
}

Basically from my other script which has a reference of this FPSWalker script, I am calling FreezeMovement, then moving the terrains and player (via transform.position), then calling UnfreezeMovement. I am unsure if this is even freezing the character’s movement as I desire, but I do know it does not solve the problem of the player going through the terrain.

So basically what I’m looking for is a way to freeze the player mid frame, do something, then have them resume at their pre-freeze velocity in the same frame. Anyone have any ideas?

Unfortunately, I can’t just “use a rigidbody” or something else, as this script is actually being designed as a tool other people will use (the resetting functionality is just one optional part to the script), so there’s no way for me to know how they will control their character. I actually still need to test rigidbodies to see if they present a similar problem as the character controller, but for now I just want to focus on the character controller.

Thanks in advance for any help!

It seems this only occurs when the character is moving at high speeds, so I’ll probably not worry about it and just include a disclaimer about the issue with my tool.

Still, I would be interested in a solution if anyone has any thoughts.

I am back to trying to solve this same problem. I’m pretty sure I know what the cause is, but I am still no closer to a solution.

What I think is happening is when I move the terrain and player, the player collider is just barely going through the terrain, and so when the player starts moving again, it falls through the world. Basically I need a way to move the player from one point to another, but still have it detect a collision and do whatever it does to keep the player from going through the terrain. using Move (since currently I’m utilizing a character controller), won’t work (I think), and I am drawing a blank to another method.

Please, any help would be greatly appreciated!

Not sure if this is your problem, but sometimes collisions don’t get detected at high speeds because of the collision settings. If you have a rigidbody on your gameobject you can change the collision option from discreet to continuous and it should solve the problem, the other way to solve it if you don’t have a rigidbody is to use raycasts to detect the floor and adjust the players position with the results of the raycast.

How about casting a ray after a move and readjusting the player height?

Thanks for the help. Unfortunately, I don’t believe this is the problem I’m facing. As far as I can tell the issue is no collision detection is done when you move a gameObject by adjusting its transform.position.

I’m going to pursue this approach, but in the mean time, anyone else have any suggestions?

Thanks for the help so far!

A thing you may need to watch out for, in character controllers. If they are even a slight bit through the ground, you will fall through. So make sure when your terrain resets and what not, that your character is high enough above the ground. Its really sensitive, even being a centimetre into the ground is enough to fall through.

I think I’ve got a handle on this. So far I have yet to fall through the world in my testing, so fingers crossed.

In my player movement script, the player is moved via the CharacterController.Move function. Previously, I would reset the player’s location and do nothing else, so the next Move call after resetting would happen normally. This was causing the issue for the Physics engine, as it seems when moving a collider via it’s transform, you need to allow one FixedUpdate() to occur where the collider is not moved again. The solution, then, is to just “freeze” the player for one fixed update only.

Here’s some code to show what I mean, in C#:

Previous Method:

void ResetPlayerWithoutFreezing()
{
	//Reset 50 meters in the x dimension
	Vector3 resetAmount = new Vector3(50f, 0f, 0f);
	player.position -= resetAmount;
}

New Method:

IEnumerator ResetPlayerWithFreezing()
{
	//Reset 50 meters in the x dimension
	Vector3 resetAmount = new Vector3(50f, 0f, 0f);
	player.position -= resetAmount;
		
	//Since this coroutine executes before fixed update, this next line should
	//cause the movement script to skip moving the player the next time it's called
	movementScript.FreezePlayer();
		
	//The coroutine continues only after all FixedUpdate methods have been called during a FixedUpdate Frame, 
	//so the player movement will not be unfrozen the same frame it was frozen
	yield return new WaitForFixedUpdate();
		
	movementScript.UnFreezePlayer();
}

Hopefully this will help someone, and if others want to run some test on this to make sure it works (obviously you’ll have to implement the FreezePlayer and UnFreezePlayer methods however you need to), that would be awesome.

One thing that may be obvious, but I haven’t seen it mentioned.
How about fully stopping the player before doing the reset?

Yeah, that’s what the solution I came up with effectively does, since it skips one FixedUpdate (which is reponsible for moving the player via CharacterController.Move() ).

Now, this takes care of issues with movement via the CharacterController class, but I’m still unsure if I’ll face problems when using a rigid body.

In all my test, I have been unable to get the rigid body to fall through the world during a reset, so I’m hoping the issue is only with the character controller class. Has anyone experienced a rigid body falling through the world as a result of manipulating its transform.position or rigidbody.position? I am not talking about going through colliders because of high speeds, that is an entirely different issue.

Thanks Ingot and everyone else for the help.