QB Express

Issue #27  ~  March 8, 2008

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

In This Issue



From The Editor's Desk

Written by Pete

No, your eyes don't deceive you... it's another issue of QB Express...ALREADY!

It has been less than a month since the last issue came out, and for the first time since the fall, QB Express is getting released on a regular schedule again!

This month, we've got a collection of fantastic articles and tutorials that pack quite a whallop -- so watch out! This may not be the biggest QB Express issue that's ever come out, but it certainly is no slouch... with SEVEN tutorials and THREE articles, as well as one of the biggest news briefs sections we've had in a long time.

MystikShadows reviews Dabooda's game Sokoban; h4tt3n gives us part two of his Spring Simulation series; Mentat brings us "Going Deep: Football and 3D Graphics"; Hezad teaches us how to generate awesome moving plasmas using the Electric Field equation; Wallyfblu teaches us about Window Procedure; Dean Menezes brings us "CGI and QBasic"; and Kiyote Wolf helps us reinvent the Commodore Wedge. And as always, we've got all the latest news from around the QB and FB community, and much, much more. QB Express #27 truly delivers a knock-out punch!

But I won't keep rambling and leave you down for the count...go ahead and read the issue!


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 pberg1@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 everyone,

No doubt, another great release in a line of great releases. You weren't kidding when you said it was worth the wait. It sure was.

I'm very please to see actual articles about the community, or lack there of. That people are writing about. I gotta say though that it feels somewhat familiar, I think other peole have noticed this but for 2 people to write about it, I think it indicates something here. Although some of the first to notice this weren't exactly respected and revered for stating so, it seems that this issue of a community is getting to be more important than it used to be. Or, at least seen in a very different way. But the fact that today people can write about this subject and not get thrown in some corner somewhere might be a good sign. Time will tell. But it's no surprise that fb.net is not a community forum. It's just pretty sad that those that noticed this earlier than this had to pay a very different price than those writing about it now. The real question is what should be done about it, I gave my suggestion already, it's just that,a suggestion. Be it that suggestion or another, I think this is always going to be a big lack and I don't see it going away and dissipating all that fast (if at all). the FB forums need to be support forums, it's always good to have one place to go to get this kind of atmosphere, but that's just about a quarter of what's needed to make a complete online presence to a project. In my opinion. I'm not sure what happened to na_th_an and some of the others like them. IF a complete community existed, I'm not sure if they'd come back, maybe, but I think the focus should be on not losing any more. na_th_an were some of the more participating community members we were blessed with (literally) so I think that for them to decided to leave indicates a pretty big lack somewhere. The only thing I don't like about Pritchard's article is that it seems to talk in the past tense. IE, it gives the impression of admitting defeat and I don't think we're there yet, but I do think it's time things start to take another direction.

Lachie's article about the community I think is worth the read, there's a lot of points made there as well. I wish I could contact all the people that left just to ask them why they left. Maybe some of them just moved on, maybe some of them found something big was mussing, we won't know until we can just sit down and ask them. But I bet we'd all learn a whole lot from some of their replies. And if na_th_an just happens to hop on to this QB Express release, I'm still waiting for part 8 of his IF series. ;-).

It's always good to see the newsbrief section. More importantly, seeing the newsbriefs, to me, reflects what's really been happening, if I take the time to try to find each of these news items in the forums or such it doesn't look like a lot has been happening. Not the same effect as seeing them all grouped together in a newsbrief section. And from this newsbrief, people have been busy. Many games added to the fbgames direction. where's the fbapps directory, or the fbutility directory? ;-) don't mind me, I'm an app person. hehe. But that's the newsbriefs. Seems people have been busy project wise too if you look at the project section. Lots of games in the making, lots of tools for game designers. I for one want an up from Open on his FB CAD project, it looked great and I wonder if any more work has been done on it sinc ehis last update. Sure fb doesn't seem bad for games, look at everything that's been made, but well, bring me, I sure can't wait to see apps, tools and utilities built with fb. IO bet you fb isn't bad at those either ;-). The gallery is one of the better ones I've seen in a while. Plenty of items in there this time around. to all the authors of these items, great work and I want to see more of that.

I think codemss's "Writing Tutorials" tutorial is awesome. I'm sure there's alot of people that think "hey, I wanna write about this or that" but I have no clue where to start. Well, now they should know. I hope this article will yield many new creations by many new contributors. I think the tutorial is rather short (for that type of article) but it surprisingly covers a lot of ground. So codemss, great job there. I can't wait to see how many people it will help and what will come out of it. Also, codemss's "Worm-style Scrolling Engine" tutorial was very elaborate, well thought out and well written. I think codemss found a new potential career choice ;-). I'm gonna want a autographed copy of the first book that comes out hehe.

You know, as a programmer most of us seem to forget (often enough) just what it's like to get back to basics. IF I was to ask some people here "what is a function? How does it work? I think not many people would think of specifying everything about function that Mentat did in his "Functions and Local Variables" tutorial. It was great to read that article even though I know what functions are. ;-). And well, I think it's a very clear tutorial at that. One that can and will help many newcomers to the world of programming. Great work there.

Kiyote Wolf's "GridMulti:Using String Variables..." article is one of the most intelligent ways I've seen described on how to save and optimize stack memory thus far in QB. Every point made are worth learning and remembering and his samples are pretty easy to follow (which is often rare when the goal is to optimize, you tend to want to shortcut everything which ruins readability to some extend". So I was happy to see how readable the examples are in this article. I wonder what other tricks he has up his sleeve that he'll want to share with us, the readers. ;-).

Ok, show of hands, how many people knew absolutely everything that is mentioned in Moneo's "Art Of Rounding".tutorial? Yeah, I thought so, I don't see any hands. and yeah, I have to say, my hand wasn't up either ;-). When Moneo sets out to explain a concept, well we can all see how thorough he gets. Experience and ability to research a subject thoroughly hold no secret or mystery to Moneo. I often wonder what impact tutorials like this one has on most readers. Sometimes, no matter what people say, rounding using a standard "Round()" function just doesn't cut it. And one of the rounding techniques explained here would probably yield more exptended results. I'd be curious if, in the next QB Express (or in emails to me) people reading his tutorial could let me know what they thought of it (specifically as far as the importance of the information inside the tutorial). It's worth it's weight in gold as far as I'm converened, specifically for business applications, but I bet some games could also benefit from these techniques. So if you all want to, let me know, one way or another, how a tutorial like this one affects and impacts you.

Spring Physics. That to me (probably cause I'm not much into games yet (working hard at it hehe)) was a rather new subject to me. And I have to say that Michael "h4tt3n" Nissen's tutorial on the subject was very informative. IE, I learned a lot from it. I like his way of detailing the subject and everything he covered in that one tutorial. Very clearly written I love to read tutorials like that one. I'm so looking forward to his second installment.

Lachie's "A beginner's guide to fmod" I'm sure will be a popular tutorial. Who doesn't want to put sounds or music into theor game creations? I can't think of anyone. And from the looks of this tutorial, how straightforward it is, and how simple it makes using FMOD seem. I can already see most of the readers start playing with their porjects and adding music and sound effects here and there. OF course FMOD is one of the easier things to use to do just that, but Lachie's tutorial is one of the simplest tutorial I've read on it. So great work there.

All in all, it's another knock out issue of QB Express, just how I like 'em. Full of content, activities, artticles, and tutorials. NOw that there's some more games, perhaps some game reviews would be good to get back into the issues. I miss reading them. But as I said before, it was well worth the wait here. I can't wait to get my hands on the next issue. Awesome work from all the contributors.

MystikShadows
Stephane Richard

I would write a response, but I'm pretty sure that you've covered every single possible topic that I could write about related to the last issue.

Anyway, QB Express is definitely back on track, seeing as it's been less than a month since the last issue came out -- and I promise to keep the issues coming on schedule!

-Pete


Letter from Lachie Dazdarian

Oh, hi.

Anyway, very nice work with the last issue. It provided much more content than I expected it would. I must be honest. I kinda had written off QBE, but without any malicious intent. I sorta lost hope it would pick up again and found comfort in my semi-successful FBGD forum (a FB game dev corner I always dreamed of). But you managed to bring QBE back into picture and kudos for that.

I did not like the few formatting mistakes that happened with the last issue. I'm sorry I'm so anal about that, but I just can't help myself. I hope you'll be compiling the next issue in much less hasty manner (which I know, is not your fault; real life just takes its toll).

Regarding the content in the last issue, I would first like to say that my Where Are We Now? article was quite outdated, and that my opinions about the community changed in the meantime. It was actually written in November of 2007, and I have no idea why the date was removed from the article title (then again, I might have sent the original version like that).

Very nice scoop of the community happenings in the News Briefs. I hope this issue’s News Briefs will be on the same level.

Other stuff?

Hmm...

Excellent contributions by Codemess, this including his article and tutorial.

The Art of Rounding by Moneo was yet another highly educational tutorial that illustrates how little you know about something you think there isn’t much to know about.

BTW, thanks for making FBGD the site of the month.

Sorry for not being able to contribute anything for this issue. For issue #28 I had in mind an article or two about being a game developer and a programmer, and what traits make you a good programmer, but in the same time a bad game developer. Anyway, just some thoughts I’m having lately.

Right.

Until next month, make me a darn game! ;)

-Lachie Dazdarian

I understand your lack of enthusiasm for QB Express, considering all the delays and the downturn in amount of content the last few issues. Real life is a downer sometimes.

It's good to hear that you're not giving up on QBE just yet! I look forward to any articles you write in the future, because we (the readers -- me included) love reading your thought-out, detailed and opinionated articles. (Your tutorials and game reviews are awesome too!)

And thanks once again for helping out with the News Briefs this month. I know you're busy, but every small contribution really helps!

-Pete


Letter From Seb McClouth

Hi Pete,

First and all I’d like to say once again it’s good to have you back with QBE. I’ve missed the mag really the last couple of months.

I know you’re really busy and all so it’s really great you still try to maintain the site, forum and mag.

As I’ve said before, keep doing this!!

Grtz
Seb

Thanks, I appreciate the props! QB Express and Pete's QB Site will be around for years to come, don't you worry. :)

-Pete


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


News Briefs

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

Site News

FreeBasic Games Directory updates with four new games

Lachie Dazdarian added four new games to the FreeBasic Games Directory this month. They are (with ratings):

  • UnAlien - 12/20
  • Panda Hack - 11/20
  • SKB - 13/20
  • Trucs Qui s'Emboitent - 12/20

Lachie also updated the watchlist of FB games that may soon be completed (and thus can be added to the games directory): Plix, Alzir, Chuckie's Egg, Moon Project, Bullet Cloud, HEX Factor, Snake, Two Lords, Link Line, Royal Rumble, Armour Ranger, FreeBASIC Nibbles and Gods and Idols.

News Brief by Pete


Russian Freebasic forum opens

ShenZN announced the opening of a Russian forum for Freebasic programming. There are several posts there already, but I don't speak Russian, so I can't exactly tell you what they're about. The site sure does have a nice flying horse / unicorn logo, though! (Way more epic than the standard Freebasic stallion.)

News Brief by Pete


New Freebasic Sites popping up everywhere

Several new personal FB programming websites have been launched in the past month... Here are a few of them.

News Brief by Pete


Project News

Tommy CactusBoy

A newcomer to the QB scene named ACC has posted screenshots and information about a new QuickBasic platformer called "Tommy CactusBoy," and it looks fantastic. Check out these screenshots:



Here's a bit of info from ACC about the game (no demos have been released yet):

I'm planning three little episodes like early 90ies platform games. The first one is "save the princess" plot (nothing fancy). You go as Tommy and try to find where Lydia (the only girl how seems to care about him) is gone. Tommy already got troubles of his own, since his parent disappeared years ago. He must now work all alone in his "cactus farm" -- other kids in town are making fun out of him and nickname him "CactusBoy" because of that (the village where he lives only got two resources: cactus and chicken feathers).

In this first part, you'll have to survive against Calamidades (some sort of cactus-eating dogs, no way affiliated with Chupacabras :) ), mad crows, lost souls (in this haunted mine)...

If you're wondering, the character himself and his background were made when I was asked to make a few pages of comics --weird weird people--. I had something like three days to do them and I really was in a hurry. So I tied a couple of stories altogether and somehow, it turned out to this. Recently I felt the urge to do something with it...

I look forward to Tommy CactusBoy -- it sounds truly original, and a whole lot of fun!

News Brief by Pete


Vince DeCampo releases Hex Factor

Vince DeCampo has released a few beta versions of a FreeBasic game called "Hex Factor" in the past month. The latest release was Beta .994, and it's already looking great:


You can download the game here: HexFctr.zip. Vince is looking for feedback and beta testing, so if you play the game, be sure to weigh in on it on the FreeBasic Games Directory forum!

News Brief by Pete


Armour Ranger

SSJX has released an early version of a platformer / shooter (a "Turrican / Metroid type game") called Armour Ranger.

The engine demo lets players "wander around a large cave and shoot some things", and features a "Turrican-style stream weapon," destructable blocks, many enemies, and cool little explosions.

The demo is still quite rough, and has many timing issues to be worked out, but the game definitely has potential. There aren't nearly enough platformers / Metroid-style games made in FB, so Armour Ranger is an exciting little project.

Visit SSJX's website to download the demo and find out more.

News Brief by Pete


Panda Hack Roguelike

N3trunn3r released an ASCII-based roguelike game called "Panda Hack," which has a unique (and kind of twisted) premise:

Panda Hack is a Rogue like where the game purpose is to hunt pandas and to make money by selling their meat. Collect money, buy ammo, fight the demons to get darts and finally kill some pandas and sell their meat. Then get to the next zone... to kill even more pandas.


You can download Panda Hack here.

News Brief by Pete


New Stuff from Lachie

Lachie Dazdarian has been busy this past month.

First off, Lachie posted a teaser image of his upcoming game Barren (formerly known as LONG / Legend of Noname General):


If you don't know, LONG (now Barren) is a point and click adventure game I've been developing since 2002, and am determined now to finish it in the next 2 months.

As much as the game is close to completion (like I said, 2 more scenes to add out of some 26 total), it's quite far from it. Because, there are 2 very demanding animations to be drawn and coded (one of them is the end game animation), and the music (which is composed) and sound effects need to be implemented. Don’t forget all those final nitpicks and extra unplanned changes.

Nevertheless, I'm hopeful and finally see the end of this project.

Secondly, Lachie has released an update to his screen-by-screen scrolling engine that he put out last month.


This engine could be very helpful if you're trying to create a simple game and don't want to start from scratch.

Way to go, Lachie!

News Brief by Pete


No Freebasic Racing games? Let's start a competition!

There was an interesting post, started by Vince DeCampo at the Freebasic.net forums about how there have been no finished racing / driving games released for Freebasic thus far. This got people thinking: why haven't there been any racing games for FB?

The discussion soon turned to starting a Freebasic driving game competition, and the king of FB competitions himself, ciw1973, said that he would foot 100 GBP in prizes (though he doesn't have time to organize the competition himself). A competition has not formally started yet, but by the sounds of things, one will begin soon.

News Brief by Pete


Dr_D releases 3D racer demo: 502 Rally

Just as people are talking about how there have been no Freebasic driving games (see the last post), Dr_D steps in to buck the trend!

He recently released a demo of a 3D racing engine, which looks remarkable so far:


The game is in early stages right now, but it is definitely worth a test drive.

News Brief by Pete


Snake and Nibbles!

In the past month, several different versions of the time-honored "snake" game format have been released in Freebasic: "Freebasic Nibbles" by Lithium, and "Snake" by hd_ and "Multiplayer Snake Game Made in 30 minutes" by KristopherWindsor. The premise is that you're an ever-growing snake that has to eat things...without running into any walls, obstacles, or your own body. It was probably one of the first games you played in QBasic...or on your cell phone...and now you can play Freebasic versions!

Lithium's version, "Freebasic Nibbles," can be found here:


And "Snake" by hd_ can be found here.


Finally, KristopherWindsor's 30-minute two-player snake game can be found here.

Gulp. Gulp. Gulp.

News Brief by Pete


Phycowelder's 2D Map Editor

Phycowelder has released an openGL Map Editor, to create overworld maps for your games.


Here is a list of features:

-a random path/river generator /W clean tile AI
-and a random tree planter and dirt patches
-UNWRAP_TILE.EXE makes the main 512x512 BMP into 16x16 bmp tiles for the utility.
-the Template BMP is only a basic design for setting up Chipsets that use my Mapping AI.

I'm going to add a few utilities!
-to shorten the tile requirements for the game/engine
-also to use either separate tile images or a master tile image
-also for FBGFX loading or OPENGL loading!
(i haven't tested it yet going to next, but i suspect that the open GL tiles are reversed on the Y-axis so when i make the map formatting utility i have to switch those, maybe.)
-and a utility to choose loading mode fbgfx or openGL to walk around in!
-Tutorial/help PDF

You can download the demo here: MAP_GEN.zip

News Brief by Pete


Suggestions?

Several FB game programmers are looking for feedback on their upcoming projects. Here are a few of them. Be sure to chime in with your thoughts!

  • Penguins?

    Conexion posted a penguin-related question...

    The racing game I'm programing (Tentatively titled: "Pengo: The Last to First") features a 2.5D racing system with a variety of fun and colorful levels as well as different forms of travel (Karts, boats, etc)

    Now, I have quite a few different design ideas down... but I'm curious... what would you guys rather see?

    A variety of penguins in different styles racing against each other?

    Or

    A variety of different creatures racing? (Turtles, Spiders, Trees, etc..)


    There's a lot more info about this game in this thread.


  • Breakout?

    Tusike is looking for help and suggestions with his new Breakout clone. Help him out!


News Brief by Pete



Have news you'd like featured in QB Express? Email us!


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!


Gods and Idols

Z!re is working hard on a ridiculously ambitious, massively multiplayer online RTS called "Gods and Idols." Though Z!re hasn't released many details about the mechanics / gameplay elements of Gods and Idols, he has released dozens of screenshots that show off different beautiful views of space and celestial objects created with the engine. The screenshots are absolutely beautiful, though they don't reveal much about the game (which leads me to believe that the gameplay elements are in the very infancy of their development).

Below, you'll find many screenshots that Z!re has released, including several QB Express Exclusive shots, as well as a short FAQ Z!re wrote about this game in a Freebasic.net thread.

Enjoy! These screenshots are just the kind of thing you'd want to check out in a section called the "Gallery."





F.A.Q.



Q: What is Gods and Idols?
A: Gods and Idols is an MMORTS built on the FVe2 game engine.
The game is set in space where players take the role of godlike beings, nudging their worshipers in the direction the player wants

Q: What is the game play like?
A: As a godlike being, players are able to order their worshipers to perform research, build structures or amass fleets.
Players can also take a more direct approach, and use their immense powers to destroy entire planets.
Players do not have direct control over individual spaceships or worshipers, instead they'll issue general directives for their worshipers to follow.

Q: Is Gods and Idols Free?
A: Yes, you can play for free without any restrictions.
For players wanting some more fast-paced and aggressive play, you can always sign up for the monthly challenge.



Q: What is the monthly challenge?
A: Once per month all players who are taking part in the challenge play get reset to 0 points, and given a new planet. After one month has passed the player with the highest score will collect the cash prize. There is a small fee for entering the challenge play.

Q: When is it going to be released?
A: The release date for a public playable release is set for March 31st.

Q: Where can I find more information?
FVe2 Engine techdemo: http://www.youtube.com/watch?v=v-vp4Dt1PLc
Official Gods and Idols site: http://www.godsandidols.com


More Screenshots!



Sokoban - Game Review

A review by Stéphane Richard (Mystikshadows)

INTRODUCTION:

On January 18th 2008, dabooda, a rather newcomer to the freebasic forum (at least as far as I've seen) announced his game called Sokoban in This FreeBasic forum thread. This game is the first creation from his DBOS (DaBooda Old School) initiative. And what a first creation it is. It's no wonder it got the comments it did on that thread. QUite a great piece of programming work.

Sokoban is a perfect example of a great old school game. It has many features in it that make it a complete and very playable game. Features like parallax scrolling, pretty neat graphics for the game and an impressive user interface that lives up to the reputation of that kind of game. All in all Sokoban reflects the spirit of old school gaming perfectly.

ABOUT SOKOBAN:

Sokoban is a game where you move boxes around a predetermined area. You move them by pushing them in a selected direction. The main goal is to get out of the current area in order to move to the next. This means that you have to be careful where you move those boxes so that they don't block you from reaching the exits. This can happen rather easily so a little thinking over before moving boxes is probably your best bet to make sure you get out of the area.

The maze like areas start rather simply and work their way up in complexity and in possible situations where you just might lock yourself in the area. Sokoban also has an editor available to create very unique challenges which adds to the playability factor of the game.

SOKOBAN SCREENSHOTS:

The first screenshot below presents the title page of the game. You can't see it here but there's a parallax scrolling background (the stars) and with that, after a few seconds looking at the title screen, things start to scroll up to presents some journal entries that describe the situation.


The second screenshot (below) is that of the game's main menu. As you can see it's very classic of the type of game, and sound effects are there when selecting menu options and such. The sound options allows you to adjust the volume of the music and the sound effects to your liking.


This last screenshot is a sample game play area. As you can see here. It features great graphics in the background and for the play area itself too. I really like the high-tech look the game gives with the space background and high-tech design.


There are plenty of other screens I haven't shown here like the screen that allows you to enter your name, another great one done in the spirit of old school games once again and others. I think dabooda really has a knack for this type of game as far as playability and overall look and feel of the game. Add to that excellent music and sound effects and it's plain to see that this game is a complete game with all the features you'd expect from this type of game.

And now, let's start the actual review of the game. The review is done in my usual style of review using the same gaming parameters I always use in my evaluations and reviews of games.

PLAYABILITY: Score (4/5)

I gave this a 4 out of 5 because of how great the game reflects the spirit of old school gaming. The fonts, the colors, the combination of graphics, look and feel of the game everything fits in perfectly. The game is rather easy to learn (even by trial and error) but for those of you that have to have a manual, well one of those is available too that explains everything you need to know about how to play the game. Also, let's not forget that added to that is music and sound effects that add to the game's playability just because of of the way it sets you in the mood to do things fast. Great selection of both music and sound effects.

OVERALL ENTERTAINEMENT: Score (4/5)

This deserves a 4 our of 5 because of all of the elements included in the game. The combination of good graphics (for the type of game), quick and easy setup, concise but good story line involved, the goal of a typical Sokoban game all contribute to the entertainement value of the game. No screen is overfilled with details making all parts of the game veyr easy to see and very clear to follow. If you add sound and music to the mix you have a very complete playability value that is as complete as it can be.

REPLAY VALUE: Score (3/5)

It seems that this specific implementation of Sokoban is lacking something compared to other implementations of the game. Other Sokoban games offer a choice of tilesets for example. Also Most other Sokoban games have an original level set (some have more than one original). However, since it does have an editor available, it's possible to create some very original challenges in a rather easy way. This helps greatly with the replay value of the game because it adds abilities to create challenges maybe in the goal of an championship competition for example.

TECHNICAL FEATURES: Score (4/5)

The technical level of this implementation is pretty awesome in itself. The game features many old school expected characteristics and features that help it live up to the reputation of a great old school game. Everything is there, graphics of course, but also a parallax scrolling engine, smooth font and text scrolling, sound and music. All combined in a pretty convincing way to give all aspects of a Sokoban game it needs to make it a very successful implementation.

THE FINAL VERDICT: 15/20 (75%)

It's no secret that this game deserves every point given to it in this review. The first reason is how well implemented the game is as far as programming is concerned. The game is pretty stable and works very smoothly even in my PC which isn't the fast thing in the computer industry with a rather poor video card in comparison to what's available out there. It combines all the expected feature of a good classic old school game visually as well as music and sounds too. In other words, it's a complete playable sobkan implementation that is must definitaly worth downloading and giving it a good try (or 12).

There are two places where you can get the game and read about it. The first is dabooda's website itself and the second place is Lachie's FB Game directory Sokoban Entry. This review is my own personal evaluation of the game. If you really want to make up your own mind about the game, the best thing to do is to download it from one of the links provided and try it for yourself. Have fun playing this one, I know I did.

MystikShadows
Stéphane Richard
mystikshadows@gmail.com


QBinux Update

Written by Seb McClouth

Hey all

As some of you might have read in PCOPY #60’s qbinux article, QBinux is still alive. I did however make a big mistake and that is saying I named Novix Filesystem… In these I’d like to apologize for this since it was already named by Z!re.

QBinux has been undergoing several re-writes and the current form pleases me best. Most of the code is now translated from C (original source) to Basic and works. There have been some traps in original C-functions which I’ve managed to port to Basic. But ofcourse I didn’t do this alone and I’ve included the contributors names. Just like Linux I like to aim QBinux at a program by the community for the community, and that is still my intention.

There have been some people who wanted to become alpha and beta testers. Until now I’ve only gotten one review from the alpha-test. Because I’m still working on the beta-release, I’d like to ask people who want to beta-test QBinux to mail me at mcclouth.software@gmail.com and as soon I as have working beta, I can release it to the beta-testers.

Well, the recent alpha-release was based upon Linux kernel 1.0. There were lots of traps like I’ve said before. And I’d like to share one with you: complementing.

I had come up with the following code for it:

FUNCTION Complement (VALUE AS INTEGER)
temp$ = "" 
temp1 = VALUE
temp2 = 0 
mirrortemp$ = "" 
temp3 = 0 
compltemp$ = "" 
mirrorcompltemp$ ="" 
temp4 = 0 

FOR a = 0 TO 20  'I'm assuming for the test we only need 20 bits 
  If temp1 AND 2^a THEN 'as Ted told me to test, I'm testing here if it goes 
      temp$ = "1"+ temp$ 'Why did I assume again this has to be one? 
      temp2 = temp2 + (temp1 AND 2 ^ a) 'to test if temp2 = temp1 then 
     IF temp2 = temp1 THEN EXIT FOr        'we leave the for-next-thing 
ELSE 
     temp$ = "0" + temp$ 
END IF 
NEXT 

'The above code gives us the bits for temp1 

FOR a = 1 to len (temp$) 
mirrortemp$ = MID$(temp$, a, 1) + mirrortemp$  'mirror the bit from temp$ for check 
NEXT 

'from here I'm lost in remarks, but it works. 

FOR a = 1 TO LEN(mirrortemp$) 
IF MID$(mirrortemp$, a, 1) = "1" THEN 
  IF a = 1 THEN 
    temp3 = temp3 + 1 
  ELSE 
    temp3 = temp3 + 2 ^ (a-1) 
  END IF 
END IF 
NEXT 

IF temp3 = temp1 THEN 'compare temp3 and temp1 
FOR a = 1 TO LEN(temp$) 
   IF MID$(temp$, a, 1) = "0" THEN 
      compltemp$ = compltemp$ + "1" 
   ELSEIF MID$(temp$, a, 1) = "1" THEN 
      compltemp$ = compltemp$ + "0" 
   END IF 
NEXT 

FOR A = 1 to LEN(compltemp$) 
mirrorcompltemp$ = mid$(compltemp$, a, 1) + mirrorcompltemp$ 
NEXT 

FOR a = 1 to LEN(mirrorcompltemp$) 
IF MID$(mirrorcompltemp$, a, 1) = "1" THEN 
   IF a = 1 THEN 
      temp4 = temp4 + 1 
   ELSE 
      temp4 = temp4 + 2 ^ (a-1) 
   END IF 
END 
NEXT

Well, I didn’t wrote it alone. Mac, Burger2227, Patz, gave me some idea’s by the examples they provided. The code still wasn’t clean enough. It still isn’t but Burger2227’s code for the bitcalcuation:

SUB Dec2Bin 
COLOR 14: LOCATE 2, 30: PRINT "Decimal to Binary" 
COLOR 10: LOCATE 5, 20: INPUT "Enter a decimal number: ", num& 
DO 
remain = num& MOD 2    'remainder is used for binary result 
num& = num& \ 2           'discard any remainder with integer division 
Bin$ = LTRIM$(STR$(remain)) 'make string number 
Binary$ = Bin$ + Binary$    'add remainder to binary number 
LOOP UNTIL num& = 0 
COLOR 11: LOCATE 10, 30: PRINT "Binary number = "; Binary$  'binary result 

END SUB

Did give me a possibility to make atleast one part more understanding. Linux is afterall a C-based (and yeah ASM too) based operating (like all the others). This is only a little excerpt of QBinux but with the first public release of core version 0.01 I’m releasing the source code as well.

And I hope that people will continue giving feedback and adjusting/adapting the original code just like the real deal, Linux.

I’ll try to keep you guys updated in every QBE coming from now, concerning the progress of QBinux.

And please give me feedback!

Grtz

Seb



Monthly Awards

Written by Pete

Site of the Month

N3trunn3r's Coder Hideout
http://n3trunn3r.110mb.com

Webmaster: N3trunn3r


There have been several new personal FB sites launched in the past month, but the cream of the crop is N3trunn3r's Coder Hideout. Although it is a very small site, and was just launched less than two weeks ago, it provides quite a bit of quality content, and boasts an eyecatching design.

This site features downloads of N3trunn3r's games: Endless Adventures ("Endless Adventures is a about a dude (you) who is going on adventures and then hitting the goblin with his sword... ah, you know."); and his new Roguelike, Panda Hack ("the game's purpose is to hunt pandas and to make money by selling their meat.")

Aside from the games, though, this site has a very useful resource: a FreeBasic Library Archive, where N3trunn3r has compiled a collection of the most useful FB libraries that will help you create your own programs. It's still a small collection, consisting of PNG, Pload and BMP libraries, but it will certainly grow over time. This is something that has been sorely needed in the FB community for some time, and I'm glad N3trunn3r has stepped up to do it!

For its collection of useful Freebasic libraries, original games and great design, the N3trunn3r's Coder Hideout is our Site of the Month!



Programmer of the Month

Dr_D
games.freebasic.net/forum

Dr_D (also known as Dr_Davenstein), has been wowing us with his 3D OpenGL Freebasic demos for years. It seems like every month, he's got a new Kart racing demo, or 3D walkaround demo to show off -- and every single one of them is better than the last.


This past month, Dr_D released a demo of his kart racing game called 502 Rally, that looks incredibly promising. Not only does it run smoothly and quickly in super high resolution, it has beautiful textures and 3D models that will make you drool. The 502 Rally Demo is undoubtedly one of the best looking programs ever released in FreeBasic, and it is definitely worth a few minutes of your time. Just imagine how good it's going to be when the game is finished!

For his amazing work with Freebasic and OpenGL, and his constant contributions to the FB gaming scene, Dr_D is the QB Express Programmer of the Month.




A Basic Comic

Written by Anonymous

This is a series of "text-based comics" about the medieval QB/FB "kingdom", submitted by writer who asked to remain anonymous. A bit odd, but it's an interesting concept.!

-Pete


Topics: Allegory, Satire, and Basic Politics

Setting: Medieval Europe


The Kingdom of QB is subject to hard times, as the majority of its citizens have revolted, and left to form the FreeBASIC army.

The army tries to conquer the QB kingdom to free its remaining citizens, and reclaim the wealth in the castle's treasury.

Meanwhile, a small, nomadic group has joined the QB Kingdom, renaming it the QB64 Kingdom, and placed its leader Galleon in charge of the kingdom.


QB64 Kingdom: a castle and kingdom

FreeBASIC: a strong army of knights on horseback

Other


***** Comic 01 *****

OLPC to Galleon: The FreeBASIC army is coming!

Galleon: I was hoping they wouldn't attack for another year! Doom is inevitable!

V1ctor: Surround the castle! Fire the nuke cannon at the castle's back door! Take no prisoners alive!

LachieD: Aren't you glad this strip is non-partisan?


***** Comic 02 *****

MystikShadows to Galleon: The pizza is --

*trip*

*clunk + smash*

MystikShadows: I think my leg broke!

Galleon to Clippy: Get out of the way, Clippy.


***** Comic 03 *****

OLPC to Galleon: The enemy army has new weapons technology and is coming this way!

Galleon: Don't worry; I am making a tool that will help us rule the earth!

Galleon: It features a cloaking device, time machine, and brain washer.

Galleon: I would have finished it by now, but I took a break to setup my iPod.


***** Comic 04 *****

Mac to Galleon: You know why I really don't like FreeBASIC? I have never seen higher taxes, but they claim they are free.

Galleon: I know, and I will help the people advance without raising taxes. BTW...

Galleon: Tell the executioner I have some more people who won't donate $1M to the music-for-all fund.


***** Comic 05 *****

Sir Mud the Great to his henchmen: I have invented the greatest disease cure ever!

Sir Mud the Great: If fully explored, this has the potential to improve everything!

Sir Mud the Great: I must now reinvent the wheel, and tell everyone about it!


***** Comic 06 *****

Narrator: The Kingdom has gathered an army, and is on battle lines, preparing for war against FreeBASIC.

(Showing bird's eye view of both armies, grouped just outside the castle)

Narrator: Both sides are marked with courage, determination, and patriotism...

Clippy to V1ctor: Your shoelace is untied.


***** Comic 07 *****

Setting: Pizza Parlor

MystikShadows (behind cash register) to customers: Enjoying your pizza? I'd like to take a few minutes to make you reconsider this civil war.

MystikShadows: It has caused unrest, poverty, political comics, war, hate, and separation. Deep down inside, we all want the same thing.

MystikShadows: We can all be united! Join my new army now and promote world peace!


***** Comic 08 *****

OLPC to Galleon: The FreeBASIC army is coming to attack us! We need a defensive strategy!

Galleon: Don't worry; I have a plan to save the entire QB64 Kingdom!

Galleon: We will rename the empire, "_QB64 Kingdom!"

Galleon: Then when they come looking for "QB64 Kingdom," they won't find it!


***** Comic 09 *****

Galleon to OLPC: I have a brilliant plan! We'll prepare our empire for world domination by conquering Mars and building a secret army there!

OLPC: The FreeBASIC army has already conquered Mars, and has a robot guarding it by disintegrating any space craft within its range.

OLPC: Besides, we haven't even invented indoor plumbing yet.


***** Comic 10 *****

Title: Galleon reelection campaign

Galleon to the audience: Thank you all for attending tonight. If reelected, I promise to be a benevolent dictator even more than last year.

Galleon: I promise to give the citizens the freedom they deserve, and even more...

OLPC (in the audience) to Galleon: Think you'll let us humble subjects read the laws this year?



Comics

By Ryan North

Aside from the "text-based" comics you just read (above), I didn't receive any comics this month. So, instead, I'm going to provide you with a few strips from my personal favorite web comic, Dinosaur Comics by Ryan North -- which coincidentally have to do with game programming AND make references to QBasic. And if you're wondering, the basic premise of Dinosaur Comics is that every strip uses the SAME EXACT six panels, and only the text/dialogue is changed. And somehow, every strip is completely different. Hurray!

-Pete





it's time to kick ass and repent the murders i have committed to become king, and i'm all out of repentance

why do i need to learn this stuff? i'm never going to use it in real life! now, back to shakespeare.




Going Deep: Football and 3D Graphics

Written by Mentat

The Once Upon A Time:

Today’s a practice game and you’re a quarterback for your high school football team. Your receivers are all trying to shake off the other team. Yet you have a hard time seeing them because the opposing team is in the way. So you sort out the players and throw the ball to a relatively open player (on your team of course).

After practice you go home, get on your computer, and stare at the screen trying work on your 3d computer game. You’re stumped on how to draw things closer to you over things farther away. Well, at a decent speed at any rate. Do you sort? It seems the only option. Or you could just use OpenGL or something like that. But then I wouldn’t have a plot to tell, and besides, your too stubborn. Sorting 3d objects is time consuming, and is difficult for complex objects. What if one part of the object is in front of another but the other half is behind!?

Since you have yet to bathe, and you stink to the high heavens, you go and take a shower. A good long shower. Both the football game and your own program runs through your mind. The receiver isn’t shown in front of the safety. It’s so intuitive! Yet no matter how hard you try, you can’t seem to perfect your own graphics. It’s so easy for you to sort out distance and ‘draw’ images in the correct order in your mind. Why can’t the computer? Of course you know that the computer draws pixels to the screen, while in reality oodles of photons bounce off the oodles of atoms. Back to sorting. Sort by triangles? Even more work and time to do something so obvious. What about breaking the triangles into smaller parts…uh…no. Hm.

The hot water is running out and you’re becoming quite frantic. Wait a minute…what about breaking the triangles into pixels! And then load the pixels and their corresponding depths into a buffer. By the Greek Gods, it just might work!

The Point

So what’s the point of this tutorial? A method of not drawing stuff in the back over stuff in the front. Basically, I’m going to use a big array to store the depths of individual pixels. So lets say I want to draw a triangle. With a couple nested loops, I can iterate through all points of the triangle and draw each pixel if it’s depth is less than the pixel-depth of the array. For example, I’ll convert point (Vx,Vy,Vz) to a pixel coordinate (Px,Py), then I’ll check Vz to the number at the array location (Px,Py), and if Vz is less than the previous depth, I’ll draw (Px,Py) and then store Vz to the array. The depth array is pretty much like a screen you don’t see, and it holds the depth of each point.

For me, this means a little extra work regarding points. I normally draw 3d triangles with the built in LINE(#,#)-(#,#) command. Now we need to draw each individual pixel. Not to worry, I’ve got the math.

The Foundation

First thing is first. Before I get into the actual math, I’ll have to establish some standards and I’ll have to say this: the code isn’t optimized. Feel free to copy and paste, but I’d rather you read and use your own interpretation, so you have a better understanding. Depth testing can be used in conjunction with other buffers or arrays. And there are different ways to use this, and to draw.

Another thing is that I’ve used the term array and buffer loosely. From now on, by buffer I mean a chunk of memory used by the command ALLOCATE(#). An array is…well an array. For simplicity, I’ll use an array.

And now for the conventions. I’ll use f<name> to indicate a function or a subroutine. fPixel draws the pixel and depth tests. a<name> is an array. aDepth is the depth array. I use i, j, c, and r in FOR loops. FOR i=1 to 7. The suffixes x and y are generally coordinates, the prefix p means pixel, and v means 3d vertex. And pay attention to capitalization. If I write aHat, I mean for an array to be called hat, while I mean Abbot to be a simple variable. But hopefully you’ll see what I mean.

Now for the good stuff. First, let’s set up the screen and buffer. This is in FreeBASIC, by the way.

The Frame

'Depth Demo

‘set up the screen
CLS
SCREEN 18

'resets the depth array
DECLARE SUB ResetDepth

'Declare the variables
DIM SHARED Pi AS SINGLE
DIM SHARED Wdth AS INTEGER
DIM SHARED Height AS INTEGER
DIM SHARED OffsetX AS SINGLE
DIM SHARED OffsetY AS SINGLE
DIM SHARED ScaleX AS SINGLE
DIM SHARED ScaleY AS SINGLE

DIM SHARED MaxDepth AS USHORT

'Loop variables
DIM SHARED r AS SHORT
DIM SHARED c AS SHORT
DIM SHARED i AS SINGLE
DIM SHARED j AS SINGLE

'3.14159265358979323...
Pi = ATN(1) * 4

'screen dimensions
SCREENINFO Wdth, Height

‘set to the same dimensions of the screen
DIM SHARED aDepth(0 TO Wdth - 1, 0 TO Height - 1) AS SINGLE

'middle of the screen
OffsetX = Wdth / 2
OffsetY = Height / 2

'depth scale
ScaleX = Wdth / TAN(90 * 180 / Pi)
ScaleY = Height / TAN(90 * 180 / Pi)

'maximum depth
MaxDepth = 65365

'Load up the depth array, otherwise nothing can be drawn to it.
fResetDepth


SUB fResetDepth
    FOR r = 0 TO Height-1
        FOR c = 0 TO Wdth-1
            aDepth(c, r) = MaxDepth
        NEXT c
    NEXT r
END SUB

The Hypocrisy

If you’ve read my previous tutorial, then you may be wondering why I made so many global variables. This is a demo, and I want it to be small and simple as possible. Actually, I would just put them into a global UDT and make them private. Again, this code isn’t made for an actual game or large program.

Now look at that last ‘paragraph’ in the code. If you’re just as absent minded as I, you may have forgotten to set a ‘back’ to the array. Otherwise the depth for every pixel is 0. And that’s just not good. Also pay attention to aDepth(#,#). I had to make sure it is aligned with each screen pixel.


The Almighty Dot

Onward to making our beautiful pixels. This is a subroutine which will host the depth testing. The wonderful thing about our pixel function is that extra things can be added. Pixels alone may not seem much, but they’ll be the base for drawing lines (we shouldn’t use the built in LINE(#,#)-(#,#) command), and then triangles.

SUB fPixel(Px AS SINGLE, Py AS SINGLE, Vz AS SINGLE)
	IF Px >= 0 AND Px <= Wdth - 1 AND Py >= 0 AND Py <= Height - 1 THEN
		IF Vz <= aDepth(Px, Py) AND Vz>=1 THEN
			PSET (Px, Py)
			aDepth (Px, Py) = Vz
		END IF
	END IF
END SUB

The Conversion

Of course, we’ll need to convert the vertex to a screen pixel before we plug in this sub. I didn’t add anything for color, partially because the FB compiler would black out the screen. Also, there are all kinds of things you can do with colors. You can add fog, blur, fade, etc. If I went in all of that then I wouldn’t be able to finish. With a modified array/buffer, you can add transparency, lighting (I think), and more special effects. There’s a lot of stuff you can do to a pixel.

Now about the this specific sub. First we check to make sure the pixel is on the screen, and therefore on the array. If so, then check for the depth of the pixel, and if so, PSETify it and put in a new value for the array. Simple, right? Just say yes. Finding the pixel on a triangle will be messy. I originally only used a single sub, and I had point-point equations all over the place. Functions can incur an overhead, but they do a good job at simplifying code. I’ve tested the code so, and it does work, albeit slowly.

After this, we need a function to convert a 3d vertex to a screen pixel. I’m assuming you know how to convert 3d vertices to 2d pixels. Otherwise this isn’t the best tutorial for you.

FUNCTION fGetX (Vx AS SINGLE, Vz AS SINGLE) AS SINGLE
	RETURN OffsetX + (Vx – OffsetX) * ScaleX / Vz
END FUNCTION

FUNCTION fGetY (Vy AS SINGLE, Vz AS SINGLE) AS SINGLE
	RETURN OffsetY + (Vy – OffsetY) * ScaleY / Vz
END FUNCTION

The Mathematics

Now for lines. Don’t be fooled; lines aren’t as simple as they look. If the absolute value of the slope is greater than one, then there are more pixels per x than there are per y. So if we drew this type of line by iterating x, it will be spotty and incomplete. But if we iterate with x for a slope of less than 1 then it will fill out quite nicely. So if ABS(Slope)>1 THEN ITERATE Y ELSE ITERATE X. If you don’t know what I’m talking about, you will. Normally, we are taught Y=MX+B, but there’s nothing stopping us from using X=MY+B.

Goody, now for some good ol’ algebra. We have a point (Px1, Py1) and point (Px2, Py2), and we want to find a point on a line made by them, which I’ll call (Px?, Py?). First, let’s get an equation from the first two. As you probably know, M = (Py2 – Py1) / (Px2 – Px1). If you like, you can use an alternate : (Py2 – Py1) / (Px2 – Px1) = -(Py1 – Py2) / (Px2 – Px1) = --(Py1 – Py2) / (Px1 – Px2) = (Py1 – Py2) / (Px1 – Px2) = M. 1-2 or 2-1, it doesn’t matter, just make sure the numerator and denominator match. I prefer the latter. And make sure Px1 <> Px2.

So now we have Y = B + X*(Py1 – Py2) / (Px1 – Px2). Now to solve for B, simple enough. Plug and chug a point. Py1 = B + Px1*(Py1 – Py2) / (Px1 – Px2). Py1 - Px1*(Py1 – Py2) / (Px1 – Px2) = B. Now plug back in to the original equation:

Y = Py1 - Px1*(Py1 – Py2) / (Px1 – Px2) + X*(Py1 – Py2) / (Px1 – Px2).

Interestingly enough, we can factor this.

Y = Py1 + (X – Px1)* (Py1 – Py2) / (Px1 – Px2)

In most algebra books, you often see this equation as: Y-Py1 = (Py1 – Py2) / (Px1 – Px2) (X – Px1) or even Y – Py1 = M * (X- Px1). It’s just convention.

And to solve for X if you iterate with Y:

X = Px1 + (Y – Py1)* (Px1 – Px2) / (Py1 – Py2).

Now why did I make you jump through these math hoops when you can look this up in an algebra book? Because (1) We need to tweak it so the computer can understand it, (2) some people understand better when they know the reasoning and how it works, and (3) I wanted to show how flexible this equation is, because we’re going to bend and twist it to find the depth and coordinates of pixels we’re looking for.


The Line

For the line sub, so you can see what I’m talking about. By the way, if you didn’t know this, but you can put the argument of functions on multiple lines if each line (except the last) ends with a _.

SUB fLine (Vx1 AS SINGLE, Vy1 AS SINGLE, Vz1 AS SINGLE, _
	        Vx2 AS SINGLE, Vy2 AS SINGLE, Vz2 AS SINGLE)
    'Temporary coordinates
    DIM AS SINGLE Vx3
    DIM AS SINGLE Vy3
    DIM AS SINGLE Vz3
    DIM AS SINGLE Px
    DIM AS SINGLE Py

    'if the slope is less than 1, iterate along the x points
    IF ABS(Vx1 - Vx2)  > ABS (Vy1 - Vy2) THEN
        FOR Vx3 = Vx1 TO Vx2 STEP ABS(Vx2-Vx1)/(Vx2-Vx1)
            Vy3 = Vy1 + (Vx3 - Vx1)* (Vy1 - Vy2) / (Vx1 - Vx2)
            'Now do you see the versatility of this equation?
            Vz3 = Vz1 + (Vx3 - Vx1)* (Vz1 - Vz2) / (Vx1 - Vx2)
	
	‘lets get the pixel coordinates
            Px = fGetX(Vx3, Vz3)
            Py = fGetY(Vy3, Vz3)
	‘Now draw it!
            fPixel(Px, Py, Vz3)
        NEXT I
    ELSE
        FOR Vy3 = Vy1 TO Vy2 STEP ABS(Vy2-Vy1)/(Vy2-Vy1)
            Vx3 = Vx1 + (Vy3 - Vy1)* (Vx1 - Vx2) / (Vy1 - Vy2)
            Vz3 = Vz1 + (Vy3 - Vy1)* (Vz1 - Vz2) / (Vy1 - Vy2)

            Px = fGetX(Vx3, Vz3)
            Py = fGetY(Vy3, Vz3)
            fPixel(Px, Py, Vz3)
        NEXT I
    END IF
END SUB

The Humility, and What Came of It

Ugh, I ran it and it was messy when the slope was around 1. But I’m going to use vertical lines for filling in triangles. I don’t know why it’s so messy. It makes you appreciate the existing graphics library. Well, me at least. Remember what I said about blurring? Well, this would be a good reason to use it.


The Valiant Function

Well, I could have put this in earlier but I wanted you to see the Y = Y1 + (X – X1) * (Y1 – Y2) / (X1 – X2). Well, now to make our lives easier (or not);

FUNCTION fPoint (X AS SINGLE, X1 AS SINGLE, X2 AS SINGLE,_
         Y1 AS SINGLE, Y2 AS SINGLE) AS SINGLE
	RETURN Y1 + (X – X1) * (Y1 – Y2) / (X1 – X2)
END FUNCTION

We’re going to use it again. So the first parameter is the accompanying coordinate you want to find. The second and third is the same coordinate type for the known point. The last two are the same type of coordinates for what you’re trying to find. If it’s too confusing (it certainly is tough for me), then use the original equation.


The Three Sided Triangle

Now for some triangles. The actual point of this tutorial. With depth testing, triangles should be draw correctly and are truncated against the screen if they’re too close. Which means no point-of-infinity and no flipped objects ‘behind’ the eye. In theory. Notice how parameters on multiple lines can make the function look relatively neater. I could fit it on two lines, but what’s an extra line more?

SUB fTri (Vx1 AS SINGLE, Vy1 AS SINGLE, Vz1 AS SINGLE,_
	     Vx2 AS SINGLE, Vy2 AS SINGLE, Vz2 AS SINGLE,_
                 Vx3 AS SINGLE, Vy3 AS SINGLE, Vz3 AS SINGLE)

	‘sweeping point
	DIM X AS SINGLE
	DIM Top AS SINGLE
	DIM Bottom AS SINGLE
	DIM TopDepth AS SINGLE
	DIM BottomDepth AS SINGLE
	
	‘set the points such that X1 < X2 < X3
	IF Vx1 > Vx2 THEN
		SWAP Vx1, Vx2
		SWAP Vy1, Vy2
		SWAP Vz1, Vz2
	END IF

	IF Vx2 > Vx3 THEN
		SWAP Vx2, Vx3
		SWAP Vy2, Vy3
		SWAP Vz2, Vz3
	END IF

	IF Vx1 > Vx3 THEN
		SWAP Vx1, Vx3
		SWAP Vy1, Vy3
		SWAP Vz1, Vz3
	END IF

	‘draw left half of the triangle
	IF Vx1<>Vx2 THEN
		FOR X = Vx1 TO Vx2
			Top = fPoint(X, Vx1, Vx2, Vy1, Vy2)
			Bottom = fPoint(X, Vx1, Vx3, Vy1, Vy3)
			TopDepth = fPoint(X, Vx1, Vx2, Vz1, Vz2)
			BottomDepth = fPoint(X, Vx1, Vx3, Vz1, Vz3)
			fLine(X,Top,TopDepth, X, Bottom, BottomDepth)
NEXT X
	END IF
	
	‘draw right half of the triangle
	IF Vx2<>Vx3 THEN
		FOR X = Vx2 TO Vx3
			Top = fPoint(X, Vx2, Vx3, Vy2, Vy3)
			Bottom = fPoint(X, Vx1, Vx3, Vy1, Vy3)
			TopDepth = fPoint(X, Vx2, Vx3, Vz2, Vz3)
			BottomDepth = fPoint(X, Vx1, Vx3, Vz1, Vz3)
			fLine(X,Top,TopDepth, X, Bottom, BottomDepth)
NEXT X
	END IF

END SUB

The the End

If you’ve tested the code (I’ve been writing and adjusting the whole demo as I write), you’ll notice that the triangles can get quite hollow (though at farther distances they become solid). Nevertheless, I’ve tested it and the pixels that do manage to get drawn work quite well, and that is the point of this tutorial. I did have some code that solved the problem, but it was messier and had its own problems. I had two intersecting triangles and it gave me two crossing triangles, which is good enough. So you, the football player, can now get back to your program.

If you have any questions (or comments) regarding the information I covered, then feel free to ask me at milkmaroo<delThis>@suddenlink.net . Just remember to delete <delThis>; I put it there so I don’t have Spam bots finding me. Chances are that there are errors in this tutorial and in the code (though I did test it). As for my own sources, I used a variant of RelSoft’s way to fill in triangles with two points (originally I anchored a point and swept another, a horrible technique). The idea of using an array for pixels came upon me as I took a shower, though I don’t play on a football team. I’m definitely not the first to figure it out. Thanks to Pete for hosting this tutorial in the QB Express. And of course, my knowledge of FB and QB due to each respective community. Plus whomever I didn’t name.


Post Script: Of Things to Come

I’m hoping for the next tutorial to be based on more rendering. If you have some suggestions on what you want to be illustrated in rendering, then you may ask me via e-mail. Just please keep in mind that I’m still new to 3d rendering.


Download a copy of this tutorial, including source code: Depth_tutorial.zip
Download the source code individually: DEPTH.BAS and Depth_test.bas


Spring Simulation Tutorial, Part 2

Written by Michael Nissen (h4tt3n)

1. INTRODUCTION

Hello and welcome to part 2 of my little spring tutorial!

In part 1 we went through all the underlying physics that we need to know about in order to create a realistic spring simulation, and we looked at a simple one dimensional code example.

In this next part we will go 2D and look into the mysteries of damping, which we need in order to make our springs behave in a more realistic way. In order to do all this we first need to know a little bit about vectors...


2. VECTORS

When you experimented with the code example from part 1 of this tutorial, you might have noticed how the force, acceleration and velocity values could be either positive or negative as the spring bounced back and forth. Surely there has to be something wrong here, because you can't drive at -20 mph! This is because these values arent "regular" numbers but in fact one dimensional vectors.

So, what's a vector? you might ask. Put very shortly, a vector is like a "regular" number, except it has both a value and a directon. A normal number, which we also call a scalar, only has a value. To stay with the driving analogy, the speed of a car is a scalar. If we want to say something about both the speed and direction of the car we can use a vector. Vectors are commonly depicted as arrows, like this:


In 2D space a vector has a tail, usually at the coordianates (0, 0), and a head at the coordiates (x, y). The x and y values can be any negative or positive number, and they're called the horizontal component (x axis) and vertical component (y axis). In addition, any vector has a total length, also called the magnitude, which is defined by the distance between head and tail.

Thats a lot of things to have in mind, but all we need to do in order to use vectors in our programs is to store two numbers: the vector's length along the x and y axis, which define the position of the vector's head. Per definition the position of the tail is always (0, 0), so we don't need to store that. We don't always need to know the magnitude of the vector, so we don't calculate or store that either.

Now that we've been around the basics, let's take a look at how to use vectors in a program:


3. GOING FROM 1D TO 2D...

Once you understand the concept of vectors, going from 1D to 2D (or any dimension) is almost trivial. All that we do is use vectors instead of scalars when we calculate and store the vaules of force, acceleration, velocity and position.

Let's look at an example. This is a simple stripped-down simulation of masses and springs chained together, forming a rope-like structure:

'******************************************************************************'
'
'   Michael "h4tt3n" Nissen's FreeBasic mass-spring-damper tutorial, feb 2008
'
'   Example # 2.1
'
'   Simple two dimensional spring simulation. This program simulates an elastic 
'   rope made of serially connected springs and masses. (Press esc to quit)
'
'******************************************************************************'

''  set constants. experiment with these and see how the simulation reacts
Const Pi                =     4*Atn(1)          ''  pi (better not change ^^)
Const Timestep          =     0.01              ''  timestep
Const Num_Masses        =     5              -1 ''  number of masses in rope
Const Point_Mass        =     10                ''  mass of each point mass
Const Point_Density     =     0.01              ''  density of each point mass
Const Rope_Length       =     350               ''  rope length
Const Spring_Stiffnes   =     6000              ''  spring stiffnes
Const Grav_Acc          =     800               ''  gravitational acceleration
Const Rest_Length = Rope_Length/Num_Masses      ''  rest length of each spring

''  define types
''  two dimensional vector type
Type Vector_Type
  As Single X, Y
End Type

''  mass type including force-, acceleration-, velocity-, and position vectors
Type Mass_Type
  As Single Mass, Density, Radius
  As Vector_Type Frc, Acc, Vel, Pos
End Type 

''  dimension variables
Dim As Vector_Type Length, Normalised_Length, Vel
Dim As Mass_Type Mass(Num_Masses)
Dim As Single Force, Spring_Length
Dim As Integer i, i2, Scrn_Wid, Scrn_Hgt

''  set screen width, height, and bit depth
Scrn_Wid = 800
Scrn_Hgt = 600
ScreenRes Scrn_Wid, Scrn_hgt, 16

''  set startup conditions of masses
For i = 0 To Num_Masses
  
  With Mass(i)
    
    .Mass = Point_Mass
    .Density = Point_Density
    .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
    .Pos.x = (Scrn_Wid\2)+(i*Rest_Length)
    .pos.y = 100
    
  End With
  
Next

''  main program loop
Do
  
  ''  calculate the spring forces acting upon each point mass in the rope
  For i = 0 To Num_Masses-1
    
    i2 = i+1
    
    ''  spring length (Pythagoras' theorem)
    ''  (we need this to find the magnitude of the spring force)
    Length.X = Mass(i2).Pos.X-Mass(i).Pos.X
    Length.Y = Mass(i2).Pos.Y-Mass(i).Pos.Y
    Spring_Length = Sqr(Length.X*Length.X+Length.Y*Length.Y)
    
    ''  normalise the spring length vector
    ''  (same as finding sine and cosine to the angle between masses)
    ''  (we need this to find the direction of the spring force)
    Normalised_Length.X = Length.X/Spring_Length
    Normalised_Length.Y = Length.Y/Spring_Length
    
    ''  spring force (Hooke's law of elasticity)
    Force = -Spring_Stiffnes*(Spring_Length-Rest_Length)
    
    ''  split spring force into horizontal and vertical vector component
    ''  (each action has an equal opposite reaction, hence the - and + sign)
    Mass(i).Frc.X -= Force*Normalised_Length.X
    Mass(i).Frc.Y -= Force*Normalised_Length.Y
    
    Mass(i2).Frc.X += Force*Normalised_Length.X
    Mass(i2).Frc.Y += Force*Normalised_Length.Y
    
  Next
  
  ''  update acceleration, velocity, and position of point masses
  ''  (except for mass 0, which we'd like to keep fixed)
  For i = 1 To Num_Masses
    
    With Mass(i)
      
      ''  accelerate masses:
      ''  acceleration = force / mass
      .Acc.X = .Frc.X/.mass
      .Acc.Y = .Frc.Y/.mass
      
      ''  add gravity (downwards acceleration)
      .Acc.Y += Grav_Acc
      
      ''  update velocity:
      ''  delta velocity = acceleration * delta time
      ''  new velocity = old velocity + delta velocity
      .Vel.X += .Acc.X*Timestep
      .Vel.Y += .Acc.Y*Timestep
      
      ''  update position:
      ''  delta position = velocity * delta time
      ''  new position = old position + delta position
      .Pos.X += .Vel.X*Timestep
      .Pos.Y += .Vel.Y*Timestep
      
      ''  reset force vector
      .frc.x = 0
      .frc.y = 0
      
    End With
    
  Next
  
  ''  display the rope in graphics
  ScreenLock
    
    ''  clear screen
    Cls
    
    ''  draw springs
    For i = 0 To Num_Masses-1
      i2 = i+1
      Line (Mass(i).Pos.X, Mass(i).Pos.Y)-(Mass(i2).Pos.X, Mass(i2).Pos.Y), Rgb(64, 255, 64)
    Next i
    
    ''  draw masses
    For i = 0 To Num_Masses
      With Mass(i)
        Circle(.Pos.X, .Pos.Y), .Radius, Rgb(255, 64, 64),,, 1, f
      End With
    Next i
    
  ScreenUnlock
  
  Sleep 1, 1
  
Loop Until Multikey(1)

End

Essentially this code works exactly like the the example in part 1 of the tutorial, but the 2D environment presents some new obstacles, that we need to overcome:

Let's start out with an easy one. In order to calculate spring forces we need to know the distance between the two endpoint masses of the spring, which effectively is the same as the spring's length. In order to do that we use Pythagoras' theorem, which in short form states that for any right triangle the square on the hypotenuse is equal to the sum of the squares on the other two sides:


This also means that the hypotenuse - or the spring's length - is equal to the square root of the sum of the two other sides squared. This is how it might look in code:

Lng.X = Mass(2).Pos.X-Mass(1).Pos.X
Lng.Y = Mass(2).Pos.Y-Mass(1).Pos.Y
Spring_Length = Sqr(Lng.X*Lng.X+Lng.Y*Lng.Y)

Now lets move on to a slightly harder obstacle. We only keep track of the forces acting upon each mass along the x and y axis, but the springs may be rotated in any non-vertical or non-horizontal angle. This means that the endpoint masses of the springs may be influeced by forces acting on them in any possible angle.

How do we split these angled forces up into vertical and horizontal force components, which the simulation can handle?

Once again we need to look at vectors and vector math, specifically on how to normalise a vector. Normalising a vector means changing its magnitude to 1 without changing its direction. When a vector has been normalised it is called a unit vector or "hat" vector, because it is written like this... Â, with a little hat on top. Normalising a vector is done by dividing all of its components by its magnitude. For 2d vector A it looks like this:

 = A/|A|

or if we look at each vector component separately

Â.x = A.x/|A|
Â.y = A.y/|A|

where x and y are the vector components, A is the vector, Â is the normalised vector, and |A| is the magnitude of the vector. In code form it might look something like this:

Vector_magnitude = SQR(Length.X^2+Length.Y^2)
Normalized_Vector.X = Length.X/Vector_magnitude
Normalized_Vector.Y = Length.Y/Vector_magnitude

A normalised vector is so to speak stripped of any information about magnitude, it only tells us direction. Now, since the force applied by a spring is always along the spring axis - either away from or towards the spring's centre - all we need to do is normalise the spring (as if it were a vector) in order to get the force direction along the x and y axis. Thats what happens in this part of the code example:

Length.X = Mass(i2).Pos.X-Mass(i).Pos.X
Length.Y = Mass(i2).Pos.Y-Mass(i).Pos.Y
Spring_Length = SQR(Length.X^2+Length.Y^2)

Normalised_Length.X = Length.X/Spring_Length
Normalised_Length.Y = Length.Y/Spring_Length

Then we multiply the spring force with the normalized x and y vector components to get the magnitude of the force vector along the x and y axis:

Mass(i).Frc.X -= Force*Normalised_Length.X
Mass(i).Frc.Y -= Force*Normalised_Length.Y

Mass(i2).Frc.X += Force*Normalised_Length.X
Mass(i2).Frc.Y += Force*Normalised_Length.Y

Remember Newton's 3rd law from part 1? It says that for each action - or force - there is an opposite and equal reaction. This is why it says -= for one mass and += for the other.

Ok, now we got that one solved too. Before moving on to the final part, here's another code example showing a somewhat different implementation of spring physics. In this case the example simulates a number of elastic balls, which jump around and interact. Spring forces are only applied when the balls touch, making them bounce away from each other.

'******************************************************************************'
'
'   Michael "h4tt3n" Nissen's FreeBasic mass-spring-damper tutorial, feb 2008
'
'   Example # 2.2
'
'   Simple two dimensional spring simulation. This program simulates a number
'   of elasic balls which jump around and interact. (Press esc to quit.)
'
'******************************************************************************'

''  set constants. experiment with these and see how the simulation reacts.
Const Pi                =     4*Atn(1)      ''  pi (better not change ^^)
Const Timestep          =     0.01          ''  timestep (delta time)
Const Num_Balls         =     12         -1 ''  number of elastic balls
Const Mass_Min          =     400           ''  smallest elastic ball mass
Const Mass_Max          =     8000         ''  biggest elastic ball mass
Const Density           =     0.01          ''  elastic ball density 
Const Spring_Stiffnes   =     5e5           ''  elastic stiffnes

''  define types
Type Vector_Type
  As Single X, Y
End Type

Type Ball_Type
  As Uinteger Col
  As Single Mass, Density, Radius
  As Vector_Type Frc, Acc, Vel, Pos
End Type 

''  dimension variables
Dim As Vector_Type Dst, Vel
Dim As Ball_Type Ball(Num_Balls)
Dim As Single Force, Ball_Distance, Cos_Angle, Sin_Angle, Dist_Min
Dim As Integer i, i2, Scrn_Wid, Scrn_Hgt

''  set screen width, height, and bit depth
Scrn_Wid = 800
Scrn_Hgt = 600
ScreenRes Scrn_Wid, Scrn_hgt, 16

''  use timer to generate random numbers
Randomize Timer

''  set startup conditions of elastic balls
For i = Lbound(Ball) To Ubound(Ball)
  
  With Ball(i)
    
    .Col = RGB(64+Rnd*192, 64+Rnd*192, 64+Rnd*192)
    .Mass = Mass_Min+(i/Ubound(Ball))*(Mass_Max-Mass_Min)
    .Density = Density
    .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
    .Pos.x = .Radius+Rnd*(Scrn_Wid-.Radius)
    .pos.y = .Radius+Rnd*(scrn_hgt-.Radius)
    .vel.x = (Rnd-Rnd)*200
    .vel.y = (Rnd-Rnd)*200
    
  End With
  
Next

''  main program loop
Do
  
  ''  test all elastic balls against each other. calculate forces if they touch.
  For i = Lbound(Ball) To Ubound(Ball)-1
    
    For i2 = i+1 To Ubound(Ball)
      
      ''  distance between elastic balls (Pythagoras' theorem)
      Dst.X = Ball(i2).Pos.X-Ball(i).Pos.X
      Dst.Y = Ball(i2).Pos.Y-Ball(i).Pos.Y
      Ball_Distance = Sqr(Dst.X*Dst.X+Dst.Y*Dst.Y)
      Dist_Min = Ball(i).Radius+Ball(i2).Radius
      
      If Ball_Distance < Dist_Min Then
        
        ''  cosine and sine to the angle between Ball i and i2 (trigonometry)
        Cos_Angle = Dst.X/Ball_Distance
        Sin_Angle = Dst.Y/Ball_Distance
        
        ''  spring force (Hooke's law of elasticity)
        Force = -Spring_Stiffnes*(Ball_Distance-Dist_Min)
        
        ''  split spring force vector into horizontal and vertical component
        Ball(i).Frc.X -= Force*Cos_Angle
        Ball(i).Frc.Y -= Force*Sin_Angle
        Ball(i2).Frc.X += Force*Cos_Angle
        Ball(i2).Frc.Y += Force*Sin_Angle
        
      End If
      
    Next
    
  Next
  
  ''  update acceleration, velocity, and position of elastic balls
  ''  (using the Euler-Cromer 1st order integration algorithm)
  For i = Lbound(Ball) To Ubound(Ball)
    
    With Ball(i)
      
      ''  accelerate balls (acceleration = force / mass)
      .Acc.X = .Frc.X/.Mass
      .Acc.Y = .Frc.Y/.Mass
      
      ''  reset force vector
      .frc.x = 0
      .frc.y = 0
      
      ''  update velocity
      ''  delta velocity = acceleration * delta time
      ''  new velocity = old velocity + delta velocity
      .Vel.X += .Acc.X*Timestep
      .Vel.Y += .Acc.Y*Timestep
      
      ''  update position 
      ''  delta position = velocity * delta time
      ''  new position = old position + delta position
      .Pos.X += .Vel.X*Timestep
      .Pos.Y += .Vel.Y*Timestep
      
    End With
    
  Next
  
  ''  keep elastic balls within screen boundaries
  For i = Lbound(Ball) To Ubound(Ball)
    
    With Ball(i)
      
      ''  right
      If .Pos.x > Scrn_Wid-.Radius Then
        .Vel.X = -.vel.x
        .pos.x = Scrn_Wid-.Radius
      End If
      ''  left
      If .Pos.x < .Radius Then
        .Vel.X = -.vel.x
        .pos.x = .Radius
      End If
      ''  bottom
      If .Pos.Y > Scrn_Hgt-.Radius Then
        .vel.y = -.vel.y
        .pos.y = Scrn_Hgt-.Radius
      End If
      ''  top
      If .Pos.Y < .Radius Then
        .vel.y = -.vel.y
        .pos.y = .Radius
      End If
      
    End With
    
  Next
  
  ''  display elastic balls in graphics
  ScreenLock
    
    ''  clear screen
    Cls
    
    ''  draw elastic balls
    For i = Lbound(Ball) To Ubound(Ball)
      With Ball(i)
        Circle(.Pos.X, .Pos.Y), .Radius    , .Col,,, 1
        Circle(.Pos.X, .Pos.Y), .Radius-0.5, .Col,,, 1
        Circle(.Pos.X, .Pos.Y), .Radius-1.0, .Col,,, 1
      End With
    Next i
    
  ScreenUnlock
  
  Sleep 1, 1
  
Loop Until Multikey(1)

End

4. DAMPING

In the beginning of part 1 of this tutorial I mentioned that the correct technical term is mass-spring-damper system. We've been around masses and springs, but what about dampers?

In the previous code examples you've probably noticed how the springs and rubberballs keep on bouncing - apparently forever - once they've been prodded into movement. We don't see this behaviour in real life, and damping is simply a way of reducing this effect in a somewhat physically plausible way.

Damping a simulated spring works a little like the damper inside a car spring. Without damping the car would bounce up and down for quite a while every time it hit a bump on the road. On the other hand a car with oversized, hard dampers would be very rigid, because they counter the effect of the spring. If you get springs and dampers adjusted just right, you've got a spring that serves your need and at the same time stops bouncing very quickly - like a real world spring.

So, how do we simulate spring damping? Again we need to look at some vector math. First we need to look at the so called dot product of two vectors. Vectors are different from scalars in the way that they can be multiplied by each other in several different ways. The one we are interested in is called the dot product or scalar product, because the result is expressed as a "regular" scalar number.

Let's look at scalar s and 2d vectors A and B. This is how for example "s equals A dot B" is written on paper:

s = A . B (except the dot is a bit higher up)

And this is what it means:

s = A.x * B.x + A.y * B.y

Or in plane words... the product of the horizontal components is added to the product of the vertical components. The useful thing about the dot product is that

A . B = |A| * |B| * cos(v)

where A and B are 2d vectors, |A| and |B| is the magnitude of A and B, and v is the angle between the two vectors.

Next we need to look at how to do a so called scalar projection of one vector onto another vector, as shown in this illustration:


Basically what we do is find out how long a line we would get if we flattened vector A out on vector B, or if we "cast the shadow" of A at a right angle onto B. The scalar projection of A onto B is equal to

|A| * cos(v)

where |A| is the magnitude of A, and v is the angle between vector A and B.

Since we already know that

A . B = |A| * |B| * cos(v)

then we can see that

A . B / |B| = |A| * cos(v)

Or in other words that the scalar projection of A onto B is equal to A dot B divided by the magnitude of B. Now look at this image where we find the scalar projection of the velocity vector on the spring vector:


Notice the arrow called damping force? It is simply the negative value of the scalar projection of the velocity vector onto the spring. And it is the heart of our damping algorithm! This force has two very useful qualities:

-It always has the right direction. It always pushes the endpoint mass in the opposite direction of its velocity, thus slowing it down.

-It always has the right size. If the velocity vector is perpendicular to the spring, then the scalar projection and therefore the damping force is very small. If the velocity vector is almost parallel to the spring, then the scalar projection and damping force is big.

The damping force - which is a scalar - is subtracted from the spring force before this is split up into a horizontal and vertical component, so it will always work along the spring axis and never dampen the "sideways" or perpendicular movement of the spring masses.

Voila! And it actualy works. Take a look at this code sample. It is exactly like the first one, except damping has been added:

'******************************************************************************'
'
'   Michael "h4tt3n" Nissen's FreeBasic mass-spring-damper tutorial, feb 2008
'
'   Example # 2.3
'
'   Simple two dimensional spring simulation. This program simulates an 
'   elastic rope made of serially connected springs and point masses.
'   Damping is added, which makes the rope look more realistic. 
'   (Press esc to quit)
'
'******************************************************************************'

''  set constants. experiment with these and see how the simulation reacts
Const Pi                =     4*Atn(1)          ''  pi (better not change ^^)
Const Timestep          =     0.01              ''  timestep
Const Num_Masses        =     5             -1 ''  number of masses in rope
Const Point_Mass        =     10                 ''  mass of each point mass
Const Point_Density     =     0.01              ''  density of each point mass
Const Rope_Length       =     350               ''  rope length
Const Spring_Stiffnes   =     6000             ''  spring stiffnes
Const Spring_Damping    =     2e2              ''  spring damping
Const Grav_Acc          =     800              ''  gravitational acceleration
Const Rest_Length = Rope_Length/Num_Masses      ''  rest length of each spring

''  define types
Type Vector_Type
  As Single X, Y
End Type

Type Mass_Type
  As Single Mass, Density, Radius
  As Vector_Type Frc, Acc, Vel, Pos
End Type 

''  dimension variables
Dim As Vector_Type Lng, Norm_Lng, Vel
Dim As Mass_Type Mass(Num_Masses)
Dim As Single Force, Spring_Length
Dim As Integer i, i2, Scrn_Wid, Scrn_Hgt

''  set screen width, height, and bit depth
Scrn_Wid = 800
Scrn_Hgt = 600
ScreenRes Scrn_Wid, Scrn_hgt, 16

''  set startup conditions of masses
For i = Lbound(Mass) To Ubound(Mass)
  With Mass(i)
    .Mass = Point_Mass
    .Density = Point_Density
    .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
    .Pos.x = (Scrn_Wid\2)+(i*Rest_Length)
    .pos.y = 100
  End With
Next

''  main program loop
Do
  
  ''  calculate the forces acting upon each point mass in the rope
  For i = Lbound(Mass) To Ubound(Mass)-1
    
    i2 = i+1
    
    ''  spring length (Pythagoras' theorem)
    Lng.X = Mass(i2).Pos.X-Mass(i).Pos.X
    Lng.Y = Mass(i2).Pos.Y-Mass(i).Pos.Y
    Spring_Length = Sqr(Lng.X*Lng.X+Lng.Y*Lng.Y)
    
    ''  normalise the distance vector 
    ''  (same as finding cosine and sine to the angle)
    Norm_Lng.X = Lng.X/Spring_Length
    Norm_Lng.Y = Lng.Y/Spring_Length
    
    ''  relative velocity (needed for damping, see below)
    Vel.X = Mass(i2).Vel.X-Mass(i).Vel.X
    Vel.Y = Mass(i2).Vel.Y-Mass(i).Vel.Y
    
    ''  spring force (Hooke's law of elasticity)
    Force = -Spring_Stiffnes*(Spring_Length-Rest_Length)
    
    ''  spring damping 
    ''  (scalar projection of velocity vector onto spring)
    Force -= Spring_Damping*((Lng.X*Vel.X+Lng.Y*Vel.Y)/Spring_Length)
    
    ''  split spring force vector into horizontal and vertical component
    ''  (each mass moves in opposite directions, hence the - and + sign)
    Mass(i).Frc.X -= Force*Norm_Lng.X
    Mass(i).Frc.Y -= Force*Norm_Lng.Y
    Mass(i2).Frc.X += Force*Norm_Lng.X
    Mass(i2).Frc.Y += Force*Norm_Lng.Y
    
  Next
  
  ''  update acceleration, velocity, and position of point masses
  ''  (using the Euler-Cromer 1st order integration algorithm)
  For i = Lbound(Mass)+1 To Ubound(Mass)
    
    With Mass(i)
      
      ''  accelerate masses (acceleration = force / mass)
      .Acc.X = .Frc.X/.mass
      .Acc.Y = .Frc.Y/.mass
      
      ''  add gravity (downwards acceleration)
      .Acc.Y += Grav_Acc
      
      ''  update velocity
      ''  delta velocity = acceleration * delta time
      ''  new velocity = old velocity + delta velocity
      .Vel.X += .Acc.X*Timestep
      .Vel.Y += .Acc.Y*Timestep
      
      ''  update position 
      ''  delta position = velocity * delta time
      ''  new position = old position + delta position
      .Pos.X += .Vel.X*Timestep
      .Pos.Y += .Vel.Y*Timestep
      
      ''  reset force vector
      .frc.x = 0
      .frc.y = 0
      
    End With
    
  Next
  
  ''  display the rope in graphics
  ScreenLock
    
    ''  clear screen
    Cls
    
    ''  draw springs
    For i = Lbound(Mass) To Ubound(Mass)-1
      i2 = i+1
      Line (Mass(i).Pos.X, Mass(i).Pos.Y)-(Mass(i2).Pos.X, Mass(i2).Pos.Y), Rgb(64, 255, 64)
    Next i
    
    ''  draw point masses
    For i = Lbound(Mass) To Ubound(Mass)
      With Mass(i)
        Circle(.Pos.X, .Pos.Y), .Radius, Rgb(255, 64, 64),,, 1, f
      End With
    Next i
    
  ScreenUnlock
  
  Sleep 1, 1
  
Loop Until Multikey(1)

End

That's it, hope you had fun. In part 3 we will take a closer look at friction forces and go 3D. Homework until then is to add damping to the elastic ball code sample and experiment with it ^^

Cheers, Michael


Download all three of the code_examples.zip, or get them individually: 1 2 3


CGI and QBasic

Written by Dean Menezes

Introduction

The Common Gateway Interface (CGI) is a standard protocol for interfacing external application software with an information server, commonly a web server.

The task of such an information server is to respond to requests (in the case of web servers, requests from client web browsers) by returning output. Each time a request is received, the server analyzes what the request asks for, and returns the appropriate output. The two simplest ways, for the server, of doing this are the following:

CGI defines a standard way of doing the second. It defines how information about the server and the request is passed to the command in the form of arguments and environment variables, and how the command can pass back extra information about the output (such as the type) in the form of headers.

CGI is usually used with Python and Perl. However, there is nothing to stop you from using FORTRAN, C, or even QBASIC.

Which brings us to...


THE QBASIC CGI TUTORIAL:

The first task is to write a very simple program which just prints out an HTML document. This should include all the usual <head> ... </head> and <title> ... </title> commands. However, there are two things to note.

First of all, the first two lines of the print out are special. The first line must be

Content-type: text/html

so that the browser knows what kind of document it is and therefore how to display it. In this case it is a text document of the subclass HTML. You can also send back plain ASCII documents by using text/plain instead. Sound samples and images also have to have the content type specified appropriately. The second line must be blank (ie it must contain just a line feed). This line is reserved for future development.

Let's try it:

PRINT "Content-type: text/html;charset=us-ascii"
PRINT
PRINT "<!DOCTYPE HTML PUBLIC " + CHR$(34) +
"-//W3C//DTD HTML 4.01//EN" + CHR$(34)
PRINT CHR$(34) +
"http://www.w3.org/TR/html4/strict.dtd" + CHR$(34) +
">" ' DOCTYPE declaration
PRINT "<html>"
PRINT "<head>"
PRINT "<title>Hello World</title>"
PRINT "</head>"
PRINT "<body>"
PRINT "<h1>Hello, World!</h1>"
PRINT "<p>"
PRINT "<a
href='http://validator.w3.org/check?uri=referer'> <img
"
PRINT "
src='http://www.w3.org/Icons/valid-html401'"
PRINT "    alt='Valid HTML 4.01 Strict' height='31'
width='88'>"
PRINT "</a>"
PRINT "</p>"
PRINT "</body>"
PRINT "</html>"
END

HTML allows us to use single quotes to quote attributes, so we don't have to use CHR$(34).

To connect this to the internet, you use your web server program. Two web server programs are Apache and Abyss.

Abyss - http://en.wikipedia.org/wiki/Abyss_Web_Server

Apache - http://en.wikipedia.org/wiki/Apache_HTTP_Server

I was sucessfully able to get a CGI program that was compiled with QB 7.1 running using Abyss. You may also be able to configure it so it runs the code using QBASIC /RUN


To set up a CGI program in Abyss:

In the Scripting Parameters dialog, use the Interpreters table to add the QBASIC interpreter and the filename extensions it handles. Alternately, add the program to the list of scripts (screenshot: http://img149.imageshack.us/img149/752/cgiwu5.png)

When a CGI script is called several environment variables are set.

These variables include:

SERVER_SOFTWARE
SERVER_NAME
GATEWAY_INTERFACE = CGI/1.1
SERVER_PROTOCOL = HTTP/1.1
SERVER_PORT = 80
REQUEST_METHOD (either GET or PUT)
HTTP_ACCEPT
PATH_INFO
PATH_TRANSLATED
SCRIPT_NAME (name of the script)
QUERY_STRING = (string which you got using GET)
REMOTE_HOST =
REMOTE_ADDR (user's IP)
REMOTE_USER =
CONTENT_TYPE =
CONTENT_LENGTH =

You can access these using the ENVIRON$ function.

Usually the information supplied by the QUERY_STRING variable should come from the user pressing buttons and entering text in the HTML document. It is this information we would like to package up and send to the CGI script. Each group of buttons and text boxes is called a form, and forms are enclosed between the HTML tags <form> ... </form>. You also have to tell it the URL to send the information to, and how the information is sent. The result is something like this:

A submit button is the input device that actually calls the URL. It has a value which is the message that appears on the button. Here is the code for a form with just a submit button in it. When you click on the submit button the URL specified in the form's action is called.

<form action="whatever.exe"
     method="GET">
<input type="submit" value="Click me">
</form>

A checkbox is a simple on/off button. A checkbox has a name (its key) and a value that this key has when the box is checked. As an example, here is the HTML code for a form with a checkbox and a submit button in it.

<form action="whatever.exe" method="GET">
<input type="checkbox" name="lights" value="on">
<input type="submit" value="Do it">
</form>

Now if the submit button is clicked when the box is checked then the information lights=on is packaged into QUERY_STRING. However if the box is not checked then no information is packaged into QUERY_STRING and it remains empty. You have to add the message to the textbox as ordinary HTML text.

Radio buttons are just like checkboxes except they are grouped together and only one button in the group may be selected at a time. All the buttons in a group must have the same name and each one should have a different value. You can also specify which buttons (if any) are checked initially. When the submit button is clicked the name and the value of the selected button are packaged up for QUERY_STRING. Here is some example code for five such buttons. They are all of type radio, and are in the group named cert. The 15 button is checked initially.

<form action="whatever.exe"
     method="GET">
<input type="radio" name="cert" value="u">  U
<input type="radio" name="cert" value="pg"> PG
<input type="radio" name="cert" value="12"> 12
<input type="radio" name="cert" value="15" checked="checked"> 15
<input type="radio" name="cert" value="18"> 18
<input type="submit" value="Certify">
</form>

Text input devices. These are simply boxes into which the user can enter some text which is then packaged up under a particular name. Here is some example code for two text boxes and a submit button. The <br> tag causes a line break.

<form action="whatever.exe" method="GET">
Director: <input type="text" name="dir">  <br>
Producer: <input type="text" name="prod">
<input type="submit" value="Fire">
</form>

You can use single quotes instead of double quotes if you want it to work easier in QBASIC.

Example:

DECLARE FUNCTION FACTOR& (N&)
DEFLNG A-Z
PRINT "Content-type: text/html;charset=us-ascii"
PRINT
PRINT "<!DOCTYPE HTML PUBLIC " + CHR$(34) + "-//W3C//DTD HTML 4.01//EN" + CHR$(34)
PRINT CHR$(34) + "http://www.w3.org/TR/html4/strict.dtd" + CHR$(34) + ">"
PRINT "<html>"
PRINT "<head>"
PRINT "<title>Factor Tree</title>"
PRINT "</head>"
PRINT "<body>"
PRINT "<pre>"
QSTR$ = UCASE$(ENVIRON$("QUERY_STRING"))
N = VAL(MID$(QSTR$, INSTR(QSTR$, "NUM=") + 4))
DO
x = FACTOR(N)
IF x = -1 THEN PRINT TAB(43); N: EXIT DO
PRINT x; "<"; STRING$(39 - LEN(STR$(x)), "-"); "|"; N
PRINT SPACE$(41); "|"
N = N \ x
LOOP
PRINT "</pre>"
PRINT "<form action='factor.exe' method='GET'>"
PRINT "<p>"
PRINT "<input type='text' size='11' name='num' >"
PRINT "<input type='submit' value='Factor!' >"
PRINT "</p>"
PRINT "</form>"
PRINT "<p>"
PRINT "<a href='http://validator.w3.org/check?uri=referer'> <img "
PRINT "src='http://www.w3.org/Icons/valid-html401'"
PRINT "    alt='Valid HTML 4.01 Strict' height='31'
width='88'>"
PRINT "</a>"
PRINT "</p>"
PRINT "</body>"
PRINT "</html>"
END

FUNCTION FACTOR (N)
FOR I = 2 TO SQR(N)
IF (N MOD I) = 0 THEN FACTOR = I: EXIT FUNCTION
NEXT
FACTOR = -1
END FUNCTION

Sometimes, getting lost is the most straightforward path to finding ourselves.


Download a copy of this tutorial: cgi_and_qbasic.txt


Commodore Wedge Reinvented

Written by Kiyote Wolf

Most QBasic programmers remember C= 64 programming since the two were around at the same time. If anybody used a Commodore, they are probably familiar with the term Wedge, a small assembly language routine that rides the screen update at 60 Hz to do stuff automatically, such as monitor keys, play music or update sprites, or more than likely all three at once.

I still have my old QBasic manual, and looking over it for hours on end I finally figured out how to recreate the Wedge interrupt program in a QBasic program. We use the PLAY statement, which creates an interrupt when the play buffer queue gets low to leapfrog out of our code into a side routine and do something useful, then let it leap back to our regular code.

I have been searching my code for an example of this and have since lost the example, but re-coding it (with maybe one error,.. maybe not..) is quite easy. First, set your ON PLAY statement to jump to your Wedge code (your small routine), and set PLAY ON so when the buffer goes empty it will automatically run your Wedge code. Next, initalize your variable, and get your code up to speed, and when you get to the loop of your main routine, just before it put PLAY "T200P32P32". With PLAY ON flagged as ON, your code will now interrupt itself every time the first delay is over, because the only "note" left in the buffer is another "rest", because it is in QBasic to jump to the ON PLAY vector when the buffer drops to only 1 note left. Now, to interrupt this constant Wedge code jumping, simply enter PLAY OFF somewhere in your code to give you main routine full attention again, but if you want to re-enable the Wedge code, make sure you give it a push to start moving again, set PLAY ON and then another PLAY "T200P32P32" statement after to trigger the vector jump again.

The code will still run itself until the buffer catches up, but once it has finished all the "rests" in the queue, the Wedge code will not run again until you let it and then push it into action again. Now, this is of course going to happen very fast, and I'm quite sure that it is not a steady beat because of the haphazard way code is going to process at different speeds. You are going to find that high priority commands such as disk access and most anything is going to run until it is done with the CPU, and then once that task is finished, then next code that will run is going to be your Wedge code. Now because the Wedge code is not stable like a heartbeat, what you can do to moderate it's actions is tie it's reactions into the TIMER or TIME functions to see how much real time occurs between callings of itself, or just don't worry about the differences in time slices because it happens so quickly anyway. Whatever you prefer will of course prevail.

Now, one major bit of information on this, is routines that access memory directly using the DEF SEG routines are drastically going to be affected, especially if your wedge code uses DEF SEG routines of it's own. I have already ran into this and have a solution already. Even though DEF SEG is a regular QBasic command, simply push the DEF SEG command into a SUB or FUNCTION to keep track of the code segment memory address that was fed to it, and make that value a global variable that your Wedge code can find and set the SEG back to when it's done goofing off. That way, if your Wedge code is updating sprites while your main routine is off updating your sprite memory array, your SEG will not be left skewed off course and run the risk of crashing your program. Getting used to a DEF SEG .. SUB is not that hard to do, especially after getting the full use of having a Wedge code piece in your QBasic games can do.

Lastly, the Wedge code is going on, even as it is running itself, so you have to force it not to allow recursion of itself. How do I .. what?? Ok.. what you do is when the Wedge code first runs, have it check for a global flag to tell itself that it is running once already, and if so, return to the regular code without running. After a test for an instance goes false, meaning this code is running only one copy of itself, right after that test, set the global flag to tell itself it's running. The flag set now, set PLAY OFF so the remaining "rests" in the queue die off and get kicked by the "busy flag" test. Now your Wedge code is running. Do your fun stuff.. sing a song,.. move a pixel.. whatever.. Now, if there was a DEF SEG address saved, re-set that flag so the main code will have it's code segment set, then set PLAY ON, then right before the code goes RETURN, do another WorkingFlag = 0 then PLAY "T200P32P32".

*** Example that works

ON PLAY(1) GOSUB STOPHERE
PLAY ON

PLAY "T200P32P32"
SCREEN 0: WIDTH 80, 25: CLS

REM MAIN LOOP
DO
  PRINT "HI WORLD"
  t = TIMER: WHILE TIMER - t < .1: WEND
  COLOR INT(RND(1) * 6) + 1
LOOP

REM WEDGE CODE
STOPHERE:
IF WORKING THEN RETURN
WORKING = -1
PLAY OFF
PRINT "INTERRUPTING COW"
PLAY ON
WORKING = 0
PLAY "T600P32P32"
RETURN

You can use this code to do lots of stuff in the background. Instead of wasting precious cycles doing a blank dull timer to keep your code from running too fast, you can use this to one, slow your code down a tiny bit or more if you code it to, and two, use this code to make things simultaneous, or to watch for things without having to code complex jumping routines to make your program crazy. You can do all this in the background without having to even reference the code in question.

I hope people can use this, because I think it's great to have the C= 64 Wedge back again.

P.S. One drawback of this routine is it will toggle the PC speaker, so at different operating speeds, you may notice strumming of the PC speaker that sounds like a light hum, or maybe more annoying noise, but generally you shouldn't even hear a thing with it. If you use DOSBOX to run and your code already uses the Soundblaster, simply disable the PC speaker if you do encounter this phantom noise. You cannot use standard PLAY statements while this routine operates or even SOUND, since they use the same port to do the same function, so use a Soundblaster bit of code of some kind in your program, or just no sound at all. Other people should be able to drop the volume of the internal speaker on their sound settings, but do not mute it. If you mute it, it will probably enable the speaker internally which will just do the same thing, just drop the volume to zero on the slider.

KW 2/9/08


Download a copy of this tutorial: commodore_wedge.txt


FreeBASIC File/Folder Management

by Stephane Richard (MystikShadows)

NOTE: This tutorial was originally published in PCopy! Issue #40, and is being republished in QB Express this month due to a request for an article on this topic in the QB Express Article Requests thread.

INTRODUCTION:

Many times when programming an application (or even games) you find yourself coming across the subject of creating files folders (or deleting them) for more than one reason. Luckily FreeBASIC has everything we need to help us with all of these functionalities.

In this tutorial I'll cover all of the file/folder oriented statements and functions that exist in FreeBASIC and well see how and when they can be used in order to bring some good standard file and folder management functionality to your programming projects. So let's start off with a little reference of the commands we'll be using and take it from there.

FREEBASIC FILE AND FOLDER COMMANDS AND FUNCTIONS:

There are several commands and functions available to create, delete, manage files and folders.

And now that we have the reference material we need let's see what we can do with them. We will take each one and see how to use them in given situations and perhaps implement some safeguards in code to help minimize errors that could occur. For this we will create our own little COMMAND.COM so to speak. A little program that will accept input from the user and perform the current actions required by the commands we'll enter in the system.

BUILDING OUR OWN COMMAND PROCESSOR:

As a first note, I'd like to point out that this will be a very simplistic command processor. it's purpose is to highlight the FreeBASIC commands and functions for file/folder manipulation, so it will essentially be a simple loop with string manipulation to decipher the commands entered. But it will be something functional that can later be altered to perform as expected. It will not allow to execute other files for examples, just internal commands for the purpose of this tutorial. The sample program created here can be acquired from this link so you don't have to type or edit anything.

First off we'll create the core processor/command acquisition system and we'll add commands to it as we progress in this tutorial. The framework itself shown here has functions to get parts of the entered command, this is how we'll know which command has been entered and what to do with them.

' -------------------------------------
'  BASIC command processor application
'          by MystikShadows
' -------------------------------------

#INCLUDE "dir.bi"

' -------------------------------
'  Sub and Function Declarations
' -------------------------------
DECLARE SUB PrintApplicationInformation()
DECLARE SUB DisplayHelpScreen()
DECLARE SUB ProcessCommand(ToBeProcessed AS STRING)
DECLARE FUNCTION GetWordCount(WorkString AS STRING) AS INTEGER
DECLARE FUNCTION GetWordNumber(WorkString AS STRING, WordNumber AS LONG) AS STRING

' -----------------------------
'  Needed Constant Definitions
' -----------------------------
CONST False = 0
CONST True  = NOT False

CONST HeaderColor  = 15 ' White
CONST InfoColor    = 11 ' Light Cyan
CONST CommandColor = 14 ' Yellow
CONST ErrorColor   = 12 ' Light Red
CONST ResultColor  = 10 ' Light Green

' -------------------------
'  Needed Global Variables
' -------------------------
DIM SHARED CurrentCommand AS STRING
DIM SHARED CanExit        AS INTEGER

' ------------------------------------------------------
'  Start by displaying the title and general information
' ------------------------------------------------------
WIDTH 80, 50
COLOR 7, 1
CLS
CALL PrintApplicationInformation()

' -----------------------------------------------------
'  Main loop where commands are acquired and processed
' -----------------------------------------------------
DO WHILE CanExit = False
   ' ---------------------------
   '  Get command from the user
   ' ---------------------------
   COLOR CommandColor, 1
   PRINT "->";
   INPUT CurrentCommand
   CALL ProcessCommand(CurrentCommand)
   ' -------------------------------------------
   '  We Clear the command, ready for a new one
   ' -------------------------------------------
   CurrentCommand = ""

LOOP

' ========================================================
'  NAME........: PrintApplicationInformation()
'  PARAMETERS..: None
'  RETURN......: No value
'  ASSUMES.....: Nothing
'  CALLED FROM.: Main Program Loop
' --------------------------------------------------------
'  DESCRIPTION.: This Sub simply displays some regular
'                information to the user about this
'                current program
' ========================================================
SUB PrintApplicationInformation()

    COLOR HeaderColor, 1
    PRINT "MystikShadow's Command Processor Application"
    PRINT "Version 1.00a"
    PRINT
    COLOR InfoColor, 1
    PRINT "Type 'HELP' for a list of commands"
    PRINT "Type 'EXIT' to end the program"
    PRINT

END SUB

' ========================================================
'  NAME........: DisplayHelpScreen()
'  PARAMETERS..: None
'  RETURN......: No Value
'  ASSUMES.....: Nothing
'  CALLED FROM.: The Main Program Loop
' --------------------------------------------------------
'  DESCRIPTION.: This Sub displays a list of valid
'                commands available to the user. So that
'                he/she can learn the allowed commands.
' ========================================================
SUB DisplayHelpScreen()

    COLOR ResultColor, 1
    PRINT "Valid Commands Are:"
    PRINT
    PRINT "TIME                       Returns The Current Time"
    PRINT "DATE                       Returns The Current Date"
    PRINT "CD []            Returns Current Path or Changes to PathName"
    PRINT "DIR     Lists files that match FileSpecification
    PRINT "REN      Renames an existing file to a new name"
    PRINT "RENAME   Renames an existing file to a new name"
    PRINT "MD             Creates a Directory on the Disk"
    PRINT "RD             Removes a Directory from the disk"
    PRINT "DEL     Removes a Directory from the disk"
    PRINT "DELETE  Removes a Directory from the disk"
    PRINT
END SUB

' ========================================================
'  NAME........: ProcessCommand()
'  PARAMETERS..: ToBeProcessed AS STRING
'  RETURN......: No Value
'  ASSUMES.....: ToBeProcessed Is Not Empty
'  CALLED FROM.: Main Program Loop
' --------------------------------------------------------
'  DESCRIPTION.: This Sub is the main core of the program
'                it will take the passed command in it's
'                parameter, parse it and perform the
'                corresponding action.
' ========================================================
SUB ProcessCommand(ToBeProcessed AS STRING)

    ' -------------------------------------
    '  First we take care of easy commands
    ' -------------------------------------
    COLOR ResultColor, 1
    SELECT CASE TRIM$(UCASE$(ToBeProcessed))
           CASE "HELP"
                CALL DisplayHelpScreen()
                EXIT SUB
           CASE "QUIT"
                CLS
                END
    END SELECT

END SUB

' ========================================================
'  NAME........: GetWordCount()
'  PARAMETERS..: WorkString AS STRING
'  RETURN......: 0 Or the number of words
'  ASSUMES.....: WorkString is valid string with contents
'  CALLED FROM.: ProcessCommand()
' --------------------------------------------------------
'  DESCRIPTION.: This function accepts a string as a
'                parameter, peruses it and accumulates
'                the number of words it finds in the
'                string.
' ========================================================
FUNCTION GetWordCount(WorkString AS STRING) AS INTEGER

    ' ----------------
    '  Work Variables
    ' ----------------
    DIM  Counter      AS LONG
    DIM  WordCounter  AS LONG
    DIM  Position     AS LONG
    DIM  Skipper      AS LONG

    ' ----------------------
    '  Initialize Variables
    ' ----------------------
    WordCounter  = 1
    Position     = 1
    ' ---------------------------------------------------------
    '  First we put ourselves at the beginning of the 1st word
    ' ---------------------------------------------------------
    IF MID$(WorkString, Position, 1) = " " THEN
       DO WHILE MID$(WorkString, Position, 1) = " "
          Position = Position + 1
       LOOP
    END IF
    ' --------------------------------------
    '  Then we start the Word Count Process
    ' --------------------------------------
    FOR Counter = Position TO LEN(WorkString)
        IF MID$(WorkString, Counter, 1) = " " THEN
           WordCounter = WordCounter + 1
           DO WHILE MID$(WorkString, Counter, 1) = " "
              Counter = Counter + 1
           LOOP
        END IF
    NEXT Counter
    ' ----------------------------------------
    '  Return the number of words accumulated
    ' ----------------------------------------
    GetWordCount = WordCounter

END FUNCTION

' ========================================================
'  NAME........: GetWordNumber()
'  PARAMETERS..: WorkString AS STRING
'                WordNumber AS LONG
'  RETURN......: "" Or the desired word Number
'  ASSUMES.....: WorkString has contents, WordNumber > 0
'  CALLED FROM.: ProcessCommand()
' --------------------------------------------------------
'  DESCRIPTION.: This function accepts a string and a
'                Word Number as parameters, it then finds
'                the given word number and returns it to
'                the calling function.
' ========================================================
FUNCTION GetWordNumber(WorkString AS STRING, WordNumber AS LONG) AS STRING

    ' ----------------
    '  Work Variables
    ' ----------------
    DIM  Counter      AS LONG
    DIM  WordCounter  AS LONG
    DIM  Position     AS LONG
    DIM  Skipper      AS LONG
    DIM  ReturnString AS STRING

    ' ----------------------
    '  Initialize Variables
    ' ----------------------
    WordCounter  = 1
    Position     = 1
    ReturnString = ""
    ' ---------------------------------------------------------
    '  First we put ourselves at the beginning of the 1st word
    ' ---------------------------------------------------------
    IF MID$(WorkString, Position, 1) = " " THEN
       DO WHILE MID$(WorkString, Position, 1) = " "
          Position = Position + 1
       LOOP
    END IF
    ' --------------------------------------
    '  Then we start the Word Count Process
    ' --------------------------------------
    FOR Counter = Position TO LEN(WorkString)
        IF MID$(WorkString, Counter, 1) = " " THEN
           WordCounter = WordCounter + 1
           DO WHILE MID$(WorkString, Counter, 1) = " "
              Counter = Counter + 1
           LOOP
        END IF
        ' ----------------------------------------------------------
        '  If it's the wanted word number we put it in ReturnString
        ' ----------------------------------------------------------
        IF WordCounter = WordNumber THEN
           IF MID$(WorkString, Counter, 1) <> " " THEN
              ReturnString = ReturnString + MID$(WorkString, Counter, 1)
           END IF
        END IF
    NEXT Counter
    ' ---------------------------------
    '  Return the word in ReturnString
    ' ---------------------------------
    GetWordNumber = ReturnString

END FUNCTION
         

What this does code so far is wait for the user to enter a command in the system. Once entered, the ProcessCommand() subroutine is called. As you can see from the code to ProcessCommand() subroutine so far we manage two commands HELP and QUIT. The last two functions you see in the code are the string manipulation function we'll need to get the different parts of the command entered by the user. If you compile the program like it is now, run it and enter the help command. Here's what you will see on the screen:

After that, just enter EXIT to leave the program. The command list you see on the screenshot are the commands we will be implementing in this tutorial. Four of the commands (CLS, TIME, DATE and CD without parameters are very easy to code for so in the ProcessCommand we'll just add CASE statements for each of them. Here's what the ProcessCommand() subroutine should look like:

	SUB ProcessCommand(ToBeProcessed AS STRING)

    ' -------------------------------
    '  We take care of easy commands
    ' -------------------------------
    COLOR ResultColor, 1
    SELECT CASE TRIM$(UCASE$(ToBeProcessed))
           CASE "TIME"
                PRINT TIME$
                PRINT
                EXIT SUB
           CASE "DATE"
                PRINT DATE$
                PRINT
                EXIT SUB
           CASE "CLS"
                COLOR 7, 1
                CLS
                EXIT SUB
           CASE "CD"
                PRINT CURDIR$
                PRINT
                EXIT SUB
           CASE "HELP"
                CALL DisplayHelpScreen()
                EXIT SUB
           CASE "EXIT"
                CLS
                END
    END SELECT
         

In the case of these commands, as you can see, what goes on is very straightforward. Simple code is executed in each of these cases. now we need to add validation and tell the user if they entered a command that isn't recognized. Since we already have the list of valid command it will be rather easy to know if the command entered is valid or not. So We'll add, at the beginning of the subroutine, a condition to ProcessCommand() to validate the command like so:

	SUB ProcessCommand(ToBeProcessed AS STRING)

    DIM FirstWord AS STRING

    ' -------------------------------------------------
    '  First we test of the command is a valid command
    ' -------------------------------------------------
    FirstWord = TRIM$(UCASE$(GetWordNumber(ToBeProcessed, 1)))
    IF FirstWord <> "TIME"   AND FirstWord <> "DATE" AND _
       FirstWord <> "CLS"    AND FirstWord <> "CD"   AND _
       FirstWord <> "CD.."   AND FirstWord <> "CD\"  AND _
       FirstWord <> "DIR"    AND FirstWord <> "REN"  AND _
       FirstWord <> "RENAME" AND FirstWord <> "DEL"  AND _
       FirstWord <> "DELETE" AND FirstWord <> "MD"   AND _
       FirstWord <> "RD"     AND FirstWord <> "HELP" AND _
       FirstWord <> "EXIT"   THEN
       COLOR ErrorColor, 1
       PRINT "An Invalid Command Was Entered"
       PRINT
       EXIT SUB
    END IF

    ' ------------------------------------
    '  Next we take care of easy commands
    ' ------------------------------------
    COLOR ResultColor, 1
    SELECT CASE TRIM$(UCASE$(ToBeProcessed))
           CASE "TIME"
                PRINT TIME$
                PRINT
                EXIT SUB
           CASE "DATE"
                PRINT DATE$
                PRINT
                EXIT SUB
           CASE "CLS"
                COLOR 7, 1
                CLS
                EXIT SUB
           CASE "CD"
                PRINT CURDIR$
                PRINT
                EXIT SUB
           CASE "HELP"
                CALL DisplayHelpScreen()
                EXIT SUB
           CASE "EXIT"
                CLS
                END
    END SELECT

END FUNCTION
         

Note that I added "CD.." and "CD\" as valid commands because typical DOS users didn't put spaces for these two commands (just a hint of user consideration). Basically, if the command entered isn't one that appears in the if statement we will warn the user that an invalid command has been entered.

ADDING COMMANDS TO THE COMMAND PROCESSOR:

Now that we validate our commands it's time to get to the real part of the tutorial. This is were we start to really look at what the user wants to do. To do so, we'll need a few other variables in our ProcessCommand() subroutine. We will put them at the beginning of the subroutine after the first DIM statement already there.

	DIM SecondWord    AS STRING
    DIM ThirdWord     AS STRING
    DIM WorkFile      AS STRING
    DIM WordCounter   AS INTEGER
    DIM FolderCounter AS INTEGER
    DIM FileCounter   AS INTEGER
         

These variables will come in handy to implement the rest of the supported commands. As you can see from the screenshot SecondWord and ThirdWord are there get the 2nd and 3rd part of the statement (since 2 parameters will be the most we need to deal with we only need SecondWord and ThirdWord). First we'll Implement the CD family of commands. After the SELECT CASE that's already there, we will first add statements to get the second and third word if we need to in order to complete the statements entered by the user. Here is the code to add at the end of the ProcessCommand() subroutine:

	' ---------------------------------------------
    '  We get the rest of the words in the command
    ' ---------------------------------------------
    WordConter = GetWordCount(ToBeProcessed)
    IF WordCounter = 2 THEN
       SecondWord = GetWordNumber(ToBeProcessed, 2)
    ELSEIF WordCounter = 3 THEN
       SecondWord = GetWordNumber(ToBeProcessed, 2)
       ThirdWord  = GetWordNumber(ToBeProcessed, 3)
    END IF
         

Now the first command we will implement is the CD command. Remember that CD by itself simply displays the current directory to the user and is already coded for. This part will take care of the other forms of CD commands. Typically the user will enter CD followed by a folder name, a .. or a \. Like I mentioned earlier DOS users don't usually enter spaces for .. and \ so we will add direct support for CD.. and CD\ without spaces. Here's the code to the CD command:

	' -------------------------------------------------------------------------
    '  This SELECT CASE decides which code to executed for the command entered
    ' -------------------------------------------------------------------------
    SELECT CASE FirstWord
           ' -------------------------------
           '  THE CD command implementation
           ' -------------------------------
           CASE "CD", "CD..", "CD\"
                ' -----------------------------------------
                '  Test for the existence of the directory
                ' -----------------------------------------
                IF DIR$(SecondWord, fbDirectory) = "" AND SecondWord <> "\" THEN
                   ' -------------------------------------------------------
                   '  If the directory does not exist, we display a message
                   ' -------------------------------------------------------
                   COLOR ErrorColor, 1
                   PRINT "This Directory does not exist. Path remains unchanged."
                   PRINT CURDIR$
                   PRINT
                   COLOR ResultColor
                ELSE
                   ' ------------------------------------------------
                   '  Test to see if none spaced .. and \ were used
                   '  Execute appropriate command in these cases.
                   ' ------------------------------------------------
                   IF FirstWord = "CD.." THEN
                      CHDIR ".."
                   ELSEIF FirstWord = "CD\" THEN
                      CHDIR "\"
                   ' ---------------------------------------------------
                   '  In all other cases we just CHDIR to the directory
                   ' ---------------------------------------------------
                   ELSE
                      ' -----------------------------------------------
                      '  If it does exist we CHDIR right to the folder
                      ' -----------------------------------------------
                      CHDIR SecondWord
                   END IF
                   PRINT CURDIR$
                END IF
                PRINT
                EXIT SUB
    END SELECT
         

You can note that we also added a test to see if the folder the user wants to change to actually exists. We warn them if it doesn't exist and nothing else happens. If the directory does exist, We use CHDIR to change to the specified directory. Using this code the user can enter any valid CHDIR statements after the CD command and it will perform as expected.

The next command on our list is the MD command. This command is a bit simpler to code for than the CD command because it doesn't have any variants to it. The user would simply type MD FolderName to create the desired folder. We will simply test to see if the folder they are trying to create already exists and warn them if it does. Here's the code for the MD command. It is added to the SELECT CASE statement we just created previously.

	  	   ' -------------------------------
           '  The MD Command Implementation
           ' -------------------------------
           CASE "MD"
                ' -------------------------------------
                '  We test to see if the folder exists
                ' -------------------------------------
                IF DIR$(SecondWord, fbDirectory) <> "" THEN
                   ' ------------------------------------
                   '  If it does exist, we warn the user
                   ' ------------------------------------
                   COLOR ErrorColor, 1
                   PRINT "This Directory already exists, unable to create it."
                   PRINT
                   COLOR ResultColor, 1
                ELSE
                   ' -------------------------------------------------------
                   '  If it doesn't exist, we simply go ahead and create it
                   ' -------------------------------------------------------
                   MKDIR SecondWord
                END IF
                EXIT SUB
         

As you can see, the code is straight forward and rather clear we either create the folder or we don't (if the folder already exist). This is the only test we really need to do.

Up next on our list is the RD command which removes a directory from the disk. Remember that RD will not work if the directory to be removed already has files in it. Coding for the RD command should be quite similar to the MD command with the added test to see if there are files in the folder we want to remove. If there are, we'll warn the user that they are files. If no files exist in the directory we'll go ahead and use RMDIR to remove the folder.

	       ' -------------------------------
           '  The RD Command Implementation
           ' -------------------------------
           CASE "RD"
                ' ------------------------------------
                '  First we test if the folder exists
                ' ------------------------------------
                IF DIR$(SecondWord, fbDirectory) = "" THEN
                   COLOR ErrorColor, 1
                   PRINT "This Directory does not exist hence cannot be removed."
                   PRINT
                   COLOR ResultColor, 1
                ' --------------------------------------------------
                '  Next we test if there are files in the directory
                ' --------------------------------------------------
                ELSEIF DIR$(CURDIR$ + "\" + SecondWord + "\*.*") <> "" THEN
                   COLOR ErrorColor, 1
                   PRINT "The Directory has files in it and cannot be removed."
                   PRINT
                   COLOR ResultColor, 1
                ' -----------------------------------------------------
                '  If all is good we go ahead and remove the directory
                ' -----------------------------------------------------
                ELSE
                   RMDIR SecondWord
                END IF
                EXIT SUB
         

Simply put we do two consecutive tests, the first to see if the folder exists in the first place, if it does, we're good we can go to the second test which is to test if the directory has any files in it. If there are files we warn the user since the directory won't be removed. if there are no files we go ahead and remove the directory.

The next command on the list is the DIR command. The DIR command lists files that match the specified File masks. Shortcuts can be used here such as the standard "*.*" and the "?" to filter out unwanted files. In this version I am doing two things when a DIR command is issued. I first list the folders (if any) that match the file mask and then I list the files that match the file mask. I will also accumulate the results to tell the user how many folders and files were found for the DIR command they issued. I added a little feature which if DIR is called without a file mask I default it to *.* instead of warning them of an invalid command. here's the code to the DIR command CASE STATEMENT (again to be added at the end of the SELECT CASE STATEMENT):

	       ' --------------------------------
           '  The DIR Command Implementation
           ' --------------------------------
           CASE "DIR"
                FolderCounter = 0
                FileCounter   = 0
                ' ----------------------------------------------------
                '  We first list the folders in the current directory
                ' ----------------------------------------------------
                IF SecondWord = "" THEN
                   WorkFile = DIR$("*", fbDirectory)
                ELSE
                   WorkFile = DIR$(SecondWord, fbDirectory)
                END IF
                DO WHILE TRIM$(WorkFile) <> ""
                   IF LEFT$(WorkFile, 1) <> "." THEN
                      FolderCounter = FolderCounter + 1
                      PRINT WorkFile & "\"
                   END IF
                   WorkFile = DIR$()
                LOOP
                ' -------------------------------------------------
                '  We then list the files in the current directory
                ' -------------------------------------------------
                IF SecondWord = "" THEN
                   WorkFile = DIR$("*")
                ELSE
                   WorkFile = DIR$(SecondWord)
                END IF
                DO WHILE TRIM$(WorkFile) <> ""
                   FileCounter = FileCounter + 1
                   PRINT WorkFile
                   WorkFile = DIR$()
                LOOP
                ' --------------------------------------------------------------
                '  We finally display the folder and file counts we accumulated
                ' --------------------------------------------------------------
                PRINT
                PRINT "There were " + STR$(FolderCounter) + _
                      " directories and " + STR$(FileCounter) + _
                      " files listed."
                PRINT
                EXIT SUB
          

Basically, this code is created to show you examples (hopefully real word examples) on how to use these commands, the things to watch out for when using them and the likes. This is why I do things like check for the existence of a folder before I try to CHDIR to it or something. Nothing happens if there's no files to list. If there are files then they will be listed one after the other as long as the search yields a file. Quite straight forward.

And now we have the DEL and DELETE commands. Like DOS you can used both DEL or DELETE (whatever you are used to) to delete files on your disk. The only test we need to do here is to see if the file they want to delete actually exists, if so, then we use the KILL statement to delete the file. If it does not exist we'll simply warn the user about it and do nothing else. here's the CASE statement for the DEL, DELETE command (you guessed it, at the end of the SELECT CASE statement is where it goes):

	       ' -------------------------------------------
           '  The DEL and DELETE Command Implementation
           ' -------------------------------------------
           CASE "DEL", "DELETE"
                ' ---------------------------------
                '  First we see if the file exists
                ' ---------------------------------
                IF DIR$(SecondWord) = "" THEN
                   COLOR ErrorColor, 1
                   PRINT "This file does not exist, unable to delete it."
                   PRINT
                   COLOR ResultColor, 1
                ' ----------------------------------------------------------
                '  If the file exists we go ahead and use KILL to delete it
                ' ----------------------------------------------------------
                ELSE
                   KILL SecondWord
                END IF
                EXIT SUB
          

Very simple test that basically says that if the file exists we can delete it, if it does not exist we just let the user know (in which case he will just repeat the command with the right file name the next time around, hopefully). Pretty simple isn't it? I thought so.

And we are at the final command to be implemented, the REN and RENAME commands. This is the only command that needs all three parameters, hence we'll have to do some validation on them to make sure everything is good to perform the renaming of the file in question. First test will be the old file name is not empty. If it is empty we let the user know and we do nothing else (EXIT SUB). The next test is to see that the new file name is not empty. if it is empty, once again, we warn the user and exit the subroutine. If it is not empty we go to the third test which is to see that the old file name actually exists on the current or specified folder. If we're still good after these 3 tests we go ahead and use the NAME statement to rename the old file name (SecondWord) to the new file name (ThirdWord). Here is the last CASE STATEMENT to be added to our codebase:

		   ' -------------------------------------------
           '  The REN and RENAME Command Implementation
           ' -------------------------------------------
           CASE "REN", "RENAME"
                ' ----------------------------------------------
                '  First we test that Old Filename Is not empty
                ' ----------------------------------------------
                IF SecondWord = "" THEN
                   COLOR ErrorColor, 1
                   PRINT "The name of an existing file is needed to rename the file."
                   PRINT
                   COLOR ResultColor, 1
                   EXIT SUB
                ' ---------------------------------------------
                '  Next we test that New Filename Is not empty
                ' ---------------------------------------------
                ELSEIF ThirdWord = "" THEN
                   COLOR ErrorColor, 1
                   PRINT "A new name is needed to rename the file."
                   PRINT
                   COLOR ResultColor, 1
                   EXIT SUB
                ' ----------------------------------------------
                '  Finally we test that the old filename exists
                ' ----------------------------------------------
                ELSEIF DIR$(SecondWord) = "" THEN
                   COLOR ErrorColor, 1
                   PRINT "The file you are trying to rename does not exist."
                   PRINT
                   COLOR ResultColor, 1
                   EXIT SUB
                ' --------------------------------------------------------------
                '  If all went well we go ahead and use NAME to rename the file
                ' --------------------------------------------------------------
                ELSE
                   NAME SecondWord, ThirdWord
                   EXIT SUB
                END IF
          

As you can see, it's really all about implementing some basic tests before performing the actual command just to be sure everything is good, ready and safe to perform the actual command. With these in place the command processor becomes a bit more stable a tool to use for the user and for the user's files and folders of course.

A FEW NOTES:

Now remember this sample program is mean to illustrate the use of the file and folder commands provided by FreeBASIC. Hence the emphasis is on those commands. Of course I could have used another method than input to get the user's commands. I could have done things a bit different (like perhaps add some good Drive, Path and file name validation for example) but the important part is what I do with the file and folder commands themselves.

IN CONCLUSION

And there you have it, I believe we've covered all of the commands offered by FreeBASIC as far as files and folder management goes. Of course FreeBASIC has more commands (like CHAIN, EXEC, RUN and SHELL) that have the potential to add more functionality to a program such as this command processor sample. This sample is designed to respond to user commands but any of these commands could be coded in a subroutine and called for any purpose from any type of program really. The choice is yours.

As always, I hope this tutorial helped you in some way or another. I hope it was clear but readers tend to read things differently. So, if any of this isn't clear in your head, be sure to email me to let me know what parts is not clear or whatever other comments or questions you may have about this tutorial. Until next, happy coding (and command processing).


Download a copy of this tutorial: filefolder.zip


Window Procedure: A Quick Review

Written by Wallyfblu

Introduction

This is the first article I try to write and I hope I explained it clearly.

This text comes from my need to clarify some aspects about WIN32 GUI programming.

I know there are some good tutorials and articles on the net about this topic, but I want to examine the window procedure, namely the function which handles windows messages.

I assume you already code a simple Win32 GUI app using API.


The Window Procedure: first step

When you start to write a Win32 Gui application, you usually use a window proc that handles the messages you want.

A common scheme is a big "Select Case" where every message is verify in a "Case" statement and the "Case Else" processes the unwanted messages.


A usual window procedure is shown below.

Function WndProc(ByVal hWin As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM) As Integer

Select Case uMsg
	Case WM_INITDIALOG
		hWnd=hWin
			'
	Case WM_COMMAND
		Select Case HiWord(wParam)
			Case BN_CLICKED
				Select Case LoWord(wParam)
					Case IDM_FILE_EXIT
						SendMessage(hWin,WM_CLOSE,0,0)
						'
					Case IDM_HELP_ABOUT
						ShellAbout(hWin,@AppName,@AboutMsg,NULL)
							'
				End Select
				'
		End Select
		'
	Case WM_SIZE
		'
	Case WM_CLOSE
		DestroyWindow(hWin)
		'
	Case WM_DESTROY
		PostQuitMessage(NULL)
		'
	Case Else
		Return DefWindowProc(hWin,uMsg,wParam,lParam)
			'
	End Select
	Return 0
End Function

So, if you need to intercept a new message, you insert a "Case WM_XXX" and write the code you want.

You can use this approach when you have a little amount of messages and little code to manage because when the program growing up, the window proc management become complex.

The inherent problems about this style comes from the rigidity of the code, the function don’t perform a single task (or a little amount of similar tasks) and you lack modularity and readability.


The Window Procedure: second step

In the next step you always use the “Select case” but, after have made the choice, call a function for each message you need to manage, that does the work. The principle is the same, but you use less code in the WP (i.e. Window procedure).

The code might look like this:

.
.
.
	Case WM_INITDIALOG
		OnInit(parameters)
		'
	Case WM_COMMAND
		OnCommand(parameters)
		'
	Case WM_SIZE
		OnSize(parameters)
		'
	Case WM_XXX
		OnXxx(parameters)
			'
.
.
.

You have a better solution, but you still have the same structure.

Now you could say: "Why should I worry about this? If I need to manage a new message, I add the code and here you are.” I try to answer explaining the latest method I used (and I know).


The Window Procedure: next step

This approach is composed of a data structure to store the correspondence between the event that has occurred and work to be done. A framework like this use an array of function pointers and the message number as index.

Dim Shared As WndProc MsgList(WM_USER)

Function WndProc(ByVal hWin As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM) As Integer

Dim As WndProc guifunc

	GuiFunc=MsgList(uMsg)
	If Guifunc>0 Then
		Return guifunc(hWin,uMsg,wParam,lParam)
	End If

	Return DefWindowProc(hWin,uMsg,wParam,lParam)
	Return FALSE

End Function

To assign an array element to a function, we could use a support proc :

Sub Assign(mess As Long, proc As WndProc)
	MsgList(mess)=proc
End Sub

and use it in this way into the main body of program:

Assign(WM_INITDIALOG, Cast(WndProc,@OnInit))
Assign(WM_COMMAND, @OnCommand)

This approach has at least two advantages:

These are some answer to questions asked before and have been a reason for me to go ahead to explore this technique in order to use it in a best way.

Obviously this solution opens other problems and every problem can be managed in several ways.

Now I only list them, because a full discussion is too hard for my strength. :)


Conclusion

This simple explanation about the WIN32 window procedure management it’s far to be complete and exhaustive.

I hope I’ve explained it in a clearly way despite my poor English.

I hope to receive feedback in order to continue… or halt me.


Attached file: WndProc.zip

I use resource file instead Createwindow etc.

Compile them as: fbc -s gui WndProc.bas WndProc.rc


If you have any idea , suggestion or disappointment please mail me to: wallyfblu@libero.it

Greets to:
All article writers
Codemss for his: Writing tutorials

Download this tutorial: WndProc.zip


Using Electric Field Equation To Generate Awesome Moving Plasmas

Written by Hezad

Hi ! First, I wanna say this is my first tutorial and i won't ask you to be indulgent ! If some physicians read that, they have to know that I'll use this field UNIQUELY in a graphical goal so some developments doesn't mean anything from a physics point of view. For the others, if you wanna clarify, add some informations, shout at me, don't hesitate to contact me : Narky00-at-laposte.net

Today, we will see the electric field equation (don't panic, we'll just use a physics equation for its graphical effects !).


INTRO :

In our case, we'll need to put some special points called charges (Symbol : Q) which can be positive or negative (Q+ or Q-). Two charges with the same sign (+/+ or -/-) push away each other and two charges with different signs (+/- or -/+) attract each other (but we won't use those forces to move points but to colorize the screen).


A vectors field is an equation which associate a vector to any point (called r) in space. Each charge will "diffuse" the electric force everywhere around it :


Mathematically, the field equation looks like this :


Nice, uh? But in fact we won't really need the vector notation so we can write it like this :



Some explanations :


HOW TO PROGRAM THIS ?

Okay, let's enter in the main thing ! We'll mainly need :

First, let's build a type which contain x and y values for positions/velocities/vectors and define some constants :

Randomize Timer '' Random seed for later use of rnd

Const SW = 640  '' Screen Resolution
Const SH = 480

Const PI = 3.1415926535897932384626  '' So much precision is not needed ...
Const Epsilon = 8.8541878*10^-12     '' This is the value of Epsilon in Void space

Const MAX_CHARGES = 3 
'' We will use a maximum of 3 charges but you can do as you please !

Type Val2d       
    x as single
    y as single
End type

That's a good start :) Now, let's build our charge TYPE and declare an array of charges :

Type ChargeType
    Pos as Val2d     '' Position
    V as Val2d       '' Velocity
    Sign as integer  '' +1 or -1
End type

'' Array of charges
Dim shared as ChargeType Charge(MAX_CHARGES)

"Shared" because we'll use this array in subs ! Now, to calculate the field, we need an array of vectors to handle the E field. Theorically, there should be a vector on each point of the screen but it'd make 640*480 vectors to calculate on each frame !! So we'll define the field each 10*10 pixels (=> 64*48 vectors to calculate on each frame) :

'' Array of Vectors to handle E
Dim shared as Val2d E(SW/10,SH/10)

(Remember, SW and SH are Screen Width and Height)

We defined Charges and E field, so we can now write the CalcE Sub ! Let's get a closer look to the previous equations :


Pay attention to the next sentence, i'm sorry if I badly explain the thing but the code should help you to understand if it seems incomprehensible.

We'll need to loop through screen coordinates (remember, each 10*10 pixels) to calculate the corresponding vector. And as you can see in the equations, we'll need to sum some data. So we'll write one more loop IN the previous loop. Once E is calculated, remember we'll use its value for coloring screen ! So we'll divide it by its norm to have a value between 0 and 1 :

Sub CalcE()
   
    '' Let's Dim some variables we'll need
   
    Dim as Val2d CurrentPosition
    Dim as Val2d Sum
    Dim as single ENorm
   
   
    '' First Loop (the first loop is in fact 2 loops : through x and through y
   
    For x as integer = 0 to SW/10
        For y as integer = 0 to SH/10
           
            CurrentPosition.x = x*10  '' We need to multiply per 10 as the loop stops at SW/10
            CurrentPosition.y = y*10  '' Idem
           
            Sum.x = 0  '' Init of the Sum
            Sum.y = 0
           
            '' Sum loop :
            For i as integer = 1 to MAX_CHARGES
               
                '' Here is the main calc ! It may seem a bit complicated but it's
                '' just the Sum part of the equation !
               
                Sum.x += (Charge(i).Sign * (CurrentPosition.x-Charge(i).pos.x)) / _
                (sqr((CurrentPosition.x-charge(i).pos.x)^2+(CurrentPosition.y-charge(i).pos.y)^2)^3)
               
                Sum.y += (Charge(i).Sign * (CurrentPosition.y-Charge(i).pos.y)) / _
                (sqr((CurrentPosition.x-charge(i).pos.x)^2+(CurrentPosition.y-charge(i).pos.y)^2)^3)
               
            Next
           
            '' Now we just have to multiply the sum per 1/(4*pi*epsilon)
            ''and we'll find E !
           
            E(x,y).x = Sum.x * (1/4*PI*Epsilon)
            E(x,y).y = Sum.y * (1/4*PI*Epsilon)
           
           
            '' And we divide E by its norm to have a value between 0 and 1 !
            ENorm = sqr(E(x,y).x^2+E(x,y).y^2)
           
            E(x,y).x/=ENorm
            E(x,y).y/=ENorm
           
        Next
    Next
   
    '' Done ! E is calculated !
End sub

Fiuh !! That was a big one ! And Now ? Now, let's write the Render Sub ! We'll draw 10*10 squares with the line instruction, and we'll use the value of E.x and E.y for coloring those ones :

Sub Render()
   
    '' Once again, we'll loop through the screen to render each square :
   
    Dim as integer x1,x2,y1,y2
    Dim as integer r,g,b
   
    For x as integer = 0 to SW/10
        For y as integer = 0 to SH/10
           
            x1 = x*10 : y1 = y*10    '' *10 To have the right screen coords
            x2 = x*10+10 : y2 = y*10+10   '' +10 because we draw a 10 pixels square
           
            '' Here is the graphic trick : we'll use the E values in the rgb(r,g,b) function
            '' You can play with these lines to totally change the effect :)
           
            r = 250*cos(E(x,y).x+E(x,y).y)
            g = 255*-cos(E(x,y).y-E(x,y).x)
            b = 60
           
            '' Why Cosines ? Why + for r and - for g ? Don't know ! I tried this
            '' and it looked awesome so I kept it ^^
           
            Line(x1,y1)-(x2,y2),rgb(r,g,b),bf
        Next
    Next
   
End sub

You're still here ? Cool ! It was two big subs ! Now, things will be much easier ! We have to write the Moving Sub ! Well, you could do as you please, but a random movement with screen clipping (don't forget we consider charges as dots so they can get out of the screen !) could be nice :

Sub Movement()
   
    '' Let's loop through the Charges Array
    For i as integer = 1 to MAX_CHARGES
       
        '' about 1/100 chances of change each frame
        If rnd<.01 then
            charge(i).V.x = -2+rnd*4 '' random direction
            charge(i).V.y = -2+rnd*4
        End if
       
        '' And to finish, let's add the velocity to the position
        '' to make movement :
        Charge(i).pos.x += charge(i).V.x
        Charge(i).pos.y += charge(i).V.y
       
        '' Clipping
        If Charge(i).pos.x>SW or Charge(i).pos.x<0 then
            Charge(i).pos.x -= charge(i).V.x
        End if
       
        If Charge(i).pos.y>SH or Charge(i).pos.y<0 then
            Charge(i).pos.y -= charge(i).V.y
        End if
       
    Next
   
End sub

Pretty simple, uh ? And it's almost finished ! We just need to init the charges position and velocity and put everything in a Do-Loop loop to make it work !

'' Charges Init :
For i as integer = 1 to MAX_CHARGES
    Charge(i).pos.x = rnd*SW
    Charge(i).pos.y = rnd*SH
    Charge(i).Sign = iif(rnd<.5,1,-1) '' equivalent to if rnd<.5 then Sign=1 else Sign=-1
   
    Charge(i).V.x = -2+rnd*4  '' Velocity between -2 and 2
    Charge(i).V.y = -2+rnd*4
Next

'' And finally, the main loop !

Screenres SW,SH,16,2

Do
    Screenlock : CLS
   
    CalcE
   
    Movement
   
    Render
   
    Screenunlock : Sleep 2
Loop until multikey(&h01)   '' Loop until ESC key is pressed

Done !! Now you can enjoy a superb weirdo' moving plasma ! I apologize if this tutorial is not clear or not accurate enough, but as I said, it's my first one and the subject was a bit difficult to approach in a tut' way ! Don't hesitate to contact me by mail or on the c0de/FB Forums

Further Ideas :


COMPLETE CODE :

Randomize Timer

Const SW = 640  '' Screen Resolution
Const SH = 480

Const PI = 3.1415926535897932384626  '' So much precision is not needed ...
Const Epsilon = 8.8541878*10^-12     '' This is the value of Epsilon in Void space

Const MAX_CHARGES = 3   '' We will use a maximum of 3 charges but you can do as you please !

Type Val2d       
    x as single
    y as single
End type

Type ChargeType
    Pos as Val2d     '' Position
    V as Val2d       '' Velocity
    Sign as integer  '' +1 or -1
End type

'' Array of charges
Dim shared as ChargeType Charge(MAX_CHARGES)

'' Array of Vectors to handle E
Dim shared as Val2d E(SW/10,SH/10)

Sub CalcE()
   
    '' Let's Dim some variables we'll need
   
    Dim as Val2d CurrentPosition
    Dim as Val2d Sum
    Dim as single ENorm
   
   
    '' First Loop (the first loop is in fact 2 loops : through x and through y
   
    For x as integer = 0 to SW/10
        For y as integer = 0 to SH/10
           
            CurrentPosition.x = x*10  '' We need to multiply per 10 as the loop stops at SW/10
            CurrentPosition.y = y*10  '' Idem
           
            Sum.x = 0  '' Init of the Sum
            Sum.y = 0
           
            '' Sum loop :
            For i as integer = 1 to MAX_CHARGES
               
                '' Here is the main calc ! It may seem a bit complicated but it's
                '' just the Sum part of the equation !
               
                Sum.x += (Charge(i).Sign * (CurrentPosition.x-Charge(i).pos.x)) / _
                (sqr((CurrentPosition.x-charge(i).pos.x)^2+(CurrentPosition.y-charge(i).pos.y)^2)^3)
               
                Sum.y += (Charge(i).Sign * (CurrentPosition.y-Charge(i).pos.y)) / _
                (sqr((CurrentPosition.x-charge(i).pos.x)^2+(CurrentPosition.y-charge(i).pos.y)^2)^3)
               
            Next
           
            '' Now we just have to multiply the sum per 1/(4*pi*epsilon)
            ''and we'll find E !
           
            E(x,y).x = Sum.x * (1/4*PI*Epsilon)
            E(x,y).y = Sum.y * (1/4*PI*Epsilon)
           
           
            '' And we divide E by its norm to have a value between 0 and 1 !
            ENorm = sqr(E(x,y).x^2+E(x,y).y^2)
           
            E(x,y).x/=ENorm
            E(x,y).y/=ENorm
           
        Next
    Next
   
    '' Done ! E is calculated !
End sub

Sub Render()
   
    '' Once again, we'll loop through the screen to render each square :
   
    Dim as integer x1,x2,y1,y2
    Dim as integer r,g,b
   
    For x as integer = 0 to SW/10
        For y as integer = 0 to SH/10
           
            x1 = x*10 : y1 = y*10    '' *10 To have the right screen coords
            x2 = x*10+10 : y2 = y*10+10   '' +10 because we draw a 10 pixels square
           
            '' Here is the graphic trick : we'll use the E values in the rgb(r,g,b) function
            '' You can play with these lines to totally change the effect :)
           
            r = 250*cos(E(x,y).x+E(x,y).y)
            g = 255*-cos(E(x,y).y-E(x,y).x)
            b = 60
           
            '' Why Cosines ? Why + for r and - for g ? Don't know ! I tried this
            '' and it looked awesome so I kept it ^^
           
            Line(x1,y1)-(x2,y2),rgb(r,g,b),bf
        Next
    Next
   
End sub

Sub Movement()
   
    '' Let's loop through the Charges Array
    For i as integer = 1 to MAX_CHARGES
       
        '' about 1/100 chances of change each frame
        If rnd<.01 then
            charge(i).V.x = -2+rnd*4 '' random direction
            charge(i).V.y = -2+rnd*4
        End if
       
        '' And to finish, let's add the velocity to the position
        '' to make movement :
        Charge(i).pos.x += charge(i).V.x
        Charge(i).pos.y += charge(i).V.y
       
        '' Clipping
        If Charge(i).pos.x>SW or Charge(i).pos.x<0 then
            Charge(i).pos.x -= charge(i).V.x
        End if
       
        If Charge(i).pos.y>SH or Charge(i).pos.y<0 then
            Charge(i).pos.y -= charge(i).V.y
        End if
       
    Next
   
End sub


'' Charges Init :
For i as integer = 1 to MAX_CHARGES
    Charge(i).pos.x = rnd*SW
    Charge(i).pos.y = rnd*SH
    Charge(i).Sign = iif(rnd<.5,1,-1) '' equivalent to if rnd<.5 then Sign=1 else Sign=-1
   
    Charge(i).V.x = -2+rnd*4  '' Velocity between -2 and 2
    Charge(i).V.y = -2+rnd*4
Next

'' And finally, the main loop !

Screenres SW,SH,16,2

Do
    Screenlock : CLS
   
    CalcE
   
    Movement
   
    Render
   
    Screenunlock : Sleep 2
Loop until multikey(&h01)   '' Loop until ESC key is pressed

Download a copy of this tutorial: Electric_Field_Tutorial.rar


Final Word

Whew! That was quite the issue.

After all that reading, you probably *need* a break before next month's QB Express!

But don't rest for too long, because we need SUBMISSIONS for the next issue -- tutorials, articles, editorials, game reviews, comics, "text comics" -- whatever you have that's QB / FB related -- we want it!

ISSUE #27 DEADLINE: April 1st, 2008.

As always, please email your submissions to qbexpress@gmail.com.

(And don't you guys go pulling any April Fools pranks on me either, heheh.)

Also, if you've written articles or tutorials for your own website, or posted them on forums throughout the community, we'd love to reprint your work. Just send us links to the article (and permission) at our email address.

Have a great month, and we'll see you again in April!

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.

QB Top 50 - The best QBasic and QuickBasic Sites    FreeBasic Top 50