Winter Alpine Game (Snowboard/Skiing)

Dear Community

I was thinking about making a rather simple alpine game, but i am having issues on making the controller. I can’t manage to get the board or skis to follow the terrain without adding rigidbody, but when i do that the character controller and rigidbody works together to spin everything out of control.

My question is:

Does anyone know how to apply the right amount of physics to the character, with the board or skis, so that he follows the terrain but can still be controlled, without everything going full donkeyape?

This is a LOADED question. I spent months trying to get a good feel for my game downhill OMG before finally being satisfied. I honestly don’t think I was satisfied with controls and physics until 4-6 months into the project. I’ll post what I can to help you, but there is no tutorial on this anywhere, just basic stuff out there. Rigid body is the way, but, also, I ended up using a vehicle system with engine torque, etc, but this is truly a can of worms if you want something that is smoothly controlled and handles the way it does in Downhill OMG.

The only thing I can offer right now is a hard copy/paste of some of my code, I’ll leave it to you to dissect the important parts. I’m sure parts will help you out somewhere.

using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Random = UnityEngine.Random;

public class PlayerMovement : MonoBehaviour
{
    private float hpMax = 50f;
    private float hpCur = 50f;

    public int curSpinZone = 0;
    public bool isSpinning = false;
    public int beginSpinZone = 0;
    public int targetSpinZone = 0;
    public bool isUserInputEnabled = true;

    public VehicleItem equippedVehicle;
    
    public List<PlayerVehicle> playerVehicles = new List<PlayerVehicle>();
    public GameObject vehiclesParent;
    public GameObject skiDudeParent;
    //public float lSmall = 10f;
    //public float lBig = 30f;
    public GameObject frostPotionGO;
    public GameObject[] rocketBoosterFX;


    /* vehicles */
    public bool doDeath = false;
    public bool godMode = false;

    // Audio stuff
    private AudioSource audio_Slide;
    public float audio_Slide_Time;
    public bool audio_Slide_IsPlaying = false;
    private AudioSource audio_Jump1;
    private AudioSource audio_Landing1;
    private AudioSource audio_Falling;
   
    
    public LayerMask terrainLayer;
    public GameObject sledder;
    public string sledderName = "SkiBoy4";
    public float speedJump = 1500;					//  Jump Speed
    public Vector3 playerCenterOfMass = new Vector3(0f, -1f, .25f);
    public GameObject sled;
    public GameObject sledderAlive;
    public GameObject sledderRagdoll;
    public GameObject sledderRootBone;
    private SpawnItemsByPlayer sibp;
    public GameObject dirtyHoeResult;
    public GameObject ohBucketResult;
    


    public MsgDisplay msgDisplay;
    public MasterCamera masterCamera;
    public MCScroll masterCameraScroll;
    private GameObject masterCameraRotation;
    private StatisticsHandler statHandler;

    private float lastEmissionRate;
    private int spawnRateIncreaseCount;
// ReSharper disable once ConvertToConstant.Local
    private float hitTorqueChange = 0.02f;
    public float slidingPitch = 40f;

    public bool isWhiteSmokePlaying = false;
    public bool isPausingAudio;
    public bool isHangingTime;
    //private bool isTurning = false;
    public float minVelocityDamage = 4f;
    //private bool isMiniBoosting = false;
    private bool canShowMiniBoostTip = true;

    public bool isLanding = false;							//  used to determine if player has landed yet
    public bool isJumping = true;
    public bool isHoverCorrecting = false;
    public bool canPlayJumpVoice;
    public bool canLandAgain = true;

    private InGameUIHandler guiHandler;
    private Vector2 startTouchPos;					//  The first position we touch
    public GameObject player;						//  The player object

    public WheelCollider frontLeftWheel;
    public WheelCollider frontRightWheel;
    public WheelCollider rearLeftWheel;
    public WheelCollider rearRightWheel;
    public WheelCollider rearRearWheel;
    public GameObject axle;

    // These variables are for the gears, the array is the list of ratios. The script
    // uses the defined gear ratios to determine how much torque to apply to the wheels.
    public float[] gearRatio; // TODO: not sure about translation with array of float[] from java
    public int currentGear = 0;

    // These variables are just for applying torque to the wheels and shifting gears.
    // using the defined Max and Min Engine RPM, the script can determine what gear the
    // sled needs to be in.
    public float engineTorque = 25f;
    public float curEngineRPM = 0f;
    public float maxEngineRPM = 1500f;
    public float minEngineRPM = 500f;
    public float engineTorqueAdd = 0.15f;

    public float magnetPower = 750f;
    public float magnetRadius = 1000f;

    // Controls
    public float sensH = 1f;
    public float sensV = 10f;
    public float sensVAirBrakes = 100f;
    public float sensHMobile = 1f;
    public float sensVMobile = 10f;
    public float smooth = 0.5f;
    private float getAxisH;					// Horizontal Axis after zeroed calc
    private float getAxisV;					// Vertical Axis after zeroed calc
    public float turnRadius = 20f;				// How far to turn the wheels
    public float turnSensitivity = 0.02f;		// How fast it takes to turn sled
    public float tiltSensitivity = 0.05f;
    public float turnBackToStraightSensitivity = 0.05f;  // how fast player turns back to direction heading

    private Vector3 zeroAc;
    private Vector3 curAc;
    public float curVel;
    public float localVel;
    public float lastVel;
    public float speedSlowDown = 20f;
    public float speedMinToAddForceToJump = 6f;
    public float hangTimeStart;
    public float hangTimeEnd;
    public float hangTimeCoinsMultiplier = 1.5f;
    public float topSpeedCoinsMultiplier = 1.5f;
    private int hangTimeCoins = 50;
    private int topSpeedCoins = 50;
    public float distToGroundOffset = 2f;

    private ParticleEmitter velDamSmoke;
    private GameObject whiteSmokePrefab;
    ParticleSystem psWhiteSmoke;
    public float slowDownForce = 0.01f;
    public float velDam01 = 5;
    public float velDam02 = 10f;
    public float velDam03 = 15f;
    public float velDam04 = 20f;
    public float velDam05 = 25f;
    public float velDam06 = 30f;
    public float velDam07 = 35f;
    public float velDam08 = 40f;
    public float velDam09 = 45f;
    public float velDam10 = 50f;
    private bool isPlayingHighSpeedVoice;
    private bool canGiveNewHangTimeAward = true;
    private bool canGiveNewTopSpeedAward = true;
    private bool isCheckingAchievementDistance;

    private int statHealthPotionsUsed; // *
    private int statSpeedBoostsUsed; // *
    //private int statCoinsCollected; // *  still might need to use for achievement
    //private int statBonusCoinsCollected;
    private int statBestHangTime; // *
    private int statDistanceTraveled; // *
    private int statCollectiblesCommon;
    private int statCollectiblesUncommon;
    private int statCollectiblesRare;
    private int statCollectiblesLegendary;
    //private int statCollectiblesTotal;
    //private int statDamageTaken;
    private int statSpeedRecord; // *
    //private int statLvlSpeedsBeaten;
    //private int statLvlBestHangTimesBeaten;
    //private int statLvlCoinsCollected; // *
    //private int statLvlSpeedBoostsUsed; // *
    //private int statLvlHealthPotionsUsed; // *
    //private int statLvlBonusCoinsCollected;
    private float lastAch_DistanceTraveled1000;
    private float lastAch_DistanceTraveled100000;
    private float lastAch_DistanceTraveled1000000;
    private int lastAch_SpeedBoostUsed100;
    private int lastAch_HealthPotionUsed100;
    private int lastBoostLevel1;
    private int lastBoostLevel2;
    private int lastBoostLevel3;
    private int lastBoostLevel4;
    private int lastBoostLevel5;

    private float statN_BestHangTime;
    private float statN_SpeedRecord;
    private int statN_DistanceTraveled; // *
    private int statN_CollectiblesCommon;
    private int statN_CollectiblesUncommon;
    private int statN_CollectiblesRare;
    private int statN_CollectiblesLegendary;
    private int statN_LvlDamageTaken;
    private int statN_LvlSpeedsBeaten;
    private int statN_LvlBestHangTimesBeaten;
    private int statN_LvlCoinsCollected;
    private int statN_LvlSpeedBoostsUsed;
    private int statN_LvlHealthPotionsUsed;
    private int statN_LvlBonusCoinsCollected;
    private int statN_LvlDistanceTraveled;
    private  bool isSpawningEnemies;
    public float newPitch = 0.5f;
    [HideInInspector]
    public bool isUnstableSled = false;
    [HideInInspector]
    public bool isInvulnerable = false;
    public float coinPickupMultiplier = 1f;

    public Utils.VehicleNames debugVName;
    public bool debugEquipVehicle = false;
    private bool isUsingRockets;
    private bool canSledRegenHPAgain = true;
    public bool isSmellyOrStinky = false;
    private float startDistanceZ;
    private bool canCheckDistance;
    

    void ResetAxes()
    {
        zeroAc = Utils.GetGameCalibration();
        //Debug.Log ("game settings reads: " + gs.GetGameCalibration().ToString());
        //Debug.Log ("zero ac: " + zeroAc.ToString());
        curAc = Vector3.zero;
    }

    //IEnumerator DoLightningDebug()
    //{
    //    GameManager.GetSpawnHandler().StartSpawning(SpawnHandler.SPAWNSTYLE.LINEDUP, new SpawnHandler.SPAWNNAME[] { SpawnHandler.SPAWNNAME.LIGHTNINGBOLT, }, 1f, 0.2f, 75f, Vector3.one * lSmall, Vector3.one * lBig);
    //    yield return new WaitForSeconds(1f);
    //    StartCoroutine(DoLightningDebug());
    //}

    public void DisplayHealthChange(float amount)
    {

    }

    public float SetHPMax(float amount)
    {
        hpMax = amount;
        return hpMax;
    }

    public float AddToHPMax(float amount)
    {
        hpMax += amount;
        return hpMax;
    }

    public float GetHPMax()
    {
        return hpMax;
    }

    public float GetHPCurrent()
    {
        return hpCur;
    }

    public void SetHPCurrent(float amount, bool showAmountChange, bool checkDeath)
    {
        hpCur = amount;
        if (showAmountChange)
        {
            DisplayHealthChange(amount);
        }

        if (checkDeath)
        {
            if (hpCur <= 0)
            {
                DoDeath();
            }
        }
    }

    public float AddToHPCurrent(float amount, bool showAmountChange, bool checkDeath)
    {
        float diffFromMax = hpMax - hpCur;
        hpCur += amount;

        if (hpCur > hpMax) { hpCur = hpMax; amount = diffFromMax; }
        
        if (showAmountChange)
        {
            DisplayHealthChange(amount);
        }

        if (checkDeath)
        {
            if (hpCur <= 0)
            {
                DoDeath();
            }
        }
        return hpCur;
    }

    public void EquippedVehicle()
    {
        Debug.Log(Utils.GetVehicleName(equippedVehicle.vName));
    }

    void Start()
    {
        //StartCoroutine(DoLightningDebug());
    }

    void Awake()
    {
        StartCoroutine("AchievementSurvivalist");
        StartCoroutine("AchievementPennyPincher");
        msgDisplay = GameManager.GetMsgDisplay();
        statHandler = GameObject.Find("StatisticsHandler").GetComponent<StatisticsHandler>();
       

        if (GameManager.IsInGame())
        {
            ConfigureLevelStartStats();
        }
  
        //Set the sleep time to never
        Screen.sleepTimeout = SleepTimeout.NeverSleep;  //todo: can this be done elsewhere?

        // player = GameObject.Find("Player_Prefab").transform;  // here just in case java translation didn't work, delete if OK
        player.rigidbody.centerOfMass += playerCenterOfMass;
        SetupGUI();
        ResetAxes();
        //camMounted = GameObject.Find("MasterCameraRig_Mounted");
        masterCamera = GameManager.GetMasterCamera();
        masterCameraScroll = GameManager.GetMasterCamScroller();
        masterCameraRotation = GameObject.Find("MC.RotationPoint");
        //camMounted_FPS = GameObject.Find("MasterCameraRig_Mounted_FPS");
        //camUnmounted = GameObject.Find("MasterCameraRig_UnMounted");
        //camUnmounted_FPS = GameObject.Find("MasterCameraRig_UnMounted_FPS");

        sibp = gameObject.GetComponent<SpawnItemsByPlayer>();
        velDamSmoke = GameObject.Find("SkidSmoke").GetComponent<ParticleEmitter>();
        whiteSmokePrefab = GameObject.Find("Far_Big_Smoke_White");
        whiteSmokePrefab.transform.parent = transform;
        whiteSmokePrefab.transform.localPosition = Vector3.zero;
        psWhiteSmoke = whiteSmokePrefab.GetComponent<ParticleSystem>();
        sledder = GameObject.Find(sledderName);
        audio_Slide = GameObject.Find("Audio_Player_SledRide").GetComponent<AudioSource>();
        audio_Jump1 = GameObject.Find("Audio_Player_Jump").GetComponent<AudioSource>();
        audio_Landing1 = GameObject.Find("Audio_Player_Landing").GetComponent<AudioSource>();
        audio_Falling = GameObject.Find("Audio_Player_Falling").GetComponent<AudioSource>();

        audio_Falling.Stop();
        if (!audio_Falling.isPlaying)
        {
            audio_Falling.pitch = Random.Range(0.5f, 1.5f);
            audio_Falling.Play();
        }
        equippedVehicle = GameManager.GetVehiclesHandler().GetVehicle(Utils.GetEquippedVehicleName());
        if (equippedVehicle.vName == Utils.VehicleNames.YETISLED) 
        {
            Utils.SetEquippedVehicle(Utils.VehicleNames.BASESLED, Utils.GetCurrentPlayerID());
            equippedVehicle = GameManager.GetVehiclesHandler().GetVehicle(Utils.VehicleNames.BASESLED);
        }
        RefreshShopChanges(equippedVehicle.vName, 0f, true);
    }

    public void DisableUserInput()
    {
        isUserInputEnabled = false;
    }

    public void EnableUserInput()
    {
        isUserInputEnabled = true;
    }


    public void RefreshShopChanges(Utils.VehicleNames vName, float newBoostAmount = 0f, bool firstRun = false)
    {
        EquipVehicle(vName);
        ConfigureSpeedBoost(newBoostAmount, firstRun);
        ConfigureGravity();
    }

    private void EquipVehicle(Utils.VehicleNames vName)
    {
        //Debug.Log("EquipVehicle(" + vName.ToString() + ") from " + methodName);
        equippedVehicle = GameManager.GetVehiclesHandler().GetVehicle(vName);
        GameManager.GetVehiclesHandler().SetEquippedVehicle(vName);
        foreach (PlayerVehicle pv in playerVehicles)
        {
            pv.vehicleToManage.SetActive(false);
        }
        
        PlayerVehicle pi = playerVehicles.FirstOrDefault(i => i.vehicleName == vName);
        if (pi != null)
        {
            pi.vehicleToManage.SetActive(true);
            rigidbody.drag = equippedVehicle.terrainDrag;
            //rigidbody.angularDrag = equippedVehicle.airResistance;
        }
        else
        {
            Debug.Log("Null Vehicle Item for " + vName.ToString());
        }
    }

    private void ConfigureSpeedBoost(float newBoostAmount, bool firstRun)
    {
        AddToTorque(firstRun
            ? GameManager.GetSkillsHandler().GetCurrentSkillAmountReal(Utils.SkillsNames.BOOST)
            : newBoostAmount);
    }

    private void ConfigureGravity()
    {
        float gravityStat = GameManager.GetSkillsHandler().GetCurrentSkillAmount(Utils.SkillsNames.GRAVITY);
        //Debug.Log("Current Gravit = " + gravityStat.ToString());
        if (gravityStat == 0f) { gravityStat = 0.05f; }
        // old stat Physics.gravity = new Vector3(0, ((gravityStat * 30f) + 12.5f) * -1f, 0);
        Physics.gravity = new Vector3(0,((gravityStat * 10f) + 12.5f) * -1f,0);
        
        rigidbody.drag = 0.1f + (gravityStat / 4);
        
        float newPosForVehicles = GetVehiclePosition(gravityStat);
        //Debug.Log(newPosForVehicles.ToString());
        //vehiclesParent.transform.parent.localPosition = new Vector3(0f, newPosForVehicles + 0.75f, vehiclesParent.transform.localPosition.z);

        Vector3 newPosForSkiDude = new Vector3(skiDudeParent.transform.localPosition.x, newPosForVehicles, skiDudeParent.transform.localPosition.z);
        skiDudeParent.transform.localPosition = newPosForSkiDude;

        JointSpring js = new JointSpring {damper = 1000f, spring = 10000/gravityStat};
        int bouncyHairBallsLeft = 0;
        foreach (BuffsItem bi in GameManager.GetBuffsHandler().buffs)
        {
            if (bi.bName == Utils.BuffNames.BALLBOUNCE)
            {
                bouncyHairBallsLeft++;
            }
        }
        if (bouncyHairBallsLeft == 0) { js.targetPosition = 0f; }

        rearRearWheel.suspensionSpring = js;
        rearLeftWheel.suspensionSpring = js;
        rearRightWheel.suspensionSpring = js;
        rearRearWheel.suspensionSpring = js;
        frontLeftWheel.suspensionSpring = js;
        frontRightWheel.suspensionSpring = js;

        float newSuspensionDistance = 0.1f + (0.4f * gravityStat);
        frontLeftWheel.suspensionDistance = newSuspensionDistance;
        frontRightWheel.suspensionDistance = newSuspensionDistance;
        rearLeftWheel.suspensionDistance = newSuspensionDistance;
        rearRearWheel.suspensionDistance = newSuspensionDistance;
        rearRightWheel.suspensionDistance = newSuspensionDistance;
    }

    private float GetVehiclePosition(float gravityStat)
    {
        int pos = (int)(gravityStat * 20f);
        float newPos = 1.01f;
        const float multiplier = .018f;
        newPos += pos * multiplier;
        return (newPos * -1f);
    }

    void SetupGUI()
    {
        guiHandler = Application.loadedLevelName == "game_Tutorial" 
            ? GameObject.Find("NguiMain_TutorialVersion").GetComponent<InGameUIHandler>() 
            : GameObject.Find("Ngui").GetComponent<InGameUIHandler>();

    }

    private bool IsGrounded()
    {
        
        float distToGround = axle.collider.bounds.extents.y;
        int countGroundedWheels = 0;
            if (frontLeftWheel.isGrounded) countGroundedWheels++;
            if (frontRightWheel.isGrounded) countGroundedWheels++;
            if (rearLeftWheel.isGrounded) countGroundedWheels++;
            if (rearRearWheel.isGrounded) countGroundedWheels++;
            if (rearRightWheel.isGrounded) countGroundedWheels++;

            newPitch = (curVel / slidingPitch) + 0.5f;
            audio_Slide.pitch = newPitch;

            if (audio_Slide.pitch > 5.0f)
            {
                audio_Slide.pitch = 5.0f;
            }

        if (countGroundedWheels > 0 || Physics.Raycast(axle.transform.position, -Vector3.up, distToGround + distToGroundOffset, terrainLayer))
        {
            if (!audio_Slide_IsPlaying)
            {
                audio_Slide_IsPlaying = true;
                audio_Slide.time = audio_Slide_Time;
                audio_Slide.Play();
                isUsingRockets = false;
            }
            return true;
        }
        if (audio_Slide_IsPlaying)
        {
            audio_Slide_Time = audio_Slide.time;
            audio_Slide.Pause();
        }
        audio_Slide_IsPlaying = false;

        if (!isHangingTime)
        {
            isHangingTime = true;
            hangTimeStart = Time.time;
            StartCoroutine(PlayJumpVoice(Random.Range(3f, 10f)));

            sledderAlive.animation.Stop();
            int rand = Random.Range(0, 9);
            switch (rand)
            {
                case 0:
                    sledderAlive.animation.Play("RightTurnFast");
                    break;
                case 1:
                    sledderAlive.animation.Play("PullForward");
                    break;
                case 2:
                    sledderAlive.animation.Play("HandStandFast");
                    break;
                case 3:
                    sledderAlive.animation.Play("SledStandNormal");
                    break;
                case 4:
                    sledderAlive.animation.Play("LeftTurnFast");
                    break;
                case 5:
                    sledderAlive.animation.Play("LeftTurnNormal");
                    break;
                case 6:
                    sledderAlive.animation.Play("RightTurnNormal");
                    break;
                case 7:
                    sledderAlive.animation.Play("SledStandCross");
                    break;
                case 8:
                    sledderAlive.animation.Play("SledBackflip");
                    break;
            }
                
        }
        if (!audio_Falling.isPlaying)
        {
            audio_Falling.pitch = Random.Range(1.1f, 1.5f);
            audio_Falling.Play();
        }
        return false;
    }

    private void DebugEquipVehicle()
    {
        if (debugEquipVehicle)
        {
            debugEquipVehicle = false;
            Utils.SetEquippedVehicle(debugVName, Utils.GetCurrentPlayerID());
            EquipVehicle(debugVName);
        }
    }

    void Update()
    {
        if (doDeath)
        {
            doDeath = false;
            Debug.Log("DoDeath == true");
            DoDeath();
        }

        CheckPauseForAudio();
        if (!GameManager.GetPauseHandler().IsGamePaused()  GameManager.GetPlayer() != null)
        {
            //if (!GameManager.GetPlayer().GetComponent<CombatantClick>().combatant.isDead)
            //{
                DebugEquipVehicle();
                GetPlayerInput();
                ManageJump();
                ManageTopSpeed();
                //ManageAirTilt();
                ManageAirReverse();
                ManageRockets();
                PlayHighSpeedVoice();
                CheckVelocityChanges();
                SledMotion();
                CheckVelocity();
                MoveGuiUpdate();
                PreventWheelie();
                lastVel = curVel;
                //ManageSpinGame();
                ManageSpinGame();
                CheckEnemySpawner();
                ManageHealthRegenOverTime();
                CheckPlayerDeath();
                if (canCheckDistance)
                {
                    CheckAchievement_Distances();
                }
                else
                {
                    StartCoroutine(StartDistanceDelaySetter());
                }
            //}
            //else
            //{
            //    // is dead
            //    Debug.Log("Player combatant is dead");
            //    DoDeath();
            //}
        }

    }

    IEnumerator StartDistanceDelaySetter()
    {
        yield return new WaitForSeconds(5f);
        startDistanceZ = transform.position.z;
        canCheckDistance = true;
    }

    private void ManageHealthRegenOverTime()
    {
        if (canSledRegenHPAgain)
        {
            canSledRegenHPAgain = false;
            if (equippedVehicle.canRegenerateHealth)
            {
                if (GameManager.GetOrkHPBarHandler().GetCurrentHealth() < GameManager.GetOrkHPBarHandler().GetMaxHealth()  GameManager.GetOrkHPBarHandler().GetCurrentHealth() != 0f)
                {
                    GameManager.GetOrkHPBarHandler().ConsumeHealthBall(0.5f);
                    GameManager.GetMsgDisplayBonus().DisplayMessage("Bone Sled Regen", Utils.MESSAGESTYLEBONUS.BONESLEDREGEN, Utils.GetTrinketImageName(Utils.COLLECTIBLEITEMNAMES.BoneSledRegen));
                }
                StartCoroutine(WaitToSledRegenHPAgain());
            }
            else
            {
                canSledRegenHPAgain = true;
            }
        }
        
    }

    IEnumerator WaitToSledRegenHPAgain()
    {
        yield return new WaitForSeconds(10f);
        canSledRegenHPAgain = true;
    }

    private void CheckEnemySpawner()
    {
        if (!isSpawningEnemies)
        {
            isSpawningEnemies = true;
            StartCoroutine(SpawnYeti());
        }
    }

    IEnumerator SpawnYeti()
    {
        int distance = (int)(transform.position.z / 2000);
        int maxYetiSpawn = 60 - (distance * 2);
        if (maxYetiSpawn < 10)
        {
            maxYetiSpawn = 10;
        }
        //maxYetiSpawn = 10;
        yield return new WaitForSeconds(maxYetiSpawn);
        if (IsGrounded())
        {
            GameManager.GetSpawnHandler().StartSpawning(SpawnHandler.SPAWNSTYLE.LINEDUP, new[] { SpawnHandler.SPAWNNAME.ORC}, 1f, 1f, 100f, Vector3.one * 0.8f, Vector3.one * 2);
        }
        isSpawningEnemies = false;
    }


    public float GetMagnetRadius(bool useFullMagnetPower)
    {
        if (useFullMagnetPower)
        {
            return magnetRadius + 50f;
        }
        return (GameManager.GetSkillsHandler().GetCurrentSkillAmount(Utils.SkillsNames.MAGNET) * magnetRadius) + 50f;
    }

    public float GetMagnetPower(bool useFullMagnetPower)
    {
        if (useFullMagnetPower)
        {
            return magnetPower + 50f;
        }
        return (GameManager.GetSkillsHandler().GetCurrentSkillAmount(Utils.SkillsNames.MAGNET) * magnetPower) + 50f;
    }

    private void PlayHighSpeedVoice()
    {
        if (!isPlayingHighSpeedVoice)
        {
            if (curVel > 40f  IsGrounded())
            {
                isPlayingHighSpeedVoice = true;
                GameManager.GetAudioHighSpeedVoices().PlayAudio();
                StartCoroutine(ResetIsPlayingHighSpeedVoice(Random.Range(5f, 15f)));
            }
        }
    }

    IEnumerator ResetIsPlayingHighSpeedVoice(float timeToWait)
    {
        yield return new WaitForSeconds(timeToWait);
        isPlayingHighSpeedVoice = false;
    }

    private void PreventWheelie()
    {
        rigidbody.AddForceAtPosition(Vector3.down, new Vector3(transform.position.x, transform.position.y - 100.2f, transform.position.z + 1f), ForceMode.Force);
    }

    IEnumerator ResetCanLandAgain()
    {
        yield return new WaitForSeconds(1f);
        canLandAgain = true; 
    }

    private void ManageJump()
    {
        if (IsGrounded())
        {
            if (isLanding  canLandAgain)
            {
                GameManager.GetSpinMiniGameHandler().ResetSpinGame();
                isSpinning = false;
                hangTimeEnd = Time.time - hangTimeStart;
                canLandAgain = false;
                StartCoroutine(ResetCanLandAgain());
                
                audio_Landing1.pitch = Random.Range(0.8f, 2.5f);
                audio_Landing1.Play();
                
                isLanding = false;

                sledderAlive.animation.Stop();
                sledderAlive.animation.Blend("SpeedUp", 0.3f, 0.3f);
                
                isHangingTime = false;
                if (hangTimeEnd > statN_BestHangTime)
                {
                    statN_BestHangTime = hangTimeEnd;
                    guiHandler.SetTopHangTime(statN_BestHangTime);
                    if (sibp)
                    {
                        sibp.TryIncreasingSpawnRate(true);
                    }

                    if (statN_BestHangTime > 2f)
                    {
                        if (canGiveNewHangTimeAward)
                        {
                            canGiveNewHangTimeAward = false;
                            GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.HANGTIME);
                            statN_LvlBestHangTimesBeaten++;
                            AddStatBonusCoinsCollected(hangTimeCoins);

                            
                            const Utils.FxName fx = Utils.FxName.FireExplosionBase;
                            float scale = Random.Range(10f, 15f);
                            GameManager.GetFxCaster().SpawnFX(fx, true, true, scale, GameManager.GetPlayerVehicleParent().transform.position, GameManager.GetPlayerVehicleParent().transform.rotation, GameManager.GetPlayerVehicleParent());
                            


                            msgDisplay.DisplayMessage("New Top Hangtime!\r\n " + hangTimeCoins + " Coins!", Utils.MESSAGESTYLE.GOALS_HANGTIME);
                            CoinStore.AddCurrency(CoinStore.CURRENCYTYPE.COINS, hangTimeCoins, true);
                            msgDisplay.UpdateGoldCount();
                            float multiplier = Random.Range(hangTimeCoinsMultiplier - 0.7f, hangTimeCoinsMultiplier + 1.2f);
                            hangTimeCoins = (int)(hangTimeCoins * multiplier);
                            TimeSpan hangSeconds = TimeSpan.FromSeconds( statN_BestHangTime );
                            if (Utils.GetKonPlatformBasic() == Utils.KONPlatformBasic.WEB  GameManager.GetMerchant() == Utils.MERCHANT.KONGREGATE)
                            {
                                KongregateAPI.SubmitStat(KongregateAPI.GetStatisticName(KongregateAPI.STATISTICNAME.BESTHANGTIME), (int)hangSeconds.TotalMilliseconds);
                            }
                            GameManager.GetSpawnHandler().StartSpawning(SpawnHandler.SPAWNSTYLE.MASSIVEDROP, new[] { SpawnHandler.SPAWNNAME.HEALTHBALLS }, 1f, 0.2f);
                            GameManager.GetSpawnHandler().StartSpawning(SpawnHandler.SPAWNSTYLE.MASSIVEDROP, new[] { SpawnHandler.SPAWNNAME.SPEEDBALLS }, 1f, 0.5f);
                            StartCoroutine(ResetHangTimeAward());
                        }

                        CheckAchievements_Jump(statN_BestHangTime);
                    }
                    spawnRateIncreaseCount++;
                }

                

                audio_Falling.Pause();
                

            }

            if (isWhiteSmokePlaying  psWhiteSmoke)
            {
                psWhiteSmoke.emissionRate = lastEmissionRate;
            }
            else
            {
                psWhiteSmoke.emissionRate = 0;
            }
        }
        else
        {
            psWhiteSmoke.emissionRate = 0f;
            isLanding = true;

            if (!isSpinning)
            {
                isSpinning = true;
                targetSpinZone = GetTargetSpinZone(GetCurrentSpinZone(transform.eulerAngles.y));
            }
        }
    }

    public int GetTargetSpinZone(int spinZone)
    {
        switch (spinZone)
        {
            case 1:
                return 5;
            case 2:
                return 6;
            case 3:
                return 7;
            case 4:
                return 8;
            case 5:
                return 1;
            case 6:
                return 2;
            case 7:
                return 3;
            case 8:
                return 4;
        }
        return 1;
    }

    public int GetCurrentSpinZone(float rot)
    {
        if (rot >= 0  rot < 45f)
        {
            return 1;
        }
        if (rot >= 45f  rot < 90f)
        {
            return 2;
        }
        if (rot >= 90f  rot < 135f)
        {
            return 3;
        }
        if (rot >= 135f  rot < 180f)
        {
            return 4;
        }
        if (rot >= 180f  rot < 225f)
        {
            return 5;
        }
        if (rot >= 225f  rot < 270f)
        {
            return 6;
        }
        if (rot >= 270f  rot < 315f)
        {
            return 7;
        }
        return 8;
    }

    public void ManageSpinGame()
    {
        if (isSpinning)
        {
            int curZone = GetCurrentSpinZone(transform.eulerAngles.y);
            if (curZone == targetSpinZone)
            {
                targetSpinZone = GetTargetSpinZone(curZone);
                GameManager.GetSpinMiniGameHandler().DoSpinBonus();
            }
        }

        if (IsGrounded())
        {
            GameManager.GetSpinMiniGameHandler().ResetSpinGame();
            isSpinning = false;
        }
    }

    public void DoFrostPotion(bool state)
    {
        if (state)
        {
            godMode = true;
            frostPotionGO.SetActive(true);
        }
        else
        {
            godMode = false;
            frostPotionGO.SetActive(false);
        }
    }

    private void ManageAirReverse()
    {
        if (!IsGrounded())
        {
            if (getAxisV < 0)
            {
                float sens = sensVAirBrakes * (GameManager.GetSkillsHandler().GetCurrentSkillAmount(Utils.SkillsNames.AIRBRAKES));
                Vector3 tempNegativeForce = rigidbody.velocity * (getAxisV * -1) * sens * -1;
                Vector3 negativeForce = new Vector3(tempNegativeForce.x, rigidbody.velocity.y, tempNegativeForce.z);
                //Debug.Log("NegativeForce: " + negativeForce.ToString() + " CurrentVelocity: " + rigidbody.velocity);
                rigidbody.AddForce(negativeForce);
            }
        }
    }

    private void ConfigureLevelStartStats()
    {
        statHealthPotionsUsed = Utils.GetPlayerStat(Utils.PSTATS.HEALTHPOTIONSUSED);
        //Debug.Log(((double)Mathf.Floor(statHealthPotionsUsed / 100.0f) * 100.0f).ToString() + " boosts used");
        lastAch_HealthPotionUsed100 = (int)((double)Mathf.Floor(statHealthPotionsUsed / 100.0f) * 100.0f);
        statSpeedBoostsUsed = Utils.GetPlayerStat(Utils.PSTATS.SPEEDBOOSTSUSED);
        //Debug.Log(((double)Mathf.Floor(statSpeedBoostsUsed / 100.0f) * 100.0f).ToString() + " healths potions used");
        lastAch_SpeedBoostUsed100 = (int)((double)Mathf.Floor(statSpeedBoostsUsed / 100.0f) * 100.0f);
        //statCoinsCollected = Utils.GetPlayerStat(Utils.PSTATS.COINSCOLLECTED);
        //statBonusCoinsCollected = Utils.GetPlayerStat(Utils.PSTATS.BONUSCOINSCOLLECTED);
        statSpeedRecord = Utils.GetPlayerStat(Utils.PSTATS.TOPSPEEDRECORD);
        statBestHangTime = Utils.GetPlayerStat(Utils.PSTATS.BESTHANGTIME);
        statDistanceTraveled = Utils.GetPlayerStat(Utils.PSTATS.DISTANCETRAVELED);
        lastAch_DistanceTraveled1000 = (int)((double)Mathf.Floor(statDistanceTraveled / 1000f) * 1000f);
        lastAch_DistanceTraveled100000 = (int)((double)Mathf.Floor(statDistanceTraveled / 100000f) * 100000f);
        lastAch_DistanceTraveled1000000 = (int)((double)Mathf.Floor(statDistanceTraveled / 1000000f) * 1000000f);
        statCollectiblesCommon = Utils.GetPlayerStat(Utils.PSTATS.COLLECTIBLESCOMMON);
        statCollectiblesUncommon = Utils.GetPlayerStat(Utils.PSTATS.COLLECTIBLESUNCOMMON);
        statCollectiblesRare = Utils.GetPlayerStat(Utils.PSTATS.COLLECTIBLESRARE);
        statCollectiblesLegendary = Utils.GetPlayerStat(Utils.PSTATS.COLLECTIBLESLEGENDARY);
        //statCollectiblesTotal = statCollectiblesCommon + statCollectiblesUncommon + statCollectiblesRare + statCollectiblesLegendary;
        //statDamageTaken = Utils.GetPlayerStat(Utils.PSTATS.DAMAGETAKEN);
        //statLvlSpeedsBeaten = Utils.GetPlayerStat(Utils.PSTATS.LEVELRECORD_TOPSPEEDSBEATEN);
        //statLvlBestHangTimesBeaten = Utils.GetPlayerStat(Utils.PSTATS.LEVELRECORD_BESTHANGTIMESBEATEN);
        //statLvlCoinsCollected = Utils.GetPlayerStat(Utils.PSTATS.LEVELRECORD_COINSCOLLECTED);
        //statLvlBonusCoinsCollected = Utils.GetPlayerStat(Utils.PSTATS.LEVELRECORD_BONUSCOINSCOLLECTED);
        //statLvlSpeedBoostsUsed = Utils.GetPlayerStat(Utils.PSTATS.LEVELRECORD_SPEEDBOOSTSUSED);
        //statLvlHealthPotionsUsed = Utils.GetPlayerStat(Utils.PSTATS.LEVELRECORD_HEALTHPOTIONSUSED);
    }

    private void CheckAchievements_Jump(float hangTime)
    {
        if (hangTime > 5f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.BIGAIR).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BIGAIR);
            }
        }

        if (hangTime > 10f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.LONGAIR).isUnlocked)
                {
                    GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.LONGAIR);
            }
        }

        if (hangTime > 15f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.HUGEAIR).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.HUGEAIR);
            }
        }

        if (hangTime > 20f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.KINGAIR).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.KINGAIR);
            }
        }

        if (hangTime > 25f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.GODLYAIR).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.GODLYAIR);
            }
        }

        if (hangTime > 30f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.MASTEROFTHESKY).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.MASTEROFTHESKY);
            }
        }
        
    }

    private void CheckAchievements_Speed(float speed)
    {
        if (speed > 50f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.HIGHSPEED).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.HIGHSPEED);
            }
        }

        if (speed > 100f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.CRAZYSPEED).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.CRAZYSPEED);
            }
        }

        if (speed > 200f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.FURIOUSSPEED).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.FURIOUSSPEED);
            }
        }

        if (speed > 300f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.RIDICULOUSSPEED).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.RIDICULOUSSPEED);
            }
        }

        if (speed > 400f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.LUDICROUSSPEED).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.LUDICROUSSPEED);
            }
        }

        if (speed > 500f)
        {
            if (!GameManager.GetAchievementManager().GetAchievementByName(Utils.ACHIEVEMENTNAMES.ULTIMATESPEED).isUnlocked)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.ULTIMATESPEED);
            }
        }
    }

    private void CheckAchievement_Distances()
    {
        if (!isCheckingAchievementDistance)
        {
            isCheckingAchievementDistance = true;
            StartCoroutine(CheckAchievement_DistancesDelay());
        }
    }

    IEnumerator CheckAchievement_DistancesDelay()
    {
        yield return new WaitForSeconds(1f);
        int posZ = (int)transform.position.z;
        statN_LvlDistanceTraveled = posZ;
        //Debug.Log(statDistanceTraveled);
        statN_DistanceTraveled = posZ + statDistanceTraveled;

        if (statN_DistanceTraveled > lastAch_DistanceTraveled1000 + 1000 + startDistanceZ)
        {
            lastAch_DistanceTraveled1000 += 1000;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.TRAVEL1000METERS);
            //Debug.Log(lastAch_DistanceTraveled1000 + " is last distance traveled achievement");
        }

        if (statN_DistanceTraveled > lastAch_DistanceTraveled100000 + 100000 + startDistanceZ) 
        {
            lastAch_DistanceTraveled100000 += 100000;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.TRAVEL100000METERS);
        }

        if (statN_DistanceTraveled > lastAch_DistanceTraveled1000000 + 1000000 + startDistanceZ)
        {
            lastAch_DistanceTraveled1000000 += 1000000;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.TRAVEL1000000METERS);
        }

        isCheckingAchievementDistance = false;
    }

    IEnumerator ResetHangTimeAward()
    {
        yield return new WaitForSeconds(5f);
        canGiveNewHangTimeAward = true;
    }

    IEnumerator ResetTopSpeedAward()
    {
        yield return new WaitForSeconds(5f);
        canGiveNewTopSpeedAward = true;
    }

    IEnumerator PlayJumpVoice(float timeToWait)
    {
        yield return new WaitForSeconds(timeToWait);

        if (!GameManager.GetAudioFlyingVoices().IsPlaying()  isLanding  !GameManager.GetAudioFlyingVoices().audio.isPlaying)
        {
            GameManager.GetAudioFlyingVoices().PlayAudio();
        }
    }

    private void ManageTopSpeed()
    {
        curVel = rigidbody.velocity.magnitude;
        localVel = transform.InverseTransformDirection(rigidbody.velocity).magnitude;
        guiHandler.SetCurrentSpeed(curVel);
        if (curVel < 10f)
        {
            transform.rigidbody.AddForce(Vector3.Lerp(transform.rigidbody.velocity, new Vector3(transform.rigidbody.velocity.x, transform.rigidbody.velocity.y, 300f), Time.deltaTime * 1f));
        }
        if (curVel > statN_SpeedRecord)
        {
            statN_SpeedRecord = curVel;
            guiHandler.SetTopSpeed(statN_SpeedRecord);
            
            if (sibp)
            {
                if (sibp.TryIncreasingSpawnRate(true))
                {
                    spawnRateIncreaseCount++;
                }
            }

            if (statN_SpeedRecord > 30f)
            {
                if (canGiveNewTopSpeedAward)
                {
                    canGiveNewTopSpeedAward = false;
                    statN_LvlSpeedsBeaten++;
                    if (statN_LvlSpeedsBeaten == 3)
                    {
                        GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.TOPSPEED);
                        GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.SPEED);
                    }

                    const Utils.FxName fx = Utils.FxName.ChaosMissle;
                    float scale = Random.Range(1f, 2f);
                    GameManager.GetFxCaster().SpawnFX(fx, true, true, scale, GameManager.GetPlayerVehicleParent().transform.position, GameManager.GetPlayerVehicleParent().transform.rotation, GameManager.GetPlayerVehicleParent());

                    AddStatBonusCoinsCollected(topSpeedCoins);
                    msgDisplay.DisplayMessage("Top Speed!\r\n " + topSpeedCoins + " Coins!", Utils.MESSAGESTYLE.GOALS_TOPSPEED);
                    CoinStore.AddCurrency(CoinStore.CURRENCYTYPE.COINS, topSpeedCoins, true);
                    msgDisplay.UpdateGoldCount();
                    float multiplier = Random.Range(topSpeedCoinsMultiplier - 0.3f, topSpeedCoinsMultiplier + 0.3f);
                    topSpeedCoins = (int)(topSpeedCoins * multiplier);
                    TimeSpan topSpeed = TimeSpan.FromSeconds(statN_SpeedRecord);
                    if (Utils.GetKonPlatformBasic() == Utils.KONPlatformBasic.WEB  GameManager.GetMerchant() == Utils.MERCHANT.KONGREGATE)
                    {
                        KongregateAPI.SubmitStat(KongregateAPI.GetStatisticName(KongregateAPI.STATISTICNAME.TOPSPEED), (int)topSpeed.TotalMilliseconds);
                    }
                    GameManager.GetSpawnHandler().StartSpawning(SpawnHandler.SPAWNSTYLE.MASSIVEDROP, new[] { SpawnHandler.SPAWNNAME.HEALTHBALLS, SpawnHandler.SPAWNNAME.SPEEDBALLS }, 1f, 0.2f);
                    StartCoroutine(ResetTopSpeedAward());
                }
                CheckAchievements_Speed(statN_SpeedRecord);
            }
        }
    }

    //IEnumerator DoMiniBoost()
    //{
    //    yield return new WaitForSeconds(1);
    //    //Debug.Log("Boost");
    //    //rigidbody.velocity = transform.forward * 20;
    //    //rigidbody.velocity = transform
    //    isMiniBoosting = false;
    //}

    private void CheckAchievement_BoostLevels()
    {
        //BOOSTERLEVEL1, // collect 100 boost in a single run
        //BOOSTERLEVEL2, // collect 250 boost in a single run
        //BOOSTERLEVEL3, // collect 500 boost in a single run
        //BOOSTERLEVEL4, // collect 750 boost in a single run
        //BOOSTERLEVEL5, // collect 1000 boost in a single run
        
        int e = (int)engineTorque - 25;
        if (e >= lastBoostLevel1 + 100)
        {
            lastBoostLevel1 += 100;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BOOSTERLEVEL1);
        }

        if (e >= lastBoostLevel2 + 250)
        {
            lastBoostLevel2 += 250;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BOOSTERLEVEL2);
        }

        if (e >= (lastBoostLevel3 + 500))
        {
            lastBoostLevel3 += 500;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BOOSTERLEVEL3);
        }

        if (e >= (lastBoostLevel4 + 750))
        {
            lastBoostLevel4 += 750;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BOOSTERLEVEL4);
        }

        if (e >= (lastBoostLevel5 + 1000))
        {
            lastBoostLevel5 += 1000;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BOOSTERLEVEL5);
        }
    }

    public float GetEngineTorque()
    {
        return engineTorque;
    }

    public void AddToTorque(float amount = 0)
    {
        if (amount > 0)
        {
            engineTorque += amount;
        }
        else
        {
            if (GameManager.IsDemo()  engineTorque >= 124f)
            {
                GameManager.GetPnlDemoBoxBoost().SetActive(true);
            }
            else
            {
                engineTorque += engineTorqueAdd * ((GameManager.GetSkillsHandler().GetCurrentSkillAmount(Utils.SkillsNames.COINBOOST)) + 0.05f) * 1.3f;
            }
        }
        guiHandler.SetCurrentBoost(engineTorque);
        CheckAchievement_BoostLevels();
    }

    IEnumerator AchievementPennyPincher()
    {
        yield return new WaitForSeconds(600);
        if (!sledderRagdoll.activeSelf)
        {
            if (!GameManager.GetHasUsedCoinsThisRound())
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.PENNYPINCHER);
            }
        }
    }

    IEnumerator AchievementSurvivalist()
    {
        yield return new WaitForSeconds(1200);
        if (!sledderRagdoll.activeSelf)
        {
            if (GameManager.GetPlayer() != null)
            {
                GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.SURVIVALIST);
            }
        }
    }

    public void AddStatCollectibleRarity(Utils.COLLECTIBLERARITY rarityName, Utils.COLLECTIBLEITEMNAMES colName)
    {
        GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.TRINKETS);
        switch (rarityName)
        {
            case Utils.COLLECTIBLERARITY.COMMON:
                statN_CollectiblesCommon++;
                break;
            case Utils.COLLECTIBLERARITY.UNCOMMON:
                statN_CollectiblesUncommon++;
                break;
            case Utils.COLLECTIBLERARITY.RARE:
                statN_CollectiblesRare++;
                break;
            case Utils.COLLECTIBLERARITY.LENGENDARY:
                statN_CollectiblesLegendary++;
                break;
        }

        int common = statCollectiblesCommon + statN_CollectiblesCommon;
        int legendary = statCollectiblesLegendary + statN_CollectiblesLegendary;
        int rare = statCollectiblesRare + statN_CollectiblesRare;
        int uncommon = statCollectiblesUncommon + statN_CollectiblesUncommon;

        if ((common) >= 5  (legendary) >= 2  (rare) >= 5  (uncommon) >= 5)
        {
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.JUNKDEALER);
        }

        if (rarityName == Utils.COLLECTIBLERARITY.COMMON  common != 0  common % 15 == 0) { GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.COLLECT15COMMON); }
        if (rarityName == Utils.COLLECTIBLERARITY.UNCOMMON  uncommon != 0  uncommon % 10 == 0) { GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.COLLECT10UNCOMMON); }
        if (rarityName == Utils.COLLECTIBLERARITY.RARE  rare != 0  rare % 5 == 0) { GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.COLLECT5RARE); }
        if (rarityName == Utils.COLLECTIBLERARITY.LENGENDARY) { GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.COLLECT1LEGENDARY); }

        if (colName == Utils.COLLECTIBLEITEMNAMES.THEDIRTYHOE)
        {
            int amount = Utils.GetTrinketCount(Utils.COLLECTIBLEITEMNAMES.THEDIRTYHOE);
            if (amount != 0)
            {
                if (amount % 10 == 0)
                {
                    GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.HOESBEDIRTY);
                }
            }
        }

        if (colName == Utils.COLLECTIBLEITEMNAMES.HEARTSHAPEDBOX)
        {
            
            int amount = Utils.GetTrinketCount(Utils.COLLECTIBLEITEMNAMES.HEARTSHAPEDBOX);
            if (amount != 0)
            {
                if (amount % 10 == 0)
                {
                    GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.FEELTHELOVE);
                }
            }
        }

        if (colName == Utils.COLLECTIBLEITEMNAMES.SKINNYBOBBY)
        {
            if (Utils.GetKonPlatformBasic() == Utils.KONPlatformBasic.WEB  GameManager.GetMerchant() == Utils.MERCHANT.KONGREGATE)
            {
                KongregateAPI.SubmitStat(KongregateAPI.GetStatisticName(KongregateAPI.STATISTICNAME.SKINNYBOBBY), 1);
            }
        }
        
    }

    public void AddStatSpeedBoostsUsed(int amount)
    {
        statN_LvlSpeedBoostsUsed += amount;
        GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BOOSTUSE1);

        if ((statN_LvlSpeedBoostsUsed + statSpeedBoostsUsed) >= (lastAch_SpeedBoostUsed100 + 100))
        {
            lastAch_SpeedBoostUsed100 += 100;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.BOOSTUSE100);
        }
    }

    public void AddStatHealthPotionsUsed(int amount)
    {
        //Debug.Log("Adding " + amount + " health potions used");
        statN_LvlHealthPotionsUsed += amount;
        GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.HEALTHPOTIONUSE1);

        if (statN_LvlHealthPotionsUsed + statHealthPotionsUsed >= (lastAch_HealthPotionUsed100 + 100))
        {
            lastAch_HealthPotionUsed100 += 100;
            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.HEALTHPOTIONUSE100);
        }
    }

    public void AddStatBonusCoinsCollected(int amount)
    {
        statN_LvlBonusCoinsCollected += amount;
    }

    public void AddStatCoin(int amount)
    {
        statN_LvlCoinsCollected += amount;

        if (statN_LvlCoinsCollected > 20)
        {
            GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.COINS);
        }

        if (statN_LvlCoinsCollected > 40)
        {
            GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.BOOSTAMOUNT);
        }
        
        if (statN_LvlBonusCoinsCollected + statN_LvlCoinsCollected > 500)
        {
            GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.SHOP);
        }
        // insert coin achievement here
    }

    private void CheckAchievements_Death()
    {
        if (GameManager.Instance.isInGame)
        {
            // 100 death king
            Utils.AddToPlayerStat(Utils.PSTATS.DEATHS, 1);
            int deaths = Utils.GetPlayerStat(Utils.PSTATS.DEATHS);
            if (deaths >= 100)
            {
                if ((deaths / 100) % 1 == 0)
                {
                    GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.DEATHKING);
                }
            }

            if (deaths >= 25)
            {
                if ((deaths / 25) % 1 == 0)
                {
                    GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.DEATHMACHINE);
                }
            }

            GameManager.GetAchievementManager().UnlockAchievement(Utils.ACHIEVEMENTNAMES.DEATHVIRGIN);
        }
    }

    public void DoDeath()
    {
        if (!sledderRagdoll.activeSelf)
        {
            GameManager.GetAudioMusic().StopPlaying();
            GameManager.GetAudioHurtVoices().PlayAudio(100f);
            if (masterCamera.cameraRotationMode == MasterCamera.CameraRotation.FPS)
            {
                masterCameraScroll.ToggleCamModes();
            }
            masterCamera.smooth.Rotation = 50f;
            masterCamera.preferredDistance = 8f;
            StartCoroutine(LerpDeathCamWaitForDistanceChange());
            StopCoroutine("AchievementSurvivalist");
            StopCoroutine("AchievementPennyPincher");
            CheckAchievements_Death();
            UpdateStatsOnDeath();
            sled.AddComponent<Rigidbody>();
            sled.AddComponent<BoxCollider>();
            sled.transform.parent = null;

            sledderRagdoll.SetActive(true);
            sledderRagdoll.transform.parent = null;

            sledderAlive.SetActive(false);

            const Utils.FxName fx = Utils.FxName.DeathExplosionBase;
            float scale = Random.Range(4f, 8f);
            GameManager.GetFxCaster().SpawnFX(fx, true, true, scale, sledderRagdoll.transform.position, sledderRagdoll.transform.rotation, GameObject.Find("ArmatureRagdoll"));

            masterCamera.strCameraTargetFPS = sledderRootBone.name.ToString();
            masterCamera.strCameraTargetReg = sledderRootBone.name.ToString();
            masterCamera.player = sledderRootBone;
            masterCamera.rotationObject.transform.localEulerAngles = new Vector3(45f, masterCamera.rotationObject.transform.localEulerAngles.y, masterCamera.rotationObject.transform.localEulerAngles.z);
            //        masterCamera.player = sled;
            // 78 270 0
            rigidbody.drag = 3f;
            Rigidbody[] rbs = sledderRagdoll.GetComponentsInChildren<Rigidbody>();
            foreach (Rigidbody rb in rbs)
            {
                rb.drag = 0.1f;
                rb.angularDrag = 0.5f;
                rb.mass = 10f;
            }
            //StartCoroutine(ResetRagDollRigidbody(rbs));
             
            
            SkidmarksSled sms = sledderRagdoll.GetComponent<SkidmarksSled>();
            sms.player = sledderRagdoll;
            sms.ConfigureRigidbody();
            GameManager.GetSkidMarks().markWidth = 2f;
            DoFunnyDeathRoutine();
            if (audio_Slide.isPlaying || audio_Slide_IsPlaying)
            {
                audio_Slide.Stop();
            }
            GameManager.GetGameUI().pnlQuickBar.SetActive(false);
            GameManager.GetGameUI().pnlQuickBar_Mobile.SetActive(false);
            GameManager.GetGameUI().pnlFinishGame.SetActive(true);
            GameObject snow = GameObject.Find("Light Snow");
            snow.GetComponent<SnowFollowPlayer>().enabled = false;
            snow.GetComponent<SnowFollowPlayerTrailer>().enabled = true;
            StartCoroutine(DisplayStatsOnDeathDelay());
            Physics.gravity = new Vector3(0, -50, 0);
            enabled = false;
        }
    }

    IEnumerator ResetRagDollRigidbody(IEnumerable<Rigidbody> rbs)
    {
        yield return new WaitForSeconds(5f);
        foreach (Rigidbody rb in rbs)
        {
            rb.drag = 1.3f;
            rb.angularDrag = 0.8f;
        }
    }

    IEnumerator LerpDeathCamWaitForDistanceChange()
    {
        yield return new WaitForSeconds(2f);
        StartCoroutine(LerpDeathCam());
    }

    IEnumerator LerpDeathCam()
    {
        masterCameraScroll.enableWheelScrolling = true;
        masterCamera.preferredDistance = Mathf.Lerp(masterCamera.preferredDistance, 20f, Time.deltaTime * 0.1f);
        yield return new WaitForSeconds(7f);
    }

    private void DoFunnyDeathRoutine()
    {
        // todo
        //Debug.Log("************ Insert Funny Song Here 15 seconds Max Looping *************");
        //Debug.Log("************ Insert Funny Death Routine Here 5 seconds Max *************");
    }

    private void UpdateStatsOnDeath()
    {
        Utils.AddToPlayerStat(Utils.PSTATS.HEALTHPOTIONSUSED, statN_LvlHealthPotionsUsed);
        Utils.AddToPlayerStat(Utils.PSTATS.SPEEDBOOSTSUSED, statN_LvlSpeedBoostsUsed);
        Utils.AddToPlayerStat(Utils.PSTATS.COINSCOLLECTED, (statN_LvlCoinsCollected));
        Utils.AddToPlayerStat(Utils.PSTATS.BONUSCOINSCOLLECTED, statN_LvlBonusCoinsCollected);

        TimeSpan topSpeed = TimeSpan.FromSeconds(statN_SpeedRecord);

        if (Utils.GetKonPlatformBasic() == Utils.KONPlatformBasic.WEB  GameManager.GetMerchant() == Utils.MERCHANT.KONGREGATE)
        {
            KongregateAPI.SubmitStat(KongregateAPI.GetStatisticName(KongregateAPI.STATISTICNAME.TOPSPEED), (int)topSpeed.TotalMilliseconds);
        }
        if (topSpeed.TotalMilliseconds > statSpeedRecord)
        {
            statHandler.isNewSpeedRecord = true;
            Utils.SetPlayerStat(Utils.PSTATS.TOPSPEEDRECORD, (int)topSpeed.TotalMilliseconds);
        }
        
        TimeSpan hangSeconds = TimeSpan.FromSeconds( statN_BestHangTime );
        if (Utils.GetKonPlatformBasic() == Utils.KONPlatformBasic.WEB  GameManager.GetMerchant() == Utils.MERCHANT.KONGREGATE)
        {
            KongregateAPI.SubmitStat(KongregateAPI.GetStatisticName(KongregateAPI.STATISTICNAME.BESTHANGTIME), (int)hangSeconds.TotalMilliseconds);
        }
        if (hangSeconds.TotalMilliseconds > statBestHangTime)
        {
            statHandler.isNewHangTimeRecord = true;
            Utils.SetPlayerStat(Utils.PSTATS.BESTHANGTIME, (int)hangSeconds.TotalMilliseconds);
        }

        Utils.AddToPlayerStat(Utils.PSTATS.DISTANCETRAVELED, statN_LvlDistanceTraveled);
        Utils.AddToPlayerStat(Utils.PSTATS.COLLECTIBLESCOMMON, statN_CollectiblesCommon);
        Utils.AddToPlayerStat(Utils.PSTATS.COLLECTIBLESUNCOMMON, statN_CollectiblesUncommon);
        Utils.AddToPlayerStat(Utils.PSTATS.COLLECTIBLESRARE, statN_CollectiblesRare);
        Utils.AddToPlayerStat(Utils.PSTATS.COLLECTIBLESLEGENDARY, statN_CollectiblesLegendary);
        Utils.AddToPlayerStat(Utils.PSTATS.DAMAGETAKEN, statN_LvlDamageTaken);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_TOTALDAMAGE, statN_LvlDamageTaken);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_TOPSPEEDSBEATEN, statN_LvlSpeedsBeaten);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_BESTHANGTIMESBEATEN, statN_LvlBestHangTimesBeaten);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_COINSCOLLECTED, statN_LvlCoinsCollected);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_BONUSCOINSCOLLECTED, statN_LvlBonusCoinsCollected);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_SPEEDBOOSTSUSED, statN_LvlSpeedBoostsUsed);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_HEALTHPOTIONSUSED, statN_LvlHealthPotionsUsed);
        Utils.SetPlayerStat(Utils.PSTATS.LEVELRECORD_DISTANCETRAVELED, statN_LvlDistanceTraveled);
    }

    IEnumerator DisplayStatsOnDeathDelay()
    {
        yield return new WaitForSeconds(7f);
        //GameManager.GetPauseHandler().PauseGame(true);
        Destroy(GameManager.GetPlayer());
        GameManager.GetAudioPlayerDeathMusic().Play();
        if (guiHandler.pnlAchievements.activeSelf) { guiHandler.pnlAchievements.SetActive(false); }
        if (guiHandler.pnlShop.activeSelf) { guiHandler.pnlShop.SetActive(false); }
        if (guiHandler.pnlTrinkets.activeSelf) { guiHandler.pnlTrinkets.SetActive(false); }
        
        //guiHandler.btnTrinkets.GetComponent<UIButton>().isEnabled = false;
        //guiHandler.btnAchievements.GetComponent<UIButton>().isEnabled = false;
        //guiHandler.btnShop.GetComponent<UIButton>().isEnabled = false;
        //guiHandler.btnHelp.GetComponent<UIButton>().isEnabled = false;

        DisplayStatsOnDeath();
    }

    void DisplayStatsOnDeath()
    {

        statHandler.DisplayStatsWindowOnDeath();
        
    }

    private void CheckPauseForAudio()
    {
        if (GameManager.GetPauseHandler().IsGamePaused()  !isPausingAudio)
        {
            audio_Slide_IsPlaying = false;
            isPausingAudio = true;
            if (audio_Slide.isPlaying) { audio_Slide.Pause(); }
            if (audio_Jump1.isPlaying) { audio_Jump1.Pause(); }
            if (audio_Landing1.isPlaying) { audio_Landing1.Pause(); }
            if (audio_Falling.isPlaying) { audio_Falling.Pause(); }
            
        }
        else if (!GameManager.GetPauseHandler().IsGamePaused()  isPausingAudio)
        {
            audio_Slide_IsPlaying = false;
            isPausingAudio = false;
            if (audio_Slide.isPlaying) { audio_Slide.Pause(); }
            if (audio_Jump1.isPlaying) { audio_Jump1.Pause(); }
            if (audio_Landing1.isPlaying) { audio_Landing1.Pause(); }
            if (audio_Falling.isPlaying) { audio_Falling.Pause(); }
        }

    }

    private void CheckVelocityChanges()
    {
        int amount = 0;
        if (lastVel - velDam10 > curVel)
        {
            amount = 20;
        }
        else if (lastVel - velDam09 > curVel)
        {
            amount = 18;
        }
        else if (lastVel - velDam08 > curVel)
        {
            amount = 16;
        }
        else if (lastVel - velDam07 > curVel)
        {
            amount = 14;
        }
        else if (lastVel - velDam06 > curVel)
        {
            amount = 12;
        }
        else if (lastVel - velDam05 > curVel)
        {
            amount = 10;
        }
        else if (lastVel - velDam04 > curVel)
        {
            amount = 8;
        }
        else if (lastVel - velDam03 > curVel)
        {
            amount = 6;
        }
        else if (lastVel - velDam02 > curVel)
        {
            amount = 4;
        }
        else if (lastVel - velDam01 > curVel)
        {
            amount = 2;
        }

        if (amount != 0)
        {
            //Debug.Log("Damage @ : " + amount);
            DoLandingSmoke(amount);
            LowerTorque(amount);
            LandingDamage(amount);
        }
    }

    public void CheckPlayerDeath()
    {
        if (hpCur <= 0)
        {
            DoDeath();
        }
    }

    public void DoDamage(float amount)
    {
        if (!godMode  !isInvulnerable)
        {
            amount = amount * equippedVehicle.damageResistance;
            GameManager.GetToolTipHandler().ShowToolTip(ToolTipHandler.TOOLTIPNAME.HEALTHBAR);
            statN_LvlDamageTaken += (int)amount;
            
            if (hpCur - amount <= 0)
            {
                AddToHPCurrent(-amount, true, false);
                DoDeath();
            }
            else
            {
                GameManager.GetAudioHurtVoices().PlayAudio();
                AddToHPCurrent(-amount, true, false);
            }
        }
    }
    
    private void LandingDamage(float amount)
    {
        //Debug.Log("Landing Damage");
        if (amount >= minVelocityDamage)
        {
            //Debug.Log("hangtimeEnd = " + hangTimeEnd);
            amount = amount * 2;
            DoDamage(amount);
        }
        else if (amount > 0)
        {
            if (!isHangingTime) { GameManager.GetAudioAchievementVoices().PlayAudio(); }
        }
    }
    private void LowerTorque(int p)
    {
        engineTorque -= hitTorqueChange * p;
        GameManager.GetGameUI().SetCurrentBoost(engineTorque);
    }

    void DoLandingSmoke(float amount)
    {
        // make landing smoke
        if (velDamSmoke)
        {

            //whiteSmokePrefab.transform.position = transform.position;
            //whiteSmokePrefab.SetActive(true);
            isWhiteSmokePlaying = true;
            psWhiteSmoke.startSize = amount;
            psWhiteSmoke.emissionRate = 100;
            lastEmissionRate = 100f;
            //Debug.Log("Moved Smoke");
            StartCoroutine(DoLandingSmokeNow(amount / 2));
        }
        //else
        //{
        //    //Debug.Log("NoDamSmoke");
        //}

    }

    IEnumerator DoLandingSmokeNow(float time)
    {
        //velDamSmoke.Emit(pos, rigidbody.velocity, rigidbody.velocity.magnitude, rigidbody.velocity.magnitude, Color.white);
        yield return new WaitForSeconds(time);
        //whiteSmokePrefab.SetActive(false);
        psWhiteSmoke.emissionRate = 0;
        isWhiteSmokePlaying = false;
    }

    public void DoJump(bool force = false)
    {
        if ((!isJumping  IsGrounded()) || force)
        {
            isJumping = true;
            audio_Jump1.pitch = Random.Range(0.5f, 1.5f);
            audio_Jump1.Play();
            if (!audio_Falling.isPlaying)
            {
                audio_Falling.pitch = Random.Range (1.1f,1.5f);
                audio_Falling.pitch = (curVel / (slidingPitch * 2)) + 0.5f;
                audio_Falling.Play();
            }
            //Add up force
            rigidbody.AddForce(transform.up * 3000 * (curVel / 200) * speedJump * GameManager.GetSkillsHandler().GetCurrentSkillAmount(Utils.SkillsNames.JUMP));

            //boy.animation.Play("jump");
            // play jump animation
            if (curVel < speedMinToAddForceToJump)
            {
                //print ("Added Forward Force to " + curSpeed);
                rigidbody.velocity = transform.forward * 20;
                //rigidbody.AddForce (Vector3.forward * 100 * speedJump);
                //Debug.Log (transform.forward.ToString());
                //Debug.Log(Vector3.forward.ToString());

            }

            //Start WaitToJump
            StartCoroutine(WaitToJump());
        }
    }

    private void ManageRockets()
    {
        if (!isUsingRockets)
        {
            if (GameManager.GetAudioPlayerRockets().isPlaying)
            {
                foreach (GameObject goRocket in rocketBoosterFX)
                {
                    goRocket.SetActive(false);
                }
                GameManager.GetAudioPlayerRockets().Stop();
            }
        }
    }

    private void DoRockets()
    {
        if (!IsGrounded()  equippedVehicle.canUseRockets)
        {
            if (GameManager.GetOrkRocketFuelBarHandler().CanUseRockets())
            {
                isUsingRockets = true;
                if (!GameManager.GetAudioPlayerRockets().isPlaying)
                {
                    GameManager.GetAudioPlayerRockets().Play();
                }

                // do rocket fx here
                foreach (GameObject goRocket in rocketBoosterFX)
                {
                    goRocket.SetActive(true);
                }

                //consume fuel
                GameManager.GetOrkRocketFuelBarHandler().AddToCurrentFuel(GameManager.GetOrkRocketFuelBarHandler().rocketFuelConsumptionRate);
                
                //Add up force
                float baseBoost = GameManager.GetOrkRocketFuelBarHandler().rocketBoostBase;
                float gravity = GameManager.GetSkillsHandler().GetCurrentSkillAmount(Utils.SkillsNames.GRAVITY);
                if (gravity == 0) gravity = 0.05f;
                gravity += 1f;
                
                baseBoost = baseBoost * gravity;
                float multipliedBoost = baseBoost * equippedVehicle.rocketPower;

                //Debug.Log("Gravity = " + gravity.ToString() + " multiplied: " + multipliedBoost.ToString());
                
                rigidbody.AddForce(transform.up * multipliedBoost);
                
            }
            else
            {
                isUsingRockets = false;
            }
        }
    }

    void IncreatePerspectiveOnVelocity()
    {
        // if going faster than 10, then increase perspective
        if (curVel > 20f)
        {
            if (Camera.main.fieldOfView <= 130)
            {
                if (masterCamera.cameraRotationMode != MasterCamera.CameraRotation.FPS)
                {
                    if (Camera.main.fieldOfView >= 60)
                    {
                        if (curVel * 2.5f >= 60  curVel * 2.5f <= 130)
                        {
                            Camera.main.fieldOfView = Mathf.Lerp(Camera.main.fieldOfView, curVel * 2.5f, Time.deltaTime * 0.2f);
                        }
                    }
                }
            }
            if (masterCamera.cameraRotationMode == MasterCamera.CameraRotation.FollowBehind)
            {
                masterCamera.preferredDistance = Mathf.Lerp(masterCamera.preferredDistance, masterCameraScroll.LimitInner, Time.deltaTime * 0.15f);
                Vector3 lR = masterCameraRotation.transform.localEulerAngles;
                masterCameraRotation.transform.localEulerAngles = Vector3.Lerp(lR, new Vector3(15, lR.y, lR.z), Time.deltaTime * 0.05f);
            }
        }
        else
        {
            if (masterCamera.cameraRotationMode != MasterCamera.CameraRotation.FPS)
            {
                if (Camera.main.fieldOfView >= 60)
                {
                    Camera.main.fieldOfView = Mathf.Lerp(Camera.main.fieldOfView, 60f, Time.deltaTime * 0.2f);
                }
            }

            //if (masterCamera.preferredDistance < masterCameraScroll.LimitOuter)
            //{
            if (masterCamera.cameraRotationMode == MasterCamera.CameraRotation.FollowBehind)
            {
                masterCamera.preferredDistance = Mathf.Lerp(masterCamera.preferredDistance, masterCameraScroll.LimitOuter, Time.deltaTime * 1f);
                Vector3 lR = masterCameraRotation.transform.localEulerAngles;
                masterCameraRotation.transform.localEulerAngles = Vector3.Lerp(lR, new Vector3(20, lR.y, lR.z), Time.deltaTime * 1f);
            }
            //}
        }
    }

    //void GUI_MoveHorizontalIndicator(float distance)
    //{
    //    Debug.Log(distance);
    //    GameObject lbl = lblHorizontalIndicator;
    //    Vector3 posZero = lblHorizontalIndicator_zero_position;
    //    if ((distance <= 0.2f  distance >= 0) || (distance >= -0.2f  distance <= 0))
    //    {
    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, posZero, Time.deltaTime * 4);
    //        lbl.transform.position = posZero;
    //    }
    //    else if (distance > 0.8f)
    //    {
    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, new Vector3(2f, lbl.transform.position.y, lbl.transform.position.z), Time.deltaTime * 10f);
    //        lbl.transform.position = new Vector3(1f, lbl.transform.position.y, lbl.transform.position.z);
    //    }
    //    else if (distance < -0.8f)
    //    {
    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, new Vector3(-2f, lbl.transform.position.y, lbl.transform.position.z), Time.deltaTime * 10f);
    //        lbl.transform.position = new Vector3(-1f, lbl.transform.position.y, lbl.transform.position.z);
    //    }
    //    else
    //    {
    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, new Vector3(posZero.x + distance * 3, lbl.transform.position.y, lbl.transform.position.z), Time.deltaTime * 10);
    //        lbl.transform.position = new Vector3(posZero.x + distance, lbl.transform.position.y, lbl.transform.position.z);
    //    }
    //}

    //void GUI_MoveVerticalIndicator(float distance)
    //{
    //    Debug.Log(distance);
    //    GameObject lbl = lblVerticalIndicator;
    //    Vector3 posZero = lblVerticalIndicator_zero_position;
    //    if ((distance <= 0.2f  distance >= 0) || (distance >= -0.2f  distance <= 0))
    //    {

    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, posZero, Time.deltaTime * 4);
    //        lbl.transform.position = posZero;
    //    }
    //    else if (distance > 0.8f)
    //    {
    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, new Vector3(lbl.transform.position.x, posZero.y + 2f, lbl.transform.position.z), Time.deltaTime * 10f);
    //        lbl.transform.position = new Vector3(lbl.transform.position.x, posZero.y + 1f, lbl.transform.position.z);
    //    }
    //    else if (distance < -0.8f)
    //    {
    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, new Vector3(lbl.transform.position.x, posZero.y - 2f, lbl.transform.position.z), Time.deltaTime * 10f);
    //        lbl.transform.position = new Vector3(lbl.transform.position.x, posZero.y - 1f, lbl.transform.position.z);
    //    }
    //    else
    //    {
    //        //lbl.transform.position = Vector3.Lerp(lbl.transform.position, new Vector3(lbl.transform.position.x, posZero.y + distance, lbl.transform.position.z), Time.deltaTime * 10f);
    //        lbl.transform.position = new Vector3(lbl.transform.position.x, posZero.y + distance, lbl.transform.position.z);
    //    }
    //}

    public void CycleCam()
    {
        // cycle cam sound
        GameManager.GetAudioSFX().PlayAudioSpecific(false, (int)Utils.AUDIO_MSGFX.CYCLE_CAM);
        
        if (masterCamera.cameraRotationMode == MasterCamera.CameraRotation.FollowBehind)
        {
            GameManager.GetMsgDisplayBonus().DisplayMessage("First Person Cam", Utils.MESSAGESTYLEBONUS.NONE);
        }
        else if (masterCamera.cameraRotationMode == MasterCamera.CameraRotation.FPS)
        {
            GameManager.GetMsgDisplayBonus()
                .DisplayMessage(
                    Utils.GetKonPlatformBasic() == Utils.KONPlatformBasic.MOBILE ? "Pinch to Zoom Cam" : "Scroll Cam",
                    Utils.MESSAGESTYLEBONUS.NONE);
        }
        else if (masterCamera.cameraRotationMode == MasterCamera.CameraRotation.FollowBehindZoomControl)
        {
            GameManager.GetMsgDisplayBonus().DisplayMessage("Auto Cam", Utils.MESSAGESTYLEBONUS.NONE);
        }
    }

    private void GetPlayerInput()
    {
        if ((Input.GetKeyDown(KeyCode.G) || Input.GetKeyDown(KeyCode.C))  isUserInputEnabled)
        {
            CycleCam();
        }

        if (Utils.GetKonPlatformBasic() != Utils.KONPlatformBasic.MOBILE)
        {
            if (IsGrounded())
            {
                getAxisV = Input.GetAxis("Vertical") * sensV;
                getAxisH = Input.GetAxis("Horizontal") * sensH;
                if (isUnstableSled) { getAxisH = getAxisH * -1; }
            }
            else
            {
                getAxisV = Input.GetAxis("Vertical");
                getAxisH = Input.GetAxis("Horizontal");
            }
        }
        else
        {
            curAc = Vector3.Lerp(curAc, Input.acceleration - zeroAc, Time.deltaTime / smooth);
            // if can move left
            getAxisH = Mathf.Clamp(curAc.x * sensHMobile, -1f, 1f);
            getAxisV = Mathf.Clamp(curAc.y * sensVMobile, -1f, 1f);
        }

        //if (getAxisV > -0.2  curVel < 100f  engineTorque < 100f)
        //{
        //    getAxisV = 1f;
        //}

        if (getAxisH > 0.15f)
        {
            //isTurning = true;
            //RotateCameraBasedOnInput();
            //boy.animation.CrossFade("left");
        }
        // move right
        else if (getAxisH < -0.15f)
        {
            //isTurning = true;
            //RotateCameraBasedOnInput();
            //boy.animation.Play("right");
        }
        // move no where, the input wasn't strong enough
        else
        {
            getAxisH = 0;
            //isTurning = false;
        }

        // if we can move forward and backwards
        if (getAxisV > 0.2f)
        {
            //boy.animation.Play("fast");
        }
        // move backwards
        else if (getAxisV < -0.2f)
        {
            //boy.animation.Play("brake");

        }
        // move no where, the input wasn't strong enough
        else
        {
            getAxisV = 0;
            //boy.animation.Play("idle");
        }

        if (!isUserInputEnabled)
        {
            getAxisH = 0f;
            getAxisV = 0f;
        }

        //If the game is not running on a android device
        if (Utils.GetKonPlatformBasic() != Utils.KONPlatformBasic.MOBILE)
        {
            //If get Space key down and we can jump and is on the ground
            if ((Input.GetKeyDown(KeyCode.Space) || Input.GetButton("Jump"))  isUserInputEnabled)
            {
                DoJump();
            }

            if ((Input.GetKey(KeyCode.Space) || Input.GetButton("Jump"))  isUserInputEnabled)
            {
                DoRockets();
            }
            else
            {
                isUsingRockets = false;
            }
        }
        else
        {
            // setup android jump
            if (isUserInputEnabled)
            {
                foreach (Touch touch in Input.touches)
                {
                    //Touche phase = began
                    if (touch.phase == TouchPhase.Began)
                    {
                        //Set first touch position
                        startTouchPos = touch.position;
                    }
                    //Touch phase = moved
                    if (touch.phase == TouchPhase.Moved)
                    {
                        //If we can jump and is grounded and touch position y is bigger than first touch position y + 100
                        if (touch.position.y > startTouchPos.y + 100)
                        {
                            DoJump();
                        }
                    }

                    DoJump();
                }
            }
        }
    }

    void MoveGuiUpdate()
    {
        //GUI_MoveHorizontalIndicator(getAxisH);
        //GUI_MoveVerticalIndicator(getAxisV);
        IncreatePerspectiveOnVelocity();
    }

    void AutoRotateToDirectionMoving()
    {
        // turn in direction player is moving down slope from gravity
        Quaternion myQ = Quaternion.identity;
        if (rigidbody.velocity.magnitude > 0f)
        {
            if (rigidbody.velocity.magnitude > 0)
            {
                myQ = Quaternion.LookRotation(rigidbody.velocity);
            }
        }
        if (curVel > 5f)
        {
            myQ.z = transform.rotation.z;
            myQ.x = transform.rotation.x * .8f;
            transform.rotation = Quaternion.Slerp(transform.rotation, myQ, turnBackToStraightSensitivity);
        }
    }


    void ShiftGears()
    {
        // this funciton shifts the gears of the vehicle, it loops through all the gears, checking which will make
        // the engine RPM fall within the desired range. The gear is then set to this "appropriate" value.
        int appropriateGear = currentGear;
        if (curEngineRPM >= maxEngineRPM)
        {
            //if (curSpeed < speedMax)
            //{
                for (int i = 0; i < gearRatio.Length; i++)
                {
                    if (frontLeftWheel.rpm * gearRatio[i] < maxEngineRPM)
                    {
                        appropriateGear = i;
                        break;
                    }
                }

                currentGear = appropriateGear;
            //}
        }

        if (curEngineRPM <= minEngineRPM)
        {
            appropriateGear = currentGear;

            for (int j = gearRatio.Length - 1; j >= 0; j--)
            {
                if (frontLeftWheel.rpm * gearRatio[j] > minEngineRPM)
                {
                    appropriateGear = j;
                    break;
                }
            }
            currentGear = appropriateGear;
        }
    }

    void SledMotion()
    {
        
        // Compute the engine RPM based on the average RPM of the two wheels, then call the shift gear function
        curEngineRPM = (frontLeftWheel.rpm + frontRightWheel.rpm) + rearLeftWheel.rpm + rearRightWheel.rpm + rearRearWheel.rpm / 5 * gearRatio[currentGear];
        if (curEngineRPM > maxEngineRPM || curEngineRPM < maxEngineRPM)
        {

            ShiftGears();
        }

        // set the audio pitch to the percentage of RPM to the maximum RPM plus one, this makes the sound play
        // up to twice it's pitch, where it will suddenly drop when it switches gears.
        //audio_Slide1.pitch = Mathf.Abs(EngineRPM / MaxEngineRPM) + 0.5f ;
        

        // finally, apply the values to the wheels.	The torque applied is divided by the current gear, and
        // multiplied by the user input variable.
        frontLeftWheel.motorTorque = engineTorque / gearRatio[currentGear] * getAxisV;
        frontRightWheel.motorTorque = engineTorque / gearRatio[currentGear] * getAxisV;
        rearLeftWheel.motorTorque = engineTorque / gearRatio[currentGear] * getAxisV;
        rearRightWheel.motorTorque = engineTorque / gearRatio[currentGear] * getAxisV;
        rearRearWheel.motorTorque = engineTorque / gearRatio[currentGear] * getAxisV;


        // the steer angle is an arbitrary value multiplied by the user input.
        //frontLeftWheel.steerAngle = -(turnRadius * getAxisH);
        //frontRightWheel.steerAngle = -(turnRadius * getAxisH);
        //rearLeftWheel.steerAngle = (turnRadius * getAxisH);
        //rearRightWheel.steerAngle = (turnRadius * getAxisH);


        if (getAxisH == 0f)
        {
            AutoRotateToDirectionMoving();
        }
        else
        {
            //	Quaternion rot = rigidbody.transform.rotation;
            //	float rotY = rot.y * GetAxisH * turnSensitivity * Time.deltaTime;
            //	rot.Set(rot.x, rotY, rot.z, rot.w);
            //  Debug.Log(rotY);
            //  rigidbody.transform.rotation = rigidbody.transform.rotation * rot;
            //			string rotateLog = "";
            //			rotateLog += "Rotation Before rotate = " + transform.rotation.ToString() + System.Environment.NewLine;
            if (IsGrounded())
            {
                transform.Rotate(Vector3.up * getAxisH * turnSensitivity * Time.deltaTime * equippedVehicle.steeringSensitivity, Space.World);
            }
            else
            {
                transform.Rotate(Vector3.up * getAxisH * turnSensitivity * Time.deltaTime * equippedVehicle.steeringSensitivity / 2, Space.World);
            }

            //			rotateLog += "Rotateion AFTER rotate = " + transform.rotation.ToString() + System.Environment.NewLine;
            //			rotateLog += "rotated around " + (Vector3.up * GetAxisH * turnSensitivity * Time.deltaTime).ToString() + System.Environment.NewLine;
            //			rotateLog += "Vector3.up = " + Vector3.up.ToString() + System.Environment.NewLine;
            //			rotateLog += "GetaxisH = " + GetAxisH.ToString() + System.Environment.NewLine;
            //			rotateLog += "turnSensitivity = " + turnSensitivity.ToString() + System.Environment.NewLine;
            //			rotateLog += "DeltaTime = " + Time.deltaTime.ToString() + System.Environment.NewLine;
            //			Debug.Log(rotateLog);
        }

        //		if (isTurning){
        //			FrontLeftWheel.enabled = false;
        //			FrontRightWheel.enabled = false;
        //			RearLeftWheel.enabled = false;
        //			RearRightWheel.enabled = false;
        //		}else{
        //			FrontLeftWheel.enabled = true;
        //			FrontRightWheel.enabled = true;
        //			RearLeftWheel.enabled = true;
        //			RearRightWheel.enabled = true;
        //		}	
    }

    private void CheckVelocity()
    {
        //if (curVel > maxVelocity  IsGrounded()  localVel > 0f)
        //{
        //    float x = rigidbody.velocity.x;
        //    float y = rigidbody.velocity.y;
        //    float z = rigidbody.velocity.z;

        //    //if (x < 0) { x = 0 - x * slowDownForce; } else { x = (0 - x) * slowDownForce; }
        //    //if (y < 0) { y = 0 - x * slowDownForce; } else { y = (0 - y) * slowDownForce; }
        //    //if (z < 0) { z = 0 - x * slowDownForce; } else { z = (0 - z) * slowDownForce; }

        //    if (x < 0) { x = 0 - x; } else { x = (0 - x); }
        //    if (y < 0) { y = 0 - x; } else { y = (0 - y); }
        //    if (z < 0) { z = 0 - x; } else { z = (0 - z); }

        //    Vector3 negativeVelocity = new Vector3(x, y, z);
        //    //rigidbody.AddForce(negativeVelocity);
        //    rigidbody.AddRelativeForce(negativeVelocity * slowDownForce, ForceMode.Force);
        //    //Debug.Log(negativeVelocity.ToString());
        //}
        if (curVel < speedMinToAddForceToJump  canShowMiniBoostTip  transform.position.z < 2500  IsGrounded())
        {
            canShowMiniBoostTip = false;
            msgDisplay.DisplayMessage("Press Jump to Mini Boost!", Utils.MESSAGESTYLE.NONE);
            StartCoroutine(WaitToShowMiniBoostTip());
        }
    }

    IEnumerator WaitToShowMiniBoostTip()
    {
        yield return new WaitForSeconds(10f);
        canShowMiniBoostTip = true;
    }

    IEnumerator WaitToJump()
    {
        //Wait 1 second
        yield return new WaitForSeconds(1);
        //We can jump
        isJumping = false;
    }
}

Woah, thanks man, i can see that this must have taken alot of time, and i really appreciate it! I remember playing a early version of your game like 2 months ago or so and you really have had alot of progress. On my side however i’ll have to study what everything does so i can filter out what i need for my own game :slight_smile:

“Edit”
Jesus, you have put alot of work into perfecting this, but the truth is that i have about two week to make a simple downhill snowboarding/skiing game. Unfortunatly i don’t have enough time to go through all 2200 lines (Amazing, by the way) so maybe i should find something more simple to make.

Last thing
It would be great if you started a downhill / alpine game series tutorial seeing as it’s winter now, and that there is, as far as i know, no such tutorials out there. (Atleast not good ones)

Also, make sure you cover the Unity Car Tutorial. I worked a long time without using wheel colliders, but every method I took was a dead end and a HUGE waste of time. I had no idea, getting into this, I was going to end up using vehicle physics and wheel colliders, but it was definitely the way to go in hindsight. Unity Asset Store - The Best Assets for Game Making

Not that you mentioned it, but just in case, the ski starter kits on the Unity Asset Store is basically worthless.

Is there any way of adding a constant speed forwards, while having the rigidbody attatched? Because so far i can rotate the character but because there is no force forwards it just glides frictionless along the terrain. If you have a idea it would really help.

use wheel colliders and engine torque that is constantly on. Don’t use rigidbodies for moving, use rigidbodies for sliding and player stability.

*i mean rigidbodies with colliders for sliding and player stability.

This sounds like fun and a great idea, i might end up doing that.

this is the motherload, no one else will give you this. it is predetermined that you will; take this and make it awesome.

I did in fact

I did in fact check out the so called “ski starter kit” package from the asset store, it was absolute rubbish :slight_smile: I played the demo, and the only thing it seemed to acomplish was that it made me laugh my rearend off. I have no idea why he would publish that in the asset store, or how it was accepted.

If:

If you do make a tutorial, could you please send me a PM? I would highly appreciate it! :slight_smile: Looking forwards to this!

Looking into the car tutorial now :face_with_spiral_eyes:

Found a solution!

I managed to get something really good, yet simple by using a physical material, rigidbody, and the line “rigidbody.AddRelevantForce(x,y,z);” It works great so far and i got some really cool mechanics, i will try to make a video showing my current progress soon.

Here we go:
Took me a bit more than a week, but here is the result:

Hope you like it!

pretty good.

Needed something to jump like a ramp. Also more then 1 crystal would have been good.

Played nice and smooth anyway.