以前、こちらのサイトでカードを利用したボードゲームをオンラインで遊べるようにしてみた。
そして、ここでは以前作ったJQueryのオンラインのボードゲームをVueにしてみたらこうなる。
と言うイメージを書いてみました。
その後
まずはVueの勉強
JQueryをVueかにしようと思い、オンラインサイトを色々見てみた、、、
結果的に、良く分からなかった。やはりホントは違い必要最小限しか書いていないため、Vue.jsやらVUE CLIとか良く分からなかったので、Amazonで電子書籍を買った
この本は中々良かったです。内容としては、最初にVue.jsをCDNを使う方法。
簡単なVueならこれで問題なし。
そして、Vue cliを使った本格的なプロジェクト構築。
その後にNuxt.jsを使ったページ管理ができるフレームワークの構築。
最後にはFirebaseとの接続など書いてった。
けど、今回はVue cliのプロジェクトまでわかれば良かったので、そこまで読んでからあとは実践。
その結果
まずはこちらがJQueryで頑張ったもの
AsIs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>THE GAME(ザ・ゲーム) ONLINE</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
</head>
<body>
このゲームは1~5人用となっています。<br/>
<div id='app2'>
<div id='entry' style='display:none'>
<input id="cName_inp" placeholder="ニックネームを入力してください"><br/>
<br/>
{% if gameid %}
{% else %}
<button id='createGame'>ゲームを作る(Make a Game)</button><br/>
Game ID: <span id='gId'></span><br/>
<input type='text' id='uriWgId' /><input type='button' id='clickCopy' value='copy'><br/>
{% endif %}
Game Status: <span id='gStatus'></span><br/>
<hr/>
<button id="joinGame">ゲームに参加する(Join the Game)</button><br/>
{% if gameid %}
<input type='hidden' id="gId_inp" value='{{ gameid }}'><br/>
{% else %}
<input id="gId_inp" placeholder="GameIDを入力してください"><br/>
{% endif %}
Your ID: <span id='cId'></span><br/>
Your Name: <span id='cName'></span><br/>
<hr/>
<h2>Member Applying</h2>
<span id='applyingList'></span>
<br/>
</div>
<div id='sec1' style='display:none'>
<hr/>
<button id="startGame">ゲームを始める(Let's start the Game)</button><br/>
<input type='checkbox' name='rule_type' id="rule_type" value='original'>オリジナルルール(Use Original Rule)<br/>
1. 直前がぞろ目ならぞろ目を出せる(大小制限なし)<br/>
2. 10単位での戻りが無制限(大小制限なし)<br/>
</div>
<div id='sec2' style='display:none'>
<h2>Member Rotation</h2>
<span id='routeList'></span>
<hr/>
Your Turn: <span id='yTurn'></span><br/>
Your Cards:<br/>
<span id='holdCards'></span><br/>
<div id='sec3' style='display:none'>
<input type='number' min='2' max='99' id="cardNum_inp" placeholder="番号を入力してください" style="width:200px;"><br/>
<button id="putCard">カードを出す(Put the cards)</button><br/>
<br/>
<button id="nextPlayer">次の人へ(Go on Next user)</button><br/>
</div>
<hr/>
Game Cards:<br/>
<input type='radio' name='area' id='area' value='0'/>[100 > 2] <span id='gameCards0'></span><br/>
<input type='radio' name='area' id='area' value='1'/>[100 > 2] <span id='gameCards1'></span><br/>
<input type='radio' name='area' id='area' value='2'/>[1 > 99] <span id='gameCards2'></span><br/>
<input type='radio' name='area' id='area' value='3'/>[1 > 99] <span id='gameCards3'></span><br/>
Submited Cards in turn:<br/>
<div id='submitCards'></div>
<hr/>
</div>
<br/>
<span id='message'></span><br/>
</div>
</body>
</html>
var timeout = 1000;
var timer = '';
$(function() {
var gId = '';
var cId = '';
$('#entry').show();
$('#clickCopy').click(function(){
var text = $('#uriWgId').val();
var clipboard = $('<textarea></textarea>');
clipboard.text(text);
$('body').append(clipboard);
clipboard.select();
document.execCommand('copy');
clipboard.remove();
});
// Create Game
$('#createGame').click(function() {
$('#message').empty();
$.ajax('/create' + '/' + $('#cName_inp').val(),
{
type: 'get',
}
)
.done(function(data) {
$('#gId').text(data);
$('#cId').text(data);
$('#cName').text($('#cName_inp').val());
$('#gStatus').text('waiting');
$('#uriWgId').val(location.href + data + '/join');
gId = data;
cId = data;
$('#sec1').show();
timer = setTimeout(status_check(gId, cId), timeout)
})
.fail(function() {
$('#message').text('エラーが発生しました');
});
});
// Join Game
$('#joinGame').click(function() {
$('#message').empty();
$.ajax('/' + $('#gId_inp').val() + '/join/' + $('#cName_inp').val(),
{
type: 'get',
}
)
.done(function(data) {
_tmp = data.split(' ,');
$('#cId').text(_tmp[0]);
$('#cName').text(_tmp[1]);
$('#gStatus').text(_tmp[2]);
gId = $('#gId_inp').val();
cId = _tmp[0];
timer = setTimeout(status_check(gId, cId), timeout)
})
.fail(function() {
$('#message').text('エラーが発生しました');
});
});
// Start Game
$('#startGame').click(function() {
$('#message').empty();
$.getJSON('/' + gId + '/start/' + $('input:checkbox[name="rule_type"]:checked').val(),
{
type: 'get',
}
)
.done(function(data) {
})
.fail(function() {
$('#message').text('エラーが発生しました');
});
});
// Put your card
$('#putCard').click(function() {
$('#message').empty();
// put your card
$.ajax('/' + gId + '/' + cId + '/set/' + $('input[name="area"]:checked').val() + '/' + $('#cardNum_inp').val(),
{
type: 'get',
}
)
.done(function(data) {
$('#message').text(data);
})
.fail(function() {
$('#message').text('エラーが発生しました');
});
$('#cardNum_inp').val('')
});
// Next player
$('#nextPlayer').click(function() {
$('#message').empty();
$.ajax('/' + gId + '/next',
{
type: 'get',
}
)
.done(function(data) {
// console.log(data)
$('#message').text('次の人に移動しました');
})
.fail(function() {
$('#message').text('エラーが発生しました');
});
});
});
var status_check = function(gId, cId){
setTimeout(function(){
$('#message').empty();
// all status
$.getJSON('/' + gId + '/status',
{
type: 'get',
}
)
.done(function(data) {
console.log(data)
$('#gStatus').text(data.status);
playerPos = 0;
// Applying List
$('#applyingList').empty();
for(var pIdx in data.players){
// console.log(data.players[pIdx])
$('#applyingList').append(data.players[pIdx].nickname + '(' + data.players[pIdx].playerid + ')' + ',');
if(cId == data.players[pIdx].playerid){
playerPos = pIdx;
}
}
if(data.status == 'started'){
$('#entry').hide()
$('#sec2').show()
// route List
$('#routeList').empty();
for(var rIdx in data.routelist){
if(data.routelist[rIdx].playerid == data.routelist[data.routeidx].playerid){
$('#routeList').append('<b>>' + data.routelist[rIdx].nickname + '</b><br/>');
}else{
$('#routeList').append(data.routelist[rIdx].nickname + '<br/>');
}
}
// game cards
$('#gameCards0').text(data.hightolow[0])
$('#gameCards1').text(data.hightolow[1]);
$('#gameCards2').text(data.lowtohigh[0]);
$('#gameCards3').text(data.lowtohigh[1]);
// hold cards
$('#holdCards').text(data.players[playerPos].holdcards.join(','))
// submit cards
$('#submitCards').text(data.submit.join(','))
// checking turn
if(data.routelist[data.routeidx].playerid == cId){
$('#yTurn').text('your turn')
$('#sec3').show();
if(data.submit.length >= 2){
$('#nextPlayer').prop('disabled', false);
}else{
$('#nextPlayer').prop('disabled', true);
}
}else{
$('#yTurn').text('not your turn')
$('#sec3').css('display', 'none');
}
}
})
.fail(function() {
$('#message').text('エラーが発生しました');
});
timer = setTimeout(status_check(gId, cId), timeout)
}, timeout);
}
ToBe
<template>
<v-container>
<v-flex xs10>
<v-card-title class="font-weight-bold">THE GAME</v-card-title>
<v-card-text>このゲームは1~5人用となっています。</v-card-text>
<br/>
<div v-if="game == '***'">
<v-text-field v-model="cName_inp" label="ニックネームを入力してください" filled></v-text-field><br/>
<div v-if="!game_id">
<v-btn v-on:click='createGame'>ゲームを作る(Make a Game)</v-btn><br/>
</div>
<div v-else>
<v-btn v-on:click="joinGame">ゲームに参加する(Join the Game)</v-btn><br/>
</div>
</div>
<div v-else>
<!-- {{ game }} -->
<div v-if="game.status == 'waiting'">
Your ID: {{ client_id }}<br/>
Your Name: {{ nick_name }}<br/>
<!-- clint:{{ client }} -->
<div v-if="!guest">
<v-row>
<v-col cols='12' sm='6'>
<v-text-field v-model='uriWgId' readonly></v-text-field>
</v-col>
<v-col cols='12' sm='6'>
<v-btn @click='clickCopy'>copy</v-btn>
</v-col>
</v-row>
<br/>
<v-btn v-on:click="startGame">ゲームを始める(Let's start the Game)</v-btn><br/>
<v-checkbox v-model='rule_type'>
<template v-slot:label>
オリジナルルール(Use Original Rule)<br/>
1. 直前がぞろ目ならぞろ目を出せる(大小制限なし)<br/>
2. 10単位での戻りが無制限(大小制限なし)<br/>
</template>
</v-checkbox>
</div>
</div>
<div v-else-if="game.status == 'started'">
Your ID: {{ client_id }}<br/>
Your Name: {{ nick_name }}<br/>
Last cards: {{ game.stocks.length }}<br/>
<br/>
Your Turn: {{ (turn? 'Your turn':'Not your turn') }}
<br/>
Your Cards:<br/>
<table>
<tr>
<td v-for="(value, key) in client.holdcards" :key="key">
<ul>
<template v-if='turn'>
<li v-on:click='putCard(value)' class='card'>
{{ value }}
</li>
</template>
<template v-else>
<li class='card'>
{{ value }}
</li>
</template>
</ul>
<!-- <template v-if='turn'>
<span v-on:click='putCard(value)' class='card'>
{{ value }}
</span>
</template>
<template v-else>
<span class='card'>
{{ value }}
</span>
</template> -->
</td>
</tr>
</table>
<br/>
<br/>
<template v-if="turn">
<v-btn v-on:click="nextPlayer">次の人へ(Go on Next user)</v-btn><br/>
</template>
</div>
<hr/>
<br/>
<h2>Member Applying</h2>
<span v-for="(player, key) in game.players" :key="key">
{{ player.nickname }}({{ player.playerid }})
</span>
<br/>
<h2>Game Cards</h2>
下から配置したい場所をエリアを選択してください<br/>
<table class='board'>
<tr v-for="(hightolow, key) in game.hightolow" :key="key" class='board'>
<td v-on:click='setArea(key)' class='board'>
<template v-if="key === sel_area">
★
</template>
<ul>
<li v-for="(card, key) in hightolow" :key="key">
<span class='card'>
{{ card }}
</span>
</li>
</ul>
</td>
</tr>
</table>
<table>
<tr v-for="(lowtohigh, key) in game.lowtohigh" :key="key" class='board' >
<td v-on:click='setArea(key+2)' class='board'>
<template v-if="key+2 === sel_area">
★
</template>
<ul>
<li v-for="(card, key) in lowtohigh" :key="key">
<span class='card'>
{{ card }}
</span>
</li>
</ul>
</td>
</tr>
</table>
</div>
{{ error_message }}
</v-flex>
</v-container>
</template>
<script>
const axios = require('axios');
export default {
data: function(){
return {
cName_inp: '',
game: '***',
error_message: '',
guest: false,
turn: false,
rule_type: false,
game_id: '',
uriWgId: 'aaaa',
area: '',
sel_area: '',
}
},
mounted() {
this.game_id = this.$route.query.game_id;
this.guest = (this.$route.query.game_id != ''? true : false);
console.log(process.env.NODE_ENV);
},
methods: {
clickCopy: function() {
navigator.clipboard.writeText(this.uriWgId)
.then(() => {
// console.log("copied!");
alert('copied!');
})
.catch(e => {
console.error(e);
})
},
createGame: function() {
axios.get(process.env.VUE_APP_API_BASE_URL+'create/'+this.cName_inp).then((res) => {
this.game_id = res.data;
this.client_id = this.game_id;
this.guest = false;
// this.uriWgId = 'http://localhost:5000/?game_id='+this.game_id;
this.uriWgId = window.location.href+'?game_id='+this.game_id;
console.log(res.data);
setInterval(() => {
this.status_check();
}, 1000);
})
},
joinGame: function() {
axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/join/'+this.cName_inp).then((res) => {
console.log(res.data);
this.client_id = res.data.split(',')[0].trim();
this.guest = true;
setInterval(() => {
this.status_check();
}, 1000);
})
},
startGame: function() {
axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/start'+(this.rule_type?'/original':'')).then((res) => {
this.game = res.data;
// console.log(this.game);
})
},
nextPlayer: function() {
axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/next');
this.error_message = '';
},
putCard: function(value) {
if(this.sel_area === ''){
alert('下から配置したいエリアを選択してください');
return;
}
axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/'+this.client_id+'/set/'+this.sel_area+'/'+value)
.then(res => {
this.error_message = res.data;
})
},
setArea: function(value) {
this.sel_area = value;
},
status_check: function() {
axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/status').then((res) => {
this.game = res.data;
// console.log(this.game);
this.client = res.data.players.find((v) => v.playerid.trim() == this.client_id.trim());
this.nick_name = this.client.nickname;
if(this.game.status == 'started'){
this.turn = (this.game.routelist[this.game.routeidx].playerid == this.client_id? true: false);
}
})
},
},
}
</script>
Vue.jsでも少しゴミゴミした書き方になってしまいましたが、JQueryとVueの違いを理解してもらうには十分かと思います。
結果的にわかったこと
- コードをhtml/jsで分けて考えず、htmlを1つのjsと考えながらプラグラミングできる便利さ
- データの反映について、いちいちjs側で出力ロジックを記載しなくても、必要な変数を更新すれば勝手に反映される
なんか、htmlへ表示する際に、わざわざ出力ロジックを書く必要がないのが大変便利。
ということで、今後はVue.jsをメインに使っていこうと思います。
また、Nuxt.jsや、Firebaseなどのリアルタイムデータベースを使ったものにバージョンアップしていきたいと思います。
なお、Vueで書き直した「THE GAME」はこちらのリンクから遊べます。