About Design - Have we Talked too Much About Design? A great thing about having a tool like FreeBASIC around is how capable it is. I think when people begin coding in FreeBASIC, and also when they use FreeBASIC as a stepping stone to other languages like C++ and Java, they dream of achieving much larger goals than ever before. So large, in fact, that you'd have to be crazy to think one person achieve what they want to do in a timely manner. There are few people in the community that are good enough to pull it off, and if you ever talked to them about game design, they'll tell you a few things. One of the first things you'll hear is how long the project took. What are considered small or medium-sized games in general are very, very large projects for a one or two man team. Lynn's Legacy took years to complete, and cha0s was shooting for more than he could hit when he began coding it in QB. Syn9's a busy guy, but a capable one as well. If you take a look at projects, they look great. They were built around tight schedules, however. He knows that unless he wants to code something for years, the only way to move forward is to limit project size and scope. The other thing I've heard from experienced developers is in fact, design-related. There's different approaches to game development. Generally you're taking one approach or another, whether you know it or not. Bottom-Up has you going lower-level to higher-level. Top-Down has you going from higher-level concepts to lower-level code. I know from the few projects of mine that did kick off quickly, that if you build a library at the lower level thinking you'll need it later on, your project won't go anywhere. You'll have a hell of a time building layers of abstraction over that library to match the concept that's floating around your head. All the same, if you code at the very top level, you'll most-likely be coding something impractical if you're working on a large project. Coding a fairly large project becomes difficult when you try and have an advanced design process that's supposed to work out in the end, but you're not quite sure why it will. You always have to have some idea of what you're doing, and always try and code something useful. I'm going to advocate the idea of designing as you code. That is, coding at the highest level of code you can while still making something practical. Don't worry if you don't have the libraries to do this yet. Code something. Code it as if the functions already exist. However, make sure you try and avoid vague concepts and the use of libraries that can't exist because you don't know how they'll work. You want to evaluate all of your technical capabilities and design a piece of code which call functions and libraries you either have already coded, know you can code, or know you can learn how to code quickly. If you can't assure yourself of this capability, you either have to design your code without using your magic library, or drop down a level and design a test which uses it. Here's an example piece of "design code", in our first stage: ------------------- '' we're going to fire a block at a target, and the target is going to dodge it. dim as Entity ent(0 to 1) ent(0).isHuman = true '' position and size ent(0).x = 100 ent(0).y = 100 ent(0).w = 30 ent(0).h = 50 '''' speed '' move n pixels per second ent(0).moveSpeed = 400 '' fire a bullet every n seconds ent(0).fireSpeed = .4 '' set up our ai... it does nothing but dodge. ent(1).isHuman = false ent(1).x = 540 ent(1).y = 380 ent(1).w = 30 ent(1).h = 50 ent(1).moveSpeed = 200 ent(1).target = ent(0) '' init and run screenres 640, 480, 32 screenlock do '' loop through our entities and update them for doVar as integer = lBound(ent) to uBound(ent) ent(doVar).getInput() ent(doVar).updatePosition() next '' draw our entities for doVar as integer = lBound(ent) to uBound(ent) ent(doVar).draw() next '' flip our screen screenunlock sleep 15, 1 screenlock cls clock.flip() loop until multikey(fb.SC_ESCAPE) screenunlock ------------------- This is a piece of designed code. It's a test, which won't compile yet. We hope to use this exact or similar code will compile and become our 'main'. There is no real "design", as you may normally think it. We want to avoid such vague ideals. This is just a program outline. After coding our program outline, we do two things. The first is to attempt to make this code compile and actually do something. The second thing is to evaluate the technical challenges we currently face, and how the code can be made better. Sometimes this involves changing your objects and rewriting the test completely to adjust to the new "technologies" available. Remember, coding is an iterative process, as is just about anything else. A class design method I often use, when things get too complex, is to code the objects and methods without outlining their internals. Code the methods, use the internals, but don't code the internal private data first then design your methods around it. This is once again us trying to avoid outlining the internal implementation before the functional code. I highly recommend this when objects get too complex. Here's the final result of this particular piece of design code: ------------------- #include once "fbgfx.bi" #define false (0) #define true (not false) enum KeyInput LEFT = 01 RIGHT = 02 UP = 04 DOWN = 08 FIRE = 16 end enum '' our clock type Clock as double cur, last, dif declare sub flip() end type sub clock.flip() last = cur cur = timer dif = (cur - last) end sub '' our entity type Entity '' dimensions related stuff as double x, y, w, h, moveSpeed '' bullet related stuff as double bX, bY, lastFire, fireSpeed, bulletSpeed '' input and cpu as integer isHuman, inBit '' target and timer as Entity ptr target as Clock ptr clock declare sub getInput() declare sub update() declare sub draw() end type sub Entity.getInput() if( isHuman = false ) then exit sub inBit = 0 if( multikey(fb.SC_LEFT) ) then inBit or = KeyInput.LEFT end if if( multikey(fb.SC_RIGHT) ) then inBit or = KeyInput.RIGHT end if if( multikey(fb.SC_UP) ) then inBit or = KeyInput.UP end if if( multikey(fb.SC_DOWN) ) then inBit or = KeyInput.DOWN end if if( multikey(fb.SC_ENTER) ) then inBit or = KeyInput.FIRE end if end sub sub Entity.update() select case isHuman case true '' move our player if( inBit and KeyInput.LEFT ) then x -= moveSpeed * clock->dif end if if( inBit and KeyInput.RIGHT ) then x += moveSpeed * clock->dif end if if( inBit and KeyInput.UP ) then y -= moveSpeed * clock->dif end if if( inBit and KeyInput.DOWN ) then y += moveSpeed * clock->dif end if '' fire a bullet if( inBit and KeyInput.FIRE ) then if( timer - lastFire > fireSpeed ) then bX = x bY = y lastFire = timer end if end if case false '' dodge a bullet if it's too close if( abs( target->bY - y ) < 100 ) then '' find the fastest direction to dodge at if( target->bY + 19 < y + h / 2 ) then y += moveSpeed * clock->dif else y -= moveSpeed * clock->dif end if end if end select '' move our bullet bX += bulletSpeed * clock->dif end sub sub Entity.draw() '' draw us line( x, y ) - step (w - 1, h - 1), rgb(255, 255, 255), bf '' draw our bullet if( isHuman = true ) then circle (bX, bY), 20, rgb(255, 128, 0),,,,f end if end sub '' we're going to fire a block at a target, and the target is going to dodge it. dim as Clock myClock dim as Entity ent(0 to 1) ent(0).isHuman = true '' position and size ent(0).x = 100 ent(0).y = 100 ent(0).w = 30 ent(0).h = 50 '''' speed '' move n pixels per second ent(0).moveSpeed = 400 '' fire a bullet every n seconds ent(0).fireSpeed = .4 ent(0).bulletSpeed = 800 ent(0).clock = @myClock '' set up our ai... it does nothing but dodge. ent(1).isHuman = false ent(1).x = 540 ent(1).y = 380 ent(1).w = 30 ent(1).h = 50 ent(1).moveSpeed = 200 ent(1).target = @ent(0) ent(1).clock = @myClock '' init and run screenres 640, 480, 32 screenlock do '' loop through our entities and update them for doVar as integer = lBound(ent) to uBound(ent) ent(doVar).getInput() ent(doVar).update() next '' draw our entities for doVar as integer = lBound(ent) to uBound(ent) ent(doVar).draw() next '' print some stuff to the screen locate 1, 1 print "Hit enter to fire a bullet." '' flip our screen screenunlock sleep 15, 1 screenlock cls myClock.flip() loop until multikey(fb.SC_ESCAPE) screenunlock ------------------- Quick and easy. Demonstrates the concept at least. Now to recognize several technical challenges. - It would have been much easier to use a 2D Vector class to handle the positions and sizes, as well as help us do some distance math and possibly enhance bullet paths. - The inBit seems like a waste, so I'll probably get rid of that for now, but it might be useful to have some abstraction between the internals of how keys are detected and what actions are taken later on - ex: if we want to change the behavior that pressing left takes. - Bullet should probably become its own separate object later on as to avoid saying bullets have to be a part of entities, and allow Bullets to become more advanced. - Finally, multiple bullets are difficult at the moment. This is truly a technical challenge. I didn't want to code a big thing to manage bullets, when they die, how they die, and maintaining a list of them for such a small example... The important thing here is that we actually got something done. Stop thinking so much about design and start thinking about development. I do have a solution to those of you who want to base your design process on a professional model. It's called Research and Development. We're hobbyist game developers. We're usually working alone. Programming is an iterative process and games have a lot of different concepts, each of them needing testing at one point or another. The most important part of a game is the game play, so test it. The basic idea of Research and Development is what keeps large companies alive, and it's what keeps small developers able to write practical code. Don't let the capabilities and possibilities of modern computing overwhelm you. Write tests first. Tests of some kind or another. Large companies may write something close to regression tests first, to have an outline as to how an object should work in code. Small developers are going to have game play tests. One test for how bullets move. Another test for how input is gotten and how the characters will move. Collision tests, level generation tests, AI tests, you name it. These can all be separate objects, quickly developed, with the sole purpose of helping you recognize technical challenges so that *later on* you can develop what may eventually become the final release code. I've found tremendous success in this way of thinking, and this way of designing. I've never had more fun during development than I am now. Concept Tests may seem like a waste at first, but think about it - Game play's the eventual goal, and right here in the beginning, you already have tests showing you what it will look like. If anything, you should feel like you're cheating the game, not losing it. So long, Pritchard