Search Unity

layers, collision and one-way platforms (a question)

Discussion in 'Scripting' started by hiphish, Dec 23, 2010.

  1. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Hi

    I'm trying to make a 2D platformer, and a feature I want is one-way collisions. Basically this means that a creature (player, enemy, powerup, whatever) can pass through a platform from below without any interference, but when it collides from above the platform is rock solid.
    if you don't know what i mean, look at this video.

    At 1:05 he jumps though the grass but then he stands safely on top of it.


    Here is what it looks like in my project:

    The capsule is the player, and the sphere will be an enemy.

    There is a trigger slightly lower than the platform and it is a child object of the platform. The idea is to alter something about the platform before the player touches the collider.
    So here I jump, enter the trigger, which lets me pass the collider and once I leave the trigger, the collider is restored to its default and becomes solid.

    The obvoius thing to do would be to deactivate the collider somehow. However, this would also make the enemy fall right through it. If I had a separate collider for enemies and players, it would prevent bouncing enemies (such as the flying turtles from Mario) from passing the platform.

    So instead of altering the collider, I woud have to alter the object.

    Then I remembered a feature from Torque: Collision groups and layers.
    What does this mean? Each object has a layer (for rendering order) and a group. Any object could be made to interact only with specific groups and layers. So I could for example have the first ten groups only for stuff in the foreground, the last ten for collisions in the background and only a handful of groups for stuff in between, and nothing would interfere with each other.

    So here is my idea: Once the player, or any other object with such an ability, enters the trigger, its collision group is set to a certain group. The platform will never be able to have any collision with that group (by default, and nothing about it should be changed), so it should just ignore the object. Once the object leaves the trigger its group is reset to something normal again. So if I jumped onto the platform i can stand on it, and if I jumped back down I will safely land on the platform below, even if it is one-way as well.
    It is necessary to be able to only make specific platforms one-way, as there are actual ceilings supposed to exist as well.


    The big question is of course whether there is such a feature as collision groups and how to change them.

    The trigger itself works, I use a pretty simple script and get my response:
    Code (csharp):
    1. function OnTriggerEnter (jumper: Collider) {
    2.     //set jumper's layer to something that can pass through the platform
    3.     Debug.Log ("ping");
    4. }
    5.  
    6. function OnTriggerExit (jumper: Collider) {
    7.     //reset jumper's layer to something that the platform collides with
    8.     Debug.Log ("pong");
    9. }
     
    Last edited: Dec 25, 2010
  2. Vicenti

    Vicenti

    Joined:
    Feb 10, 2010
    Posts:
    664
    In your modeling program, create a copy of the mesh and do not provide a downward-facing face. You might also omit sideways faces. Use that copy as the collision mesh.

    Collision faces only block movement coming from the direction of their normal, so an upward-pointed face will prevent things from moving downward, but allow them to move upwards.
     
  3. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    So you are suggesting, to use a custom mesh, that only detects collision from above, instead of the box collider, on the platform? Okay, that would be even easier.
    Only problem: How do I get the Box Collider's mash out and inside Blender? And then make it like that? Well, I can ask that last question on some Blender forum, but I'd still need the mesh.

    I'm sorry, all I was doing with Unity so far only involved scripting and using the regular primitive shapes that come with the editor. The art (either 2D with Sprite Manager or 3D models) was supposed to come from an artist, after I got the gameplay skeleton finished.

    EDIT:
    There is still a problem with that technique: In games like Contra on NES the player can duck, then press down, and he'll fall through the one-way platform.

    at 3:08

    My idea looks like it would handle both jumping and falling through these platforms. When the player ducks, his collision group is set to the special one, and once he passes the platform, the trigger automatically restores it back to normal.
     
    Last edited: Dec 24, 2010
  4. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    PUSH PLZ HELP!!111!!!

    Nah, just kidding, I figured it out ;)

    So here is what I did, just in case someone digs up this thread:
    The setup is the same as above, except I adjusted the size of the trigger a bit:

    It goes so much to the side to make sure the player does not hit the edge. It's also higher to give the trigger enough time to react.

    Of course this leads to another problem: If the platform below is a one-way platform as well, the player falls right through that one even if not jumping.
    So intead of messing with layers, I used the Physics.IgnoreCollision() function which will only affect the trigger's parent, nothing else.

    For jumping down I wrote a second script which will set the player's layer to the special one and the trigger below will restore it back to normal. So the trigger can only restore layers, but not deviate from the default.


    So, here are the scripts:
    Code (csharp):
    1. function OnTriggerEnter (jumper: Collider) {
    2.     //make the parent platform ignore the jumper
    3.     var platform = transform.parent;
    4.     Physics.IgnoreCollision(jumper.GetComponent(CharacterController), platform.GetComponent(BoxCollider));
    5. }
    6.  
    7. function OnTriggerExit (jumper: Collider) {
    8.     //reset jumper's layer to something that the platform collides with
    9.     //just in case we wanted to jump throgh this one
    10.     jumper.gameObject.layer = 0;
    11.    
    12.     //re-enable collision between jumper and parent platform, so we can stand on top again
    13.     var platform = transform.parent;
    14.     Physics.IgnoreCollision(jumper.GetComponent(CharacterController), platform.GetComponent(BoxCollider), false);
    15. }
    This one goes onto the trigger. The trigger MUST be the platform's child and will ONLY affect its parent platform.

    Code (csharp):
    1. function Update () {
    2.     //tracks if the button combo for falling through is pressed
    3.     //usually in video games this is down + jump
    4.     if(Input.GetAxis("Vertical") == -1){
    5.          //the layer moving platforms cannot collide with
    6.         gameObject.layer = 9;
    7.     }
    8.     else{
    9.         gameObject.layer = 0; //default layer
    10.     }
    11. }
    This one is attached to the player, if such an ability is even desired (e. g. Mario cannot jump down, so no need there). It's just a proof of concept and it would probably need more refinement to implement it into a proper control scheme. Also the moving platforms MUST have a special layer for this and the collision under Edit-> Project Setting-> Physics must be deactivated for these layers.


    Messing with layers is only necessary if the player is supposed to be able to jump down.
     
    Last edited: Dec 24, 2010
    sonofbryce and dyrkabes like this.
  5. sonofbryce

    sonofbryce

    Joined:
    May 19, 2009
    Posts:
    111
    Just wanted to say thanks hiphish! This thread is 2 years old, but this solution helped me a ton! Thanks for sharing!
     
  6. JGriffith

    JGriffith

    Joined:
    Sep 3, 2012
    Posts:
    22
    Probably one of the better solutions I've found on this.

    I however went a much "lazier" route, as my situation is not as complex. Hehe.

    I simply check if the character's velocity is greater than 0(he is jumping) and use IgnoreLayerCollision on the platforms accordingly.

    Code (csharp):
    1.          
    2.                     // pretty dirty way to do this, hehe
    3.                     Physics.IgnoreLayerCollision(8, 12, (m_myRB.velocity.y > 0.0f));    
    4.  
     
    Last edited: Aug 15, 2013
  7. dbanfield

    dbanfield

    Joined:
    Apr 20, 2013
    Posts:
    8
    Lazy or not, that is a great solution :) Thanks JG!
     
    JGriffith likes this.
  8. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    in the prototype of the other brothers it was simple : there's pass through platforms and there's solid platforms. whenever the player jumps, the player's collision layer changes to solid only, and when the player is coming back down, the collision layer is is changed to pass through platforms. This is elegant, simple and always works with just 2 lines of code for everything. No special meshes needed.

    However, in the end I wrote my own custom collision and physics system (mostly because I wanted to).
     
    MrGreenish, dyrkabes and demid like this.
  9. POLYGAMe

    POLYGAMe

    Joined:
    Jan 20, 2009
    Posts:
    196
    I've been messing around with this too. I just made it if the player is higher than the platform, activate the platform's collider, if not, he can jump through it. Using tags it works well. I did this for a different reason, though... It was because when my character would jump and hit the side of a platform, it would "grab" onto it, not just fall... So I needed to turn the platform off. Added bonus (or negative side-effect) is that the player can jump up through the platform.
     
  10. 01010111

    01010111

    Joined:
    Apr 12, 2013
    Posts:
    2
    I wrote a pretty simple script that does the same thing - http://pastebin.com/KArtkV1E
     
  11. justmail0116

    justmail0116

    Joined:
    Sep 7, 2013
    Posts:
    4
    /*********************************************************
    * Author: Justin Williamson
    * CubeScript for simple pass through script.
    * Create your character two emptygameObjects and tag them ("TopTrigger")("BottomTrigger")
    * getComponent these emptygameObjects box colliders
    * check the isTrigger box for both box colliders
    * TopTrigger Should be the length of the gameObject "player" and the BottomTrigger
    * should be small and at the bottom of the gameObject.
    * getComponent your ground blocks rigidbodies and uncheck useGravity
    * drop this script into your ground blocks and poof, jump through blocks.
    * only error, hits his head on second jump first time. Small Glitch.
    * ********************************************************/

    using UnityEngine;
    using System.Collections;

    public class CubeTriggerScript : MonoBehaviour {

    void OnTriggerStay(Collider hit){
    if(hit.gameObject.tag == "TopTrigger"){
    rigidbody.collider.isTrigger = true;
    rigidbody.isKinematic = true;
    }
    }
    void OnTriggerExit(Collider hit){
    if(hit.gameObject.tag == "BottomTrigger"){
    rigidbody.collider.isTrigger = false;
    }
    }
    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
    }
     
    Last edited: Dec 1, 2013
  12. Watapax

    Watapax

    Joined:
    Sep 3, 2013
    Posts:
    34
    hello, I dealt with this problem recently and I came up with a simple solution. Just move the player collider when velocity.y is greater than zero in a position where not collide with the platform, and when velocity.y is equal or less than zero move the collider back in its original position. I illustrate this in a draw, "is side view". The enemy collider need to be larger than the platform collider if the player hits the enemy when is jumping.

    $onewayplatform.jpg
     
    dyrkabes and Pyrocidle like this.
  13. Khalanar_Dev

    Khalanar_Dev

    Joined:
    Mar 25, 2013
    Posts:
    34
    Justmail's approach is easy and nice, problem is if you've got an enemy right in the platform you are jumping to. He wont then kill you =) I'll go to hiphish's IgnoreCollision() to false platform-player. That way enemies can still kill you. Layers won't do when you have more than one player in scene but this Physics.IgnoreCollision( player, platform ) system seems to be 100% usable for having 2 players right?

    I love this post, so many interesting workarounds!!!
     
    JGriffith and kelvinc1024 like this.
  14. yuriythebest

    yuriythebest

    Joined:
    Nov 21, 2009
    Posts:
    1,125
    this solution is brilliant!!!!! You are a genius!!!
     
    JGriffith likes this.
  15. HessianForHire

    HessianForHire

    Joined:
    Mar 11, 2014
    Posts:
    2
    Hi, I know this post is old but I found a solution to justmail0116's glitch.

    so first change

    void OnTriggerExit(Collider hit){
    if(hit.gameObject.tag == "GroundCheck"){
    rigidbody.collider.isTrigger = false;
    }
    }

    To

    void OnTriggerEnter(Collider hit){
    if(hit.gameObject.tag == "GroundCheck"){
    rigidbody.collider.isTrigger = false;
    }
    }

    then add this after the OnTriggerEnter method.

    void OnTriggerExit(Collider hit){
    if(hit.gameObject.tag == "GroundCheck"){
    rigidbody.collider.isTrigger = true;
    }
    }



    This prevents your characters head from "bumping" into the platform the first time you try to jump through.
    This also resets the platform so you can jump through it as many times as you want without "bumping" into it.
    Hope this helps :)

    Not bad for a novice programmer eh? :]
     
  16. Zaflis

    Zaflis

    Joined:
    May 26, 2014
    Posts:
    438
    Well, this problem is really difficult as far as i can see. I have a few requirements for the algorithm:
    1) Platform is a near flat BoxCollider in any angle. You can't just analyze the Y axis, but treat the issue as Vector2D if necessary.
    2) All moving entities must be able to hit and pass through the platforms, not just player.

    I used to use simple dotproduct calculation to set collider active when player is above the platform with script in question, but then they may shut off when monsters are walking over them.

    This is becoming a big mess overtime, and i think i may have to rethink the approach entirely. Might have to make 2 subobjects inside the main one, invisible at runtime but mark the surface level vector with 2 "points". It's rather difficult to retrieve from box collider, although possible too.

    Random code i got so far, it's not working:
    Code (CSharp):
    1. public class Platform : MonoBehaviour {
    2.  
    3.     BoxCollider2D thisColl;
    4.     Vector2 platformAxis;
    5.     Vector2 frontAxis;
    6.  
    7.     void Start () {
    8.         thisColl = GetComponent<BoxCollider2D>();
    9.         thisColl.isTrigger = true;
    10.  
    11.         float angle = (transform.rotation.eulerAngles.z+90) * Mathf.Deg2Rad;
    12.         platformAxis = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
    13.         frontAxis = new Vector2(platformAxis.y, platformAxis.x);
    14.     }
    15.  
    16.     void OnTriggerEnter2D(Collider2D coll) {
    17.         GameObject entity = coll.transform.parent.gameObject;
    18.         Vector2 velocity = entity.rigidbody2D.velocity;
    19.  
    20.         //if (Vector2.Dot(coll.transform.position - transform.position, platformAxis) >= 0) {
    21.         if (Vector2.Dot(entity.rigidbody2D.velocity, platformAxis) <= 0) {
    22.             Debug.Log("hit");
    23.             //Debug.Log(entity.name);
    24.             Debug.Log(velocity);
    25.  
    26.             //entity.transform.position -= (Vector3)velocity;
    27.             //entity.transform.position += (Vector3)(platformAxis *
    28.             //    ( Vector3.Distance(entity.transform.position, transform.position) ));
    29.             //entity.transform.position -= new Vector3(0, velocity.y, 0);
    30.  
    31.             //entity.rigidbody2D.velocity += //new Vector2(velocity.x, 0);
    32.             //    platformAxis * velocity.magnitude*0.1f;
    33.  
    34.         } else {
    35.             Debug.Log("pass through");
    36.         }
    37.     }
    38.  
    39.     void OnCollisionEnter2D(Collision2D coll) {
    40.         //coll.
    41.         /*if (Vector2.Dot(coll.transform.position - transform.position, platformAxis) < 0) {
    42.             Physics2D.IgnoreCollision(thisColl,    coll.collider, true);
    43.             coll.gameObject.rigidbody2D.velocity -= 2*coll.relativeVelocity;
    44.             Debug.Log("pass through");
    45.         } else
    46.             Debug.Log("hit");*/
    47.     }
    48.  
    49.     void OnCollisionExit2D(Collision2D coll) {
    50.         Physics2D.IgnoreCollision(thisColl,    coll.collider, false);
    51.     }
    52.  
    53.     /*Transform player;
    54.     BoxCollider2D coll;
    55.    
    56.     void Start () {
    57.         GameObject playerObj = GameObject.Find("Monty");
    58.         if (playerObj != null) player = playerObj.transform;
    59.         coll = GetComponent<BoxCollider2D>();
    60.     }
    61.  
    62.     void Update () {
    63.         if ((player != null) && (coll != null)) {
    64.  
    65.             float angle = (transform.rotation.eulerAngles.z+90) * Mathf.Deg2Rad;
    66.             Vector2 axis = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
    67.  
    68.             coll.enabled = Vector2.Dot(player.transform.position - transform.position, axis) >= 0;
    69.  
    70.             // DEBUGGING
    71.             //Debug.DrawLine(transform.position, transform.position+
    72.             //               new Vector3(axis.x, axis.y, 0));
    73.             //GetComponent<SpriteRenderer>().enabled = coll.enabled;
    74.         }
    75.     }*/
    76. }
     
  17. Limeoats

    Limeoats

    Joined:
    Aug 6, 2014
    Posts:
    104
    While I like the simplicity of this solution, is not true that if your jump peaks while you are inside the platform and velocity.y changes to 0, won't the collision turning back on mean that the player will get stuck inside the platform (or, well, pushed up out of it if that's how your collision works)?
     
  18. Zaflis

    Zaflis

    Joined:
    May 26, 2014
    Posts:
    438
    Player will most likely be pushed up.

    I have another code here, which is simple in theory. Have 1 trigger and 1 solid collider for platform. Trigger is a bit bigger box placed under the main collider. Too bad it's not working... player still hits the platform for some reason. Next i might look into collision layers.
    Code (CSharp):
    1.     Collider2D thisColl;
    2.  
    3.     void Start () {
    4.         Component[] colliders = GetComponents<BoxCollider2D>();
    5.         // Find the solid collider within gameobject
    6.         foreach (BoxCollider2D coll in colliders) {
    7.             if (!coll.isTrigger) thisColl = coll.collider2D;
    8.         }
    9.     }
    10.  
    11.     void OnTriggerEnter2D(Collider2D coll) {
    12.         Physics2D.IgnoreCollision(thisColl, coll.collider2D, true);
    13.     }
    14.  
    15.     void OnTriggerExit2D(Collider2D coll) {
    16.         Physics2D.IgnoreCollision(thisColl, coll.collider2D, false);
    17.     }
    Reason this is not working is very likely that my player is trying to simulate capsule shape with 2 circles and 1 box colliders. So when the first circle at head turns off it bounces back off.
     
    Last edited: Aug 26, 2014
  19. Zaflis

    Zaflis

    Joined:
    May 26, 2014
    Posts:
    438
    Ok, the end solution is finally found that meets all the demands. Use default layer (0) for normal walls, and 8 for platforms, i name it Platform. Layer 9 called GoThrough. Basically, when entity velocity.y < 0 (going down), set layer 0, and if it's > 0, set it 9. That's all fine with players and monsters, with platforms in almost any angle from -45 to 45 degrees.

    But then you discover that when entity start going down while it's in the middle of platform, the collider solidifies and player suddenly jumps to either nearby side, up or down of platform. To combat that i make a new subobject for platform object and give it a trigger box collider, slightly under the platform. Set this trigger in GoThrough layer! This is to check that layer value is not changed while trigger is collided with. It has to be in subobject, because if it was in platform object, having platform layer would make trigger never activate.

    Long story short, here is code to place for all entities like players and monster scripts. I am assuming that entity may be combination of multiple colliders, not just 1 capsule (which i hope will someday be implemented for 2D aswell):

    Code (CSharp):
    1. int inPlatform = 0;
    2. GameObject colliders;
    3.  
    4. void Start () {
    5.     // Colliders are in childobject named Colliders
    6.     colliders = GameObject.Find(gameObject.name+"/Colliders");
    7. }
    8.  
    9. void Update () {
    10.     Vector2 vel = rigidbody2D.velocity;
    11.     // Change collision layer for go-through platforms
    12.     if (inPlatform <= 0) {
    13.         inPlatform = 0;
    14.         int newLayer = colliders.layer;
    15.         if (vel.y >= 0) {
    16.             // Only set GoThrough layer if upwards speed is slightly under jump speed
    17.             // Prevents bug where player drops through angled platform when walking over it
    18.             if (vel.y > 3.0f) newLayer = 9;
    19.         } else newLayer = 0;
    20.         if (colliders.layer != newLayer) colliders.layer = newLayer;
    21.     }
    22. }
    23.  
    24. void OnTriggerEnter2D(Collider2D coll) {
    25.     if ((coll.gameObject.layer == 9) && (colliders.layer == 9)) {
    26.         inPlatform++;
    27.     }
    28. }
    29.  
    30. void OnTriggerExit2D(Collider2D coll) {
    31.     if (coll.gameObject.layer == 9) {
    32.         inPlatform--;
    33.     }
    34. }
    And finally go to Edit -> Project settings -> Physics 2D, and disable collision between layers GoThrough and Platform.
     
    Last edited: Aug 27, 2014
  20. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    I'm just starting out w/ Unity, but tell me if this concept is workable:

    • All "normal" fully-solid scenery colliders are on layer 1
    • Jumpthrough colliders are on layer 2
    • Player ignores ALL layer colliders on layer 2 (by default) while in the air
    • While in air, player raycasts from his feet upward to see if he's colliding with a collider from Layer 2, and if he is, he does nothing else but continue jumping/falling and ignoring colliders from layer 2 (to prevent the head from getting stuck in geometry for layer 2)
    • However, if while in the air, and he's NOT colliding with anything from layer 2, while jumping up we cast rays from his feet to down below his feet a short distance (i.e. the length of his current falling speed ) checking for layer 2 colliders (perhaps with multiple rays casting downward from his feet, consisting of rays across the width of his body)
    • if a collider is found on layer 2 colliding with those downward rays from his feet at that position, stop ignoring collisions w/layer 2
    • start ignoring collisions with layer 2 again when jumping up OR when no collider from layer 2 is found beneath the player's feet anymore (i.e. since the player is moving upwards and only the upward rays are being cast atm, which disables the need to cast downward rays until the player is falling again -- but once the player starts falling again, it checks once more w/upward rays from feet to top of player's head to see if the player is colliding w/layer 2 colliders, and if not, it checks below for a collider -- otherwise, once again, the player ignores layer 2 colliders)
    • also, if the player is "onGround" the vertical rays are disabled when upward speed is 0 to prevent falling through jumpthrough/one-way platforms you're already on

    Do you think this is workable for jumpthrough geometry without the need to setup special trigger areas?
     
    Last edited: Oct 11, 2014
  21. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
    I know this is an old thread, but figured I'd throw in my two cents as well. I ran into the exact same problem @Zaflis was, namely:
    However, I found all of these collision layers to become a bit unwieldly, so here is another solution for the pile, this one making use of a delegate system (and Unity 4.6's UnityEvents). The idea is that when the player goes from jumping to falling, each platform gets notified and checks whether to become "solid" or not based on whether or not it's intersecting the player already.

    Anywhere (i.e. Utils.cs):
    Code (csharp):
    1. public class BoolEvent : UnityEvent<bool>{};
    Player.cs:
    Code (csharp):
    1. new public static BoxCollider2D collider;
    2.  
    3. void Awake(){
    4.     collider = GetComponent<BoxCollider2D>();
    5. }
    6.  
    7. void Update(){
    8.     if(jumping && velocity.y < 0f){ //or whatever logic you have running here
    9.         World.SetPlayerPlatformCollision(true);
    10.     }
    11.     else if(!jumping && Input.GetButtonDown("Jump")){
    12.         World.SetPlayerPlatformCollision(false);
    13.     }
    14. }
    World.cs:
    Code (csharp):
    1. public static BoolEvent OnTogglePlayerPlatformCollision {get; private set;}
    2.  
    3. static World(){ //or Awake()/ Start() if using an instanced MonoBehaviour
    4.     OnTogglePlayerPlatformCollision = newBoolEvent();
    5. }
    6.  
    7. public static void SetPlayerPlatformCollision(bool collide){
    8.     OnTogglePlayerPlatformCollision.Invoke(collide);
    9. }
    Platform.cs:
    Code (csharp):
    1. new BoxCollider2D collider;
    2.  
    3. void Start(){
    4.     collider = GetComponent<BoxCollider2D>();
    5.     World.OnTogglePlayerPlatformCollision.AddListener(OnTogglePlayerPlatformCollision);
    6. }
    7.  
    8. void OnTogglePlayerPlatformCollision(bool collide){
    9.     collider.enabled = collide && !collider.bounds.Intersects(Player.collider.bounds);
    10. }
    Hope this helps someone!
     
    Last edited: Feb 10, 2015
  22. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    That's awesome you want to help, but the issue with this method is that it works only for players -- not a second player, or enemies, or other sorts of things that might want to use one-way platform physics.

    Any creative solution to this problem would be most welcome.

    I'm all about tossing unwieldy methods out the window.
     
  23. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Since I got a notification about this thread I might as wall add my opinion now that I have more experience with Unity: the best solution seems to be a one-faced collider like the first answer said. However, I would advise against using 3D modelling and do it programmatically instead. The advantage is that you write the script once and you get the right mesh for any size of block. Creating meshes through code is really not hard, here is tutorial:
    http://blog.nobel-joergensen.com/2010/12/25/procedural-generated-mesh-in-unity/

    A one-faced mesh collider can only detect collisions in one direction, making it ideal. As for dropping down, that's where I would still use the Physics.IgnoreCollision method. Re-activate collisions after five frames or so and it should be safe to use. Or, re-activate collision once the actor's Y-velocity becomes greater or qual zero, because that's when the actor has stopped falling (this might be the case for example if a player bounced off an enemy and as a result was thrown back on the platform it dropped through; you wouldn't want the player to not be able to land on the platform). You can also re-active collision when the actor attempts to fall through another platform because then it is no longer on the previous platform.
     
    shakenbake444 and JGriffith like this.
  24. r0gerg

    r0gerg

    Joined:
    Mar 24, 2014
    Posts:
    2
    I implemented recently this using raycasts but is not a good idea at all since Raycast is an expensive operation. I post it anyway if helps someone.

    - I used 2 empty game objects: footChecks and headChecks under the foots and on the head of the player respectively.
    -Each check has multiple children at different locations but at the same height.
    -Positions the checks at a sufficient distance ahead and behind the character so that when you go fast you can spot the platform.
    -Raycast using layerMask from each of check's children to only hit the platform layer.
    -Use a small distance in the Raycast so you can walk between the platforms.

    Code (CSharp):
    1. void Update(){
    2.     if (IsOnPlatform ()) {
    3.            print ("On Platform");
    4.            ChangeLayersOfChildrens (transform, "Default");
    5.    } else if (IsUnderPlatform ()) {
    6.            print ("Under Platform");
    7.            ChangeLayersOfChildrens (transform, "PassThroughPlatfrorms");
    8.     }
    9. }
    Code (CSharp):
    1. private bool IsNearPlatform(Vector3 direction, Transform[] checkers){
    2.         float smallDistance = 0.05f;
    3.        //Layer
    4.         int platformLayer = LayerMask.NameToLayer("Platform");
    5.         //Only hit the platform layer
    6.         int layerMask = 1 << platformLayer;
    7.         foreach (Transform check in checkers) {
    8.             RaycastHit hit;
    9.             if(Physics.Raycast(check.position, direction, out hit, smallDistance, layerMask)){
    10.                 return true;
    11.             }
    12.         }
    13.         return false;
    14.     }
    15.  
    Code (CSharp):
    1. private bool IsOnPlatform(){
    2.         return IsNearPlatform (Vector3.down, footCheckers);
    3.     }
    Code (CSharp):
    1.     private bool IsUnderPlatform(){
    2.         return IsNearPlatform (Vector3.up, headCheckers);
    3.     }
    Code (CSharp):
    1. public static void ChangeLayersOfChildrens(Transform trans, string name)
    2.     {
    3.         trans.gameObject.layer = LayerMask.NameToLayer(name);
    4.         foreach(Transform child in trans)
    5.         {    
    6.             ChangeLayersOfChildrens(child,name);
    7.         }
    8.     }
    9.  
     
    Last edited: Feb 26, 2015
  25. JGriffith

    JGriffith

    Joined:
    Sep 3, 2012
    Posts:
    22
    Hey, I read the forums a lot but never log in to post. Did today and some stuff brought me back here.

    I do indeed like some of the other solutions, and frequently use custom meshes for stuff like this as well.

    To answer your question, it has not been a problem, mainly because our colliders on the platforms are rather "skinny". But yes, this could be a buggy solution, it just happened to work fine in our case at the time.

    We've also done an entire character movement/ground collision system based on raycasts, which yea are expensive, but not THAT expensive to do like 2 per frame.
     
  26. Cosmonausta

    Cosmonausta

    Joined:
    Dec 22, 2013
    Posts:
    1
    I'm having some trouble with implementing this. I've managed to work my way through some errors, but I'm stuck on "OnTogglePlayerPlatformCollision = newBoolEvent();" newBoolEvent() isn't defined anywhere.

    Would you mind elaborating on this set up?
     
  27. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
    Of course! I defined BoolEvent earlier in my post. ;)

    Code (csharp):
    1. public class BoolEvent : UnityEvent<bool>{};
    For some reason you can't use UnityEvent<T> as a generic directly, so I just made an empty subclass of it. Hope that helps!

    EDIT: I just noticed I'd typed "newBoolEvent()" – that should of course be "new BoolEvent()". =)
     
  28. qqqbbb

    qqqbbb

    Joined:
    Jan 13, 2016
    Posts:
    113
    You don't need scrips to make one way platform. Watch this video at minute 9.
     
    Last edited: Jul 17, 2016
  29. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
    This will save me so much time in the future; I had no idea that existed. Thanks for sharing!
     
  30. Zaflis

    Zaflis

    Joined:
    May 26, 2014
    Posts:
    438
    Instead of having to watch the whole video, TLDR - Platform Effector 2D. Also check the Used By effector on the collider of the platform.
     
  31. maxizrin

    maxizrin

    Joined:
    Apr 13, 2015
    Posts:
    20
    SOLVED
    Since this is the second thing that pops up when this topic is searched, and the first thing links here as well, I will leave the solution here.

    Unity has a built in component for this now: PlatformEffector2D.
    Attach it to your object, set the angle that allows collision (depending on whether you want things to collide with the side of the platform), mark it as "one way", and of course, in your 2D collider: check the box that says "Used by Effector".

    When using tile maps, or multiple colliders that you want to combine (for efficiency), add a CompositeCollider2D, and set the "Used by Effector" there instead of in collider, which will have "Used by composite" checked.
     
    tfishell likes this.
  32. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    Might want to mention how slopes work with this approach to "solve" the one-way platform issue properly.
     
  33. maxizrin

    maxizrin

    Joined:
    Apr 13, 2015
    Posts:
    20
    The Effector has an angle of effect, that can be adjusted as needed, it's very intuitive, so I didn't think it was worth a mention.
    Should work for slopes just as well, and also has an option to adjust the sides.
     
    awesomedata likes this.
  34. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    Understandable -- it's just always important to remember that there are lots of things that seem inconsequential to us that new users looking for an answer tend to not have any idea how to approach.

    For example, how does one move up a one-way slope properly without sliding back down or losing momentum?

    This is a tough problem for a new user -- even if there's a tool / component out there that gets them 80% there, they still have some manual-labor to do in setting up the physics to manage this. They may not have any clue how to even start on this part.

    Thanks for mentioning this stuff though. It gives newer users the terminology to start looking for their own answers.
     
    shakenbake444 likes this.
  35. codyf112

    codyf112

    Joined:
    Feb 21, 2018
    Posts:
    4
    Apologies for digging up an old thread, but I'm trying to implement something like what you said here in my own project using Tilemaps. For some reason the collision matrix is not applying to the tilemap as a whole and this whole thing does not work. I would imagine that it should work on a non-tilemap gameobject, and this is an issue with tilemapCollider2D (IgnoreCollision doesn't work with tilemap colliders for some reason either since TilemapCollider2D can't be casted to a Collider2D even though it inherits from it *shrugs*). Anyways, if anyone knows how to work around this, please let me know. If I end up working out a solution I will post here
     
  36. codyf112

    codyf112

    Joined:
    Feb 21, 2018
    Posts:
    4
    Wow, I was using Physics and not Physics2D...IgnoreCollision() is in fact the proper solution here!
     
  37. th3hatb0y

    th3hatb0y

    Joined:
    Feb 18, 2020
    Posts:
    4
  38. MrGreenish

    MrGreenish

    Joined:
    Oct 20, 2019
    Posts:
    34
    This is brilliant, thanks. I checked if the y velocity on the rigidbody was greater than 0. If it was I turned of collision with between the player layer and the platforms, else collision is on.
    Code (CSharp):
    1. // If player is moving up, ignore collisions between player and platforms
    2.         if (myRigidBody.velocity.y > 0)
    3.         {
    4.             Physics2D.IgnoreLayerCollision(8, 10, true);
    5.         }
    6.         //else the collision will not be ignored
    7.         else
    8.         {
    9.             Physics2D.IgnoreLayerCollision(8, 10, false);
    10.         }
     
  39. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    But does it work for TWO players... mwahahaha...
     
  40. shakenbake444

    shakenbake444

    Joined:
    Jul 23, 2019
    Posts:
    21
    this just takes you learn.unity.com. Can you post the YouTube address if applicable?