Icy/Slippery floor

I am making a game for a class and I am stuck on trying to create and icy/slippery floor.

My character is being controlled via a character controller script.

I am trying to create a slippery floor that acts in either of 2 fashions(which one I go with depends on my teamates and the complexity on implementation).

First style would be similar to Mario Bros. 3 on NES in that the character is harder to control. Starting and Stopping movement would be harder.

Second style would be that you lose control of the character once on this icy terrain. The path of motion would be a straight line from whatever direction you entered the icy terrain in. You would continue this path until the end of the icy terrain.

I really just dont know where to begin to try and implement this. I thought about the second method, and just applying force through a script. I would do this by 2 planes(an OnTriggerEnter and OnTriggerExit plane), but my character does not have a rigidbody attached to it, so I dont think that would work. For the first method, I tried changing the physics type of the floor to “Icy” but that didnt seem to do anything. Honestly, I just dont know where to start on this. Any suggestions?

The CharacterController ignores physics, thus modifying the physic material makes no difference.

To make this “slippery ice” effect, you must modify the character movement script. The easiest way is to store the velocity vector passed to Move in a member variable (variable defined outside any function) and make it follow the controls via Lerp: the speed at which the velocity follows the controls is proportional to the friction. This works fine in flat surfaces, but will not make the character slip at inclined surfaces.

The actual code depends on your current movement script. If it’s your own script, maybe the code below can replace it - or at least be adapted to suit your needs (character script - includes detection of “Ice” triggers):

var speed: float = 6.0;
var jumpSpeed: float = 8.0;
var friction: float = 1.0; // 0 means no friction;
private var curVel = Vector3.zero;
private var velY: float = 0;
private var character: CharacterController;

function Update(){
  // get the CharacterController only the first time:
  if (!character) character = GetComponent(CharacterController);
  // get the direction from the controls:
  var dir = Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
  // calculate the desired velocity:
  var vel = transform.TransformDirection(dir) * speed;

  // here's where the magic happens:
  curVel = Vector3.Lerp(curVel, vel, 5 * friction * friction * Time.deltaTime);

  // apply gravity and jump after the friction!
  if (character.isGrounded){
    velY = 0;
    if (Input.GetKeyDown("Jump")){
      velY = jumpSpeed;
    }
    velY -= gravity * Time.deltaTime;
  }
  curVel.y = velY;
  character.Move(curVel * Time.deltaTime);
}

function OnTriggerEnter(other: Collider){
  if (other.name == "Ice") friction = 0.1; // set low friction 
}

function OnTriggerExit(other: Collider){
  if (other.name == "Ice") friction = 1; // restore regular friction
}

To define a slippery region, create a trigger covering the desired area and name it “Ice”. You may have as many “Ice” objects you want.

NOTE: If you’re using the standard First Person Controller, you must replace the input control script with the script below: save it in some Assets subfolder, add it to the First Person Controller and remove the original FPSInputController script:

var friction: float = 1.0;

private var motor : CharacterMotor;
var curVel = Vector3.zero;

// Use this for initialization
function Awake () {
  motor = GetComponent(CharacterMotor);
}

// Update is called once per frame
function Update () {
  // Get the input vector from keyboard or analog stick
  var dir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
  var vel = transform.TransformDirection(dir);
  // Apply the friction factor:
  curVel = Vector3.Lerp(curVel, vel, 5*friction*friction*Time.deltaTime);
  motor.inputMoveDirection = curVel;
  motor.inputJump = Input.GetButton("Jump");
}

function OnTriggerEnter(other: Collider){
  if (other.name == "Ice") friction = 0.1; // set low friction 
}

function OnTriggerExit(other: Collider){
  if (other.name == "Ice") friction = 1; // restore regular friction
}

I believe that there is a friction value that can be changed in the character collider. You could place a trigger over the area you want to be icy, so when the player enters that trigger his/her friction value is reduced. You could also reduce the friction of the ground as well.

Hope this points you in the right direction!

Thank you Aldo!

I had the revelation last night about physics not effecting my character. I realized there is no collider or physics attached to my character model other than gravity. Therefore it never really interacts with the floor underneath it, therefore changing the physics of the floor would do nothing. I was unsure if sliding/slippery movement could be handled via the movement of the character(I was thinking there might be a way to delay movement input by half a second to make it less responsive to simulate being harder to move in the direction you desired). That movement script you posted is very similar to the one I have so it should be easy to adopt it to mine. I believe this will function the way I want it to but if not I am sure I will be back here with another question.

Again, thank you Aldo and others for your input.

just implemented my own icy surface solution without Lerping and thought to share the idea:

private Vector3 moveDirection = Vector3.zero;
private Vector3 lastMoveDirection = Vector3.zero;
private float moveSpeed = 2f;
private float slideSpeed = 1.75f;
private bool icy = false;

public void Update() {

	// read inputs
	float h = Input.GetAxis("Horizontal");
	float v = Input.GetAxis("Vertical");
	Vector3 moveDirection = v * camera.forward + h * camera.right;

	float inputMagnitude = Mathf.Min(new Vector3(h, 0, v).sqrMagnitude, 1f);
	
	// store last direction when received some movement
	if(inputMagnitude > 0.225f) { 
		lastMoveDirection = moveDirection;
	}
	
	// add speed
	// keeps sliding when still, runs slowly when moving
	if(icy) {
		moveDirection = lastMoveDirection * slideSpeed;
	} 
	else {
		moveDirection *= moveSpeed;
	}
	
	// ...
	// apply
	controller.Move (moveDirection * Time.deltaTime)
}

void OnControllerColliderHit(ControllerColliderHit hit) {
	icy = hit.collider.CompareTag("Ice");
}

the sliding is now constant, I would improve this by adding speed deceleration