Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

1372123 Posts in 64705 Topics- by 56838 Members - Latest Member: Airec

January 27, 2020, 07:42:34 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsLeilani's Island
Pages: 1 ... 51 52 [53] 54
Print
Author Topic: Leilani's Island  (Read 198648 times)
JobLeonard
Level 10
*****



View Profile
« Reply #1040 on: December 16, 2019, 11:58:15 AM »

The new lava and water look great, but I’m loving that goopy poison! The wind looks good coming out of the pipes, but something about the big patches of purple wind seems off. I’m sure it will be fine for gameplay, but I guess the purple looks odd for wind/steam? Would a neutral gray, like in the pipe image, really be that distracting?

Hoping we get a demo or something soon - I really want to play this game!
Maybe the issue is that the purple is too dark? I think we associate steam and fog with either white or near-white colors. Darker colors feel more "solid"
Logged
mindless
Level 0
*


View Profile
« Reply #1041 on: December 16, 2019, 12:12:03 PM »

Yeah, I was just thinking about that after noticing the steam coming out of the pipes also looks like it has a purple tint, but is more gray. So maybe choosing colors that are a little more washed out/grayed out look better.
Logged
fall_ark
Level 1
*



View Profile
« Reply #1042 on: December 16, 2019, 06:18:58 PM »

The new lava and water look great, but I’m loving that goopy poison! The wind looks good coming out of the pipes, but something about the big patches of purple wind seems off. I’m sure it will be fine for gameplay, but I guess the purple looks odd for wind/steam? Would a neutral gray, like in the pipe image, really be that distracting?

Hoping we get a demo or something soon - I really want to play this game!

I feel like it's also because how thick and defined/wavy the borders and the air streams look like. It could probably afford to look a bit more subtle especially since they tend to appear in groups.
Logged

Chinese localizer and influencer. Translated Dead Cells, Slay the Spire, The Count Lucanor, Katana Zero, Dicey Dungeons, and involved in the localization of Reigns, The Curious Expedition, Desktop Dungeons, etc.
If you have questions about Chinese loc and publishing etc., fin me at Twitter @SoM_loc
linebyline
Level 0
*



View Profile
« Reply #1043 on: December 17, 2019, 02:03:24 PM »

Yeah, I think the purple wind in the challenge level is too purple, and too dark. It reads, first of all, like a background element, and second, less like wind than some kind of ghostly energy. Maybe that's just me? And I don't think that's just the color. The first and third examples look worlds better, but they still don't read to me like gusts of wind. The effect seems too viscous. I think fall_ark is right: It's the wavy, clearly-defined edges. Might also be the slow movement speed.

On everything else, though, the clearly-defined edges are a huge improvement. With that and the other adjustments, the lava and poison are downright gorgeous!
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #1044 on: December 18, 2019, 05:07:25 PM »

Thanks all for the feedback!

I've stretched out and sped up the wavy animation which I think makes the updraft look more swooshy and less viscous.

I also had a go at tweaking the updraft colours for the challenge level, hopefully that looks a bit better. If I make it much brighter it starts to have really high contrast with the background. Still might be something I come back to in the future though. Maybe I should think about changing the background instead, I do have some different colours of challenge background so a brighter one would probably work better.





I also tried out swapping the dark/light colours of the wavy border so it's darker on the outside, to see if it looked softer that way. I don't think I like it as much though, so will stick with the version above.



Logged

linebyline
Level 0
*



View Profile
« Reply #1045 on: December 18, 2019, 05:31:29 PM »

Fantastic! You're very close. I'm not sure exactly what, if anything, needs to change now, but I'm sure you'll come along and make it looks ten times better in a polish pass a few months from now.

The color of the updraft in the challenge room is just right. I think it was mostly the reduced saturation that did the trick.
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #1046 on: December 20, 2019, 12:41:20 PM »

Slopes!
(aka Collision System Part 2C)

I was recently inspired to add something new to the game's core systems: slopes! This was something I'd avoided throughout the project just to avoid all the issues that often come up when implementing slopes. However, I had an idea for a method of doing slopes that would avoid a lot of those issues.

To prevent this exploding into a mess of bugs and edge cases I came up with some sensible limitations:

  • Only one gradient of slope
  • No ceiling slopes, just floor
  • Slopes must be capped off with flat tiles (not unlike the slopes in Mario Maker 2)
  • No jump-through slopes, only solid

The basic slope case is this:



In this example all of the solid tiles, with the exception of the one on the bottom right, are mandatory. So there's no need to handle entities colliding with the slope tiles from below, or from the side.

This devlog post works as part of my series of posts about collision. I recommend looking at Part 2A first.

It's worth noting that this system is essentially being hacked into the game, in a way that causes as little disruption as possible. It may not be the best slope solution for other games or if more complex slopes are needed.

General principle

Incorrect approach

Firstly let's examine the common-sense approach to adding slope collision to the game.

As mentioned in previous posts, when entities move, X movement and Y movement are handled separately. When moving, three collision checks are done, and the closest collision is used to prevent the entity from clipping into the solid collision.

When handling downwards Y movement, what if we simply allow each collision check to hit the slope?



We get this rather awkward case where one corner of the entity collides with the slope, and the entity kind of hovers above it.

This also doesn't play well with other collision checks that the game uses. For example, when enemies are walking, they perform small collision checks below their front edge, to check if they're about to walk off the edge of a platform. If they don't detect any collision, they turn around.

This slope approach would cause enemies to turn around rather than walking down the slope, because their front edge has no collision beneath it:



Correct approach

The method I actually used has a simple change: when calculating collision against the slope, rather than using the X position of each collision 'ray', always use the X position of the centre of the entity instead, shown here by a pink line:



This means that the left, central and right collision checks will all collide against the slope at the same Y position. As the entity moves left or right, the Y position at which the collision checks collide with the slope will move up or down with the slope.

This essentially turns the slope into a flat surface that moves up and down as the entity moves left/right. The entity no longer hovers awkwardly above the slope, instead the centre of the entity is grounded on the slope in a way that looks much better.

This fixes the problem with enemies turning around on slopes. When they do their collision check, they find solid ground in front of them - because as far as they're concerned, they're on a flat surface.



Horizontal

How should slopes be handled when doing the X-axis movement for entities?



We just ignore the slope altogether. This allows the entity to move left or right without being impeded by the slope. If the entity moves to the right, into the slope, then the Y-axis collision pass will push them upwards back onto the surface of the slope.

Hooking slopes into the collision check system

So that's the general principle of how collision against slopes should work. Now, how is it integrated into the collision engine?

When doing each one of the three collision checks, we query the world to get the collision at the end point of the check. What we want is a collision rectangle to collide against. The query checks the main tile grid of the level to see what type of collision is there. If it's a solid tile, it'll return the rectangle for that tile. If it's a solid entity, it returns the rectangle for the whole entity. Nice and simple.

Here's an example showing the collision rectangles that are being used for collision checks:



So the flow is:

  • Collision check queries the world
  • Collision rectangle is returned from entities or tile system
  • Entity collides against this rectangle

How do slopes fit into this? My first thought was the following:

  • Collision check queries the world
  • Collision rectangle or slope is returned from entities or tile system
  • Entity collides against this rectangle or slope

However this would be a bit fiddly. If entities are essentially treating the slopes as flat surfaces, then it doesn't seem necessary for entities to even know about slopes. So the easier solution is:

  • Collision check queries the world
  • Collision rectangle is returned from entities or tile system (including slope tiles)
  • Entity collides against this rectangle

When the collision check queries the tile grid for its collision rects, the slope tiles will also just return collision rects! The centre position of the entity is passed into the query, so the slope tile can return a collision rect based on the X position of the centre of the entity.

A quick look at this in action:



An important point is that even if two or more entities query collision for the same slope tile at the same time, it can return different collision rects for each entity, because the entities are at different X positions.



Tile collision types

The final thing to solve is how to specify the slope collision within the tile data.

To keep it simple, only two types of slope tile exist within the tileset. Lower half of slope, and upper half of slope. Any other surrounding tiles are just the standard solid tile.



It's worth noting that, visually, there may be more tiles. For example the slopes for my test environment are made up of four tiles to make them look nice. However the bottom two of these tiles are just standard solid tiles as far as the collision system is concerned.



Basic slope definition for the main tiles

The two types of slope tile have the slope data defined as you would expect - see the solid green line in these images:





However it's also important that the slope extends out beyond the edges of the tile along the dotted green line. It's a bit tricky to get your head around, but consider the following example:



The entity is moving down to the slope. The rightmost of the three collision checks collides with the slope tile outlined in solid green. However, the centre X position of the entity is actually to the left of that tile's edge. So when calculating the height of the slope it needs to handle input X positions that extend beyond the bounds of the tile. This ensures that all collision returned by a continuous slope is at the same height (represented by the blue line) regardless of which of the slope tiles the collision check hits.

Edge cases

Now the real fun begins. The following edges cases have various solutions but these are all done as an automatic process by checking where slope tiles and solid tiles neighbour each other, and marking the collision as needing special treatment. So this doesn't make the level editing any more difficult - the level editor and tileset only needs to be aware of the two main types of slope tile.

Top of slope -> flat

When the top of a slope meets a solid tile, we run into the following problem:



The solid tile at the top is treated as a wall. Because Leilani is still on the slope, she's lower down than the solid tile, so the X-axis collision pass collides with the side of the solid tile as if it was a wall.

The solution is to mark solid tiles at the top of slopes as being another kind of slope tile. When collision checks are done against these tiles, they use the slope data as shown in this diagram:



This means the slope continues smoothly all the way up. But wait - now the slope extends too far and creates this janky situation at the top of the slope:



While it's important for the slope collision to extend beyond the bounds of the tile for the reasons mentioned previously, in the case where the slope ends and meets a flat tile, the slope needs to be capped off.




Notice the dotted green lines no longer extend above the top of the slope. If a collision check hits the slope tile, but the entity is to the right of the tile (past the top of the slope), then the collision rectangle that's returned won't go higher than the top of the slope.



Now it's all smooth!

Flat -> Bottom of slope

The next problem is that when moving from a flat tile onto the bottom of the slope, Leilani is too low to actually collide with the slope. Her collision checks just land on the solid tile below the slope.



The solution is again to mark up the solid tile below the slope with some slope data. This way, when the collision check hits the tile below the slope, the collision rectangle that it returns correctly represents the height of the slope at that point, and Leilani is pushed upwards onto the slope.





And even more edge cases...

There are a few more edge cases and they grow increasingly niche. A quick example is that enemies were turning around at the bottom of slopes:



This is again solved by adding slope data to a neighbouring tile:



I don't think it's worth going into all of these little edge cases, hopefully you get the idea that fixing these issues is just a case of preventing any discontinuities in where the floor appears to be. This all comes together to allow entities to move smoothly along slopes without really caring that they're there.

Having to solve all these edge cases by applying slope collision data to neighbouring tiles is messy, although since it's all an automated process done in code, rather than having to be set up when editing the level, I don't mind too much. It does prevent the possibility of adding slopes of different steepness, because it would just be too awkward to handle all the possible cases of flat surfaces meeting different slope gradients or different slope gradients meeting each other. If the game was programmed to handle slopes properly from the beginning these would be easier problems to solve.

One last hack

Just one thing I'd like to mention quickly. If an entity is moving fast and going downhill on a slope, the inevitable problem occurs that the entity loses contact with the floor. I hacked this by detecting the entity is on a slope and simply forcing them to move downwards each frame. For example if the entity is in contact with the floor and is going downhill, and they move 4 pixels across this frame, then also move them 4 pixels downwards. This simply ensures that they will still be in contact with the floor after all collisions are done.

Dev time

I've been using toggl.com to keep track of my work hours, and it's interesting to see how much work actually goes into certain things.

For slopes, the actual dev time came out at 7h30m. I'm happy with this - it's a justifiable amount of time to spend on some feature creep that can add a lot to the game. The limitations I placed on slopes to begin with definitely helped to reduce the amount of time it took to implement.

Also this devlog post has taken 3 hours, which is hefty but I think is worthwhile.

Results

I think even this limited implementation of slopes can add a lot to the game!

Here's a montage I made for screenshot saturday on twitter. It shows the slope collision working nicely in various cases - including as a moving platform.



And my first attempt at sneaking a couple of slopes into an existing level:



I like that it can make the non-mechanical parts of the environment feel a little more natural.

I'll be working on a new late-game level next and will make heavy use of slopes. I'm looking forward to seeing how it turns out.

Thanks for reading!
Logged

JobLeonard
Level 10
*****



View Profile
« Reply #1047 on: December 20, 2019, 12:53:31 PM »

Fantastic write-up as always. And only now that they're in I notice how strange it would be to not have them in a game that involves rolling around, so it's good that you added them.

Also, I'm not seeing anything about acceleration/deceleration. Will it affect rolling speeds?

Quote
For slopes, the actual dev time came out at 7h30m.
WHAAAAAAAT Waaagh! ONLY SEVEN AND A HALF HOURS?! For a system this well-structured? What kind of insanely organized 10x programmer are you?!
Logged
qMopey
Level 5
*****


View Profile WWW
« Reply #1048 on: December 20, 2019, 01:34:43 PM »

I love it  Kiss
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #1049 on: December 20, 2019, 02:21:53 PM »

Also, I'm not seeing anything about acceleration/deceleration. Will it affect rolling speeds?

Ah yeah, I forgot to really talk about the gameplay implications!

My plan is for slopes not to affect speed, jumping, etc, which is why I chose a fairly shallow gradient for the slopes. The game already has various ways to move more quickly - for example fireball Leilani can roll faster, or it's possible to maintain higher speed after being shot from a boost ring or tube cannon - so I felt it would be messy to introduce more ways for speed to be altered.

The game's physics are also not realistic in the first place. Enemies or objects roll forever until they hit a wall without losing speed. Adding slope acceleration / deceleration into this mix doesn't seem like it would fit.

Objects like the falling shells in the gif also don't take the slope into account when they bounce - they just bounce the same as on a flat surface. But since they will fall further when bouncing down hill they do tend to bounce farther, so it kind of works.

I do plan on having some mechanics that explicitly take slopes into account. For example, a boulder that will begin rolling downhill if it falls onto a slope.

WHAAAAAAAT Waaagh! ONLY SEVEN AND A HALF HOURS?! For a system this well-structured? What kind of insanely organized 10x programmer are you?!

Well like I say it was all being hacked on top of the existing collision system so didn't take that much new code. Most of the work was done in the shower thinking about how to do it, but I don't track that time Coffee
Logged

mindless
Level 0
*


View Profile
« Reply #1050 on: December 20, 2019, 09:02:33 PM »

I think it’s a testament to this game that I didn’t even notice there were no slopes before now. The new slopes look TERRIFIC. The little GIF of Leilani walking back and forth over the top of the slope is just great. Nice work!

And the steam looks better, too!
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #1051 on: December 21, 2019, 07:44:32 PM »

Initial work on Volcano Climb level

I started work on this level this weekend. The plan is a diagonal climb up the volcano, with lots of slope usage!

So far I've just done the tileset and background. The actual level layout is placeholder.



Camera

Currently the camera in this level is locked onto a path, which is a system I've used on other levels - for example the downhill section of this one:



It works simply by placing markers throughout the level, which represent the desired vertical camera position. As the player moves left and right, the camera moves left and right as normal, but the vertical position of the camera is locked onto the path made by the markers.

In this instance though I've also allowed the camera to deviate by a few units above and below the fixed path. As an example, here's the camera fully locked to the path:


Camera strictly locked to a path

You can see that the level layout would have to be changed to keep Leilani visible on screen at all times - there wouldn't be much room for interesting layouts.

On the other hand, here's the camera when it's able to deviate by +/- 4 tiles vertically from the path:


Camera loosely locked to a path

It allows for more flexibility, but is still more constrained than an entirely free camera would be - I think it helps the player to focus on climbing the hill, and also prevents cases where the camera can move far enough for the edges of the background layers to show Smiley

There are points during this level layout where Leilani gets too close to the top or bottom, which I would try to avoid when designing the final level.

As I was writing this, I decided to try out a completely free camera and see how it feels. It works pretty nicely...


Free-roaming camera

So I'm not 100% sure yet which option I'll choose. Game dev in action! Coffee

Background - Diagonal loop

Normally I make level backgrounds that loop horizontally or vertically. Diagonal backgrounds are new. Functionally they are the same as a horizontally scrolling background - but I apply a vertical offset each time the background loops. Here's the furthest layer of the mountain slope as an example:



Apply a vertical offset to the second and third instances of the sprite, and:



Background - Lava

The flowing lava effect in the background is a super simple trick. There's a scrolling pattern which is visible through transparent parts of the layer above. The easiest way to show it is by removing the layer above:



Then adding the above layer back on top:



I used the same effect on the waterfalls in the rainy level background:



Background - Heat Haze

There's also a heat haze effect on the background which is re-used entirely from other lava/volcano levels I've done previously. The only really interesting note about this effect is that it only distorts the background by a single pixel to the right. I found that distorting the background any further than this was too extreme, and didn't look nice with the single-pixel-wide borders that the game's visual style uses.
Logged

MondayHopscotch
Level 0
**


View Profile
« Reply #1052 on: December 22, 2019, 06:15:16 PM »

Awesome write-up! Thanks for the three hours of your life to give us more insight into this. The slopes definitely add a bit of organic atmosphere to the levels. I've tried a couple times to do some slopes in my engines and they never come out feeling how I want. Very excited to play this game!
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #1053 on: January 10, 2020, 03:20:14 PM »

Boulders

Since the last post, I've finished the first pass of the Volcano Climb level, currently titled Rolling-Rock Rise. It's mechanic is rolling boulders!



The boulders are the slope-based mechanic that I had in mind for this level. Like with many other objects in the game, the boulders will roll when something rolls into them. This includes being able to roll uphill just like other objects - there's no real concept of momentum, they will simply keep rolling until they hit a wall. The main difference that the boulder has over other objects however is that if a boulder comes to rest on a slope, then it will begin to roll downhill.

The boulders do differ from other rolling objects in more ways than that. Firstly, they are both a moving platform and an interactible object, so Leilani can stand on them (rather than bouncing off like she would when jumping on enemies, for example). This also means boulders will block lava falls.

While the boulders are rolling they have the conveyor belt effect - the same one used for the rotating platforms - so you have to move in the opposite direction to stay on top.



Sprite iterations

The sprites for the boulder went through a few iterations. Boulders roll more slowly than most objects so I wanted them to look particularly heavy and chunky.

Initially I drew out a looping rolling animation with a very polygonal look. Rather than give the boulder an 'idle' frame, I just had it stop on the most recent frame of the rolling animation.



This worked ok, I liked that when the boulder stopped rolling it didn't change to a different anim. However I didn't like how the stationary boulder actually looked - none of the frames of the rolling animation looked that great when not in motion.

Next I tried putting some blurring on the rolling animation, and using a more visually pleasing idle sprite. This is the version shown in the first gif; I'll insert it again here:



I loved the chunky look of the boulder when stationary, but the transition from stationary to rolling is very rough.

In the final version, I adjusted the idle frame to better match the rolling frame. There's also a couple of frames of transition animation from the idle to the roll to smooth things out even more:



Another important difference here is deciding when to flip the boulder sprite between facing left / facing right. Previously, it would stay facing the same direction until it began rolling. This would prevent the transition from idle to roll from being smooth because the sprite would flip at the same time.

The solution I came up with was to flip the sprite when the boulder stops rolling. I look at the floor beneath the boulder and predict which way the boulder is going to roll after its brief pause, and pre-flip the sprite to face the right direction. Then when the boulder eventually begins rolling down the slope, it's able to do a smooth transition into the rolling animation.

You can see this in the gif above - regardless of whether the boulder will roll back the way it came from, or will continue rolling in the same direction, the transition from idle to roll is nice and smooth.
Logged

kevin andersson
Level 0
***


Producer/ Designer by day and Programmer by night


View Profile WWW
« Reply #1054 on: January 11, 2020, 01:42:56 PM »

I just read through the slope post and the other ones with water and boulders. It's really inspiring to see such informative posts (and progress ofc). Very well done!
Logged

Prinsessa
Level 10
*****


Ava Skoog


View Profile WWW
« Reply #1055 on: January 14, 2020, 06:15:52 AM »

I've missed so much goodness lately that it's just overwhelming to comment on it all, but wow! Continually so very impressed by this. I'll pick out an immediate standout favourite tho: the springy ropes (maybe they're not new to the project but they are to me!) just look so good. Amazingly pulled off. Kiss
Logged

Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #1056 on: January 18, 2020, 12:20:08 PM »

I've missed so much goodness lately that it's just overwhelming to comment on it all, but wow! Continually so very impressed by this. I'll pick out an immediate standout favourite tho: the springy ropes (maybe they're not new to the project but they are to me!) just look so good. Amazingly pulled off. Kiss

Thanks! There's a post here about how they're drawn if you're interested Smiley

Improved sprite compression

This week I've been adding new tech to my sprite system!

The goal

While thinking about things I might want to add to the game, I thought of a 'How to play' screen in the menu. While this game isn't the most complex, I think there could still be value in this kind of in-game instruction manual - though of course not as a replacement for trying to teach things through smart level design - but just as extra help to players who may need it, or a reminder of the mechanics for people who haven't played for a while.

If I added this screen, I'd definitely want to have animations showing Leilani's moves, which is so much more accessible and informative than just text. The problem then is how to display this. Here's a gif I made as an example:



Possible solutions

There a few ways to approach this.

  • Run a small instance of the game world, with prerecorded inputs, and draw it in a small window. This is nice in theory but my game is not structured anywhere near cleanly enough for me to be able to easily do this. Grin
  • Use a library to load and play gifs. I looked into this very briefly but wasn't really into it - the idea of depending on a library and fitting it into my engine just didn't really inspire me Shrug
  • Just load the whole animation into memory and play it like a normal sprite animation - a bit too wasteful on memory for my tastes. No No NO
  • Improve my existing sprite system to support playing large animations - now this sounds like fun. Coffee

The existing tech, and how to change it

For sprite animation the game has some existing tech in place already.

Animation/sprite system: This uses XML data to define animations, specifying the length of each frame, etc. The frames can either be loaded from a single image file, or from a pre-packed sprite sheet. (More information in this post.)

Sprite packing system / build tool: This is a process I can run on the game's data which does various things, including packing sprites into sheets. For example, Leilani's run animation is normally a single image containing six frames. This process loads those frames and packs them into a big sheet along with the rest of Leilani's animation frames. It does things like cropping the individual frames and removing duplicates, so when I'm authoring the animations I don't need to worry about being efficient about reducing empty space around the edge of the image, or anything like that - the build tool will fix it later. (More information in this post and this post.)

I wanted to use the existing features of this system to reduce the amount of memory that a large animation will take up. The idea is to break each frame of the animation into small tiles - say 8x8 - and then run it through the normal duplication step. Duplicate tiles will be removed, then all the small tiles will be packed really nicely into a (hopefully small) sprite sheet.

I'll now describe the process of implementing this into the engine!

Getting the inefficient animation into the game

The starting point is just to get the animation into the game in some form. I used VirtualDub - which I normally use to create gifs - to export the gif as 148 individual image files.



A small change was needed to my sprite system to allow these frames to be loaded and played. As mentioned above, for a single animation, the sprite system either loads all frames from a single image, or loads frames from a single pre-packed sprite sheet.

(Note: The system refers to animations as 'strips' and the frames of the animation as 'cels'. I'll use these terms when talking about the actual sprite system. But will refer to them as animations and frames when talking about animations in general.)

I added the "filePerCel" property which instead allows the strip to be created by loading each cel from an individual image file. The filename will be automatically formatted using the cel index - for example "CrouchRoll%04i.png" formatted with the number 15 will become "CrouchRoll0015.png".

Code:
<?xml version="1.0" encoding="utf-8" ?>
<AnimatedSprite
horizontalHandle="1"
verticalHandle="1"
>

<Strip name="CrouchRoll"
filename="CrouchRoll%04i.png"
filePerCel="true"
cels="148"
length60="2"
/>

</AnimatedSprite>

And... the animation can now be loaded and played in-game! It's not at all efficient. 148 frames x 128 width x 80 height x 4 bytes per pixel = 6062080 bytes = 5920KB = 5.7MB. Sure, on a modern PC this is not a lot of memory. But if I want to have a large number of these animations - many of which could be longer in terms of frame count - then it's going to add up quickly.

However, it's important that the game is able to load and play the animation without relying on the build tool to pack the sprites first. In normal day to day development I never use the build tool - the game just uses all of the raw, unpacked, unprocessed data. The build tool just provides a more efficient version of the game's data, but the game can run using either the raw or the built data.

Trying the existing packing system

Speaking of the build tool, let's see how the existing sprite packing tech handles this animation.



The 148 frames are reduced down to 90 frames by removing duplicates! (For example, Leilani is standing still for 13 frames at the beginning of the animation).


Packed sprite sheet: 1024x1024

The frames fit into a 1024x1024 sprite sheet. This is 4MB in memory, so a bit of saving already.

Composite sprites

Next up is to add another new feature to the sprite system - composite cels. Previously, each cel in a strip was a single image. But if the build tool is going to divide the cels into multiple small tiles, then the system needs to support multiple images per cel.

I hijacked Leilani's run animation as a testing ground for this feature. Here's the animation data for the "Run" strip.

Code:
<Strip name="Run"
filename="Run.png"
cels="6"
length60="5">
<Cel index="0" custom="2"/>
<Cel index="3" custom="2"/>
</Strip>

(Side note, the 'custom' values on cel 0 and cel 3 are used to trigger footstep sounds while Leilani runs)

And here's how the data for a strip using composite cels looks.

Code:
<CompositeStrip name="Run"
filename="Run.png"
celsInFile="6"
length60="5">
<Cel custom="2">
<Piece sourceCel="0" offsetx="0" offsety="0"/>
<Piece sourceCel="0" offsetx="16" offsety="-16"/>
</Cel>     
<Cel>     
<Piece sourceCel="1" offsetx="0" offsety="0"/>
<Piece sourceCel="1" offsetx="16" offsety="-18"/>
</Cel>     
<Cel>     
<Piece sourceCel="2" offsetx="0" offsety="0"/>
<Piece sourceCel="2" offsetx="16" offsety="-20"/>
</Cel>
<Cel custom="2">
<Piece sourceCel="3" offsetx="0" offsety="0"/>
<Piece sourceCel="3" offsetx="16" offsety="-22"/>
</Cel>     
<Cel>     
<Piece sourceCel="4" offsetx="0" offsety="0"/>
<Piece sourceCel="4" offsetx="16" offsety="-24"/>
</Cel>     
<Cel>     
<Piece sourceCel="5" offsetx="0" offsety="0"/>
<Piece sourceCel="5" offsetx="16" offsety="-26"/>
</Cel>
</CompositeStrip>

It loads the same source image - "Run.png". But then each cel in the strip is manually specified, and each cel can contain multiple 'pieces' - which are the individual images that make up the cel. Each cel contains two pieces, with the second one being offset from the first. We can see it in action in the game:



This is pretty silly but works well enough as a test! The gameplay code hasn't changed at all - it's just using the sprite system to play the "Run" strip on Leilani's sprite. But each cel of the strip can now contain multiple pieces.

Splitting the image

Finally it's time to have the build tool break up the animation into small 8x8 tiles!



Each 128x80 cel is split into a composite cel containing 160 pieces (16 across and 10 downwards). With 148 cels that's a total of 23,680 pieces.

Next, duplicate cels are combined. This is different from the step that removes duplicates throughout the individual pieces! This is more of a larger scale, common sense compression, that removes consecutive frames that are the same. Remember those 13 frames that Leilani stands still at the start of the animation? Those were 13 cels all with a length of 1/30 of a second. They will be merged together into a single cel with a length of 13/30 of a second. This leaves 92 frames containing 14720 pieces.

The individual pieces are then checked for duplicates, which reduces the number down to 700! These are packed together.


Packed sprite sheet: 256x256

I find this super satisfying to look at Kiss The memory usage is also now down to 0.25MB which is great.

The result

Here's a gif of the animation now being played in-game.



Not very exciting to look at, so I made a couple of visualisations. In this one I offset the cel pieces to make gaps inbetween, I guess to prove that it's being drawn from individual bits Smiley



And then this one is my favourite - I darkened the pieces when they are re-used in the animation. So the lighter ones are new to the sequence, and the darker ones are essentially showing all of the saved memory.



I like that when Leilani stands back up after crouching, most of the frame is re-used but you can see her strand of hair getting some new animation data.

I'm happy with the results! I did keep the original gif very simple - particularly with the solid colour background and floor - with the intention of making it compress better. But I think this simple style would be suitable for a how-to-play style video clip anyway.

I did also try applying the compression after making the background transparent, instead of the muddy yellow colour:


Packed sprite sheet: 256x256

It compresses even smaller, because the transparent parts of frames can be cropped out.

Currently about half of that sprite sheet is empty (because the build tool sticks to producing power-of-two sizes). However the plan would be to pack all of the how-to-play videos into a single sprite sheet, allowing the space to be filled and hopefully lots of duplicate tiles between the different videos.

Further improvement

I don't think I need to improve this any further, but a couple of possible ideas:

  • Detect duplicate pieces that are the same when flipped or rotated. This would potentially remove a lot more sprites.
  • Lossy (rather than lossless) compression. For example low contrast pieces could be squashed to half resolution. Not really applicable to the animation I used to test the system here, where I want all the detail to remain. But potentially an easy win if I wanted to use the system for other things in the future.
  • Experiment with different tile sizes. Smaller tiles will naturally result in more duplicates being found - however the XML data for the packed animations starts to get very big, as the position of each piece of each cel has to be specified!

There are also easy wins such as changing the framerate of the animation to begin with. The original test gif is 30fps though and I probably wouldn't want to go lower than that.

Also adjusting the input gif so the floor's black outline doesn't cross the boundary into the 8x8 tile above the floor would probably help too.

Other uses

Finally... is this useful anywhere else in the game?

One potential use I can imagine is for the title screen. I currently have this rather flat-looking animation where each letter glows individually:



I could instead manually animate a nice glossy shine that moves over the whole thing, using as many frames as I want, and then use the compression to pack it nice and small.

Another idea is using the system to compress non-animated things too. For example, the diagonally-scrolling background for the volcano level uses some rather large sprites that don't pack very nicely.


Packed sprite sheet: 1024x1024

Compressing this by splitting it into 32x32 tiles reduces the sprite sheet to an eighth of the original size.


Packed sprite sheet: 512x256

Thanks for reading!
Logged

JobLeonard
Level 10
*****



View Profile
« Reply #1057 on: January 18, 2020, 04:05:52 PM »

How do you manage to go this insanely deep into a topic with every post man Shocked
Logged
PypeBros
Level 0
***


Simplest, but not Simpler


View Profile WWW
« Reply #1058 on: January 19, 2020, 11:23:35 AM »

For sufficiently deep gameplay (and your game surely has enough depth), a sort of demo is imho more than welcome. It doesn't necessarily need to be a full-blown 'attract mode' like in SEGA games of the '90s, though. I liked the 'demo level' of Super Mario World, showing off what Yoshi can do. Another interesting place to have it was in the 'high score table' in Commander Keen (video).

Alternatively, it could also be small cutscenes à la Pac Man between levels, if the techniques only need to be mastered one after another.
Logged

Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #1059 on: January 19, 2020, 07:08:49 PM »

How do you manage to go this insanely deep into a topic with every post man Shocked

Well they take a long time to write, but knowing that people read them and enjoy them makes it worthwhile! Smiley

For sufficiently deep gameplay (and your game surely has enough depth), a sort of demo is imho more than welcome. It doesn't necessarily need to be a full-blown 'attract mode' like in SEGA games of the '90s, though. I liked the 'demo level' of Super Mario World, showing off what Yoshi can do. Another interesting place to have it was in the 'high score table' in Commander Keen (video).

Alternatively, it could also be small cutscenes à la Pac Man between levels, if the techniques only need to be mastered one after another.

Well the game already does have an attract mode on the title screen Smiley



This is done by running the normal gameplay but with a recorded set of inputs. The game runs at the same framerate as the recording was made at so it (hopefully) doesn't de-sync.

For the small how-to-play videos I would want these to just be part of a menu, which could be opened from the pause menu, which is why it would be too messy to use the same system of playing back recorded inputs for those, there's already an instance of the game world in existence.

I love the idea of attract modes and intend to use them to show some cool tricks; the how-to-play screen would be a nice extra on top of that which explains the controls for each move. A how-to-play screen is also easier to find as it doesn't require the player to do nothing for a while Smiley
Logged

Pages: 1 ... 51 52 [53] 54
Print
Jump to:  

Theme orange-lt created by panic