Howdy, Stranger!

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

Line Rider (Issues & Code inside)

edited February 26 in Code Sharing Posts: 1,595

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:

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:

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

Comments

  • Posts: 107

    Dit looks cool, but how exactly do I play?

  • Posts: 1,595

    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).

  • Posts: 1,972

    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.

  • dave1707dave1707 Mod
    Posts: 5,744

    @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.

  • Posts: 1,595

    @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.

  • Posts: 1,595

    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?

  • Posts: 1,595

    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:

  • Posts: 1,595

    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?

  • dave1707dave1707 Mod
    Posts: 5,744

    @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.

  • Posts: 1,595

    @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?

  • edited March 4 Posts: 1,595

    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?

  • Posts: 141

    @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
  • dave1707dave1707 Mod
    Posts: 5,744

    @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.

  • Posts: 1,595
    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.
  • dave1707dave1707 Mod
    edited March 4 Posts: 5,744

    @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.

  • dave1707dave1707 Mod
    edited March 6 Posts: 5,744

    @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..

  • Posts: 1,595
    @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).
  • dave1707dave1707 Mod
    Posts: 5,744

    @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.

  • dave1707dave1707 Mod
    Posts: 5,744

    @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.

  • Posts: 1,595

    Hmm, I have tried numerous possible solutions but nothing solid. I'm afraid this might not be possible without a fixed time step version of Box2D. It would solve problems with box2D overcompensating when a Codea is paused too as far as I can tell.

    Without any idea of the integration of box2D in Codea it's hard to gauge exactly what it is but the timestep issue seems intuitive. I was hoping Simeon or John would give their view on this.

Sign In or Register to comment.