2

Let me preface this question by saying that I'd like to avoid using javascript to solve this problem. If possible (it may not be) I'd like to keep all of this "logic" in CSS.

I am creating a board game (Khet) for fun in javascript. The board pieces can have multiple states, such as:

  • Color (which player owns the piece)
  • What type of piece it is (pyramid, sphinx, blank tile, etc)
  • What direction the piece is facing (pieces may be rotated as well as moved)
  • If the piece is reflecting a laser
  • If the piece is damaged by a laser

That being said, I have one sprite.png file containing all of the possible combinations for every piece and I would like to be able to use CSS classes to move the pieces through their states.

For example, imagine the red player has a pyramid piece on the board and it is facing northeast. This piece would have the classes .red, .pyramid, and .northeast. If the piece is struck by a laser and reflects it, all I would have to do is add a .laser class to the piece and CSS background-position classes would kick in and change the piece to have the proper image.

This leads to a few questions:

  1. Is this even possible to do in pure CSS?
  2. How would you arrange the images in sprite.png such that these background-position classes would work?
  3. What would the classes look like (this probably depends on #2)?
  4. Do I need to break the image apart? Like placing all red pieces in one sprite_red.png and blue in sprite_blue.png?

I've put this on the back burner while I work on some of the other portions of the game. Any help would be appreciated!

jbabey
  • 45,965
  • 12
  • 71
  • 94
  • I guess *yes* for #4, since you can only encode two dimensions in a grid. – bfavaretto Jan 24 '13 at 01:20
  • @bfavaretto yes i was just thinking about the same thing. only having `background-position-x` and `background-position-y` available restricts the states I can toggle to two per image file. – jbabey Jan 24 '13 at 01:21
  • But (just thought of that), you could put all reds at x<=500, blues at 501-1000 etc. Interesting question. – bfavaretto Jan 24 '13 at 01:23
  • @bfavaretto so weird. I just thought of the same thing. As long as there's an established system between the image and the classes I could use conjuct classes like `.red.pyramid.northeast` to segment an axis of the image and then toggle `.laser` on the other axis. If you can write that up as an answer I would accept (assuming no one can think of something better). – jbabey Jan 24 '13 at 01:25
  • I still don't see how to avoid js. I prefer to think about it a bit longer, and I'll write an answer if I come up with a good/complete solution. – bfavaretto Jan 24 '13 at 01:30

4 Answers4

3

You can definitely accomplish this all in one sprite. It's just a matter of how large of an image you want to work with.

|      SPHINX         |        Pyramid       |
Red  |  Blue  | Oran  |  Red  |  Blue  | Oran
 N       N       N        N       N       N
 NE      NE      NE       NE      NE      NE
 E       E       E        E       E       E
 SE      SE      SE       SE      SE      SE
 S       S       S        S       S       S
 SW      SW      SW       SW      SW      SW
 W       W       W        W       W       W
 NW      NW      NW       NW      NW      NW
|               DAMAGE                       |
 N       N       N        N       N       N
 NE      NE      NE       NE      NE      NE
 E       E       E        E       E       E
 SE      SE      SE       SE      SE      SE
 S       S       S        S       S       S
 SW      SW      SW       SW      SW      SW
 W       W       W        W       W       W
 NW      NW      NW       NW      NW      NW
 ....etc....

From there, CSS like

.piece { background:url('giant-sprint.png') no-repeat 0 0 }

.piece.sphinx.red.n { background-position:0px 0px }
.piece.sphinx.red.n.damage { background-position:0px 108px }

.piece.sphinx.red.s.damage { background-position:0px 156px }
.piece.pyramid.blue.s.damage { background-position:60px 156px }

.piece.pyramid.blue.sw { background-position:60px 72px }
.piece.sphinx.blue.sw.damage { background-position:60px 168px }

It won't be that long, and you'll memorize the column's left and top positions and which piece they represent just from the pattern established.

I'd definitely limit the pieces to a single container (at least for v1.0). One sprite for normal state, and one for damaged pieces...maybe... may make your life easier from a maintenance POV. As the game matures, the proper method should become more obvious to you.

Dawson
  • 7,567
  • 1
  • 26
  • 25
2

1) Is this even possible to do in pure CSS?

Yes! But you'll have to face two limitations: no math, and no separate assignment of background-position-x and background-position-y (since those properties are non-standard). As a result, you will need a selector and rule for every single combination, like .red.pyramid.northeast, .red.pyramid.northeast.reflecting, .red.pyramid.northeast.damaged and so on. I guess this answers question #3 too.

2) How would you arrange the images in sprite.png such that these background-position classes would work?

There are multiple possibilities. I'd go with something like grouping the same shapes in columns, then repeating those columns for each different color. The rows would be for the 8 possible orientations, times the 3 states.

4) Do I need to break the image apart? Like placing all red pieces in one sprite_red.png and blue in sprite_blue.png?

I initially thought that would be a good idea. Maybe it is, but you'll still end up with a large number of rules.

If you really want to use just CSS, I strongly suggest you generate the stylesheet with some sort of script (maybe LESS os SASS? I never used them, so I don't know), then minify for deployment. Otherwise, maintaining it will probably be an unpleasant experience.

Community
  • 1
  • 1
bfavaretto
  • 71,580
  • 16
  • 111
  • 150
2
1. Is this even possible to do in pure CSS?

YES, definitely possible to do and many possible ways to do it.

2.How would you arrange the images in sprite.png such that these
background-position classes would work?

Let's say for arguments sake that you have six different board pieces and six different colours that these board pieces can take.

It's totally up to you how you want to arrange them. Do it by whatever way makes sense to you.

You could have all the Pyramid pieces in the first row in all of their different colours. Then you could have all of the Sphinx pieces in the second row and so on.

For the direction of the board pieces and whether it's showing a reflected laser or laser damage I'd recommend a separate element for each of the possible three layers like so:

So you have a SPAN tag containing two other SPAN tags.

  • The first SPAN tag has a background of the board piece in the correct colour.
  • The second SPAN tag has a background of the direction icon.
  • The third SPAN tag has a background of the reflected or damaged laser icon.

And you just stack the SPANS on top of each other using CSS absolute positioning and z-index. Obviously the direction and reflected/damaged icons would need to be PNG images so they could be transparent so you could see the board piece icon below them.

This means you don't have to create a version of each board piece in each colour varaition with laser damage, in a particular direction, with a laser reflected etc.

3. What would the classes look like?

.layer1 {
    position: relative;
    z-index: 1;
    background-image: url(spite1.png);
    background-repeat: no-repeat;
}

.layer2 {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    background-image: url(spite2.png);
    background-repeat: no-repeat;
}


.layer3 {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 3;
    background-image: url(spite2.png);
    background-repeat: no-repeat;
}

.pyramid-red {
    background-position: 0 0;
}

.pyramid-blue {
    background-position: -24px 0;
}



4. Do I need to break the image apart? Like placing all red pieces in one sprite_red.png and blue in sprite_blue.png?

I'd recommend using two sprirtes:

  • The first one would contain the board pieces and their colour variants.

  • The second one would contain the directional images and the reflected state and damaged state.

I hope that all makes sense!

Billy Moat
  • 20,792
  • 7
  • 45
  • 37
  • if i wanted to the use the multiple background images, how would i specify which image i want to shift when i use `background-position` to change states? – jbabey Jan 29 '13 at 13:28
  • That's a damned good point. My bad. I'll revise my answer to a slightly different method. – Billy Moat Jan 29 '13 at 14:41
  • @jbabey - I've posted a different solution above now. Have a read and see if that one makes sense after my first failed attempt! – Billy Moat Jan 29 '13 at 14:57
0

The first step is to plan the sprite map.

I will set 2 types. That will be classes type1 and type2, and go in horizontal. And I will set 4 directions, that will go in vertical. If the size of the sprite is 32, then type2 will have an horizontal offset of 32, and the directions will have an offset of 32, 64, and 96.

Next will be the color. Here, I need to do some math. Posible with CSS ? no and yes. If I put an element inside another, the left property is cumulative. So, we will need another element, and will play with left and tops:

.type1             { position: relative;}
.type2             { left: -32px; position: relative;}

.dir1             { }
.dir2             { top: -32px}
.dir3             { top: -64px}
.dir4             { top: -96px}

.color2 img  { left: -64px}

The markup for that is

<div class="clip solution">
<div class="type2 dir4 color2 reflecty damagey">
<img src="./SpritewithImages.png">
</div>
</div>

The color class should go with the image, but then it would be a problem having classes in 2 different places, so I set the CSS rule to work with the selector in the father. The color classes will be increments of 64 pixels because we have 2 types. If we had more, that would increase accordingly Now we are left with 2 dimensions more. To acomodate that, we would need another layer of div. To avoid this, we pack 2 decisions in 1 dimension. reflecting yes, damage yes, and both (no need to specify no damage or no reflection). The composite selectors:

.reflecty img  { top: -128px}
.damagey img  { top: -256px}
.reflecty.damagey img  { top: -384px}

And that is all ! Of course, the target could be not to do everything in sprites. Then probably you could play with rotations , and the laser thing could go in a transparent layer over-imposed.

That is the fiddle

You will see there is a display of the intermediate sprite selected, before getting to the final one

edited

I have included in the fiddle buttons to toggle states. You can see to the right the grid with the sprites. the columns are intended to be:

1 - figure 1, color 1
2 - figure 2, color 1
- separator
3 - figure 1, color 2
4 - figure 2, color 2

and the columns are

1 - direction 1
2 - direction 2
4 - direction 4
- separator
5 - reflection, direction 1
8 - reflectio , direction 4
- separator
9 - damaged, direction 1
12 - damaged, direction 4
- separator 
13 reflection, damaged, direction 1

the updated fiddle is here

vals
  • 61,425
  • 11
  • 89
  • 138
  • I believe you meant to edit your original answer instead of posting another. – jbabey Jan 29 '13 at 02:09
  • I started a new one because I have changed most of the solution. Now I have put the previous answer in "delete" status – vals Jan 29 '13 at 06:13
  • @jbabey I have added functionality to the fiddle, now you can see it changing the sprite – vals Jan 31 '13 at 10:26