QB Express

Issue #28  ~  May 10, 2008

"A magazine by the QB community, for the QB community!"

In This Issue



From The Editor's Desk

Written by Pete

I can feel the unrest.

Every month, you guys are clamoring for a new issue of QB Express, and every month it comes late.

This was supposed to be an early April issue, and now we're a week into May, and it's finally coming out. It's always "better late than never," but I do genuinely apologize for all the delays. Life gets in the way...(not to mention the dozens of other ridiculously time consuming projects I've got going on as well!)

I'd like to take this opportunity to respond to some of the criticisms that have been leveled at QB Express lately.


Syn9 at Pete's QB Site

"ya know... just like we all said last year, and the year before... i wish qbe had multiple editors instead of relying on one person to edit it. perhaps then it would feel really community driven. it sucks when a bunch of people come out to support, but there work cant be seen because of one single point failure. dont get me wrong pete, you do a phenomenal service. but as we've all said a hundred times, please get some backup editors for when you decide to disappear off the face of the planet. as much as id love to take all the glory from my projects, i know that they'd never be anywhere as good if it wernt for my collaborators."

You're right that I am the "single point failure" for why QB Express gets delayed...but I am also the sole reason why the magazine gets released in the first place. If I didn't go out of my way to put QB Express together, none of these 28 issues would exist.

I think there's more to it than just asking "why can't there be more editors so that if Pete's busy, someone else can release QB Express!" QB Express isn't just a collection of random articles -- it has a certain writing style, attitude, level of quality, and identity -- and a lot of that comes from my editing. That's why QB Express is QB Express and not PCopy!, QB Cult Magazine, QB:TM, etc. It's not just an issue of "someone" releasing the next issue, it's also a matter of it being done right, so the magazine is of high quality, and still feels like QB Express.

But to address your point -- QB Express does, indeed, have multiple editors. MystikShadows and Imortis Inglorian both contribute heavily as assistant editors, and I've also enlisted the help of Lachie Dazdarian to help with things like news briefs for quite some time.

Still, I am the lead editor, and I'm the one that compiles everything and writes the bulk of the magazine. I suppose part of it is that I'm a poor delegator, and don't make use of the other editors as much as I should. Ultimately, though, it's not so much that I don't have people that are clamoring to help me -- I do -- it's that I want QB Express done a certain way, and I'd rather just do it myself...whenever it is that I get around to it.

That's not to say I want to do it all myself. That would be silly. I need the help of Imortis, MystikShadows, Lachie, and all the regular contributors. Without them, there would be no magazine. But the final edit of every QB Express issue has has been my own...and that's why it's QB Express.

I understand that the delays frustrate a lot of people, and turn off contributors. I would love for QB Express to adhere to the ideal monthly schedule that I set. Of course it bothers me when it's late. It's not like I'm trying to burn you guys, I just have a lot of other commitments that come first, and I also refuse to release a shoddy magazine. I'd rather the issues be late and good, rather than on-time and crappy.


Lachie Dazdarian at Freebasic.net

"I just don't understand how Pete allows himself to postpone QBE for several weeks while only having to complete the news briefs (2-3 hours of work). He had this issue almost complete 3 weeks ago (we talked). I mean, if he didn't have time to work on the issue at all I would understand more. But with every next issue he comes so near to release it to drop the ball again and again. Annoying."

I understand your frustration, but the the news briefs and all the sections I do take a lot more than 2 - 3 hours of work! For a typical issue of QB Express, I spend a full day compiling the issue, formatting it, and writing up all of my sections. I spend up to five hours formatting other people's submissions and and at least 10 hours writing my sections, finding news briefs, and doing all the stuff I do (all the way up to uploading the magazine and spamming about it across all the message forums).

This issue is a bit of an exception, because I actually had help with the news briefs this time around -- but it still took a significant chunk of time. And it's hard to devote a full day to anything, when I run a dozen other websites, am starting my own business, am involved in a time-consuming TV development think tank, have friends and a girlfriend...oh, and work a full-time job for 60+ hours a week.


E.K. Virtanen at Freebasic.net

"I can only try to guess how hard it would be for Pete to give QBE for hands of someone else. Though, even it sounds rude, i think Pete is killing QBE and everything it and he has achieved with 27 previous issues."

Ouch.

You're right, I'm not going to give up QB Express to someone else. But I would beg to differ that I'm "killing" QB Express.

If anything, I'm keeping it alive.

Time and time again in the QB community, people have tried "retiring," and passing on their magazines or websites to new webmasters/editors, hoping to keep them alive. And time and time again, they've died a sad, lonely death. We've seen it with V Planet!, QBasic News, Alternate Logic, QB45.com and more. A few months after the websites change hands, they slow down and die, due to lack of time and interest. I fear that the fastest way to kill QB Express would be for me to pass it on to another editor.

I've been doing QB Express for nearly four years. Twenty-eight issues have been released -- and each and every issue is HUGE (the smallest QBE issue tends to be larger than the biggest issue of most other scene mags). I've also been running Pete's QB Site since 1998! My website will be TEN years old this year, making it undoubtedly the longest running active QB site that's ever existed. If you're worried about QB Express's longevity, you're picking on the wrong magazine. If there's anything I've proven, it's that Pete's QB Site and QB Express are in it for the long haul.

So no, I'm not going to give up QB Express.

But on the other hand, I'm going to be in a unique situation for the next few months, and for this special situation, I'm willing to give other people a chance to try their hand at running QB Express.

I have an announcement to make:

I will be leaving the country for six months beginning on July 8th. I will not be able to release new issues of QB Express, and I will be passing the magazine off on to the other staff members -- Imortis Inglorian and MystikShadows, and any other people who would like to join the staff.

Where am I going, you ask? I am hitchhiking around the world!

A friend and I booked one-way, non-refundable tickets to Tokyo, Japan in July. And we're going to head WEST...until we come back to the U.S. We're doing this on a shoestring budget, so we'll be sleeping in a tent, cooking our own food, and bumming rides the whole way. The first few legs of our journey will take us through Japan, to Taiwan via ferryboat (through Okinawa), to Hong Kong, followed by China for the Olympics, heading north to Mongolia, then north to Lake Baikal in Irkutsk. Then wherever the winds take us from there. Suffice to say, my Internet access will be pretty limited.

I'm still going to be "in charge" of QB Express...but since I'll be unable to compile and run the magazine, I'm willing to let someone else try their hand at releasing issues for a few months. If it goes well, GREAT! But if issues don't get released, you can rest assured that when I'm back in the U.S. in January 2009, QB Express will continue.

I plan on releasing one more issue before I leave (#29 in June), and then I'll be passing the magazine on. Depending how it goes, I may come back in January '09 pick up as editor where I left off. But if issues do get released -- and they're on-time and high quality -- I would not be opposed to having the editing team continue releasing QB Express. I would just come back as an "executive" managing editor, and not do the nuts and bolts formatting and writing news briefs). I suppose we'll see what happens.

If you're interested in joining MystikShadows and Imortis Inglorian on the QB Express editing team, please email me at qbexpress@gmail.com, and we'll talk!

-Pete

Submit To QB Express

You all know the drill. This magazine can't exist without people SUBMITTING articles, editorials, tutorials, reviews, news and feedback. This is not just a solo effort by me... it's a group effort by people throughout the QB community. If you have anything to submit, or have time to write something, DO IT!

If you want to write about something, but can't think of a topic, or need inspiration, check out the "Official QB Express Article Requests" thread! There have been quite a few articles requested -- and even if none of them strikes your fancy, we can help you come up with something that you would like to write about. If you're interested in getting your own monthly column or just want to write an article or two, by all means, do it! Anything that is submitted will be included!

I also want feedback and letters to the editor regarding this magazine. I want suggestions and critiques. What do you like? What don't you like? What could be done better? Let me know!

All submissions and feedback can be sent to qbexpress@gmail.com. You can also PM me on the Pete's QB Site or QBasic News message forums. If QB Express is going to continue to be so good, YOU need to contribute!

-Pete



Letters

Letter From MystikShadows

Hello to the editors and all the readers.

And yet another awesome issue of QB Express. I really like to see all these good articles and tutorials and well you were right, it's the best news section I've seen in a little while. I'm not sure how it happens but it always seems that somewhere somehow in the back of the contributors head they know what to write about now and what to write about next more and more. Is it just me or does every issue of QB Express seem to just make more and more sense? I said it before and I can still say it today, People's taste in what they want to read here is growing still today. People seem to have decided what they want to do with their programming tools (QB or FB) and they are pushing themselves to be better at whatever it is they chose to do. This brings more specific subjects they'd like to know more about and well we can all read the results in every issue. I can't wait to see what the future issues have in store for us.

First a bit about Seb McClouth's Qbinux project. I for one am glad that he plans to keep us updated on his project in QB Express. I'm sure that I'm not the only one and I'm also sure he has a lot of fans in the silent part of the Qmunity. Qbinix is one of those project that really set a standard as far as goals because well making an OS is always a project that shapes not only his efforts per se but also shapes the impression of the language used by other people (especially people from outside the community that are curious about if and how the language is being used these days). His update in this issue was great. His code sample in his update is just one more example of how one can shape a programming language to really do things it wasn't originally designed for and do it well. So Kudos for Qbinux, Kudos for Seb himself with his relentless perseverance on his project and I think Qbinux has more influence and impact than he might be thinking right now. I can't wait to read the next update.

The monthly awards this month were more than warranted by the recipients. N3trunn3r's Coder Hideout is very well designed fun to go in and look around. Great work. And well what's to say about Dr_D's game Kart Racing demo. Not much to add to that very impressive screen shot except that Dr_D's really living up to his high quality 3D design reputation here. Absolutely amazing talent beautifully represented on a 3D screen. It's these kind of projects that really make the image of FB to the outside world and well I'm sure jaws dropped and eyes popped out of visitors' head when they tumbled upon this game. I realize that presenting an application in galleries probably defeat the purpose of the magazine section. So perhaps, when something is available, a section of FB application projects should be presented here too. This just to show the outside world and the newcomers what FB is capable of other than games. Take that as a possible suggestion for future issues.

Mentat has again outdone himself with “Going Deep: Football and 3D Graphics “. It really amazes me how well he expresses the bits and pieces of knowledge and information the way he does. It seems that he can take a subject like 3D graphics and just give it a natural flow of thought or something that makes the whole tutorial coherent and fluid to read in such a way that it just sinks in and gets understood. I'm gonna have to make myself a list of subjects I wanna know more about and basically email it right to him ;-). His talent and creativity are really appreciated by, I'm sure, a whole lot of other people, not just me. I can't wait to see what else is on it's way from Mentat.

Now in my last letter to the editor I said I was looking forward to Michael “h4tt3n” Nissen's second part to his sprint tutorial and I have to say I was NOT disappointed one bit. It's really amazing to see how clearly everything from the first part comes into place when it's put into a dimension we can see and understand (IE the second dimension in this case). Everything that he mentioned in the first part suddenly made that much more sent when I read and understood his second part. Great work from Michael. I don't know what he has in store in future issues but I do know this, I'll be reading it with great interest and enthousiasm.

Now, I'll just bet that Dean Menenez answered a question many of us has had for a while (I know I did) with his CGI and Qbasic tutorial. And I think that with the contents of that tutorial it can become very easy to create many things most of us would need from such a tutorial and I think that's an important point that sets apart a good tutorial. And well, good tutorials don't seem to be one of Dean's problems at all, I've read nothing but great anything he wrote thus far so you know that I love to read anything he has to talk about and if it's gonna teach me something on top of it, kudos for him. Great work Dean and don't keep me waiting too long for the next thing to read about.

I don't know if this will put me to shame or not, but before the last issue of QB Express I haven't heard too much of Kiyote Wolf. It's just not a name I've seen too often at least not in my time as part of the Qmunity. But one thing I'm seeing is I like is topics for tutorials. The commodore wedge tutorial proved itself to be one of the most interesting thing to read in this issue probably because I've owned just about every commodore computer then invented from the commodore PET series to the VIC-20, the C-64, the C-128, the Amiga series. Man those were the days, the real time for computers. Today there's no more choices like that, no more commodore or Atari, there's just Apple and IBM pretty much. I miss those days, and I say we're long overdue for a new wave of computers that are there to set a whole new range of standards in computing today. I thought his trick with the PLAY statement was absolutely brilliant and I can't wait to read more from him.

Wallyfblu I think managed to put an awesome alternative to classic Windows API programming into a very concise article. He seems to question his ability to write in English but I think I can tell Wally not to worry about that. The English used is more than adequate to say the least. It got the message across clearly in my opinion. I enjoyed reading it a lot. So great work for a first submission and I hope to read more from Wally.

Hezad, once again manages to impress me with his “using electric field equation to generate awesome moving plasmas. This one impressed me not just by the fact that it was well written with the use of graphics and Algebraic formulas but also by the accuracy of the contents of the article. This is the type of contents that makes itself so interesting to read. Might just be me, but I love a lot of information in one tutorial. I mean if I didn't stop myself I'd put everything there is to know about a subject into one single submission every chance I get. But yeah, I really liked that article for all the reasons I mentioned above and of course, I can't wait to see what else is gonna come out of Hezad's mind in the future. Great work there.

I gotta say I always enjoy the comics in every issue (at least it would be awesome of there was at least one in every issue. In this issue, well I love the world of dinosaurs so needless to say that your pick of a comic strip with the dinosaurs I really liked. I could be a fan of that comic's author really fast. As for the text comic, it's all in my signature below hehe. Definitely another great release and looking forward to future issues. Great work to everyone involved.

MystikShadows

The American Express of the QB/FB Community...Accepted Everywhere! ;)

I agree, last month's issue was truly excellent. "Great work to everyone involved," indeed!

-Pete


Letter From Cleverson

Accessibility missing at BASIC forums

Hi Petes

I live in Brazil and use to stay tuned to QB Express and other BASIC resources. Recently, I needed to make a BASIC-related question at some comunity's forum, but I can't even register myself to them. The reason is that at many forums, including your messageboard, the registration forms use the CAPTCHA system (see: http://en.wikipedia.org/wiki/Captcha). That consists of a confirmation code shown in a picture to be typed. Such confirmation code is not accessible to screen reader softwares used by blind or visually impaired people like me, see for example: www.nvda-project.org and www.hj.com

Notice that the above Wikipedia's article for CAPTCHA has a topic talking about accessibility issues.

I tried to register to your message board at http://www.petesqbsite.com/forum/

At the registration form, it's stated the following: "If you are visually impaired or cannot otherwise read this code please contact the Administrator for help."

I have sent a letter to the related e-mail address, forum@petesqbsite.com, asking the administrator to create an user account for me, but the letter has returned to me.

The FreeBasic forum located at http://www.freebasic.net/forum/ has the same registration barrier. I also e-mailed them at rubentbstk@gmail.com but they haven't answered until now.

I'd like to suggest the forums administrators to implement an alternative to CAPTCHA for registration, e.g. spoken random numbers to by heard, as stated in the Wikipedia's article above. You could also replace the CAPTCHA's pictures with a simple math calculation any person could do, e.g. "how much is 9 plus 11?".

Finally, I'll be grateful if you publish this letter at QB Express or somewhere else, to help making the issue better known by the public. Besides it, congratulations for your good work on the Petes QB site and QB Express.

Thanks for your attention,

Cleverson

I'm sorry to hear about the problems you experienced, Cleverson! I've gone and changed the forum admin email address to one where I will actually receive your emails, and I hope that all other forum owners will do the same. I've also created a forum account for you on your behalf.

Designing accessible websites is a very serious issue for the QB/FB community, as well as the Internet at large. Even if there are only a handful of visually-impaired people that ever code Freebasic, we should make sure that our websites are accessible to all. With such a small community, we don't want to alienate any (potential) members!

-Pete


Letter From Moneo

Thanks to Mystic and Lachie for their kind words in the latest QBE about my recent tutorial about Rounding.

It's quite a difficult subject, and I myself must admit that it can still be improved.

One of the difficulties of rounding is that most programmers have their own proprietary version of a particular rounding algorithm, and after time, these programmers become convinced that their version of the algorithm works fine. Of course, their algorithm has never been confronted by another programmer.

For years, I myself was using a variation of the Symmetric Arithmetric Method which I developed. It seemed to work fine, and no one ever confronted the method, including the Accounting Dept. "Ignorance is bliss." Years later I realized that it did not work 100%.

Thanks again, friends..... Moneo


Letter From Pritchard

Hi Pete.

I want to write some articles. I have so much I'm sure that I can code about, but I don't see people requesting anything of the magazine any more. Just what have people been requesting, and where can I get some ideas on what to write? It's the strangest thing having a fairly large code base behind me, and knowing that half of the things I code that are old and boring to me are new to other members of the community. This makes me wonder how people like cha0s feel when they look at their code base :-)

Anyways, the "QBE Suggestion Thread" hasn't been posted in in months. There's really no place that I've seen requesting information on one thing or another. The only request I have seen, really since the beginning of FreeBASIC, is the whole Windows API Tutorial/GUI thing. I wish I had enough time and patience to port the rest of those Windows Tutorials over, but I can only enjoy something irrelevant to my current projects for so long. I'm really hoping for more information on just what people have been requesting in the future.

I hope something comes up so people can start submitting more content overall.

Glad QBE's back on a regular schedule,

We need more topic ideas,

Pritchard

Pritchard had a great suggestion here, so I went ahead and started up a brand new (and much more active!) QB Express Article Requests at the Freebasic.net forums.

This thread is full of unique and interesting ideas for FB / QB tutorials. On top of that, there have already been several suggestions that have been addressed in this very issue. Pritchard took two suggestions from this thread and turned them into some of the tutorials you'll be reading in just a few minutes. :)

-Pete


Letter From E.K. Virtanen

Hi QBE staff.

I would like to ask, do you have any plans to interview peoples around the QB/FB communities? Someones thinks this is waste of time and space but personally i would love to read these kind of contributions.

QB64 is a baby, but when it (hopefully) get's out and is ready to use, will QBE cover it also?

I would like to say "thank you" for Dean Menezes about he's article in last issue (CGI and QBasic). I have fought with those things like weeks and for some reason, they didn't open to me. With this article, i could do few neat things i have wanted to do (just for fun). So thanks Dean, you rock. :)

Site of the Month went in perfect website. N3trunn3r's Coder Hideout is one of the nicest places i have seen. That kind of places i would love to see more. Everything today is so boring (white/grey/blue), so this place is worth of visit just because of how it looks.

Rock on QBE and peoples around it.

E.K.Virtanen

We have done interviews in QB Express before, and it's a feature that will undoubtedly return at some point. We've interviewed Marcade, V1ctor, Angel Mottola, Piptol and others for past issues. I'll make a note that people want more interviews (and hopefully get around to doing some for a future issue)!

As for the QB64 project, we will certainly cover it. This magazine is about QuickBasic and all of the spin-off versions (Freebasic included) that end up getting created by the "QB community." We're not language-specific, so much as community-specific...and the QBasic, FreeBasic and QB64 coders are all members of the same online community (with a lot of overlap).

Anyhow, glad you're enjoying the magazine! (And sorry for all the delays.)

-Pete


Letter from mennonite

pete, i just wanted to say what a joy it was reading the comic by anonymous in qbe #27. even though galleon is actually working on something many of us have always wanted, that is, an open source project that works more like qbasic, it's great to see that people can criticise his efforts without being made into some kind of pariah.

these days, us "douchebags" can even say that the community isn't what it used to be, like pritchard did in a previous issue. it seems like just two years ago i totally pulled the notion out of my ass, but it's come true obviously, and look! it's like suddenly being in a free country... it's nice to see not just you but the community admiring critical opinions.

so cheers, anonymous! you're in good company. and total anonymity, good thinking, sometimes a handle gives people too much to consider. better to let people think about the message than the person, and i'm sure most of the readers would agree. my hat is off to you and your splendid comic, even though i totally disagree with you about galleon and myst. i think they will surprise you if you pay attention. sometimes the obvious is like the suns' rays, taking years to reach people.

i'd suggest a comic that was as critical of pete berg, but lets face it, i'm the only one that thinks he's worth one, and what's a couple stray opinions from people that aren't even involved? not to suggest that the same arguments used on me for years also apply to your comic, seriously i plan to tease stephane about "mystik pizza" for a while. we have no one to thank for that but anonymous. keep your message strong!

with love,

mennonite

Glad you enjoyed the comic.

As for your letter, let me reiterate that QB Express has always embraced critical opinions, and we've never censored anybody! The only thing we take issue with is when people level their criticism without tact...and go on incoherent, circuitous, expletive-filled rants that nobody understands. That's when people end up getting called "douchebag".

-Pete



Have a letter for the editor? Send all your rants, raves, ideas, comments and questions to qbexpress@gmail.com.


News Briefs

News from all around the QB community, about the latest games, site updates, program releases and more!

Web Site News

Four new games added to the FreeBasic Games Directory

Lachie Dazdarian added FOUR more games to the Freebasic Games Directory this past month, which brings the directory to a total of 96 finished and fully-playable FreeBasic games:

Hex Factor - 13/20
Royal Rumble - 09/20
FreeBASIC Nibbles - 11/20
Rise of the Bustrons! - 13/20

By the next update, Lachie expects to pass the landmark of 100 games. Help him out by finishing some of your FB projects!

News Brief by Pete

Project News

Two New FreeBasic Releases

Since our last QB Express was released we have had no less than 2 releases of the compiler. There has been a lot of bug fixes done and QB Compatibility has been improved as well. Here's the rundown of the releases:

FreeBASIC 0.18.4 – Released On March 30th 2008

This release introduced the -lang fblite mode (to take the place of the -lang deprecated mode) which promises a better compatibility with FB 0.16b code among other things. You can get the details of the release in this post Forum Post.

FreeBASIC 0.18.5 – Released On April 18th 2008

This one was more of a maintenance release that fixed a few bugs and also addressed some installation issues. The details of that release can be read right here.

News Brief by MystikShadows


New Version Of a Classic by xbtgc

On April 28th, xbtgc made this post about his War Game simulation. War Game was a popular movie back in the 1980s and was renouned all over the world. There were many games made based on the movie and now we can count this simulation as a new classic addition to the FB projects' arsenal.

Here are some screenshots taken from the post above.

News Brief by MystikShadows


Fefe Initiates a Macintosh Port of FreeBASIC.

Fefe (fairly new to FB community) has begun, with a few friends, to port FreeBASIC to the Mac OS/X platform for Intel based CPUs. Work has already begun and you can read all about it in this thread. It looks like a very promising effort that just might give us a Macintosh version of FreeBASIC. Now is a good time to get a Mac.

News Brief by MystikShadows


Dr. Mudball's Laboratory: Rise of the Bustrons

Sir_Mud and Dr_D teamed up to create a new game called Rise of the Bustrons using FBEXT, and it looks like a whole lot of fun!


According to Dr_D: "This actually started out as a demo after playing Jezzball and Filler on Flash and we thought it would be nice to have some little games like that in FB. That being the case, we didn't really plan far enough ahead to add a bunch of things we ended up wanting without pretty much re-writing the entire game. We've decided to release this one and make a sequel to expand on the game-play and gfx. The new one will have many surprises, most of which I'm not going to tell you about, but just to list a couple... you will be able to grow different shapes and there will be many power-ups/pitfalls."

You can download the game here: drmudball.googlepages.com

News Brief by Pete


Syn9 seeking beta testers for Aphex Razor

Syn9 is in needs of beta testers for his new creation called Aphex Razor. As you can see from the screenshots in his Request Post , this game promises to be an instant classic. To get the latest and greatest on the game and the beta testing you can read the post above where you can get contact information to get in touch with syn9.

For more on Aphex Razor, check out this issue's Gallery.

News Brief by MystikShadows


Hezad Has released a Tetris Clone called “Trucs Qui S'Emboîtent"

For those that don't know French too well this title basically means “things that fit" . From the screen shot it looks like it has excellent graphics and more than adequate music to complete the game. It also features some different game play rules devised by Hezad. This games looks like an instant classic for all the tetrist lovers out there.

News Brief by MystikShadows


Seb McClouth's QBLinux Latest And Greatest

A recent post (April 20th) indicates that QBInux is still there beign developed. Seb's post says it best so here is the post (from Pete's Qbasic Site. In case some of our readers haven't heard of the QBInux project it is an exhaustive project to create a linux distribution with everything (that possibly can be) in QuickBASIC Professional Development System. Progress is being made on a regular basis which looks very promising. Read more in the post above and on Seb's website right here. Seb is always open to suggestions and comments so feel free to get in touch with him.

News Brief by MystikShadows


TmEE Releases Glass Breaker QB

TmEE announced on March 29th the release of Glass Breaker. You are in a in the street in front of buildings and the goal is to break windows without getting caught in the rpocess. Being a QB game it's hard to have a screenshot available. But the graphics and game play are interesting to say the least. An excellent example of what QB can do. See the details and download links on TmEE's post on Pete's QB Site.

News Brief by MystikShadows


Pharaoh And Brandon And QML

Pharaoh is working on a project to create a markup language that would work good for a text mode Qbasic browser. He calls this project QML and it looks like a veryt interesting concept. Brandon Cornell is known for his GUI development projects. He created a browser for QML in FreeBASIC and is available for linux and Windows. Read all about it on their post posted on April 26th in Pete's QB Site.

News Brief by MystikShadows


Pritch Library

Pritchard sent in the following message about his self-titled library project:

Hi everyone.

I've been working on a new library. I feel it meets up to a lot of my more recent coding standards. Unfortunately, development's been taking a long time and I'm more dedicated on writing applications than I am writing this library. So, rather than releasing this a year from now with full documentation, I'm going to give an "update" and "pre-release", which comes with a good number of classes I've written thusfar and examples for each. I've been switching back and forth between Java and FreeBASIC a bit, so there's a few classes I've written in Java that I've yet to port to FB, but all that aside, this update comes with the following:

  • A frame timer object with speed warping
  • A thread handler object, for easy thread creation and management
  • A pointer array macro to turn objects into arrays... Note: Try taking a look at FB-ext if you want more advanced versions of this kinda thing
  • A key object for key press handling. Up, Down, On Up, On Down, and how long a key's been in a certain state. Note: Code uses the new "explicit enum" syntax from more recent versions of FBC. If this library doesn't compile, just remove the "explicit" after the enum.
  • A 2D vector object I've been working on for some time
  • A number randomizer that accepts a variable, minimal range multiplier, and maximum range multiplier
  • An "Event" and "Event List" object which can load procedures from DLLs. This is fairly recent and from my few experiments in scripting and procedure pointer objects.

Overall, the library's still in a very early state due to my strange programming schedule and habits. I only hope the code is useful to one person or another.

News Brief by Pritchard


QB64 Demo V0.7SC Released

From Network54

a href="http://www.mediafire.com/?dbpdnibw9xj">Downlod Here

Demo #7 is the first major version of QB64 which is self-compiled.

Demo #7 contains around 50 bug fixes and also provides some newly implemented commands.

Galleon is moving along nicely, and has released a new version of the QB64 compiler.

For those who don't know, QB64 is an attempt to make a 100% QB code compatible compiler that will work on new Operating systems and hardware.

So far, progress has been steady and promising. For mor information and discussion go here.

News Brief by Imortis Inglorian


AMIB Joiner 0.1

From here.

Hi everybody

First i'd like to thank FB and NSIS developers for their hard effort and creating such nice programs

AMIB Joiner is a program to join multiple files and generate a single file

Usage is adding overlay data to existing executable files

For example you can add config.txt and compressed 7z archive to setup sfx of 7-Zip

Find bugs if you can ;)

Download installation package - 41KB

Download Full FB Src and NSIS Src - 24 KB

This looks like a promising project form AMIBCT.

News Brief by Imortis Inglorian


Network multiplayer Tic Tac Toe in FB

arenth has writen a simple Tic-Tac-Toe game with network multiplayer in FB using eNet. Will this spark more network programming in the FB scene?

More info here.

News Brief by Imortis Inglorian


Other News

Z!re has a competition

On April 28th Z!re posted this post announcing a competition with a pretty decent prize at the end. The competition will last a month and the goal is to “pimp your game". This is open to everyone. The main idea is to start (or move an existing gaming project forward, talk about it, come up with a screen shot or two or three and let your game be known.

An excellent initiative to get the gaming world active. We'll see in a month how this competition went until then you can read about the details and what's going on so far in the competition on the freebasic post above or get the specific rules on the APIStudios website.

News Brief by MystikShadows

Where's Mac?

Mac, the Network54 QB forum admin, has been mysteriously absent. Many wonder where he is.

See here for more info.

News Brief by Imortis Inglorian


FreeBASIC added to the openSUSE Wishlist

Users of openSUSE linux, may soon have a the ability to get a FreeBASIC package for it, making for hastle-free installation. No word yet as to when or if this will acctually happen.

See here.

News Brief by Imortis Inglorian



Gallery

Written by Pete

Every issue QB Express features a preview and exciting new screenshots from an upcoming QB game. If you would like your game featured, send in some screenshots!

Aphex Razor

Every game that Syn9 releases manages to blow our minds in one way or another, and his latest effort, Aphex Razor, is no exception.

This action game is in the style of the Strike series (ie: Desert Strike, Jungle Strike, etc.), where you pilot a helicopter over a warzone and shoot all types of bad guys on the ground. Syn9 describes the storyline for this game as follows:

In this game, the United States has taken over the entire northern hemisphere and changed its name to United States of the World (USW). A resistance force has started striking back against during an important international conference that is trying to force a break up of the USW. And it is your job to wipe them out.

Sounds like as good an excuse as any to fly around a war plane and blow stuff up! Without further ado, here are the screenshots:








Visit the Official Site for more information.


The Eternal Struggle: The Game Developer versus the Programmer

Written by Lachie Dazdarian (April, 2008)

Perhaps the title is somewhat overdramatic, but I'm quite convinced that such internal struggle indeed exists in most of us who once attempted to code a computer game.

Maybe this type of article would have been much more current in the good old 80ties when most professional game developers were programmers first, and then game developers in one/two man teams. The situation today is quite different, with professional game developing teams counting over 100 people and with game making tools which many hobbyist game developers nowadays use.

Nevertheless, still a great deal of hobbyist game developers are programmers first, or at least have to become one. Very rarely one individual possesses enough skill in, for example, pixel art and story writing, to easily motivate skilled programmers to work on his dream game project for free.

So I want to discuss here two types of hobbyist game developers: those who learned to code so they could complete a game, and programmers who felt that internal desire to create a computer game, among their other programming challenges. I'll discuss the pros and cons of both of these profiles and try to suggest which traits we should try to restrain and which we should try to develop, depending on which of these two groups I mentioned we might best fit.

As I am a game developer and a hack coder it's obvious I will slightly favour the first group and can primarily speculate about the problems of the second. Still, observing the development process of game projects started by programmers I think I can pinpoint quite well what are their shortcomings as game developers. But let's start with the first group, game developers who learned to code only (or mainly) for the purpose of game design.

The positive aspect of this profile is that such persons are quite clear with what they want to accomplish during the actual process of programming. Programming above all is a tool for them with a very precise purpose. They are not distracted with certain programming quirks, like keeping the code very clean, reusable and...how programmers like to say, safe. Game developers don't shun global variables, redundant code or some extra memory that probably didn't have to be used. The code for a game developer simply has to get the job done, no matter how. And when I say "get the job done" I mean result in a smooth and playable game on the most PCs possible. So game developers will strive for effective game engine solutions equally as programmers, but maybe for slightly different reasons.

Game developers are quite clear that game development is rarely a fun process. The true satisfaction of game development resides in the positive feedback you receive after completion of a project. In seeing people enjoying your hard work and it having an effect on them, bigger or smaller. For me that is, and should be, the primarily purpose of hobbyist game development. If you don't think so, you should really imagine yourself being the last man on Earth and coding a computer game that extends from being a simple toy, if you would be willing to code even a simple toy. It's quite difficult to picture yourself coding an epic RPG in that situation.

A skilled programmer approaches the programming part of game development much more confident and prepared. The programmer is quite assured that he will be able to solve all or almost all issues that might arise during coding and he/she is usually right in this position. On the top of that, most of programmer's solutions for specific problems in the game code will be better and more reusable that those coded by a game developer. This is the programmer's biggest asset. Rarely a game code executed by a programmer will end up in a dead-end. Programmer's sense for structure and order becomes a great advantage as a certain project grows bigger. Something that will seem unnecessary at the very start to a game developer will fit in place later. A game developer, on the other hand, is always in danger of killing the project at the very start by employing shortcuts and quick solutions just to get the basic engine off the ground. All of this will of course become a huge problem later.

Let's now concentrate more on the negative sides of these two profiles.

As I previously mentioned, game developer's built-in need for quick solutions quite often, in the case of inexperienced game developers, results in projects that simply can't be continued any more after a certain point. They become a big mess that is impossible to maintain any more. All that is left is for the project to be restarted from scratch and only the graphics and sound resources can be salvaged. The second problem lies in the fact that most game developers learn only the amount of programming they need at the specific moment of time to solve a certain game programming problem. This forces them to employ inappropriate solutions with the next new problem where better ones probably exist, but a game developer is simply unaware of them or is too impatient to explore and learn them. This is usually a reason for programming beginners only interested in game design to wander around programming languages and switch between bad programming habits. I would say that the initial learning process (to learn to program a game) for a game developer is faster than that for a beginner programmer interested in generic programming, but much more frustrating and less effective. Still, rarely a beginner only interested in game design will be willing to allocate months of his free time to learn programming (which is really the proper way to learn programming) that is barely related to his object of interest - game design.

The programmer's game developing deficiency lies in the very traits which define a programmer. A programmer is a problem solver. And once a problem is solved he/she losses the interest to make something out of this solution - a proper game. Coding menus, various display screens (energy bars, etc...) and interfaces, gameplay balancing, plot triggers, you name it. All these tedious tasks are an ordeal to a programmer. The programmer knows he/she can solve them, but also knows that solving them won't improve him/her as a programmer. The programmer is often in danger of continuing to add features and instances to the game engine indefinitely. The programmer dislikes the necessity of hard coding certain situations in a computer game. He/she is more willing to create an implementation structure for every instance appearing in the game which then can be used in the actual process of designing the game, that is, if the programmer ever comes to that.

Also, a great deal of programmers don't have the most refined taste in computer games by my opinion. You could say that programmers are the toy makers and game developers the story tellers. The programmers usually don't appreciate the longevity factor of a game, or the atmosphere and depth it can provide. They prefer games that are more toy-like. Easily learned and very rarely featuring progress. With minimum effort delivered in the graphics and sound department. The fact that these sort of games are small and coded in short amount of time works great for their need for programming to be fun. With a toy-like game the very programming solution is usually the game, while with a progressive game it's only the start. A game developer, on the other hand, strives to deliver a world of its own. Something that will draw the player in. Occupy both his mind and fingers. I dare to say, the game developer strives to art while the programmer doesn't.

But I should also say that a game developer is less imaginative and explorative in creating original and unique game concepts, mostly as the result of appreciating the game concepts already done. He/she is more drawn to expand and mimic them. A programmer is usually unaware of most game concepts already done so is prone to come up with something completely original, or is simply more motivated to explore new gameplay solutions.

Of course, you can consider all this as my blind speculation and a clumsy attempt to fit the game developers into two groups. I am fully aware that exceptions to all mentioned characteristics of these two groups exist. Think about them as two extremes.

The conclusion of this article is quite self-evident.

You must be aware, above all, of what you are trying to accomplish with your game project. After you set this you must observe which of your traits as a game developer need to be tamed and which exploited. If you are working on a larger project, you must restrain your need for fast solutions and carefully plan the code of your game. Define what it has to contain and provide. And if game design is not really your domain, please be aware that working on your game project won't be fun for the most of time. Fun comes later. If you can't handle that, don't design games. Above all, remember that game design is not programming (and it really shouldn't be).


An article by Lachie Dazdarian (April, 2008)

Email: lachie13@yahoo.com

Website: The Maker Of Stuff



An Epic Freebasic Tale

Written by Anonymous

The night lay a steady, lowly theta, now at nearly 1 degrees from its origin, based on the current point of view. The sun was hidden beneath a thick, black fog, hardly allowing any light to be passed through the sendLight( Earth.getLand() ) procedure. v1ctor walk forth through the wasteland of what was once a thriving village. Several twigs and leaves crumble underneath his feet, which quickly sink into the ashes they lay upon. Upon a dynamic type check, requesting a boolean of whether or not these are in fact, an instance of plant life, comes a groan of sorrow and agony. Twas naught an instance of plant life, but an instance of human, charred to the bone.

"What sort of monster could be responsible for this?", v1ctor whisper out to the bones, attempting to run them through GDB to backtrace their activity and find the source call, and the one responsible for calling such wretched functions. Function call after function call, he crawl through the stack, nearly at the top. He stopped.

"DAMNIT! A corrupted stack!"

v1ctor could not let himself get angry now. The winds called to him. Destruction was still in the air. Poor syntax, buffer overflows, and in fact, private method and field accessing, and very-much-so inappropriate type castings. It was all here. The culprit must be near. Would v1ctor finally meet his foe today?

v1ctor remembers when he first encountered the battle scars of this foe. This code injector. This memory annihilator. It appeared, corrupted the memory of every human and life instance which populated a given process, and left without a traceable stack. There were very few coders left these days, and even less survivors. He felt responsible for what happened. He was a coder. In fact, he was a damned good one. Yet every single time, he's been unable to stop this monster. He was unable to protect the people he loved.

Now was no time to dwell on his past. v1ctor rose back onto his feet, passing a reference of his sword to several functions contained within the ground API, allowing his sword to rest in place as he felt the vibrations of the operating system, Earth. Just a little further, and v1ctor would have to search no more. His destiny await.

Not but a mile within the battle field, and the fog of a corrupted stack grew ever thicker. Not even the ground directly in front of him was visible. This memory was freshly corrupted.

"Could proper code even exist on such grounds?", v1ctor wondered as he search for the memory address of the ground meant to be in front of him. Never had he been forced to debug such a terribly aligned path. He worried he would segfault at any moment, only worsening the situation, but he must walk on. It was his purpose. He was the only one who seemed able to stop whatever force was responsible for this destruction.

Further v1ctor tread until a dark silhouette appear in the distance. His foe was now visible. The beat stand still, stiff as a stone. v1ctor now haste through the land with all his might, creating a separate thread to manage running, and another to seek through the resources, properly aligning a path in front of him to walk on. As the beast came clearer into view, it was now apparent that this was no silhouette, but the beast itself, black as a void pointer, empty as a null. This could only be one man, if you could call it that any more.

"Mac!", v1ctor extend his sword outward, implying the potential to call a hostile function at any moment.

"... v1ctoooooorrr!!!!", Mac groaned soullessly at v1ctor with utter hatred and despair. Bad coding practice and functions sure enough to cause memory leaks in hand, not a single breath of properly functioning memory could be seen through his terribly dark armor. His armor, large enough for a horse, brandished by a man. Mac's armor was long-since corroded. Viral flesh filled the leaks and holes in code logic. Viral flesh, feeding on all good memory the OS made available to this monster, outputting waste. It was a forever hostile code base, retaining its existence at the cost of system resources.

Mac and v1ctor, two powerful entities, aligned on opposite sides. This was a battle of ideals. This began a fight for legends.. v1ctor rushed towards his opponent hoping for a starting blow, while Mac stand stiffly, confident in his capabilities as a memory corrupter.

Let it begin.

~ End

Author's Comments: I have no intention of causing any drama. I had been meaning to write a small FreeBASIC story for some time. I couldn't think of anything. So I went for good guy vs bad guy, and I chose the one I liked best as the good guy, and one I didn't get along with at all as the bad guy.



Steal This Game: To 'Clone' a Game

Written by Pritchard

You ever have a game you find really fun and want to clone in FreeBASIC? You might run into a few problems while trying. Either the game is too well-polished and complex for you to port over, and you lose focus quickly, or you don't feel like cloning a game in FreeBASIC because you want to make your own, original game.

First of all, in regards to the latter, no game is ever completely original. Game play concepts have been borrowed from an older game, by nearly every game since the first few. You move. You shoot. You dodge. You make combos. You charge bullets. You do lots and lots of stuff that's been done over and over again throughout the massive library of games in existence. As a game developer, you have to begin to see games like art and music. These are two things most of us want to feel are completely creative, and in fact, it's hard to imagine at times, when you hear music or look at a beautiful work of art, that a complex, very-much-so programmable process was behind its creation.

Understanding the process and mechanics that are present in a game might feel strange at first. It's as though you're disassembling what was meant to be viewed as a whole, and played through carelessly. It's the game developer's job to use 'sleight of hand' and 'game magic' tricks to full the player into thinking that the game is one fluent experience. It's also the game developer's job, however, to be knowledgeable of the underlying concepts of any game.

For example, you may wish to first disassemble the 2D Platformer Genre. Let's say Mario. Mario runs, he dodges enemies, he kicks the crap outta them, he powers up with mushrooms, and his has pretty darned expansive interaction with his environment, no? When you're playing Mario, you may feel this way. You feel like this character really exists in this world, and you can imagine it being far more complex and "free" than it really is. What Mario really is, however, is a controllable entity box. He can collide with land boxes. Some of these land boxes can be bumped from the bottom and cause an event, while others can be jumped on top of and cause an event. He can be hurt by damage boxes. Some of these damage boxes can be jumped on and destroyed. Entities may be damage boxes. Fire balls are damage boxes. Ghosts are smart damage boxes. Etc, etc.

That's not a full description of the game, of course, and might seem like an oversimplification. That's a good thing, however. We've now completely ripped apart and ruined the experience the game developer tried to present to us. With the concepts and objects within the game, along with their behaviors and purpose, laid out in front of us, we can now make a *study* out of this presentation. While the presentation might have seemed fascinating before, and seem ruined now, you're sure to be more fascinated in the long-run by how such simplistic, basic concepts can be so entertaining. Not only will you learn more about the structure of games, but you'll also enter a "The more answers you have, the more questions you ask" situation. An entire new way of seeing games and being absolutely amazed by them has been presented to you.

So, now that we've gone over the fact that games are built up of these simple objects and concepts, we can destroy our excuses not to make a clone of an already existing game more completely. No game is too complex. Very few games reach a high level of complexity at root, you'll find. Rather than focusing on the "complexity" and high polish of a game you wish to clone or remake in FreeBASIC, disassemble the game into its root concepts and focus on the game mechanics. Recreate the game play in the most simplistic manner you are able to. Add to this test as you desire. If you can't make the game fun using nothing but primitives and the basic game mechanics, you are either building the wrong system or need to tweak it.

As for the latter point, once again, your "original" game wasn't really going to be original. While your game usually won't offer anything truly new, it may present some game play concepts which had yet to be thought of or present in a game before. Most of the time, these "fresh" games have a fun factor that lies simply in the fact that they offer something new, but it takes time and a real master of game mechanics to tweak the concept just right and create a classic. How you present game play concepts is completely up to you. Whether or not they are truly new concepts altogether isn't as significant.

Original game play tends to come from concept tweaking and presentation. You can present us a game that's a near perfect clone of another game, in regards to the game play concepts, but if you present these objects and obstacles in a way that make us think differently than the game you took the concepts from, only game disassemblers such as ourselves will care to notice. In fact, it's very likely that even you, the developer, will notice how the simple act of tweaking and bending game play concepts makes a game feel original.

If you're interested in experimenting, I recommend the following genres and games to get you familiar with the most common concepts found in games:

Platformers (Mario, Contra), Shooters (1942), Side Scrollers (Double Dragon's a great one), RPGs (Final Fantasy - To save you some time, story interaction w/ puzzles are generally the main game play element), and tons of others. I think you get the point by now. As for games which take the above concepts a bit further - Platformers (Kirby, Sonic), Shooters (Bullet Hell games, tons of others... this genre breaks down into far too many pieces to name), Side Scrollers (Kid Chameleon), RPGs (Final Fantasy, Dragon Quest, Chrono Trigger... This genre has really been defined by its games).

Once you get the hang of the concepts and can setup small "systems" to demonstrate them, you're sure to get the hang of game mechanics and presentation in no time. Good luck!

See ya!,

Pritchard



Kiyote Wolf on a Variety of Subjects

Written by Kiyote Wolf

I have no energy to do a full article submission at the moment, so I'm going to touch briefly on a few subjects here and there.


7x7x16 Trick for SVGA

How to do backgrounds and sprites that are independent of each other in Super VGA.

If you work in Super VGA, either using Freebasic, or some fancy QBasic code, what you notices is you get a lot of subtlety, which is wonderful, if you are doing photo realistic work. That said, there is a way to use that extra fancy color to your advantage, especially if you are doing a down and dirty game for a contest or something.

If you look at your bits, you have 8 bits for red, 8 for green, and 8 for blue. Now, most of your colors are going to be, mathmatically, far apart from each other, you will not need the subtle changes in between. Cut your colors down to half those bits. 4 bits, BUT, use EVEN bits for one layer, and ODD bits for the other layer. Now, calculate your capable colors. 4 bits,.. um.. that's about.. 512 colors?? or so? I don't have my calculator handy, but it's a fair few. If necessary, make a RGB table of the possible colors using even bits only, and using odd bits only. This will help you later to match full RGB color codes to your new 4-bit colors. What you now have, with one half, you have the same colors as the other half, only a shade darker. This is not too troublesome, because you still get a wide range of colors.

Now.. match your color palette down so your background uses one band of colors, and your game sprites use the other band of colors.

Do a test simulation. You will find, you can XOR, OR or whatever you want, and the background will not interfere with the foreground. You will also get a neat half-transparent effect, because the overall color going to the screen is a sum of the total color bits.

This works because the bits never collide. You will only see a problem is when your foreground characters interact with one-another. You will see color inversion and other stuff, but that is common and can be solved by your own discression.

You now have a quick and fast way to make Super VGA backgrounds and sprites that are independent of each other.


Creating new file extension types

Playing God..

What I like most about working in DOS, is your down and dirty, or should i say, guerilla warfare work in the real trenches of your computer. You can almost play god when you invent a new file extension type, because YOU have the say in what goes in it, draw up the rules for it's proper formatting, make it legal or illegal based on tests from your programs when they check for errors..

Every time I invent a new extension, it's like I have discovered a new source of gold or something. I am proud to say that I.. me me me.. created that file extension, and maybe it could become the norm in the code of other users as well.. We can dream can't we? Being able to do a directory sort by your extension, sort and file them. All kinds of mischief and fun.

That I think is one simple pleasure I will always enjoy. What's also amusing is seeing what file type Windows tries to qualify your new extension as, usually as always guessing wrong what you made it into.


The day the DB 15 Pin port, died..

Using a joystick once again in Qbasic

Well, as Windows 98 paraded around, I knew that the joystick port on the computer, which QBasic covets for user input, was on the way out. I really knew it once I started seeing all the new joysticks with USB plugs and actually had a moment of fear. But, I reasoned in the back of my mind, there would be a way, someone would invent a way to do without, just in case, so I programmed all my games with a failsafe keyboard only mode.

One of my work-arounds, which I never managed to actually finish, was to retrofit a keyboard as a joystick, tying the buttons to a controller, or multiple controllers. I reasoned this would be quite useless if I ever went to market a game, and wanted the non-existant joystick support.

Waiting finally paid off when I did a google search for DOSBOX and found a download listed there for a program named JOYKEY.

It would take any USB stick and emulate a keystroke for each movement or button press. My theory proved right. Keyboard would be the failsafe if anything to make my game survive the tragic and sudden loss of it's appendage, the controller.

Now, it's the standard on my computer games, but I have incorporated a key-checker which updates constantly so I don't have to wait for the keyboard-key-repeat to work, which was always annoying.


DOS Shell vs DOSBOX vs nVidia

Forced into DOSBOX under nVidia, learned alot about DOS Shell

When I got my laptop, I got really excited to use a laptop to work on, only to find out QBasic came up with a big blank black screen. Naturally, I royally freaked out. I went on a rampage, changing every setting I could think of in the computer to try to figure out what the glitch was, and eventually discovered, nVidia has no support for legacy video modes, especially ones used by QBasic. I was crushed. A new laptop, totally unusable. (You laugh, but live with QBasic forever and see how you'd miss it too.. ) So, wandering the net, ran into DOSBOX. AHAH! Did some tests, nope, not as quick by far as using a real DOS Shell on either an XP computer or an old 486, but.. at least it worked.

Working in this mode, I have discovered a few good and bad points of DOSBOX vs standard good old DOS.

  1. Legacy sound cards are harder to program for because Windows Vista, ++, etc.... is leaving those old interrupts and memory addresses in the dust. It is very hard to find legacy code that will even make a modern sound card even beep anymore, or at least I have that much trouble. I did find an external MIDI routine that can be called through QBasic to play background music, which uses legacy soundblaster stuffs to work, and works just fine in DOSBOX. It of course, does not run in naitive XP, or most much of anything, so I depend on DOSBOX to do the musical support for me. I'm sure there is some magic code that does work wonders, calling something higher and more powerful than I to rain down a god-like presence in my code and make music without failing, but I'm never quite able to reach that plataeu.

  2. DOSBOX lets you cheat a little, because it virtualizes the entire computer, including the conventional memory where QBasic programing code resides. What that means, is you are able to squeeze so many more lines of code into a QBasic program because of this cheating, make it as full and featured as you want, and THEN, do a trial run in a REAL DOS Shell, and see if it is even within the memory limits of a normal operating environment. I have been able to create the most versatile sprite editor I have ever created, using both EGA and VGA, with dithering and even EMS, and I was only able to do it because I could squeeze in those extra lines of code into the DOSBOX shell ran copy. When I ported it back to real DOS on another machine, I could then pick off the excess and redundant code, and trim it just down to the point where it fits perfectly in memory. That project took me 30 days straight, programming every night. God,.. I still get whiplash just thinking about that month.

  3. DOSBOX protects you and keeps you from reseting your entire machine every time you crash. When I used to code in QBasic, I would do daring things, venture into random code segments, and of course, crash the system. What DOSBOX lets you do, is run your code in a bubble, so if it pops, you can just open a new virtual OS, and be off and running again. When I would do stuff in plain DOS, I would run the risk of crashing the entire PC, and waiting those extra minutes just to get back to work again, and possibly doing it again and again, not quite knowing yet what bit of code did the crash in the first place.

  4. I have been able to create shortcuts listed in the MIME entry part of Windows, so that when I double click an ANSI file, it will run a selected QBasic program and open the file. I even was able to add a second command, which was accessable by right clicking and choosing another command, to run a slowed down copy of the same program to view ANSI animations with. Using DOSBOX command line stuffs with %1 in it of course..


Scrolling technique

Using BSAVE and BLOAD to scroll

When I looked around for trying to make a scrolling game, namely the code for it, I found a few things, EMS & XMS stuff, memory moving routines in ASM, and ran into a bit of headache. Using ASM in XP or any other high caliber OS is a headache sometimes, because when you go POKE'ing and PEEK'ing around, sometimes Windows catches you like the lunch lady and stops you with a big fat error.

One nasty error is when you try to use a Double-Byte MOV command for EVEN numbered movements ONLY, to move a block of memory, ONE BYTE, at a time. This is.. of course, a big NO NO. Most every memory move command, is written to utilize 16-bit memory operations, which "speeds things up." Well, this forces you, to move memory only in even numbered amounts, the smallest being,.. 2 ! Well,.. I ran into a way to scroll the screen, but I could only use it, by moving the entire screen memory map, one pixel to the left, shifting the entire screen, but when I used the ASM routine to do it, it crashed. Of course, it might've ran for awhile, but eventually, it would inevitably crash. Dual byte MOV commands using extended Assembly is not meant for single byte precision. To get around this little problem, I stole a trick from way back in the 8088 days of Microsoft's flight simulator. I read once about using a disk write to do an update of the screen, creating a virtual image before commiting the final picture to screen.

Well, this was all wonderful and stuff, it went on to complain that it could be done in real time, only if the drive access time was fast enough.. going on to calculate vs the seek time and other crap, .. well.. TIMES HAVE CHANGED!!! We now have "virtual drive accesses," which of course, commit the drive data to a Queue, and sometimes it can be updated from within the queue itself and straight back onto the disk almost instantly.

Roughly translated, you can BSAVE 63999 bytes of screen data, starting from an offset of 1, and then BLOAD that same data into the offset of 0, thereby scrolling the screen.

And, if you want to keep the same screen data the same, such as in a scrolling single level game such as Mappy Land, you can Get 199 vertical pixels and move then back to where they were, and the odd pixel that got displaced when you scrolled by one byte, and voila, you have your entire screen, now scrolled one pixel in one direction. Reverse the theory to change direction.

To make use of this in a game, a real game, what I intend to do, which has been validated using a simple batch of testing, is create the level to be scrolled onto the screen as a collection of vertical 200 pixel sprites, loaded as needed into a virtual buffer in the EMS memory, and then PUT'ed onto the right or left part of the screen as the screen is scrolled. Of course, taking a page from SMB 1, going one direction and one direction only makes the scrolling much easier to accomplish and manage, and makes it easier to do over all.

I am currently making a scrolling level editor for this type of game. There will be no vertical scrolling, because that would cause un-ending headaches for me, and am going to post them .. whenever I get them finished.. (God help me...)

My props go out to the guy who did that Sonic clone, cause his levels scroll all four ways, and omg quick.... wow...

This also, is going to use my 7x7x16 trick to make the background and foreground completely independent of each other, and use the 4th bit to mark where the floor is for interaction with the background. Of course colors will suffer, but that's what creativity and dithering are fore, no?

By the way, this technique is actually quite doable, and is relatively fast enough, even in DOSBOX running under emulation. I am thouroughly satisfied with the results of doing this.


No music, no problem

Using the beats to save on programming real music.

Using FM synthesis, Soundblaster, WAV's,.. etc etc etc, .. is fine.. wonderful, and also,.. sometimes a headache. One tiny little trick, for those of us that don't quite have that level of music ability yet, would be to make a rythm section go off in the background, just using a few little minor SOUND or PLAY statements, just to make a little bit of shake and rhumba in your game. If you can't afford the whole orchestra, try just for a drum and a triangle instead. You might find it's more catchey and addictive than a whole musical score anyways.. Stranger things have happened.


Special project

The haunted mouse

I want someone, anyone, to create a QBasic controlled ball style mouse, replacing the little optical sensors with tiny motors, so the mouse can move all over the desktop. I would pay someone to make this a reality. Just a little side project for y'all.


Windows Clone

Organizing projects by grouping using a fake OS shell

I have been kicking around the idea of creating a fake OS shell, windows style, just for QBasic so I can assign files into groups to keep myself from losing track of all my projects. I have 2000+ files in my directory, and they really don't like being apart from each other, which is bogus and sad, so finding a way to sort them all out is becoming more of a necessity. The way I have invented to organize the mass of data to do this is actually something new I just invented, but it's so novel and also so insane, I fear I will get laughed at for posting the workings of it. I plan only to post the details when it is proven and not so laughable, and I have more of a chance to knead out the bubbles.



A Short Description of the Subforums of The QBasic Forum, the forum for QBasic.com

Written by Mac

For years, The QBasic Forum has been dedicated to the QBasic language, and it so continues today. That's the place to help newbies as well as to work out solutions with other senior QBasic programmers.

There are still schools with older equipment, especially in foreign places, so newbies will feed us a while longer.

If you love QBasic (I use QB1.0) and the challenge of solving problems within its limitation just to prove you can do it, The QBasic Forum rules!

Of course, I visit all the forums I can find and hop on any QB1.0 question I see. Unfortunately, many are dying. Hate that.

Anyway, that said, the fact is that there exists valid programming information in the world other than QBasic, so we have what we call "subforums", places for other kinds of posts. Here is a list thereof, which can be found at http://www.network54.com/Index/10167


The Non-QBasic Forum

This is for any question or discussion regardless of language. ASM is often discussed. Also variations of C, FreeBasic, Operating systems (VISTA) and equipment. Whatever you want.


QB Forum FAQ & Samples

From The QBasic Forum, you can reach the QB_FAQ.

But the interesting thing here is that the FAQ is a forum. This means that if we have FAQ "How do you print" and the answer is "Use LPRINT", anyone (we are a democracy) can post a response "But there are other ways" and post sample programs or whatever. It is a FAQ maintained by the public, not by some FAQ guru.


QB Tricks & Techniques

Sometimes you find a neat way to solve a problem and would like to share it. Of course, you can simply post that info at The QBasic Forum, but it would quickly scroll into the 10,000 pages of history. Tough to find later. So this forum is meant to have posts that the author thinks should be permanently easy to find.


QBasic Challenges and Projects

As you would guess, something to challenge the brain. Ideally, these would be "Solve this using QBasic", but it's not a restriction. We've had some good ones, including writing as a team a Kriegspiel Referee in QBasic 1.0 that is astounding. We had to make a special sub-sub forum just for that.


Programs you are proud of

Here a person such as petk010 can establish a thread such as "ProgramList petko10" and describe himself if desired. Then, subordinate to that initial post, he would post QBasic programs (No EXE, etc.) that he was proud.

Why "ProgramList"?

It is to support the network54 search function. If you wanted to see programs by our QB guru TheBob, you would search for "ProgramList TheBob" (in quotes) and get it. "Program List" (with space) would get impossibly too many hits.

If you just search on "ProgramList", you get all of the people who have created threads here.


Magazines and Other QBasic Forums

Just a place for your friendly SPAM. Maybe you have created a forum. It is also a place for announcements such as QB Express availability. It is a forum of its own so that the entries don't scroll away.


Distractions

Sometimes we want to forget about computers and just rant about religion, politics, and philosophy.


Big Programs

This is a tool to support The QBasic Forum. You see, the forum has the option "View All" where all the posts on the current page can be opened simultaneous viewing.

That's nice except when someone posts a zillion line program that makes it hard to use. It takes forever to get past the large programs. For that reason, we only allow reasonably small programs on The QBasic Forum. One would put the big code here and supply the URL in the main post.


QB64 Project

Recognizing that we need a version of QBasic that would run in the 64 environment, our hero Galleon has taken on this task and our regulars support him as possible. Anyone is welcome to download latest versions, try in various configurations, find bugs, etc.


Qbasic on Linux Club

Here reside people who want to get QBasic to work on Linux. People like neuro, mennonite, Ildûrest, Ted (Clippy), rpgfan3233, Michael Calkins, E.K.Virtanen and others, especially our special Pete.


Miscellaneous

If you can't figure out where else to post.

For posts that don't fit anywhere else.


Visit The QBasic Forum and post something!


Monthly Awards

Written by Pete

Site of the Month

FreeBasic Extended Library
http://hmcsoft.org/ext

Webmaster: sir_mud


Back in October, Sir_mud launched the FreeBasic Extended Library website with the help of several notable contributors (stylin, Dr_D, MindlessXD, DrV, yetifoot, cha0s and more) -- and since then, it's been one of the most useful sites in the FB community. Basically, FBExt is a collection of free, useful routines for FB that you'll commonly need to use in your own projects, and the website delivers it in a convenient HTML format. But sir_mud's mission statement says it much more elegantly:

"The goal of the FreeBASIC Extended Library is to provide free peer-reviewed portable FreeBASIC source libraries. It includes many commonly used functions written by the community and licensed under a BSD style license to ensure maximum usage. The source is reviewed prior to inclusion for bugs and is constantly monitored to ensure changes do not introduce new bugs into existing code. All community members are encouraged to contribute to the library."

This month, sir_mud and company, released the newest version of the library -- v0.2.2. If you haven't gotten it yet, be sure to visit the site to grab it!

For its collection of useful Freebasic libraries and continuing dedication to the FreeBasic community as a whole, the FreeBasic Extended Library is our Site of the Month!



Programmer of the Month

Pritchard
http://pritchard.hmcsoft.org/

Pritchard is one of the most active and outspoken members of the Freebasic community -- but there is one thing we can all agree on: he is prolific.

In this issue of QB Express alone, he contributed an article, three tutorials and a few news briefs and other things as well. He's also posted tutorials and thoughtful articles all over the Freebasic.net forums over the last few months (which I've linked to in previous QB Express issues). And that's just the stuff Pritchard has written. He is also an active FB developer. His latest release project is Pritch Library, which we covered up in the News Briefs.

Some of you may be surprised the Pritchard is now one of QB Express's most active contributors, considering the criticism he's leveled against the magazine in the past. But I suppose he had a change of heart -- and you should all be glad that he did. Hopefully he will continue to be such an active contributor to this magazine and to the FB scene as a whole. Judging by his track record, Pritchard isn't going to slow down anytime soon!

For his continued contributions to the FB community, Alexander Joseph Pritchard is this issue's Programmer of the Month!




Scripting Solutions for Games
Part I: Rolling your own interpreter

Written by James S. J. Durtka (notthecheatr)

So you're about to write a game and you aren't sure what to do about scripting? Scripting is a nice element that is very useful in the more complex game engines to allow things to be easily changeable. Sure you can code everything into the engine, but then what if you want to add something to the game? External scripts, along with external maps, tilesets, etc., help to separate between the engine and the game, between the art (story, music, and graphics) and the part you program. More importantly, it makes your engine much more general - in theory, at least, the same engine could be used to make an entirely different game!

This is a series that will probably contain at least four parts, and possibly more depending on how much I decide to do. My goal is not to teach you everything you could possibly know about the subject, but to give you some ideas with which to work from - help you start thinking on your own so you can come up with the solution that's best for you.

The first and most obvious scripting solution for games is simply to write your own scripting interpreter. That's what this tutorial is about, and what the next one will also be about. Later we'll look at XML and Lua, a couple of very standard and useful languages. Quite possibly you'll use more than one solution in a single game, and anyways it's good to look at all the possible alternatives so you know which is best. Another thing I'd like to cover if I've got time is a practical look at using these things. The first four tutorials look at how to use various scripting languages, and when to use them. But that's all theory. If I get around to writing a fifth tutorial, I hope to cover a bit of practical use - perhaps writing a simple game that uses what we've covered so far.

Writing a simple interpreter is actually a pretty easy thing to do, thanks to FreeBASIC's nice handling of strings. Depending on your level of expertise you may be able to write something rather complicated, or not. You may just want to start with a very simple interpreter. Either way, I'll get into the basics here which will help you get started. After that, the future is yours!

So how do we write an interpreter? Well, first we have to figure out what kinds of commands we need. For an RPG, a lot of different commands might be good. You'll probably find that complex stories need a lot of commands to be executed well. Just how much you pack into your script interpreter is up to you. We'll start with a very simple PRINT command that does just what its name suggests. In an RPG scripting engine, the PRINT command might display character dialog inside a little message box. In our example, it just uses the FreeBASIC Print command to display the text.

How exactly would we implement all that? Well, you basically have to be able to look for certain strings - usually the keywords you're looking for, such as Print, Sleep, End, etc. For this we use various combinations of Left, Right, and Mid. These give you part of a string, allowing you to cut out the bits you don't need. When possible, if we only need a single character, we use string indexing with []. These treat the string as a byte pointer array, which means the first character of a string called myString would be myString[0]. In fact, it returns a number rather than a character, so you have to do conversions back and forth with Chr() and Asc(). There are also LTrim, RTrim, and Trim, which cut specific characters off the edges only. Then we have LCase and UCase. Normally we convert everything to lowercase so it's case-insensitive. Of course, if you want a case-sensitive language you can remove the uses of these, but it's pretty nice to be able to use any case you like without difficulty. Using all these string manipulation functions, we manage to parse through strings quite easily. Then we just do what is supposed to be done by the commands we find, as we find them.

Back to our interpreter - since it's to be used in a game, you can't be executing the script interpreter all the time. After all, you have to do graphics, input, AI, and all the rest. If you know how to do multithreading, you might do that. More likely you'll have a "next instruction" command for your interpreter. To make things simple, this will be a line-by-line interpreter: each instruction must be on its own line. That way, we can use the FreeBASIC Line Input command. You may have your own way to do this, but that's how I'm going to do it for this simple example. I'm going to do this with OOP because it's much simpler that way. The nice thing is that you can use more than one interpreter at the same time this way, and therefore run more than one script at a time.

Type interpreter
  Public:
    Declare Constructor (filename As String)
    Declare Destructor ()

    Declare Sub nextInstruction ()

    As uInteger ended
    As uInteger error

  Private:
    As String _filename
    As uInteger _filehandle
    As uInteger _line_number
End Type

This is the interface; the code comes later. As you see, (or not, if you don't understand OOP), if we want to run a script named "someScript.fbsc" then we would simply do this:

Dim As interpreter myInterpreter = interpreter("someScript.fbsc")

Do
  myInterpreter.nextInstruction()
Loop Until myInterpreter.ended <> 0

Sleep

End

Here's an example script:

someScript.fbsc:
'Display "Hello, World!" on the screen
Print "Hello, World!"

'Wait for a keypress
Sleep

'End the program
End

We want this to perform exactly as expected when executed by our interpreter. How do we do that?

Let's start with the constructor. That really just sets everything up in the object, including opening the file specified.

Constructor interpreter (filename As String)
  this._filename = filename
  this._filehandle = FreeFile()
  Open filename For Input As #this._filehandle
  this._line_number = 1
  this.ended = 0
  this.error = 0
End Constructor

We store a copy of the name of the script file internally, just in case we ever need it. Then we open it, storing the file handle (which is the next free file handle). We start at the first line number, and the script has neither ended nor had any errors so far.

Next the destructor. This basically undoes anything the Constructor has done, and anything that has happened in between then and now.

Destructor interpreter ()
  this.ended = Not 0
  Close this._filehandle
  this._filehandle = 0
End Destructor

Seems pretty simple. We mark ourselves ended so we don't try to execute any more instructions, close the file, and set the filehandle to null.

Now for the interpreter. As I said before, everything in the script will be pretty much the same as in FreeBASIC. Only End will be different - it will end the script, but not the main program. Print will simply Print the string, though, and Sleep will simply Sleep. Now because this is meant to introduce you to parsing, I do it in a simple and somewhat hackish way, doing everything step-by-step. Later we'll look at better ways to do things, but there's no point my giving you a solution unless you understand it.

Sub interpreter.nextInstruction()
Dim As String curLine, tmp
Dim As uInteger inQuote = 0

  If this.ended <> 0 Then Exit Sub

  If Eof(this._filehandle) Then
    this.ended = Not 0
    Exit Sub
  End If

  'Read the next line of input
  Line Input #this._filehandle, curLine

  'Trim leading and trailing whitespace off
  curLine = Trim(curLine)

  'There are no commands shorter than three characters, so quit now if we need to
  If Len(curLine) < 3 Then Exit Sub

  'If it's a comment, quit
  If curLine[0] = Asc("'") Then Exit Sub
  If LCase(Left(curLine, 3)) = "rem" Then Exit Sub

  'If the first five characters are "sleep" then sleep
  If LCase(Left(curLine, 5)) = "sleep" Then Sleep

  'If it's Print then search for the first quotation mark, then go until the second.
  If LCase(Left(curLine, 5)) = "print" Then
    'If there are no more characters afterwards, just do a linefeed
    If Len(curLine) <= 5 Then
      Print ""
    Else
      For i As uInteger = 5 To Len(curLine)-1
        'If it's a quotation mark, begin storing the characters beyond it - or stop, depending on where we are.
        If curLine[i] = Asc("""") Then
          inQuote = Not inQuote
          'If we were in a quotation before, quit the loop
          If inQuote = 0 Then Exit For
        Else
          If inQuote <> 0 Then
            tmp = tmp + Chr(curLine[i])
          End If
        End If

        'If we happen to hit the end of the line with no end quotation, raise an error.
        If i = Len(curLine)-1 And curLine[i] <> Asc("""") Then this.error = 1
      Next i

      'When it's all said and done, print the string
      Print tmp
    End If
  End If

  'If the command is to end, simply end the script.
  If LCase(Left(curLine, 3)) = "end" Then
    this.ended = Not 0
  End If
End Sub

If you don't understand it yet, keep trying. Particularly important to understand is the LCase(Left()) lines. For every possible command, we convert the first n characters of the line (after trimming whitespace off) to lowercase (that way it's a case-insensitive language, so you can write PRINT, Print, or print if you like) and check if it's that command). In this case, most things are pretty simple, though the Print command is a little bit complicated to implement. We basically search for the first quotation mark, and after that we keep track of each character. When we hit the second quotation mark, we stop. If we hit the end of the line before the second quotation mark, we note the error but don't do anything about it. After all, it's not a major error.

Here's everything we have so far:

interpreter1.bas:
Type interpreter
  Public:
    Declare Constructor (filename As String)
    Declare Destructor ()

    Declare Sub nextInstruction ()

    As uInteger ended
    As uInteger error

  Private:
    As String _filename
    As uInteger _filehandle
    As uInteger _line_number
End Type

Constructor interpreter (filename As String)
  this._filename = filename
  this._filehandle = FreeFile()
  Open filename For Input As #this._filehandle
  this._line_number = 1
  this.ended = 0
  this.error = 0
End Constructor

Destructor interpreter ()
  this.ended = Not 0
  Close this._filehandle
  this._filehandle = 0
End Destructor

Sub interpreter.nextInstruction()
Dim As String curLine, tmp
Dim As uInteger inQuote = 0

  If this.ended <> 0 Then Exit Sub

  If Eof(this._filehandle) Then
    this.ended = Not 0
    Exit Sub
  End If

  'Read the next line of input
  Line Input #this._filehandle, curLine

  'Trim leading and trailing whitespace off
  curLine = Trim(curLine)

  'There are no commands shorter than three characters, so quit now if we need to
  If Len(curLine) < 3 Then Exit Sub

  'If it's a comment, quit
  If curLine[0] = Asc("'") Then Exit Sub
  If LCase(Left(curLine, 3)) = "rem" Then Exit Sub

  'If the first five characters are "sleep" then sleep
  If LCase(Left(curLine, 5)) = "sleep" Then Sleep

  'If it's Print then search for the first quotation mark, then go until the second.
  If LCase(Left(curLine, 5)) = "print" Then
    'If there are no more characters afterwards, just do a linefeed
    If Len(curLine) <= 5 Then
      Print ""
    Else
      For i As uInteger = 5 To Len(curLine)-1
        'If it's a quotation mark, begin storing the characters beyond it - or stop, depending on where we are.
        If curLine[i] = Asc("""") Then
          inQuote = Not inQuote
          'If we were in a quotation before, quit the loop
          If inQuote = 0 Then Exit For
        Else
          If inQuote <> 0 Then
            tmp = tmp + Chr(curLine[i])
          End If
        End If
        'If we happen to hit the end of the line with no end quotation, raise an error.
        If i = Len(curLine)-1 And curLine[i] <> Asc("""") Then this.error = 1
      Next i

      Print tmp
    End If
  End If
  
  'If the command is Cls, Cls!
  If LCase(Left(curLine, 3)) = "cls" Then
    Cls
  End If

  If LCase(Left(curLine, 3)) = "end" Then
    this.ended = Not 0
  End If
End Sub

Dim As interpreter myInterpreter = interpreter("someScript.fbsc")

Do
  myInterpreter.nextInstruction()
Loop Until myInterpreter.ended <> 0

Print ""
Print "SCRIPT ENDED."

Sleep

End
If you compile this and put a script "someScript.fbsc" in the same directory with it, you'll be able to run the script. Nothing very useful can be written with this yet, but it's a start. If you were paying attention, you noticed that I added another command I didn't mention yet: Cls. Here's a demo of all the features so far:

someScript2.fbsc:
Print "Hi!  This is a script!"
Print "Press a key to clear the screen!"

Sleep

Cls

Print "Press a key to end this script!"

Sleep

End

As you can see, it's pretty easy to add simple commands, but as you add parameters to a command things become more complicated. To simplify this whole process, I created a nice object for line-by-line parsing. I call it the words_list, because that's essentially what it does - convert a string into a list of words. You can get it at http://www.freebasic.net/forum/viewtopic.php?t=10131. It won't work with anything multi-line (though with some basic modifications it probably could) but it's perfect for what we're doing here. No need to search for the quotation marks or whitespace between parameters; it does that for you! It splits strings up based on whitespace, but it handles strings correctly and ignores comments properly as it should. If you'd like, you can read through the source and try to understand how it works; it's very similar to what we do above, only a lot more complicated. Fortunately, you don't have to understand how it works to use it.

Here's how we do our nextInstruction() sub:

Sub interpreter.nextInstruction()
Dim As String curLine, tmp, thisWord

Dim As words_list wordsList = words_list()

  If this.ended <> 0 Then Exit Sub

  If Eof(this._filehandle) Then
    this.ended = Not 0
    Exit Sub
  End If

  'Read the next line of input
  Line Input #this._filehandle, curLine

  'Set the string in the wordslist to the inputted line
  wordsList.setString(curLine)

  'Get the first word, make it lowercase
  thisWord = LCase(wordsList.getWord(0))

  'Try different things depending on its length
  Select Case Len(thisWord)
    'If it's a 3-character word...
    Case 3:
      'Check if it's END or CLS
      Select Case thisWord
        Case "end":
          this.ended = Not 0
          MutexUnlock(this._info_mutex)
          Exit Sub
        Case "cls":
          Cls
      End Select

    'If it's a 5-character word...
    Case 5:
      'Check if it's SLEEP or PRINT
      Select Case thisWord
        Case "sleep":
          'Variable parameters for Sleep
          Select Case wordsList.numWords
            Case 1:
              Sleep
            Case 2:
              Sleep Val(wordsList.getWord(1))
            Case 3:
              Sleep Val(wordsList.getWord(1)), Val(wordsList.getWord(2))
          End Select

        'For Print, we just have to get the next word and trim the first quotation marks off the end.
        Case "print":
          tmp = Mid(wordsList.getWord(1), 2, wordsList.wordLength(1)-2)
          Print tmp
      End Select
  End Select
End Sub

You may be surprised to find that this is only two lines shorter than the original nextInstruction(). That's because of our extensive use of Select Case. However, you can also see how much simpler this is, and you can be sure that adding more instructions will take a lot fewer lines of code than it would have with the old method. Already we've added the two optional parameters to Sleep here. You can do the same with Print if you like.

someScript3.fbsc:
Print "10"
Sleep 500, 1
Cls
Print "9"
Sleep 500, 1
Cls
Print "8"
Sleep 500, 1
Cls
Print "7"
Sleep 500, 1
Cls
Print "6"
Sleep 500, 1
Cls
Print "5"
Sleep 500, 1
Cls
Print "4"
Sleep 500, 1
Cls
Print "3"
Sleep 500, 1
Cls
Print "2"
Sleep 500, 1
Cls
Print "1"
Sleep 500, 1
Cls
Print "0"
Sleep 500, 1

For all this, we still aren't really doing much of anything yet! What about variables?

As usual, I'm going to take the simple road. That is, only numerical variables will be allowed. In fact, all variables will be of the type Double. You're smart, you can figure out how to do other variables types if you need to.

We need some kind of object to handle variables. For every variable that is created using Dim, we need to store the name of the variable and of course the variable itself. Thus, for each variable there will be a String and a Double to deal with. If you wanted other variable types, you'd also need to store the variable type and instead of the double you'd store a Ptr to the variable, whatever it may be.

Type variable
  As String varName
  As Double varValue
End Type

Type variableSpace
  Public:
    Declare Constructor ()
    Declare Destructor ()

    Declare Sub addVariable (vname As String, initialVal As Double)
    Declare Function getVariable (vname As String) As Double
    Declare Sub setVariable (vname As String, newVal As Double)
  Private:
    As variable Ptr _variables
End Type

Of course, the first thing is simply to create the variable, using Dim. Since we're only using the Double type, there's no need to check for "As type" - that's implied. We'll use the good old fashioned QBASIC way. Thus, there are only two real complications - multiple variable declarations on a single line, and initial values. If you added types, things would be even more complicated (especially if you allowed a FreeBASIC-like open syntax, where the "As type" could come before or after variable names), since you'd have to handle all that in addition to multiple variables and initializers.

Dim a = 1, b = 2, c = 3, d = 4

Can we do all that? Sure, and it'll be a lot simpler than you think. The main things to remember:

  • We won't allow expressions to be initializers. That is, you can't initialize a variable to a combination of other variables and/or numbers. For example, you couldn't initialize a variable to 23+5*6. That would just initialize it to 23. We should be able to initialize to other variables, but no expressions. Sorry!
  • There has to be a space between everything. If not, words_list doesn't break things up separately and we have to do that ourselves and it becomes a lot more complicated. If you want you can modify words_list to do this properly - but for simplicity we'll just use spaces.

    So it's really not that hard. If the first word returned by words_list is "dim" then we look for the variable name. If the next word is an equal sign, then we take the next word and turn it into a number using Val, then assign it to the variable we create by calling varSpace.addVariable(). If either the variable another one coming so we loop through this way. If we ever expect something and don't get it, we'll flag an error, but we can probably ignore it. This being a simple interpreter, very few errors really matter in the grand scheme of things. We'll just warn and keep on going.

    variables.bas:
    Type variable
      As Double varValue
      As String varName
    End Type
    
    Type variableSpace
      Public:
        Declare Constructor (parentSpace As Any Ptr = 0)
        Declare Destructor ()
    
        Declare Sub addVariable (vname As String, initialVal As Double)
        Declare Function getVariable (vname As String) As Double
        Declare Sub setVariable (vname As String, newVal As Double)
        Declare Function accessVariable (vname As String) As Double Ptr
    
        Declare Sub destroyAllParents ()
    
      Private:
        As variable Ptr _variables
        As uInteger _num_variables
    
        As variableSpace Ptr _parent_space
    
        As Any Ptr _info_mutex
    End Type
    
    Constructor variableSpace (parentSpace As Any Ptr = 0)
      this._num_variables = 0
      this._variables = Allocate(1)
      this._parent_space = CPtr(variableSpace Ptr, parentSpace)
      this._info_mutex = MutexCreate()
    End Constructor
    
    Destructor variableSpace ()
      MutexLock(this._info_mutex)
      DeAllocate(this._variables)
      MutexDestroy(this._info_mutex)
    End Destructor
    
    Sub variableSpace.addVariable (vname As String, initialVal As Double)
      
      vname = LCase(vname)
      
      'Thread safety, as usual
      MutexLock(this._info_mutex)
    
      'Add a new variable
      this._num_variables += 1
      this._variables = ReAllocate(this._variables, (this._num_variables+1)*SizeOf(variable))
      this._variables[this._num_variables-1].varValue = initialVal
      this._variables[this._num_variables-1].varName = vname
    
      MutexUnlock(this._info_mutex)
    End Sub
    
    Function variableSpace.getVariable (vname As String) As Double
    Dim As uInteger found = 0
    
      If this._num_variables = 0 Then Return 0
    
      vname = LCase(vname)
    
      'Special variables - RND and TIMER
      If vname = "rnd" Then Return Rnd
      If vname = "timer" Then Return Timer
    
      'Thread safety, as usual
      MutexLock(this._info_mutex)
      
      'Default value
      Function = 0
    
      'Check each variable and see if it has the right value
      For i As uInteger = 0 To this._num_variables-1
        'If we find it, return its value
        If this._variables[i].varName = vname Then
          Function = this._variables[i].varValue
          found = Not 0
        End If
      Next i
    
      'If we didn't find it, check our parent space if we have one...
      If found = 0 Then
        If this._parent_space <> 0 Then
          Function = this._parent_space->getVariable(vname)
        End If
      End If
    
      MutexUnlock(this._info_mutex)
    End Function
    
    Sub variableSpace.setVariable (vname As String, newVal As Double)
    Dim As uInteger found = 0
    Dim As Double Ptr dblPtr
    
      If this._num_variables = 0 Then Exit Sub
    
      vname = LCase(vname)
    
      'If trying to set RND, set the seed
      If vname = "rnd" Then Randomize newVal
    
      'Thread safety, as usual
      MutexLock(this._info_mutex)
    
      'Check each variable and see if it has the right value
      For i As uInteger = 0 To this._num_variables-1
        'If we find it, return its value
        If this._variables[i].varName = vname Then
          dblPtr = @(this._variables[i].varValue)
          found = Not 0
        End If
      Next i
    
      'If we didn't find it, check our parent space if we have one...
      If found = 0 Then
        If this._parent_space <> 0 Then
          dblPtr = this._parent_space->accessVariable(vname)
        End If
      End If
    
      If dblPtr <> 0 Then *dblPtr = newVal
    
      MutexUnlock(this._info_mutex)
    End Sub
    
    Function variableSpace.accessVariable (vname As String) As Double Ptr
    Dim As uInteger found = 0
    
      If this._num_variables = 0 Then Return 0
    
      vname = LCase(vname)
    
      'Thread safety, as usual
      MutexLock(this._info_mutex)
    
      'Default value
      Function = 0
    
      'Check each variable and see if it has the right value
      For i As uInteger = 0 To this._num_variables-1
        'If we find it, return its value
        If this._variables[i].varName = vname Then
          Function = @(this._variables[i].varValue)
          found = Not 0
        End If
      Next i
    
      'If we didn't find it, check our parent space if we have one...
      If found = 0 Then
        If this._parent_space <> 0 Then
          Function = this._parent_space->accessVariable(vname)
        End If
      End If
    
      MutexUnlock(this._info_mutex)
    End Function
    
    Sub variableSpace.destroyAllParents()
      If this._parent_space <> 0 Then
        this._parent_space->destroyAllParents()
        Delete this._parent_space
      End If
    End Sub
    

    You may notice that we allow variable spaces to have parents. This allows for scope, so we can have local scopes. That way a local scope variable always overrides the global one with the same name. Obviously we don't have any scoping implemented yet, but we've made it possible for scoping to be implemented later. If a variable is not found in the local variable space, the next one up is checked.

    Another thing we've allowed is to access the variable wherever it is located - you can get its value, with getVariable, or you can get a pointer to it, with accessVariable. The reason for this is actually pretty obvious - your engine might need to access variables used by scripts. For example, a script might create a variable called "player.health" to store the health of the player - and of course the engine only knows what the players health is by accessing this variable directly from the variable space of the script.

    Then we have a simple method for telling a local space to destroy all its parents. The simple reason is that we only store a pointer to one local scope at a time. When the interpreter's destructor is called, it will have to destroy the current local scope as well as all the scopes above it all the way up to the global scope. If this is hard to understand, don't worry - it will become clearer.

    Notice that as usual we always convert variable names to lowercase - this ensures case-insensitivity. If you want case sensitivity (not standard for FreeBASIC or most other languages, though some interpreted languages such as Lua do it) just remove the line

      vname = LCase(vname)
    

    from the beginning of each sub. This will probably speed things up, but it makes things more complicated unless you always remember to use the same case.

    Finally, one small thing to note - notice that we have two "special" variables. These are TIMER and RND, which return the FreeBASIC functions of the name name. One thing worth noting is that if we try to set RND to something, we actually set the random seed. So once we implement variable setting, we could do

    Rnd = Timer
    

    to perform the equivalent of:

    Randomize Timer
    

    Now we have to make a couple of helper functions to determine whether a variable name is valid or not.

    Function isNumeric (testStr As String) As uInteger
      'Check if the first character is numeric or not
      For i As uInteger = 0 To Len(testStr)-1
        Select Case testStr[i]
          Case Asc("0"), Asc("1"), Asc("2"), Asc("3"), Asc("4"), Asc("5"), Asc("6"), Asc("7"), Asc("8"), Asc("9")
            Return Not 0
          Case Else:
            Return 0
        End Select
      Next i
      Return Not 0
    End Function
    
    Function isKeyword (testStr As String) As uInteger
      'Check if the variable name is a keyword
      If LCase(teststr) = "dim" Then Return Not 0
      If LCase(teststr) = "cls" Then Return Not 0
      If LCase(teststr) = "rnd" Then Return Not 0
      If LCase(teststr) = "end" Then Return Not 0
      If LCase(teststr) = "print" Then Return Not 0
      If LCase(teststr) = "sleep" Then Return Not 0
      If LCase(teststr) = "timer" Then Return Not 0
    
      Return 0
    End Function
    

    This should be pretty easy to understand. We'll just have to be sure to update the isKeyword function each time we add a new keyword.

    Now we've got some big changes to make in our interpreter.bas. First, we have to add a local and a global variable space to our interpreter object. The global space is the very top level of scope. Every scope can access variables in the global space so long as there are no local variables with the same name. So when accessing a variable, we check the local space, and it will automatically check everything all the way to the top. But the global space is special; it doesn't have a parent, and it never changes (whereas the local scope can change, at least once we add scope controls).

    #Include Once "words_list/words_list.bas"
    #Include Once "variables.bas"
    
    Enum errorType
      NONE = 0
      BAD_VAR_NAME = 1
      KEYWORD_VAR_NAME = 2
    End Enum
    
    Type interpreter
      Public:
        Declare Constructor (filename As String)
        Declare Destructor ()
    
        Declare Sub nextInstruction ()
    
        As uInteger ended
        As uInteger error
    
      Private:
        As String _filename
        As uInteger _filehandle
        As uInteger _line_number
        
        As variableSpace Ptr _global_var_space
        As variableSpace Ptr _local_var_space
    End Type
    
    Constructor interpreter (filename As String)
      this._filename = filename
      this._filehandle = FreeFile()
      Open filename For Input As #this._filehandle
      this._line_number = 1
      this.ended = 0
      this.error = NONE
      
      this._global_var_space = New variableSpace()
      this._local_var_space = this._global_var_space
    End Constructor
    
    Destructor interpreter ()
      this.ended = Not 0
      Close this._filehandle
      this._filehandle = 0
      
      this._local_var_space->destroyAllParents()
      Delete this._local_var_space
    End Destructor
    

    In the beginning, the local variable space is the global space. But if we enter a new scope, then a new local variable space is created, whose parent is the old local variable space. And when we exit the scope, that variable space is destroyed and its parent comes into scope. When the interpreter is destroyed, all scopes are destroyed at once using destroyAllParents().

    Now for our ever-growing interpreter.nextInstruction() sub. We just have to add the code for Dim (later we'll do some code for Scope...End Scope). This is quite easy. I won't even put the entire listing here, just the part that has changed. Figure out where this goes:

      'Try different things depending on its length
      Select Case Len(thisWord)
        'If it's a 3-character word...
        Case 3:
          'Check if it's END or CLS
          Select Case thisWord
            Case "end":
              this.ended = Not 0
              MutexUnlock(this._info_mutex)
              Exit Sub
            Case "cls":
              Cls
            
            Case "dim":
              'Get the first word after Dim
              tW = 1
              tWord = wordsList.getWord(1)
              
              'Continue so long as there is a comma to the right of a word
              Do
                'If the variable name is invalid, record the error but continue
                If IsNumeric(Left(tWord, 1)) Then this.error = BAD_VAR_NAME
                If IsKeyword(tWord) Then this.error = KEYWORD_VAR_NAME
    
                'If the variable already exists, record an error
                If this._local_var_space->accessVariable(tWord) = 0 Then this.error = DUPLICATED_DEF
    
                'If we find an equal sign next, get the value after the equal sign...
                If wordsList.getWord(tW+1) = "=" Then
                  tW += 2
                  'Get the next word
                  If IsNumeric(wordsList.getWord(tW)) Then
                    'If it's a number, convert it to numeric form
                    tVal = Val(wordsList.getWord(tW))
                  Else
                    'Otherwise assume it's a variable
                    tVal = this._local_var_space->getVariable(LCase(wordsList.getWord(tW)))
                  End If
                Else
                  tVal = 0
                End If
                
                'Create the variable with an initial value of the value specified or 0 if none is specified
                this._local_var_space->addVariable(tWord, 0)
                
                tW += 1
                
                'Get the next word
                tWord = wordsList.getWord(tW)
              Loop While ((Right(tWord, 1) <> ",") Or (wordsList.getWord(tW+1) = ",")) And tWord <> "" 
          End Select
    

    Now we can create a variable and assign it a value. But we can't do anything with the variable! Let's do the most obvious thing: printing.

            Case "print":
              'Start with the first word
              tW = 1
              tWord = wordsList.getWord(1)
              
              If tWord = "" Then Print ""
              
              'For each argument
              Do While tWord <> ""
                'If it ends with a ;, we trim it off and Print with ; so everything is on the same line
                If Right(tWord,1) = ";" Then
                  'If it's a string...
                  If Left(tWord, 1) = """" Then
                    'Truncate the leading and trailing " off plus the trailing ;
                    tmp = Mid(tWord, 2, Len(tWord)-3)
                  'Otherwise, assume it's a variable
                  Else
                    'Get the variable value, removing the trailing ;
                    tmp = Str(this._local_var_space->getVariable(Left(tWord,Len(tWord)-1)))
                  End If
                  Print tmp;
                'But if it doesn't end with ;, don't trim it off
                Else
                  'If it's a string...
                  If Left(tWord, 1) = """" Then
                    'Truncate the leading and trailing " off
                    tmp = Mid(tWord, 2, wordsList.wordLength(tW)-2)
                  'Otherwise, assume it's a variable
                  Else
                    tmp = Str(this._local_var_space->getVariable(tWord))
                  End If
                  Print tmp
                  
                  'Since it's the last parameter to print, we exit the loop
                  Exit Do
                End If
                
                'Next word
                tW += 1
                tWord = wordsList.getWord(tW)
                
                'Forever, until there are no parameters left.
              Loop
    
    

    Whew, how's that work?! Well, take it a thing at a time. Like the Dim, there's a loop involved here. Basically the loop only ends when there are no parameters left - either no more ; at the end or else an empty word. During this loop, we go through each parameter to Print. If the parameter ends with ;, we know more are following, so we remove the ; and print with ; so everything stays on the same line. Otherwise, we print without ; and then exit sub. If the parameter starts with a " then we assume it's a string and remove the leading and trailing "; otherwise, we assume it's a variable and remove nothing, using the entire string to get the locallest variable with that name (0 if the variable doesn't actually exist).

    It seems complicated, but if you think about it hard enough you'll get it.

    Now we'll add this sub to the interpreter object:

    Sub interpreter.accessGlobal (vname As String) As Double Ptr
      Return this._global_var_space->accessVariable(vname)
    End Sub
    

    Which allows anyone to access global variables.

    Now before we quit today, we'll add Input. That should be pretty easy, of course. Input is actually nearly identical to Print, since we print anything in quotation marks. However, variables are inputted instead of printed.

            Case "input":
              'Start with the first word
              tW = 1
              tWord = wordsList.getWord(1)
              
              'For each argument
              Do While tWord <> ""
                'If it ends with a ;, we trim it off and Print with ; so everything is on the same line
                If Right(tWord,1) = ";" Then
                  'If it's a string...
                  If Left(tWord, 1) = """" Then
                    'Truncate the leading and trailing " off plus the trailing ;
                    tmp = Mid(tWord, 2, Len(tWord)-3)
                    Print tmp;
                  'Otherwise, assume it's a variable
                  Else
                    'Get the variable value, removing the trailing ;
                    Input tVal
                    this._local_var_space->setVariable(Left(tWord,Len(tWord)-1), tVal)
                  End If
                'But if it doesn't end with ;, don't trim it off
                Else
                  'If it's a string...
                  If Left(tWord, 1) = """" Then
                    'Truncate the leading and trailing " off
                    tmp = Mid(tWord, 2, wordsList.wordLength(tW)-2)
                    Print tmp
                  'Otherwise, assume it's a variable
                  Else
                    Input tVal
                    this._local_var_space->setVariable(tWord, tVal)
                  End If
                  
                  'Since it's the last parameter to print, we exit the loop
                  Exit Do
                End If
                
                'Next word
                tW += 1
                tWord = wordsList.getWord(tW)
                
                'Forever, until there are no parameters left.
              Loop
    

    Comparing it to the code for Print, you'll see it's very similar but there are of course those few changes mentioned. And now we can do this:

    someScript5.fbsc:
    Dim someVar
    
    Input "Enter a number ";  someVar
    
    Print "You entered:  ";  someVar
    

    We've come a long way, but we've still got a ways to go. In particular, we need to be able to implement expressions so we can manipulate variables properly and so we can implement conditionals and looping. All that will be covered in the Part II of Scripting Solutions for Games. Once we finish this, we'll move on to using XML and Lua. Finally, we'll bring it all together and build a mini-game with the stuff we've learned.

    I hope you've enjoyed this. As usual, any comments, errata, questions, etc. should be sent to TheMysteriousStrangerFromMars@yahoo.com or addressed to me (notthecheatr) on the forums.

    Downloads:
    This tutorial by itself
    This tutorial and the example files included with this tutorial



  • Game Camera Systems: An Important Part of Gameplay

    Written by Pritchard

    One of the most important devices in a game is going to be your camera. Different games have had different cameras, for different reasons.

    I'm going to spray you with a few camera systems. Rather than develop a small number of very advanced camera systems, I want to show you some of the different methods you can use to update your camera and focus on the player. I hope that once you've gotten familiar with the idea, you study motion and targeting enough to develop your own camera system.

    The main camera updating code will be in camera.updatePos().

    1) Hover Over a Player at a Given Point

    A camera's just a point that's focusing on one or more targets. The camera moves right, you appear to move left. Rendering is just your (position - camera position). I also gave the camera a "center" vector in case you wanted to adjust what point the camera keeps you at.

    2) Follow the Player Once They Try and Escape the Bounding Box

    Here we just move the camera when the character moves out of the boxed area. This is one of the most simple, yet most effective camera systems there are.

    3) Follow the Player Once They Try and Escape the Circular Distance Area

    You may wish to use a circular camera, especially if your game play focuses on the center of the screen. Here we projected a point on the edge of our camera, based on the angle between you and the camera, and made sure you didn't escape the circle. Once we find the point on the edge of our camera, this is just like the first camera example.

    4) Follow the Player at a Constant Rate

    The camera here moves at a constant rate towards you. It might skip around a bit since it doesn't slow down when it gets close to you. To prevent massive shaking, however, I have it simply stick to you if you're at or closer than 5 pixels to its center.

    5) Follow the Player at a Rate of Reaching Them in N Seconds

    This is a more advanced version of the second camera example. We set how long it should take the camera to reach the player. We update the camera speed every frame, so the camera will slow down as it gets closer to you.

    6) Jolted Movement

    The jolted movement really only works well when you have a more advanced system for it, but here's the basic idea. Your camera's now being held by someone with unsteady hands.

    7) Velocity-Based Movement

    We gave this camera velocity. It may skip over the player a bit, like a real camera operator would, or at least how something with velocity would. Since when did we move toward the target until we're exactly over its center, anyways? A more advanced version of this would randomize the point the camera focuses on, making the focus a bit off center, so we're not some mad genius focusing exactly on the target.

    For the most part, I've found that unless you create a very advanced camera system, the extra work to make it seem more human isn't worth the effort, or the processing power. Depending on the game, however, you may want the camera to reflect environmental effects, player actions, and player state (Ex: unhealthy has a blurry/shaky camera). All for dramatic ambiance of sorts.

    8) Camera Bounding Box Collision

    The bounding box collision here just checks if you're collided now. If you weren't last frame, then you must have *just* collided this frame, so we can push you back on that edge. Using this technique, you could collide with multiple edges, moving at all angles.

    Overall, my goal here was just to get you thinking about camera systems and show some I've played around with. The perfect camera system for your game will depend on a lot of variables. I just hope I've sparked a few ideas in your heads. Hopefully you'll study motion a bit and come up with the camera system best fit for your game.



    The Simplicity of Custom Camera Coordinates

    Written by Pritchard

    So you don't want to depend on screen resolution for how your graphics are drawn, eh? You want coordinates that will work no matter what resolution you have? Alright, well we can do this.

    You can do this in one of two ways:

    The first is to use OpenGL and gluOrtho2D and set the desired coordinates after setting your viewport. No matter the resolution of your display, OpenGL will always render your primitives within the region (x, x2, y2, y).

    Don't know OpenGL? Then let's move on to an FBgfx version. We're going to develop a very simple coordinate system for our FBgfx window, which will use (0, 0) as the upper left of the window, and (1, 1) for the end of the screen. Precision levels will vary with screen resolutions. Using (1, 1) for our end coordinates makes learning this topic a lot easier, because when multiplying or dividing by one, you get the number your started with. The transformation from custom coordinates to screen coordinates is thus, very simple.

    #include once "fbgfx.bi"
    
    '' set up our screen
    const as integer w = 640, h = 480
    screenres w, h, 32
    
    '' our object dimensions - x, y, width height
     dim as double oX = 0.1, oY = 0.1, oW = .8, oH = .8
    
    '' draw our object
    line ( w * oX, h * oY ) - _
           ( w * (oX + oW), h * (oY + oH) ), rgb(255, 255, 255), bf
    sleep

    The transformation between custom coordinates, and screen coordinates, is simple. Since we're using a 1-based system, we don't have to calculate much anything. Our coordinates will render at the same respective positions no matter the screen resolution. Let's take a quick look at what our math's doing here though. We call our line statement with the following arguments:

    (w * oX) - Screen width, times our coordinate. This turns out to be 640 * .1, which is 64, one tenth of the screen width. Will W * .1 will still be (w/10) no matter the resolution, so we always start at one tenth the width. The same math applies to (h * oY).

    (w * (oX + oW)) - This may be a little trickier for some people. They would notice that the oX and oW have to be in parenthesis together for this to work. If you remove the parenthesis, you're actually doing (w * oX) + oW, due to order of operations. That definitely won't work. Both variables have to add their values together *before* being transformed to the screen coordinates. Because oX and oW are compatible in (1, 1) coordinates, while our w comes from the screen resolution coordinates.

    I hope you understand how our math works now, because we're now going to add the ability to change your coordinate system. There's a little additional math explained when doing this, but it's all still very simple.

    #include once "fbgfx.bi"
    
    '' set up our screen
    const as integer w = 640, h = 480
    '' set up our custom coordinates
    const as double customW = 100, customH = 100
     screenres w, h, 32
    
    '' our object dimensions - x, y, width height
    dim as double oX = 10, oY = 10, oW = 80, oH = 80
    
    '' draw our object
    line ( w * (oX / customW), h * (oY / customH) ) - ( w * ((oX + oW) /
    customW), h * ((oY + oH) / customH) ), rgb(255, 255, 255), bf
     sleep

    Here we added a custom coordinate system which is based on (100,100) resolution.

    Remember how we said that a 1-based system was easiest? Look at the math. It's the same (w * oX). Due to 1 not changing any values in multiplication, you can think of the original math as (w * (oX / 1)). 1 is our coordinate system value. We just didn't have to include the 1 before, because it makes no difference as to the result of the computation.

    We divide by our coordinate system value because we first need to adjust our object positions to our custom coordinates (remember about coordinate compatibility). The result of this transformation is how much of our custom coordinate system our object value is taking up. That result is then used to compute the screen coordinates.

    (oX / customW) - 10 / 100. 10 / 100 is .1. .1 * 640 = ... 64!

    Same result as before.

    I hope that was simple enough. If not, remember - Group your coordinates together. Every set of compatible coordinates is enclosed in parenthesis, and every multiplication or division transforms a group of coordinates into *another* group. In total, we have three groups. Our object position coordinates, the transformation coordinates to our custom coordinate system (meaning that if your custom values are the same as your real values, no more transformations are needed - multiplication is the inverse of division), and the final screen coordinates.

    See ya!,

    Pritchard


    Download a copy of this tutorial: The_Simplicity_of_Custom_Camera_Coordinates_-_Pritchard.txt


    Bitflags and You: A FreeBASIC/QBASIC tutorial

    By Imortis Inglorian (Luther Ramsey)

    Introduction:

    Hello, class. My name is Mr. Inglorian and today we will be learning about a very special type of variable called a flag. Now, some of you may know exactly what I am talking about and may think that this tutorial is a waste of time... Well, you know what... *sniffle* I DON'T LIKE YOU ANY MORE! DON'T LOOK AT ME!

    Seriously though, this is something that many people may not understand, so I figured a thorough tutorial would be good.

    Let's Get Started:

    What is a flag, you ask? A flag is any variable in which the values have a very specific significance. That value could be anything. Just so long as it has some significance to the program. Traditionally, flags are either yes/no, true/false, or 0/1. For those of you who have used newer languages, boolean variables are flags. A flag could just as easily be any other value. When I was in a beginning programming class, we would often use the number 9999 as a flag, or even the word “done”. The point here is that it does not matter what the flag value is, just that it is important to the program in some way. For this tutorial we will be talking about a special type of flag called a bit flag. This i s where you use a single bit for a flag. That means that you are working with the 0/1 type of flag because that is the only numbers that can be represented by a bit.

    Binary Counting:

    Now, when using bits, you have some very interesting problems. One of which is counting. For those of you who know how to count in binary, you may wish to skip this section.

    For those of you left, let's get started. Most of us are used to base 10 number systems. That is, decimal. We understand the number 128 or 1,024 at a glance. We have no problem deciphering that. But if we have the number &b10000000, or perhaps &b10000000000, the mind goes in a completely different direction. These numbers are, in fact, decimal 128 and 1,024, just represented in base 2 instead of base 10. Base 10 has a 1's column, a 10's column, a 100's column, and so on. Base 2 and a 1's column, a 2's column, a 4's column, a 8's column, a 16's column, a 32's column, and so on. If you look at base 10, every place value goes up by an order of 10, that is, add a zero to the end. In base 2, every place value goes up double the one before it. This may seem confusing, but it works just the same as decimal. When you count in decimal you have 10 digits to work with: 0-9. You start out like this:

    0,1,2,3,4,5,6,7,8,9...
    

    What happens when you run out of digits? You start a new column out with 1, then keep going:

    10,11,12,13,14,15,16,17,18,19...
    

    Now what do you do? You ran out of digits with the right column again... Well, now you increment the first column again:

    20,21,22,23,24,25,26,27,28,29
    

    And so on. It is no different with binary. The only difference is that it only has two digits to work with. Instead of 0-9, you only have 0 and 1 so it looks like this:

    0,1...
    

    Oh, we ran out... Time to make a new column and start it at 1:

    10,11...
    

    Dang, ran out again... New Column:

    100,101...
    

    Now what? We ran out in the first column, so we reset it to zero and move up to the next:

    100,101,110,111...
    

    And so on. It's just like decimal, only with fewer digits.

    I Told You That To Tell You This:

    Now that you (hopefully) understand binary, we can look at the rest of the topic. Lets look at a complex looking binary number:

    10101100
    

    This is decimal 172, but that's not important right now. What is important is what the number is made up of. It is made up of 8 individual flags. 8 ones or zeros. From those eight digits you can have 256 possible combinations of numbers. Do you realize that you can use those 8 digits to store the answer to 8 different yes/no style questions? It's true! And that is what I am going to show you how to do in this tutorial: To store and retrieve information from bitflags in any variable you like.

    Use of Bitflags:

    Imagine you are making a game. Your player can do all sorts of thing in your game. He can jump, attack, fall, get hit, and die. Now imaging trying to keep track of all those pieces of information:

    Dim as String isJumping
    Dim as String isAttacking
    Dim as String isFalling
    Dim as String isHit
    Dim as String isDead
    
    If isJumping = “Yes” then
         'Do some code related to jumping
    ElseIf isAttacking = “Yes” then
         'Do some code related to attacking
    ElseIf
    ...
    EndIf
    

    And thats just a small sample! What if you wanted the player to be able to attack while jumping? What if you wanted the player to be able to crawl? Or fly? You would have to make a new string variable for each possible state, and check if it is “Yes” or “No” on every single turn. That is a lot of strings! In QB you have some memory limitations, and strings can bet pretty costly. Plus, strings take more time for the computer to work with, so you can slow down your program considerably in that manor in both QB and FB. A better way to handle it is to use a number:

    Dim as Integer isJumping
    Dim as Integer isAttacking
    Dim as Integer isFalling
    Dim as Integer isHit
    Dim as Integer isDead
    
    If isJumping = 1 then
         'Do some code related to jumping
    ElseIf isAttacking = 1 then
         'Do some code related to attacking
    ElseIf
    ...
    EndIf
    

    This is much more efficient in terms of memory and speed, but still not great. With bitflags you can store all those question variables in one variable.

    Dim as Integer PlayerFlags
    

    This one variable can hold 16 questions in QB and 32 in FB. Nice huh? Talk about conserving memory. Now let me show you how it is done:

    How It's Done:

    First thing you do is use ANDing. You use the AND keyword in FB or QB to accomplish this task. ANDing will only return 1 if both pieces it looks at are 1. So here is what is called a Truth Table for AND:

    0 AND 1 = 0
    1 AND 0 = 0
    0 AND 0 = 0
    1 AND 1 = 1
    

    Understand? That means that every time AND finds a 1 in both of your binary numbers, it will return a 1 in its output. Here is a more complex example:

    10101100 AND 10010100
    

    I'm going to put them one over the other so you can see the results more clearly.

    10101100
    10010100
    ------------
    10000100
    

    The only time a 1 came through is when both numbers had a one in the same column. This is the process of ANDing. So, if you want to see if the 3rd bit is currently a 1, you just do this:

    10101100 – Number to check for third bit
    00000100 – Just the third bit active
    -----------
    00000100 – Result will be just the third bit set.
    

    If the result comes out as the bit you were looking for, then the bit was set. If it comes out as 0, then the bit was not set in the number you checked for. See? Easy, right? In code you can check it like this:

    If ([bitflag] and [desired bit]) = [desired bit] then
         ‘Do code if bit is set
    EndIf
    

    Now you know how to see if a bit was set. Next, I’ll show you have to set a bit. Setting a bit is done through ORing. OR returns 1 if either the first number or the second number has a one in that column. Here is a truth table for OR:

    0 OR 1 = 1
    1 OR 0 = 1
    0 OR 0 = 0
    1 OR 1 = 1
    

    Using the numbers I used earlier, that would look like this:

    10101100
    10010100
    ------------
    10111100
    

    The result is that everywhere there is a one in either number, the result will be a one. So if you want to set the fifth bit in a number you just OR then number with the bit you are looking for. Like this:

    10101100 – Original Number
    00010000 – fifth bit
    -----------
    10111100 – Result will be original number with fifth bit set.
    

    Nice huh? And here is the code for doing it:

    [bitflag] = [bitflag] OR [desired bit]
    

    See? It’s that simple. Now, that’s all fine and good, but what if you want to set a bit to zero? OR won’t work for that. Instead you need to use AND and NOT. NOT takes every 1 bit and changes it to 0, and every 0 bit gets changed to 1. NOT just switches all bits to the opposite digit. Here is a truth table for NOT:

    NOT 1 = 0
    NOT 0 = 1
    

    So if you were to use NOT on the Number we have been using, it would look like this:

    10101100 – This is the original number
    -----------
    01010011 – The is the result
    

    So to set bit 4 to 0, you need to do this:

    10101100 – Original Number
    11110111 – All but fourth Bit set (result of NOT on fourth bit alone)
    ----------
    10100100 – The only thing changed was the fourth bit.  It is now off.
    

    See. That’s simple too. The code for that looks like this:

    [bitflag] = [bitflag] AND NOT([desired bit])
    

    See. It’s easy. The cool part is that this code will work in FB or QB, with no changes between them. You can use the same code in both and it will work perfectly. But if you don’t like doing all that in FB it does have a few macros that you can use to make it a little less painful.

    BIT:

    BIT works like ANDing to find out if a bit is 1 (set). The difference is that you don’t need to pass it the number that evaluates out to just that bit being set. Instead you just pass to it the number of that bit. So if you want to know if the fourth bit is set, you do this:

    Result = BIT([original numbet], 4)
    

    If the fourth bit is set, then it will return 1, otherwise it returns 0.

    BITSET:

    BITSET sets the bit you request. Its usage is just like BIT, so I won’t go into a long explanation. Instead, I’ll just show you:

    Result = BITSET([original number], [number for desired bit])
    

    The result is the original number with the bit you requested set.

    BITRESET:

    BITRESET looks just like BITSET, only it sets the desired bit to 0:

    Result = BITRESET([original number], [number for desired bit])
    

    The result will be the original number with the requested bit being set to 0 (or reset).

    Conclusion:

    So, there you have it. This is a simple and memory-efficient way to do a boolean style variable in FB or QB. You can do all kinds of neat stuff with it, but I will leave that for another tutorial if anybody wants one. Here is a source code file that demonstrates what I just showed you. Let me know by e-mailing me at ImortisInglorian@gmail.comImortisInglorian@gmail.com.


    Download a copy of this tutorial: bitflags.html


    Kiyote Wolf's Amazing Cheap Texturemapping

    Written by Kiyote Wolf

    It's cheap cause it's SLOOOOOOOOOOOOOOOOOOW...

    This routine works with a few givens, it uses a standard array, used for PUT/GET sprite graphics, the index of the sprite, and a chain style entry of data for the 4 corners and the amount of repeating of the texture.


    You will notice, it takes the first 8 entries and applies it to where the corners of the polygon are. It uses the same standard polygon definition, where it goes from the upperleft corner, clockwise, to the bottom left corner. Then, it see's if there was a command "SCALE" in the chain and takes the next two entries as the values to repeat the texture within the area plotting, in both X and Y directions.

    How does it work? Well, one day awhile ago.. last year?? I had one of those genius, Einstein moments, and it had something to do with a problem i was upset about with Google Sketchup, not "Google's Ketchup..", but anyway, I decided to make a SVGA texture mapping routine.


    Well, that was fun for awhile, but what do I do most? Work with sprites. So I worked at it for a week and condensed the entire program down into a single SUB, which lets me take my ordinary sprites, and make them extrodinary textures.

    The code self checks itself, and modifies it's way of rendering to adjust for redundancy, speed, scale, and size, and makes minor adjustments based on a radius method of checking your proposed work. I'm sure that making each of these modes of drawing would make for another copy of this routine, letting me manually choose the scale and quality based on individual results, but I am obviously quite lazy and am going to let the computer choose the quality as per it's measuring.

    What happens in this code is a giant binary tree search, which does a sliver of the mapped polygon in one direction, as a binary tree, grabbing pixels from the source to the destination by measuring each point in the tree as it goes along. Why is this important? Because, when we go into details of how the polygon stretches, by going at it in a binary fashion, where the pixels are originally and where they ultimately should go, the computer is able to faithfully recreate the texture in a real stretched reproduction because it measures half of half of half of this distance between the actual and the model.

    I have even tested this routine to do a floor mapping technique, which it lacks the checking ability to control the pixels to make them large enough for floor mapping, but that can be changed of course eventually, which means,..

    You can plot your texture mapped polygon on the screen, or have every point off the screen without having to worry about clipping. The math is still going to work even with negative numbers and far off positive numbers that do not appear on the screen, and your texture is going to appear on the screen as it should, only what is visible appearing, because the code will do so faithfully.

    It is slow as 8088 GWBasic, or worse, but for those of us who wanna grasp the idea of 3D math without having as much headache, and those of us who cheat and pre-render.. (hee hee.. me..), this is a golden opportunity to test out something random in the name of 3D math, and get away with it.

    I am very very proud of the insane but actually doable nature of this code, because that has always been my way of doing anything anyway.

    Kudos 3D programmers, and 3D n00bs like myself.

    '*******  WORKING EXAMPLE OF CODE,.. FINISHED AND READY TO USE.. ASSUMING THE GRID MULTI
    ' CODE IS PRESENT AS WELL AS PER MY PREVIOUS COLUMN ENTRY.
    ' YOU WILL ONLY NEED THE VERT AND THE GRID MULTI ROUTINES TO USE THIS EXAMPLE.
    ' I USED THE CHAIN DATA ENTRY METHOD CAUSE I HATE USING TWENTY MILLION VARIABLES JUST FOR X/Y DATA
     
    SUB TextureSpr (Sprite(), image, DataStrg2$)
    DataStrg$ = DataStrg2$
    REDIM CornerX(20), CornerY(20)
    
       ImageWidth = Sprite(0) / 8          'get the image width
       ImageHeight = Sprite(1)             'and height
       WWidth = ImageWidth
       WHeight = ImageHeight
       ElmPerImg = (4 + INT((ImageWidth * 8 + 7) / 8) * ImageHeight + 1) \ 2
       'without going into a LONGINTEGER format, we can check an overflow
       'by subtracting the ADDEND from the .....you get the point...
       wbuf = 0
       FOR w = 1 TO image
     wbuf = wbuf + ElmPerImg
     IF (32768 - wbuf) < ElmPerImg THEN EXIT SUB
       NEXT w
       StartElm = ElmPerImg * image                            'element # in the
       IF UBOUND(Sprite) < StartElm THEN EXIT SUB
       SourceSeg = VARSEG(Sprite(StartElm))                    'source image
       SourceOfs& = VARPTR(Sprite(StartElm)) + 4    'skip size info
     
    CornerX(4) = GridMulti(DataStrg$, 0)
    CornerY(4) = GridMulti(DataStrg$, 1)
    CornerX(5) = GridMulti(DataStrg$, 2)
    CornerY(5) = GridMulti(DataStrg$, 3)
    CornerX(6) = GridMulti(DataStrg$, 4)
    CornerY(6) = GridMulti(DataStrg$, 5)
    CornerX(7) = GridMulti(DataStrg$, 6)
    CornerY(7) = GridMulti(DataStrg$, 7)
    '
    Scale2 = 7  
    ScaleX = 1
    ScaleY = 1
    ScalePtr = GridIF(DataStrg$, "SCALE")
    IF ScalePtr > 0 THEN
      ScaleX = GridMulti(DataStrg$, ScalePtr + 1)
      ScaleY = GridMulti(DataStrg$, ScalePtr + 2)
    END IF
    
    CornerX(0) = 0
    CornerY(0) = 0
    CornerX(1) = ImageWidth * ScaleX
    CornerY(1) = 0
    CornerX(2) = ImageWidth * ScaleX
    CornerY(2) = ImageHeight * ScaleY
    CornerX(3) = 0
    CornerY(3) = ImageHeight * ScaleY
    FOR w = 4 TO 7
      IF w = 4 THEN
        CornerAvgX = CornerX(w)
        CornerAvgY = CornerY(w)
      ELSE
        CornerAvgX = (CornerAvgX + CornerX(w)) \ 2
        CornerAvgY = (CornerAvgY + CornerY(w)) \ 2
      END IF
    NEXT w
    CornerDist! = 0
    CornerMax! = 0
    FOR w = 4 TO 7
      CornerDist! = SQR((CornerAvgX - CornerX(w)) ^ 2 + (CornerAvgY - CornerY(w)) ^ 2)
      IF CornerDist! > CornerMax! THEN CornerMax! = CornerDist!
    NEXT w
    Scale = 0
    '<> Does the max size go beyond 15 pels? If so,.. work large,.. if not.. small
    IF CornerDist! < 15 THEN Scale = -1
    Scale3 = 0
    IF CornerDist! > 30 THEN Scale3 = -1
    Scale4 = 0
    IF CornerDist! > 50 THEN Scale4 = -1
    'IF Scale2 > 1 THEN Scale = -1
    ' 0   1
    '
    ' 3   2
    '        We render our simple texture mapped polygon similar to standard
    ' methods
    FOR w = 0 TO 7
      CornerX(w + 8) = CornerX(w)
      CornerY(w + 8) = CornerY(w)
    NEXT w
    REDIM pgn(10, 10, 10)
    '4095 = 12 bits
    FOR BigCnt = 0 TO (63 + 48 * Scale * (-1 * (Scale2 = 1)))
      FOR BigCnt2 = 0 TO (63 + 48 * Scale * (-1 * (Scale2 = 1)))
    FOR w = 0 TO 7
      CornerX(w) = CornerX(w + 8)
      CornerY(w) = CornerY(w + 8)
    NEXT w
    FOR t = 0 TO (7 + 4 * Scale * (-1 * (Scale2 = 1)))
    Flag = SGN(BigCnt AND 2 ^ t)
    Flag2 = SGN(BigCnt2 AND 2 ^ t)
    pgn(1, 1, 1) = CornerX(0)
    pgn(2, 1, 1) = (CornerX(0) + CornerX(1)) \ 2
    pgn(3, 1, 1) = CornerX(1)
    pgn(1, 2, 1) = (CornerX(0) + CornerX(3)) \ 2
    pgn(2, 2, 1) = (CornerX(0) + CornerX(1) + CornerX(2) + CornerX(3)) \ 4
    pgn(3, 2, 1) = (CornerX(1) + CornerX(2)) \ 2
    pgn(1, 3, 1) = CornerX(3)
    pgn(2, 3, 1) = (CornerX(3) + CornerX(2)) \ 2
    pgn(3, 3, 1) = CornerX(2)
    pgn(1, 1, 2) = CornerY(0)
    pgn(2, 1, 2) = (CornerY(0) + CornerY(1)) \ 2
    pgn(3, 1, 2) = CornerY(1)
    pgn(1, 2, 2) = (CornerY(0) + CornerY(3)) \ 2
    pgn(2, 2, 2) = (CornerY(0) + CornerY(1) + CornerY(2) + CornerY(3)) \ 4
    pgn(3, 2, 2) = (CornerY(1) + CornerY(2)) \ 2
    pgn(1, 3, 2) = CornerY(3)
    pgn(2, 3, 2) = (CornerY(3) + CornerY(2)) \ 2
    pgn(3, 3, 2) = CornerY(2)
    pgn(1, 1, 3) = CornerX(0 + 4)
    pgn(2, 1, 3) = (CornerX(0 + 4) + CornerX(1 + 4)) \ 2
    pgn(3, 1, 3) = CornerX(1 + 4)
    pgn(1, 2, 3) = (CornerX(0 + 4) + CornerX(3 + 4)) \ 2
    pgn(2, 2, 3) = (CornerX(0 + 4) + CornerX(1 + 4) + CornerX(2 + 4) + CornerX(3 + 4)) \ 4
    pgn(3, 2, 3) = (CornerX(1 + 4) + CornerX(2 + 4)) \ 2
    pgn(1, 3, 3) = CornerX(3 + 4)
    pgn(2, 3, 3) = (CornerX(3 + 4) + CornerX(2 + 4)) \ 2
    pgn(3, 3, 3) = CornerX(2 + 4)
    pgn(1, 1, 4) = CornerY(0 + 4)
    pgn(2, 1, 4) = (CornerY(0 + 4) + CornerY(1 + 4)) \ 2
    pgn(3, 1, 4) = CornerY(1 + 4)
    pgn(1, 2, 4) = (CornerY(0 + 4) + CornerY(3 + 4)) \ 2
    pgn(2, 2, 4) = (CornerY(0 + 4) + CornerY(1 + 4) + CornerY(2 + 4) + CornerY(3 + 4)) \ 4
    pgn(3, 2, 4) = (CornerY(1 + 4) + CornerY(2 + 4)) \ 2
    pgn(1, 3, 4) = CornerY(3 + 4)
    pgn(2, 3, 4) = (CornerY(3 + 4) + CornerY(2 + 4)) \ 2
    pgn(3, 3, 4) = CornerY(2 + 4)
      CornerX(0) = pgn(1 + Flag, 1 + Flag2, 1)
      CornerY(0) = pgn(1 + Flag, 1 + Flag2, 2)
      CornerX(1) = pgn(2 + Flag, 1 + Flag2, 1)
      CornerY(1) = pgn(2 + Flag, 1 + Flag2, 2)
      CornerX(2) = pgn(2 + Flag, 2 + Flag2, 1)
      CornerY(2) = pgn(2 + Flag, 2 + Flag2, 2)
      CornerX(3) = pgn(1 + Flag, 2 + Flag2, 1)
      CornerY(3) = pgn(1 + Flag, 2 + Flag2, 2)
      CornerX(4) = pgn(1 + Flag, 1 + Flag2, 3)
      CornerY(4) = pgn(1 + Flag, 1 + Flag2, 4)
      CornerX(5) = pgn(2 + Flag, 1 + Flag2, 3)
      CornerY(5) = pgn(2 + Flag, 1 + Flag2, 4)
      CornerX(6) = pgn(2 + Flag, 2 + Flag2, 3)
      CornerY(6) = pgn(2 + Flag, 2 + Flag2, 4)
      CornerX(7) = pgn(1 + Flag, 2 + Flag2, 3)
      CornerY(7) = pgn(1 + Flag, 2 + Flag2, 4)
    
    NEXT t
     
      FOR m = 1 TO 3
        FOR n = 1 TO 3
      
      IF Scale = 0 THEN
      FOR o = -1 TO 1
      FOR p = -1 TO 1
       
       px = ABS(pgn(m, n, 1) - (o * (Scale2 > 1)) * (-1 * (Scale <> 0))) MOD ImageWidth '+ o
       py = ABS(pgn(m, n, 2) - (p * (Scale2 > 1)) * (-1 * (Scale <> 0))) MOD ImageHeight'+ p
       IF Scale3 THEN
         px = (pgn(m, n, 1) MOD ImageWidth) '+ o
         py = (pgn(m, n, 2) MOD ImageHeight)' + p
       ELSE
         px = (pgn(m, n, 1) MOD ImageWidth) + o
         py = (pgn(m, n, 2) MOD ImageHeight) + p
       END IF
    
       BLoc = py
       IF BLoc > ImageHeight THEN BLoc = ImageHeight
       IF BLoc < 0 THEN BLoc = 0
        
       CLoc = px
       IF CLoc > ImageWidth THEN CLoc = ImageWidth
       IF CLoc < 0 THEN CLoc = 0
       ALoc = (BLoc) * ImageWidth + CLoc
       DEF SEG = SourceSeg
       sc = PEEK(SourceOfs& + CLNG(ALoc))         'check source
       IF Scale3 THEN
         IF ((ABS(SGN(o)) = 1) AND (ABS(SGN(p)) = 1) AND (Scale4 = 0)) THEN
       REM
         ELSE
       IF VGAFlag THEN
         PSET (pgn(m, n, 3) + o, pgn(m, n, 4) + p), sc
       ELSEIF SVGAFlag THEN
         PutPRGB sc
         Pset24 pgn(m, n, 3) + o, pgn(m, n, 4) + p
       END IF
         END IF
       ELSE
         IF (o >= 0) AND (p >= 0) THEN
       IF VGAFlag THEN
         PSET (pgn(m, n, 3) + o, pgn(m, n, 4) + p), sc
       ELSEIF SVGAFlag THEN
         PutPRGB sc
         Pset24 pgn(m, n, 3) + o, pgn(m, n, 4) + p
       END IF
       END IF
         END IF
      NEXT p
      NEXT o
        ELSE
       px = pgn(m, n, 1) MOD ImageWidth
       py = pgn(m, n, 2) MOD ImageHeight
       BLoc = py
       IF BLoc > ImageHeight THEN BLoc = ImageHeight
       IF BLoc < 0 THEN BLoc = 0
       
       CLoc = px
       IF CLoc > ImageWidth THEN CLoc = ImageWidth
       IF CLoc < 0 THEN CLoc = 0
       ALoc = (BLoc) * ImageHeight + CLoc
       DEF SEG = SourceSeg
       sc = PEEK(SourceOfs& + CLNG(ALoc))         'check source
       IF VGAFlag THEN
         PSET (pgn(m, n, 3), pgn(m, n, 4)), sc
       ELSEIF SVGAFlag THEN
         PutPRGB sc
         Pset24 pgn(m, n, 3), pgn(m, n, 4)
       END IF
      END IF
        NEXT n
      NEXT m
        NEXT BigCnt2
      NEXT BigCnt
    
    END SUB
    
     
    '**************** END OF CODE EXAMPLE

    Download a copy of this tutorial: Cheap_Texturemapping.txt


    About Design - Have we Talked too Much About Design?

    Written by Pritchard

    A great thing about having a tool like FreeBASIC around is how capable it is. I think when people begin coding in FreeBASIC, and also when they use FreeBASIC as a stepping stone to other languages like C++ and Java, they dream of achieving much larger goals than ever before. So large, in fact, that you'd have to be crazy to think one person achieve what they want to do in a timely manner. There are few people in the community that are good enough to pull it off, and if you ever talked to them about game design, they'll tell you a few things.

    One of the first things you'll hear is how long the project took. What are considered small or medium-sized games in general are very, very large projects for a one or two man team. Lynn's Legacy took years to complete, and cha0s was shooting for more than he could hit when he began coding it in QB. Syn9's a busy guy, but a capable one as well. If you take a look at projects, they look great. They were built around tight schedules, however. He knows that unless he wants to code something for years, the only way to move forward is to limit project size and scope.

    The other thing I've heard from experienced developers is in fact, design-related. There's different approaches to game development. Generally you're taking one approach or another, whether you know it or not. Bottom-Up has you going lower-level to higher-level. Top-Down has you going from higher-level concepts to lower-level code. I know from the few projects of mine that did kick off quickly, that if you build a library at the lower level thinking you'll need it later on, your project won't go anywhere. You'll have a hell of a time building layers of abstraction over that library to match the concept that's floating around your head. All the same, if you code at the very top level, you'll most-likely be coding something impractical if you're working on a large project.

    Coding a fairly large project becomes difficult when you try and have an advanced design process that's supposed to work out in the end, but you're not quite sure why it will. You always have to have some idea of what you're doing, and always try and code something useful. I'm going to advocate the idea of designing as you code. That is, coding at the highest level of code you can while still making something practical. Don't worry if you don't have the libraries to do this yet. Code something. Code it as if the functions already exist. However, make sure you try and avoid vague concepts and the use of libraries that can't exist because you don't know how they'll work. You want to evaluate all of your technical capabilities and design a piece of code which call functions and libraries you either have already coded, know you can code, or know you can learn how to code quickly. If you can't assure yourself of this capability, you either have to design your code without using your magic library, or drop down a level and design a test which uses it.

    Here's an example piece of "design code", in our first stage:

    '' we're going to fire a block at a target, and the target is going to dodge it.
    dim as Entity ent(0 to 1)
    
    ent(0).isHuman = true
    '' position and size
    ent(0).x = 100
    ent(0).y = 100
    ent(0).w = 30
    ent(0).h = 50
    '''' speed
    '' move n pixels per second
    ent(0).moveSpeed = 400
    '' fire a bullet every n seconds
    ent(0).fireSpeed = .4
    
    '' set up our ai... it does nothing but dodge.
    ent(1).isHuman = false
    ent(1).x = 540
    ent(1).y = 380
    ent(1).w = 30
    ent(1).h = 50
    ent(1).moveSpeed = 200
    ent(1).target = ent(0)
    
    
    '' init and run
    screenres 640, 480, 32
    
    screenlock
    do
    
    	'' loop through our entities and update them
    	for doVar as integer = lBound(ent) to uBound(ent)
    		ent(doVar).getInput()
    		ent(doVar).updatePosition()
    	next
    	
    	'' draw our entities
    	for doVar as integer = lBound(ent) to uBound(ent)
    		ent(doVar).draw()
    	next
    
    	'' flip our screen
    	screenunlock
    		sleep 15, 1
    	screenlock
    	cls
    	clock.flip()
    
    loop until multikey(fb.SC_ESCAPE)
    screenunlock

    This is a piece of designed code. It's a test, which won't compile yet. We hope to use this exact or similar code will compile and become our 'main'. There is no real "design", as you may normally think it. We want to avoid such vague ideals. This is just a program outline.

    After coding our program outline, we do two things. The first is to attempt to make this code compile and actually do something. The second thing is to evaluate the technical challenges we currently face, and how the code can be made better. Sometimes this involves changing your objects and rewriting the test completely to adjust to the new "technologies" available. Remember, coding is an iterative process, as is just about anything else.

    A class design method I often use, when things get too complex, is to code the objects and methods without outlining their internals. Code the methods, use the internals, but don't code the internal private data first then design your methods around it. This is once again us trying to avoid outlining the internal implementation before the functional code. I highly recommend this when objects get too complex.

    Here's the final result of this particular piece of design code:

    #include once "fbgfx.bi"
    #define false (0)
    #define true (not false)
    
    enum KeyInput
    	LEFT   = 01
    	RIGHT  = 02
    	UP     = 04
    	DOWN   = 08
    	FIRE   = 16
    end enum
    
    '' our clock
    type Clock
    	as double cur, last, dif
    	
    	declare sub flip()
    end type
    
    sub clock.flip()
    	last = cur
    	cur = timer
    	
    	dif = (cur - last)
    end sub
    
    
    '' our entity
    type Entity
    	'' dimensions related stuff
    	as double x, y, w, h, moveSpeed
    	'' bullet related stuff
    	as double bX, bY, lastFire, fireSpeed, bulletSpeed
    	
    	'' input and cpu
    	as integer isHuman, inBit
    	
    	'' target and timer
    	as Entity ptr target
    	as Clock ptr clock
    	
    	declare sub getInput()
    	declare sub update()
    	declare sub draw()
    end type
    
    sub Entity.getInput()
    	if( isHuman = false ) then exit sub
    
    	inBit = 0
    	if( multikey(fb.SC_LEFT) ) then
    		inBit or = KeyInput.LEFT
    	end if
    	if( multikey(fb.SC_RIGHT) ) then
    		inBit or = KeyInput.RIGHT
    	end if
    	if( multikey(fb.SC_UP) ) then
    		inBit or = KeyInput.UP
    	end if
    	if( multikey(fb.SC_DOWN) ) then
    		inBit or = KeyInput.DOWN
    	end if
    
    	if( multikey(fb.SC_ENTER) ) then
    		inBit or = KeyInput.FIRE
    	end if
    	
    end sub
    
    sub Entity.update()
    	select case isHuman
    	case true
    		
    		'' move our player
    		if( inBit and KeyInput.LEFT ) then
    			x -= moveSpeed * clock->dif
    		end if
    		if( inBit and KeyInput.RIGHT ) then
    			x += moveSpeed * clock->dif
    		end if
    		if( inBit and KeyInput.UP ) then
    			y -= moveSpeed * clock->dif
    		end if
    		if( inBit and KeyInput.DOWN ) then
    			y += moveSpeed * clock->dif
    		end if
    		
    		'' fire a bullet
    		if( inBit and KeyInput.FIRE ) then
    			if( timer - lastFire > fireSpeed ) then
    				bX = x
    				bY = y
    				lastFire = timer
    			end if
    		end if
    		
    	case false
    	
    		'' dodge a bullet if it's too close
    		if( abs( target->bY - y ) < 100 ) then
    			
    			'' find the fastest direction to dodge at
    			if( target->bY + 19 < y + h / 2 ) then
    				y += moveSpeed * clock->dif
    			else
    				y -= moveSpeed * clock->dif
    			end if
    			
    		end if
    	
    	end select
    	
    	'' move our bullet
    	bX += bulletSpeed * clock->dif
    end sub
    
    sub Entity.draw()
    
    	'' draw us
    	line( x, y ) - step (w - 1, h - 1), rgb(255, 255, 255), bf
    	
    	'' draw our bullet
    	if( isHuman = true ) then
    		circle (bX, bY), 20, rgb(255, 128, 0),,,,f
    	end if
    
    end sub
    
    
    
    '' we're going to fire a block at a target, and the target is going to dodge it.
    dim as Clock myClock
    dim as Entity ent(0 to 1)
    
    ent(0).isHuman = true
    '' position and size
    ent(0).x = 100
    ent(0).y = 100
    ent(0).w = 30
    ent(0).h = 50
    '''' speed
    '' move n pixels per second
    ent(0).moveSpeed = 400
    '' fire a bullet every n seconds
    ent(0).fireSpeed = .4
    ent(0).bulletSpeed = 800
    ent(0).clock = @myClock
    
    '' set up our ai... it does nothing but dodge.
    ent(1).isHuman = false
    ent(1).x = 540
    ent(1).y = 380
    ent(1).w = 30
    ent(1).h = 50
    ent(1).moveSpeed = 200
    ent(1).target = @ent(0)
    ent(1).clock = @myClock
    
    
    '' init and run
    screenres 640, 480, 32
    
    screenlock
    do
    
    	'' loop through our entities and update them
    	for doVar as integer = lBound(ent) to uBound(ent)
    		ent(doVar).getInput()
    		ent(doVar).update()
    	next
    	
    	'' draw our entities
    	for doVar as integer = lBound(ent) to uBound(ent)
    		ent(doVar).draw()
    	next
    	
    	'' print some stuff to the screen
    	locate 1, 1
    	print "Hit enter to fire a bullet."
    
    	'' flip our screen
    	screenunlock
    		sleep 15, 1
    	screenlock
    	cls
    	myClock.flip()
    
    loop until multikey(fb.SC_ESCAPE)
    screenunlock

    Quick and easy. Demonstrates the concept at least. Now to recognize several technical challenges.

    - It would have been much easier to use a 2D Vector class to handle the positions and sizes, as well as help us do some distance math and possibly enhance bullet paths.

    - The inBit seems like a waste, so I'll probably get rid of that for now, but it might be useful to have some abstraction between the internals of how keys are detected and what actions are taken later on - ex: if we want to change the behavior that pressing left takes.

    - Bullet should probably become its own separate object later on as to avoid saying bullets have to be a part of entities, and allow Bullets to become more advanced.

    - Finally, multiple bullets are difficult at the moment. This is truly a technical challenge. I didn't want to code a big thing to manage bullets, when they die, how they die, and maintaining a list of them for such a small example...

    The important thing here is that we actually got something done. Stop thinking so much about design and start thinking about development. I do have a solution to those of you who want to base your design process on a professional model. It's called Research and Development. We're hobbyist game developers. We're usually working alone. Programming is an iterative process and games have a lot of different concepts, each of them needing testing at one point or another. The most important part of a game is the game play, so test it. The basic idea of Research and Development is what keeps large companies alive, and it's what keeps small developers able to write practical code.

    Don't let the capabilities and possibilities of modern computing overwhelm you. Write tests first. Tests of some kind or another. Large companies may write something close to regression tests first, to have an outline as to how an object should work in code. Small developers are going to have game play tests. One test for how bullets move. Another test for how input is gotten and how the characters will move. Collision tests, level generation tests, AI tests, you name it. These can all be separate objects, quickly developed, with the sole purpose of helping you recognize technical challenges so that *later on* you can develop what may eventually become the final release code.

    I've found tremendous success in this way of thinking, and this way of designing. I've never had more fun during development than I am now. Concept Tests may seem like a waste at first, but think about it - Game play's the eventual goal, and right here in the beginning, you already have tests showing you what it will look like. If anything, you should feel like you're cheating the game, not losing it.

    So long,

    Pritchard


    Download a copy of this tutorial: design_in_game_programming_-_pritchard.txt


    Windows Tutorials

    Written by Pritchard

    Hello there. I've been porting over theForger's Windows API tutorial source code for some time. You can find the original tutorials along with source code at that link. It's been quite the pain porting them over, actually, as the later and larger examples range from 10kb to about 20kb in size. Rather than procrastinating for another month, then working for hours only to get a single tutorial ported and tested, I've decided to release what I've got now for all of you to enjoy.

    You can download the current version code here: forgerswin32tutorial.zip.

    Make sure you have a somewhat recent version of FBC to compile the code within. The C files and my partially ported examples are included. I should also note that I put the Windows.bi in a win namespace, as there's been a time or another where I've run into duplicate definitions while porting. In most examples, this can be removed easily if you do not like it.

    And just for good measure, let me post some of the more simple ported examples over for you:

    A Simple Menu:

    http://copy-pasta.com/pasta156476

    A Simple Window:

    http://copy-pasta.com/pasta156477

    A Message Box:

    http://copy-pasta.com/pasta156478

    Click the Window:

    http://copy-pasta.com/pasta156479

    While the code to do such simple things may seem large at first, after coding with Windows for a while, you begin to see just how much of it repeats exactly the same way in every file. Thus, creating a UDT of some sort to manage all of this for you wouldn't be very difficult. Have fun, Pritchard.


    Download the current version: forgerswin32tutorial.zip.


    The Game Loop: Freebasic Version

    Written by Koen Witters; Code ported by Pritchard

    This is a very interesting tutorial on game programming, originally by a guy named Koen Witters. I stumbled across it on Reddit one day, and shared it on the Freebasic.net forums... and then Pritchard went ahead and ported it to Freebasic! This is the full article, word-for-word, just with Freebasic code replacing the original article's C++ code.

    Thanks to Koen Witters for writing the original article, and to Pritchard for porting the code!

    Original Article: The Game Loop

    A follow-up article RE: The Game Loop -- this article makes some clarifications and points out a few flaws in the original game loop tutorial, so it's also worth a read.

    -Pete


    Introduction

    The game loop is the heartbeat of every game, no game can run without it. But unfortunately for every new game programmer, there aren't any good articles on the internet who provide the proper information on this topic. But fear not, because you have just stumbled upon the one and only article that gives the game loop the attention it deserves.

    Thanks to my job as a game programmer, I come into contact with a lot of code for small mobile games. And it always amazes me how many game loop implementations are out there. You might wonder yourself how a simple thing like that can be written in different ways. Well, it can, and I will discuss the pros and cons of the most popular implementations, and give you the (in my opinion) best solution of implementing a game loop.


    The Game Loop

    Every game consists of a sequence of getting user input, updating the game state, handling AI, playing music and sound effects, and displaying the game. This sequence is handled through the game loop. Just like I said in the introduction, the game loop is the heartbeat of every game. In this article I will not go into details on any of the above mentioned tasks, but will concentrate on the game loop alone. That's also why I simplified the tasks to only 2 functions: updating the game and displaying it.

    Here is some example code of the game loop in its most simplest form:

    	#define false 0
    	#define true (not false)
    	
    	dim as integer game_is_running = true
    	
    	while( game_is_running = true )
    		update_game()
    		display_game()
    	wend

    The problem with this simple loop is that it doesn't handle time, the game just runs. On slower hardware the game runs slower, and on faster hardware faster. Back in the old days when the speed of the hardware was known, this wasn't a problem, but nowadays there are so many hardware platforms out there, that we have to implement some sort of time handling. There are many ways to do this, and I'll discuss them in the following sections.

    First, let me explain 2 terms that are used throughout this article:

    FPS

    FPS is an abbreviation for Frames Per Second. In the context of the above implementation, it is the number of times display_game() is called per second.

    Game Speed

    Game Speed is the number of times the game state gets updated per second, or in other words, the number of times update_game() is called per second.


    FPS dependent on Constant Game Speed

    Implementation

    An easy solution to the timing issue is to just let the game run on a steady 25 frames per second. The code then looks like this:

    	#include once "windows.bi"
    	
    	const as integer FRAMES_PER_SECOND = 25
    	const as integer SKIP_TICKS = 1000 / FRAMES_PER_SECOND
    	
    	dim as DWORD next_game_tick = GetTickCount()
    	dim as integer sleep_time = 0
    	
    	dim as bool game_is_running = true
    	
    	while( game_is_running = true )
    	    update_game()
    	    display_game()
    	
    	    next_game_tick += SKIP_TICKS
    	    sleep_time = next_game_tick - GetTickCount()
    	    if( sleep_time >= 0 ) then
    	        Sleep( sleep_time, 1 )
    	    else 
    	        '' Shit, we are running behind!
    	    end if
    	wend
    	
    	''
    	''
    	''
    	'' or, OS independent:
    	''
    	''
    	''
    	#define false 0
    	#define true (not false)
    	
    	const as integer FRAMES_PER_SECOND = 25
    	const as double SKIP_TIME = 1 / FRAMES_PER_SECOND
    	
    	dim as double next_game_tick = timer()
    	dim as integer sleep_time = 0
    	
    	dim as integer game_is_running = true
    	
    	while( game_is_running = true )
    	    update_game()
    	    display_game()
    	        
    	    next_game_tick += SKIP_TIME
    	    sleep_time = (next_game_tick - timer) * 1000
    	    if( sleep_time >= 0 ) then
    	        sleep( sleep_time, 1 )
    	    else 
    	        '' Shit, we are running behind!
    	    end if
    	wend

    This is a solution with one huge benefit: it's simple! Since you know that update_game() gets called 25 times per second, writing your game code is quite straight forward. For example, implementing a replay function in this kind of game loop is easy. If no random values are used in the game, you can just log the input changes of the user and replay them later.

    On your testing hardware you can adapt the FRAMES_PER_SECOND to an ideal value, but what will happen on faster or slower hardware? Well, let's find out.


    Slow hardware

    If the hardware can handle the defined FPS, no problem. But the problems will start when the hardware can't handle it. The game will run slower. In the worst case the game has some heavy chunks where the game will run really slow, and some chunks where it runs normal. The timing becomes variable which can make your game unplayable.


    Fast hardware

    The game will have no problems on fast hardware, but you are wasting so many precious clock cycles. Running a game on 25 or 30 FPS when it could easily do 300 FPS... shame on you! You will lose a lot of visual appeal with this, especially with fast moving objects.

    On the other hand, with mobile devices, this can be seen as a benefit. Not letting the game constantly run at it's edge could save some battery time.


    Conclusion

    Making the FPS dependent on a constant game speed is a solution that is quickly implemented and keeps the game code simple. But there are some problems: Defining a high FPS will pose problems on slower hardware, and defining a low FPS will waste visual appeal on fast hardware.


    Game Speed dependent on Variable FPS

    Implementation

    Another implementation of a game loop is to let it run as fast as possible, and let the FPS dictate the game speed. The game is updated with the time difference of the previous frame.

    	#include once "windows.bi"
    	
    	dim as DWORD prev_frame_tick
    	dim as DWORD curr_frame_tick = GetTickCount()
    	
    	dim as bool game_is_running = true
    	while( game_is_running = true)
    	    prev_frame_tick = curr_frame_tick
    	    curr_frame_tick = GetTickCount()
    	
    	    update_game( curr_frame_tick - prev_frame_tick )
    	    display_game()
    	wend
    	
    	
    	''
    	''
    	''
    	'' or, OS independent:
    	''
    	''
    	''
    	
    	#define false 0
    	#define true (not false)
    	
    	dim as double prev_frame_time
    	dim as double cur_frame_time = timer()
    	
    	dim as integer game_is_running = true
    	while( game_is_running = true )
    		
    		prev_frame_time = cur_frame_time
    		cur_frame_time = timer()
    		
    		update_game( cur_frame_time - prev_frame_time )
    		display_game()
    		
    	wend

    The game code becomes a bit more complicated because we now have to consider the time difference in the update_game() function. But still, it's not that hard.

    At first sight this looks like the ideal solution to our problem. I have seen many smart programmers implement this kind of game loop. Some of them probably wished they would have read this article before they implemented their loop. I will show you in a minute that this loop can have serious problems on both slow and fast (yes, FAST!) hardware.


    Slow Hardware

    Slow hardware can sometimes cause certain delays at some points, where the game gets "heavy". This can definitely occur with a 3D game, where at a certain time too many polygons get shown. This drop in frame rate will affect the input response time, and therefore also the player's reaction time. The updating of the game will also feel the delay and the game state will be updated in big time-chunks. As a result the reaction time of the player, and also that of the AI, will slow down and can make a simple maneuver fail, or even impossible. For example, an obstacle that could be avoided with a normal FPS, can become impossible to avoid with a slow FPS. A more serious problem with slow hardware is that when using physics, your simulation can even explode!


    Fast Hardware

    You are probably wondering how the above game loop can go wrong on fast hardware. Unfortunately, it can, and to show you, let me first explain something about math on a computer.

    The memory space of a float or double value is limited, so some values cannot be represented. For example, 0.1 cannot be represented binary, and therefore is rounded when stored in a double. Let me show you using python:

    >>> 0.1
    0.10000000000000001

    This itself is not dramatic, but the consequences are. Let's say you have a race-car that has a speed of 0.001 units per millisecond. After 10 seconds your race-car will have traveled a distance of 10.0. If you split this calculation up like a game would do, you have the following function using frames per second as input:

    >>> def get_distance( fps ):
    ...     skip_ticks = 1000 / fps
    ...     total_ticks = 0
    ...     distance = 0.0
    ...     speed_per_tick = 0.001
    ...     while total_ticks < 10000:
    ...             distance += speed_per_tick * skip_ticks
    ...             total_ticks += skip_ticks
    ...     return distance

    Now we can calculate the distance at 40 frames per second:

    >>> get_distance( 40 )
    10.000000000000075

    Wait a minute... this is not 10.0??? What happened? Well, because we split up the calculation in 400 additions, a rounding error got big. I wonder what will happen at 100 frames per second...

    >>> get_distance( 100 )
    9.9999999999998312

    What??? The error is even bigger!! Well, because we have more additions at 100 fps, the rounding error has more chance to get big. So the game will differ when running at 40 or 100 frames per second:

    >>> get_distance( 40 ) - get_distance( 100 )
    2.4336088699783431e-13

    You might think that this difference is too small to be seen in the game itself. But the real problem will start when you use this incorrect value to do some more calculations. This way a small error can become big, and fuck up your game at high frame rates. Chances of that happening? Big enough to consider it! I have seen a game that used this kind of game loop, and which indeed gave trouble at high frame rates. After the programmer found out that the problem was hiding in the core of the game, only a lot of code rewriting could fix it.


    Conclusion

    This kind of game loop may seem very good at first sight, but don't be fooled. Both slow and fast hardware can cause serious problems for your game. And besides, the implementation of the game update function is harder than when you use a fixed frame rate, so why use it?


    Constant Game Speed with Maximum FPS

    Implementation

    Our first solution, FPS dependent on Constant Game Speed, has a problem when running on slow hardware. Both the game speed and the framerate will drop in that case. A possible solution for this could be to keep updating the game at that rate, but reduce the rendering framerate. This can be done using following game loop:

    	#include once "windows.bi"
    	
    	const as integer TICKS_PER_SECOND = 50
    	const as integer SKIP_TICKS = 1000 / TICKS_PER_SECOND
    	const as integer MAX_FRAMESKIP = 10
    	    
    	dim as DWORD next_game_tick = GetTickCount()
    	dim as integer loops
    	
    	dim as bool game_is_running = true
    	while( game_is_running = true )
    	
    	    loops = 0
    	    while( (GetTickCount() > next_game_tick) and (loops < MAX_FRAMESKIP) )
    	        update_game()
    	        
    	        next_game_tick += SKIP_TICKS
    	        loops += 1
    	    wend
    	    
    	    display_game()
    	wend
    	
    	
    	''
    	''
    	''
    	'' or, OS independent:
    	''
    	''
    	''
    	
    	#define false 0
    	#define true (not false)
    	
    	const as integer TICKS_PER_SECOND = 50
    	const as double SKIP_TIME = 1 / TICKS_PER_SECOND
    	const as integer MAX_FRAMESKIP = 10
    	    
    	dim as double next_game_tick = timer()
    	dim as integer loops
    	
    	dim as integer game_is_running = true
    	while( game_is_running = true )
    	
    	    loops = 0
    	    while( (timer > next_game_tick) and (loops < MAX_FRAMESKIP) )
    	        update_game()
    	        
    	        next_game_tick += SKIP_TIME
    	        loops += 1
    	    wend
    	    
    	    display_game()
    	wend

    The game will be updated at a steady 50 times per second, and rendering is done as fast as possible. Remark that when rendering is done more than 50 times per second, some subsequent frames will be the same, so actual visual frames will be dispayed at a maximum of 50 frames per second. When running on slow hardware, the framerate can drop until the game update loop will reach MAX_FRAMESKIP. In practice this means that when our render FPS drops below 5 (= FRAMES_PER_SECOND / MAX_FRAMESKIP), the actual game will slow down.


    Slow hardware

    On slow hardware the frames per second will drop, but the game itself will hopefully run at the normal speed. If the hardware still can't handle this, the game itself will run slower and the framerate will not be smooth at all.


    Fast hardware

    The game will have no problems on fast hardware, but like the first solution, you are wasting so many precious clock cycles that can be used for a higher framerate. Finding the balance between a fast update rate and being able to run on slow hardware is crucial.


    Conclusion

    Using a constant game speed with a maximum FPS is a solution that is easy to implement and keeps the game code simple. But there are still some problems: Defining a high FPS can still pose problems on slow hardware (but not as severe as the first solution), and defining a low FPS will waste visual appeal on fast hardware.


    Constant Game Speed independent of Variable FPS

    Implementation

    Would it be possible to improve the above solution even further to run faster on slow hardware, and be visually more atractive on faster hardware? Well, lucky for us, this is possible. The game state itself doesn't need to be updated 60 times per second. Player input, AI and the updating of the game state have enough with 25 frames per second. So let's try to call the update_game() 25 times per second, no more, no less. The rendering, on the other hand, needs to be as fast as the hardware can handle. But a slow frame rate shouldn't interfere with the updating of the game. The way to achieve this is by using the following game loop:

    	#include once "windows.bi"
    	
    	const as integer TICKS_PER_SECOND = 25
    	const as integer SKIP_TICKS = 1000 / TICKS_PER_SECOND
    	const as integer MAX_FRAMESKIP = 5
    	
    	dim as DWORD next_game_tick = GetTickCount()
    	dim as integer loops
    	dim as double interpolation
    	
    	dim as bool game_is_running = true
    	while( game_is_running = true )
    	
    	    loops = 0
    	    while( (GetTickCount() > next_game_tick) and (loops < MAX_FRAMESKIP))
    	    
    	        update_game()
    	
    	        next_game_tick += SKIP_TICKS
    	        loops += 1
    	    wend
    	
    	    interpolation = cast( double, ( GetTickCount() + SKIP_TICKS - next_game_tick ) / cast( double, SKIP_TICKS ) )
    	    display_game( interpolation )
    	wend
    	
    	
    	''
    	''
    	''
    	'' or, OS independent:
    	''
    	''
    	''
    	
    	#define false 0
    	#define true ( not false )
    	
    	const as integer TICKS_PER_SECOND = 25
    	const as double SKIP_TIME = 1 / TICKS_PER_SECOND
    	const as integer MAX_FRAMESKIP = 5
    	
    	dim as double next_game_tick = timer()
    	dim as integer loops
    	dim as double interpolation
    	
    	dim as integer game_is_running = true
    	while( game_is_running = true )
    	
    	    loops = 0
    	    while( (timer() > next_game_tick) and (loops < MAX_FRAMESKIP))
    	    
    	        update_game()
    	
    	        next_game_tick += SKIP_TIME
    	        loops += 1
    	    wend
    	
    	    interpolation = cast( double, ( timer() + SKIP_TIME - next_game_tick ) / cast( double, SKIP_TIME ) )
    	    display_game( interpolation )
    	wend

    With this kind of game loop, the implementation of update_game() will stay easy. But unfortunately, the display_game() function gets more complex. You will have to implement a prediction function that takes the interpolation as argument. But don't worry, this isn't hard, it just takes a bit more work. I'll explain below how this interpolation and prediction works, but first let me show you why it is needed.


    The Need for Interpolation

    The gamestate gets updated 25 times per second, so if you don't use interpolation in your rendering, frames will also be displayed at this speed. Remark that 25 fps isn't as slow as some people think, movies for example run at 24 frames per second. So 25 fps should be enough for a visually pleasing experience, but for fast moving objects, we can still see a improvement when doing more FPS. So what we can do is make fast movements more smooth in between the frames. And this is where interpolation and a prediction function can provide a solution.


    Interpolation and Prediction

    Like I said the game code runs on it's own frames per second, so when you draw/render your frames, it is possible that it's in between 2 gameticks. Let's say you have just updated your gamestate for the 10Th time, and now you are going to render the scene. This render will be in between the 10Th and 11Th game update. So it is possible that the render is at about 10.3. The 'interpolation' value then holds 0.3. Take this example: I have a car that moves every game tick like this:

        position = position + speed;

    If in the 10Th gametick the position is 500, and the speed is 100, then in the 11Th gametick the position will be 600. So where will you place your car when you render it? You could just take the position of the last gametick (in this case 500). But a better way is to predict where the car would be at exact 10.3, and this happens like this:

        view_position = position + (speed * interpolation)

    The car will then be rendered at position 530.

    So basically the interpolation variable contains the value that is in between the previous gametick and the next one (previous = 0.0, next = 1.0). What you have to do then is make a "prediction" function where the car/camera/... would be placed at the render time. You can base this prediction function on the speed of the object, steering or rotation speed. It doesn't need to be complicated because we only use it to smooth things out in between the frames. It is indeed possible that an object gets rendered into another object right before a collision gets detected. But like we have seen before, the game is updated 25 frames per second, and so when this happens, the error is only shown for a fraction of a second, hardly noticeable to the human eye.


    Slow Hardware

    In most cases, update_game() will take far less time than display_game(). In fact, we can assume that even on slow hardware the update_game() function can run 25 times per second. So our game will handle player input and update the game state without much trouble, even if the game will only display 15 frames per second.


    Fast Hardware

    On fast hardware, the game will still run at a constant pace of 25 times per second, but the updating of the screen will be way faster than this. The interpolation/prediction method will create the visual appeal that the game is actually running at a high frame rate. The good thing is that you kind of cheat with your FPS. Because you don't update your game state every frame, only the visualization, your game will have a higher FPS than with the second method I described.


    Conclusion

    Making the game state independent of the FPS seems to be the best implementation for a game loop. However, you will have to implement a prediction function in display_game(), but this isn't that hard to achieve.


    Overall Conclusion

    A game loop has more to it than you think. We've reviewed 4 possible implementations, and it seems that there is one of them which you should definitely avoid, and that's the one where a variable FPS dictates the game speed.

    A constant frame rate can be a good and simple solution for mobile devices, but when you want to get everything the hardware has got, best use a game loop where the FPS is completely independent of the game speed, using a prediction function for high framerates.

    If you don't want to bother with a prediction function, you can work with a maximum frame rate, but finding the right game update rate for both slow and fast hardware can be tricky.

    Now go and start coding that fantastic game you are thinking of!

    Koen Witters

    FreeBASIC version 1.0 by Pritchard


    Download this tutorial: gamelooptutorial.txt


    Final Word

    Like I said in the opening letter, I apologize for any delays that have made your confidence in QB Express waiver. I geniunely try to get the issues out every month, but sometimes life gets in the way. But there is one thing you can count on -- QB Express issues *will* come out!

    In order for issues to come out, we need submissions! Submissions of all types -- tutorials, articles, editorials, comics (which we've been sorely lacking lately), news briefs, and more.

    Deadline for QB Express #29: JUNE 1ST, 2008

    Also, as I mentioned in the opening letter, I am currently recruiting staff members to help Imortis Inglorian and MystikShadows run QB Express when I leave for my 6 month trip this July. If you are interested, shoot us an email and we'll talk!

    All submissions and correspondence can be sent to: qbexpress@gmail.com

    And yes, the next issue will be out in mid-June. Promise.

    Until next time...END.

    -Pete


    Copyright © Pete Berg and contributors, 2008. All rights reserverd.
    This design is based loosely on St.ndard Bea.er by Altherac.

    FreeBasic Top 50