シェフィの実装を始めた俺達はようやく仮UIをまともにしようと活動を始めたばかり。何となくそれっぽい感じにカードを表示してみたものの、まだまだやることは沢山残っている。
果たしてよりカッコイイ見た目にできるのであろうか……?
(※ソースコードはGitHubで公開されておりすぐに遊ぶこともできます)
前回、個々のカードをそれっぽく表示するところまでは行いましたが、いくつか改善すべき点が残っていました:
と言う訳で順番に片付けていきましょう。
本題に入る前にちょっと整理をしておきます。
個々のカードを表すDOMツリーを返す visualizeCard
ですが、
前回では以下のような実装にしていました:
function visualizeCard(card) {
var $body = $('<span>');
$body.addClass('body');
$body.text(card.name);
var $card = $('<span>');
$card.addClass('card');
$card.addClass(cardType(card));
$card.addClass('rank' + card.rank);
$card.append($body);
return $card;
}
このままだとカードの位置を自由自在に調整しようとした時に面倒なので、
カードの外枠用にもう一段階レイヤー(.border
)を追加することにします:
function visualizeCard(card) {
var $body = $('<span>');
$body.addClass('body');
$body.text(card.name);
var $border = $('<span>');
$border.addClass('border');
$border.append($body);
var $card = $('<span>');
$card.addClass('card');
$card.addClass(cardType(card));
$card.addClass('rank' + card.rank);
$card.append($border);
return $card;
}
これに合わせてCSSも微調整しておきます。
.card {
display: inline-block;
margin: 0.5ex;
}
.card > .border {
display: inline-block;
border: thin solid #000;
}
.card > .border > .body {
overflow: hidden;
white-space: nowrap;
}
/* ... 以下省略 ... */
以上の変更で見た目は変わらないものの、
以降の表示の調整がやり易くなります。
追放領域はさておいて、捨て山は割と大量のカードが並びます。
現状の表示はカードとカードの間にある程度マージンを入れているのですが、
このマージンは手札の表示には良いとしても、
捨て山に対してはいささか横幅を取り過ぎています。
と言う訳でカードを重ね気味に並べて表示してコンパクトな見た目にしましょう。
HTML側では捨て山と追放領域に該当する部分を以下の形で記述していましたが:
<div id="discardPile">Discard pile: <span class="cards"></span></div>
<div id="exile">Exile: <span class="cards"></span></div>
表示形態を区別する為に class
を追記しましょう:
<div id="discardPile">Discard pile: <span class="lined cards"></span></div>
<div id="exile">Exile: <span class="lined cards"></span></div>
後はCSSをちょいと調整すれば完成です:
.lined.cards > .card {
margin: 0;
width: 0;
overflow: visible;
}
.lined.cards > .card {margin-left: 1.5em;}
.lined.cards > .card:first-child {margin-left: 0;}
こうすると以下のような表示になります:
うーん。間延びしていた頃よりは良いと思いますが、何だか綺麗に並び過ぎてて暖かみに欠けますね。
実物のカードをここまでキッチリ並べることは不可能なので、もう少し遊びを入れてみることにしましょう:
.lined.cards > .card:nth-child( 1) {transform: rotate(-4deg);}
.lined.cards > .card:nth-child( 2) {transform: rotate( 0deg);}
.lined.cards > .card:nth-child( 3) {transform: rotate( 2deg);}
.lined.cards > .card:nth-child( 4) {transform: rotate(-3deg);}
.lined.cards > .card:nth-child( 5) {transform: rotate( 1deg);}
.lined.cards > .card:nth-child( 6) {transform: rotate( 4deg);}
.lined.cards > .card:nth-child( 7) {transform: rotate( 2deg);}
.lined.cards > .card:nth-child( 8) {transform: rotate( 1deg);}
.lined.cards > .card:nth-child( 9) {transform: rotate( 4deg);}
.lined.cards > .card:nth-child(10) {transform: rotate(-3deg);}
.lined.cards > .card:nth-child(11) {transform: rotate(-4deg);}
.lined.cards > .card:nth-child(12) {transform: rotate( 0deg);}
.lined.cards > .card:nth-child(13) {transform: rotate( 4deg);}
.lined.cards > .card:nth-child(14) {transform: rotate(-1deg);}
.lined.cards > .card:nth-child(15) {transform: rotate(-3deg);}
.lined.cards > .card:nth-child(16) {transform: rotate( 1deg);}
.lined.cards > .card:nth-child(17) {transform: rotate( 2deg);}
.lined.cards > .card:nth-child(18) {transform: rotate(-1deg);}
.lined.cards > .card:nth-child(19) {transform: rotate( 1deg);}
.lined.cards > .card:nth-child(20) {transform: rotate(-3deg);}
.lined.cards > .card:nth-child(21) {transform: rotate(-4deg);}
.lined.cards > .card:nth-child(22) {transform: rotate( 0deg);}
結果はこうなります:
まあこんなところではないでしょうか。
さて、次はひつじ山の見た目を整えましょう。
まずはHTML側を調整します:
<div id="sheepStock">
Sheep stocks:
<div id="sheepStock1" class="stacked cards"></div>
<div id="sheepStock3" class="stacked cards"></div>
<div id="sheepStock10" class="stacked cards"></div>
<div id="sheepStock30" class="stacked cards"></div>
<div id="sheepStock100" class="stacked cards"></div>
<div id="sheepStock300" class="stacked cards"></div>
<div id="sheepStock1000" class="stacked cards"></div>
</div>
次にCSSをちょろっと書き足して:
.stacked.cards > .card,
.lined.cards > .card {
margin: 0;
width: 0;
overflow: visible;
}
.stacked.cards > .card {position: relative;}
.stacked.cards > .card:nth-child( 1) {top: 0.00ex; left: 0.00ex;}
.stacked.cards > .card:nth-child( 2) {top: 0.30ex; left: 0.30ex;}
.stacked.cards > .card:nth-child( 3) {top: 0.60ex; left: 0.60ex;}
.stacked.cards > .card:nth-child( 4) {top: 0.90ex; left: 0.90ex;}
.stacked.cards > .card:nth-child( 5) {top: 1.20ex; left: 1.20ex;}
.stacked.cards > .card:nth-child( 6) {top: 1.50ex; left: 1.50ex;}
.stacked.cards > .card:nth-child( 7) {top: 1.80ex; left: 1.80ex;}
.stacked.cards > .card:nth-child( 8) {top: 2.10ex; left: 2.10ex;}
.stacked.cards > .card:nth-child( 9) {top: 2.40ex; left: 2.40ex;}
.stacked.cards > .card:nth-child(10) {top: 2.70ex; left: 2.70ex;}
.stacked.cards > .card:nth-child(11) {top: 3.00ex; left: 3.00ex;}
.stacked.cards > .card:nth-child(12) {top: 3.30ex; left: 3.30ex;}
.stacked.cards > .card:nth-child(13) {top: 3.60ex; left: 3.60ex;}
.stacked.cards > .card:nth-child(14) {top: 3.90ex; left: 3.90ex;}
.stacked.cards > .card:nth-child(15) {top: 4.20ex; left: 4.20ex;}
.stacked.cards > .card:nth-child(16) {top: 4.50ex; left: 4.50ex;}
.stacked.cards > .card:nth-child(17) {top: 4.80ex; left: 4.80ex;}
.stacked.cards > .card:nth-child(18) {top: 5.10ex; left: 5.10ex;}
.stacked.cards > .card:nth-child(19) {top: 5.40ex; left: 5.40ex;}
.stacked.cards > .card:nth-child(20) {top: 5.70ex; left: 5.70ex;}
.stacked.cards > .card:nth-child(21) {top: 6.00ex; left: 6.00ex;}
.stacked.cards > .card:nth-child(22) {top: 6.30ex; left: 6.30ex;}
#sheepStock > .stacked.cards {
display: inline-block;
width: 10ex;
}
後は drawGameTree
におけるひつじ山のレンダリング方法をテキストからカードに差し替えます:
S.RANKS.forEach(function (rank) {
$('#sheepStock' + rank).html(visualizeCards(w.sheepStock[rank]));
});
結果はこの通り:
おお……大分それっぽい。やる気が湧いてきますね。
最後に、山札の表示を改善しましょう。
実物で遊ぶ時はカードの山を作るものですが、
この実装で山として表示すると物凄く右下に伸びちゃいますし、
かといってカード間の隙間を減らすと残り枚数が視覚的に認識し辛くなります。
と言う訳で山札は捨て山のようにカードを並べて表示することにしましょう。
ただし、捨て山のようにカード名を見せる必要は無いので、
捨て山よりもカード間の隙間は狭めにしましょう。
隙間の度合いの調整も含めて、HTMLの調整は以下のようにしましょう:
<div id="deck">Deck: <span class="tightly lined cards"></span></div>
<div id="discardPile">Discard pile: <span class="loosely lined cards"></span></div>
<div id="exile">Exile: <span class="loosely lined cards"></span></div>
CSSの調整は一瞬で済みますね:
.tightly.lined.cards > .card {margin-left: 0.25em;}
.loosely.lined.cards > .card {margin-left: 1.5em;}
.lined.cards > .card:first-child {margin-left: 0;}
ただし問題は drawGameTree
です。
他の領域と異なり、山札のカードは裏向きになっています。
一先ず裏向きのカードをN個作る makeFaceDownCards
があるものと仮定しましょう:
$('#deck > .cards').html(visualizeCards(makeFaceDownCards(w.deck.length)));
makeFaceDownCards
はざっくり以下のように実装しましょう:
function makeFaceDownCards(n) {
var cards = [];
for (var i = 0; i < n; i++)
cards.push({name: '', type: 'face-down'});
return cards;
}
それと、カード種別を判断する cardType
もこれに合わせて変更しておきましょう:
function cardType(card) {
return card.type || (card.rank === undefined ? 'event' : 'sheep');
}
最後に裏向きのカード用のCSSを書き足せば完成です:
.card.face-down {
line-height: 0;
}
.card.face-down > .border {
background: #393;
}
.card.face-down > .border > .body {
display: inline-block;
width: 4.6ex;
height: 6.8ex;
margin: 0.5ex;
padding: 0.5ex;
border-radius: 1ex;
background: #9d9;
}
結果はこうなります:
うーむ。なかなか良い感じの盤面の表示になってきました。
最初の仮UIに比べればずいぶんやる気が出てきそうな気がします。
見た目に凝り始めるとキリがないので、
そろそろ利便性を考慮した改善を行いたいものです。
という訳で次回はUI本格化編/オンラインヘルプの巻です。