Saving a Game
Blitz3D Forums/Blitz3D Beginners Area/Saving a Game
| ||
I've never played a real commercial game that can't save. How do I save my game on my hard drive so I can continue where I left off? |
| ||
You have to design it in from the start (well, you *could* add load/save routines after the fact, but it would not be enjoyable). Should the user be able to save at the start of a level (which would involve data about which level he was on and his stats), at at predetermined checkpoint (level, stats, checkpoint and scripted element status) or anywhere he likes (level, stats, scripted element status, enemy status etc)? The more flexible the system, the more data you will have to read and write with the file commands. You will also have to design your game in such a way that play can be initiated by reading the data off the save game file and having the engine place/update all the elements as needed - I would get that in early, using a dummy file for a new game if needs be. |
| ||
Check out the commands like WriteFile, ReadFile. The examples show how to open and close these, along with reading and writing information to them. Other applicable commands are WriteInt, ReadInt, etc. |
| ||
I posted something to the code archive a long time back (in the File Utils section)... http://www.blitzbasic.com/codearcs/codearcs.php?code=1284 Basically, it allows you to very easily save Types. You then write a custom parser to read them back in to your game. There are some routines for reading/writing XML in there that might be more suitable. |
| ||
How simple would that be if we could save a snapshot of the ram, program pointer etc. then simply reload the ram etc. and set the program pointer to where it was saved before. I think this wouldn't work cause of dynamic Ram/Vram management and other inconsistant factors of the OS. So when you want to have a flexible, powerful SaveGame then I'd suggest you make it saveable from beginning of the game project on, because it's real hard to add it to a full game. |
| ||
The savegame function in a game I'm working on was designed from practically the very start. If there is anymore info I need to save, I can always add that later. |
| ||
Thanks for all of your answers! |
| ||
I want to save my game by pressing F6 And load it by pressing F7 how do you do that? |
| ||
;main game loop repeat if KeyDown(64) Then SaveGameFunction() if KeyDown(65) Then LoadGameFunction() ProcessOtherInput() ProcessAIandStuff() RenderWorld Flip Until KeyDown(1) You then need to write your Load and save routines inside the two named functions. You will need a bit of planning to avoid variable scope issues... Putting all variables inside types is one way to achieve this |
| ||
thanks black jumper in a game How do you save your entities current position in a game into a save file just curious :) |
| ||
file = writefile("savegame.dat") writefloat file,entityx(entity) writefloat file,entityy(entity) writefloat file,entityz(entity) etc... closefile file Like mentioned above you need to think about saving/loading early on in the project. In my engine all entities are saved to file and the way i designed it makes its simple to both save and reload. |
| ||
I was playing with Blackjumpers code a bit but it says "parameter must be positive" I guess I goofed up a bit What I was trying also writefloat commands in my way seems to save and when the program starts up it never gives memory access violation problem like Sinu's above. here's my code with some of Blackjumpers too AppTitle "My little box" Graphics3D 800,600,32,2 SetBuffer BackBuffer() Type test Field x Field y Field name$ End Type For count = 1 To 4 n.test = New test n\x = Rand(10) n\y = Rand(10)+100 n\name$ = Chr(Rand(26)+65) +Chr(Rand(26)+65) +Chr(Rand(26)+65) Next camera=CreateCamera() light=CreateLight() RotateEntity light,0,0,90 cube=CreateCube() PositionEntity cube,0,0,5 tex=LoadTexture("IMAGE3.BMP") brush1=CreateBrush() BrushTexture brush1,tex PaintMesh cube,brush1 BrushShininess brush1,1 Repeat If KeyDown(200)=True Then MoveEntity cube,0,0,1 If KeyDown(203)=True Then MoveEntity cube,-1,0,0 If KeyDown(205)=True Then MoveEntity cube,1,0,0 If KeyDown(208)=True Then MoveEntity cube,0,0,-1 If KeyHit(64) Then save If KeyHit(65) Then load UpdateWorld RenderWorld Flip Until KeyHit(1) End Function save() fileout = WriteFile("Shay1.SM") For n.test = Each test Print Str$(n) WriteString (fileout, Str$(n)) WriteFloat (fileout,cube) WriteFloat (fileout,EntityX) WriteFloat (fileout,EntityY) WriteFloat (fileout,EntityZ) Next CloseFile( fileout ) Cls Print "game data written to file... press any key to continue" Print WaitKey End Function Function load() Print "... now reading from disk..." filein = ReadFile("Shay1.SM") While Not Eof(filein) Read1$ = ReadString$( filein ) Read1$ = ReadFloat( filein ) RestoreTestInfo(Read1$) Wend End Function Function RestoreTestInfo( SavedString$ ) Print "Read from file --> " + SavedString$ SavedString$ = Mid$( SavedString$, 2, Len(SavedString$)-2) ; remove end square brackets firstcomma = Instr(SavedString$, ",") firstvalue% = Left$(SavedString$, firstcomma-1) ; convert first value (up to comma) to an int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to 1st comma firstcomma = Instr(SavedString$, ",") secondvalue% = Left$(SavedString$, firstcomma-1) ; convert up to new first comma to another int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to new 1st comma ThirdString$ = Mid$( SavedString$, 2, Len(SavedString$)-2) ; remove quotes from string reloaded.test = New test ; make a new type reloaded\x = firstvalue% ; and assign the reloaded\y = secondvalue% ; reloaded values reloaded\name$ = ThirdString$ ; to the fields End Function |
| ||
Hi sefery, I have modified your code below to show a working example of saving and loading object positions (using F6 and F7 keys.) Note that my Str$ saving example save a human readable text file, but you were then trying to save machine-format floats at the end of this... This is probably a bad idea. This example only saves and loads Ints for position, but as mentioned in my CodeArc entry, there is a float parsing function available. It would make sense to create specific ParseInt(), ParseFloat() and ParseString() functions and then use these inside RestoreTestInfo() rather than hacking away at the SavedString$ as I have done here... I just think that the above example makes it more obvious what is going on rather than hiding processing away in functions. I will add the code above to the codeArc entry. [edit] BTW, I accidentally spent a couple of hours on this when it repeatedly crashed my machine... I eventually tracked it to a corrupted savefile - the While not EOF... Wend was never exiting ! Moral: be careful if you decide to manually edit that human-readable text file ;-) Also.... remember that any strings should not be allowed to contain commas ',' or they will wreck the parsing of the saved type ! [/edit] |
| ||
I want to save my game by pressing F6 And load it by pressing F7 how do you do that? As a general rule, I'd NOT put the Load/Save keys immediately next to each other, but put them at least one key apart (ie one key between them). The reason is, I like to play old Sega CD games on my PC using the Wgens emulator. They have a couple of F-keys used to Load and Save, and they are right next to each other. Let me tell you, I hit the wrong one more than once. It's more than merely annoying -- when you are really into your game, and have just mastered some difficult situation in the game, it is really an "aaarrrggghhh" situation when you hit the Load F-key instead of the Save F-key because they're right next to each other. Trust me on this one. |
| ||
how Do I write a parse command? |
| ||
here is add's Parser function... http://www.blitzbasic.com/codearcs/codearcs.php?code=161 ... and the example of usage... http://www.blitzbasic.com/codearcs/codearcs.php?code=162 |
| ||
I created a parser for a game, which is a little different from the above. In essence, I have to print text in a limited width column. My parser searches through text that is wider than the column, looking for spaces, and then prints everything up to the space, and the remainder is moved down to the next line, deleting from the string the text already printed, and repeats until the string is fully printed out. |
| ||
I figured out how to get the results I wanted So I'm sharing it with the rest of guys for your input on it. AppTitle "My little box" Graphics3D 800,600,32,2 SetBuffer BackBuffer() Global message$ = "move the boxes... '[' and ']' to change box" Type test Field x Field y Field z Field name$ Field shape Field anim End Type For count = 1 To 1 n.test = New test n\x = Rand(10) n\y = Rand(10) n\z = Rand(10)+10 n\name$ = Chr(Rand(26)+65) +Chr(Rand(26)+65) +Chr(Rand(26)+65) n\shape = LoadAnimMesh("BOX.b3d") Animate n\shape,1,2,0,100 EntityColor n\shape, count*50, 0,0 Next camera=CreateCamera() light=CreateLight() RotateEntity light,0,0,90 Global reload.test Global control.test = Last test maxshape = control\shape control.test = First test minshape = control\shape ;box=LoadAnimMesh("BOX.b3d") ScaleEntity n\shape,.1,.1,.1 PositionEntity n\shape,0,-.9000,5 TranslateEntity n\shape,30,-.70,-.70 EntityShininess n\shape,1 EntityBlend n\shape,1 SetAnimKey n\shape,15,True,True,True AddAnimSeq(n\shape,15) EntityAutoFade n\shape,80,70 Repeat If KeyDown(200)=True Then control\y = control\y + 1 If KeyDown(203)=True Then control\x = control\x - 1 If KeyDown(205)=True Then control\x = control\x + 1 If KeyDown(208)=True Then control\y = control\y - 1 If (KeyHit(26)=True And control\shape <> minshape) Then control = Before control If (KeyHit(27)=True And control\shape <> maxshape) Then control = After control For update.test = Each test PositionEntity update\shape, update\x, update\y, update\z EntityColor update\shape,count*50, 0,0 Next If KeyHit(64) Then save If KeyHit(65) Then load UpdateWorld RenderWorld Text 20, 20, message$ Flip Until KeyHit(1) End ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function save() fileout = WriteFile("Shay1.SM") For n.test = Each test Print Str$(n) WriteString (fileout, Str$(n)) Next CloseFile( fileout ) Cls message$ = "game data written to file..." FlushKeys End Function ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function load() Cls For destroy.test = Each test FreeEntity destroy\shape Delete destroy Next filein = ReadFile("Shay1.SM") While Not Eof(filein) Read1$ = ReadString$( filein ) If Read1$<>"" Then reload.test = RestoreTestInfo(Read1$) EndIf Wend DebugLog "finished with loaded file" FlushKeys control = First test End Function ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function RestoreTestInfo.test( SavedString$ ) message$ = "Read from file --> " + SavedString$ + " ... " DebugLog message$ ; typical type looks like ... [-3,-4,14,"LQT",18898368] SavedString$ = Mid$( SavedString$, 2, Len(SavedString$)-2) ; remove end square brackets firstcomma = Instr(SavedString$, ",") xvalue% = Left$(SavedString$, firstcomma-1) ; convert first value (up to comma) to an int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to 1st comma firstcomma = Instr(SavedString$, ",") yvalue% = Left$(SavedString$, firstcomma-1) ; convert up to new first comma to another int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to new 1st comma firstcomma = Instr(SavedString$, ",") zvalue% = Left$(SavedString$, firstcomma-1) ; convert up to new first comma to another int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to new 1st comma ThirdString$ = Mid$( SavedString$, 2, Len(SavedString$)) ; remove first quote from string firstcomma = Instr(SavedString$, ",") ThirdString$ = Mid$(SavedString$, 2, firstcomma-3) message$ = message$ + "x: " + xvalue + " y: " + yvalue + " z: " + zvalue + " name: " + ThirdString$ DebugLog message$ reloaded.test = New test ; make a new type reloaded\x = xvalue% ; and assign the reloaded\y = yvalue% ; reloaded values reloaded\z = zvalue% reloaded\name$ = ThirdString$ ; to the fields reloaded\shape = LoadAnimMesh("BOX.b3d") ScaleEntity reloaded\shape,.1,.1,.1 Animate reloaded\shape,1,2,0,100 Return (reloaded) End Function ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| ||
Great.... now go make us a kick ass "box 'em up" - or whatever! ;-) |
| ||
;Initialise Graphics mode Graphics3D 1024,768,32 SetBuffer BackBuffer() Const GRAVITY#=-0.01 Global message$ = "move the boxes... '[' and ']' to change box" Type test Field x Field y Field z Field name$ Field box Field update End Type For count = 1 To 1 n.test = New test n\x = Rand(10) n\y = Rand(10) n\z = Rand(10)+10 n\name$ = Chr(Rand(26)+65) +Chr(Rand(26)+65) +Chr(Rand(26)+65) box = LoadAnimMesh("model\box.b3d") Animate box,1,2,0,100 Next Global reload.test Global control.test = Last test maxshape = control\box control.test = First test minshape = control\box ;Camera has to be set, so the scene is visible Camera=CreateCamera() CameraRange Camera,1,8000 PositionEntity camera,30,0,1,True ;Light The Scene, so that it doesn't look flat Light=CreateLight() RotateEntity Light,90,0,0 ;Create the "Terrain" Entity... terrain=LoadMesh("Land\Land2.b3d") PositionEntity terrain,40,17,0 RotateEntity terrain,0,0,0 TranslateEntity terrain,40,-20,-.1 ;...and texture it Grass=LoadTexture("Land\terrain-1.jpg") EntityTexture terrain,Grass ;Create A Border Line Wall=CreateCube() ScaleMesh Wall,600,600,600 PositionMesh Wall,60,17,0 EntityAlpha Wall,0 EntityOrder Wall,20 FlipMesh Wall ;Create the "Box" entity and animate it ;box=LoadAnimMesh("model\box.b3d") ScaleEntity box,.1,.1,.1 PositionEntity box,0,-.9000,5 TranslateEntity box,30,-.70,-.70 EntityShininess box,1 SetAnimKey box,15,True,True,True AddAnimSeq(box,15) Animate box,1,2,0,100 ;Set Collision types fot the "collisions" command 1-player, 2-ground 3-border type_box=1 type_terrain=2 type_Wall=2 EntityType box,1 EntityType terrain,2 EntityType Wall,2 ;Adjust Radius of Sphere, that's used for Sphere-Polygon collisions box_radius#=1 EntityRadius box,box_radius ;Initialise Collisions between player,ground and border Collisions type_box,type_terrain,2,3 Collisions type_terrain,type_box,2,3 Collisions type_Wall,type_terrain,2,3 Collisions type_terrain,type_Wall,2,3 ;Dither scene, so that it does look good with 16 Bit Color-Depth ; Toggle dither enable value between true and false when F10 is pressed If KeyHit( 68 )=True Then enable=1-enable ;Dither enable ;other information about gravity speed#=0 x_vel#=0:prev_x#=EntityX( box ) y_vel#=0:prev_y#=EntityY( box ) z_vel#=0:prev_z#=EntityZ( box ) ;Main Loop While Not KeyHit(88) ;press F12 To Exit ;Press F11 to see the world in Wireframe mode If KeyHit(87)=True Then enable=1-enable WireFrame enable ;CameraFogRange camera,1,FOG_RANGE ;calculate box velocities cx#=EntityX( box ):x_vel=cx-prev_x:prev_x=cx cy#=EntityY( box ):y_vel=cy-prev_y:prev_y=cy cz#=EntityZ( box ):z_vel=cz-prev_z:prev_z=cz EntityParent(camera,box,1) ;Keyboard Input (cursor-keys) For moving the player around If KeyHit(57) If move_entities = True move_entities = False Else move_entities = True EndIf EndIf If move_entities = False ;all of your movement code If KeyDown(30) TurnEntity box,0,1,0 If KeyDown(32) TurnEntity box,0,-1,0 If KeyDown(31) MoveEntity box,0,0,-.01 If KeyDown(17) MoveEntity box,0,0,.01 If EntityCollided( box,terrain) If KeyDown(17) speed=speed+.00 If speed>.0 speed=.0 Else If KeyDown(31) speed=speed-.02 If speed<-.5 speed=-.5 Else speed=speed*.9 EndIf MoveEntity box,0,0,speed TranslateEntity box,0,GRAVITY#-.01,0 Else TranslateEntity box,x_vel,y_vel+GRAVITY,z_vel EndIf EndIf For update.test = Each test PositionEntity reloaded\box,update\x,update\y,update\z,False Next If KeyHit(64) Then save If KeyHit(65) Then load ;Update the animation-Frames and Render the calculated scene, Flip Back- with Frontbuffer after that, so the new frame becomes visible UpdateWorld RenderWorld Flip Wend ;Free the Memory and end the program ClearWorld End ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function save() fileout = WriteFile("Shay1.SM") For n.test = Each test Print Str$(n) WriteString (fileout, Str$(n)) Next CloseFile( fileout ) Cls message$ = "game data written to file..." FlushKeys End Function ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function load() Cls For destroy.test = Each test FreeEntity destroy\box Delete destroy Next filein = ReadFile("Shay1.SM") While Not Eof(filein) Read1$ = ReadString$( filein ) If Read1$<>"" Then reload.test = RestoreTestInfo(Read1$) EndIf Wend DebugLog "finished with loaded file" FlushKeys control = First test End Function ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function RestoreTestInfo.test( SavedString$ ) message$ = "Read from file --> " + SavedString$ + " ... " DebugLog message$ ; typical type looks like ... [-3,-4,14,"LQT",18898368] SavedString$ = Mid$( SavedString$, 2, Len(SavedString$)-2) ; remove end square brackets firstcomma = Instr(SavedString$, ",") xvalue% = Left$(SavedString$, firstcomma-1) ; convert first value (up to comma) to an int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to 1st comma firstcomma = Instr(SavedString$, ",") yvalue% = Left$(SavedString$, firstcomma-1) ; convert up to new first comma to another int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to new 1st comma firstcomma = Instr(SavedString$, ",") zvalue% = Left$(SavedString$, firstcomma-1) ; convert up to new first comma to another int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to new 1st comma ThirdString$ = Mid$( SavedString$, 2, Len(SavedString$)) ; remove first quote from string firstcomma = Instr(SavedString$, ",") ThirdString$ = Mid$(SavedString$, 2, firstcomma-3) message$ = message$ + "x: " + xvalue + " y: " + yvalue + " z: " + zvalue + " name: " + ThirdString$ DebugLog message$ reloaded.test = New test ; make a new type reloaded\x = xvalue% ; and assign the reloaded\y = yvalue% ; reloaded values reloaded\z = zvalue% reloaded\name$ = ThirdString$ ; to the fields reloaded\box = LoadAnimMesh("model\box.b3d") ScaleEntity reloaded\box,.1,.1,.1 Animate reloaded\box,1,2,0,100 Return (reloaded) End Function ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| ||
and when I change like this For update.test = Each test PositionEntity update\box,update\x,update\y,update\z Next this second version here tells me entity not found |
| ||
For update.test = Each test PositionEntity update\shape, update\x, update\y,update\z Next the tiny snippet above call update.test is giving me a headache it keeps on telling me "variable must be a type" is there something I missed :( |
| ||
Your code isn't the clearest but in this section ...For count = 1 To 1 n.test = New test n\x = Rand(10) n\y = Rand(10) n\z = Rand(10)+10 n\name$ = Chr(Rand(26)+65) +Chr(Rand(26)+65) +Chr(Rand(26)+65) box = LoadAnimMesh("model\box.b3d") Animate box,1,2,0,100 Next You should have n\Box = loadanimmesh( etc.. ) and Animate n\Box,1,2,0,100 Stevie |
| ||
Stevie your n\box works great but because I'm using car velocities and gravity it seems to get in the way of the car velocities that and I'm using entityparent(Camera,n\box,1) but entity parent doesn't like the above code unless it's like this below entityparent(Camera,box,1) |
| ||
I've no idea what your trying to do but you need to tell blitz that n is of type instance "test" I'm guessing you get an object does not exist error? If you put a n.test = first test before the entityparent command and change all instanced of box to n\box in that part of your code then this should work. I'm assuming you will have more than one test type though so it would probably be more appropriate to use a loop .. for n.test = each test ... etc... Stevie Stevie |
| ||
I'm a stupid I didn't explain myself here on the forum okay I have a game with a skybox with blue skies , terrain in blitz3d model format , a square box with a stick in the center of it with one wheel on each side this box with wheels is using gravity from driver blitz3d examples and car velocities with entity collide etc. sorry |
| ||
it seem's that the RestoreTestInfo from black jumper seem to have problems with entityparent(camera,n\car,1) ;align car to wheels zx#=(EntityX( wheels[2],True )+EntityX( wheels[4],True ))/2 zx=zx-(EntityX( wheels[1],True )+EntityX( wheels[3],True ))/2 zy#=(EntityY( wheels[2],True )+EntityY( wheels[4],True ))/2 zy=zy-(EntityY( wheels[1],True )+EntityY( wheels[3],True ))/2 zz#=(EntityZ( wheels[2],True )+EntityZ( wheels[4],True ))/2 zz=zz-(EntityZ( wheels[1],True )+EntityZ( wheels[3],True ))/2 AlignToVector n\car,zx,zy,zz,1 zx#=(EntityX( wheels[1],True )+EntityX( wheels[2],True ))/2 zx=zx-(EntityX( wheels[3],True )+EntityX( wheels[4],True ))/2 zy#=(EntityY( wheels[1],True )+EntityY( wheels[2],True ))/2 zy=zy-(EntityY( wheels[3],True )+EntityY( wheels[4],True ))/2 zz#=(EntityZ( wheels[1],True )+EntityZ( wheels[2],True ))/2 zz=zz-(EntityZ( wheels[3],True )+EntityZ( wheels[4],True ))/2 AlignToVector n\car,zx,zy,zz,3 ;calculate car velocities cx#=EntityX( n\car ):x_vel=cx-prev_x:prev_x=cx cy#=EntityY( n\car ):y_vel=cy-prev_y:prev_y=cy cz#=EntityZ( n\car ):z_vel=cz-prev_z:prev_z=cz |
| ||
I am guessing that the problem with what you are trying to do is contained in that last section... You are trying to work out the car's velocity by subtracting where it *was* previously from where it is *now* That means that you will have to record not only the 'current' state of the game, but also what it was on the previous frame. So... (1) are you storing all this info in a Type (see below*) ? (2) are you saving it using Writefile(destination, Str$) (3) have you modified RestoreTestInfo to take into account all of the fields (and their order) in your Type definition * It looks as if the Driver demo stores info directly in the wheel objects - and these are held in an array... hence "EntityX(wheel[1])" is pulling information about X-position directly from wheel number 1. To use the Save/Load code developed above you need to be storing that information in a type OR you need to scrap this and go for writing/reading floats directly to a file. In that case you will need to write the save routine as well as the load routine. |
| ||
can you do this in the save and load routines WriteFloat(fileout,n\car) readfloat(filein,n\car) |
| ||
No. I assume that n\car holds the mesh you are loading for the car. This is just a number used internally by B3D to locate the actual mesh in memory (n\car is a 'handle' to the mesh.) You cannot reload a handle as the memory referred to will no longer be valid at a later date. What you would need to do is something like... basecar = Loadmesh("car.b3d") ;+++++++++++ Loading +++++++++++++ n.test = new test n\car = CopyEntity(basecar) SavedFile = ReadFile("saved.dat") SavedX# = ReadFloat(SavedFile) SavedY# = ReadFloat(SavedFile) SavedZ# = ReadFloat(SavedFile) CloseFile(SavedFile) PositionEntity n\car, SavedX, SavedY, SavedZ Of course you would have to do this for every wheel as well. And based on previous comments you will probably need to do it twice so that the velocity can be worked out from the previous positions |