Line Rider (Issues & Code inside)

Well it’s all good fun getting back in to Codea, although it hasn’t taken long for me to be stumped. I’m in the middle of creating a line rider clone (a teeny tiny bit different) so I can get the hang of Codea again, I’ve been looking at more math than I can get my head round but so far I’ve come up with these good :# results:
https://www.youtube.com/watch?v=dqELP6LBLgA

I’m hoping I can improve this at some point but right now I feel there’s not much point me carrying it on until I can get past this problem:
https://www.youtube.com/watch?v=XbVDdJ7nuxA

I’m putting the code below for anyone with any inkling on how to help either problem or for anyone who wants to play about and if you can read the code then hopefully learn something too as some of it was new to me.

http://pastebin.com/L2gGMS5F

Dit looks cool, but how exactly do I play?

Sorry, was late last night when I put it up. Press and drag with one finger to draw lines, two finger drag/pinch to move and zoom. You can edit lines by pressing on a line vertex and dragging the vertex. Play will run the simulation and stop will stop the simulation and reset the rider to the start. Pause will pause the simulation leaving the rider where he is (good for continuing tracks).

Hi @Luatee , this has come up a couple of times before hasn’t it?

https://codea.io/talk/discussion/2838/inaccuracy-of-the-physics-engine

https://codea.io/talk/discussion/3795/game-loop-physics-step-question

Do you see the same behaviour on odd/ even runs, like in the above thread?

Box2D should be deterministic.

@yojimbo2000 I tried running my 2 ball program that you point to in the link above. I had to make a change to the text statement so both sets of text don’t overlay each other. I let it run for 10 loops and the results for loops 2 thru 10 were exactly the same as loop 1. That tells me that either there was a fix in the physics engine for restarts, or the change from 32 to 64 bit math fixed it.

@yojimbo2000 It should be deterministic but I’m not even seeing odd and even runs like the project I did before, this seems to be okay on some runs, even consecutively and then on others it’s wrong.

Having had a read through those posts, I have noted something said in there about box2D changing results when a arbitrary new body has been added to the scene. It seems more reproduceable now but the problem it seems lies with adding new lines to the scene, which looks unavoidable to me… any clues?

Code update, allows nose manuals and bit better physics. Fixed issue with odd and even runs (I think) but the issue that persists is the simulation outcome changes when more lines are added to the scene. Trying to determine what is causing this but a few things I’ve tried have brought no results.

Code: http://pastebin.com/x2chrLRX

Video: https://www.youtube.com/watch?v=JRNbek72VW8

Having had a read through some box2D threads, I’ve come across what may be an answer to this problem.

http://www.box2d.org/forum/viewtopic.php?t=1800

In this thread it’s stated that box2D cannot be deterministic if it is running on floating point math, which I believe Codea box2D is. It seems fairly obvious that you might get different out comes based on this. What the user does mention is there is a fixed integer version of box2D that apparently will solve this. It may be worth @Simeon or @John having a look at this?

@Luatee Apparently you didn’t read all of the posts in the above link. In the post 2nd from the bottom, a person says he works at Gas Poeered Games and he says that floating point math is deterministic. As for new bodies being added to the run, I wrote another program that had 8 balls bouncing around in an area and I would print out 10 pairs of x,y positions for one of the balls each time it collided 10 times. I saved those values so I could compare the results of subsequent runs. Letting it go for several runs, all of the x,y positions were the same as the first run. I then added 12 more balls that were colliding in a seperate area of the screen from the original 8 balls, and after several runs of the 8 balls and 12 balls colliding in seperate areas, the x,y values of the 8 balls were always the same as the original run. When I increased the ball count from 12 to 24, the x,y values for the 8 balls were still the same. So as far as I can tell, it doesn’t matter if more objects are added.

@dave1707 I did read that as well, and he seems correct in saying that which makes me think it’s either the code itself which I’ve scoured up and down and cannot find a fix, otherwise it would be box2D integration in Codea.

Another problem which I remember mentioning before is box2D doesn’t pause when Codea pauses, as Codea just stops the frame tick. Box2D is then seeing this huge time step and making exaggerated calculations based on how long its paused.

Also @dave1707 another difference between your test and this is you use the body by itself and bounce it off others, in my code I’m not colliding any bodies through box2D, I’m using a bit of math to do the line collisions, which should be deterministic in nature?

I believe a fixed time step would solve this, I’ve been doing a lot of testing and I’ve made a link between slight FPS fluctuations and outcome difference. As there is no way to alter the time step or set it to fixed in Codea would @Simeon or @John be able to chime in with the feasibility of this?

http://stackoverflow.com/questions/39152985/is-there-a-way-to-enable-fixed-point-calculations-in-box2d I think this would also fix the problem noted above that is caused when a Codea pauses, the time step ends up the same size as the Codea pause time and usually ends up with my player being off the map when resumed. With a fixed time step you wouldn’t get the increase.

Is this a realistic expectation or should I put this game to rest?

@Luatee It is possible to get a fixed timestep in Codea… If you implement your own physics engine / integration.

Here’s an example of something which uses a fixed timestep. It’s a kind of springy line thing visually, but the code is more generic. I wrote it quite a long time ago from reading this article (so I’ve forgotten how it works exactly). I hope you find it helpful :).


--# Main
-- Timestep

-- Use this function to perform your initial setup
function setup()
    
    integrator = RK4()
    
    parameter.number("force_k",0,50,2)
    parameter.number("force_b",0,20,0.5)
    
    tbl = {
        position=0, 
        velocity=0,
        force = function(self,t)
            local k = force_k
            local b = force_b
        
            if CurrentTouch.state == MOVING then
                k = 0
            end
            return -k * self.position - b * self.velocity
        end
    }
    
    state = integrator:createState(tbl)
    prevstate = state
    
    t = 0
    dt = 0.1
    
    
    time = Timestep({integrator=integrator})
    
    txt = ""
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)

    
    -- Do your drawing here
    
    local curstate = time:integrate(state)
    
    txt = string.format("%.2f %.2f",curstate.position, curstate.velocity)

    
    local y = HEIGHT/2 + curstate.position
    
    line(0,HEIGHT/2,WIDTH/2,y)
    line(WIDTH/2,y,WIDTH,HEIGHT/2)
    
    text(txt, WIDTH/2, HEIGHT/2 + curstate.position)
end

function touched(touch)
    
    local inc
    
    if touch.deltaY < 0 then
       -- inc = -inc
    end
    
    state.position = state.position + touch.deltaY
    
   -- state.velocity = state.velocity + -touch.deltaY 
end

--# RK4
RK4 = class()

local function newstate(state)
    state = state or {}
    state.position = state.position or 0.0
    state.velocity = state.velocity or 0.0
    state.momentum = state.momentum or 0.0
    state.mass = state.mass or 0.0
    state.invmass = state.invmass or 0.0
    
    state.recalculate = function(self)
        self.velocity = self.momentum * self.invmass
    end
    
    state.force = state.force or function(self,t)
            local k = 10
            local b = 1
            return -k * self.position - b * self.velocity
    end
    
    return state
end

local function newderivative()
    return { velocity=0.0, force=0.0 }
end

--http://gafferongames.com/game-physics/integration-basics/

-- float accelleration(State state, float t)
local function force(state,t)
    return state:force(t)
end

local function derivative(state,t)
    local value = newderivative()
    
    value.velocity = state.velocity
    value.force = force(state, t)
    
    return value
end

--Derivative evaluate(State initial, float time, float dt, Derivative d)
local function evaluate(initial,t,dt,d)
    local state = newstate()
    
    state.position = initial.position + d.velocity * dt
    state.velocity = initial.velocity + d.force * dt
    
    state.force = initial.force
    
    return derivative(state,t+dt)
end

local function delta(a,b,c,d)
    return 1.0 / 6.0 * (a + 2.0 * (b +c) + d)
end

local function increment(v,d,dt)
    return v + d * dt
end

-- void integrate(State state, float t, float dt)
local function integrate(state,t,dt)
    local a,b,c,d
    
    a = derivative(state, t)
    b = evaluate(state, t, dt * 0.5, a)
    c = evaluate(state, t, dt * 0.5, b)
    d = evaluate(state, t, dt * 0.5, c)
    
    local dxdt = delta(a.velocity,b.velocity,c.velocity,d.velocity)
    
    local dvdt = delta(a.force,b.force,c.force,d.force)
    
    state.position = increment(state.position, dxdt , dt)
    state.velocity = increment(state.velocity, dvdt, dt)
end

function RK4:init()
    
end

function RK4:createState(state)
    return newstate(state)
end

function RK4:integrate(state,t,d)
    integrate(state, t, dt)
end


--# Timestep
Timestep = class()

function Timestep:init(tbl)
    tbl = tbl or {}
    self.integrator = tbl.integrator
    
    self.t = 0.0
    self.dt = 0.01
    self.curTime = os.clock()
    self.acc = 0

end

local function calculateAlpha(alpha, prevstate, curstate)
    local state = integrator:createState(curstate)
    state.position = state.position * alpha + prevstate.position * (1-alpha)
    return state
end

function Timestep:integrate(state)
    if self.integrator == nil then return end
    
    local newtime = os.clock()
    local frameTime = newtime - self.curTime
    self.curTime = newtime
    
    self.acc = self.acc + frameTime  
    
    local prevstate = state
    
    while self.acc >= self.dt do
        
        prevstate = self.integrator:createState(state)
        
        self.integrator:integrate(state,t,dt)
        
        self.acc = self.acc - self.dt
        
        self.t = self.t + self.dt 
        
    end
    
    local alpha = self.acc / self.dt
    
    return calculateAlpha(alpha,prevstate,state)
end

@Luatee I took your code and added 2 tables so I could compare the rocket position on different runs to a previous run. What I saw when I would do play and then stop, play, stop, the 2 tables would be the same up to a certain offset. Sometimes they would be the same for only 6 displays, and sometimes to 30. It varied as it kept doing play, stop. I’m not about to try and debug your code, but there might be something not quite right with the restarts. I’ll keep playing with other code to see if I can find a problem with physics restarts.

Interesting @dave1707. I’ve been destroying, deleting and recreating the spaceship body in my latest attempt but I still see the problem. I wouldn’t expect anyone to spend time trying to debug ‘unclean’ code as it time consuming and I can do that, I’m curious to know if someone else has come across problems like this and their way of solving it. I know you have a fair bit of experience in this area so your insight is as good as mine.

@Luatee My test program has 13 balls bouncing around the screen. I save the x value of one of the balls in a table every 5th collision. After 100 collisions, I destroy all 13 balls and recreate them saving the x value in a second table to compare with the first run.

What I’m seeing in my test program is:

  1. Doing a physics pause and resume has no effect on the results. The numbers from the first run is the same as the other runs no matter how long I pause the program.

  2. Starting the program at 60 FPS or as low as 6 FPS give the same numbers for all the runs. The numbers at 6 FPS are the same as 60 FPS.

  3. Changing the FPS from 60 to 6 while the program is running results in the even/odd matches. The even runs have the same values and the odd runs have the same values, but the even/odd values are different from each other.

What I get out of number 3 is that the physics engine might get out of sync with the draw function in some way.

I’m still trying different things just to see what kind of results I get. My program is very simple, so I have better control over what happens. Your program is complex, so it’s a lot harder to see what’s happening.

EDIT: Starting the program at 3 FPS give the even/odd results right off.

@Luatee I tried some more testing with another program. This one had 1 ball falling with gravity. After 100 draw cycles, I would save the y position. I could put the ball back to the starting position anytime I touched the screen. What I found was that I could get 9 different y values after several runs. That means you could have different runs with your program as well whenever you press play. I’m not sure why my physics program that uses gravity has different values after starting again, but the ones that don’t use gravity have the same values after starting again…

@dave1707 that’s a great observation, when I’m back at my iPad tomorrow I’ll test out using a constant addition of negative y velocity to simulate gravity and see if that negates the issue with physics.gravity() (which I assume is the one you’re talking about).

@Luatee Yes, physics gravity. Giving it a constant initial downward velocity might fix the problem. I’ll give it a try and see what I get.

@Luatee I tried my program with an initial downward linearVelocity and the results I got were still different. If I let the program restart itself after a certain number of draw cycles, it would show the same results for runs 2 thru whatever. But those runs would be different from run 1. If I manually do a restart by touching the screen, the results were also different. There would be 2 sets of numbers. Sometimes they would equal the initial run and sometimes they would be equal to the other run. They wouldn’t alternate, but it seemed like it just happened to be when I touched the screen. So it seems like it just depends on when the physics engine decides to restart.