Mindbleach
Members-
Posts
10 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Gallery
Events
Store
Community Map
Everything posted by Mindbleach
-
Need help creating Tower Toppler's Scrolling Effect!
Mindbleach replied to AtariHacker's topic in Atari 2600 Programming
Hello and welcome to severe limitations, AtariHacker. Being a sucker for "how could I do X?" concepts, I remembered that horribly frustrating pig puzzle from a Let's Play and gave it a shot. Here, and this is the only forum I would ever say this in, is a dump of my Paint workspace. Forgive the colors. Initially I tried to approach it with the tower as a static, reflected background, but the double-mortar looked ugly and the little brick in the middle of the workaround looked uglier. The best-looking brick pattern I had was one intended for the turning animation, so I went with that. The sprites and missiles combined couldn't handle the platforms, so I made them part of the background, interrupting the brick pattern without ruining the tower look. Adjacent platform segments could appear solid or have gaps, purely for aesthetics. The tower is drawn in stretches of brick punctuated by spaces that could be filled with platforms. The brick is one of a few patterns, with gaps representing doors. Those gaps could be placed after the background bytes are shifted into RAM or hardcoded for all possibilities, but the latter might end up being more complicated. The platforms take up seven positions onscreen - you can just add the byte patterns for each together, since there's no overlap and thus no carrying. Since the ball would be the same color as one of the background colors and this game's background is very busy, the player gets an honest-to-god sprite, leaving two missiles and a sprite to form the enemies who will harass him. There's no flicker, which is unusual for me, but the background is asymmetric and vertically dense. You might be able to do the doors just-in-time if the 2600 has a bitwise XOR command. Animating the camera's rotation around the tower is technically possible, but it would be a pain to get all the bricks and platforms looking right in that 40-pixel grid, not to mention the joys of smoothly moving the enemies relative to the player. Personally, I'd stick with discrete rotations that jump from one platform to the next without any smooth animation. Anyway, best of luck with this - it's a good effect and I think it's possible. If you have experience torturing the 6502 to your own ends, you should be fine. -
I think you're overshooting what the 2600's capable of. Floorcasting is not a pretty business, so unless you've got some very nice algorithms I'm unfamiliar with, that and your sprite worries could kill any chance of a workable framerate. In terms of RAM - if we're going to cheat like engineers and say "just add hardware," Why not go the full nine yards and do texturing? It's just one-dimensional stretching, and it needn't be pixel-perfect so long as the wall's the right height. Perhaps a more Wolf3D-like use of wall shading? I know that game did it with separate textures, but here we could make north-south bright and east-west walls dark for clarity... not that your map system has anything like that implicitly. Maybe we should consider this a Supercharger game... That hanger picture is sexy, by the way - throw in some proper multicolor sprites and this could be a high-speed 7800 game. I'm going to keep thinking of this as a vanilla 2600 game. Bankswitching, sure, but not more than 16K.
-
I've taken your call for a reality check to heart. Here's the basic outline for a naive, ortho, single-height raycasting kernel: This is somewhat simplified from the version I have on paper, which takes corners & floor/ceiling heights into account. It's a bit more high-level than it should be, lacking such details as incrementing the column number after finding and writing the wall height for a given column. Every line that starts with "get" involves some sort of table lookup or nontrivial math, but each happens only once per ray. Most of the CPU drain comes from stepping and checking. Each step is two or four additions, depending on whether the X & Y coords of the player take up one or two bytes each, and each check requires some minor conversion (e.g. bitmasking as a modulus) before reading part of a byte from ROM. Let's we assume we are very bad at optimizing and give ourselves figures of 100 operations overhead and 50 operations per step. There's just over 5000 cycles in a normal VBLANK, plus whatever we add in by turning off the screen early. We need at least 7 columns per VBLANK to make 10 FPS. Division says that' just over 700 operations available per column. Subtract 100, divide by 50, get a maximum draw distance of 12 unit steps. The maps to be used are perhaps 32 units square, so that's not terrible despite ample room for improvement. Please shoot me down if I've overestimated the 2600's ability to add numbers, apply bitwise logic, and look up values in ROM, but I'm feeling strangely optimistic. If the screen-drawing algorithm isn't overzealous, this crazy scheme just might work.
-
Ahhhh. That's actually quite nice. Using points instead of blocks is a lot more flexible, and the way you've done non-ortho walls is orders of magnitude better than the terrible methods I've been thinking up for it. I think I'd still use a hybrid method, filling 20 or 40 bytes with per-column data instead of trying to unify the 'modulated' line. Find the points in your field of view and in range, construct lines, and then build the columns based on the highest (i.e., closest) line going through it. The problem I see with finding and drawing from the modulated line directly is that partially obscured walls need to be detected and dealt with to avoid having the line jump around. I've taken your step 2 and marked where the points are: The ones offscreen - outside the field of vision - have to be found and accounted for. The ones 'inside' the wall can throw the final drawing algorithm if they're not recognized as part of a seperate wall section. If those problems can be dealt with reliably, this algorithm may be more efficient than a straight raycaster. The only potential snag I see is that complex rooms in front of you will take a long time to sort out even if your face is pressed up against a wall and all you see is solid brown. With rays you have consistently mediocre performance, but points could vary wildly without conservative level design. Not to one-up you, but I've been sitting on an idea for a few days because it's absurdly high-end. Maybe a 5200 or Supercharger idea, just for the RAM involved. I tried to go past what the 2600 could do, and figured it could fake the walls for variable-height raycasting as a reasonable extension of my method (or yours, now), but floors would be completely beyond it. Even in Doom and Rise of the Triad, floorcasting is the least efficient part of the engine, and once you have it you have to store it. Floors & ceilings can't be done per-column or per-scanline with any sort of quality, and using huge pattern blocks was a hack not worth its limitations. In comes the ball. You can't texture or color the floors, but it's not like you're texturing the walls - just a hint indicating the presence of a wall is enough. The idea is explored in the middle six shots. Top pair: wire & filled walls with full lines indicating ceiling/floor jumps. Second pair: 'sketch' edges indicating ceiling/floor jumps. Third pair: dashed lines indicating ceiling/floor jumps. I'm fond of the third method because it's constrained; there is a definite maximum number of 'edge hint' points to be drawn, and they can be handled every few scanlines. Also note that the last two methods have occlusion, so the walls don't show through the floor or ceiling. The set of images on the far right shows an example conversion, including the simulated effect of distance-based flickering for darker depths. The final image (not the white one, but the red above it) represents about 80 bytes of information. Every column starts and ends at some value from 0 to 63 (doubled pixels), requiring two bytes for each of 40 columns. The edges are stored as four-bit horizontal positions every fourth frame, taking the unused high bits of the first 32 column start-end byte pairs. Drawing is a matter of turning a column on between its two high/low bytes, plus the placement of a single horizontally mark on every fourth scanline. Occlusion is done per-column, clipping the far walls by finding the height of near ceilings and floors. I'm not actually sure what should happen if two edges want to use the edge marker on a given scanline. It's academic, really, since the Atari barely has enough memory to store the absolute necessities, let alone the CPU power to make the game worth playing. Anyway, it's really late and I'm incredibly tired. Thank you for explaining your method visually, it helped immensely. It may be the key to good performance if any of this ever ends up as more than idle banter. Edit: the example picture is wrong. There's no way this engine could do a wall over a hallway, as in the bottom right of the screen. The equivalents of Doom's High and Low texture spaces would be black, with only ceiling-to-floor walls and the hints of edges. When casting a ray, you keep the first floor & ceiling height change as your visible edge, the highest floor and lowest ceiling as your occlusion limits, and the first wall encountered as the stopping point. I'll draw proper examples with maps tomorrow.
-
Playfield pixel height is shown as 1 in the examples only because there's space for it. If the 3D display area is 192x128, the most detail you could possibly put into wall height is just 64 pixels, so you get two flag bits for free. Doubling up so you only have to play with the BG every other line is probably a very good idea. Assuming I understand what you mean with modulation, I like the idea, but I'm unsure about the horizontal resolution being enough for what I think you have in mind. Let me play it back and see if I received correctly. You want to store the wall data as a series of lines, filling everything in from the top of the wall to the middle of the screen (and vice-versa, though we'll take that to be assumed from here on out). To make these straight lines more interesting, you want to apply a sort of filter that will fiddle with the height of any given wall, making straight lines jagged, wavy, square-waved, etc. This could look very good if used properly, and would in fact be a nice alternative to texturing. However I worry about its effects on distant and therefore small walls - little details would get blown out, and short walls might appear to grow and shrink when you turn. Perhaps you're suggesting column end-caps with finer horizontal detail than the 40 columns of the background? I don't think that's possible without some serious ball abuse, and it would flicker like nobody's business. Or maybe I've read it completely wrong, and you're thinking of a texturing of sorts... where every column has a few bits controlling the halves or quarters of the wall. Kind of like the window idea, but also allowing thick striping, tall doors, high & low windows, or anything blocky pattern that's vertically symmetrical. If so, even better. An adaptation of this would be to repeat some number of bits as a one-dimensional texture down the length of the column. That way you could do finer details, but the effect in motion would be strange. The whole world would be textured like that salesman's jacket in Monkey Island... he moves, but the plaid doesn't. However you mean, I'm left with the impression that I explained myself poorly with regards to storage. The screens shown use only 40 bytes to hold the walls, flags included. "Only" anything is a stretch with 128 bytes to your name, but those bytes and 6 bytes of scrap space are all that's needed to draw the walls once the rays to find them are cast. Except... oops, I missed something. You can only call it 40 bytes if you update your rays in-place, so the new wall heights sort of slide across the screen every tenth of a second. A tolerable compromise if your framerate's good. If you can update one ray/column during each VBLANK, it would take 40 VBLANKs to update the whole scene... meaning our framerate's nearly unplayable unless we can eke out at least seven rays per VBLANK. This is why I was worried about raycasting as the bottleneck. We do a lot of it. If 40 bytes is really too much, making playfield pixels four lines tall gives you 16 possible wall heights. That's hex, so you can double up the columns into 20 bytes and worry about windows and other effects elsewhere. When I mentioned oddly shaped rooms, I wasn't thinking of visual hacks, I was thinking of actually building the level out of geometric shapes. It's probably excessive, especially if the static level data can stay in ROM.
-
I'm not sure on framerate. I don't actually have any non-Batari experience getting code onto an emulator, though that may change after I put some time into the Warning Forever-inspired shooter I've been doodling about. Anything above 10 would be playable; over 15 would be incredible. The bottleneck is in casting rays - stepping through the map in 40 directions. Getting the direction is a hack, stepping through is a hack, and god knows the map will have to be a hack of some sort. It would probably be easier to store a level as a series of lines than as a two-dimensional array... and I should be worried that I'm now thinking about circular rooms and non-ortho walls. Ooh, I think I just solved a problem I had with non-turned, wall-based 3D like this: I know how to do do depth shading. If every logical frame lasts multiple actual frames (persistent through multiple VBLANKs, sprite flickering aside), make the background black and flicker the shorter columns. So if you work at 15 FPS, tall (near) columns are solid, but columns less than 66% of full-height are ignored on every fourth frame, and less than 33% are ignored every other frame... distant walls appear darker, blending with the background. This could become problematic with flickering sprites, but might be an acceptable effect with properly dim walls and bright objects. Anyway. don't worry about tending to reality before this lark. This subforum isn't exactly overwhelmed with traffic, and I took a long hiatus from it without your initial, inspiring post sliding off into oblivion. The content will still be here whenever you've got time. Frankly, it was a pleasant surprise to see such a quick reply.
-
Alright, here's a half-decent mockup of what the high end could look like for this wall-based raycaster idea. This is a straight dump of the file I've been toying with in MSPaint, so forgive the repetition and random junk all around. The walls are all single-color per scanline and symmetric about the vertical midpoint. Since you're definitely not going to have 256 possible wall heights, there are some options for the one-dimensional array holding the values. One: halve the storage space needed by putting the left half into the lower nibble and the right half into the upper nibble. This limits the engine to 16 wall heights. This is only really useful if you absolutely need a few more free bytes during VBLANK, since the values will have to be bitshifted or booleaned into full bytes before you can use them. Not a great idea. Two: limit the engine to 64 or 32 wall heights and use the upper 2/3 bits for per-line or per-frame effect flags. 00 for normal (solid), 01 for even lines only, 10 for even frames only, and 11 for windows. Windows are solid below their height value, but clear below half their height value - just decoration. Maybe "doors" is a more appropriate term, if they're to be used for something. The per-line effect is shown here, marking what was a door in the Wolf3D screenshot I drew over. Since drawing is symmetrical, it's better to think of the upper and lower halves of the screen as separate routines, or at least to think of the vertical midpoint as the zero. Objects clinging to the ceiling will stop at or above the centerline, so you don't have to consider them for the lower half. Vice-versa for floor objects like that barrel. Tall objects like the guy about to shoot you can be divided, literally or rhetorically, into a ceiling object and a floor object. The horizontal positions of both sprites have to be set only twice per frame: once at the top, and once at the midpoint. The rest is managing which bytes of sprite data to display and what color to make them. In short, you could display four tall objects or eight half-screen objects with 30Hz flicker - and only worry about clock cycle timing on two lines. The sprites near the top show example scaling. The one marked with an X is 150% tall, and the one-two tracking necessary for it probably isn't worth the hassle. The rest are hardware-scaled to 2x or 4x wide and vertically scaled by an integer multiplier.
-
Never seen wireframe on the 2600, but with a good line-drawing algorithm, you could store only the transformed vertices and calculate some number of wires per scanline. Bresenham's is a good example, since it's designed to be absurdly fast, but it'll work best here if you're drawing mostly vertical lines. You have some number of lines - let's say five, one for each player/object and one for the ball, all against a black background - and they can move one pixel left or right every scanline. Actually, since you can stretch them 2 or 4 times, there's more horizontally-oriented lines you could describe without breaks or background-based hacks. Bear in mind this is all per-pixel and could use the full 192x168 resolution, so long as you don't need to any vectors outside that magic range of ~210-330 degrees. I think I see how Ben's doing it, and it's entirely possible to get walls without turning the screen. For however many columns you have, you store an integer representing the height of the wall filling that column. Wherever you start drawing your walls, initialize a counter to your maximum wall height, and for every scanline from there until mid-screen, you decrement the counter and turn any columns with height equal to or greater than the counter from BG0 to BG1. Okay, so it's not really per-scanline, because you won't have that much precision, but you decrement and check every N scanlines and it's the same idea. From mid-screen to wherever you stop drawing the walls, do it backward - increment the counter from 0 to the max height, and turn off any columns that aren't in range anymore. The included example illustration is backwards from the description, but the idea's the same. Perspective correction can be done before any of the columns are drawn, since you can tweak the heights of columns by adjusting a single row of numbers. If you wanted to, you could throw in some odd special effects by playing with the column height vector - sinusoidal finagling for drunkenness, uniform scaling for fading in/out, capping with a BG color change for the appearance of closing doors, etc. As an added bonus, this method leaves all sprites untouched and only needs to really fiddle with things every 8th scanline or so.
-
The fullscreen 3D look was intended to cut down on the need for flickering and put a realistic, concrete limit on draw distance, but I can understand the desire for a few hundred extra cycles every frame with as many sprite objects as you want. I haven't seen true Mode 7 implemented on a GBC, by the way, and I'd very much like to see the demo you mentioned in the first post. I've seen some great fake 3D by Phantasy and Tiertex, plus the proof-of-concept raycaster in Back From Earth, but not the iconic planecasting effect. A note on that, by the way: you can shrink a whole lookup table to half as many entries as you have pixels to render. 40 pixels wide for 90 degrees FOV? Then store rays for all 360 degrees, preferably as a strided list of shrinking concentric circles, and rendering the playfield in any direction is a matter of choosing 40 entries in each row of the lookup table. Obviously you don't need all 360 degrees, since a circle is concentric about both axes - subtract x & y values where necessary, and you need only store 90 degrees. It's also concentric about x=y, so you can halve that to 45 degrees if you can elegantly swap x & y values as needed. You could have a convincing 3D plane with just over 1000 entries - still not a slim structure to store, but there's room to shave off more with cheap bitshifting multiplication & division.
-
Love the topic. Seeing hardware pushed to its limits and beyond is a personal interest of mine. Here's a mockup for an alternative take, really a few ideas pushed into a single image. First is the perspective change - basically, eliminate the background. This makes sprite design more challenging and requires you to draw more lines of faux 3D, but it does limit your view distance. Additionally, since it's halfway to overhead, there's fewer sprites per scanline and thus less need for flickering. Shown here is a tank (the player) turning and firing his guns, hitting one target and moments away from being hit by another. Bullets will mostly be coming at you or from you, again making per-scanline tweaking with the balls and missile possible. You mentioned floor-based 3D as opposed to wall-based 3D, and I felt that a grid was appropriate for that. The grid is accomplished with the foreground color (the player's shots don't match, my mistake), changed like the background color according to a table. Seen here, the changes are staggered ground-line-ground-line to suggest a finer gradient than the 2600's palette really allows. The water idea was too good to eschew, though, so it stays in as an interlaced effect. Additional uses for colored patches of ground with different effects: sand that slows you down, rocks that stop you cold, black minefields that occasionally blow you up, and green zones that repair you. The gradient effect could be further enhanced by using a different hue for these alternate scanlines. This inaccurate mockup is based only on the 2600's drawing limitations, not on its ability to compute and store the information needed to draw it.
