Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

First-Person Grid Based Movement

Discussion in 'iOS and tvOS' started by SteveJ, Apr 20, 2010.

  1. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    I'm struggling with something and was hoping someone might have a few pointers for me.

    I'm try to create first-person movement in a style similar to the old-school RPGs like Eye Of The Beholder.

    I basically want to place four movement buttons on screen over my 3D environment. When the user presses the forward button, the player object should move forward x points. When the user presses the left button, the player should rotate 90 degrees left, etc, etc.

    Does anyone have any examples of something similar or some general tips on how to get this done? I've got my environment all setup with the player, camera, etc.
     
  2. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,043
    Hi mate, could you be a little bit more specific about which bit you are struggling with?

    In really general terms I would say your player object could have a target position/rotation. During the update if the current transform doesn't match this you move the character towards the target position/rotation (for simplicity sake you could apply rotations first).

    Your forward button would add some fixed amount to the target position (using something like TransformDirection() to ensure you move in the right direction).
     
  3. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    Mostly having problems with getting the player to move a set distance with each button press - rather than move continuously when the buttons are held.

    I'm playing around with Unity on Windows at the moment (cause I'm at work) just to try and get this control part worked out.

    I've attached the "First Person Controller" prefab from the standard assets and removed the MouseLook script from it so you can't look around.

    I'm now trying to figure out how to change the FPSWalker script. By default, it moves forward (for example) continuously when W is held down. I need to adapt it so that if you press W (or hold it), the player will move in the direction that the camera is facing by a set amount and then stop (even if the W is still held). It's mostly the "animation" part that's got me stumped. I'm not sure how I go about telling script to "move forward 1 'step' per frame(?) for the next 10 frames".

    I figure if I can't get it working with keys, then swapping it over to use on screen buttons on the iPhone should be the least of my worries.
     
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    You could adapt this, which has grid-based movement. Basically, don't use Update at all, just lerp from point A to point B in a coroutine.

    --Eric
     
  5. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    Thank you!!!!!!!!!!!

    That's EXACTLY what I was looking for!

    I'll get to work :D
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    I just realized that this basically has Eye of the Beholder movement. The Start function in the player object has this routine:

    Code (csharp):
    1.     var move : int;
    2.     while (true) {
    3.         move = CheckAxis();
    4.         if (move != 0) {
    5.             yield Walk(move);
    6.         }
    7.        
    8.         if (directionToTurn != 0) {
    9.             yield Turn(directionToTurn, turnSpeed);
    10.         }
    11.        
    12.         yield;
    13.     }
    "directionToTurn" is checked in Update since players may want to turn at any time, so a turn is initiated when the move is done (or immediately if there's no move in progress). "move" is 1 for forward and -1 for backward; "directionToTurn" is likewise -1 for left and 1 for right.

    --Eric
     
  7. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    Hmmm... confused now :(

    That's one of your apps? I'll buy it tonight (when I'm back on WIFI) and take a look at the movement in it.

    I'm getting somewhere with that original gridmove code but it still needs lots of work. Will try to get my head around this new code and see how it fits in.

    Thanks heaps for the help!
     
  8. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Yes, but it's under 20MB so you don't need wifi.

    --Eric
     
  9. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    But trust me - I need something better than the GPRS that I get in the basement that they call my "Office" :D
     
  10. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    @Eric - Realmaze3D looks great, and yes - that's exactly what I'm trying to do, minus the ability to look up and down.
     
  11. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    Are there special considerations for collisions in this situation?

    I've attached the standard "FPSWalker" script (with a bit cut out) to my object as well as a slightly modified "GridMove" script (original linked in above posts).

    If I switch off GridMove and turn on FPSWalker then I can move around inside my mesh using the FPSWalker script without any issues. If I turn on GridMove and turn off FPSWalker, then I can move in my grid-based way but I no longer collide with walls - I just walk straight through them.

    I can't see how the scripts themselves are affecting whether or not the player object collides with the walls.

    Anyone have any hints for me? Both scripts below - go easy on me, I've only just started playing with Unity.

    Code (csharp):
    1. // FPSWalker
    2.  
    3. var speed = 6.0;
    4.  
    5. private var moveDirection = Vector3.zero;
    6.  
    7. function FixedUpdate() {
    8.         moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    9.         moveDirection = transform.TransformDirection(moveDirection);
    10.         moveDirection *= speed;
    11.    
    12.         var controller : CharacterController = GetComponent(CharacterController);
    13.         controller.Move(moveDirection * Time.deltaTime);
    14. }
    15.  
    16. @script RequireComponent(CharacterController)
    Code (csharp):
    1. // GridMove
    2.  
    3. var walkSpeed : float = 5.0;
    4. var gridSize : float = 1.5;
    5. enum Orientation {Horizontal, Vertical}
    6. var gridOrientation = Orientation.Horizontal;
    7. var allowDiagonals = false;
    8. var correctDiagonalSpeed = true;
    9. private var input = Vector3.zero;
    10.  
    11. function Start () {
    12.     var myTransform = transform;
    13.     var startPosition : Vector3;
    14.     var endPosition : Vector3;
    15.     var t : float;
    16.     var tx : float;
    17.     var moveSpeed = walkSpeed;
    18.    
    19.     while (true) {
    20.         while (input == Vector3.zero) {
    21.             GetAxes();
    22.             tx = 0.0;
    23.             yield;
    24.         }
    25.            
    26.         startPosition = myTransform.position;
    27.         endPosition = myTransform.position - Vector3(System.Math.Sign(input.x)*gridSize, 0.0, System.Math.Sign(input.y)*gridSize);
    28.        
    29.         t = tx;
    30.        
    31.         while (t < 1.0) {
    32.             moveSpeed = walkSpeed;
    33.             t += Time.deltaTime * (moveSpeed/gridSize) * (correctDiagonalSpeed  input.x != 0.0  input.y != 0.0? .7071 : 1.0);
    34.             myTransform.position = Vector3.Lerp(startPosition, endPosition, t);
    35.             yield;
    36.         }
    37.         tx = t - 1.0;
    38.         GetAxes();
    39.     }
    40. }
    41.  
    42. function GetAxes () {
    43.     input = Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
    44.     if (allowDiagonals)
    45.         return;
    46.     if (Mathf.Abs(input.x) > Mathf.Abs(input.y))
    47.         input.y = 0.0;
    48.     else
    49.         input.x = 0.0;
    50. }
     
  12. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Well, they don't. I forgot to mention that little detail. ;) There is no "real" collision; instead the Walk function I mentioned above has this:

    Code (csharp):
    1.     if (!mazeScript.CheckForOpenCell(myTransform.position, facing, moveDirection)) {
    2.         return;
    3.     }
    The maze script creates the maze in an array, something like

    Code (csharp):
    1. 11111
    2. 10101
    3. 10101
    4. 10001
    5. 11111
    So the CheckForOpenCell function translates the transform.position to the appropriate array cell, and if there's a wall in the way of the direction the player is trying to move, nothing happens.

    --Eric
     
  13. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    Very clever :)
     
  14. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    After much swearing, I've managed to come up with the following code for implementing grid-based movement in first person. I'm not sure that it's GOOD code, but it works very nicely and I'm extremely happy to have had my first "success" in Unity.

    If anyone else is looking for something like this, my code might at least get you started - though I'm sure there are probably MUCH better ways to do it (HINT, HINT - if you can point out improvements let me know!).

    By the way, this code does not take the walls into account - still working on that.

    Code (csharp):
    1. var MakeMove : int = 0;
    2. var IsMoving : boolean = false;
    3. var Facing : int = 2;
    4. var WalkSpeed : float = 5.0;
    5. var TurnSpeed : float = 1.5;
    6. var GridSize : float = 1.5;
    7. var StartPosition : Vector3;
    8. var EndPosition : Vector3;
    9. var Inputer = Vector3.zero;
    10. var t : float;
    11.  
    12. function OnGUI() {
    13.         if (GUI.Button(Rect (10, 50, 100, 30), "Forward"))
    14.             MakeMove = 1;
    15.            
    16.         if (GUI.Button(Rect (10, 90, 100, 30), "Left"))
    17.             MakeMove = 2;          
    18.            
    19.         if (GUI.Button(Rect (370, 50, 100, 30), "Back"))
    20.             MakeMove = -1;
    21.            
    22.         if (GUI.Button(Rect (370, 90, 100, 30), "Right"))
    23.             MakeMove = -2;
    24. }
    25.  
    26. function Update() {
    27.     if (MakeMove == 1  !IsMoving)
    28.         GoForward();
    29.        
    30.     if (MakeMove == -1  !IsMoving)
    31.         GoBack();
    32.        
    33.     if (MakeMove == 2  !IsMoving)
    34.         TurnLeft();
    35.        
    36.     if (MakeMove == -2  !IsMoving)
    37.         TurnRight();       
    38. }
    39.  
    40. function GoForward() {
    41.     MakeMove = 0;
    42.     IsMoving = true;
    43.    
    44.     StartPosition = transform.position;
    45.        
    46.     if (Facing == 0)
    47.         EndPosition = transform.position - Vector3(0.0, 0.0, GridSize);
    48.            
    49.     if (Facing == 1)
    50.         EndPosition = transform.position - Vector3(GridSize, 0.0, 0.0);
    51.            
    52.     if (Facing == 2)
    53.         EndPosition = transform.position - Vector3(0.0, 0.0, -GridSize);
    54.            
    55.     if (Facing == 3)
    56.         EndPosition = transform.position - Vector3(-GridSize, 0.0, 0.0);
    57.            
    58.     t = 0.0;
    59.        
    60.     while (t < 1.0) {
    61.         t += Time.deltaTime * (WalkSpeed/GridSize);
    62.         transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    63.         yield;
    64.     }
    65.    
    66.     IsMoving = false;
    67. }
    68.  
    69. function GoBack() {
    70.     MakeMove = 0;
    71.     IsMoving = true;
    72.  
    73.     StartPosition = transform.position;
    74.  
    75.     if (Facing == 0)
    76.         EndPosition = transform.position - Vector3(0.0, 0.0, -GridSize);
    77.            
    78.     if (Facing == 1)
    79.         EndPosition = transform.position - Vector3(-GridSize, 0.0, 0.0);
    80.            
    81.     if (Facing == 2)
    82.         EndPosition = transform.position - Vector3(0.0, 0.0, GridSize);
    83.            
    84.     if (Facing == 3)
    85.         EndPosition = transform.position - Vector3(GridSize, 0.0, 0.0);
    86.  
    87.     t = 0.0;
    88.        
    89.     while (t < 1.0) {
    90.         t += Time.deltaTime * (WalkSpeed/GridSize);
    91.         transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    92.         yield;
    93.     }
    94.    
    95.     IsMoving = false;
    96. }
    97.  
    98. function TurnLeft() {
    99.     MakeMove = 0;
    100.     IsMoving = true;
    101.  
    102.     Facing -= 1;
    103.     if (Facing < 0)
    104.         Facing = 3;
    105.    
    106.     var OldRotation = transform.rotation;
    107.     transform.Rotate(0, -90, 0);
    108.     var NewRotation = transform.rotation;
    109.    
    110.     for (t = 0.0; t <= 1.0; t += (TurnSpeed * Time.deltaTime)) {
    111.         transform.rotation = Quaternion.Slerp(OldRotation, NewRotation, t);
    112.         yield;
    113.     }
    114.    
    115.     transform.rotation = NewRotation;
    116.    
    117.     IsMoving = false;  
    118. }
    119.  
    120. function TurnRight() {
    121.     MakeMove = 0;
    122.     IsMoving = true;
    123.  
    124.     Facing += 1;
    125.     if (Facing > 3)
    126.         Facing = 0;
    127.        
    128.     var OldRotation = transform.rotation;
    129.     transform.Rotate(0, 90, 0);
    130.     var NewRotation = transform.rotation;
    131.    
    132.     for (t = 0.0; t <= 1.0; t += (TurnSpeed * Time.deltaTime)) {
    133.         transform.rotation = Quaternion.Slerp(OldRotation, NewRotation, t);
    134.         yield;
    135.     }
    136.    
    137.     transform.rotation = NewRotation;
    138.    
    139.     IsMoving = false;
    140. }
     
  15. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Congrats on getting it to work! You can remove the Update function since it serves no real purpose; just call the functions directly from OnGUI. Also, the forward/back functions do almost the same thing, so you can make a single function for both and pass a variable instead (such as multiplying gridSize by 1 for forward and -1 for back). Same deal with the turn left/right functions. Also it's a good idea to get into the habit of using lowercase for variable names and uppercase for function/class names; that's what the convention is and it will make your code easier to understand. I'd recommend sticking to integer values for the grid size, since it will make various calculations easier later.

    --Eric
     
  16. SteveJ

    SteveJ

    Joined:
    Mar 26, 2010
    Posts:
    3,085
    Thanks Eric - will go through your suggestions and make some improvements tonight. I appreciate all your input on this.
     
  17. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    Hello all. I have been searching, reading, and studying for a while now to learn programming and Unity3D. I really love your movement script, Eric5h5 and SteveJ, because it reminds me so much of the older dungeon crawlers I am so fond of.

    I don't suppose either of you found a way to prevent the player from walking through walls, have you? I undertstand that transform. position ignores colliders which is why the player walks through walls, but I cannot figure out how to work a different kind of movement into your gridmove script. I keep getting errors. I am not really sure how to work an array in there either.

    Thanks for your time.
    Ham08
     
    Last edited: Dec 28, 2013
  18. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    First, I am a newbie programmer and Unity developer, starting only about a week ago. The last time I had to program anything was in QBasic and before that was on an Atari 2600. I really don't know what I am doing

    Anyway, I have been mulling this over for a while and my first idea is to just map out everything with an invisible collision layer Array map to deal with the player walking through walls issue with the Grid Move script and still keep the transform.position method of movement.

    Read from a text file:
    0= normal
    1= blocked
    2= doorway
    3= doorway to next map
    etc.

    CollisionMap = { {0, 0, 0, 0},
    {0, 1, 1, 0},
    {0, 0, 0, 2},
    {0, 3, 0, 2}}


    Some may advocate against using an invisible collision layer, though. The problem is that these projects can get very large. There may be a great number of terrain variation and many maps.

    Isn't Passability already a possible property of the object/prefab so we don't have to worry about its space on the map? Shouldn't we be able to label a certain type of object to be always passable, or always not passable and then programatically query that property? It makes more sense to store this as a kind of metadata about the impassable wall on the object itself, doesn't it? If that's true, then shouldn't I use an array that defines the passability of each type of object (wall=notpassable) rather than building arrays out of many maps of different sizes and makeup? Any ideas on how to do that? Can I just add a tag to a prefab and then just query the object tag to see if it's passable or not(if the tag is "wall", then you cannot move there)? That seems like it would be the easiest, least tedious way to go about this. I am just trying to build a foundation that has the least amount of tedium involved. Does anyone have any idea what I am talking about or what I am trying to do? Please help with a tiny sample code for this type of situation.

    Thank you much!
     
    Last edited: Dec 31, 2013
  19. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    An array is trivial and takes up hardly any memory unless it's really incredibly stupidly large (especially since you could use 1 byte per cell if you don't have more than 256 types and don't need any other info). Remember that computers have gigabytes of RAM as standard these days, and even mobile devices aren't actually that far behind. See here for a demo that uses arrays for movement and collision, no physics. It currently uses 7 bytes per tile for various info, so even with one million tiles the entire map uses a little less than 7MB of RAM. The movement script is here and the level generation script is here.

    --Eric
     
  20. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    Happy New Year! Thank you very much for your swift reply and the example code, Eric5h5.

    Again, be warned that I really don't know what I am doing. I want to handcraft the dungeon rather than use a level generator and I am more worried about how tedious and long the task of making a large array to describe every grid square, which could be hundreds of entries per map with maybe 100 maps. I was hoping I could just put a tag on a prefab wall, call it "wall", then do a linecast and when it finds something directly in front (direction of movement), have the program query the tag of the object and if that query returns "wall" as the tag which is not passable then no movement would happen or a sound would play and some sort of notifier could be printed to the screen like "you can't walk through walls".
     
  21. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    You would use a level editor to make the dungeon. Don't do linecasting or anything, just use an array and determine what movement is possible by looking up the appropriate cell in the array. You can't really use prefabs as such since there would be far too many objects in the scene.

    --Eric
     
  22. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    Thank you for the response, I hope you had a trouble free holiday.

    I'm sure that it's just that I don't understand arrays. I feel like it is more work than necessary to build the 3d dungeon first in Unity, then build it again with a third party dungeon editor.

    I know that you are a very talented programmer and understand things that I have no clue about and I am sorry if I sounded so arrogant with my ignorance.

    Could you tell me where I can learn to use an array in the manor you speak of?

    Thank you very much for your time which is very valuable.
     
  23. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    I'm not sure why you'd build it twice...just use a level editor. If you don't want to program one there are some you can buy instead. I expect you can learn about arrays pretty much anywhere that teaches programming.

    --Eric
     
  24. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    What exactly do you mean by "level editor"? I take it that you do not mean another 3D program used to build dungeons.
     
  25. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    FYI: My only experience with arrays was a tutorial that showed me how to build an inventory system.

    I don't suppose anyone knows of the kind of "level editor" that Eric5h5 speaks of that I would need to get the job done? Searching through the Asset Store doesn't help me because I have no idea what I am looking for (I Searched for Array/level editor). Searching the web comes up with a million websites of which most are not even close to what I am looking for. If not, maybe someone knows of a tutorial website that deals as closely as possible with the problem I am having with the player walking through walls using the grid move script as modified by Steve (above in this thread).

    I have learned how to program health mana bars, combat system, main/battle menus, enemy AI, character classes, stats,skills, attributes, inventory, targeting, random spawns/instantiating, camera controls, armor destruction, etc. I just need to stop the player from walking through walls. I guess I could just give up on the grid move all together, but I really love the way it works. It reminds me so much of the older dungeon crawlers I am so fond of.

    Thanks for any pointers or direction I can take.

    Cheers!
     
  26. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    I mean one that you make, or buy. Here's one that looks like it would work for a 3D dungeon.

    --Eric
     
  27. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    It looks like a way to make 2d tile mapping "easier", but I don't see how that relates to preventing the player from walking through walls. It doesn't say anything about creating an array which allows you to query a cell for properties like passable/not_passable. And with this plugin, I am still required to map out the entire dungeon a second time which I feel should be uneccessary. It is perplexing and frustrating to me that I cannot do what I need to do with Plain-Jane Unity (the level geometry is already there, why can't I just query that?). If I can do what I need to do with just Unity, then I ask where is a tutorial resource for me to learn how to do it. If no tutorial resource exists that deals exactly with this problem, then I humbly ask for someone who has a few minutes of time to explain in detail the steps needed to get this done. If it takes too much time to explain the method then it must be way too tedious and impractical to use. There must be a better way.

    Telling someone to just use an array and level editor is not as much help as you might think to someone who doesn't understand that particular use of an array or even see why it is necessary.
     
  28. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    If you have a 2D array where 0 is passable and 1 is impassable, and the player position is, say, (10, 15), and the player is attempting to go right, then check (11, 16). If it's 0 then allow movement, if it's 1 then disallow. It's "necessary" because there is no "passable/impassable" attribute for level geometry, it's something you create yourself. Unity can't have all conceivable features built-in; you're expected to do some programming.

    --Eric
     
  29. Ham08

    Ham08

    Joined:
    Dec 28, 2013
    Posts:
    15
    I'm sorry to try your patience, Eric5h5. I am poor (existing far below the poverty line) and cannot afford to buy other people's scripts, so I will have to do it the hard/long way which isn't really a bad thing since I need to learn how to do it and to understand it anyway.

    I am still clueless about how to do this. I am hoping because you are a moderator, that you would be able to point me to a forum thread that explains in detail how to do this along with example code that I can clearly see how this method is used for this very purpose (maybe just a tiny "handcrafted" NOT procedurally generated 3D dungeon level with a few walls and 3 or 4 small rooms; very small scale so I can actually see what the code is doing and not get hopelessly lost; I need to see how the code sets up the array, how it is connected to the actually geometry of the level, and how those queries are performed; all so that I can use what I have learned for my own handcrafted 3d dungeon levels). Since you understand exactly what I am trying to do, you are very well suited to point me in the right direction, that is if you don't mind (I hope I have not been too much of a bother already). My week-long searches have turned up every possible use for an array except something very specific to this situation. The sheer volume of websites dealing with arrays is overwhelming.

    I am stuck. Please, point me in the right direction if you can. Thank you much.
     
  30. kafanasiff

    kafanasiff

    Joined:
    Sep 25, 2013
    Posts:
    31
    Hi,

    I came up with a simple solution for this type of movement last night, before I ran into this thread today looking for other ways of doing it today. Here's the code for my solution, hope it helps. I am not using a grid for this, which has some advantages/disadvantages. It's fast for building the world in the scene view, because you don't have to add every cell to an array. On the other hand, I think a grid would be much better if you want to do procedural generation or pathfinding. This way works as a quick and dirty solution though, if you plan to build your world in the scene view and are OK with just scripting enemy behavior instead of using pathfinding. I'm not an expert in Unity by any means, so if others have objections to this way of doing it please let me know so I can learn!

    This solution requires that a 1x1x1 box collider be attached to cells that you do not want to be able to walk through (like walls), and positioned correctly. It then uses the Physics.OverlapShere function to check if there is a collider in the cell where the movement is going, and only allows movement if a collider does not exist. You could modify this function to check for a tag or layer on the game object so that only some colliders prevent the movement, instead of all of them. Here's the code, and good luck!

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class AvatarMovement : MonoBehaviour {
    6.  
    7.     private int cellSize = 1;
    8.  
    9.     // Use this for initialization
    10.     void Start () {
    11.    
    12.     }
    13.    
    14.     // Update is called once per frame
    15.     void Update () {
    16.         if (Input.GetKeyUp ("up")) {
    17.             MoveForward ();
    18.         }
    19.         if (Input.GetKeyUp ("down")) {
    20.             MoveBackward ();
    21.         }
    22.         if (Input.GetKeyUp ("left")) {
    23.             RotateLeft ();
    24.         }
    25.         if (Input.GetKeyUp ("right")) {
    26.             RotateRight ();
    27.         }
    28.     }
    29.    
    30.     private void MoveForward() {
    31.         Vector3 newPos = transform.position + transform.TransformDirection (Vector3.forward) * cellSize;
    32.         Collider[] hitColliders = Physics.OverlapSphere (newPos, 0.1f);
    33.         if (hitColliders.Length == 0) {    transform.Translate (Vector3.forward * cellSize); }    }
    34.    
    35.     private void MoveBackward() {
    36.         Vector3 newPos = transform.position + transform.TransformDirection (Vector3.forward) * -cellSize;
    37.         Collider[] hitColliders = Physics.OverlapSphere (newPos, 0.1f);
    38.         if (hitColliders.Length == 0) {    transform.Translate (Vector3.forward * -cellSize); }
    39.     }
    40.    
    41.     private void RotateLeft() {
    42.         transform.Rotate (0,-90,0);
    43.     }
    44.  
    45.     private void RotateRight() {
    46.         transform.Rotate (0,90,0);
    47.     }
    48. }
    49.  
     
  31. amiino

    amiino

    Joined:
    Oct 31, 2014
    Posts:
    1
    converted to C# for people in need :

    public int MakeMove = 0;
    public bool IsMoving = false;
    public int Facing = 2;
    public float WalkSpeed = 5.0f;
    public float TurnSpeed = 1.5f;
    public float GridSize = 1.5f;
    private Vector3 StartPosition;
    private Vector3 EndPosition;
    private Vector3 Inputer = Vector3.zero;
    private float t;


    public void Update() {
    if (MakeMove == 1 && !IsMoving) {
    StartCoroutine( GoForward () );
    }

    if (MakeMove == -1 && !IsMoving) {
    StartCoroutine (GoBack ());
    }

    if (MakeMove == 2 && !IsMoving) {
    StartCoroutine( TurnLeft() );
    }

    if (MakeMove == -2 && !IsMoving) {
    StartCoroutine( TurnRight() );
    }
    }

    public void OnGUI() {
    if (GUI.Button(new Rect (10, 50, 100, 30), "Forward"))
    MakeMove = 1;

    if (GUI.Button(new Rect (10, 90, 100, 30), "Left"))
    MakeMove = 2;

    if (GUI.Button(new Rect (370, 50, 100, 30), "Back"))
    MakeMove = -1;

    if (GUI.Button(new Rect (370, 90, 100, 30), "Right"))
    MakeMove = -2;
    }


    IEnumerator GoForward() {
    MakeMove = 0;
    IsMoving = true;

    StartPosition = transform.position;

    if (Facing == 0) {
    EndPosition = transform.position - (new Vector3(0.0f, 0.0f, GridSize));
    }

    if (Facing == 1) {
    EndPosition = transform.position - (new Vector3 (GridSize, 0.0f, 0.0f));
    }

    if (Facing == 2) {
    EndPosition = transform.position - (new Vector3 (0.0f, 0.0f, -GridSize));
    }

    if (Facing == 3) {
    EndPosition = transform.position - (new Vector3 (-GridSize, 0.0f, 0.0f));
    }

    t = 0.0f;

    while (t < 1.0) {
    t += Time.deltaTime * (WalkSpeed/GridSize);
    transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    yield return new WaitForSeconds(0);
    }

    IsMoving = false;
    }


    IEnumerator GoBack() {
    MakeMove = 0;
    IsMoving = true;

    StartPosition = transform.position;

    if (Facing == 0)
    EndPosition = transform.position - (new Vector3(0.0f, 0.0f, -GridSize));

    if (Facing == 1)
    EndPosition = transform.position - (new Vector3(-GridSize, 0.0f, 0.0f));

    if (Facing == 2)
    EndPosition = transform.position - (new Vector3(0.0f, 0.0f, GridSize));

    if (Facing == 3)
    EndPosition = transform.position - (new Vector3(GridSize, 0.0f, 0.0f));

    t = 0.0f;

    while (t < 1.0) {
    t += Time.deltaTime * (WalkSpeed/GridSize);
    transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    yield return new WaitForSeconds(0);
    }

    IsMoving = false;
    }



    IEnumerator TurnLeft() {
    MakeMove = 0;
    IsMoving = true;

    Facing -= 1;
    if (Facing < 0)
    Facing = 3;

    var OldRotation = transform.rotation;
    transform.Rotate(0, -90, 0);
    var NewRotation = transform.rotation;

    for (t = 0.0f; t <= 1.0f; t += (TurnSpeed * Time.deltaTime)) {
    transform.rotation = Quaternion.Slerp(OldRotation, NewRotation, t);
    yield return new WaitForSeconds(0);
    }

    transform.rotation = NewRotation;

    IsMoving = false;
    }

    IEnumerator TurnRight() {
    MakeMove = 0;
    IsMoving = true;

    Facing += 1;
    if (Facing > 3)
    Facing = 0;

    var OldRotation = transform.rotation;
    transform.Rotate(0, 90, 0);
    var NewRotation = transform.rotation;

    for (t = 0.0f; t <= 1.0f; t += (TurnSpeed * Time.deltaTime)) {
    transform.rotation = Quaternion.Slerp(OldRotation, NewRotation, t);
    yield return new WaitForSeconds(0);
    }

    transform.rotation = NewRotation;

    IsMoving = false;
    }
     
  32. artrebel9

    artrebel9

    Joined:
    Dec 5, 2014
    Posts:
    7
    Thank you amiino for the script. I expanded it a bit with side stepping, basic collision detection and bounce back as well as mapping to keyboard q w e a s d keys. If anyone needs it here it be:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GridMovement : MonoBehaviour
    5. {
    6.     public int MakeMove = 0;
    7.     public bool IsMoving = false;
    8.     public int Facing = 2;
    9.     public float WalkSpeed = 5.0f;
    10.     public float TurnSpeed = 1.5f;
    11.     public float GridSize = 1.5f;
    12.     private Vector3 StartPosition;
    13.     private Vector3 EndPosition;
    14.     private Vector3 old;
    15.     private float t;
    16.  
    17.  
    18.     public void Update()
    19.     {
    20.         if (MakeMove == 1 && !IsMoving)
    21.         {
    22.             StartCoroutine(GoForward());
    23.         }
    24.  
    25.         if (MakeMove == -1 && !IsMoving)
    26.         {
    27.             StartCoroutine(GoBack());
    28.         }
    29.  
    30.         if (MakeMove == 2 && !IsMoving)
    31.         {
    32.             StartCoroutine(TurnLeft());
    33.         }
    34.  
    35.         if (MakeMove == -2 && !IsMoving)
    36.         {
    37.             StartCoroutine(TurnRight());
    38.         }
    39.  
    40.         if (MakeMove == 3 && !IsMoving)
    41.         {
    42.             StartCoroutine(StepLeft());
    43.         }
    44.  
    45.         if (MakeMove == -3 && !IsMoving)
    46.         {
    47.             StartCoroutine(StepRight());
    48.         }
    49.  
    50.         if (Input.GetKeyDown("w"))
    51.         {
    52.             MakeMove = 1;
    53.         }
    54.         if (Input.GetKeyDown("s"))
    55.         {
    56.             MakeMove = -1;
    57.         }
    58.         if (Input.GetKeyDown("a"))
    59.         {
    60.             MakeMove = 2;
    61.         }
    62.         if (Input.GetKeyDown("d"))
    63.         {
    64.             MakeMove = -2;
    65.         }
    66.         if (Input.GetKeyDown("q"))
    67.         {
    68.             MakeMove = 3;
    69.         }
    70.         if (Input.GetKeyDown("e"))
    71.         {
    72.             MakeMove = -3;
    73.         }
    74.     }
    75.  
    76.     public void OnGUI()
    77.     {
    78.         if (GUI.Button(new Rect(120, 50, 100, 30), "Step Forward"))
    79.          
    80.         MakeMove = 1;
    81.  
    82.         if (GUI.Button(new Rect(10, 90, 100, 30), "Turn Left"))
    83.  
    84.             MakeMove = 2;
    85.  
    86.         if (GUI.Button(new Rect(120, 90, 100, 30), "Step Back"))
    87.            
    88.         MakeMove = -1;
    89.  
    90.         if (GUI.Button(new Rect(230, 90, 100, 30), "Turn Right"))
    91.             MakeMove = -2;
    92.  
    93.         if (GUI.Button(new Rect(10, 50, 100, 30), "Step Left"))
    94.             MakeMove = 3;
    95.  
    96.         if (GUI.Button(new Rect(230, 50, 100, 30), "Step Right"))
    97.             MakeMove = -3;
    98.     }
    99.  
    100.  
    101.     IEnumerator GoForward()
    102.     {
    103.         old = EndPosition;
    104.         MakeMove = 0;
    105.         IsMoving = true;
    106.  
    107.         StartPosition = transform.position;
    108.  
    109.         if (Facing == 0)
    110.         {
    111.             EndPosition = transform.position - (new Vector3(0.0f, 0.0f, GridSize));
    112.         }
    113.  
    114.         if (Facing == 1)
    115.         {
    116.             EndPosition = transform.position - (new Vector3(GridSize, 0.0f, 0.0f));
    117.         }
    118.  
    119.         if (Facing == 2)
    120.         {
    121.             EndPosition = transform.position - (new Vector3(0.0f, 0.0f, -GridSize));
    122.         }
    123.  
    124.         if (Facing == 3)
    125.         {
    126.             EndPosition = transform.position - (new Vector3(-GridSize, 0.0f, 0.0f));
    127.         }
    128.  
    129.         t = 0.0f;
    130.  
    131.         while (t < 1.0)
    132.         {
    133.             t += Time.deltaTime * (WalkSpeed / GridSize);
    134.             transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    135.             yield return new WaitForSeconds(0);
    136.         }
    137.  
    138.         IsMoving = false;
    139.     }
    140.  
    141.  
    142.     IEnumerator GoBack()
    143.     {
    144.         old = EndPosition;
    145.         MakeMove = 0;
    146.         IsMoving = true;
    147.  
    148.         StartPosition = transform.position;
    149.  
    150.         if (Facing == 0)
    151.             EndPosition = transform.position - (new Vector3(0.0f, 0.0f, -GridSize));
    152.  
    153.         if (Facing == 1)
    154.             EndPosition = transform.position - (new Vector3(-GridSize, 0.0f, 0.0f));
    155.  
    156.         if (Facing == 2)
    157.             EndPosition = transform.position - (new Vector3(0.0f, 0.0f, GridSize));
    158.  
    159.         if (Facing == 3)
    160.             EndPosition = transform.position - (new Vector3(GridSize, 0.0f, 0.0f));
    161.  
    162.         t = 0.0f;
    163.  
    164.         while (t < 1.0)
    165.         {
    166.             t += Time.deltaTime * (WalkSpeed / GridSize);
    167.             transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    168.             yield return new WaitForSeconds(0);
    169.         }
    170.  
    171.         IsMoving = false;
    172.     }
    173.  
    174.     IEnumerator StepLeft()
    175.     {
    176.         old = EndPosition;
    177.         MakeMove = 0;
    178.         IsMoving = true;
    179.  
    180.         StartPosition = transform.position;
    181.  
    182.         if (Facing == 0)
    183.         {
    184.             EndPosition = transform.position - (new Vector3(-GridSize, 0.0f, 0.0f));
    185.         }
    186.  
    187.         if (Facing == 1)
    188.         {
    189.             EndPosition = transform.position - (new Vector3(0.0f, 0.0f, GridSize));
    190.         }
    191.  
    192.         if (Facing == 2)
    193.         {
    194.             EndPosition = transform.position - (new Vector3(GridSize, 0.0f, 0.0f));
    195.         }
    196.  
    197.         if (Facing == 3)
    198.         {
    199.             EndPosition = transform.position - (new Vector3(0.0f, 0.0f, -GridSize));
    200.         }
    201.  
    202.         t = 0.0f;
    203.  
    204.         while (t < 1.0)
    205.         {
    206.             t += Time.deltaTime * (WalkSpeed / GridSize);
    207.             transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    208.             yield return new WaitForSeconds(0);
    209.         }
    210.  
    211.         IsMoving = false;
    212.     }
    213.  
    214.     IEnumerator StepRight()
    215.     {
    216.         old = EndPosition;
    217.         MakeMove = 0;
    218.         IsMoving = true;
    219.  
    220.         StartPosition = transform.position;
    221.  
    222.         if (Facing == 0)
    223.         {
    224.             EndPosition = transform.position - (new Vector3(GridSize, 0.0f, 0.0f ));
    225.         }
    226.  
    227.         if (Facing == 1)
    228.         {
    229.             EndPosition = transform.position - (new Vector3( 0.0f, 0.0f, -GridSize ));
    230.         }
    231.  
    232.         if (Facing == 2)
    233.         {
    234.             EndPosition = transform.position - (new Vector3(-GridSize, 0.0f, 0.0f ));
    235.         }
    236.  
    237.         if (Facing == 3)
    238.         {
    239.             EndPosition = transform.position - (new Vector3( 0.0f, 0.0f, GridSize));
    240.         }
    241.  
    242.         t = 0.0f;
    243.  
    244.         while (t < 1.0)
    245.         {
    246.             t += Time.deltaTime * (WalkSpeed / GridSize);
    247.             transform.position = Vector3.Lerp(StartPosition, EndPosition, t);
    248.             yield return new WaitForSeconds(0);
    249.         }
    250.  
    251.         IsMoving = false;
    252.     }
    253.  
    254.     IEnumerator TurnLeft()
    255.     {
    256.         MakeMove = 0;
    257.         IsMoving = true;
    258.  
    259.         Facing -= 1;
    260.         if (Facing < 0)
    261.             Facing = 3;
    262.  
    263.         var OldRotation = transform.rotation;
    264.         transform.Rotate(0, -90, 0);
    265.         var NewRotation = transform.rotation;
    266.  
    267.         for (t = 0.0f; t <= 1.0f; t += (TurnSpeed * Time.deltaTime))
    268.         {
    269.             transform.rotation = Quaternion.Slerp(OldRotation, NewRotation, t);
    270.             yield return new WaitForSeconds(0);
    271.         }
    272.  
    273.         transform.rotation = NewRotation;
    274.  
    275.         IsMoving = false;
    276.     }
    277.  
    278.     IEnumerator TurnRight()
    279.     {
    280.         MakeMove = 0;
    281.         IsMoving = true;
    282.  
    283.         Facing += 1;
    284.         if (Facing > 3)
    285.             Facing = 0;
    286.  
    287.         var OldRotation = transform.rotation;
    288.         transform.Rotate(0, 90, 0);
    289.         var NewRotation = transform.rotation;
    290.  
    291.         for (t = 0.0f; t <= 1.0f; t += (TurnSpeed * Time.deltaTime))
    292.         {
    293.             transform.rotation = Quaternion.Slerp(OldRotation, NewRotation, t);
    294.             yield return new WaitForSeconds(0);
    295.         }
    296.  
    297.         transform.rotation = NewRotation;
    298.  
    299.         IsMoving = false;
    300.     }
    301.  
    302.     public void OnCollisionEnter()
    303.     {
    304.         EndPosition = old;
    305.     }
    306. }
     
  33. WhiskeyMoonshine

    WhiskeyMoonshine

    Joined:
    Jun 25, 2020
    Posts:
    1
    Hello everyone! I am a beginner in Unity and I don't have the faintest clue on how to code yet. I applied the code provided above by artrebel9 and it works perfectly except when I am traversing staircases. I bump through them rather than climb them. I tried applying Character Controller and RigidBody components to both the stair object and the cube I attached my camera to but I cannot get the desired effect. I also pass through terrain. Can someone please help me? I know this is an old thread but any help is appreciated.