Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Craft applyForce is erratic

edited November 2021 in Bugs Posts: 1,363

Attached file includes detailed comments and code showing

  1. applyForce fails after a while, then recovers, then fails again.
  2. applyForce works less well if called during scene:update than if called during draw.

What you should see: Run the program. The ball bounces up and down, reversed by the reverse function every two seconds. After a while (7 or 8 seconds on my iPads) the whack will miss and the ball will continue on. The program continues to try to reverse it every two seconds. Usually, after a further while, it'll succeed and the ball will bounce up and down wherever it now is. (Usually off screen.)

Failure comes sooner and more frequently when doing the reverse during update rather than draw per the toggle.

ONE MORE THING: I recommend setting the parameter to false, not true, to begin. I'll put the proper zip file below.

Further explication available of course.

Comments

  • Posts: 1,363

    I think most of this behavior is consistent with the object sometimes falling asleep and waking up, but of course there could be many other causes.

  • edited November 2021 Posts: 1,363

    Zip File Here. Also, a movie of it failing is in this article.

  • dave1707dave1707 Mod
    Posts: 10,047

    @RonJeffries Ran your zip program. I ran 100 draw whacks with no problem then I switched to scene whacks. When I reached about 170 whacks, I noticed that the force was now 720 which means it missed 2 reverse forces. I ran it again, this time with 200 draw whacks and 100 scene whacks with no errors.

    I wonder if the draw cycle is executing with a certain cycle rate and the physics engine is executing with a certain rate. But there’s a problem when the 2 execute at exactly the same time and the result of the physics applyforce gets missed. I think you mentioned at one time your device ran at 120 fps. I wonder if the fps varies, you hit the sweet spot more often and miss the applyforce more.

  • Posts: 1,363

    interesting. i would not think it could accelerate beyond the starting speed: it calcs the force as 2x the negative of current velocity.

    it's weird, obv machine dependent. i hope @John can explain, and fix.

    I'll have to see if i can figure out how it can possibly accelerate. seems impossible.

    thanks!

  • dave1707dave1707 Mod
    Posts: 10,047

    @RonJeffries When I was out of wack, I had a force of 720 going back and forth. I was also going back and forth higher up on the screen. So somewhere I missed 2 reverse forces and ended up higher on the screen.

  • JohnJohn Admin Mod
    Posts: 779

    @RonJeffries I'll look into this because it's probably due to a few different things. Bullet3D and physics engines in general make use of numerical integration, which is subject to accumulation of errors over time. This means that precise forces applied over multiple frames are not precise enough, leading to unexpected behaviour as compared to analytic solutions from Newtonian equations

    That said, I need to look into exactly what timestep is being used by Craft, and use your example to experiment with it as well as make sure the fixedUpdated(dt) method is working which would provide you with precise delta time values used by the physics engine

  • dave1707dave1707 Mod
    Posts: 10,047

    @RonJeffries @John Here’s a really whacked up run. Look at the last 2 outputs. Here’s how/why it happened. I paused the program 2 times for about a second each time. Even though the draw function was paused and the ball wasn’t moving, the physics engine was still running. I’m not sure what it was calculating, but that’s the results. I didn’t pause the program that I mentioned above with the 720, but it might be something similar but on a shorter time scale.

  • Posts: 1,363

    ah. yes. I'd bet the engine uses wall clock time, so that a pause can get some weird answers ... pausing is surely gonna confuse it. now i have to the that for fun!

  • dave1707dave1707 Mod
    Posts: 10,047

    @RonJeffries The physics engine doesn’t stop unless you do a physics.pause(). There’s other things that still run even though the program is paused. The touched function is one of them. Those functions run normally, they just don’t communicate with the normal code until it starts running again. Once the code is started, it gets updated with wherever those functions were doing.

  • JohnJohn Admin Mod
    Posts: 779

    @RonJeffries it doesn't use wall clock time exactly. What happens is each frame the delta time is accumulated into a buffer. When that buffer reaches 1/60th of a second (physics fixed delta time) it does an update (and more than one if there's more multiples of 1/60 in the buffer). This ensures that all physics updates are always using the same timestep, while keeping up or slowing down based on varying framerates

    What I suspect is happening, since fixedUpdate is not working you get this:

    -- Frame 1
    scene:update(dt)
    -- physics update is triggered, force is applied from last time, rigidbody velocity is updated
    applyForce(-vel)
    
    -- Frame 2
    scene:update(dt)
    -- physics update skipped this frame due to slight differences in DeltaTime and buffer hasn't filled yet
    applyForce(-vel) -- velocity is not up to date and calculation is wrong
    
    -- Frame 3
    scene:update(dt)
    -- physics updates twice but velocity is already wrong
    
    etc...
    

    Once I get fixedUpdate() working properly we'll see what happens.

    Could also have something like

    scene.onFixedUpdate = function(dt)
    end
    for hooking into the scene fixed update directly?
  • Posts: 1,363

    @John thanks ... I'll have to think on that a bit. I am not surprised that physics time needs to be constant, and glad that it is. It's the only way a momentum calc could even be close.

    What we see happening in my program, though, doesn't seem quite explained. The velocity in my case is always 2. I set it to 2 at the beginning, and you can see from the output numbers and the force always being 240 that it's always read as 2 when I apply the force.

    (240 is the force you need to exactly reverse a mass 1 object traveling at speed 2 in 1 60th of a second.)

    I specifically coded the program so that if it works, velocity will always be 2 or -2 and since we see the velocity display always being 2 or -2 and the whack always 240 or -240, I don't think we have a problem that can be explained by a missing update due to a minor delta time difference. Next time around vel will still be 2 and the required force still 240.

    What we see after a while is that vel stays at, say -2, and we hit it again and again with 240 and it doesn't slow down or reverse, it just keeps going. Then, after a while, it does reverse and things start to work.

    I suspect it's something about activation and sleeping, not a near-miss timing issue. See what I'm at?

  • Posts: 1,363

    The scene.onFixedUpdate and object:fixedUpdate, it seems to me, need to happen at the very beginning of the physics cycle so that they'll be taken into account if they do something like applyForce.

    One way or another, you want to be sure that the impulse is always transferring momentum equivalent to the force for 1/60th. Yes?

  • Posts: 1,363

    @dave1707 that's fascinating, that pause just pauses the draw cycle. I never knew that. I'm not sure how that explains the weird velocity of the ball, though, since we won't even do a scene:update, since we don't even do a draw.

    What am I missing?

  • dave1707dave1707 Mod
    Posts: 10,047

    @RonJeffries After playing around some more with this, it appears the Craft physics is paused when the code is paused. I’m not sure how many cycles it runs before it gets paused. The regular physics doesn’t get paused.

  • Posts: 1,363

    odd. I wonder why they don't pause everything. anyway good to know.

  • JohnJohn Admin Mod
    Posts: 779

    I've pushed a new beta build that has some physics system changes/fixes. Let me know if it helps.

  • edited December 2021 Posts: 1,363

    how can i get the beta, please

  • JohnJohn Admin Mod
    Posts: 779

    @RonJeffries send me your apple id email and I'll add you to TestFlight

  • Posts: 1,363

    ronjeffries@acm.org

  • JohnJohn Admin Mod
    Posts: 779

    @RonJeffries added you to the beta, you should get an email with instructions

  • Posts: 1,363

    Ty will try to get to it. Email arrived.

  • edited December 2021 Posts: 1,363

    @John It still misses updates sometimes in both modes. Appears the the force has effect six times, then fails in 7th, then fails six and works in seventh, etc. Acting as if the object is asleep then awake, then asleep?

    Is there a way to keep official version, or am I stuck on beta now? Beta seems fine for now ... just wondering.

  • dave1707dave1707 Mod
    Posts: 10,047

    @RonJeffries If you want to go back to the regular Codea version, go to the App Store and download that Codea version. You’ll get a message or something, but just ignore it. It been awhile since I’ve loaded a normal version.

    From TestFlight, you can download different previous versions.

  • JohnJohn Admin Mod
    Posts: 779

    @RonJeffries this is to be expected. I tested your example and got the same results. Try changing the component script to use fixedUpdate(dt) instead, which I found worked every time

  • Posts: 1,363

    Agreed, works as expected on fixedUpdate. Do you know why it doesn't;t work in the other areas? Time frames shifting against each other or something?

  • JohnJohn Admin Mod
    Posts: 779

    @RonJeffries basically it's the specific combination of measuring velocity and applying force while fixed update happens either 0, 1 or 2 times per frame (which is due to small fluctuations in delta time), leading to velocity values that are sometimes a frame behind the actual values during physics simulation

    Not sure how to make it work outside of fixed update, there may be a better way to order things internally

  • Posts: 1,363

    I guess fixed update is fine, it's kind of the right place. What about the awake state not matching the flag?

  • dave1707dave1707 Mod
    Posts: 10,047

    @John Here’s an example showing inconsistencies with sleepingAllowed = true. The ball on the left uses applyForce and the right linearVelocity. When the program starts, both show that they’re awake. After about 2 seconds, they’ll show sleeping because they haven’t moved yet. If you tap the screen, the applyForce will cause it’s ball to wake and it will move. The linearVelocity won’t. Should it? If you start the program and tap the screen before they sleep, they’ll both move but sleep after a few seconds. Tapping the screen will move the applyForce ball.

    Increasing their initial speed in the code will prevent them from sleeping once they start, but is there a reason that if they’re not moving fast enough, they’ll sleep.


    viewer.mode=FULLSCREEN function setup() scene = craft.scene() scene.physics.gravity = vec3(0,0,0) scene.camera:add(OrbitViewer, vec3(0,0,0), 60, 0, 1000) create() fill(255) end function update(dt) scene:update(dt) scene.debug:line(vec3(-10,-1,0), vec3(10,-1,0), color(255)) end function draw() update(DeltaTime) scene:draw() text("applyForce",WIDTH/2-100,HEIGHT-130) text("linearVelocity",WIDTH/2+100,HEIGHT-130) if aa.awake then text("awake",WIDTH/2-100,HEIGHT-100) else text("not awake",WIDTH/2-100,HEIGHT-100) end if bb.awake then text("awake",WIDTH/2+100,HEIGHT-100) else text("not awake",WIDTH/2+100,HEIGHT-100) end end function create() a1 = scene:entity() aa = a1:add(craft.rigidbody, DYNAMIC, 1) aa.sleepingAllowed = true a1.position = vec3(4,0,0) a1:add(craft.shape.sphere, 1) a1.model = craft.model.icosphere(1,2) a1.material = craft.material(asset.builtin.Materials.Standard) b1 = scene:entity() bb = b1:add(craft.rigidbody, DYNAMIC, 1) bb.sleepingAllowed = true b1.position = vec3(-4,0,0) b1:add(craft.shape.sphere, 1) b1.model = craft.model.icosphere(1,2) b1.material = craft.material(asset.builtin.Materials.Standard) end function touched(t) if t.state==BEGAN then aa:applyForce(vec3(0,40,0)) bb.linearVelocity=vec3(0,.7,0) end end
  • JohnJohn Admin Mod
    Posts: 779

    @dave1707 There are a few extra internal settings related to sleeping, threshold and time. So there is a minimum speed that objects must be below before they will consider going to sleep (if they can), and a time delay before that happens

    I'll play around with your demo to see what it looks like, but I could also expose the additional sleep settings as well

  • Posts: 1,363

    The thing with setting the velocity property is that without a lot of rigmarole, it's probably hard to make it wake the object. Imagine how hard it'd be in our own code. We'd probably define a method setVelocity() and maybe a getter, and hide the property.

  • JohnJohn Admin Mod
    Posts: 779

    @RonJeffries I've found and fixed an issue in the 3D physics implementation related to clearing forces. It might work better now for things like this (for instance, calling applyForce inside a touched callback)

  • Posts: 1,363

    Super. Let me know when it hits a Beta and I'll try to mess with it, though I can see how doing it in fixedUpdate would be the right thing, depending on how asynchronous draw and physics are. Are they in separate threads, cooperating coroutines, or what?

Sign In or Register to comment.