Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Top Down Terrain Effects (Grass, Ice, Sand etc.)

Discussion in '2D' started by OpaIon, Nov 6, 2022.

  1. OpaIon

    OpaIon

    Joined:
    Oct 28, 2022
    Posts:
    5
    Hello,

    I just recently started out with using Unity and am currently developing a simple Top Down 2D Car Racing game. My car movements etc work just fine, and now I created a track editor according to a tutorial that uses Bezier curves to create nice and smooth tracks which are easily adjustable.(A picture of an example track is attached to this post)

    Now, I want my car to behave differently on different materials/surfaces/terrains underneath it. So while driving should behave normally on the track, when off-track grass/sand e.g. should slow down the car, and ice should make it slide/decrease friction.

    I have already done a lot of research regarding this topic, but have found no practical/simple solution to this. Overall I came up with multiple principal ideas/solutions, which I tried out (To various degrees of failure) but non are ideal.

    Idea 1: First, I wanted to use 2D Physics Materials that I wanted to apply to my track or rather to its Mesh collider. However my car only is able to actually collide, but not able to drive on the track anymore (So 2D Physics Materials are not useful for Top Down terrain, but only for objects with which one actually can collide).

    Idea 2: Another solution I had was to instead manually create a Polygon 2D collider surrounding my track, and a box collider for the rest of the map (But overlapping the Polygon 2D collider), then set my Polygon 2D collider to isTrigger and use OnTriggerEnter2D (And matching to varying tags according to materials) to adjust my max speed e.g.. While something like that works, it is very impractical and imprecise, due to me needing to manually fit/edit this Polygon 2D collider to my very smooth track mesh. Perhaps something similar could work by shaping the Polygon 2D collider (or two Edge-colliders combined via a composite collider) via script to match my track (Matching the colliders to the vertices of my track), but I am unsure how this can be achieved exactly.

    Idea 3: Using Collider.bounds to get the bounds of my Meshcollider and calculate if my car is within these bounds, and if yes/no then apply different effects. This method I have not yet tried out, do you think it could work?

    Idea 4: Finally, I wondered if it may be possible to do some raycasting/boxcasting from the player car to the terrain it touches/drives on and by that somehow determine the material it is driving on, and to then adjust speed/parameters based on the terrain type. However, raycasting only works when the start position of the ray does not lie within the collider it is supposed to detect, so this also does not work.

    Seeing that there are so many racing games out there which have the described mechanics/effects of grass and other materials affecting the car movement, there probably is some easy solution to this which I am just failing to see.

    Again, none of my solutions were really practical, so I appreciate any help/suggestions you may give me!

    Thank you!
     

    Attached Files:

  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,954
    I prefer this approach. It presumes that each mesh chunk (eg, on a per-GameObject basis) is all the same.

    This means you would have meshes of grass, meshes of concrete, but never meshes of both.

    This lets you trivially use a physics overlap (or raycast) to find what mesh you're colliding with, then use GetComponent<T>() on the same object to see if there is a MonoBehaviour on that GameObject that would tell you what the surface is like.

    That MonoBehaviour could just have an enum picklist, or it could have a ScriptableObject slot that has more information and can be globally changed.

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. // @kurtdekker - cheeseball way of encoding surface properties
    4. // put one of these on each collider, fill out the values,
    5. // make your code pick it up by raycasting / overlapping and
    6. // then using GetComponent<T>();
    7. //
    8. // Even better: use a ScriptableObject instance with this
    9. // info, and slot it into a MonoBehaviour!
    10. public class TractionProperty : MonoBehaviour
    11. {
    12.     public float RollingResistance = 1;
    13.     public float SlidingResistance = 2;
    14. }
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,620
    To be clear here, you're saying you want to raycast to see what you're already overlapping but it won't work. It will, it'll return the point at the start of the ray which is overlapped which is perfectly valid and correct, just like the rest of the ray so it is working, it's just that you are choosing the wrong tool for the job. In this case, you'd do an OverlapPoint. What you're actually asking for is to see what collider you're overlapping which is what Rigidbody2D.OverlapCollider is for.

    That said, you mention physics materials to control friction but that would make no sense because that is for contact friction. You'll not have contacts here because it's 2D and not 3D (you cannot have overlapping contact friction). You cannot simply ignore that it's 2D. Yes, you could make your car Kinematic and turn on Rigidbody2D.useFullKinematicContacts so you get contacts but all that will do is identify which collider and a single physics material. This isn't a good use of physics in my opinion.

    So in the end, this is a spatial data problem that needs to be authoring friendly. There's nothing out of the box to do this just like there's no spline tracks out of the box. What you need to do is alongside generating the rendering for your tracks, is that you also need to create collider sections (triggers) that you can then get a callback on (OnTriggerEnter2D). The GameObject that this collider on can have a component for traction data similar to what Kurt said above.
     
  4. OpaIon

    OpaIon

    Joined:
    Oct 28, 2022
    Posts:
    5
    Thank you both for the replies, I have not hear of Physics.Overlaps before, this seems like it could be a good solution. At the moment, the exact specifics of how to apply it may still be a bit beyond my skills, but I will look up as much as I can regarding this topic.

    I have found a video that explains how to use a Polygon Collider (set it to isTrigger) attached to the player, and then use the OverlapCollider to check the ground type, this would be a good start. That way I can also check for overlaps only in specific layers, so I can even put multiple layers on top of each other but always give my track/street a priority basically/make it so that if an overlap with the tracks collider is detected the code for grass, sand etc wont execute.

    The only problem here would be that my car would drive at full speed if just one small part touched the track and the rest was on the grass.

    I think I will try to use a polygon collider that is basically an inverse of my track mesh collider (Doing something like described in here):

    https://forum.unity.com/threads/can-i-do-a-2d-collider-operation-2d-collider-subtraction.782087/

    And then use the OverlapCollider.

    If I can't get it to work, I will try to practice Unity by doing even simpler projects, perhaps this was a bit much for my first try.

    Thanks again!
     
    Last edited: Nov 6, 2022
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,954
    Sure, if you wrote it that way.

    You could sense with more than one overlap (perhaps located on the car corners or wheels too?) and decide how to handle it:

    - move at the average of all speeds?
    - move at the fastest?
    - move at the slowest?
     
  6. OpaIon

    OpaIon

    Joined:
    Oct 28, 2022
    Posts:
    5
    Thanks!

    Yeah, thats probably a great way.

    I could place small box colliders over all 4 wheels, and then (Even though my track with the mesh collider is basically on top of the grass with the box collider spanning the whole map) if e.g. the front left wheel overlaps the grass but not the tracks mesh collider anymore, I can reduce my speed by a certain factor, for 2 wheels even more etc. This could maybe even be done by setting my wheels box colliders to isTrigger, and upon them leaving the track OnTriggerExit2D is used by each wheel etc and would probably be quite similar in effect. And I can do something like that for all of my different materials too using different layers and different tags that can be detected.

    I will try to implement this, and while I may be in over my head, at least it will be a nice exercise.

    Thanks again for helping a beginner out!
     
  7. karliss_coldwild

    karliss_coldwild

    Joined:
    Oct 1, 2020
    Posts:
    530
    You might even take it a few steps further. By applying forces to the car at each wheel positions instead of center of car you could potentially simulate more car physics elements. Even in big budget 3d games are often "cheating" with regards to car physics by having abstract model of forces and friction involved and then apply only resulting forces, instead of modeling individual car parts in the general purpose physics engine and hoping it will just work out. Unity itself has has special wheel collider (for 3d physics) instead of simply attaching spinning cylinders with rubber material to car. All that "cheating" mostly comes to 3 factors: relatively high simulation step size (10-30ms = 30-100Hz ), computational cost of simulating various car parts with sufficiently high detail (in many cases non rigid bodies) to the point where various effects appear naturally (stuff like tires deforming, fluids within suspension causing dampening effect, drag and downforce based on aerodynamics, complex mechanical linkages, ...), making it easier to adjust the the game feel and keep it fun while still looking believable and not loosing other aspects of car physics. The complexity of model and which effects are achieved by direct simulation vs abstract curves will depend and the type of game. But even relatively arcady racing games might have much more complex car model than you might expect, unless you are into racing games.
    You can find a videos with a bunch of GDC talks how they modeled car physics and also many of the Unity car physics tutorials. A lot of that math is equally applicable to 2d top down car game as it is to 3d.
     
  8. OpaIon

    OpaIon

    Joined:
    Oct 28, 2022
    Posts:
    5

    Thank you for the reply and suggestions!

    I originally thought about doing something like that, also utilizing Ackermann steering, tyre degradation that is affected by drifting (calculating forward vs sideways velocity and at very high sideways velocity/when the car is drifting tyre deg should be increased) and braking (if you brake very hard/decelerate fast, the tyre degradation would be high e.g.) amongst other things, but seeing that this is my first project ever (With not previous C# or any programming language, I am learning it as I learn to use Unity too) I think this is still to complicated for me, and my code and whole Unity project is probably still poorly optimized.

    When I get better, I want to try it with more complex physics, but so far I am limiting the scope of my project to relatively simple stuff.
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,620
    Not trying to make it more complex for you but also consider that the PolygonCollider2D turns your paths into primtive polygon shapes by using the Libtess2 (C++) library of which there's a C# port. The reason I mention this is that I created a CustomCollider2D which provides you direct (and super fast) access to create your own collider geometry composed of as many of the 4 primitive shapes that exist in the physics engine (Polygon, Circle, Capsule and Edge). If you have a mesh of (say) triangles for instance, you could simply upload those as 3-vertex polygons not even needing the libtess2 stuff.

    Whilst I appreciate this is another step-up in terms of complexity, with a basic understanding it'd provide you a simpler and direct access to create the shapes you need. It'd also be the best performance-wise.

     
    Kurt-Dekker likes this.
  10. OpaIon

    OpaIon

    Joined:
    Oct 28, 2022
    Posts:
    5


    After a lot of research and watching tutorials + trying out different things, it works now, in large parts thank to all of you guys suggestions!

    The last problem I faced was that mesh colliders are not able to interact with 2D Physics like Collider2D.OverlapCollider somehow (at least for me), so the mesh collider was useless. However, I found a very nice script that did a good job of using my Mesh Renderer to approximate a Polygon2DCollider based on this mesh.

    So now I can create tracks using splines/Bezier curves that will at the same time automatically create fitting Polygon2DColliders (Which are also more computationally efficient than mesh colliders) and via setting it to a specific layer (As I will my grass, sand etc. other materials) and using Collider2D.OverlapCollider I am now able to do exactly what I wanted/described in my question (Changing reactions of my car to the ground below.

    Thanks again for all your help!
     
    Kurt-Dekker likes this.
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,620
    Yes, because MeshColliders are 3D physics. The two physics systems have nothing to do with each other and certainly don't interact.

    Glad you got it working.