|
This article assumes you know: IntroductionThe following is a document on how to maintain a smooth frame rate using Blitz Basic 3D.There are two options available, one is using the same methods as used in maintaining framerates for Blitz Basic 2D using either repeat loops or delay commands. If you wish to use this method I reccomend the article 'Smart Frame Limiting: Keeping Game Speed Constant' By Morduun. The method we will be using is quite different however, it's called render tweening, please read on to find out more about it, and how to implement it into your projects. What you will be able to do when you have read and studied this article is maintain a framerate equivalent to what the machine is capable of running, but the game will run the same speed on all machines. Used commandsTo do this we need to understand a few commands, some basic, some slightly more complex. Those commands are:MilliSecs() : This command checks for the current time of the machine in thousandths of a second. Float : This changes an integer such as 1 into a float such as 1.00 Mod : This divides a number by another, then gives the remainder. So if you fed 11/3 to it, it would return 2. CaptureWorld : This takes, if you will, a snapshot of the current position and animation states of every entity currently loaded. UpdateWorld : This checks all collisions between objects that have collisions enabled. And also animates all entities. Renderworld : This command renders the world, however, we are not interested in using this as it is, we want to use the optional 'Tween' command, this renders the entities somewhere between where they were when CaptureWorld was called, and where they are now. The tweenRight, let's try and explain the tween option of renderworld in some more detail. Here is an image of an entity from above at it's captured position, it's current position, and the position it would be with varying options of the tween parameter. Now, as you can see, the value we pass to RenderWorld is between 0 and 1, 0 placing all the entities at where they were when CaptureWorld was called, and 1 placing them all at where they currently are. Now that the principle behind it has been explained we need to put it into practice. The complete codeWhat we will do is take an example of how you would use it, and go through it piece by piece explaining what each part does in turn. This will hopefully give you an idea of how you would go about implementing it into projects of your own.(The following code is extracted from the castle demo by Mark Sibly) Right, let's get started then. Step-by-step ExplanationIn the following extract a variable called "FPS" is assigned, contrary to the name, "FPS" will not control how many graphics Frames Per Second will be run, instead it controls how many time your update function will be run.By update function I mean the code that controls your AI, moving of entities, collision reactions etc... Another variable is then set, this one is called "period", this sets how many milliseconds apart your main loop will be run. Finally, "time" holds the time of the last frame in milliseconds. This first initialization sets time to be one frame in the past, which then causes your main loops of code to start straight away with no tweening. Now we have covered the initialization of the main used variables we start into the main loop that will be used. As you may already know, everything contained in the following code will be infinately run until the ESC key is pressed. This waits at least one millisecond has passed since the last frame was run, "elapsed" will then contain the amount of milliseconds since the last frame was drawn. This is due to the fact that Millisecs() will contain the current time, and "time" contains the Millisecs() value of the last frame. So if we subtract the old time, from the new time, we will come up with the amount of milliseconds that has currently passed. This loop will likely never been repeated more than once unless you have a *really* fast computer and a very basic update function. Ticks will be set to the elapsed amount of time divided by the minimum time required before the next time the update function is run. Since ticks is an integer, it can only equal whole values, so it will only equal 0..1..2..3.. etc. For example, if you have a FPS value of 30, the update function will be run every 33.333 milliseconds, so if more than that amount of millisecs has passed, it will set ticks to at least 1, as you will be dividing a greater number, by a smaller one. It is also possible that if your code is run on a slower computer, the number of milliseconds since the last time your update function was run will always be more than the time required for that frame. Ticks will never equal 0 in this case. "elapsed Mod period" gives you the remainder of elapsed divided by period. This is the number of milliseconds since the last time the update function was run. If you divide this by the number of milliseconds per frame (period) it will turn into the percentage that you will use for your tween value. So, for example, if 10 milliseconds have passed since the last update, elapsed will equal 10, we will assume period equals 33.333. 10 Mod 33.333 will return 10.0, if we then divide 10 by 33.333 we will get approx 0.3, which is the approx percentage (divided by 100) between the captured position of the objects, and the current position. This is quite hard to understand, but read it over a few times and experiment and you will understand it eventually! Everything contained in this loop will NOT be run unless ticks equals 1, which, if you have a FPS value of 30, is every 33.333 milliseconds. This updates the time to be the time that the current update function was run. This works because you know that the amount of millisecs contained in period has passed, as this code will only be run when it has, it then adds that to the current time. If the for loop is on it's last time through (it will generally go through only once at a time, unless you have a really slow computer with a lot of code being run) then set the current snapshot of the world to be the current position of all the objects. This is either where you would place all your AI code etc... or you could call the function like this example does with all that code contained in it. Updates all the animations and collisions etc... If the for loop is on it's last time through exit the loop, otherwise go round again. Render all the entities somewhere between the captured position and their current position, where is provided by tween#. This is either where you would place all your code that draws all your 2D images to the screen, or you could call the function like this example does with all your code contained in it. Flip whatever is contained in the backbuffer into the frontbuffer so you can see it! End the while loop, and when the while loop is ended, quit the program. AfterwordThat's the end of this article, I hope that you enjoyed it, and it cleared up how the tween option of RenderWorld works. Hopefully you will now all be able to create smooth running 3D games!CreditsMe (JL2k) - For writing this, and explaining some of the parts - hehe, just had to mention my name, lolPebu - For explaining all the parts that I didn't understand... which was most of it at the time :P The great Mark Sibly - For making the interesting Castle Demo this article is based on and Blitz Basic for us all the play around with! And finally, let's not forget Sopisoft for giving me the idea of putting all the main code into a seperate function! ;) Revisions1.1 - Changed a few typos, and realised that the line "UpdateGame()" had to be "If k=ticks then UpdateGame()", else if the computer was slow, it would loop longer and longer until it eventually froze.1.0 - Initial Release For a printable copy of this article, please click HERE.
This site is Copyright© 2000-2004, BlitzCoder. All rights reserved.
|