Search Unity

ForceMode.Impulse makes character 'teleport' rather than feeling like a big force?

Discussion in 'Scripting' started by ynm11, Apr 19, 2022.

  1. ynm11

    ynm11

    Joined:
    Jul 6, 2021
    Posts:
    57
    I want to make a fun "Dash" mechanic for my character that would feel like character gets a sudden big force pushing them forward, like a sudden boost of power forward. I assume ForceMode.Impulse is the correct type for this.

    However, when I put this on my player, it creates a "Teleportation" effect rather than a fast dash effect. It basically blinks in a millisecond from one location to another.

    I tried reducing the Force value but this makes it so that the Player doesn't dash forward *far* enough. So then this causes a very very short-distance dash which is also very unsatisfying.

    How would you do a medium-length dash forward that feels like an 'impulse', without teleporting?

    This is my simple Dash Script code on a Rigidbody with default values...

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class DashScript : MonoBehaviour
    6. {
    7.     CharacterControls controller;
    8.  
    9.     //Dashing
    10.     public bool isDashing;
    11.     public float dashForce;
    12.     public Rigidbody r;
    13.     private Vector3 moveDirectionDash;
    14.  
    15.     private void Awake()
    16.     {
    17.         controller = this.GetComponent<CharacterControls>();
    18.         moveDirectionDash = new Vector3(0.0f, 0.0f, 0.0f);
    19.     }
    20.  
    21.     // Update is called once per frame
    22.     void Update()
    23.     {
    24.         moveDirectionDash = controller.moveDirection;
    25.  
    26.         if (Input.GetKeyDown(KeyCode.LeftControl))
    27.         {
    28.             isDashing = true;
    29.             this.Delay(0.2f, DashOff);
    30.             print("Dashed");
    31.         }
    32.     }
    33.  
    34.     private void FixedUpdate()
    35.     {
    36.         if (isDashing == true)
    37.         {
    38.             Dash();
    39.         }
    40.     }
    41.  
    42.     void Dash()
    43.     {    
    44.         r.AddForce(moveDirectionDash * dashForce * Time.deltaTime, ForceMode.Impulse);    
    45.         isDashing = false;
    46.     }
    47.  
    48. }
    49.  

    Also I can include my CharacterController code in case it is important. It is a script largely copied from a tutorial that creates a floating capsule collider who can jump and handle slopes:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CharacterControls : MonoBehaviour
    6. {
    7.  
    8.     public float floorOffsetY;
    9.     public float moveSpeed = 6f;
    10.     public float rotateSpeed = 10f;
    11.     public float slopeLimit = 45f;
    12.     public float slopeInfluence = 5f;
    13.     public float jumpPower = 10f;
    14.  
    15.     public Rigidbody rb;
    16.     Animator anim;
    17.     float vertical;
    18.     float horizontal;
    19.     public Vector3 moveDirection;
    20.     float inputAmount;
    21.     Vector3 raycastFloorPos;
    22.     Vector3 floorMovement;
    23.     Vector3 gravity;
    24.     Vector3 CombinedRaycast;
    25.  
    26.     float jumpFalloff = 2f;
    27.     bool jump_input_down;
    28.     float slopeAmount;
    29.     Vector3 floorNormal;
    30.  
    31.     // Use this for initialization
    32.     void Start()
    33.     {
    34.         rb = GetComponent<Rigidbody>();
    35.         anim = GetComponentInChildren<Animator>();
    36.     }
    37.  
    38.     private void Update()
    39.     {
    40.         // reset movement
    41.         moveDirection = Vector3.zero;
    42.         // get vertical and horizontal movement input (controller and WASD/ Arrow Keys)
    43.         vertical = Input.GetAxis("Vertical");
    44.         horizontal = Input.GetAxis("Horizontal");
    45.  
    46.         jump_input_down = Input.GetKeyDown(KeyCode.Space);
    47.  
    48.         // base movement on camera
    49.         Vector3 correctedVertical = vertical * Camera.main.transform.forward;
    50.         Vector3 correctedHorizontal = horizontal * Camera.main.transform.right;
    51.  
    52.         Vector3 combinedInput = correctedHorizontal + correctedVertical;
    53.         // normalize so diagonal movement isnt twice as fast, clear the Y so your character doesnt try to
    54.         // walk into the floor/ sky when your camera isn't level
    55.  
    56.         moveDirection = new Vector3((combinedInput).normalized.x, 0, (combinedInput).normalized.z);
    57.  
    58.         // make sure the input doesnt go negative or above 1;
    59.         float inputMagnitude = Mathf.Abs(horizontal) + Mathf.Abs(vertical);
    60.         inputAmount = Mathf.Clamp01(inputMagnitude);
    61.  
    62.         // rotate player to movement direction
    63.         Quaternion rot = Quaternion.LookRotation(moveDirection);
    64.         Quaternion targetRotation = Quaternion.Slerp(transform.rotation, rot, Time.fixedDeltaTime * inputAmount * rotateSpeed);
    65.         transform.rotation = targetRotation;
    66.  
    67.         if (jump_input_down)
    68.         {
    69.             Jump();
    70.         }
    71.  
    72.         // handle animation blendtree for walking
    73.         anim.SetFloat("Velocity", inputAmount, 0.2f, Time.deltaTime);
    74.         anim.SetFloat("SlopeNormal", slopeAmount, 0.2f, Time.deltaTime);
    75.     }
    76.  
    77.  
    78.     private void FixedUpdate()
    79.     {
    80.         // if not grounded , increase down force
    81.         if (!IsGrounded() || slopeAmount >= 0.1f)// if going down, also apply, to stop bouncing
    82.         {
    83.             gravity += Vector3.up * Physics.gravity.y * jumpFalloff * Time.fixedDeltaTime;
    84.         }
    85.  
    86.         // actual movement of the rigidbody + extra down force
    87.         rb.velocity = (moveDirection * GetMoveSpeed() * inputAmount) + gravity;
    88.  
    89.         // find the Y position via raycasts
    90.         floorMovement = new Vector3(rb.position.x, FindFloor().y + floorOffsetY, rb.position.z);
    91.  
    92.         // only stick to floor when grounded
    93.         if (floorMovement != rb.position && IsGrounded() && rb.velocity.y <= 0)
    94.         {
    95.             // move the rigidbody to the floor
    96.             rb.MovePosition(floorMovement);
    97.             gravity.y = 0;
    98.         }
    99.     }
    100.  
    101.     Vector3 FindFloor()
    102.     {
    103.         // width of raycasts around the centre of your character
    104.         float raycastWidth = 0.25f;
    105.         // check floor on 5 raycasts   , get the average when not Vector3.zero
    106.         int floorAverage = 1;
    107.  
    108.         CombinedRaycast = FloorRaycasts(0, 0, 1.6f);
    109.         floorAverage += (getFloorAverage(raycastWidth, 0) + getFloorAverage(-raycastWidth, 0) + getFloorAverage(0, raycastWidth) + getFloorAverage(0, -raycastWidth));
    110.  
    111.         return CombinedRaycast / floorAverage;
    112.     }
    113.  
    114.     // only add to average floor position if its not Vector3.zero
    115.     int getFloorAverage(float offsetx, float offsetz)
    116.     {
    117.  
    118.         if (FloorRaycasts(offsetx, offsetz, 1.6f) != Vector3.zero)
    119.         {
    120.             CombinedRaycast += FloorRaycasts(offsetx, offsetz, 1.6f);
    121.             return 1;
    122.         }
    123.         else { return 0; }
    124.     }
    125.  
    126.     bool IsGrounded()
    127.     {
    128.         if (FloorRaycasts(0, 0, 0.6f) != Vector3.zero)
    129.         {
    130.             slopeAmount = Vector3.Dot(transform.forward, floorNormal);
    131.             return true;
    132.         }
    133.         else
    134.         {
    135.             return false;
    136.         }
    137.     }
    138.  
    139.  
    140.     Vector3 FloorRaycasts(float offsetx, float offsetz, float raycastLength)
    141.     {
    142.         RaycastHit hit;
    143.         // move raycast
    144.         raycastFloorPos = transform.TransformPoint(0 + offsetx, 0 + 0.5f, 0 + offsetz);
    145.  
    146.         Debug.DrawRay(raycastFloorPos, Vector3.down, Color.magenta);
    147.         if (Physics.Raycast(raycastFloorPos, -Vector3.up, out hit, raycastLength))
    148.         {
    149.             floorNormal = hit.normal;
    150.  
    151.             if (Vector3.Angle(floorNormal, Vector3.up) < slopeLimit)
    152.             {
    153.                 return hit.point;
    154.             }
    155.             else return Vector3.zero;
    156.         }
    157.         else return Vector3.zero;
    158.     }
    159.  
    160.     float GetMoveSpeed()
    161.     {
    162.         // you can add a run here, if run button : currentMovespeed = runSpeed;
    163.         float currentMovespeed = Mathf.Clamp(moveSpeed + (slopeAmount * slopeInfluence), 0, moveSpeed + 1);
    164.         return currentMovespeed;
    165.     }
    166.  
    167.     void Jump()
    168.     {
    169.         if (IsGrounded())
    170.         {
    171.             gravity.y = jumpPower;
    172.             anim.SetTrigger("Jumping");
    173.         }
    174.     }
    175. }

    Thank you for any help.
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    Your dashing doesn't work because you have this code which is incompatible with realistic responses of your object to forces:
    rb.velocity = (moveDirection * GetMoveSpeed() * inputAmount) + gravity;


    You are setting the velocity each simulation frame, instantly to a specific move speed. Therefore any forces you apply are only going to have a visible effect on your velocity for a single frame before you forcibly set the velocity back again to your preset move speed with that code. The effect of this is that any force you apply appears to be a single-frame teleportation, since it is only being allowed to affect velocity for a single frame.

    If you want to be able to convincingly apply forces to your character, you'll have to change that code.

    Now as for how to do a "dash" mechanic using forces?

    A "Dash" is not something that can be captured by a single force being applied at a single time. Forces cause acceleration. An acceleration is a change in speed. To do a dash with actual forces you'd need something like this:
    upload_2022-4-18_23-7-9.png
    The green line indicates velocity. The blue line indicates a sudden force to accelerate up to "dashing speed", followed by some amount of time the character moves at dashing speed, followed by the red force to decelerate back down to normal speed.

    So at the very least you would need two forces: one to accelerate to dash speed, and another to decelerate back to normal speed.

    But again, none of this matters as long as you have code that sets the velocity directly each FixedUpdate.
     
    ynm11 and Bunny83 like this.