The Mezunian

Die Positivität ist das Opium des Volkes, aber der Spott ist das Opium der Verrückten

Let’s Code a Crappy 2D Platformer Like Millions o’ Other People on the Internet & Lose Interest & Give Up Only a Few Months In, Part IV

It’s been so long (a couple days) that I forgot what I’d accomplished last time I wrote.

Block Types

’Stead o’ relying on dumb enums, block types now have 2 corresponding lists: “components” & “conditions”—numerous simple pieces that can be combined into complex block behavior. Components affect what the block does, the corresponding conditions affect what must be true for that affect to happen. Take, for instance, a heart block, which 1, heals, & 2, disappears, both on the 2 conditions that they are touched & touched by the hero sprite. Contrast this with a heart block, which is, 1, solid, & 2, heals; but whereas the 1st action happens simply by being touched, the latter only happens ’pon colliding with it from the bottom.

This allows me to reuse simple behavior oft used in different ways—touching a block, solidity, hitting it from below, disappearing ’pon 1 use—without having to copy & paste too much.

Also, as the “Wasabi Woods” screenshot shows, I added a “priority” flag to graphics, which specify which parts o’ blocks should be drawn o’er the player. This is done simply by drawing blocks onto the screen before & after sprites, drawing those without the priority flag 1st & those with it on afterward.

Level-Select Screen

Level names are gotten from some dumb function in the Level class that spits out a string given a certain level ID (which matches the ID that one goes to ’pon selecting a string) based on a super dumb switch statement. Sorry.

’Course, the reason I’d want a level-select screen is that I’ve added 3 mo’ “levels.” I put that word in quotation marks, since none o’ them are truly finished. Technically they’re all beatable, but only Wasabi Woods is a serious level—& e’en it’s missing sprites. It’s probably next to be finished. Meanwhile, “Skyscraper Caper” is halfway done, & “Soupy Sewers” isn’t e’en a real level, but a way I planned to test vertical scrolling for backgrounds, which I haven’t e’en gotten to yet—in fact, that level, ironically, is the only 1 without a background image.

Look @ that amazing cut-off. This’ll ne’er be accepted @ SMW Central.

Speaking o’ which…

Background Images

As “Wasabi Woods,” shows, multiple background images can be layered. They automatically tile horizontally & can have parallax scrolling o’ multiple speed levels, which “Wasabi Woods” uses for its 2 backgrounds. This was a pain, as I kept getting the math wrong; but after failing to sleep on Christmas Eve night, I was struck by inspiration. Unfortunately, I can’t actually remember what the math flaw was.

Here’s the code:


void Background::render( Graphics& graphics, Camera& camera )
{
	if ( texture_ >= 0 && texture_ < Graphics::SpriteSheet::LIMIT )
	{
		int sl = offset_x_ + ( (int)( camera.x() * scroll_speed_x_ ) % width_ );
		int dl = 0;
		int right = std::min( width_ - sl, camera.widthPixels() );
		int far_right = 0;

		SDL_Rect dest = { dl, 0, right, camera.heightPixels() };
		SDL_Rect source = { sl, 0, right, camera.heightPixels() };

		graphics.renderObject( texture_, source, dest );

		while ( far_right < camera.widthPixels() )
		{
			far_right += right;
			dl += right;
			right = std::min( width_, camera.widthPixels() - dl );

			dest = { dl, 0, right, camera.heightPixels() };
			source = { 0, 0, right, camera.heightPixels() };

			graphics.renderObject( texture_, source, dest );
		}

	}
};

The vital issue is the difference ’tween the source & destination, & how these 2 differences affect how things are drawn. The destination determines where the graphic is drawn, the source from where in the graphical file. Thus, we start by looking for how the left size o’ the source, “sx” here, should change. We know for a fact that we want the background to span from the far left side to the far right side, regardless o’ where one is in the level; the question is what part o’ the image is on that far left side. You’ll notice that the 2 right variables have no mention o’ source & destination; & as you’ll notice below, it’s ’cause they both turn out to have the same right values, which is either the end o’ the graphical file (the width o’ the graphical file – where we started, sx), or the end o’ the level width (the width o’ the screen – where we’re starting, which in this case is 0, thus making it just the width o’ the screen), depending on which is smaller. There’s no reason to go past the width o’ the screen, & to draw a wider destination than source width will stretch the graphic, which is hideous. If we hit the latter, then we can stop, since there’s no mo’ that needs to be drawn; but if we hit the former, then we go through this while procedure till we finally do, saving our building right variable in the “far_right”1 variable, while placing the right variable o’ the previous cycle as the current left—logically, we start out where we left off. Obviously we’re drawing just after where we’d drawn before, since we’re building the illusion o’ a continuous backdrop; meanwhile, we always start our source now @ 0; as mentioned, we stop @ either the end o’ the source, @ which point we want to restart @ 0, or we stop @ the end o’ the screen, @ which point we quit. Meanwhile, we get our new right based on the same as before,—either the end o’ the source or the end o’ the screen, depending on which is smaller—& continue the cycle till the far right = the screen width2, @ which point we’ve reached the end o’ the screen.

When I was 1st figuring this out, I drew a graph to help see this—though now it’s probably e’en mo’ incomprehensible than the word vomit I spewed earlier.

@ the top you’ll see my plans for sprite collision detection, & to the left, the edge o’ some dumb haiku.

I still haven’t finished doing this vertically. Hopefully, that won’t be too complicated to add: should just be a copy o’ the horizontal version, just used with y & height variables & an extra loop layer.

Funds

A funds total, which shows on the level-select screen & is increased by whatever money you have ’pon beating a level.

Speaking o’ funds, they’ve been changed from just “gems” in the inventory to ₧, which fits better with Boskeopolis’s world & fits better with the video-game parody aspect o’ that world’s currency, & each regular gem gives 100, with a brighter & darker gem added, which give 250 & 500, respectively (thanks to the new block component system, I only had to change a # plugged into the component added to each block). Using these, I revamped the 1st level, increasing the money requirement to 10,000 (I also added an auto-comma function for #s converted into text, which is used in the message that appears in front o’ the 1st level, regardless o’ how one changes the funds requirement).

I also added a nifty li’l detail wherein the ₧ counter in the inventory spins up ’pon gaining ₧, rather than automatically jumping up to its new value, using a technique from Super Mario 64 that this video shows off, wherein one separates the actual funds # & the funds shown on the inventory screen, & have the game check every frame if the funds shown is less than actual funds & have it increase by a small # if so. The 1 difference is that my game doesn’t have the glitches & o’ersights Super Mario 64 does, ’cause I focus mo’ on that anal shit mo’ than actually making mo’ than 1 complete level.

Scrolling Marquee

This is the kind o’ useless trinket that keeps me from making real levels. Now, in every level, @ the bottom o’ the UI scrolls random strings o’ text. They’re inspired by the news tickers in SimCity 30003. In fact, the message ’bout Kitty Kibble shortages was taken straight out o’ SimCity 3000.

Clock

One can also see that there’s a clock added to the level UI, which counts up in most levels, ending @ 9:59 (which no player should e’er reach, considering how small these levels will be). While this clock is useless in most levels, in “Skyscraper Caper” it counts down & causes the player to fail if it reaches 0.

A Hydrant Enemy

¡Finally the 1st enemy! This enemy disguises itself as 1 o’ the hydrant blocks you’ve probably seen in other screenshots, only to blink ’wake when the player gets close to it & starts bouncing toward the player, hurting her ’pon touch, ’course.

This is the place where the problems o’ components came ’bout: namely, that any data specific to specific components (such as the Hydrant movement component) can’t be used by other parts, such as the graphics component or other sprite movement components (such as the player’s) ’pon interaction, ’cause the nature o’ the polymorphism that the generic sprite’s ownership o’ these components makes it so that these other things can’t be guaranteed to get a Hydrant component, simply some child o’ the abstract “MovementComponent” pointer.

This is a big deal ’cause the Hydrant sprite has 2 forms: @ 1st, when it’s indistinguishable from a hydrant, & when it’s ’wake & dangerous. To ensure that the enemy only hurts the player when it’s ’wakened, I had to put the damage code in its movement component, which wasn’t too bad; but I can’t get the player to interact with it like a block, ’cause that relies on code in the collision object output by the collision function that runs automatically by interaction in the player’s interaction code, which is different from the enemy’s. I actually tried this with the collision object in the enemy’s interaction function, only for the player to strangely get sucked into the enemy, till I finally realized the vital difference ’tween the player’s collision with the enemy & the enemy’s collision with the player, & how this affects the overlap values. Also, I had to put the hydrant’s graphics code in its movement code, since its graphics also react to its wakening, which makes its graphics component do nothing but decide what graphic file it uses—which could already be done in the constructor o’ the parent SpriteGraphics class, meaning that the HydrantGraphics class is redundant & that the only reason I haven’t replaced it with a regular SpriteGraphics object is that I can’t be bothered & I feel I may want the HydrantGraphics class for later.

Some Dumb Message Box

It e’en comes with automatic line-changing, as seen, though it doesn’t come with any nifty auto-hyphening or any other way to keep words from awkwardly splaying out halfway onto the next line. There are apparently ways to do that, as some SNES RPGs used that kind o’ technology, but it sounds too complicated to bother with now—’specially when this message box has only been used once so far. Maybe when I’ve completed like 5 levels a’least.

(Note: Hilariously, I only just noticed while looking @ the screenshot I took that the message block dips down into the UI. Oops.)

The Future

I mainly want to work on levels & sprites now. Hopefully, I’ll finish “Wasabi Woods” & “Skyscraper Caper” soon. The problem is, most o’ the cool ideas I have require a lot o’ extra coding. For instance, for the “Skyscraper Caper” level I actually wanted have ’nother character, controlled by AI, running through the level, & the idea was that you had to beat her to the end (touching her would probably hurt you). However, I don’t think I have the capabilities to do the kind o’ sophisticated AI that could make ’nother player move through this level full o’ holes without falling off & make it look like natural gameplay.

2 other level ideas that should be mo’ manageable are a level wherein the player has to avoid being seen by guards & a level covered in gold, where touching any o’ the gold causes the player to fail. This latter level will probably be near the end, since it’ll probably be relatively hard.

Download this mo’ interesting but messy source code.

¿Liked it? ¡Take a second to support this idiot on Patreon!
Posted in Boskeopolis Land, Programming