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.

Scripted movement (with amazing hand-drawn example!)

Discussion in 'Scripting' started by bbvrdev, Mar 24, 2011.

  1. bbvrdev

    bbvrdev

    Joined:
    Aug 11, 2009
    Posts:
    221
    Hey guys,

    I'm trying to script some simple character movement, but I have a few limitations:
    - I cannot use forces or gravity with the physics engine, my character must be a trigger
    - My character cannot appear to rotate (at least, not his sprite)
    - Character must move at a constant rate

    I would like him to simply "trace" over geometry on an essentially 2D sidescroller like the image below. Can anyone give me a clue as to the most efficient way to approach this?

    Thanks!
     

    Attached Files:

  2. MADmarine

    MADmarine

    Joined:
    Aug 31, 2010
    Posts:
    627
    You could perhaps have two child objects with colliders, one in front of the character and one below.

    They report back to your character object whether anything is colliding with them.

    - The collider below takes first priority, if nothing is colliding the character should move downwards.

    - If there is a collision, check the collider in front, if there is a collision then move the character upwards.

    - Else the character should just move forwards.

    You'd need to have fiddle around with the colliders to get it to look right, or you could perhaps do the same thing but with raycasts. The only reason I don't suggest raycasts as my first solution is that when terrain gets smaller and more detailed, the ray might misinterpret the terrain and the character could run through things.
     
  3. bbvrdev

    bbvrdev

    Joined:
    Aug 11, 2009
    Posts:
    221
    Thanks MADmarine, that was essentially my first solution, but the diagonal planes mess it up because it moves straight through them. I need the rotation of the terrain object to set the translate direction of my character on the diagonal planes.
     
  4. MADmarine

    MADmarine

    Joined:
    Aug 31, 2010
    Posts:
    627
    Surely if the colliders were big enough they would pick up the diagonal surfaces? I imagine them almost being wall like in front of your character, like: o|

    Perhaps a raycast solution could work, if you got a direction vector from the normal of the surface you hit and rotated it by 90 degrees you'd have the direction your character would need to translate. You'd just have to be careful with your positioning of rays to avoid the problems I mentioned earlier.
     
  5. bbvrdev

    bbvrdev

    Joined:
    Aug 11, 2009
    Posts:
    221
    I know that's the obvious solution, I've tried many derivations of it that sort-of work, but I keep running into snags. Is there a way to simply get the angle of the block, and move the dude in that direction? This is what I've got so far, but it's returning weird values for xMove and yMove on two blocks that are at almost exactly the same angle.

    Code (csharp):
    1.  
    2.     if (other.collider.tag == "Block" || other.collider.tag == "Ground") {
    3.        
    4.         var xMove : double = -1 * Mathf.Cos(Mathf.Rad2Deg * (other.transform.eulerAngles.z));
    5.         var yMove : double = -1 * Mathf.Sin(Mathf.Rad2Deg * (other.transform.eulerAngles.z));
    6.        
    7.         if (other.transform.eulerAngles.z > 359  other.transform.eulerAngles.z < 360) {
    8.             yMove = 0;
    9.             xMove = -2.3;
    10.         }
    11.        
    12.         terrainHitCount++;
    13.  
    14.         //this is backward since soldier is flipped coming from right
    15.         if (GetCollisionQuad(other) == Direction.Top || GetCollisionQuad(other) == Direction.Right) {
    16.            
    17.             if (terrainHitCount > 1) {
    18.            
    19.                 soldierState = SoldierState.Climbing;
    20.            
    21.             }
    22.             else if (terrainHitCount > 0) {
    23.                
    24.                 UseGravity(false);
    25.                 soldierState = SoldierState.Walking;
    26.  
    27.                 //set movement direction by angles
    28.                 upwardMovementSpeed = Mathf.Abs(yMove);
    29.                 forwardMovementSpeed = xMove;
    30.                
    31.             }
    32.            
    33.         }
    34.         else if (GetCollisionQuad(other) == Direction.Bottom) {
    35.            
    36.             UseGravity(false);
    37.             soldierState = SoldierState.Walking;
    38.            
    39.             //set movement direction by angles
    40.             upwardMovementSpeed = Mathf.Abs(yMove);
    41.             forwardMovementSpeed = xMove;
    42.            
    43.         }
    44.  
    45.     }
    46.  
    47.  
     
  6. bbvrdev

    bbvrdev

    Joined:
    Aug 11, 2009
    Posts:
    221
    Ok, weird. It works if I set xMove to a constant -2.3. Can anyone tell me why?? :) :)
     
  7. dissidently

    dissidently

    Joined:
    Dec 8, 2010
    Posts:
    286
    is that an 85mm 1.2
     
  8. bbvrdev

    bbvrdev

    Joined:
    Aug 11, 2009
    Posts:
    221
    Hehe close, I think it's a 1.4 though. Unfortunately, it's like 30 years old and got that weird crystalline fungus that grows on lens coating, and when it was given to me in Indonesia, it was basically unusable :( Makes me wanna cry.
     
  9. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    Based on your example (nice image, by the way :), it look to me as if the optimal solution would be to raycast straight down from a point directly above the player, and high enough so as to be guaranteed to be higher than any 'ground' object. The intersection points would then trace out the path, more or less. (If you want the path to be offset a bit as in your image, you might try a sphere cast instead.)

    [Edit: One additional comment on this method; you'd probably need to incorporate some interpolation to prevent the object from snapping instantly to a new height when there are discontinuities in the path.]
     
    Last edited: Mar 25, 2011
  10. probbins

    probbins

    Joined:
    Oct 19, 2010
    Posts:
    216
    Why not create a simple waypoint system?

    The corners of the blue lines could be waypoint/empty game object/point in space. Pressing left or right will translate you towards the waypoint at a constant speed. left being at -constantspeed.

    waypoints could be a simple parent child hierarchy, as you get to a waypoint, the child waypoint becomes the next current waypoint.

    Key items would be using player.position = Vector3.MoveTowards(player.position, waypoint, movespeed * Time.deltaTime); or -movespeed incase of left button being pressed.
     
  11. aoakenfo

    aoakenfo

    Joined:
    Jul 14, 2009
    Posts:
    35
    Unless these obstacles are generated dynamically, I would simply create waypoints and use iTween to animate between them, done.
     
  12. bbvrdev

    bbvrdev

    Joined:
    Aug 11, 2009
    Posts:
    221
    Thanks guys, your solutions are great. Jesse, that's a really interesting way to solve the problem. Only issue is, the blocks can be tossed around by physics, so I would have to rescan the topography when items came to a stop.
     
  13. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    I was thinking more that you'd scan continuously, raycasting downward through the player's position each update in order to determine the current desired height, which should handle changes in the environment transparently. (The method still may not meet your needs, depending, but it seems like it might be fairly close to what you're looking for.)
     
  14. DPGUnit

    DPGUnit

    Joined:
    Mar 30, 2011
    Posts:
    26
    For your constant movement you can use this

    // A very simplistic car driving on the x-z plane.

    var speed : float = 10.0;
    var rotationSpeed : float = 100.0;

    function Update () {
    // Get the horizontal and vertical axis.
    // By default they are mapped to the arrow keys.
    // The value is in the range -1 to 1
    var translation : float = Input.GetAxis ("Vertical") * speed;
    var rotation : float = Input.GetAxis ("Horizontal") * rotationSpeed;

    // Make it move 10 meters per second instead of 10 meters per frame...
    translation *= Time.deltaTime;
    rotation *= Time.deltaTime;

    // Move translation along the object's z-axis
    transform.Translate (0.1, 0, translation);
    // Rotate around our y-axis
    transform.Rotate (0, rotation, 0);
    }
     
  15. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    I think that code implements a different kind of movement than the OP is asking about.
     
unityunity