Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Procedural ledge grab and wall run ect ect ect...

Discussion in 'Scripting' started by ThatHomelessGuy, Jul 31, 2011.

  1. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    Hi
    So I had a look around and everybody seems to be insisting on using triggers on the ledge.
    This method is all well and good for a linear game or a platformer, but my project is an fps on a much larger scale so setting up Litteraly hundreds of thousands of triggers is simply not an option.

    My best guess is to use raycasts to find a viable ledge and apply the correct action but I cannot work out the method for finding a viable ledge.from a raycast.

    As for the wall run I have a good idea how i might handle that but if anybody has any suggestions on that then they are welcome and much appreciated.

    Ultimately i need the player to grab before they can wall run.

    Can anybody help me out here?

    If I can get this to work I will post a complete solution of the working script for everybody to use. :cool:
     
    metamorphist likes this.
  2. iio

    iio

    Joined:
    Nov 21, 2009
    Posts:
    22
    A single raycast would probably miss the object often. How about you create an invisible cone extends from the player and if it touches a ledge object and the conditions are correct your char can grab it? It would be good to stream all ledge objects in so the look-up function isn't called all the time as I hear, can't confirm, that it is slow.

    Either that or use multiple raycasts and interpolate a shape based on where their ends finish. If the shape is greater than your test volume then you have a grabable (?) ledge.
     
  3. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    Manually setting up all those trigger boxes would indeed be a bit crazy. However, assuming that your scenery is properly built from prefabs, you would just need to add the triggers to those.

    Parkour games primarily use trigger-like setups for two very good reasons:
    1: Calculating ledge grabs etc. at runtime is expensive.
    2: Runtime systems can produce undesirable results. An editor time system can be inspected and tweaked.​
     
    metamorphist likes this.
  4. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    I think triggers are still the way forward.

    In my game, for ledge grabbing/sidling/jumping, the triggers are just a hierarchy of empty game nodes placed along the route I want the ledge to take (which can be vertex snapped into position) and I have a script applied to the parent node which goes through the hierarchy aligning the parent to it's child and applying a ledge trigger box that spans the gap between the two. The script component deletes itself and I'm left with a nice series of ledge trigger boxes exactly where I wanted them.

    The advantage being that so long as they're a hierarchy, I don't have to align them or set up each trigger box - all that's handled in code. I just need to put the nodes in the right place.

    Also, I'm using a spherecast limited to the ledge node's layer. Raycast is far too accurate and often misses the little triggerboxes as the player moves each frame.

    Here's the generateLedgesFromChildren.js script I wrote.

    The hierarchy I use runs like;

    LedgeGroup (this is the empty gameobject with the script below on it)
    ..|- LedgeNode (these are all empty game objects with no scripts on them, the gap between them will be filled with a ledge trigger)
    ....|- LedgeNode
    ......|- LedgeNode

    Code (csharp):
    1.  
    2. #pragma strict
    3.  
    4. function Awake () {
    5.  
    6.     gameObject.layer = LayerMask.NameToLayer ( "Ledge_Grab" );
    7.  
    8.     var child = transform.Find ( "LedgeNode" );
    9.    
    10.     if ( child != null ) {
    11.         // Store child transform.
    12.         var childPosition : Vector3 = child.position;
    13.         var childRotation : Quaternion = child.rotation;
    14.        
    15.         // Distance between this and child.
    16.         var distance : float = Vector3.Distance ( transform.position, child.position );
    17.    
    18.         // Move this object into place.
    19.         transform.LookAt ( child );
    20.         transform.Rotate ( 0, -90, 0 );
    21.         transform.Translate ( Vector3 ( distance / 2, -0.05, -0.05 ) );
    22.        
    23.         // Restore child transform.
    24.         child.position = childPosition;
    25.         child.rotation = childRotation;
    26.        
    27.         // Add and set up collider.
    28.         var ledgeCollider : BoxCollider = gameObject.AddComponent ( "BoxCollider" ) as BoxCollider;
    29.         ledgeCollider.size = Vector3 ( distance, 0.1, 0.1 );
    30.        
    31.         // Add this component to the child.
    32.         child.gameObject.AddComponent ( "generateLedgesFromChildren" );
    33.     }
    34.    
    35.     // Destroy this component. It's work is done.
    36.     Destroy ( this );
    37. }
    38.  
    39. function OnDrawGizmos () {
    40.  
    41.     var childrenTransforms : Transform[] = gameObject.GetComponentsInChildren.<Transform> ();
    42.    
    43.     var parentTransformPos : Vector3 = transform.position;
    44.     for ( var childTransform : Transform in childrenTransforms ) {
    45.         Gizmos.color = Color.yellow;
    46.         Gizmos.DrawLine ( childTransform.position, parentTransformPos );
    47.         parentTransformPos = childTransform.position;
    48.     }
    49. }
    50.  
     
    Last edited: Aug 1, 2011
  5. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    Just to clarify a bit: What most parkour games do is they still have an automated system detecting ledges, trenches, doors etc, but this is just run at editor time and places markup data not too dissimilar to trigger boxes.

    The level designer can then go in and inspect the result, removing unwanted markers and adding extras.
     
  6. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    I like this Idea but it still requires me to place all the nodes which is alot of work believe me. Imagine an open urban ruin the size of the Island demo and that is a fraction of the environment. You can call me lazy if you like.

    But still I went over your code and it seems pretty solid and I reckon its in the right direction. It has inspired me though. Could the node data be replaced with vertex data and search for viable ledges based on the normals that connect along the edge between the vertices?

    Even if it was run on level load or in the editor that would be fine. Obviously vertex/node spacing would then be a factor to consider. The length of a normal would also have to be worked out but then it would be a very flexible and reusable.

    If this could be run in the editor I could go back and clean it up after as AngryAnt mentioned.
     
  7. flaminghairball

    flaminghairball

    Joined:
    Jun 12, 2008
    Posts:
    868
    Yeah, if you're going to have to be messing with 'realtime ledge detection,' you may as well just build an editor script to parse your level data and automatically set up triggers for you *before* runtime. That way, you can have the benefits of triggers, and you can also tweak it by hand if need be.
     
  8. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Yeah, you could write a script that'll crawl the mesh data and give you all the edges that are viable ledges and set up triggers for them.
     
  9. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    I think I will create something like this but first of all I found a better solution by mistake here by Wolfire Games.

    http://www.youtube.com/watch?v=GFu44oeLYPI&feature=channel_video_title

    The developer here uses a sphere to detect a nearby wall and creates a cylinder above the player that checks for a surface above the player.
    If the player is close enough to the cylinder it creates a node at the ledge and that becomes the players anchor instead of usng a trigger.
    As far as i can see this should have minimal overhead and maintains a procedural process as and when its needed. this could save me weeks.
     
    SepM likes this.
  10. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    Ok so I managed to start working on this today and I thought i could use OverlapSphere since triggers won't detect colisions with static objects but after looking over the documents i found that OverlapSphere checks against the bounding volume and not the collider.

    I would prefer not to use a dirty method of rotating the scene back and foreward .00001 or something every frame so that i can use triggers. taking into consideration that this would cause larger anomalies the further away from the centre of the levels mesh you get. I was thinking of cycling a raycast one per frame (this is to reduce the overhead of raycasting) in a curcular pattern around the player at 2 - 3 different heights to detect nearby walls when the character is airborn and then reducing its raycast cycle to the raycasts only in the walls direction based on where it contacts the wall. This is all for the purpose of the wall check part of the system.

    Does anybody know of another method i could use for wall checks that would require a less processor heavy method than raycast.
    Reducing overhead is obviosly Important here. because I will be layering in a ledge check the instant that a wall check returns true, both of these will be updated whenever the player moves. Again this is to lower overhead they only need to update if the players position/rotation updates.

    Sorry for the long description I just want everybody to get an idea of the logic to avoid confused replys.

    Edit:
    I forgot to mention that more than one player may be making wall checks simultaniosly including AI. This Is why overhead Is a problem when more than one player or npc jumps near a wall.
     
    Last edited: Aug 3, 2011
  11. Kinos141

    Kinos141

    Joined:
    Jun 22, 2011
    Posts:
    969
    I did parkour coding when I used UDK. I used many traces(Raycast in Unity) and placed them at particular areas of the body. I'd have a trace at the torso, feet aiming forward, feet aiming 45 degree angle down, the head, etc. They worked great, but the team I was working on it with went bleh..so I stopped working on it. A guy that used to help with the code(because he was doing the same in his team) used WAY more traces(again, Raycasts) for better effects.
    You can use the method shown in the video.
     
  12. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    Erm sorry but, what video is this?
     
  13. Kinos141

    Kinos141

    Joined:
    Jun 22, 2011
    Posts:
    969
    The one you posted earlier in the thread. I think that was a good idea. Actually, I was thinking of doing something likewhat was show in the video (http://www.youtube.com/watch?v=GFu44...el_video_title)when I was working my parkour.
     
  14. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    yes that was the plan but unfortunately If i use a solid collider it will hit the wall causing unrealistic collision for the player If i change it to a Trigger Collider then it wont register collisions with static objects and overlap sphere only registers the bounds of the collider which is no good either. Unity just doesn't support trigger collision the way udk or the Overgrowth engine does.

    Edit: My point being is unless i can find a workaround that isnt as dirty as moving the level geometry every frame so that the trigger can register the collision then I will have to go with raycasts though I'd prefer to use something with less overhead
     
    Last edited: Aug 3, 2011
  15. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    Why not make it a kinematic rigidbody collider?
     
  16. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    I've been thinking about this for fun... might be a dumb idea but... what if you did raycast chain to empty nodes parented to the player, one basically directly in front of his head, and one a few meters above? Sort of like an angler fish? The first raycast would hit the top empty node to see if there is an open space above, the second would cast downward from the first to check for the climbable surface? Epic diagram:

     
    SepM likes this.
  17. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    would that work?
    and would it need to be set as a trigger or colider?
    I just finnished a rig using linecast and nodes that are set in a cartwheel kinda way with 8 nodes that the linecast send to and it gives a good response to wall collisions but I'm still not happy with it.
     
  18. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    this is sort of where we are going with this but I want the ledge detection to have a radial response to the environment so that it checks ledges in front, behind and too the side with as little relyance on nodes and triggers as possible. also preferably little or no raycasting but i seem to be failing to that end.
    nice epic drawing by the way, I like the idea and may use the down check which i've been thinking of.
     
  19. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    Thanks, haha. But yeah, kinematic rigidbody + collider do not need to be triggers... they basically function similarly to triggers (in that don't actually respond to collisions, get pushed around or anything), but they still send collision messages with hit data.
     
  20. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    I've been messing with it trying to get a collision to display a debug when it hits something but it still gives no readout.
    this is what i'm using to register the collision throught the debuger
    Code (csharp):
    1. function OnCollisionEnter (collision : Collision)
    2. {
    3.     Debug.Log ("I see a wall in your future");
    4. }
    this on the other hand registers but requires 8 nodes and 8 linecasts on a rig to work

    Code (csharp):
    1. var node1 : Transform;
    2. var node2 : Transform;
    3. var node3 : Transform;
    4. var node4 : Transform;
    5. var node5 : Transform;
    6. var node6 : Transform;
    7. var node7 : Transform;
    8. var node8 : Transform;
    9.  
    10. function Update ()
    11. {
    12.     var hit : RaycastHit;
    13.    
    14.     if (Physics.Linecast(transform.position, node1.position) || Physics.Linecast(transform.position, node2.position) || Physics.Linecast(transform.position, node3.position) || Physics.Linecast(transform.position, node4.position) || Physics.Linecast(transform.position, node5.position) || Physics.Linecast(transform.position, node6.position) || Physics.Linecast(transform.position, node7.position) || Physics.Linecast(transform.position, node8.position))
    15.     {
    16.         Debug.Log ("Lookout For That Ohhhh!!");
    17.     }
    18. }
    plus its messy

    Edit: Ok sorry I added a rigidbody to the level geometry and it registered the collision but launched the level into space kryptonian prison system style :(
     
    Last edited: Aug 3, 2011
  21. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    Ugh, I forgot that for a collision event to occur, at least one of the two colliders has to be a non-kinematic rigidbody... so scratch that idea.

    As for your second example, thats begging to be an array.

    Code (csharp):
    1.  
    2. public var nodes : Transform[];
    3.  
    4. function Update(){
    5.     for(var node : Transform in nodes){
    6.         if(Physics.Linecast(transform.position, node.position)){
    7.             Debug.Log("Ledge detected.");
    8.         }
    9.     }
    10. }
    11.  
     
  22. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    Thanks,
    I Thought about an array but I've only used them a bit (mostly modification of aray scripts) and i expected them to be more complicated with raycast but evidently not.
    I was also planing to maybe cycle the raycast 1 per frame to lower overhead so it only casts 1 per frame.
    I'll play with this and post the progress when I either hit another snag or get it complete.
     
    Last edited: Aug 3, 2011
  23. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    Ok so I hit another snag for some reason the RaycastHit just won't work for me I noticed this with Debug.DrawRay earlier but now its an issue i'm trying to instantiate an object at the hit point of a ray but when i do the hit point is returning (0,0,0) regardless of how i do it.
    this is the code i'm using is this

    Code (csharp):
    1. var ledgeFinder : Transform;
    2. var nodes : Transform[];
    3.  
    4. function Update()
    5. {
    6.     var hit : RaycastHit;
    7.    
    8.     for(var node : Transform in nodes){
    9.         if(Physics.Linecast(transform.position, node.position))
    10.         {
    11.             Debug.Log("Wall detected.");
    12.         //Instantiate(ledgeFinder, hit.point, Quaternion.identity);
    13.         print(hit.point);
    14.         }
    15.     }
    16. }
    i've been over the docs but it just won't work for me this happens with a singe ray test as well as when i'm using multiple rays
    also the linecasts are registering collisions perfectly

    Edit: sorry i forgot to pass the hit to raycast my bad
     
    Last edited: Aug 3, 2011
  24. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133

    A similiar idea came to my mind. This should work.

    You should also raycast straight forward, then you would get distance and height of the ledge.
     
  25. ThatHomelessGuy

    ThatHomelessGuy

    Joined:
    Jun 28, 2010
    Posts:
    348
    Ok I have come across some unusual behavior in this script

    Code (csharp):
    1. var processing : boolean = false;
    2. var ledgeFinder : Transform;
    3. var nodes : Transform[];
    4.  
    5. function Update()
    6. {
    7. /*
    8.     if (processing)
    9.     {
    10.         Debug.Log("true man");
    11.     }
    12.     */
    13.     var hit : RaycastHit;
    14.    
    15.     for(var node : Transform in nodes)
    16.     {
    17.         if(Physics.Linecast(transform.position, node.position, hit))
    18.         {
    19.             //Debug.Log("Wall detected.");
    20.             //Instantiate(ledgeFinder, hit.point, Quaternion.identity);
    21.             processing = true;
    22.             //print(hit.point);
    23.             Debug.DrawLine (transform.position, hit.point, Color.red);
    24.         }
    25.             else
    26.             {
    27.                 processing = false;
    28.             }
    29.     }
    30. }
    31.  
    all the objects in the if statement function perfectly when any of the 8 rays pointed at nodes in the array registers a true for collision accept the
    Code (csharp):
    1. processing = true;
    the strangest part is it only toggles to true if the ray pointing at the last object in the array registers a true for collision.
    I think this may be a bug but i'm not sure has anybody any idea?
     
    Last edited: Aug 3, 2011
  26. Infinite_Gamer

    Infinite_Gamer

    Joined:
    Dec 6, 2013
    Posts:
    11
  27. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I can say it was done that way in Prince of Persia (manually placing them) and Assassin's Creed (a tool placing them automatically).

    Usually, those are lines. The player grab a collection of the nearest line, check those in his path, find the best result and act accordingly.
     
    metamorphist likes this.