Ở section 4 này, mình sẽ hướng dẫn các bạn làm một game nho nhỏ để practice các kiến thức mình đã học qua 3 section trong series Vuejs của tụi mình nhé.
1/ Mục tiêu và chuẩn bị
Mình sẽ nói sơ qua tí hen, ở trò chơi này chúng ta sẽ tạo ra các chức năng bao gồm tấn công, hồi máu và xử lí thanh máu của player và quái vật Bên cạnh đó, chúng ta cũng sẽ làm thêm một chức năng nữa là batte log, nhằm mục đích hiển thị thông tin của turn đó ra…. Ok giờ chúng ta cùng nhau bắt đầu nhé.
Trước hết chúng ta sẽ download source project tại đây.
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue Basics</title>
<link
href="https://fonts.googleapis.com/css2?family=Jost:wght@400;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="styles.css" />
<script src="https://unpkg.com/vue@next" defer></script>
<script src="app.js" defer></script>
</head>
<body>
<header>
<h1>Monster Slayer</h1>
</header>
<div id="game">
<section id="monster" class="container">
<h2>Monster Health</h2>
<div class="healthbar">
<div class="healthbar__value"></div>
</div>
</section>
<section id="player" class="container">
<h2>Your Health</h2>
<div class="healthbar">
<div class="healthbar__value"></div>
</div>
</section>
<section id="controls">
<button>ATTACK</button>
<button>SPECIAL ATTACK</button>
<button>HEAL</button>
<button>SURRENDER</button>
</section>
<section id="log" class="container">
<h2>Battle Log</h2>
<ul></ul>
</section>
</div>
</body>
</html>
Và đây là giao diện của chúng ta.
2/ Practice Project vuejs
Bây giờ trước hết chúng ta sẽ bắt tay vào việc tạo methods và data hen.
Đầu tiên việc mình cần làm là tạo HP cho player và monster kèm theo là round.
const action = Vue.createApp({
data(){
return {
healthPlayer: 100,
healthMonster:100,
round:1,
}
},
watch:{
},
computed:{
},
methods:{
}
});
action.mount('#game');
Sau khi tạo HP xong thì mình tiếp túc tạo method attack cho player. Trước khi tạo method tấn công, thì mình sẽ tạo ra function random Damage trước.
function getRandomHeal(start,end){
//Hàm math.random sẽ trả về giá trị 0 -1,
return Math.floor(Math.random()*(end - start) + start);
}
Function này mình sẽ đặt đầu file nhé. Hoặc nếu muốn các bạn có có thể tạo trong method và gọi nó thông qua this.
Giờ mình sẽ tạo methods tấn công cho cả player và quái vật.
attackMonster(){
let valueDmg = getRandomHeal(5,12);
this.healthMonster -= valueDmg;
this.attackPlayer();
this.round++;
},
attackPlayer(){
let valueDmg = getRandomHeal(8,20);
this.healthPlayer -= valueDmg;
},
Các bạn có thể thấy trong method attackMonster mình tạo ra cho nút tấn công của player. Mình tạo một biến random damage ra và trừ nó vào tổng health của monster. Sau khi monster bị tấn công, thì hệ thống sẽ tự động random ra sát thương mà monster sẽ tấn công player, sau đó round sẽ tăng lên 1. Sở dĩ mình tạo biến round ở đây là để một tí nữa mình sẽ xử lí battle log nhé.
Bây giờ mình sẽ xử lí hiển thị HP của monster và player trong computed.
computed:{
healthBarPlayer(){
if(this.healthPlayer < 0){
return 0 + '%';
}
return this.healthPlayer + '%';
},
healthBarMonster(){
if(this.healthMonster < 0){
return 0 + '%';
}
return this.healthMonster + '%';
},
},
Như mình đã nói ở phần 2, computed sẽ được thực thi khi giá trị trong hàm đó được thay đổi, mà không cần phải bị gọi lại nếu không cần thiết như ở methods. Giờ thì mình sẽ bắt đầu đưa các sự kiện vào trong html
<header>
<h1>Monster Slayer</h1>
</header>
<div id="game">
<section id="monster" class="container">
<h2>Monster Health</h2>
<div class="healthbar">
<div class="healthbar__value" :style="{width: healthBarMonster}"></div>
</div>
</section>
<section id="player" class="container">
<h2>Your Health</h2>
<div class="healthbar">
<div class="healthbar__value" :style="{width: healthBarPlayer}"></div>
</div>
</section>
<section id="controls">
<button @click="attackMonster">ATTACK</button>
<button>SPECIAL ATTACK</button>
<button>HEAL</button>
<button>SURRENDER</button>
</section>
</div>
Và đây là thành quả:
Bây giờ mình sẽ làm thêm hai chức năng nữa đó là special attack và heal. Đối với special attack mình sẽ cho nó hiển thị sau khi mình tấn công được 2 lượt.
Trước hết mình sẽ tạo ra một computed để check turn
checkTurnAttack(){
return this.round % 3 !==0;
}
Sau đó mình sẽ tạo hai methods để xử lí health và special attack
specialAttack(){
let valueDmg = getRandomHeal(10,15);
this.healthMonster -= valueDmg;
this.attackPlayer();
this.round++;
this.battleLog('Layer','Special Attack',valueDmg);
},
heal(){
let valueHeal = getRandomHeal(10,50);
this.healthPlayer += valueHeal;
this.attackPlayer();
this.round++;
this.battleLog('Layer','Heal',valueHeal);
},
Như vậy là xong, giờ mình sẽ đưa vào trong html nhé
<section id="controls">
<button @click="attackMonster">ATTACK</button>
<button :disabled="checkTurnAttack" @click="specialAttack">SPECIAL ATTACK</button>
<button @click="heal">HEAL</button>
<button>SURRENDER</button>
</section>
Kết quả:
Như vậy là gần xong rồi! bây giờ chúng ta sẽ cùng nhau làm 2 chức năng cuối nữa. Đó là battle log và đưa ra kết quả của người chơi hen.
Đầu tiên mình sẽ bổ xung thêm giá trị trong data
data(){
return {
healthPlayer: 100,
healthMonster:100,
round:1,
arrayBattleLog: [],
result:null,//value: 3 - lost, 1 - win, 2 - draw
}
},
Sau đó mình tiếp tục tạo thêm methods để show ra batle log:
battleLog(who,action,value){
this.arrayBattleLog.unshift(
{
actionBy:who,
actionType:action,
value:value,
}
);
}
Rồi kế tiếp mình tiếp tục tạo watch để xử lí kết quả của trò chơi
watch:{
healthPlayer(){
if(this.healthPlayer <=0 && this.healthMonster > 0){
return this.result = 3;
} else if(this.healthPlayer <=0 && this.healthMonster <= 0){
return this.result = 2;
} else if(this.healthMonster <= 0){
return this.result = 1;
}
},
},
Và đây là source hoàn chỉnh của chúng ta:
<div id="game">
<section id="monster" class="container">
<h2>Monster Health</h2>
<div class="healthbar">
<div class="healthbar__value" :style="{width: healthBarMonster}"></div>
</div>
</section>
<section id="player" class="container">
<h2>Your Health</h2>
<div class="healthbar">
<div class="healthbar__value" :style="{width: healthBarPlayer}"></div>
</div>
</section>
<section class="container" v-if="result">
<h2>Finish</h2>
<h3 v-if="result == 1">You won</h3>
<h3 v-else-if="result == 3">You lost</h3>
<h3 v-else>Draw</h3>
</section>
<section id="controls">
<button @click="attackMonster">ATTACK</button>
<button :disabled="checkTurnAttack" @click="specialAttack">SPECIAL ATTACK</button>
<button @click="heal">HEAL</button>
<button>SURRENDER</button>
</section>
<section id="log" class="container">
<h2>Battle Log</h2>
<ul v-if="arrayBattleLog.length != 0">
<li v-for="(logItem,index) in arrayBattleLog">
{{index}}- {{logItem.actionBy}} - {{logItem.actionType}} - {{logItem.value}}
</li>
</ul>
</section>
</div>
Kết quả:
3/ Tổng kết
Chức năng cuối cùng là surrender tức là đầu hàng, các bạn hãy làm theo ý các bạn nhé. Vì là chức năng đầu hàng nên nó cũng rất đơn giản. các bạn có thể xử lí hp về 0 hoặc như thế nào đó tùy ý sáng tạo của các bạn. 😀
Như vậy tụi mình đã cùng nhau practice làm một trò chơi đơn giản rồi nhé. Cảm ơn các bạn đã theo dõi và hẹn gặp các bạn vào section sau nhé.