Search Unity

Wreck Arena - Besiege-inspired building/fighting game

Discussion in 'Works In Progress - Archive' started by MuckSponge, Jul 11, 2015.

  1. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    wreck-arena-logo.png

    A physics based building/fighting game inspired by Besiege.

    Build machines out of a vast range of blocks including motors, pistons, wheels and much more, and fight against other machines with your creations in tournaments. Unlock new parts by completing challenges.


    I'm hoping to release this game as early access next year.

    Download 0.1.0 pre-alpha

    IndieDB | itch.io

    Release notes:
    • First public release!
    • No sound yet
    • One arena available
    • No challenges, all parts unlocked

    Latest update

    Arena added, new menus, new name and logo!

    new-menu.jpg
    arena-4.jpg
    settings.jpg


    Feedback

    I'd love to hear your feedback about the game, whether it be suggestions for new parts, new game ideas, more videos, or some constructive criticism, thanks :)
     
    Last edited: Aug 30, 2015
  2. Chris3Design

    Chris3Design

    Joined:
    Aug 11, 2011
    Posts:
    115
    So this is basically like besieged but in the future and with more fighting?

    Got ninjad by the title.. I'll read better next time :(
     
    Last edited: Jul 11, 2015
  3. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    It uses very similar building mechanics and there will be an element of Besiege-style gameplay in the challenges (which will essentially be small, individual levels where you must accomplish a goal). The big difference is the focus in this game is on machines fighting each other, not the player's machine fighting the environment.
     
  4. Serinx

    Serinx

    Joined:
    Mar 31, 2014
    Posts:
    788
    Looks like a really solid foundation for a good game. Be interesting to see where this goes. Keep it up!
     
  5. slava_pankratov

    slava_pankratov

    Joined:
    Jan 17, 2015
    Posts:
    47
    Hi! Please add to asset store. Thanks! :)
     
  6. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    Thanks! :) Definitely don't intend on dropping this any time soon. I work on it around 6 days a week.

    This probably isn't a very good fit for the Asset Store because it is a game. It's very specialised and probably wouldn't be of much use to other developers as it is. I could potentially strip out the building mechanics as a useful asset but right now I'm putting all of my time and energy into developing the game further.

    The building mechanics are pretty simple anyway. In case you're interested, each part has a number of anchors that point in the desired attachment direction. When you click on an anchor it places a new part at the anchor in that direction and all of the physics joints are generated at run time by traversing over all of the parts and their anchors and comparing positions part information to determine what part is connected to what and what kind of joint it uses. Machines are saved to an XML format and basically just the positions and rotations of the parts are stored because the joints are essentially a post-process step. There's a lot more to it than that (most of the code is for handling player and AI input and behaviours for controllable parts), but that's the gist of it.

    I'll post another update soon. Got some nice AI improvements to show off :)
     
  7. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    Visual style

    A bit of progress as far as the (environment's) visual style is concerned. I'm going with across between Besiege's super smooth style and a traditional skybox.

    new-style.jpg

    I'm using a custom made procedural skybox shader which generates just the horizon, sky, and sun. The horizon is a very slight teal colour while the sky is a darker blue. I'm using fog to blend between the ground and the horizon, similar to what I believe Besiege does. Obviously it's not terribly realistic so I'm using a gradient for the ambient source with a much darker, warmer ground colour, which gives the materials some pop.

    new-style-2.jpg

    new-style-3.jpg

    Main menu

    What else have I been working on? I've got a very simple arena but that's not quite ready for your judgy public eyes. I have made a simple main menu to get you there though. Background is a placeholder at the moment. Hopefully I'll add some kind of animation in the background (maybe some real time AI attacking the player's screen behind the frosted glass I've been using for the rest of the UI).

    main-menu.jpg

    Optimisations & bug fixes

    Behind the scenes I've done some graphics optimisation and a big round of bug fixing. I accidentally doubled the draw calls by using a two camera system to render to a post processed blurry texture for the UI blur. That's fixed now, I'm just blitting the main camera to a low res render texture and then applying a blur to it, which has cut the draw calls way down. I've experimented with deferred rendering and decided at this point it probably isn't worth it. I get some small performance gains on my high end desktop (2x 780 Ti, 4770k @ 4.5GHz) but on my low end 2013 MacBook Air (HD5000) forward rendering gives me nearly 50% more performance (44FPS vs 65FPS*). I guess I do only have one light in each scene.

    *These values are on ultra quality settings 1400x900, no MSAA, just some SMAA and dynamic GI.

    Some bug fixes:
    • It is no longer possible to overlap parts.
    • Parts no longer "explode" off the machine when destroyed. The broken part falls off and any connected parts that have no other direct or indirect connection to the core block (the pink block) fall off with it and are deactivated.
    • The above fix also fixes things like pistons being fired even when not connected to the machine.
    • AI behaves more predictably and doesn't get stuck as much (some avoidance ray casts weren't restricted to the right layer and the state machine has been improved).
    • AI actions have been simplified, it is now much easier to set up AI for a machine.
    AI

    AI can now use any player-controlled part (piston, motor, angle actuator, etc.) as a weapon, which will either be turned on when the player enters the attack zone or it will fire repeatedly while the player is in the attack zone (think of a piston firing every 0.2 seconds or so). This is accomplished with player-placeable attack zones linked to each attack part. The process is as follows (note: still a WIP so the icons aren't done yet):

    attack-triggers.jpg

    I've also added ramming behaviour and counter-flip measures to the AI so they can either chase you constantly or hit you, reverse and come at you again. If the machine gets stuck or flipped upside down it will use its designated "flip" part(s) to try to correct itself (usually this is just a well-placed piston - setups can't be too elaborate because the AI has no way of knowing how long it will take to "unflip" or what order to fire the flip parts in, etc. but it is good enough). Further down the track I hope to add some generic behaviour for handling getting stuck if there's no flip part or it doesn't work or is broken. Things like moving forward and back, turning left and right, trying to jiggle free and randomly firing weapons would all be good ideas.

    Due to the added ramming behaviour I had to allow the AI to drive backwards, so with a simple flip of a boolean I can make the AI drive around in reverse as competently as it can going forward with path finding and obstacle avoidance and everything, which is kinda cool to watch.

    That's about all for this update. Hopefully I'll have some time soon to record some footage of these new features and show off the arena so stay tuned. As always, any feedback is appreciated, I want to know what you guys think :D
     
    Last edited: Jul 26, 2015
  8. Thunderpants1

    Thunderpants1

    Joined:
    May 25, 2015
    Posts:
    14
    This looks really cool.

    If you don't mind me asking, what kind of joints are you using to keep all of the blocks etc. fitting together all nice and clean.

    I am working on a building sytem that is somewhat similar and my objects just don't stick together too great, especially when moving fast, like wheels.

    Thanks.
    I look forward to developments on this project.
     
  9. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    Not at all :) Most of the joints used are fixed joints mostly just for convenience. Each part has a joint set up before it is actually placed, and this is a configurable joint, because for things like wheels you need more control over the joint. This is what I call the root joint, because it is what connects the part onto other parts. Every other joint is usually either custom configured in the editor (for pistons, etc. which require an internal joint to keep the part together) or generated at runtime when you build the machine (these are usually simple fixed joints set up at the positions of the connector orbs if they have something to connect to).

    The most important setting to ensure joints hold together well is the fixed time step. This governs how frequently the physics engine updates its rigidbodies. Unfortunately PhysX requires very small time steps if you want to use joints somewhat realistically. I have mine set to 0.0025, which is 400 times every second, or at 60 fps, over 6 steps every frame. It is best to find the highest possible value you can live with.

    So that solves rubbery joints but if you find after placing multiple blocks together especially in long structures, that the blocks start to oscillate and bounce around all over the place until they explode, this (as far as I have been able to work out) can be somewhat remedied by increasing the solver iteration count (under Physics settings). The higher the value, the more impact it will have on performance. This also doesn't actually solve the problem, it just kinda makes it less likely to happen. You just need to add a few more blocks to your structure for the problem to appear again, so to avoid this situation you should try to ensure that players make rigid structures with support beams or struts, etc. so the structure doesn't have the wiggle room to flop about.

    This structure will explode:

    will-explode.png

    This structure won't explode because those "arms" are more constrained and won't wobble as much.

    wont-explode.png

    Stay tuned for more updates, I've got a playable demo on the way, just applying finishing touches :)
     
    Last edited: Aug 22, 2015
    Deleted User likes this.
  10. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    Here's a little teaser of the upcoming demo.

    New main menu screen

    new-menu.jpg


    Settings screen added

    settings.jpg


    Arena (play mode) added

    arena-0.jpg


    Selecting a fighting machine:

    arena-1.jpg


    The match has begun:

    arena-2.jpg
     
  11. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    Pause menu:

    arena-3.jpg


    Mid-game. I've lost a few wheels. You become disqualified if you don't move within 15 seconds (count down appears at 10 seconds).

    arena-4.jpg


    End match screen. The indicators on the right of the scores show whether a player was immobilised or destroyed. Either of these will remove them from the match early. The match ends if the count down reaches 0 or if there's only one player left in the match.

    arena-5.jpg

    I should add that to destroy a machine you must attack its core (the pink block). It only takes a few hits from a weapon. To immobilise you can pin it against a wall or attack its wheels. Further down the track I will look into changing the health system so that damage to any block will decrease health, not just the core block (as it can obviously be a tricky target).

    New name and logo

    I'm proud to announce the new name and logo for what was formerly called Mech Arena. The new name is Wreck Arena and I've made a logo to suit. Any feedback is appreciated :) Stay tuned for more.

    wreck-arena-logo.png
     
    Last edited: Aug 20, 2015
  12. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    I've just released v0.1.0. It's a pre-alpha (read: unfinished and buggy, there's no sound for example).

    IndieDB
    itch.io
     
  13. HolyGoat

    HolyGoat

    Joined:
    Oct 25, 2015
    Posts:
    1
    Looks awesome!
    I'm assuming you're using wheel colliders?
    Are you dynamically adjusting their spring / damp numbers based on the mass?
     
    Last edited: Oct 25, 2015
  14. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    I'm actually just using mesh colliders ;) I split them into 3 parts (one for the inside edge, one for middle and one for outer edge). The outer parts I give a low friction material and the inside bit I give a high friction material. I do this so that if the wheel hits a wall at a glancing angle it doesn't flip the whole machine over (because realistically, wheels don't have much grip on their sides). To turn the wheels I use a configurable joint but a hinge joint would probably also do just fine in most cases.

    The reason I'm not using wheel colliders is I need a more generic physics simulation. Players can place the wheels wherever they want so the wheels won't necessarily be under the machine and pointing in the right direction. Using mesh colliders gives reasonably good results no matter what orientation, size, and direction the wheels go in.

    One issue that has plagued me is joints can only have a mass ratio of up to 1:10 so that's basically saying wheels will "weigh" at least one tenth of the weight of the whole machine, so when you rotate the wheels with high torque they will rotate the machine too. I am yet to find a good solution to this :( I've tried "stacking" joints so the wheels are applying all their torque to a dummy rigidbody, which is fixed to the machine, but this also exhibits odd behaviour.
     
  15. RCMZ

    RCMZ

    Joined:
    Dec 29, 2015
    Posts:
    4
    This game looks really cool !
    I just started to learn Unity and I plan on making a game similar to this, can you show us how your game work ?
     
  16. Zer0_Civilian

    Zer0_Civilian

    Joined:
    Jan 26, 2016
    Posts:
    1
    You NEED to get a win32 version out. I want to play it SOOOOO bad.
     
  17. A-K-Games

    A-K-Games

    Joined:
    May 22, 2016
    Posts:
    1
    Can you show how to save machines like in your game?
     
  18. slava_pankratov

    slava_pankratov

    Joined:
    Jan 17, 2015
    Posts:
    47
    Please post to asset store, thanks!
     
  19. Guacamolay

    Guacamolay

    Joined:
    Jun 24, 2013
    Posts:
    63
    I don't know if you're still checking this thread, but could I ask you what kind of values and joints you're using in your set up? I started playing around with a small Besiege like builder, but it's very hard to get the physics acting the way you'd expect them to.

    I'm currently using hinge joints for the wheels, and rotating them through the motor on the joint by setting a target velocity and setting the force value. However once I try and increase the force to build some speed, the front of the car lifts up. Even upside down the car seems to be able to flip itself as the wheels are providing so much turning force. You seemed to have solved the problem in Wreck Arena, so I was wondering could you let me know what values you use for mass on your blocks and what speed or force you use to rotate your motors/wheels?

    Looking forward to the next update of your game by the way, the latest devlog video you put up on youtube looks super nice
     
  20. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    Been a while since I've checked this thread, so sorry I didn't get back to you guys. Haven't done much work on the game since last year but at that time I basically recoded the game from scratch (see the results here).

    I moved away from robot vs robot combat because I was finding it difficult to develop the game further, with all the AI conditions, etc. That's not even getting to the networked physics part. I decided to go with a single player puzzle solving approach where you build robots to complete physics-based puzzles.

    As I said, haven't worked on the game in a while. Had to put it on the back burner, as I've been focussing my attention on other projects. Nonetheless, would be interesting to hear thoughts on the new version compared to the old version and the switch from combat to puzzle solving (though I have no footage of actual puzzle solving gameplay yet).

    Answered this one in a PM but for the benefit of anyone reading this with similar wheel issues, see the code below:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class WheelColliderSource : MonoBehaviour
    5. {
    6.     [SerializeField]
    7.     new Transform renderer;
    8.  
    9.     new Rigidbody rigidbody;
    10.  
    11.     Vector3 accumDir;
    12.  
    13.     WheelFrictionCurveSource m_forwardFriction; //Properties of tire friction in the direction the wheel is pointing in.
    14.     WheelFrictionCurveSource m_sidewaysFriction; //Properties of tire friction in the sideways direction.
    15.  
    16.     float m_forwardSlip;
    17.     float m_sidewaysSlip;
    18.     Vector3 m_totalForce;
    19.     Vector3 m_center; //The center of the wheel, measured in the object's local space.
    20.     bool m_isGrounded; //Indicates whether the wheel currently collides with something (Read Only).
    21.  
    22.     float m_wheelMotorTorque; //Motor torque on the wheel axle. Positive or negative depending on direction.
    23.     float m_wheelBrakeTorque; //Brake torque. Must be positive.
    24.     float m_wheelSteerAngle; //Steering angle in degrees, always around the local y-axis.
    25.     float m_wheelAngularVelocity; //Current wheel axle rotation speed, in rotations per minute (Read Only).
    26.     float m_wheelRotationAngle;
    27.  
    28.     [SerializeField]
    29.     float m_wheelRadius; //The radius of the wheel, measured in local space.
    30.  
    31.     float m_wheelMass; //The mass of the wheel. Must be larger than zero.
    32.  
    33.     RaycastHit m_raycastHit;
    34.  
    35.     Color GizmoColor;
    36.  
    37.     Vector3 forward;
    38.  
    39.     public Vector3 Center
    40.     {
    41.         set
    42.         {
    43.             m_center = value;
    44.         }
    45.         get
    46.         {
    47.             return m_center;
    48.         }
    49.     }
    50.     public WheelFrictionCurveSource ForwardFriction
    51.     {
    52.         set
    53.         {
    54.             m_forwardFriction = value;
    55.         }
    56.         get
    57.         {
    58.             return m_forwardFriction;
    59.         }
    60.     }
    61.     public WheelFrictionCurveSource SidewaysFriction
    62.     {
    63.         set
    64.         {
    65.             m_sidewaysFriction = value;
    66.         }
    67.         get
    68.         {
    69.             return m_sidewaysFriction;
    70.         }
    71.     }
    72.     public float MotorTorque
    73.     {
    74.         set
    75.         {
    76.             m_wheelMotorTorque = value;
    77.         }
    78.         get
    79.         {
    80.             return m_wheelMotorTorque;
    81.         }
    82.     }
    83.     public float BrakeTorque
    84.     {
    85.         set
    86.         {
    87.             m_wheelBrakeTorque = value;
    88.         }
    89.         get
    90.         {
    91.             return m_wheelBrakeTorque;
    92.         }
    93.     }
    94.     public float SteerAngle
    95.     {
    96.         set
    97.         {
    98.             m_wheelSteerAngle = value;
    99.         }
    100.         get
    101.         {
    102.             return m_wheelSteerAngle;
    103.         }
    104.     }
    105.     public bool IsGrounded
    106.     {
    107.         get
    108.         {
    109.             return m_isGrounded;
    110.         }
    111.     }
    112.     public float RPM
    113.     {
    114.         get
    115.         {
    116.             return m_wheelAngularVelocity;
    117.         }
    118.     }
    119.  
    120.     void Awake()
    121.     {
    122.         rigidbody = GetComponent<Rigidbody>();
    123.         Center = Vector3.zero;
    124.  
    125.         m_wheelMass = 2.5f;
    126.  
    127.         m_forwardFriction = new WheelFrictionCurveSource();
    128.         m_sidewaysFriction = new WheelFrictionCurveSource();
    129.     }
    130.  
    131.     void FixedUpdate()
    132.     {
    133.         UpdateSuspension();
    134.         UpdateWheel();
    135.  
    136.         if(m_isGrounded)
    137.         {
    138.             forward = Vector3.Cross(accumDir.normalized, -transform.right);
    139.  
    140.             CalculateSlips();
    141.             CalculateForcesFromSlips();
    142.  
    143.             rigidbody.AddForce(m_totalForce);
    144.         }
    145.  
    146.         m_isGrounded = false;
    147.         accumDir.Set(0, 0, 0);
    148.     }
    149.  
    150.     void OnDrawGizmosSelected()
    151.     {
    152.         Gizmos.color = GizmoColor;
    153.  
    154.         //Draw the wheel
    155.         Vector3 point1;
    156.         Vector3 point0 = transform.TransformPoint(m_wheelRadius * new Vector3(0, Mathf.Sin(0), Mathf.Cos(0)));
    157.  
    158.         for(int i = 1; i <= 20; ++i)
    159.         {
    160.             point1 = transform.TransformPoint(m_wheelRadius * new Vector3(0, Mathf.Sin(i / 20.0f * Mathf.PI * 2.0f), Mathf.Cos(i / 20.0f * Mathf.PI * 2.0f)));
    161.             Gizmos.DrawLine(point0, point1);
    162.             point0 = point1;
    163.         }
    164.  
    165.         Gizmos.color = Color.white;
    166.     }
    167.  
    168.     public bool GetGroundHit(out WheelHitSource wheelHit)
    169.     {
    170.         wheelHit = new WheelHitSource();
    171.  
    172.         if(m_isGrounded)
    173.         {
    174.             wheelHit.Collider = m_raycastHit.collider;
    175.             wheelHit.Point = m_raycastHit.point;
    176.             wheelHit.Normal = m_raycastHit.normal;
    177.             wheelHit.ForwardDir = transform.forward;
    178.             wheelHit.SidewaysDir = -transform.right;
    179.             wheelHit.Force = m_totalForce;
    180.             wheelHit.ForwardSlip = m_forwardSlip;
    181.             wheelHit.SidewaysSlip = m_sidewaysSlip;
    182.         }
    183.  
    184.         return m_isGrounded;
    185.     }
    186.  
    187.     void UpdateSuspension()
    188.     {
    189.         GizmoColor = m_isGrounded ? Color.green : Color.red;
    190.     }
    191.  
    192.     void UpdateWheel()
    193.     {
    194.         //Calculate the wheel's rotation given it's angular velocity
    195.         m_wheelRotationAngle += m_wheelAngularVelocity * Time.fixedDeltaTime;
    196.  
    197.         //Set the rotation and steer angle of the wheel model
    198.         //transform.localEulerAngles = new Vector3(/*m_wheelRotationAngle*/0, m_wheelSteerAngle, 0);
    199.         renderer.localEulerAngles = new Vector3(m_wheelRotationAngle, 0, 0);
    200.  
    201.         //Apply rolling force to tires if they are grounded and don't have motor torque applied to them.
    202.         if (m_isGrounded && m_wheelMotorTorque == 0)
    203.         {
    204.             //Apply angular force to wheel from slip
    205.             m_wheelAngularVelocity -= Mathf.Sign(m_forwardSlip) * m_forwardFriction.Evaluate(m_forwardSlip) / (Mathf.PI * 2f * m_wheelRadius) / m_wheelMass * Time.fixedDeltaTime * 100;
    206.         }
    207.  
    208.         //Apply motor torque
    209.         m_wheelAngularVelocity += m_wheelMotorTorque / m_wheelRadius / m_wheelMass * Time.fixedDeltaTime;
    210.  
    211.         //Apply brake torque
    212.         m_wheelAngularVelocity -= Mathf.Sign(m_wheelAngularVelocity) * Mathf.Min(Mathf.Abs(m_wheelAngularVelocity), m_wheelBrakeTorque * m_wheelRadius / m_wheelMass * Time.fixedDeltaTime);
    213.     }
    214.  
    215.     void CalculateSlips()
    216.     {
    217.         //Calculate the wheel's linear velocity
    218.         Vector3 velocity = rigidbody.velocity;
    219.  
    220.         //Store the forward and sideways direction to improve performance
    221.         //Vector3 forward = transform.forward;
    222.         Vector3 sideways = -transform.right;
    223.  
    224.         //Calculate the forward and sideways velocity components relative to the wheel
    225.         Vector3 forwardVelocity = Vector3.Dot(velocity, forward) * forward;
    226.         Vector3 sidewaysVelocity = Vector3.Dot(velocity, sideways) * sideways;
    227.  
    228.         //Calculate the slip velocities.
    229.         //Note that these values are different from the standard slip calculation.
    230.         m_forwardSlip = -Mathf.Sign(Vector3.Dot(forward, forwardVelocity)) * forwardVelocity.magnitude + (m_wheelAngularVelocity * Mathf.PI / 180.0f * m_wheelRadius);
    231.         m_sidewaysSlip = -Mathf.Sign(Vector3.Dot(sideways, sidewaysVelocity)) * sidewaysVelocity.magnitude;
    232.     }
    233.  
    234.     void CalculateForcesFromSlips()
    235.     {
    236.         //Forward slip force
    237.         m_totalForce = forward * Mathf.Sign(m_forwardSlip) * m_forwardFriction.Evaluate(m_forwardSlip);
    238.  
    239.         //Lateral slip force
    240.         m_totalForce -= transform.right * Mathf.Sign(m_sidewaysSlip) * m_forwardFriction.Evaluate(m_sidewaysSlip);
    241.     }
    242.  
    243.     void OnCollisionStay(Collision collision)
    244.     {
    245.         // only sample one contact point
    246.         accumDir += collision.contacts[0].normal;
    247.         m_isGrounded = true;
    248.     }
    249. }
    250.  
    251. public class WheelFrictionCurveSource
    252. {
    253.     struct WheelFrictionCurvePoint
    254.     {
    255.         public float TValue;
    256.         public Vector2 SlipForcePoint;
    257.     }
    258.     float m_extremumSlip; //Extremum point slip (default 3).
    259.     float m_extremumValue; //Force at the extremum slip (default 6000).
    260.     float m_asymptoteSlip; //Asymptote point slip (default 4).
    261.     float m_asymptoteValue; //Force at the asymptote slip (default 5500).
    262.     float m_stiffness; //Multiplier for the extremumValue and asymptoteValue values (default 1).
    263.  
    264.     int m_arraySize;
    265.     WheelFrictionCurvePoint[] m_extremePoints;
    266.     WheelFrictionCurvePoint[] m_asymptotePoints;
    267.  
    268.     public float ExtremumSlip
    269.     {
    270.         get
    271.         {
    272.             return m_extremumSlip;
    273.         }
    274.         set
    275.         {
    276.             m_extremumSlip = value;
    277.             UpdateArrays();
    278.         }
    279.     }
    280.     public float ExtremumValue
    281.     {
    282.         get
    283.         {
    284.             return m_extremumValue;
    285.         }
    286.         set
    287.         {
    288.             m_extremumValue = value;
    289.             UpdateArrays();
    290.         }
    291.     }
    292.     public float AsymptoteSlip
    293.     {
    294.         get
    295.         {
    296.             return m_asymptoteSlip;
    297.         }
    298.         set
    299.         {
    300.             m_asymptoteSlip = value;
    301.             UpdateArrays();
    302.         }
    303.     }
    304.     public float AsymptoteValue
    305.     {
    306.         get
    307.         {
    308.             return m_asymptoteValue;
    309.         }
    310.         set
    311.         {
    312.             m_asymptoteValue = value;
    313.             UpdateArrays();
    314.         }
    315.     }
    316.     public float Stiffness
    317.     {
    318.         get
    319.         {
    320.             return m_stiffness;
    321.         }
    322.         set
    323.         {
    324.             m_stiffness = value;
    325.         }
    326.     }
    327.  
    328.     public WheelFrictionCurveSource()
    329.     {
    330.         m_extremumSlip = 6;
    331.         m_extremumValue = 2000;
    332.         m_asymptoteSlip = 12;
    333.         m_asymptoteValue = 1600;
    334.         m_stiffness = 1;
    335.  
    336.         m_arraySize = 50;
    337.         m_extremePoints = new WheelFrictionCurvePoint[m_arraySize];
    338.         m_asymptotePoints = new WheelFrictionCurvePoint[m_arraySize];
    339.  
    340.         UpdateArrays();
    341.     }
    342.  
    343.     void UpdateArrays()
    344.     {
    345.         //Generate the arrays
    346.         for (int i = 0; i < m_arraySize; ++i)
    347.         {
    348.             m_extremePoints[i].TValue = (float)i / (float)m_arraySize;
    349.             m_extremePoints[i].SlipForcePoint = Hermite(
    350.                 (float)i / (float)m_arraySize,
    351.                 Vector2.zero,
    352.                 new Vector2(m_extremumSlip, m_extremumValue),
    353.                 Vector2.zero,
    354.                 new Vector2(m_extremumSlip * 0.5f + 1, 0)
    355.             );
    356.  
    357.             m_asymptotePoints[i].TValue = (float)i / (float)m_arraySize;
    358.             m_asymptotePoints[i].SlipForcePoint = Hermite(
    359.                 (float)i / (float)m_arraySize,
    360.                 new Vector2(m_extremumSlip, m_extremumValue),
    361.                 new Vector2(m_asymptoteSlip, m_asymptoteValue),
    362.                 new Vector2((m_asymptoteSlip - m_extremumSlip) * 0.5f + 1, 0),
    363.                 new Vector2((m_asymptoteSlip - m_extremumSlip) * 0.5f + 1, 0)
    364.             );
    365.         }
    366.     }
    367.  
    368.     public float Evaluate(float slip)
    369.     {
    370.         //The slip value must be positive.
    371.         slip = Mathf.Abs(slip);
    372.  
    373.         if (slip < m_extremumSlip)
    374.         {
    375.             return Evaluate(slip, m_extremePoints) * m_stiffness;
    376.         }
    377.         else if (slip < m_asymptoteSlip)
    378.         {
    379.             return Evaluate(slip, m_asymptotePoints) * m_stiffness;
    380.         }
    381.         else
    382.         {
    383.             return m_asymptoteValue * m_stiffness;
    384.         }
    385.     }
    386.  
    387.     float Evaluate(float slip, WheelFrictionCurvePoint[] curvePoints)
    388.     {
    389.         int top = m_arraySize - 1;
    390.         int bottom = 0;
    391.         int index = (int)((top + bottom) * 0.5f);
    392.  
    393.         WheelFrictionCurvePoint result = curvePoints[index];
    394.  
    395.         //Binary search the curve to find the corresponding t value for the given slip
    396.         while ((top != bottom && top - bottom > 1))
    397.         {
    398.             if (result.SlipForcePoint.x <= slip)
    399.             {
    400.                 bottom = index;
    401.             }
    402.             else if (result.SlipForcePoint.x >= slip)
    403.             {
    404.                 top = index;
    405.             }
    406.  
    407.             index = (int)((top + bottom) * 0.5f);
    408.             result = curvePoints[index];
    409.         }
    410.  
    411.         float slip1 = curvePoints[bottom].SlipForcePoint.x;
    412.         float slip2 = curvePoints[top].SlipForcePoint.x;
    413.         float force1 = curvePoints[bottom].SlipForcePoint.y;
    414.         float force2 = curvePoints[top].SlipForcePoint.y;
    415.  
    416.         float slipFraction = (slip - slip1) / (slip2 - slip1);
    417.  
    418.         return force1 * (1- slipFraction) + force2 * (slipFraction); ;
    419.     }
    420.  
    421.     Vector2 Hermite(float t, Vector2 p0, Vector2 p1, Vector2 m0, Vector2 m1)
    422.     {
    423.         float t2 = t * t;
    424.         float t3 = t2 * t;
    425.  
    426.         return
    427.             (2 * t3 - 3 * t2 + 1) * p0 +
    428.             (t3 - 2 * t2 + t) * m0 +
    429.             (-2 * t3 + 3 * t2) * p1 +
    430.             (t3 - t2) * m1;
    431.     }
    432. }
    433.  
    434. public struct WheelHitSource
    435. {
    436.     public Collider Collider; //The other Collider the wheel is hitting.
    437.     public Vector3 Point; //The point of contact between the wheel and the ground.
    438.     public Vector3 Normal; //The normal at the point of contact.
    439.     public Vector3 ForwardDir; //The direction the wheel is pointing in.
    440.     public Vector3 SidewaysDir; //The sideways direction of the wheel.
    441.     public Vector3 Force; //The magnitude of the force being applied for the contact.
    442.     public float ForwardSlip; //Tire slip in the rolling direction. Acceleration slip is negative, braking slip is positive.
    443.     public float SidewaysSlip; //Tire slip in the sideways direction.
    444. }
    This is a custom wheel "collider" that handles all of the friction and force calculations for your wheel. It requires a collider with zero friction to use (mesh collider, sphere, box, whatever you want) and works with any orientation with decent slip calculations so you can adjust it for drifting behaviour, etc. Code is based on this with some modifications to work with any arbitrary collider, not just circles. I use this solution in all the recent videos on my dev blog.
     
    HelckOne likes this.
  21. Guacamolay

    Guacamolay

    Joined:
    Jun 24, 2013
    Posts:
    63

    Thanks for this, you've saved me a lot of headaches! Regarding the new direction for the game, I think I prefer this new puzzle based approach, it might allow more creative freedom and also force people to come up with different designs for each situations, rather than focusing on creating one ultimate battle robot
     
    Last edited: Mar 7, 2017
  22. juliocdep

    juliocdep

    Joined:
    Sep 30, 2010
    Posts:
    30
    Sorry my complete ignorance, but how I use this custom wheel collider? How I setup it? And how I can rotate the wheel using a motor hinge joint?
     
  23. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    The collider is essentially your motor. Calling it a collider here is a bit misleading in that it doesn't actually resolve collisions (which is why the wheel needs another regular collider). It's just called WheelColliderSource because it was originally based on someone's custom implementation of Unity's wheel collider (which did resolve collisions), so sorry for any confusion that caused.

    So what does this mean? Well, it means you can't power this wheel with a motor joint. The wheel is powered by setting its MotorTorque and BrakeTorque properties via script, similar to how Unity's wheel works. The wheel itself doesn't actually rotate in the physics engine, so powering it with a joint won't work.

    I'm away from my desktop so I can't test but I can give you some tips:
    • The WheelColliderSource component requires a RigidBody, so add it to a "Wheel" object with a rigidbody and a collider that roughly resembles your wheel shape (remember, this collider won't rotate so make sure the collider is somewhat symmetrical like an actual wheel)
    • The collider should have no friction, as the friction is calculated separately by WheelColliderSource
    • You can joint this rigidbody to your chassis with a fixed joint (again, it won't physically rotate so fixed joint is fine)
    • When you have the wheel selected you should see a gizmo which will show you which way the wheel is pointing and its radius. Check to see if the collider is facing the right way and if it isn't, chuck it inside a rotated empty inside the rigidbody object.
    • You'll also need a renderer for your wheel. You'll notice there's a renderer field. Make the renderer a child of the wheel and assign it to the component. The wheel will rotate this renderer based on its calculated angular velocity.
    • Chuck a script on your chassis the same way you would if you followed Unity's vehicle tutorial. Give it a reference to your WheelColliderSource components and for each one, change its MotorTorque and BrakeTorque depending on input. Again, you can basically follow any Unity car tutorial for this, just replace WheelCollider with WheelColliderSource.
    If you wanted to use this with some sort of block building system like I did, and you want to attach the wheels to motor blocks, you'll need to do a check somewhere to determine if the motor has a wheel attached to it, or some other arbitrary object like a cube. If it is a wheel, it should be modifying the wheel's MotorTorque. If it isn't a wheel, it should be applying actual torque to the rigidbody (and hence will need a non-fixed joint).

    If you just wanted basic vehicle behaviour without any joints, modify the script so that the rigidbody field is exposed, and don't assign to it in Awake, but rather do it in the inspector and put the rigidbody on the chassis. You will also have to modify line 143 to:
    Code (CSharp):
    1. rigidbody.AddForceAtPosition(m_totalForce, transform.position);
    This will treat the vehicle as one solid object and apply the wheel forces at the appropriate locations relative to the chassis.

    That should get you somewhere. If you're still having some trouble I'll make an example package.
     
    Last edited: Mar 31, 2017
  24. juliocdep

    juliocdep

    Joined:
    Sep 30, 2010
    Posts:
    30
    Many thanks for your response. Now I understood better how to use it. I'm building a game like Besiged, but with other thematic. Until now my game is all based on rigidbody, fixed joints and hinge joints. I implemented this collider and notice some limitations. If you could tell me I want to know if those are right:

    • If the joints of my machine break, like in Besiged, the parts that have a wheel attached get a weird physics caused by the wheel collider.
    • I can't use a steerable block made with a hinge joint to steerable the wheel.

    I don't know if I implemented this in a wrong way or it is this way. In the wreck arena demo you used this collider? There seems haven't any this problems.
     
  25. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    What kind of weird physics are you experiencing? If the joint breaks you'll want to make sure the player can't power the wheel anymore. If it's sliding around the place, it means the flat face of the wheel must be touching the ground. I don't think the wheel can handle this direction of friction and just won't do anything . It only applies friction for contact normals perpendicular to the direction the wheel is facing (where the hub cap would be pointing). You can get around this in a few ways:
    • Modify the script so that it handles perpendicular friction (not sure what to actually call it). See line 224. You'd need an "up" velocity calculation here. You probably don't need to do a slip calculation because no one really knows how a wheel slips when it's grinding along the ground after a collision. That's all I can tell you for this solution, you'd have to do your own investigation here, it's the most complex but the most "correct" way to handle this case.
    • Add a cylinder collider to the wheel with a non-slippery physics material. Scale it so that it sticks out of either side but doesn't overlap the actual tyre. This way, when the wheel flips over, the new collider hits the ground and applies friction. When the wheel is performing normally, the original collider is used and the slip calculations are used. This is the most painless solution, but it does mean the wheel collider will be monitoring the collisions of this new collider too, unless you move the collider to be next to the wheel script in the hierarchy, or maybe a parent of it.
    You should be able to use a steerable block, that's what I'm doing in my game.To reduce headaches at the cost of some performance and added physics complexity I make my steerable block out of 2 parts. The anchor, which connects to your chassis blocks, and a "hub", which is hinge jointed to the anchor. This is what gets rotated for steering. The wheel simply connects via fixed joint to the hub. Besiege had two steering blocks last I checked; a vertically mounted one that you could attach wheels to on either side (or some beams and then wheels), and a horizontally mounted one that has more of an actual hinge motion. Neither of these designs are actually good at steering because the rotation point is too far away from the wheel. To get the best steering you need a block that rotates your wheel as close as possible to its centre, like a real car. You can also consider Ackermann steering which will give you better stability under fast cornering. I opted for a wide double steering block with a hub on each side so that I could ensure the player could steer easily with little setup.

    EDIT: Completely forgot to mention, you don't need to use the WheelColliderSource steering functionality, it can be ignored. So long as something steers the wheel, it should work, whether that be a physics joint, a script that modifies the transform, or some other technique.

    Hope that helps a bit!
     
    Last edited: Apr 4, 2017
  26. MeTheOo

    MeTheOo

    Joined:
    Dec 14, 2014
    Posts:
    22
    May I ask what shader do you use for those building parts? thanks