Main task for this week was adding two new NPC/enemy designs, but a few other simple and important changes were made.
GENERAL IMPROVEMENTS
Made Jimmy a little more independent. Now he will sometimes go off on his own for a while. A simple change that gives him a lot more personality. Combined with his already randomized aggression timer, it feels like he likes to go out hunting or just wander the space.
Changed the background to solid black with a randomly generated starfield in front of it. This way, I don't have to worry about repeated tiles, or needing one huge background image for the entire room. It also looks waaayyy better than what I had before, despite being almost the same in terms of actual content.
Made the freighters able to target larger asteroids that are directly in front of them, with some corresponding tweaks to how targets are prioritized. This will be slightly different for each object's AI, depending on its size and behavior, but should be useful in a variety of cases.
Altered the blocks so that they don't move toward each other, and only stop when colliding. This way, I could add in the bounce code when they hit large objects, which reduces janky-looking collisions a bit. As I've mentioned in the past, this has a profound effect on the patterns we end up with, but I think they're just as interesting, and more functional overall.
Something to keep in mind now that I'm making larger objects is that they tend to look better with borders that are at least two pixels wide, while the interior details can be smaller. It just makes them look more substantial, helps define the amount of space they take up. Starting to work with larger sprites is not easy, because I have an obsessive approach to making each one pixel perfect. Not in the sense that they look great, but that everything is symmetrical, consistent, etc. On a 16x16 grid, that isn't a big deal, but the larger the sprite, the less productive it is to be that picky, and the more I need to think of the entire thing as an analog drawing rather than a puzzle of individual coordinates. Fortunately, spaceships lend themselves well to lots of straight lines and symmetry, which I find much easier than trying to capture natural forms of any kind.
So, two new enemies...
KAMIKAZE SPACE BIKERS
These guys are not easy to record in the wild.
Not sure what else to call them, though they really just look like small ships, or angry tadpoles. This is definitely close to the lower limit of how small a sprite can be and still look like something. I had actually wanted them to be little people on hoverbikes, but it's just not readable. Anyway, this works.
They are fast and fragile, and extremely dangerous if they actually manage to hit something. All they do is drop bombs when they get close enough to an enemy, any one of which does enough damage to take out anything. I haven't really tuned explosion damage, and I'm not sure if I will ever nerf it in the future. They're not technically meant to be suicide bombers, but that's often the end result. They aren't very smart, so sometimes they will just dump explosives harmlessly behind an enemy before being destroyed. Other times, they'll manage to get one right in their target's path. Other times, they survive long enough to cover the whole area in explosives. Either way, the results are pretty amusing. If I actually did manage to improve their AI, they would quickly become too effective.
This one took surprisingly little time to implement. For one thing, a small object means much easier sprite work. But I was also able to reuse a lot of assets and code. The explosion is just the fighter explosion with the extra debris erased. And the steering and path-finding code that I added for my first two enemy types (which was a huge headache at the time) is pretty all-purpose, to the point where it's often quick to just duplicate one of those older objects and make a few tweaks. So it was more or less a single afternoon to get this made and working properly.
COMPANY CRUISER
Which means the rest of the week was dedicated to the other new enemy. This is the largest destructible object so far, so when I got it moving around in-game, I quickly realized that it would probably make more sense to have different damage locations that would allow it to fracture and be destroyed in pieces. I would love to have some sort of procedural destruction system, but that is not something I even want to think about tackling at this stage. This thing is just a tedious collection of objects and sprites.
I struggled for a bit on how to handle locational damage, and looked up one or two solutions online. Aside from the approaches that had already occurred to me, the main one that sounded promising was to use a duplicate of the existing sprite, color-coded for each location, drawn on the object and invisible.
Then when a collision is detected, we use a function that looks up the color of the pixel at that position. For my object, I could then transfer the damage to the relevant stat and go from there. Unfortunately, this function is also pretty slow, and the amount of work I was going to have to put in for a feature that was likely to slow the game down just didn't seem worth it.
In the end, I went with something that feels like an easy cheat that creates its own set of headaches. The enemy is just three separate objects. The front part creates the other two and passes its location and movement information to them. Seems simple on the surface, but has a bunch of disadvantages. For one, it creates a lot of edge cases where parts end up in the wrong place, especially once you add other collisions into the mix. As it stands now, making this coherent meant only having the bounce function on one part at a time; the front part. This isn't really a problem in practice, because GM's built-in bounce code often just sends an object in the opposite of its direction of motion, even when the collision is at an angle. For an object this size, that would look strange for anything except a collision on the front. Also, this thing is too heavy to be bouncing around much, anyway. Just enough to keep it from running over large objects too frequently. In fact, the strangest looking collisions are the result of rotating the image, which I can't solve with the existing function.
Another persistent problem with using multiple objects is that I have to repeatedly inform each of the three objects about the state of the other two to determine what sprite they should be using, and whether they should be taking their variables from a different object or from themselves. I never managed to solve the sprite choice problem. My initial attempt was to have an alternate sprite for each piece that showed where another piece had broken off (obviously). When a new cruiser is created, the front part creates the other two and stores the instance IDs of the created parts, and also tells each part its own ID, and tells each of them the ID of the other created part, that way everyone knows who everyone else is, and they can keep track of each other. At least that was the idea. This didn't always work. When testing for the existence of a particular instance, one part would end up detecting an unrelated instance of the same object, or something similar. This resulted in weirdness like the first cruiser to lose its midsection telling every other object in the game that they should be using the damaged sprite even when they weren't separated.
So yeah, after struggling with that for a bit, I gave up and went with a second cheat to solve the first one. The front and back sprites do not change at all; they are always damaged, but the middle sprite covers the damaged parts until it's destroyed, and is created at a higher depth. This meant the midsection itself could never appear in a damaged state, so now it just explodes if either the front or back are destroyed. This does add a bit less variety to the destruction than I was hoping for, but at least it works. It is possible to destroy only the midsection, but it's a rare occurrence.
So getting that working was the main hurdle. The enemy's actual functionality was relatively simple. Spawn two turret objects of a type I've used before, and those function as intended with no changes necessary. The cruiser also launches fighters, which were very easy to make. The image is tiny and has no animation, and the code is just the standard fighter code with its steering code ripped out (a symmetrical object never has to change facing). Even the destruction animation is reused from the other new enemy type of similar size. As usual, the most time-consuming part was the sprite work, animating the fighter bay door. This is a simple, easy task that becomes repetitive when I decide something needs to change, and have to go through and change each frame by hand. I have no doubt some of these tasks would be easier if I knew the editor a bit better, or if GM had a more full-featured editor that made copying or automating certain things possible. And I'm still not totally happy with this new ship's appearance, but it's been tweaked enough for now.
Looking over my notes from the relevant day, another time-consuming part of the process is making sure that debris objects spawn in the correct location when an object is destroyed. This is complicated by the fact that the base sprite is scaled up by almost double in-game, and the explosions can also be a different size. So it's a bit of trial and error trying to make sure everything looks the way it should.
NEXT WEEK
I'm trying to spend a bit less time on this on the weekends, aside from preparing videos and a blog post. This week felt like less progress, but when I think about it, two new enemies is all I was hoping to get through. It's not a whole new system like the powerups were, so it doesn't feel like as much has changed. I actually have two more enemy types I would like to implement that I keep forgetting about, but hopefully they will both be relatively simple. Beyond that, still planning to add randomized fog and a scoring system. Both of those are things I've done before, so I don't think they should be too much trouble.Once I have the scoring system in place, that should be a good excuse to start adding triggered events.
The things listed above are probably a week or two of work. Beyond that, I want to start thinking more about aesthetic improvements, which I haven't given much thought to. Changing to a generated background took like five minutes and was already a huge improvement. In terms of visual effects, I don't have anything planned beyond the particle fog. I could maybe add more background objects that are randomly placed, but I think a simple background is probably better. It may be time to start thinking more about the ambient audio.
Then if I can cross all that stuff off my list, I need to consider whether it's worth trying to add docking and station interiors to the prototype. Without a campaign, they wouldn't really serve any purpose, but might still be fun. But aside from a whole separate set of assets, that requires me to deal with room transitions and persistence....
Looking back at the recorded footage, I will say that I'm going to have put more work into eliminating awkward collisions at some point. Adding larger objects really reveals the limitations, so I can either try to actually solve those, or find ways to avoid having them occur in the first place. There are a lot of weird collisions involving the player object right now, but that's because I give myself a thousand times more health than I should have for testing purposes. So yeah, tuning difficulty is going to be interesting, once I actually start testing the game without cheating.
Comments
Post a Comment