[futurebasic] Re: [FB] Gworld Newbie

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : August 1999 : Group Archive : Group : All Groups

From: BMichael@...
Date: Tue, 3 Aug 1999 23:59:36 EDT
First of all, Phil's demo is definitely the way to start on this! I 
thought however since this was becoming a "general discussion" of gWorlds 
and such, I'd throw in a conversation from another list, just last 
week... especially since it sounds like the guy below is trying to do in 
C (for a "level builder" in a game) almost exactly what we're talking 
about doing here in FB. I'm not sure how to "indent" all of this, as it 
spans several messages, but here's my best attempt.

Bill

---------------------------

Subject:     Re:  L-07 scrolling pictures addendum
Sent:        7/26/99 6:09 AM
To:          MOST-MacOS-Core, most-macos-core@...

>while(StillDown())
>{
>  GetMouse(&mouseXY);
>  clickedRectXY = doConvertMouseToRectXY(mouseXY);
>
>  //draw the correct tile in its position
>  doDrawTile(windowPtr,tileNumber);
>}
>SetOrigin(0,0);
>............
>
>However, the problem is fixed if I replace the call to doDrawTile() with a
>call to doDrawLevelWindow() (which calls doDrawTile() once for each tile in
>the window). This proves that my calls to SetOrigin() aren't the problem. I
>don't want to be drawing the entire window for each tile if I can help it,
>obviously.

I can't address the SetOrigin issue, but I don't see anything wrong with 
that part. However...

Rule #1: Do _all_ of your drawing _only_ on an Update event. In other 
words, you should keep a list of tile locations on the screen, and when 
you get an update, call doDrawTile once for each tile that intersects the 
invalid region. In your routine above, doDrawTile should be replaced with 
InvalRect on the location of that tile. Likewise, scrolling should just 
add to the invalid region any window area revealed by the scroll; the 
next clock cycle will generate an update event, and you would then 
doDrawTile for any "newly exposed" tiles. (Any updating outside of an 
update event results in either duplicated updates - flicker - or, if you 
don't _also_ draw in the update handler, covering part of your window 
with another application will leave blank areas behind.)

You said that this While(StillDown()) was inside your event handler for a 
mouseDown; you're thus calling SetOrigin and doDrawTile repeatedly as 
fast as the processor can, until the user lets go of the mouse button. Is 
this so they can drag across the window, updating tiles as they go, or is 
the goal just to

>Yes. I don't want to have to make people click just one tile at a time.

refresh the tile they clicked on? If the latter, it'd be better to do 
nothing during StillDown, and make your call to InvalRect (passing the 
tile coordinates under the mouse) when they let up on the button. (If the 
click changes the tile, you'd also call your tile calculation routines on 
mouseup, just before the InvalRect.) If you want them to be able to drag, 
I'd only "force" the update when they either enter or leave a tile. (A 
case where you would update outside the update event, thus proving that 
rules are made to be broken...)

>What do you mean by "forcing" an update? Just call my screen redraw function
>from the tile-clicking function instead of my update handler?
>>
>>Same spot you're currently having problems with, but instead of 
>>continuously while the mouse is down, only once every time the mouse 
>>"enters" or "leaves" a tile. Let the update event handle all _other_ 
>>refreshing.

Another suggestion, that you may already be doing; keep your entire 
"world" in an offscreen gWorld ("visible" and "scrolled off" alike), and 
use CopyBits to refresh the window. Unless the window is very large, or 
there is a lot of animated activity going on, you may find that it's 
easier to refresh the whole window in one CopyBits call any time any of 
it becomes invalid, and not worry about keeping track of individual 
invalidated tiles. For any display where you aren't concerned about 
animation frame rates, CopyBits is fast enough that it is often faster to 
refresh the whole thing than it is to calculate the part that should be 
refreshed.

Bill

--------------

>I had been considering this, but I thought that there was a limit to the
>size of an offscreen. Does anyone know what this limit is? I sort of want my
>levels to be pretty big, so I don't want to be held back by offscreens if
>that's what they'll do.

The main issue is how fast they eat memory. I'm sure there _is_ a size 
limit, but I've sure never hit it! (Run out of RAM first.) I'd say "four 
times the size of your window" is easily doable, more than that probable. 
Regardless, you'll be glad you did it later, even if you have to resort 
to some "tricks" to handle levels bigger than the gWorld.

Here's one example of how I'd do it... Assume a level that's 10000x10000, 
an arbitrarily chosen gWorld size of 2000x2000, and a window that's 
500x500. (All chosen for simplicity.) The level starts out with all tiles 
red; clicking them turns them blue. (Real simple level builder, huh?) On 
launch, I'd "draw" the upper-left 2000x2000 corner of your level into the 
gWorld, open the window, and CopyBits the upper-left 500x500 corner of 
the gWorld to the window.

The user clicks on the upper-left tile and releases the mouse, wanting it 
to change from red to blue. You detect the click, figure out that it 
between (0,0) to (31,31) in the current window, which was on tile (1,1). 
You redraw tile (1,1) IN THE GWORLD, and post an InvalRect for 
(0,0)-(31,31). On the next update event, your update handler sees that it 
needs to refresh the screen, and copies the entire 500x500 upper-left 
corner to the screen again; however, since you changed the gWorld to have 
one blue square, the window changes likewise.

Now, the user clicks tile (1,2) and drags the mouse. As soon as the mouse 
_down_ event is received, we immediately call the routine that turns tile 
(1,2) red in the gWorld, _and_ we call our update routine; again, the 
whole 500x500 gets copied, but the user just sees that the tile they 
clicked changes. They continue to drag the mouse (we're watching) and 
they pass the edge of tile (1,2) as they move. They're now in tile (1,3), 
and we repeat our "change" then "update" calls. We keep watching until 
they release the mouse, changing tiles.

Now they hit the scroll bar arrow... to scroll more tiles into view. The 
_only_ thing that changes is the origin in the gWorld... we now are 
calling our "upper-left" corner of the window (0,1) in the gWorld, and 
every time the thumb has advanced a pixel, we copyBits the whole 500x500 
window again, but it's 'shifting' one pixel at a time. As long as they 
don't shift 1500 pixels, we don't have to worry about changing our gWorld.

They get tired of scrolling so slow, and click in the grey region of the 
scroll bar, which jumps them one tile at a time. This just means that we 
change our offset by 32 instead of by 1. The code remains the same. When 
we get to the point that (offset + windowWidth) > (width of gWorld), then 
we have to do some special handling. Namely, we copybits the right-hand 
half of the gWorld to the left-hand half of the gWorld, and we call our 
"draw" routine for all the tiles that have been exposed. We also change 
our origin by _subtracting_ 1000 (half the width of the gWorld) from it. 
(Given the numbers I used, we kick into this mode when the origin hit 
1500; we set it to 500, which means they can now scroll left a full 
window width, or right two more, before running out of gWorld again.)

They stop scrolling around and switch to EMailer to answer some C 
questions, then switch back to our level editor; EMailer covered the 
window, so we get an update event, and make one CopyBits call to 
completely refresh our window. No sweat.

The advantages of doing it this way are that you don't _care_ what part 
of the window is invalidated; _any_ update event refreshes the whole 
thing from the gWorld, which is where you keep (safe from any harm) your 
set of tiles. Your "draw tile" routine (the thing that makes one red or 
blue in the example) can be as slow as you like, within reason, because 
you only call it umpteen times at startup, once per "changed tile", and 
1/2 of umpteen times when they've scrolled beyond the boundaries of the 
gWorld. (Right now, you're calling it once per clock tick, as long as the 
mouse is down.)

I don't have any C code demonstrating all of this, unfortunately; just a 
well-thumbed copy of "Tricks of the Mac Game Programming Gurus". :-) I've 
done animated stuff, but always with a fixed window, no scroll bars. I'd 
suggest writing it with red/blue tiles for simplicity first, then adding 
later all your different tile types, tools, etc...

Good luck!

Bill