Mode 3 Scrolling Game Engine Demo (WIP)

Use this forum to share and discuss Uzebox games and demos.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Mode 3 Scrolling Game Engine Demo (WIP)

Post by Artcfox »

This is something that I've been working on sporadically since 2018, but until a couple months ago it was always just a super messy hack. I've recently started focusing on creating some new graphics and game engine mechanics that do not rely on the level data being randomly accessible, because in the future I want to stream it in from the SD card like in my previous demo.

Screenshot from 2022-12-27 16-39-50.png
Screenshot from 2022-12-27 16-39-50.png (66.22 KiB) Viewed 1176 times
For the time being, the level data is stored in PROGMEM just for simplicity, but it shouldn't be too hard for me to convert it to stream from the SD card later (requiring just a single sector read when new data needs to be loaded). Also, I just wanted to be able to draw the level in GIMP, and then have all of the tiles "come alive" by themselves without much configuration. Some additional PROGMEM arrays need to be stored in flash for the animations and collectables, but I use the whisper console ports to have the game automatically dump the commands for creating those data files, and then I just copy and paste the commands into the terminal and compile it again with that data included.

So far I have:
  • bigger sprites
  • physics engine now capable of colliding a 1x2 entity
  • camera dead zones to reduce camera bounce when doubling back and/or jumping
  • can jump as high as you want above the screen without collisions with stuff in hidden VRAM
  • pits that kill you
  • one way tiles
  • working ladders
  • dissolving one way tiles
  • ground, wall, ceiling hazards (spikes)
  • animated collectables
  • tracking system for when certain classes of tiles enter or leave the VRAM (uses a single bit of RAM for a given tracked tile)
  • ram tile-based popup menu system
  • new sound effects, some using just the noise channel
  • music (recycled from my last game for now, just to see if it plays nice with the sound effects)
  • melee combat (press X to attack)
  • prototype for sprite-based hazard (fireball) that can travel in or out of visible area
  • prototype for smooth day/night transitions by setting DDRC manually (not enabled in the demo)
Right now I just have a single level where I've been testing out all of the game mechanics. It's nowhere close to being finished, but it's still a fun little world to run around in while I'm thinking about what else still needs to be done to turn it into a real game. I'm still trying to workout how to handle off screen enemies, though I might just make environmental-type enemies that don't move around too much. One of my goals is to keep this full screen, running at 60fps so I'm really trying to keep things optimized.

Although the smooth day/night transition is not enabled in this demo, you can still mess with the DDRC value by holding down SELECT and pressing either Y or B to change the bitmask on the data direction register of the video out port. You should see values printed to the whisper port. If you press B a bunch of times, you can get it to the value I picked for night: 0x1D.

Edit: For the latest version of the demo, look for my latest post in this thread.
Attachments
bigmap.c
(95.97 KiB) Downloaded 50 times
unicorn.uze
(40.27 KiB) Downloaded 59 times
Last edited by Artcfox on Wed Feb 08, 2023 9:58 am, edited 1 time in total.
User avatar
danboid
Posts: 1930
Joined: Sun Jun 14, 2020 12:14 am

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by danboid »

Very nice Matt!

This is looking like a promising game (engine). I like the attack!

I'm aware of but I've not tried the UB platform game engine, platz. How do the two compare?

Are you planning to do a video or docs to explain how others could re-use this engine?

Thanks
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by Artcfox »

Thanks!

I read about Platz on the Wiki, and I've played B.C. Dash, but I haven't messed with the Platz toolkit myself.

I think one of the major differences is that Platz toolkit tries to optimize for fitting an entire game into flash memory, whereas the game engine I am working on is optimized for low RAM usage, and will eventually be switched over to stream the level data from the SD card, and it's not really meant to be a toolkit, but more of an engine optimized specifically for the game I am writing, but the ideas and techniques can be applied to other games.

As a result, this engine will never randomly access level data. That is one of the constraints I am forced to have. So in my first game, Bugz, it was non-scrolling, and all the level data was compressed down to the bit level per tile, and then uncompressed and 3D-ified, and then had the ladder overlays applied on top of that. Its physics engine could always just look at the tiles directly in VRAM to decide if there was a collision, and for the enemy "AI" it could also look at the tiles in VRAM to see if there was a collision with the solid level tiles, and as a result the enemies could travel all over the level. I can still do that with the player character in this engine, because the camera follows it, and anything it collides with will be on screen in VRAM, but for enemies that get scrolled off screen, that will be a problem.

One solution would be to make the enemy's movement not need to query the tiles around it (like the flying AI in Bugz), or I was thinking that I could see where an enemy travels, and as long as it is restricted to a certain limited area, cheat and maybe store a highly compressed version of just that area of the level in flash memory with the enemy's immutable data, in local coordinates, and then add those local coordinates with the world offset of that enemy to compute the enemy's position, even if it is offscreen.

Since the demo, I redid the music, and I figured out how to spawn enemies before they get scrolled into view, and can despawn them when they exceed a certain distance from the player, and they automatically allocate their own sprites to use.

I will probably do some docs or a video of how this all works, maybe as a continuation of my Uzebox Mode 3 with Scrolling Guide, though if I described it at the same level, it would be super long just to go through the physics engine part of it though, but maybe super worth it.

One of the keys of this engine is being able to go from a tile type loaded at a certain (uint8_t x, uint8_t y) coordinate in the Level_getTileAt() function to other data stored about that tile, either state that can change in RAM (ideally using a single bit per tile), or static data about it that lives in flash memory. This is used for knowing which tiles to animate, which collectables have been collected, which items have been spawned, etc.

I have a DEBUG option that I can enable which will print to the console a shell command that will loop over the level data, and combine the x and y coordinate of each special tile into a single uint16_t that I call an xy_index, and then it uses the whisper port to print a bash one-liner to sort all of these coordinates and store them in the proper include file automatically. Sometimes there is additional data that needs to be associated with the xy_index, so it gets printed in a second column, to be sorted on the first, and then everything but the second column is stripped away and stored in an include file in PROGMEM. Take the lollipops for example. There is a sorted list of each of their xy_index stored in PROGMEM. So in Level_getTileAt() when I see I am loading a lollipop tile at (0xXX, 0xYY), I perform a binary search on the PROGMEM array, looking for the xy_index: 0xYYXX, and that returns me the offset into the sorted array, say it was the 3rd element in the sorted array. In RAM, I will have an array of bytes that can be accessed (set, clear, read) at the individual bit level using offsets and masks. So in Level_getTileAt() as I'm reading that lollipop tile that had an offset of 3 in the sorted array, I will look at the 3rd bit in the bit array in RAM to see whether it's a 1 or 0 to know whether to draw it as a lollipop on screen (hasn't been collected yet) or to draw it as whatever tiles were there, but without the lollipop overlaid (has already been collected).

That offset (#3 in this example) can also be used to access other arrays stored in PROGMEM to get other data about that particular tile. For the animated lollipops, I also store the original tile type sorted by xy_index in PROGMEM, so all of the animations can be kept in sync regardless of what frame they get loaded into VRAM on. You can also take the offset, and use to to get back to the xy_index by reading the sorted list in PROGMEM at the offset position, and then further extract just the X component or the Y component by looking at the high or low byte.

The other piece that is very useful for doing the animations is the Tracker object. Say you can have up to 32 lollipops in a level, and you want to quickly know which of those lollipops exist in VRAM (on screen or the hidden parts of VRAM) at any given time, so you can animate them. The Tracker object allocates a single bit of RAM for each of those 32 lollipops, and any time one of the lollipop head tiles is returned from Level_getTileAt() (meaning it hasn't been collected already), and written to VRAM in Level_drawColumn() or Level_drawRow(), a bit in RAM is set that corresponds to its offset in the sorted array of xy_index coordinates. As you're scrolling a row or column into view, the opposite rows or columns is being overwritten in VRAM on the opposite side, so before we overwrite a row or column in VRAM with newly scrolled in data, we first look at what was tile was there in VRAM just before it gets overwritten, compute the level coordinate that would have been used to store it there to get the old xy_index in world coordinates, and then do a binary search on the sorted list to get the offset, and then clear that bit in the bit array to indicate that that particular tile no longer exists in VRAM.

So if there are 32 total lollipops, we will have 32 bits (taking up 4 bytes of RAM total) that indicate whether or not each particular lollipop is currently in VRAM, and when it is time to animate them, we can quickly loop over each of those bits, and if the bit is set, we can turn that bit position offset back into an X and a Y coordinate and then call SetTile() to switch that tile to the next lollipop color in the animation sequence.

All in all, so far it has proven itself to be a very useful technique for tracking state while using as little RAM as possible.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by D3thAdd3r »

You are going all out! Love the DDRC and RAM tile trick ideas.

The tracker object is interesting. I was considering the same concept for an abandoned SMB3 demo. It really is the most efficient possibility for RAM, where others would eat lots of time for probably no space/compression gain.

Offscreen enemies in some older platformers might have even been placement optimized to avoid being near a "page boundary". Then you could still do some off screen collision with whats loaded in VRAM, or free the slot if they go too far. Probably better ways, but they all seem to each precious RAM.

With destructable tiles it isnt really an option to precalculate a bitmap for each tile an enemy might encounter. Depending on enemy movement capabilities, it might be easier to avoid all tile checks, and merely recalculate x/y limits for up/down/left/right when a tile is destroyed, but even that is a lot of RAM with high cycle spikes.

Impressive stuff, keep it up!
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by Artcfox »

Thank you! I'm using your new flash streaming mixer and mconvert to make my new MIDI smaller. Awesome work, every little bit of space saved helps!

I also posted about another trick I discovered where you can swap the patchStruct pointer on the fly to change the instruments of a song on the fly, say if your character leaves a room, or goes underwater.

It is a shame we don't have dual VRAM buffers side-by-side in order to use some of those old school tricks. Right now for the enemies, I'm just leaning towards each of them knowing their bounds, and so it can move independently of needing to know the level data. Similar to how the flying enemies worked in Bugz. For now I'm even borrowing some of the sprites. I figure at least the butterfly, and possibly the bee might find a permanent home in the same universe as the unicorn. I'm not sure if I'll have enough cycles to run a full physics engine against each one, but I hope that I'm at least be able to run the "entity_update_flying" physics, where it's just acceleration and friction, but no collision detection.

I had been thinking about it a lot more, and came to the realization that if my Camera object simply keeps track of the level extents (x_min, x_max, y_min, and y_max) currently mapped into VRAM (both the visible and hidden tiles), I could be a lot more precise with things such as the glitch protection in the DissolvingTile_update() function, and things that happen if you jump above the screen or fall into a pit below the screen. The VRAM extent tracking turned out to be more complicated than I had expected, because depending on how close you are to the edge of a level, some rows and/or columns in VRAM might not contain valid level data at all. Once I got the VRAM extent tracking working, I tried completely rewriting the DissolvingTile_update function to use it, and then I discovered another glitch that was present in the original as well that couldn't be fixed unless I enabled my "perfect, but slow" compile-time option. Since I didn't want any glitches, this forced me to really think about how to optimize it some more. The extent tracking allowed me to apply the glitch protection fixes only exactly when I needed them, and I was able to clearly see why they were necessary.

In order to make it look like you're standing in the middle of the top of a 3D block, the tiles are divided into solid and sky tiles, a solid block that has sky above it will always have a sky block with the ground on it to give it a 3D look. When I store things in RAM, like the list of active dissolving tiles, I want to store as little as possible, so I only store the bottom "solid" portion, and the top gets animated along with the bottom. However if you pan the screen up or down and stop right on the edge where you've scrolled just the top or bottom half out of VRAM, and then you scroll back the other way, the half that got reloaded from the level data will be solid again and won't match the other half. The only solution is to check for this, and make the other half solid while it is still in the hidden portion of VRAM, so you never see the mismatch, and the only way to do that was to not wait for the expiration time to match, but to check everything on the list, regardless of expiration time.

In my first iteration this took many thousands of clock cycles (~18,000, if I remember correctly), but the CPU cycles started to become reasonable with the simpler extent-based VRAM tracking system. And then I realized the key to making it faster than my previous glitchy solution... If I'm running through the entire list of active dissolving tiles each frame, I only need to run the glitch protection checks on frames where a new row or column of level data was removed/loaded in VRAM. If not, I could just jump right to the expiration check, and if a tile's timer had expired, it could transition to the next tile in the sequence of dissolving tiles. So I modified the Camera object to have flags indicating whether or not a new row or column was loaded on that frame, and now the DissolvingTile_update function became stupid-fast.

Subsequently, I had another idea to keep peak CPU-usage per frame down, and that was to delay the somewhat expensive lollipop animations by a single frame if we just scrolled past a tile boundary and loaded a new row or column of level data. That prevents a worst-case loading new level data, doing all the glitch protection, and animating all the onscreen lollipops from happening all within a single frame.

In order to test that I would wrap my call to WaitVsync(1) in WDR instructions (handling sprite processing, and restoring the background myself, so that gets included in the clock time) and log the CPU cycles per frame to a file using uzem, and then I would take the sum, and find the maximum, change the code over with a compile flag, and do it again so I could make data-driven decisions about which code was the best.

And then I had a crazy idea, what if I just took a completely naive approach for animating the lollipops that didn't involve the Tracker object at all, and instead used the simple VRAM extent tracking system to loop over the list of all lollipops and if it's within VRAM bounds, animate it. It turned out to be more expensive that looping over just the bits from the Tracker object, but the Tracker object was super expensive when loading new level data. I did the sum and maximum comparison, and the naive approach handily beat the Tracker object. I even disabled animations completely, and still ended up with the same max CPU usage per frame as I did with the naive approach, with a fairly long torture test of running around the level, farting all over the place while collecting the lollipops. So I actually ended up removing the Tracker object completely, even though it made me a bit sad to do so.

I spent some time chasing what I thought might have been a kernel bug, but it turned out to be a bug in my program. When the unicorn fell down a pit, suddenly whichever other sprite that was using ram tile 0 would have parts of the unicorn blitted on top. I couldn't figure it out for a couple days and just worked around it by allocating a user ram tile that I never used, but that bothered me. In order to make the testing of lollipops and spikes a bit faster, the lollipops are the very first tiles, so I can just do a single less than comparison with the tile number for a lollipop, and a single greater than comparison with the tile number for a hazard. That meant that my tile 0 was a red lollipop. Calling ClearVram() would end up filling the screen with red lollipops, and loading anything else after that would take longer than one frame, so you'd hit VSync, and you'd see half a screen full of lollipops, so I removed the call to ClearVram(). However, if you load a level and the unicorn is near the edge, not all of VRAM gets initialized, because you're up against the edge of a level. It turns out that the uninitialized VRAM memory was filled with RAM tile 0! So as the unicorn fell down a pit, it was being composed with that ram tile, which happened to be what backed a real sprite on screen, so that real sprite ended up showing part of the unicorn on top of it until it fell completely past. So again, I was able to make good use of the VRAM extent tracker, and detect if there would be any uninitialized parts of VRAM in Camera_fillVram, and then fill those parts myself before loading the level data. That gained me that RAM tile and my sanity back!

After adding sprites that can kill the player, I realized that I needed to allow the lollipops to disappear if you overlap them while dead, because if you jumped into a sprite, died, and then fell on a dissolving tile that had a lollipop stem on the same tile as it's top half, it would glitch, because it was counting on the lollipop not being there in order to animate correctly. That made me a bit sad, so if that happens I prevent the new Gulp sound from playing. Yes it disappeared, but you didn't actually collect it; you just knocked it down and it's hiding under your dead unicorn's body. And yes, now that I said that, it is canon.

Attached is the latest copy of my demo. If you kill a sprite and then go far enough away it will despawn and then be respawned again. They are stationary until I work out the best way to get them moving. I think this version is actually smaller than the first, even though it has more features.

One thing that has been a huge help has been using a hacked version of cuzebox that always records your inputs (compatible with uzem, so I can play it back in uzem and use gdbserver if I need to), and that also prints whisper port stuff to the console. If you want to try it, you can compile the debug-enhancements branch of my cuzebox fork: https://github.com/artcfox/cuzebox/tree ... hancements I typically compile it once with replay enabled, and rename that executable cuzebox-replay, and then compile it with input recording, and keep that named cuzebox. That way any time that I'm testing something out and notice a bug or glitch, I have it recorded so I can reproduce it after I add some UZEMCHR = and UZEMHEX = statements to the code.
Attachments
bigmap.c
Lots of bugs fixed, lots of optimizations, lots of comments
(102.82 KiB) Downloaded 44 times
unicorn2.uze
Latest demo
(39.13 KiB) Downloaded 46 times
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by Artcfox »

So in the latest demo I posted, the spawns are placed as tiles on the map, and then sorted and stored in PROGMEM. When I added new spawns to the map, that changed the ordering, and all of the immutable spawn attributes were now in the wrong spot, and I had to manually fix them up. I don't want to do that every time I add/remove an enemy, or change its location when designing each level, so I need to come up with a better solution.

This is what I was thinking, let me know if you can think of a better way:

In Video Mode 3, there can only be 256 unique tiles, numbered 0-255. But the actual number of PROGMEM based tiles you can use is 256 - RAM_TILES_COUNT, as defined in your Makefile (36 in my case).

What if in my tileset, I include all the tiles I plan to use, then fill the rest all the way up to 256 with tiles that will never appear in my game, but are unique among the entire tileset, maybe just unique rainbow colored static. And then on the tail end of that, I overwrite the last RAM_TILES_COUNT (36 in my case) tiles so they clearly indicate their uniqueness to me as a human, maybe just stamp them with two digits written onto a single tile: 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, since I can easily fit two digits onto an 8x8 tile. Then I can drop those numbered tiles into my level to indicate each spawn.

When I run gconvert, it will give me a map that has those tile numbers written into it, but then my Makefile can run awk, sed, or grep to strip out the actual tile data from all of the junk rainbow static tiles and all of the double-digit stamped tiles in the tileset.inc file (so they don't waste PROGMEM space), leaving only the tiles I actually use for the level, but the level map will still reference each of the spawn points uniquely with those high tile indices.

In my theTile = Level_getTileAt(x, y) function, if I see a tile index coming from the level data that's within that upper range, I can pretend it read a TILE_PURE_SKY, but instead call Spawn_add() with (theTile - (256 - RAM_TILES_COUNT)) to calculate the number that was stamped on that tile, and I can use that to directly reference the immutable data stored for each spawn in PROGMEM, without having to do a binary search, and without having to worry about anything being reordered when I add, remove, or rearrange the spawns on the map and then store a sorted array.

I know I dropped a tile stamped with "07" in the top corner of my level, and so I can just go to the 7th element in my PROGMEM array of immutable attributes for that spawn, and configure its attributes, and I can move it around, add other spawns, and that 7th element will forever be associated with that tile stamped with "07" in the level data. (As long as I don't have a duplicate "07" tile in my map, which I can certainly create an automated check against.)

To me this seems like a win-win, since you can't actually use the upper RAM_TILES_COUNT tiles as PROGMEM tiles, so you're not losing anything, and as long as your Makefile patches up the tileset.inc file to strip out the actual tile data for the "filler" junk tiles, and the stamped digit tiles, you won't even waste any PROGMEM space. You literally just need it to exist in the context of gconvert so you can get unique non-mappable indices stuffed into your map of level data.

Edit: For ground-based enemies, or ones that need to spawn overlapped with something else, if I need the double-digit stamped tile to be replaced with something other than TILE_PURE_SKY, I could store the tile I need it to be replaced with as part of its immutable attributes in PROGMEM, and then that restriction is gone as well.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by Artcfox »

I just implemented this (without the static junk tiles in between my real tiles and the double-digit tiles), and it works great! I can move any spawn point around, and it just works. It also let me remove the sorted PROGMEM array of spawns, so bonus space savings!

I'm thinking that I can have some "entities" just be invisible scrolling locks, so in some parts of the level it can only scroll horizontally, or vertically, or stop scrolling completely until an enemy is defeated or something. And if the X and Y position come from the immutable data in PROGMEM, rather than the location of the double-digit tile, I can use the loading of the tile as a trigger for an entity that doesn't necessarily spawn directly on the trigger. This opens up many possibilities!
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by D3thAdd3r »

Very nice stuff. The "use unsuable tiles" for enemy placement sounds like a great idea. Development wise, that has to be a huge time saver.

On the animation, perhaps you mentioned a detail which negates this and I missed it, but is it absolutely necessary all lolipops get animated in the one frame? I would think much faster than checking bounds and all that, would simply to traverse a linear portion of VRAM each frame(maybe 25%), starting where you left off last frame, but importantly not the entire array. Then if the tile values are in a lilipop range, add whatever value it needs to get to the correct frame, and do whatever is possible to minimize conditions or unroll the horizontal of the loop.

Trying to think of an example, pretty sure I did that in Alter Ego(stars, slime/lava) and Lolo(huge water animation savings let me blit a lot more), I actually thought it looked better than perfect synchronization.

The control entity sounds like a good idea as well, and would allow some fancy stuff.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by Artcfox »

Thanks! It definitely saves time, enough time that if push comes to shove, I might even consider not using the most tiles possible in order to allow more placements if I need to, say to double the number to 64. Though, unless the levels end up being huge, I probably won't need that many enemies per level.

I did try various ways to only animate some of the lollipops per frame, but of the ROYGBV colors, each frame the R->O, O->Y, Y->G, G->B, B->V, and V->R, so it looked pretty bad when I didn't do them all on the same frame. The problem was there ended up being a lot of lollipops next to each other that were the same color, and it didn't save that many clock cycles. I tried just animating the first half of the list on one frame, and then just the second half, but since they were sorted, that tended to either be an all or nothing thing (attempting to animate all the lollipops that were offscreen, and then all the ones on screen the next frame). Then I tried skipping every other one in the list, which seemed to get about half each time no matter where you were on the screen, but it still didn't look that great to me.

The funny thing is animating all of them like that was just supposed to be a torture test. I thought it was "too busy" to have all of them changing different colors like Christmas lights. They were originally going to be static, and I was going to use the animations for something else, but then it grew on me. The other idea I had was to keep them mostly static, but use a RAM tile based shimmer effect that has like a 45 degree glare effect wipe across their surface, but only have that happen to a single color at a time (so it only uses one extra RAM tile, not six), I think that would achieve that twinkling effect, but I think the expensive part is looping through the list, so unless I also found a way to build an index sorted on color, I don't think that would save much time either.

I think I've settled on just animating them all when I realized that the max CPU usage per frame was exactly the same whether I animated them all in one frame, or if I disabled animations completely.

I did get the sprites moving now, and OH WOW is it different trying to use the melee fart attack against them, versus jumping on them like you had to do in Bugz. I may have to add a "switch weapon" button, and implement a yo-yo style projectile that fires a certain distance, and then returns to you via shortest path to your current position. I did think about allowing jumping on the enemies, but I wanted to try out a new set of game mechanics. I still need to get them to cycle through their animation frames. Hopefully I can share an animation frame counter among all of the entities, so I don't have to either use an extra byte of RAM per enemy, or multi-task with the existing counter each enemy has now for the undulation when flying, though I can probably multi-task with that counter if I really need to.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Mode 3 Scrolling Game Engine Demo (WIP)

Post by Artcfox »

Here is a video demonstrating the moving butterflies, and falling spikes. Be sure you switch it to 720p@60fps to get the full goodness.



The falling spikes have a configurable trigger zone; you can make each one as tall and/or wide as you want to symmetrically under a spike. They will still kill you on the ground until they start blinking and then they reset themselves automatically.

A bug snuck in by accident, but I like it so much that I think I'm going to keep it. If you fart on an enemy after it's already dead, it will still make it rise in the air a bit while colliding with the fart spray, so if your timing is very precise, you can "fart juggle" the enemy, and if you're really good you can even do it while climbing up a ladder.

And here is a "behind the scenes" video of an earlier version where the hitboxes are solid colors so you can get a better idea of what is happening under the hood during collision resolution for the player killing an enemy. The hitboxes for an enemy killing the player are different and smaller.



It's pretty fun to do a swan dive into the spikes and fall on a cloud that eventually dissolves once all the collectables have been collected.
Post Reply