Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411283 Posts in 69325 Topics- by 58380 Members - Latest Member: bob1029

March 29, 2024, 06:58:57 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsLeilani's Island
Pages: 1 ... 40 41 [42] 43 44 ... 67
Print
Author Topic: Leilani's Island  (Read 401042 times)
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #820 on: April 20, 2018, 08:29:30 AM »

I just signed up to say grats on the game progress so far! This is (almost) exactly what I want out of a dev blog -- and on a style of game I love.

I do wish you'd go a little deeper into the technical weeds (the programmer in me wants to know some stuff Wink ).

The style you've got going is  Hand Thumbs Up Left

What's been your favorite part to work on / or your favorite feature you have so far?

I’d love to hear more about the tech too Smiley

I do go into more technical details sometimes, the first post in this devlog has a big contents list of all the main devlog updates so might be worth a look through there to see what I've covered before. But as always, if there's anything specific you'd like to know more about, let me know!

It's hard to pick a favourite feature I've added to the game as there's a lot there, but one personal highlight is the gameplay demo / attract mode on the title screen. I love watching those demos in games that have them and really happy to have them in this game. It'll be a major milestone for me when I'm at the point where I can record the final set of demos and release the game Tears of Joy
Logged

MondayHopscotch
Level 0
**


View Profile
« Reply #821 on: April 23, 2018, 10:30:23 PM »

I do go into more technical details sometimes, the first post in this devlog has a big contents list of all the main devlog updates so might be worth a look through there to see what I've covered before. But as always, if there's anything specific you'd like to know more about, let me know!

It's hard to pick a favourite feature I've added to the game as there's a lot there, but one personal highlight is the gameplay demo / attract mode on the title screen. I love watching those demos in games that have them and really happy to have them in this game. It'll be a major milestone for me when I'm at the point where I can record the final set of demos and release the game Tears of Joy

Oh, I've read probably 80% of your devlog at this point (at least 80% of the posts linked in your table of contents). The way you have used Tiled has really put my previous uses of the tool to shame  Embarrassed   I will certainly be using the shape / path layers more in future projects.

The demo modes definitely have a certain charm that really embodies the retro style. I used to watch the Super Mario World demos as a kid, just fascinated with how they got the game to play itself.

Keep up the good work! I'm stoked to see how this project continues to progress.
Logged
qMopey
Level 6
*


View Profile WWW
« Reply #822 on: April 24, 2018, 01:52:49 PM »

I'd love to hear about how you author collision information. Defining the shapes of different tiles, game objects, and how the collision detection at run-time works, along with your physics sim as well.



I've been researching the best way to author collision information for tiles in Tiled. I really wish there was some kind of way to:

  • Define a list of shapes, and name each one (like an Enumeration)
  • Store as a custom property on each tile an enum value

This way each unique kind of shape that a tile could possibly be sits in a single global array. Then each tile stores an index (like an enum value) into the global shape array. This defines the shape of the tile. At run-time the shape is retrieved from the global array and translated to the tile to perform any necessary collision checks.

This style is quite good for authoring and for run-time.

But in Tiled the best option seems to be to either A) use a custom property on tiles that somehow maps to shapes or B) use the Tiled tile collision editor. The collision editor is quite nice, but it stores a bunch of redundant information on each tile. For example, if each tile is an identical AABB, then for each tile type Tiled will export redundant AABB information. This takes up a little extra disk space, and takes a little extra time to parse. This is probably a negligible downside, but still could be a little better.
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #823 on: April 25, 2018, 09:25:17 AM »

Thanks MondayHopscotch!

I'd love to hear about how you author collision information. Defining the shapes of different tiles, game objects, and how the collision detection at run-time works, along with your physics sim as well.

Sounds like a good topic to get into!

But in Tiled the best option seems to be to either A) use a custom property on tiles that somehow maps to shapes or B) use the Tiled tile collision editor. The collision editor is quite nice, but it stores a bunch of redundant information on each tile. For example, if each tile is an identical AABB, then for each tile type Tiled will export redundant AABB information. This takes up a little extra disk space, and takes a little extra time to parse. This is probably a negligible downside, but still could be a little better.

Yeah I agree, the collision editor looks great but seems more suited to a game like Sonic where you have large tiles with a lot of unique shapes. For a game with just a few simple tile shapes it's a bit too flexible and fiddly.

Custom properties is a decent option, though personally I opted not to use this because I find it a bit clunky to edit, the process of adding / changing custom properties for each tile is quite slow.

There's also a downside to both of these options - there's no good way to instantly visualise the collision/properties applied to each tile. As far as I know you have to click on each tile individually to see its properties or collision shape.

My solution

For these reasons my solution for authoring tile collision doesn't use tiled, I have an XML file for each tileset that supplies some game-specific details.

For example this is the file for (an old version of) the Beach tileset:

Code:
<?xml version="1.0" ?>
<Tileset>
<Collision>
0FFFFFTF
FFFFFFTF
FFFFFF0F
FF0000T0
FF0000T0
FF0000F0
FFFFFFFF
FFFFFFFF
</Collision>
<Layer>
11111111
01111111
01111111
01222212
01222212
01222212
01111111
11111111
</Layer>
</Tileset>

For the collision info, 0 = no collision, F = full solid square, T = top only (jump through platform). Those are my only collision types.

I'm also specifying Layer here, which just affects rendering order. 1 = normal, 2 = back (e.g. palm trees trunks) which is drawn behind Leilani, 0 = front which just prioritises the tiles over the normal tiles (my tiles overlap slightly, 24x24 on a 16x16 grid, so sometimes I want to force certain tiles to be in front of others).

This solution is pretty basic but works well enough, it's quick to edit although a bit awkward sometimes to keep track of which tile in the tileset each value refers to.

Alternative solution?

I was thinking about this on the journey home today and came up with an idea for an inbetween solution. It's similar to my solution of having a separate file that describes the collision for each tile, but instead of using an XML file, use a Tiled map!

You could create a tileset with tiles that represent the different collision types (full, half, left slope, right slope, etc). Then create a new map, the same size as your ingame tileset. Place all the ingame tiles on one layer. Add a new layer, and draw the appropriate collision for each tile, using the collision tileset.

Then the game would read this map file, and associate the game tile on one layer with the collision tile on the layer above. It'd be nice and easy to edit, and at a glance you could see which tiles have which collision applied to them.

Plus it's flexible, if you wanted to add more data (such as the Layer property I'm using) then just add another layer.

What do you think? It's something I'd consider for a future game I think.
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #824 on: April 25, 2018, 10:23:09 AM »

So each of your tiles is an 8x8 grid of AABBs? That's very interesting Smiley What a cool way to go about! A very different approach to the one I had in mind.

Alternative Solution

Your idea totally works, and has shipped many games. For example, Volgarr the Viking had a very similar placeable set of collision geometries all drawn in red. Another game that did similar was Even the Ocean.

Pros
* Simple to use
* Simple to implement
* Easy to visualize the collision geometry
* Flexible (no special code needed for different combinations of visual/invisible tiles with or without collision)

Cons
* Longer workflow (must place more tiles)
* Editing pre-existing levels becomes more cumbersome

For me personally the cons are really tough. They hamper iteration time a lot. It would be great to place a tile and have a default collision type. Then, in editor turn on the "collision view" to see the geometry (for example, drawn in red color), and be able to paint overrides of the defaults for custom one-off tidbits.

This way the cons are heavily mitigated by initial collision types for tiles. Each tile type could have a different initial collision type. When creating a new tile, the initial collision type can initialized as empty, or be set to something specific by the user. None of the pros would be lost, either!



You are totally right, that editing collision geometry in Tiled is cumbersome. And there is no easy way to visualize. Given the lack of visualization... Now I'm thinking the painting approach you mentioned would be the best choice, at least until Tiled is improved.
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #825 on: April 25, 2018, 12:33:04 PM »

So each of your tiles is an 8x8 grid of AABBs? That's very interesting Smiley What a cool way to go about! A very different approach to the one I had in mind.

Ah no, that's the entire tileset. The tileset is 64 tiles and each character (0/F/T) represents a single tile.

So this collision:

Code:
<Collision>
  0FFFFFTF
  FFFFFFTF
  FFFFFF0F
  FF0000T0
  FF0000T0
  FF0000F0
  FFFFFFFF
  FFFFFFFF
 </Collision>

Matches up with the 64 tiles in this tileset:



e.g. the palm tree trunks on the right are collision '0', no collision. And the thin wooden platforms are 'T' for jump through platform.

(Ignore the red tiles, I will talk about that another time)

Your idea totally works, and has shipped many games. For example, Volgarr the Viking had a very similar placeable set of collision geometries all drawn in red. Another game that did similar was Even the Ocean.

Pros
* Simple to use
* Simple to implement
* Easy to visualize the collision geometry
* Flexible (no special code needed for different combinations of visual/invisible tiles with or without collision)

Cons
* Longer workflow (must place more tiles)
* Editing pre-existing levels becomes more cumbersome

For me personally the cons are really tough. They hamper iteration time a lot. It would be great to place a tile and have a default collision type. Then, in editor turn on the "collision view" to see the geometry (for example, drawn in red color), and be able to paint overrides of the defaults for custom one-off tidbits.

This way the cons are heavily mitigated by initial collision types for tiles. Each tile type could have a different initial collision type. When creating a new tile, the initial collision type can initialized as empty, or be set to something specific by the user. None of the pros would be lost, either!

You are totally right, that editing collision geometry in Tiled is cumbersome. And there is no easy way to visualize. Given the lack of visualization... Now I'm thinking the painting approach you mentioned would be the best choice, at least until Tiled is improved.

My idea was probably badly explained (I was hungry) but the intention was not that it would be used to manually set the collision information for every tile level, but to set up the default collision information for each tile in the tileset. Similar to my XML file, but just created using Tiled. For example if the tileset "Beach" has 64 tiles, then create an 8x8 map, "BeachCollision", draw one of each tile into it, then draw the collision information in a separate layer. When the game loads a level that uses the "Beach" tileset, it also loads up "BeachCollision" and reads from there what the default collision should be for each tile.

Having this all as a proper feature in Tiled would of course be better, we can hope it gets added some time! It's had a lot of cool updates recently.
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #826 on: April 25, 2018, 01:32:18 PM »

Ah I see. What a great idea! I'll give this a try. Thanks for sharing the idea!
Logged
Louard
Level 2
**


View Profile WWW
« Reply #827 on: April 27, 2018, 08:23:50 AM »

Ishi, your Alternative Solution idea sounds like a really smart and intuitive work around!

And, qMopey, I totally agree that defining separate visual and collision tiles is a bad idea which will hamper iteration times.

One of the greatest takeaways from working on Suzy Cube levels has been that the next time I make a level-based game, I will do as much work as I can up front to create the tools I need for levels to look shippable (or as close to) by default.

Making it as fast and painless as possible to redesign, delete or refactor levels can only produce better levels in the end. And if creating levels is really fast, then it's much easier to over-produce and then only ship the best levels, or only use the best levels as a starting point for final designs.
Logged

-Louard
louardongames.blogspot.com
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #828 on: April 28, 2018, 03:55:20 AM »

One of the greatest takeaways from working on Suzy Cube levels has been that the next time I make a level-based game, I will do as much work as I can up front to create the tools I need for levels to look shippable (or as close to) by default.

Making it as fast and painless as possible to redesign, delete or refactor levels can only produce better levels in the end. And if creating levels is really fast, then it's much easier to over-produce and then only ship the best levels, or only use the best levels as a starting point for final designs.

Yep I totally agree with this! I would also take a similar approach on future games, making this process quicker and easier is such a big deal. On a related note today I want to write about...

Roots Tileset and Automatic Tiling

I touched upon this briefly in a previous post. I have a system for automatically placing tiles in levels to make level editing easier. This week I worked on a new tileset which makes use of this, so thought I'd go into a few more details.

Mockup

Firstly, the mockup timelapse! The theme for this new tileset and background is underground roots. As usual I took a screenshot of a level where I want to use it, then drew over the top of it.



The tileset is based around a repeating pattern of just 3 tiles. These tiles are designed to join seamlessly when each row is offset to the left of the previous row, like so:



The floor, ceiling and wall tiles also have 3 variations each, so they fit in with the repeating pattern.

Further development

After making the mockup above, I created the following image, to make sure I had all the permutations that I wanted.



The section on the left displays all the tiles in various setups. The section in the top left is all of the tiles that require 3 different variations in order to line up with the repeating pattern of the tileset. The bottom right section contains the tiles that don't have 3 variations - these are the outside corner tiles. I wanted to save myself some work by not having to redraw these tiles again.

Tile size

One interesting point (covered here in more detail) is that my tile grid is 16x16, but the tiles themselves are 24x24, so the tile graphic can overlap. I also separate the outlines of the tiles into a separate layer, which is drawn first, so the outlines all blend together.

For example:



This is just 3x3 tiles, but thanks to the overlap, the tiles can extend beyond the grid slightly. This is super useful, for example the upper corner tiles can have little roots poking out without having to add additional tiles to the tileset.

Getting it into the game

I next made two versions of the tileset image.

A 16x16 one for use in Tiled, and a 24x24 one for use in the game.



The red tiles are the 2nd and 3rd versions of the tiles in the repeating pattern. In the Tiled version of the tileset I colour them in red, indicating that I shouldn't place them into the level, because the repeating pattern is applied automatically (I will demonstrate this below).

The tile with the 'a' on it is a special tile for automatic tile placement! I colour this tile in green in the game version of the tileset because it should never be seen.

I add the new tileset into Tiled, and make liberal use of the 'a' tile, on a couple of different layers:



And then run the game... (also note that I added a simple roots backdrop)



Yep, that bright green 'a' tile is showing up everywhere because the automatic tiling rule isn't set up yet. As mentioned previously I have an XML file for each tileset containing some per-tile information, and one of the things it can contain is the auto-tiling rules.

Code:
<!--
A = the tile to change
()^v = left, right, top, bottom edges
lLrR = lower left, upper left, lower right, upper right corners
1739 = lower left, upper left, lower right, upper right corners - with no inner corner
C = centre
[=] = left, middle, right single-height block
_|~ = bottom, middle, top single-width block
# = single block
i = don't create a border against this tile
qpdb - top left, top right, bottom left, bottom right inner corner overlays
4862 - left, top, right, bottom double-inner-corner overlays
-->
<Automatic cornerReplacesMainTile="true">
0^()LR79
|Cbdlr13
=vpq64[]
A00028#~
0000000_
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
</Automatic>

This system looks like a total mess, but, it works. Shrug Each character here indicates which auto-tiling rule should be applied for each tile of the tileset. I won't go into full details about how this works, but the important parts are:
  • The tile marked with 'A' is the 'a' tile seen in the tileset. If this tile is used, it'll be replaced by one of the other tiles.
  • Let's look at example tile - 'L' is the upper left corner tile. So if the level contains an 'a' tile, which has 'a' tiles on its right and bottom, but doesn't have 'a' tiles to the left and top of it, then it'll be replaced with the 'L' tile.
  • Some of the auto tiles for inner corners are drawn as overlays rather than full tiles. This is just to save me having to author every permutation of tiles with inside corners. This is pretty bespoke and I use it different in each tileset, so don't worry about the details too much.

So the auto tile rule is set up, how does the game look now?



Better. Now I need to apply the 3-tile repeating pattern, as currently just the first version of each tile is being used. Again this information is added to the XML file:

Code:
<YVariation frequency="3">
03330000
33330000
33330000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
</YVariation>

'frequency="3"' means the pattern repeats every three tiles. And the '3' on each tile indicates that the next version of the tile in the repeating pattern is found 3 tiles downwards in the tileset. For example:

  • The game wants to draw a tile. The tile's position within the tileset is 1,1. The tile's position in the level is x=121,y=12
  • It decides which variation of the tile to use: (x+y) % frequency = 121+12 % 3 = 1, which is the second variation of the tile (the variations are numbered 0,1,2).
  • The rule tells us that that the next tile in the repeating pattern is 3 tiles below in the tileset, so we add variation*3=3 to the tile's Y position within the tileset. Instead of drawing the tile found at 1,1 in the tileset, we draw the tile at 1,4 in the tileset.
  • Then, we want to draw the same tile (1,1) again, but at x=122,y=12. By doing the same calculation we'll get a variation number of 2, which adds variation*3=6 to the Y position within the tileset, so we draw the tile 1,7 which is the 3rd variation of the tile. Thus the pattern repeats across the whole level.

I don't know how well I explained that but, this is the result.



Much more variation, and editing the level in Tiled takes minimal effort because I'm just placing the same 'a' tile everywhere.

Done!

« Last Edit: April 28, 2018, 08:32:42 AM by Ishi » Logged

qMopey
Level 6
*


View Profile WWW
« Reply #829 on: May 07, 2018, 06:26:03 PM »

That is an absolutely crazy system. I really like it! Looks to be very useful for you. I love seeing devs post about these specialized tools they make. Very cool.

How long did it take to design and implement this system? Do you think it really helped you make more underground roots levels faster? As in you saved time?

P.S. Went ahead and tried out the idea we talked about in a demo: https://github.com/RandyGaul/tinycavestory. Worked very well!
Logged
qMopey
Level 6
*


View Profile WWW
« Reply #830 on: May 09, 2018, 01:24:41 PM »

Oh I was also wondering how you define animations. As in frame based animation for all your different looping sprites. For things like characters and enemies or other looping images. Do you have a defined serialization format?
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #831 on: May 10, 2018, 11:17:37 AM »

How long did it take to design and implement this system? Do you think it really helped you make more underground roots levels faster? As in you saved time?

So far I've only used the tileset in that one room Smiley

I'm not sure how long I've spent on these systems - they've been pieced together bit by bit over time, starting with some old code from a Ludum Dare game and slowly improved as I wanted more features.

I don't doubt that this will save time though! Any small time saving that can be made when editing levels is a huge bonus. And not only does it save time, it just makes the level editing process so much easier, that I'll be more willing to make little tweaks that improve the level, rather than just thinking "eh that'll do".

Oh I was also wondering how you define animations. As in frame based animation for all your different looping sprites. For things like characters and enemies or other looping images. Do you have a defined serialization format?

Yep all my animations are defined in an XML format.
  • The XML defines the animation strips, the number of cels (frames) in each strip, the length of each cel, and other things like a position offset for each strip.
  • The XML also defines how the sprite is positioned, e.g. by the centre of the whole sprite, or the centre of the bottom edge of the sprite. This avoids the code needing to query the size of the sprite in order to centre it around a position.
  • The code decides when to play each strip, and also whether to play the strip forwards/backwards/looping/ping pong (alternating backwards and forwards).

Example of the XML format, this is some of Leilani's animations:

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

<Strip name="Idle" filename="Idle.png" cels="3"/>

<Strip name="Dead"
filename="Dead.png"
cels="4"
length60="4"
offsety="4">
</Strip>

<Strip name="IdleAnim0"
filename="IdleAnim_Hair.png"
cels="7"
length60="8">
</Strip>
<Strip name="IdleAnim1"
filename="IdleAnim_Jog.png"
cels="8"
length60="7">
<Cel index="0" length60="10"/>
</Strip>
<Strip name="IdleAnim2"
filename="IdleAnim_FistPunch.png"
cels="10"
length60="6">
</Strip>

<Strip name="IdleToCrouch"
filename="IdleToCrouch.png"
cels="2"
length60="5">
</Strip>
<Strip name="CrouchToIdle"
filename="CrouchToIdle.png"
cels="2"
length60="5">
</Strip>
<Strip name="Crouch" filename="Crouch.png"/>

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

...

</AnimatedSprite>

  • horizontalHandle="1": This means the sprite is horizontally centred around the X position that its been placed at.
  • verticalHandle="1": This means the sprite is vertically positioned with the bottom edge of the sprite at its Y position.
  • length60="4": This means each cel of the strip is displayed for 4 frames at 60 fps. I find this a more useful way for me to think about the length of each cel, rather than in terms of pure time (e.g. 0.0666).
  • offsety="4": This offsets the sprite 4 pixels downwards. It's an easy way to adjust how things look, and the game code doesn't have to care about it. For the 'dead' animation, Leilani sticks her legs out in the air and it just looked better to move the whole sprite downwards a bit, it makes the transition from other animations feel better.
  • I can use the <Cel> tags to optionally override the length of individual cels, so not every cel in the strip has to be the same length. Sometimes adjusting the length of a couple of the cels can really help to make the animation a bit more punchy.
  • <Cel index="0" custom="2"/>: I can also specify a 'custom' value for a cel. When the animation reaches this cel, the game code gets a callback, and can do something based on the value. In this case the value '2' in the run animation triggers a subtle footstep sound to play.
  • filename="Run.png": Each strip is a separate png file, with the cels arranged vertically. All of the cels within that strip are the same size, but for the final build of the game the Sprite Packer will crop all of the empty space out of each cel, so I don't worry about making optimal use of space in each image.

And here's a small example of some animation-playing code.

Code:
void cEntityPlayer::SelectOnFloorAnim()
{
if(m_Substate == SUBSTATE_CROUCH)
{
if(m_MultiSprite.IsAnimPlaying(m_CurrentSprite, "IdleToCrouch"))
{
return;
}

m_MultiSprite.SetAnimAllSprites(cAnimatedSprite::ANIM_LOOP, "Crouch");

When Leilani started crouching, the "IdleToCrouch" animation was played. Then this function (which is called every frame) checks whether that animation is finished yet; if it is, it starts playing the "Crouch" anim instead.

In some ways it would be nice to extract this kind of animation logic out of the code and make it data-driven. For example if I could tell the sprite "go to the crouch state", and it would know which transition animation(s) to play to get there, and handle it all automatically. But, for this kind of game where the feedback given by the animations is so important to how responsive the game feels, I think it's for the best that that animations are deeply integrated into the actual logic for how the character moves.

One more thing - "m_MultiSprite.SetAnimAllSprites" - this is using a MultiSprite which is a collection of multiple different sprites. The way I handle Leilani's four different powerups (small, big, fire, water) is to animate all four sprites all of the time. So when I play the "Run" animation, four different versions of it play on four different sprites, all positioned in the same place. But if Leilani is small at the time, then only that one sprite is visible. This works well because when Leilani collects a powerup, I can swap the visibility of the sprite to show Leilani in her appropriate colour/size, and I know the animation is just going to continue to work without problems.

Hope that was interesting Smiley

Gamedev break

I've been meaning to post to mention that I'm on a bit of a development break for the game for now. I'm in a super busy time at work right now, plus have multiple family visits / trips / holidays throughout the next couple of months, so don't have much time or energy left for game dev. I might do a couple of bits here and there, so hopefully the devlog won't be entirely silent! I also might be able to find a couple of already-implemented features to talk about to fill the space Smiley Thanks for your patience during this quiet spell.
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #832 on: May 10, 2018, 12:56:01 PM »

Hmm yes very interesting. I just realized one thing I'm missing is support for offsetx/offsety. I was hard-coding these in various places, but it would definitely be good to pull it out into the animation file. Good idea. Hmm I should also add loopbackwards and pingpong. These sound like fun tools to use. Thanks for posting!

Have you put any thought into localizing your game text? Especially in regards to font loading/rendering?
Logged
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #833 on: May 21, 2018, 01:20:24 PM »

Have you put any thought into localizing your game text? Especially in regards to font loading/rendering?

Thanks for the question and sorry for the slow reply! Localisation is definitely thought about, and I've worked on it in little bits here and there throughout the project. While I'm not planning on getting any actual translation done until the very late stages of the project, there's plenty of work that I have done / am doing to prepare for that moment.

Fonts: Supporting non-ASCII characters

Firstly I recommend a look at this post from three years ago. It explains some basics of how fonts are stored and drawn.

It mentions that I had just added support for a font to have multiple 'pages' of characters. Since then I've made more use of this system, so I'll go into a bit more detail about how this works.

A font page is an image containing some characters, with some XML that specifies which characters are in that page.

ASCII page

This is the standard ASCII character set. This page is a special case as it doesn't have an XML file with it, by default the code knows it contains ASCII characters 33-126.



The "16x24" in the corner of the image is just a reminder for myself of the pixel size of each character.

Game page - Named characters

This page has some game-related icons, for example the shell icon. This is used to draw the icon for the player's current shell count in the HUD. It's way easier to display the icon as part of the font, rather than adding it to the UI as a separate image.



The XML for this page gives a name to each character.

Code:
<FontLayout>
<Row>shell, chip, battery</Row>
</FontLayout>

These characters can be referenced by their name in strings that are drawn in the game. For example, drawing the string "$shell; 510" will appear like so:



This system of named characters is also how I display control button prompts, for example "$xboxA;" or "$keyEscape;".

Accents page - UTF8 characters

Another page is the Accents page, to which I've added a mishmash of non-ASCII characters, generally focused on characters with accents or additional symbols that are used in non-English languages.



The XML file:

Code:
<FontLayout>
<Row>ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏ</Row>
<Row>ÑÒÓÔÕÖØŠÙÚÛÜŮÝŸŽ</Row>
<Row>ÆŒẞĀĒĪŌŪ</Row>
<Row>àáâãäåçèéêëìíîï</Row>
<Row>ñòóôõöøšùúûüůýÿž</Row>
<Row>æœßāēīōū</Row>
</FontLayout>

I use the UTF8 format for non-ASCII characters. I like it as a format as it can be stored in arrays of single byte values (char[]) like basic ASCII strings. This makes it much easier to convert code to begin supporting non-ASCII characters because the way strings are stored doesn't actually need to change. It's only when it's time to actually draw the string that the font system cares whether each of the values in the string is an ASCII character or part of a UTF8 character.

Using the XML to manually assign a UTF8 character to each of the characters within the image is a simple solution that allows me to easily specify exactly which characters I want to support, and group them together in whatever way I like. For example I also have a page that contains just the "Missing Character" character:



It's useful to add this to every font. I coded the font system to automatically draw this character whenever it comes across a UTF8 character that isn't contained in the font.

Here's an example of the UTF8 support in action. The smaller font at the bottom doesn't yet have the 'Accents' page, so the Missing Character is drawn instead.



None of these characters (named characters or UTF8) require any actual special code for being drawn - they are positioned and kerned in the same way as the ASCII characters.

Hopefully this gives some insight! I have more to say about other parts of the localisation process but I'll write that next time.
Logged

EJlol
Level 0
**


View Profile
« Reply #834 on: May 22, 2018, 05:31:14 AM »

What about non latin script languages, such as chinese and arabic? Are you planning to support any of these? I imagine supporting these languages would be a lot more work than the latin script languages since you would have to draw every word used instead of just the letters.
Logged
DrDerekDoctors
THE ARSEHAMMER
Level 8
******



View Profile WWW
« Reply #835 on: May 22, 2018, 08:33:13 AM »

I only just noticed that this image...



...looks like some nightmarish pug-face which has a rotten hole in the middle. :|

And yes, that is the extent of my contribution to this thread.

EDIT: Oh, one question - what made you opt for duplicate outline images for your font thing over using a shader?
Logged

Me, David Williamson and Mark Foster do an Indie Games podcast. Give it a listen. And then I'll send you an apology.
http://pigignorant.com/
fall_ark
Level 2
**



View Profile
« Reply #836 on: May 28, 2018, 02:20:03 AM »

Hopefully this gives some insight! I have more to say about other parts of the localisation process but I'll write that next time.

Looking forward to that. This project has captivated me from the very beginning so if there ever is a plan for Chinese localization I'll gladly offer any help needed. Smiley
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., find me at Twitter @SoM_lo
Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #837 on: May 30, 2018, 11:58:35 AM »

I only just noticed that this image...
...looks like some nightmarish pug-face which has a rotten hole in the middle. :|

 No No NO

EDIT: Oh, one question - what made you opt for duplicate outline images for your font thing over using a shader?

I like the control over the appearance of the outline that I get when I draw the outline by hand.

Looking at some examples of how to outline the letter C, a shader could choose to:
- Draw the outline on adjacent and diagonally adjacent pixels (second example), this makes edges that should be smooth look way too heavy.
- Draw the outline only on adjacent pixels (third example), this makes the sharp square corners look too rounded.



By hand-drawing the outline myself (fourth example), the curvy bits can look curvy and the sharp corners can look sharp.
Logged

Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW
« Reply #838 on: May 30, 2018, 12:14:42 PM »

What about non latin script languages, such as chinese and arabic? Are you planning to support any of these? I imagine supporting these languages would be a lot more work than the latin script languages since you would have to draw every word used instead of just the letters.

I'd like to support as many languages as possible, at the moment it all feels achievable but when the time comes I'll have to make judgements on cost / time / market size to decide which are worthwhile.

For Chinese I would indeed have to draw a lot of characters, but since the game is relatively light on text, as long as I only drew the characters that were necessary then I think it would be doable. I expect line breaks would need to be done manually (at least I heard that's the case for Japanese), but other than that my font system should handle it fine.

For Arabic the right-to-left writing would be a lot of work since my system was written with left-to-right in mind. I believe also that characters change appearance when they are written next to others which I would have to add support for. I don't have a lot of knowledge about the language which also wouldn't help me to implement it well. So I suspect it won't be practical to add support for, but maybe in the future, who knows!
Logged

fall_ark
Level 2
**



View Profile
« Reply #839 on: May 31, 2018, 03:01:24 AM »


For Chinese I would indeed have to draw a lot of characters, but since the game is relatively light on text, as long as I only drew the characters that were necessary then I think it would be doable. I expect line breaks would need to be done manually (at least I heard that's the case for Japanese), but other than that my font system should handle it fine.

It's generally a bad idea to try to create Chinese pixel font by oneself, especially since that size (11~12px) is already the minimum needed to make the font readable, so you can't really have a unique style. There are a few free Chinese pixel fonts around and I can send you the one we made ourselves (it's used in The Count Lucanor and in the old pixel-font version of Dead Cells) if needed.

Basic line-breaking simply needs to treat every CJK character (unicode 0x4e00 to 0x9fea) as a space. Some extra work is needed in relation to punctuation marks, but it's not really a big deal.
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., find me at Twitter @SoM_lo
Pages: 1 ... 40 41 [42] 43 44 ... 67
Print
Jump to:  

Theme orange-lt created by panic