{"id":515,"date":"2024-12-12T16:17:38","date_gmt":"2024-12-12T15:17:38","guid":{"rendered":"https:\/\/powered-up-games.com\/blog\/wordpress\/?p=515"},"modified":"2025-01-07T01:53:01","modified_gmt":"2025-01-07T00:53:01","slug":"schrodingers-bug","status":"publish","type":"post","link":"https:\/\/powered-up-games.com\/blog\/wordpress\/archives\/515","title":{"rendered":"Schr\u00f6dinger&#8217;s bug"},"content":{"rendered":"\n<p>Any programmer with sufficient experience has probably encountered a Heisenbug. That is, a bug that goes away when you try to debug it. The name is, of course, a pun on Heisenberg whose uncertainty principle describes a similar situation in the world of quantum physics. But its twin, the Schr\u00f6dinbug, is even more elusive.<\/p>\n\n\n\n<p>The Schr\u00f6dinbug is a bug that doesn&#8217;t happen until someone checks the code and realises that it shouldn&#8217;t ever have worked. And henceforth, earlier and previously tested versions of the code also no longer work. This one is a pun on Schr\u00f6dinger and his hypothetical cat.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;I can&#8217;t believe that!&#8221; said Alice.<br>&#8220;Can&#8217;t you?&#8221; the Queen said in a pitying tone.<\/p>\n<cite>\u2014&nbsp;Lewis Carroll,&nbsp;Alice in Wonderland<br><br><\/cite><\/blockquote>\n\n\n\n<p>A student asked how to implement a jump using Unity&#8217;s <strong><code>CharacterController <\/code><\/strong>component.<\/p>\n\n\n\n<p>The Unity documentation handily provides an example which does that. Not just that. It also applies horizontal movement. So what follows is the example code with the horizontal movement removed. That is, this code performs a jump, but no other movement.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.75rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .75rem);line-height:1rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0\">JavaScript<\/span><span role=\"button\" tabindex=\"0\" data-code=\"using UnityEngine;\n\npublic class Example : MonoBehaviour\n{\n    private CharacterController controller;\n    private Vector3 playerVelocity;\n    private bool groundedPlayer;\n    private float jumpHeight = 1.0f;\n    private float gravityValue = -9.81f;\n\n    private void Start()\n    {\n        controller = gameObject.AddComponent&lt;CharacterController&gt;();\n    }\n\n    void Update()\n    {\n        groundedPlayer = controller.isGrounded;\n        if (groundedPlayer &amp;&amp; playerVelocity.y &lt; 0)\n        {\n            playerVelocity.y = 0f;\n        }\n\n        \/\/ Makes the player jump\n        if (Input.GetButtonDown(&quot;Jump&quot;) &amp;&amp; groundedPlayer)\n        {\n            playerVelocity.y += Mathf.Sqrt(jumpHeight * -2.0f * gravityValue);\n        }\n\n        playerVelocity.y += gravityValue * Time.deltaTime;\n        controller.Move(playerVelocity * Time.deltaTime);\n    }\n}\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">using<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">UnityEngine<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">Example<\/span><span style=\"color: #D8DEE9FF\"> : <\/span><span style=\"color: #8FBCBB\">MonoBehaviour<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">CharacterController<\/span><span style=\"color: #D8DEE9FF\"> controller<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Vector3<\/span><span style=\"color: #D8DEE9FF\"> playerVelocity<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">bool<\/span><span style=\"color: #D8DEE9FF\"> groundedPlayer<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> jumpHeight <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> 1<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">0<\/span><span style=\"color: #D8DEE9\">f<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> gravityValue <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\">9<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">81<\/span><span style=\"color: #D8DEE9\">f<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">void<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Start<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">controller<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">gameObject<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">AddComponent<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">CharacterController<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">void<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Update<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">groundedPlayer<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">controller<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">isGrounded<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">groundedPlayer<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&amp;&amp;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">playerVelocity<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">y<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">playerVelocity<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">y<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> 0<\/span><span style=\"color: #D8DEE9\">f<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Makes the player jump<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">Input<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetButtonDown<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Jump<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">) <\/span><span style=\"color: #81A1C1\">&amp;&amp;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">groundedPlayer<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">playerVelocity<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">y<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">+=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Mathf<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Sqrt<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">jumpHeight<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\">2<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">0<\/span><span style=\"color: #D8DEE9\">f<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">gravityValue<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">playerVelocity<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">y<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">+=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">gravityValue<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Time<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">deltaTime<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">controller<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Move<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">playerVelocity<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Time<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">deltaTime<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Except it doesn&#8217;t. At least, it doesn&#8217;t jump reliably on my machine. Your mileage may vary because (as we will see) this bug depends on the machine that runs it. For me, nine times out of ten, pressing the jump button (which is assigned to the space bar by default) does nothing.<\/p>\n\n\n\n<p>The code looks fairly familiar. In particular, looking at the last two lines, it is using the <a href=\"\/blog\/wordpress\/archives\/438\" data-type=\"page\" data-id=\"2\">Symplectic Euler Integration<\/a> method that I wrote about recently. And like that method, this code also suffers from falling short. However, that inaccuracy is not responsible for this bug.<\/p>\n\n\n\n<p>Adding a debug log line before testing the jump, we can see that the <strong><code>groundedPlayer<\/code><\/strong> flag is not consistently set when the player object is sitting on the ground. The ratio of <strong><code>True<\/code><\/strong> and <strong><code>False<\/code><\/strong> values over multiple frames defines the probability that pressing the jump button will result in a jump. In a sense, the code is making a dynamic measurement of its own runtime environment and changing its behaviour depending on the result.<\/p>\n\n\n\n<p>But why should the <strong><code>groundedPlayer<\/code><\/strong> flag ever be <strong><code>False<\/code><\/strong> while the player is on the ground? We can see from the code, that this is sampled from the <strong><code>controller.isGrounded<\/code><\/strong> property. And looking into the <a href=\"https:\/\/docs.unity3d.com\/6000.0\/Documentation\/ScriptReference\/CharacterController-isGrounded.html\">documentation for this property<\/a>, we get a clue for what might be happening. This clue is phrased in the form of a question.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Was the CharacterController touching the ground during the last move?<\/p>\n<cite>\u2014 Unity documentation<\/cite><\/blockquote>\n\n\n\n<p>Changing the code so that the <code><strong>Move<\/strong><\/code> call only happens when the <code><strong>CharacterController<\/strong><\/code> is falling or the player has pressed the jump key, this particular problem goes away. Although it introduces a new issue, where the <code><strong>CharacterController<\/strong><\/code> would effectively no longer react to changes in the environment. For example, if the surface that the <code><strong>CharacterController<\/strong><\/code> was standing on were a trap door, and it fell away, the <code><strong>CharacterController<\/strong><\/code> would be left hanging in the air. This would be the equivalent of putting an object to sleep in PhysX, but without its accompanying wake up call.<\/p>\n\n\n\n<p>However, the example code <em>does <\/em>call <code><strong>Move<\/strong><\/code> every frame because the <code><strong>CharacterController<\/strong><\/code> is always experiencing a downward acceleration due to gravity. And yet still the <code><strong>isGrounded<\/strong><\/code> flag is not reliably getting set each frame that the <code><strong>CharacterController<\/strong><\/code> is on the ground. To analyse what is going on I got the example code to output both the time and y position of the <code><strong>CharacterController<\/strong><\/code> as a CSV file and put that into a graph:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/LSBUGPG\/CharacterControllerBug\/refs\/heads\/main\/jump.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>Three things seem odd about this graph. The first is that it doesn&#8217;t rest at height 1, but rather 1.08. Secondly, the peak of the jump appears flattened. And finally, and perhaps most importantly, I expected to see it jitter while on the ground showing times when the <code><strong>isGrounded<\/strong><\/code> flag wasn&#8217;t set. But instead, it is perfectly flat. A look at the values in the <code><strong>CharacterController<\/strong><\/code> component generated by the example code explains some of these oddities.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/LSBUGPG\/CharacterControllerBug\/refs\/heads\/main\/CharacterControllerDefaults.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>The Skin Width value of 0.08 explains why the <code><strong>CharacterController<\/strong><\/code> rests at 1.08 rather than 1. It is adding this value to the collider to push the object above the surface. And the Min Move Distance value explains why the jump is flattened at the top where the movement per frame was reduced to a distance less than 0.001.<\/p>\n\n\n\n<p>A look at the <a href=\"https:\/\/docs.unity3d.com\/6000.0\/Documentation\/ScriptReference\/CharacterController-minMoveDistance.html\">documentation for this latter parameter<\/a> gives us a second clue:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>If the character tries to move less than this distance, it will not move at all.<\/p>\n<cite>\u2014 Unity documentation<\/cite><\/blockquote>\n\n\n\n<p>Putting these two clues together: if the <code><strong>CharacterController<\/strong><\/code> moves less than the <code><strong>minMoveDistance<\/strong><\/code>, then it doesn&#8217;t move, and doesn&#8217;t collide with the ground, and sets the <code><strong>isGrounded<\/strong><\/code> flag to <code><strong>False<\/strong><\/code>. Assuming we start in a grounded state, the movement in the first frame should be:<\/p>\n\n\n\n<div class=\"wp-block-katex-display-block katex-eq\" data-katex-display=\"true\"><pre>motion = playerVelocity_{y} \\times Time.deltaTime<\/pre><\/div>\n\n\n\n<p>And <code><strong>playerVelocity.y<\/strong><\/code> in turn should be:<\/p>\n\n\n\n<div class=\"wp-block-katex-display-block katex-eq\" data-katex-display=\"true\"><pre>playerVelocity_{y} = gravityValue \\times Time.deltaTime<\/pre><\/div>\n\n\n\n<p>Therefore:<\/p>\n\n\n\n<div class=\"wp-block-katex-display-block katex-eq\" data-katex-display=\"true\"><pre>motion = gravityValue \\times (Time.deltaTime)^2<\/pre><\/div>\n\n\n\n<p>Re-arrange this in terms of <code><strong>Time.deltaTime<\/strong><\/code> and we get:<\/p>\n\n\n\n<div class=\"wp-block-katex-display-block katex-eq\" data-katex-display=\"true\"><pre>(Time.deltaTime)^2 = \\frac{motion}{gravityValue}\n<\/pre><\/div>\n\n\n\n<p>And:<\/p>\n\n\n\n<div class=\"wp-block-katex-display-block katex-eq\" data-katex-display=\"true\"><pre>Time.deltaTime = \\sqrt{\\frac{motion}{gravityValue}}<\/pre><\/div>\n\n\n\n<p>So given:<\/p>\n\n\n\n<div class=\"wp-block-katex-display-block katex-eq\" data-katex-display=\"true\"><pre>motion = minMoveDistance = 0.001 \\\\\ngravityValue = 9.81<\/pre><\/div>\n\n\n\n<p>The critical timestep is:<\/p>\n\n\n\n<div class=\"wp-block-katex-display-block katex-eq\" data-katex-display=\"true\"><pre>\\sqrt{\\frac{0.001}{9.81}} \\approx 0.01<\/pre><\/div>\n\n\n\n<p>Or, 100 frames per second.<\/p>\n\n\n\n<p>So, at an evenly paced frame rate slightly lower than 100 frames per second, the minimum move distance will be reached each frame and the <code><strong>isGrounded<\/strong><\/code> flag will always be set. But with higher frame rates, or uneven frame pacing, the move distance will not be reached and the <code><strong>isGrounded<\/strong><\/code> flag will not be set.<\/p>\n\n\n\n<p>And now I think I see why this bug has hidden for so long. This bug can be traced back to as early as Unity 2019.<\/p>\n\n\n\n<p>Earlier versions still have the same code issues but the bug doesn&#8217;t show for two additional reasons. Firstly, the code uses <code><strong>GetButton<\/strong><\/code> rather than <code><strong>GetButtonDown<\/strong><\/code> to trigger the jump. This means that there might be a small lag between pressing the button and the jump triggering, but it is unlikely that the user input will be quick enough to miss a grounded frame. And secondly, the Unity Editor doesn&#8217;t run at an unlocked frame rate and so is locked to the vsync of your monitor. However, I think with a modern high refresh rate monitor and keyboard it might be possible to see this at least as far back as Unity 2017.<\/p>\n\n\n\n<p>So, have I finally uncovered a Schr\u00f6dinbug? I certainly discovered this only recently; it should never have worked; and now it can be seen to not work in versions reaching back 7 years.<\/p>\n\n\n\n<p>Five impossible things to go, then I can have breakfast!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Many believe that the Schr\u00f6dinbug is impossible. But believing that something is impossible isn&#8217;t always enough to prevent it happening&#8230; <\/p>\n","protected":false},"author":1,"featured_media":515,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,6,5],"tags":[24,39,32,35],"class_list":["post-515","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-process","category-programming","category-video-games","tag-education","tag-programming","tag-unity","tag-video-games"],"_links":{"self":[{"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts\/515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/comments?post=515"}],"version-history":[{"count":12,"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts\/515\/revisions"}],"predecessor-version":[{"id":560,"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts\/515\/revisions\/560"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts\/515"}],"wp:attachment":[{"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/media?parent=515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/categories?post=515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/powered-up-games.com\/blog\/wordpress\/wp-json\/wp\/v2\/tags?post=515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}