まえがき
うちにダーツボードが転生してきたのですが、計算機能がないので自作します。
転生される民のごとき知識なので、もっといい方法があるかもしれません。
タイトル by
なろうパロディ☆タイトルジェネレータ
成果物
See the Pen
Darts board by Oba Takeshi (@obq777)
on CodePen.
作成したい仕様
- ダーツボードを押したら点を入れたい
- 01やクリケットなど各種ゲームに対応 (現状: 501のみ)
CSSでダーツボードを再現する
ダーツボード自体は円形ですが、部分的には扇型の集合です。
が、CSSで90°以外の扇型の再現は難しく、JavaScriptのCanvasを使う手もありますが、得点計算時の当たり判定のとき苦労しそうなので、三角形の寄せ集めでダーツボードを今回再現することにします。
方針としては
- 半径が同じ二等辺三角形を20個作成し、ボトムを基点に18°(=360°/20)ずつ回転(ループで作成)
- 交互に色が変わるため、ループのインデックスに応じて色を指定
- 純粋に部品の見た目を再現するのは難しいため、中心に頂点のある三角形で基本的に再現し、z-indexを用いて内側を隠すことで再現
$points:20,1,18,4,13,6,10,15,2,17,3,19,7,16,8,11,14,9,12,5;@each$iin$points{.single-inner-#{$i}{top:50%;left:50%;position:absolute;height:0;width:0;@if(index($points,$i)-1)%2==0{border-top:black140pxsolid;}@else{border-top:white140pxsolid;}border-right:22pxsolidtransparent;border-bottom:0pxsolidtransparent;border-left:22pxsolidtransparent;transform:translate(-50%,-100%)rotate(#{18*(index($points,$i)-1)}deg);transform-origin:bottom;z-index:5;}}
ひとつの部品をつくり、18°(=360°/20)ずつ回転し、色を変えます。
SCSS (長いし同じものが続きます)
```scss
.darts-board {
position: relative;
height: 800px;
width: 800px;
margin: 0 auto;
}
.inner-bull {
position: absolute;
top: 50%; left: 50%;
height: 5%; width: 5%;
transform: translate(-50%, -50%);
border-radius: 100%;
background: black;
z-index: 7;
}
.outer-bull {
position: absolute;
top: 50%; left: 50%;
height: 10%; width: 10%;
transform: translate(-50%, -50%);
border-radius: 100%;
background: red;
z-index: 6;
}
$points: 20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5;
@each $i in $points {
.single-inner-#{$i} {
top: 50%; left: 50%;
position: absolute;
height: 0; width: 0;
@if (index($points, $i)-1) % 2 == 0 {
border-top: black 140px solid;
} @else {
border-top: white 140px solid;
}
border-right: 22px solid transparent;
border-bottom: 0px solid transparent;
border-left: 22px solid transparent;
transform: translate(-50%, -100%) rotate(#{18*(index($points, $i)-1)}deg);
transform-origin: bottom;
z-index: 5;
}
}
@each $i in $points {
.triple-#{$i} {
top: 50%; left: 50%;
position: absolute;
height: 0; width: 0;
@if (index($points, $i)-1) % 2 == 0 {
border-top: red 160px solid;
} @else {
border-top: blue 160px solid;
}
border-right: 25.14px solid transparent;
border-bottom: 0px solid transparent;
border-left: 25.14px solid transparent;
transform: translate(-50%, -100%) rotate(#{18*(index($points, $i)-1)}deg);
transform-origin: bottom;
z-index: 4;
}
}
@each $i in $points {
.single-outer-#{$i} {
top: 50%; left: 50%;
position: absolute;
height: 0; width: 0;
@if (index($points, $i)-1) % 2 == 0 {
border-top: black 280px solid;
} @else {
border-top: white 280px solid;
}
border-right: 44px solid transparent;
border-bottom: 0px solid transparent;
border-left: 44px solid transparent;
transform: translate(-50%, -100%) rotate(#{18*(index($points, $i)-1)}deg);
transform-origin: bottom;
z-index: 3;
}
}
@each $i in $points {
.double-#{$i} {
top: 50%; left: 50%;
position: absolute;
height: 0; width: 0;
@if (index($points, $i)-1) % 2 == 0 {
border-top: red 300px solid;
} @else {
border-top: blue 300px solid;
}
border-right: 47.14px solid transparent;
border-bottom: 0px solid transparent;
border-left: 47.14px solid transparent;
transform: translate(-50%, -100%) rotate(#{18*(index($points, $i)-1)}deg);
transform-origin: bottom;
z-index: 2;
}
}
.out {
position: absolute;
top: 50%; left: 50%;
height: 670px; width: 670px;
transform: translate(-50%, -50%);
border-radius: 100%;
background: black;
z-index: 1;
}
```
HTMLもループできればいいのですが、変わるところがそんなにないのでコピペでどんどん増やしていきます。
HTML (長いし同じものが続きます)
<divclass="darts-board"><divclass="inner-bull"onclick="hit(this.className)"></div><divclass="outer-bull"onclick="hit('outer-bull')"></div><divclass="single-inner-1"onclick="hit(this.className)"></div><divclass="single-inner-2"onclick="hit(this.className)"></div><divclass="single-inner-3"onclick="hit(this.className)"></div><divclass="single-inner-4"onclick="hit(this.className)"></div><divclass="single-inner-5"onclick="hit(this.className)"></div><divclass="single-inner-6"onclick="hit(this.className)"></div><divclass="single-inner-7"onclick="hit(this.className)"></div><divclass="single-inner-8"onclick="hit(this.className)"></div><divclass="single-inner-9"onclick="hit(this.className)"></div><divclass="single-inner-10"onclick="hit(this.className)"></div><divclass="single-inner-11"onclick="hit(this.className)"></div><divclass="single-inner-12"onclick="hit(this.className)"></div><divclass="single-inner-13"onclick="hit(this.className)"></div><divclass="single-inner-14"onclick="hit(this.className)"></div><divclass="single-inner-15"onclick="hit(this.className)"></div><divclass="single-inner-16"onclick="hit(this.className)"></div><divclass="single-inner-17"onclick="hit(this.className)"></div><divclass="single-inner-18"onclick="hit(this.className)"></div><divclass="single-inner-19"onclick="hit(this.className)"></div><divclass="single-inner-20"onclick="hit(this.className)"></div><divclass="triple-1"onclick="hit(this.className)"></div><divclass="triple-2"onclick="hit(this.className)"></div><divclass="triple-3"onclick="hit(this.className)"></div><divclass="triple-4"onclick="hit(this.className)"></div><divclass="triple-5"onclick="hit(this.className)"></div><divclass="triple-6"onclick="hit(this.className)"></div><divclass="triple-7"onclick="hit(this.className)"></div><divclass="triple-8"onclick="hit(this.className)"></div><divclass="triple-9"onclick="hit(this.className)"></div><divclass="triple-10"onclick="hit(this.className)"></div><divclass="triple-11"onclick="hit(this.className)"></div><divclass="triple-12"onclick="hit(this.className)"></div><divclass="triple-13"onclick="hit(this.className)"></div><divclass="triple-14"onclick="hit(this.className)"></div><divclass="triple-15"onclick="hit(this.className)"></div><divclass="triple-16"onclick="hit(this.className)"></div><divclass="triple-17"onclick="hit(this.className)"></div><divclass="triple-18"onclick="hit(this.className)"></div><divclass="triple-19"onclick="hit(this.className)"></div><divclass="triple-20"onclick="hit(this.className)"></div><divclass="single-outer-1"onclick="hit(this.className)"></div><divclass="single-outer-2"onclick="hit(this.className)"></div><divclass="single-outer-3"onclick="hit(this.className)"></div><divclass="single-outer-4"onclick="hit(this.className)"></div><divclass="single-outer-5"onclick="hit(this.className)"></div><divclass="single-outer-6"onclick="hit(this.className)"></div><divclass="single-outer-7"onclick="hit(this.className)"></div><divclass="single-outer-8"onclick="hit(this.className)"></div><divclass="single-outer-9"onclick="hit(this.className)"></div><divclass="single-outer-10"onclick="hit(this.className)"></div><divclass="single-outer-11"onclick="hit(this.className)"></div><divclass="single-outer-12"onclick="hit(this.className)"></div><divclass="single-outer-13"onclick="hit(this.className)"></div><divclass="single-outer-14"onclick="hit(this.className)"></div><divclass="single-outer-15"onclick="hit(this.className)"></div><divclass="single-outer-16"onclick="hit(this.className)"></div><divclass="single-outer-17"onclick="hit(this.className)"></div><divclass="single-outer-18"onclick="hit(this.className)"></div><divclass="single-outer-19"onclick="hit(this.className)"></div><divclass="single-outer-20"onclick="hit(this.className)"></div><divclass="double-1"onclick="hit(this.className)"></div><divclass="double-2"onclick="hit(this.className)"></div><divclass="double-3"onclick="hit(this.className)"></div><divclass="double-4"onclick="hit(this.className)"></div><divclass="double-5"onclick="hit(this.className)"></div><divclass="double-6"onclick="hit(this.className)"></div><divclass="double-7"onclick="hit(this.className)"></div><divclass="double-8"onclick="hit(this.className)"></div><divclass="double-9"onclick="hit(this.className)"></div><divclass="double-10"onclick="hit(this.className)"></div><divclass="double-11"onclick="hit(this.className)"></div><divclass="double-12"onclick="hit(this.className)"></div><divclass="double-13"onclick="hit(this.className)"></div><divclass="double-14"onclick="hit(this.className)"></div><divclass="double-15"onclick="hit(this.className)"></div><divclass="double-16"onclick="hit(this.className)"></div><divclass="double-17"onclick="hit(this.className)"></div><divclass="double-18"onclick="hit(this.className)"></div><divclass="double-19"onclick="hit(this.className)"></div><divclass="double-20"onclick="hit(this.className)"></div><divclass="out"onclick="hit(this.className)"></div></div>
JavaScriptで当たり判定+得点計算
各HTMLがクリックされてときにonclick
が発火し、クラス名をパースし当たった場所に応じて得点を入れます。
もっといい方法がある気がしますが、HTMLを最小限のコピペで書いたためこれでいきます。
<divclass="double-20"onclick="hit(this.className)"></div>
// hit()からname_to_point()が呼ばれる.// クラス名からパースを行う関数.functionname_to_point(name){if(name==="out"){return0;}elseif(name==="inner-bull"|name==="outer-bull"){return50;}else{letp=name.split('-');letbasic_point=parseInt(p[p.length-1],10);if(p[0]==="triple"){returnbasic_point*3;}elseif(p[0]==="double"){returnbasic_point*2;}else{returnbasic_point}}}
あとは、デバッグ用と各得点を足すコードです。長いし面白くないので特に取りあげません。
JavaScript (長い)
constmax_point=501letround=1;letnow_throw_player=1;letnow_throw_count=1;letplayers=[[]];// player2 = {[]};lettable=document.createElement('table');vartr=document.createElement('tr');vartr1=document.createElement('tr');append_cell(["","player1"]);append_cell(["point",max_point]);table.appendChild(tr);table.appendChild(tr1);document.getElementById('score-table').appendChild(table);functionhit(name){letp=name_to_point(name);players[round-1].push(p);lettotal_score=calc_total_score(players);table.rows[1].cells[1].firstChild.data=max_point-total_score;if(total_score==max_point){alert('finish');}elseif(total_score>max_point){alert('bust');players[round-1]=[0,0,0];now_throw_count=10;}document.getElementById("notification-click-event").innerText=round+""+now_throw_count+""+p+""+total_score+""+players;now_throw_count+=1;if(now_throw_count>3){letround_sum=function(arr){letsum=0;for(vari=0;i<arr.length;i++){sum+=arr[i];}returnsum;}append_cell(["R"+round,round_sum(players[round-1])]);now_throw_count=1;round+=1;players.push([]);}};functionname_to_point(name){if(name==="out"){return0;}elseif(name==="inner-bull"|name==="outer-bull"){return50;}else{letp=name.split('-');letbasic_point=parseInt(p[p.length-1],10);if(p[0]==="triple"){returnbasic_point*3;}elseif(p[0]==="double"){returnbasic_point*2;}else{returnbasic_point}}}functioncalc_total_score(scores){lettotal=0;for(leti=0;i<scores.length;i++){for(letl=0;l<scores[i].length;l++){total+=scores[i][l];}}returntotal;}functionappend_cell(data){varth=document.createElement('th');th.textContent=data[0];tr.appendChild(th);vartd=document.createElement('td');td.textContent=data[1];tr1.appendChild(td);}
まとめ
CSSで簡単に表現できるだろうと思ったダーツボードでしたが、扇型など簡単に表現できるものではなく、意外と詰まりました。
今後の課題としては、
- クリケットなど他ゲームへの対応
- ダーツボードのサイズが固定なのでスマホなどに対応するため、可変にする
などです。
Bustなど対応したらJavaScript部分が長く複雑になってゆきました。
ゲームルールなどをプログラムで表現しても複雑になりにくい書き方があったら知りたい…
なんとなくVueで作れば良かったかなとも思いますが以上です。