Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Side Stepping in a first person game

Discussion in 'Scripting' started by DoctorMoney, Jul 21, 2014.

  1. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    First time posting in the forums, I asked this question in Unity Answers but it didn't help much so wanted to see what you all thought.

    I don't know if anyone knows what I mean when I'm looking for a dodge side step thing in a first person game because I can't think of any examples. Basically when the player double taps a direction, they jump to the left or right to avoid a collision.

    Code (CSharp):
    1. private var one_press : boolean;
    2. private var timer_running : boolean;
    3. private var delay : float;
    4. private var two_press : boolean;
    5. private var two_press_timer : float;
    6.  
    7. function Update() {
    8.  
    9. if(Input.GetKeyDown(KeyCode.A)) {
    10.     if(!one_press)
    11.         {
    12.             one_press = true;
    13.             delay = Time.time + .05;
    14.         }
    15.     else
    16.     {
    17.         one_press = false;
    18.         two_press_timer = 0.1;
    19.         two_press = true;
    20.     }
    21. }
    22. if(one_press)
    23. {
    24.        if((Time.time - .05) > delay)
    25.         {
    26.             one_press = false;
    27.         }
    28. }
    29. if (two_press){
    30.     two_press_timer = two_press_timer - Time.deltaTime;
    31.     transform.Translate(Vector3.left * 20 * Time.deltaTime);
    32.     if (two_press_timer <= 0){
    33.         two_press = false;
    34.     }
    35.  
    36. }
    My code works but the the FPC can move through walls when doing this. Anyone have any ideas on what's a good way to fix this or have a better idea on how to execute this?
     
  2. rrh

    rrh

    Joined:
    Jul 12, 2012
    Posts:
    331
    How do you move normally that doesn't go through walls?
     
  3. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    I use the regular FPC
     
  4. Mirace

    Mirace

    Joined:
    Nov 29, 2010
    Posts:
    481
    Use http://docs.unity3d.com/ScriptReference/Physics.Raycast.html BEFORE you move your character IE.
    Pseudo:


    Code (JavaScript):
    1. if input.getkey (A )
    2.  
    3.      if(CheckColl == true)
    4.       call Dodge(5,vector3.left)
    5.      else
    6.       // donothing or
    7.        call Dodge(length to wall ,vector3.left)
    8.  
    9. and here is check coll function..
    10. function CheckColl()
    11. {
    12. do
    13. Physics.Raycast
    14. if raycast didnt hit wall
    15. return true
    16. }
    17.  
    18.  
    19. Dodge function can be something like
    20. function Dodge(var length : float, var dir : vector3)
    21. {
    22. // transform.translate to dir and use length
    23. }
    Basicly target ray to that direction where you are going to step, if raycast hits wall you can
    1. call dodge and calculate move length by looking how long raycast moved before hitting wall
    2. Just ignore dodge key if wall is too near ( look code )
    Hope this helps
     
    Last edited: Jul 21, 2014
  5. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15

    I was working on making your suggestion work and didn't see that you edited. This is what I came up with

    Code (JavaScript):
    1. if(Input.GetKeyDown(KeyCode.A)) {
    2.     if(!one_press)
    3.         {
    4.             one_press = true;
    5.             delay = Time.time + .05;
    6.         }
    7.     else
    8.     {
    9.         dodge_direction = Vector3.left;
    10.         one_press = false;
    11.         two_press_timer = 0.1;
    12.         CheckCollision();
    13.     }
    14. }
    15. if (two_press){
    16.     two_press_timer = two_press_timer - Time.deltaTime;
    17.     transform.Translate(dodge_direction * 20 * Time.deltaTime);
    18.     if (two_press_timer <= 0){
    19.         two_press = false;
    20.     }
    21.  
    22. function CheckCollision () {  
    23.     var hit : RaycastHit;
    24.  
    25.     if (Physics.Raycast (transform.position, dodge_direction, hit, Mathf.Infinity, layermask)) {
    26.         if (hit.distance >= 4){
    27.         print (hit.transform.name);
    28.             two_press = true;
    29.         }
    30.  
    31.     }
    32.  
    33. }
    It works still but I couldn't figure out how to make it check to see if there was a wall there or not so I tried to go off distance to see if there was something there. It didn't work. And I measured how much my player traveled when side stepping and it turned out to be 2.67298 units (but checking distance wasn't working). Is there a way to see if the raycast hit anything in a boolean statement? Nothing I was doing was working
     
  6. Mirace

    Mirace

    Joined:
    Nov 29, 2010
    Posts:
    481
    Hi,
    print (hit.transform.name); , you can also use .tag. Create new tag ( Wall ) and change your walls tag as "Wall"
    now on code use this on CheckCollision function

    Code (JavaScript):
    1.  
    2. function CheckCollision () : boolean {
    3.     var hit : RaycastHit;
    4.     if (Physics.Raycast (transform.position, dodge_direction, hit, Mathf.Infinity, layermask)) {
    5.         if (hit.transform.tag == "Wall"){
    6.         print ("Hitted wall object!");
    7.             two_press = true;
    8.         return true;
    9.         }
    10.         else
    11.         {
    12.         return false;
    13.         }
    14.     }
    15.     }
    16.    
    Remove that CheckCollision call from line 12 and change line 15 to look this
    Code (JavaScript):
    1. 15. if (CheckCollision  == true){
    2. two_press_timer = two_press_timer - Time.deltaTime;
    3.    transform.Translate(dodge_direction * 20 * Time.deltaTime);
    4. if (two_press_timer <= 0){
    5.   two_press = false;
    6. }
     
  7. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    Didn't work :/

    You had it set up so if the raycast did hit a wall then it would side step so I changed that.
    Then I realized the distance was set to infinity which is fine but it would always detect being near a wall so I set it if the distance was >=3 then side step but it's not very good. I'd like my character to be able to side step despite being close to a wall otherwise the game loses it's fluidity and the controls seem stiff because it only works some of the time and my game is very close quarters.

    Is there a way to just set it so that if my player does hit a wall it just stops the side stepping process and moves him in the opposite direction slightly like (-Vector3.left) or (Vector3.right)?

    I feel like my player is able to move through the walls because he is being constantly pushed in a direction, if I could somehow stop the movement and return to normal controls then it would work perfectly.
    Thanks :D
     
  8. Mirace

    Mirace

    Joined:
    Nov 29, 2010
    Posts:
    481
    Yeah there could be huge amount typos, i didnt debug anything ( write from my mind ) . If you like to stop movement you should just force player position to be same on every frame call . ie : transform.position = transform.position. or transform.position.x = transform.position.x keeps X axle intact..

    That basicly means
    if DISTANCE is higher or equal to 3 ->
    This line happens
    END


    See new variable, MinDistanceToWall. Avoid using IF statement if you can, in this case you dont need to raycast infinite ( it takes also resources:.. ) But raycasting only to specific distance..
    There you go, new function.. You should figure out what it does and implent it to your code, i dont think it works if you copy paste :)

    Code (JavaScript):
    1. var MinDistanceToWall : float = 3;
    2. function CheckCollision () : boolean {
    3.     // Raycast hit store
    4.     var hit : RaycastHit;
    5.  
    6.     // Do raycast, between player position and wanted direction, DO it only distance between startpoint and MinDistanceToWall
    7.     if (Physics.Raycast (transform.position, dodge_direction, hit, MinDistanceToWall))
    8.     {
    9.         // Raycast hitted object !
    10.         // Lets check that objects transform TAG
    11.      
    12.         if (hit.transform.tag == "Wall"){
    13.             // Object has wall tag
    14.             print ("Wall is near us, return true");
    15.          
    16.             // now you know that wall IS near
    17.             two_press = true;
    18.          
    19.             // return true , that basicly means: CheckCollision == true
    20.             return true;
    21.         }
    22.         else
    23.         {
    24.             // no wall in this direction within MinDistanceToWall, we return false
    25.             return false;
    26.         }
    27.     }
    28. }
    Without looking your code.. i would do this :

    lets think that default vector is Vector3.left
    Code (JavaScript):
    1. var dodge_direction : Vector3;
    2.  
    3. function start()
    4. {
    5. dodge_direction = Vector3.left;
    6. }
    7.  
    8. function Update()
    9. {
    10. If ( CheckCollision == true )
    11. {
    12. // stop player movement
    13. transform.position = transform.position;
    14. // change default vector aka dodge_direction
    15. dodge_direction = -Vector3.left;
    16. }
    17.  
    18. }
    19.  
     
    Last edited: Jul 21, 2014
  9. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    The first block of code is basically what I already have but with a different distance instead of infinity. I've already tried that and if it doesn't detect anything, it does nothing and my player still won't side step.

    The second block of code doesn't work either, the player still goes through walls regardless of that code being implemented.

    I think this method of side stepping might be a dead end. It doesn't quite meet the feel of what I want which is basically Unreal Tournament 2004's side stepping and has proven to be a hassle lol

    Thanks though, appreciate it :)
     
  10. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Moving the transform is generally a bad idea. You should either use a CharacterController with the Move function, or a Rigidbody and use AddForce.
     
  11. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    I've never used CharacterController before, with the scripts above, would it stop moving when it comes in contact with the wall you think? Rigidbody would be my first choice if i wasn't using FPC, but since FPC and rigidbody don't go together I'm kind of screwed lol. Would have just used rigidbody.AddForce and never have had a problem.

    But what is the best way to go about doing this with CharacterController?
     
  12. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Hmm you're using the First Person Controller which comes with Unity? That actually already uses a character controller. You will want to modify the code of the FPSInputController and CharacterMotor. Those two scripts are what control movement.
     
  13. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    I have a separate player movement script that my friend gave to me that has sprint and crouching in it. I don't know if he made it or not but it makes modifying the CharacterMotor very easy. What function should I use for CharacterController or motor to get the effect i want? I know nothing about it lol
     
  14. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    I'm not familiar with the CharacterMotor script, but instead of using transform.Translate, you could try:

    Code (CSharp):
    1. CharacterController controller;
    2.  
    3. void Awake()
    4. {
    5.         controller = GetComponent<CharacterController>();
    6. }
    7.  
    8. void Update()
    9. {
    10.         controller.Move(...);
    11. }
    Put your translation vector inside the Move function. The Move function does check for collision, so it won't just zoom through the wall. The code isn't really meant to be copy/pasted in, just to give you the idea of how to incorporate it into your own code.

    But again, I am totally unfamiliar with CharacterMotor, so there's a good chance this won't work. It will work with just a CharacterController, however.
     
  15. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    My script already had a character controller variable so I just replaced the transform.Translate with that and it almost works perfect. The player no longer goes through walls but now the side step is taking place on the global axis or whatever. Like regardless of the direction I'm facing, it will side step along the x-axis instead of the direction relative to the direction the player is facing. Any idea on how I could do that? Simple move doesn't work either
     
  16. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    If your movement vector is called moveVector, do this:

    moveVector = transform.TransformDirection(moveVector);
     
  17. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    I didn't understand what you meant at first but I got it. It's so close to working, right now it works sometimes but most of the time it either stutters and the player doesn't move or the player moves in a curve or circle instead of a straight line to the left. Here's what I have:

    Code (JavaScript):
    1. private var one_press : boolean;
    2. private var timer_running : boolean;
    3. private var delay : float;
    4. private var two_press : boolean;
    5. private var two_press_timer : float;
    6. private var dodge_direction : Vector3;
    7.  
    8. function Update(){
    9. if(Input.GetKeyDown(KeyCode.A)) {
    10.     if(!one_press)
    11.         {
    12.             one_press = true;
    13.             delay = Time.time + .05;
    14.         }
    15.     else
    16.     {
    17.         dodge_direction = Vector3.left;
    18.         one_press = false;
    19.         two_press_timer = 0.1;
    20.         two_press = true;
    21.     }
    22. }
    23. if(one_press)
    24. {
    25.        if((Time.time - .05) > delay)
    26.         {
    27.             one_press = false;
    28.         }
    29. }
    30.  
    31. if (two_press){
    32.     two_press_timer = two_press_timer - Time.deltaTime;
    33.     dodge_direction = transform.TransformDirection(dodge_direction);
    34.     print(dodge_direction); //Debug S***
    35.     ch.Move(dodge_direction * 20 * Time.deltaTime);
    36.     if (two_press_timer <= 0){
    37.         two_press = false;
    38.     }
    39.    
    40.    }
    41. }
    When it does work, I'm facing the direction that would be forward globally (which my rotation would be (0, 0, 0)) which you can see in the picture I uploaded. I can't figure out why it's only working correctly on that rotation.
     

    Attached Files:

  18. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    sorry for double post, I'm not sure if I don't reply to your post that you get notified :/
    Thanks though
     
  19. DoctorMoney

    DoctorMoney

    Joined:
    Mar 7, 2013
    Posts:
    15
    Figured it out, posting the code in case anyone comes along looking for the same thing

    Code (JavaScript):
    1. private var one_press : boolean;
    2. private var timer_running : boolean;
    3. private var delay : float;
    4. private var two_press : boolean;
    5. private var two_press_timer : float;
    6. private var dodge_direction : Vector3;
    7.  
    8. function Update(){
    9. if(Input.GetKeyDown(KeyCode.A)) {
    10.     if(!one_press)
    11.         {
    12.             one_press = true;
    13.             delay = Time.time + .05;
    14.         }
    15.     else
    16.     {
    17.         dodge_direction = Vector3.left;
    18.         one_press = false;
    19.         two_press_timer = 0.1;
    20.         two_press = true;
    21.     }
    22. }
    23. if(one_press)
    24. {
    25.        if((Time.time - .05) > delay)
    26.         {
    27.             one_press = false;
    28.         }
    29. }
    30.  
    31. if (two_press){
    32.     two_press_timer = two_press_timer - Time.deltaTime;
    33.     dodge_direction = transform.TransformDirection(Vector3.left)*20;
    34.     ch.Move(dodge_direction * Time.deltaTime);
    35.     if (two_press_timer <= 0){
    36.         two_press = false;
    37.     }
    38.    
    39. }
    Just make separate if statements for the other directions you want it in and it'll work. Thanks everyone!
     
    MikawasakiDigital likes this.