15. オブジェクト
このレッスンでは、関連するデータをまとめて管理するオブジェクトについて学びます。
変数が多すぎる
前回のレッスンで、バトルを繰り返すことができるようになりました。しかし、コードを見返すと変数がたくさんあることに気づきます。
<!-- battle.html hidden -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>バトル</title>
<link rel="stylesheet" href="css/battle.css">
<script src="js/battle.js" defer></script>
</head>
<body>
<table id="status">
<tr>
<th>ゆうしゃ</th><th>せんし</th><th>そうりょ</th><th>まほうつかい</th>
</tr>
<tr><td>HP 153</td><td>HP 198</td><td>HP 101</td><td>HP 77</td></tr>
<tr><td>MP 25</td><td>MP 0</td><td>MP 35</td><td>MP 58</td></tr>
</table>
<div id="monster">
<img class="dark-knight" src="img/dark-knight.png">
<img class="dark-lord" src="img/dark-lord.png">
<img class="demon-priest" src="img/demon-priest.png">
</div>
<div id="message">
まおうがあらわれた。
</div>
</body>
</html>
/* battle.css hidden */
body {
background-color: rgb(34, 34, 34);
color: white;
font-family: sans-serif;
}
table#status {
border: solid 2px white;
border-collapse: collapse;
margin: 10px auto 0 auto;
width: 640px;
}
table#status tr:first-child {
border-bottom: solid 1px white;
}
table#status td {
text-align: center;
}
#monster {
text-align: center;
margin-top: 40px;
}
#monster .dark-lord {
width: 400px;
}
#monster .dark-knight {
width: 150px;
}
#monster .demon-priest {
width: 150px;
}
#message {
border: solid 2px white;
border-radius: 4px;
padding: 10px;
width: 720px;
margin: 30px auto;
}
// battle.js selection:34-44
function sleep() {
return new Promise(resolve => setTimeout(resolve, 1000));
}
function calculateDamage(attack, defense) {
let damage = Math.floor((attack - defense) / 2);
if (damage < 0) {
damage = 0;
}
return damage;
}
function calculateHp(hp, damage) {
hp = hp - damage;
if (hp < 0) {
hp = 0;
}
return hp;
}
function displayMessage(message) {
document.getElementById("message").textContent = message;
}
function displayDamageMessage(name, damage) {
if (damage === 0) {
displayMessage(`${name}にダメージをあたえられない。`);
} else {
displayMessage(`${name}に${damage}のダメージ。`);
}
}
async function main() {
const name1 = "ゆうしゃ";
const maxHp1 = 153;
let hp1 = maxHp1;
const attack1 = 162;
const defense1 = 97;
const name2 = "まおう";
const maxHp2 = 999;
let hp2 = maxHp2;
const attack2 = 186;
const defense2 = 58;
while (hp1 > 0 && hp2 > 0) {
displayMessage(`${name1}のこうげき。`);
await sleep();
let damage = calculateDamage(attack1, defense2);
hp2 = calculateHp(hp2, damage);
displayDamageMessage(name2, damage);
await sleep();
if (hp2 <= 0) {
document.querySelector("#monster img:nth-child(2)").style.visibility = "hidden";
displayMessage(`${name2}をやっつけた。`);
break;
}
displayMessage(`${name2}のこうげき。`);
await sleep();
damage = calculateDamage(attack2, defense1);
hp1 = calculateHp(hp1, damage);
displayDamageMessage(name1, damage);
await sleep();
document.querySelector("#status tr:nth-child(2) td:nth-child(1)").textContent = `HP ${hp1}`;
if (hp1 <= 0) {
displayMessage(`${name1}はしんでしまった。`);
}
}
}
main();
「ゆうしゃ」に関する変数が5つ(name1、maxHp1、hp1、attack1、defense1)、「まおう」に関する変数も5つ(name2、maxHp2、hp2、attack2、defense2)あります。さらに、今後他にもMPや素早さなどの変数が必要になりそうです。
「ゆうしゃ」に関する変数をばらばらに扱うより、1つにまとめて扱えるとわかりやすそうです。
オブジェクト
JavaScriptには、関連するデータをひとまとめにするオブジェクトという仕組みがあります。オブジェクトは{ }で囲んで作ります。
const hero = {
name: "ゆうしゃ",
maxHp: 153
};
↑のように、オブジェクトも変数に代入することができます。
nameやmaxHpのことをオブジェクトのプロパティと呼びます。プロパティはプロパティ名: 値の形式で書き、複数のプロパティはカンマ(,)で区切ります。
オブジェクトのプロパティには.(ドット)を使ってアクセスできます。
console.log(hero.name); // "ゆうしゃ"
console.log(hero.maxHp); // 153
また、新しいプロパティを後から追加することもできます。
hero.hp = hero.maxHp;
ゆうしゃのオブジェクトを作る
「ゆうしゃ」に関する5つの変数をオブジェクトにまとめてみましょう。
// battle.js selection:34-40 highlight:34-40
function sleep() {
return new Promise(resolve => setTimeout(resolve, 1000));
}
function calculateDamage(attack, defense) {
let damage = Math.floor((attack - defense) / 2);
if (damage < 0) {
damage = 0;
}
return damage;
}
function calculateHp(hp, damage) {
hp = hp - damage;
if (hp < 0) {
hp = 0;
}
return hp;
}
function displayMessage(message) {
document.getElementById("message").textContent = message;
}
function displayDamageMessage(name, damage) {
if (damage === 0) {
displayMessage(`${name}にダメージをあたえられない。`);
} else {
displayMessage(`${name}に${damage}のダメージ。`);
}
}
async function main() {
const hero = {
name: "ゆうしゃ",
maxHp: 153,
attack: 162,
defense: 97
};
hero.hp = hero.maxHp;
const name2 = "まおう";
const maxHp2 = 999;
let hp2 = maxHp2;
const attack2 = 186;
const defense2 = 58;
while (hp1 > 0 && hp2 > 0) {
displayMessage(`${name1}のこうげき。`);
await sleep();
let damage = calculateDamage(attack1, defense2);
hp2 = calculateHp(hp2, damage);
displayDamageMessage(name2, damage);
await sleep();
if (hp2 <= 0) {
document.querySelector("#monster img:nth-child(2)").style.visibility = "hidden";
displayMessage(`${name2}をやっつけた。`);
break;
}
displayMessage(`${name2}のこうげき。`);
await sleep();
damage = calculateDamage(attack2, defense1);
hp1 = calculateHp(hp1, damage);
displayDamageMessage(name1, damage);
await sleep();
document.querySelector("#status tr:nth-child(2) td:nth-child(1)").textContent = `HP ${hp1}`;
if (hp1 <= 0) {
displayMessage(`${name1}はしんでしまった。`);
}
}
}
main();
5つの変数が1つのオブジェクトにまとまりました。先ほど学んだように、hpはhero.hp = hero.maxHp;で後から追加しています。
まおうのオブジェクトを作る
同様に「まおう」もオブジェクトにまとめましょう。
// battle.js selection:34-48 highlight:42-48
function sleep() {
return new Promise(resolve => setTimeout(resolve, 1000));
}
function calculateDamage(attack, defense) {
let damage = Math.floor((attack - defense) / 2);
if (damage < 0) {
damage = 0;
}
return damage;
}
function calculateHp(hp, damage) {
hp = hp - damage;
if (hp < 0) {
hp = 0;
}
return hp;
}
function displayMessage(message) {
document.getElementById("message").textContent = message;
}
function displayDamageMessage(name, damage) {
if (damage === 0) {
displayMessage(`${name}にダメージをあたえられない。`);
} else {
displayMessage(`${name}に${damage}のダメージ。`);
}
}
async function main() {
const hero = {
name: "ゆうしゃ",
maxHp: 153,
attack: 162,
defense: 97
};
hero.hp = hero.maxHp;
const darkLord = {
name: "まおう",
maxHp: 999,
attack: 186,
defense: 58
};
darkLord.hp = darkLord.maxHp;
while (hp1 > 0 && hp2 > 0) {
displayMessage(`${name1}のこうげき。`);
await sleep();
let damage = calculateDamage(attack1, defense2);
hp2 = calculateHp(hp2, damage);
displayDamageMessage(name2, damage);
await sleep();
if (hp2 <= 0) {
document.querySelector("#monster img:nth-child(2)").style.visibility = "hidden";
displayMessage(`${name2}をやっつけた。`);
break;
}
displayMessage(`${name2}のこうげき。`);
await sleep();
damage = calculateDamage(attack2, defense1);
hp1 = calculateHp(hp1, damage);
displayDamageMessage(name1, damage);
await sleep();
document.querySelector("#status tr:nth-child(2) td:nth-child(1)").textContent = `HP ${hp1}`;
if (hp1 <= 0) {
displayMessage(`${name1}はしんでしまった。`);
}
}
}
main();
オブジェクトを使うように書き換える
オブジェクトを作りましたが、まだwhile文の中では古い変数名(hp1、hp2など)を使っています。これをオブジェクトのプロパティを使うように書き換えましょう。
| 古い変数 | 新しい書き方 |
|---|---|
name1 |
hero.name |
hp1 |
hero.hp |
attack1 |
hero.attack |
defense1 |
hero.defense |
name2 |
darkLord.name |
hp2 |
darkLord.hp |
attack2 |
darkLord.attack |
defense2 |
darkLord.defense |
書き換えると次のようになります。
// battle.js selection:50-76 highlight:50-51,53-55,58,60,64,66-68,70,72-73
function sleep() {
return new Promise(resolve => setTimeout(resolve, 1000));
}
function calculateDamage(attack, defense) {
let damage = Math.floor((attack - defense) / 2);
if (damage < 0) {
damage = 0;
}
return damage;
}
function calculateHp(hp, damage) {
hp = hp - damage;
if (hp < 0) {
hp = 0;
}
return hp;
}
function displayMessage(message) {
document.getElementById("message").textContent = message;
}
function displayDamageMessage(name, damage) {
if (damage === 0) {
displayMessage(`${name}にダメージをあたえられない。`);
} else {
displayMessage(`${name}に${damage}のダメージ。`);
}
}
async function main() {
const hero = {
name: "ゆうしゃ",
maxHp: 153,
attack: 162,
defense: 97
};
hero.hp = hero.maxHp;
const darkLord = {
name: "まおう",
maxHp: 999,
attack: 186,
defense: 58
};
darkLord.hp = darkLord.maxHp;
while (hero.hp > 0 && darkLord.hp > 0) {
displayMessage(`${hero.name}のこうげき。`);
await sleep();
let damage = calculateDamage(hero.attack, darkLord.defense);
darkLord.hp = calculateHp(darkLord.hp, damage);
displayDamageMessage(darkLord.name, damage);
await sleep();
if (darkLord.hp <= 0) {
document.querySelector("#monster img:nth-child(2)").style.visibility = "hidden";
displayMessage(`${darkLord.name}をやっつけた。`);
break;
}
displayMessage(`${darkLord.name}のこうげき。`);
await sleep();
damage = calculateDamage(darkLord.attack, hero.defense);
hero.hp = calculateHp(hero.hp, damage);
displayDamageMessage(hero.name, damage);
await sleep();
document.querySelector("#status tr:nth-child(2) td:nth-child(1)").textContent = `HP ${hero.hp}`;
if (hero.hp <= 0) {
displayMessage(`${hero.name}はしんでしまった。`);
}
}
}
main();
hp1やattack2のような番号付きの変数名がなくなり、hero.hpやdarkLord.attackのように「誰の何か」が分かりやすい書き方になりました。
試してみよう
- 他の敵・味方キャラクターのオブジェクトを作ってみましょう
これまでの成果
<!-- battle.html folded -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>バトル</title>
<link rel="stylesheet" href="css/battle.css">
<script src="js/battle.js" defer></script>
</head>
<body>
<table id="status">
<tr>
<th>ゆうしゃ</th><th>せんし</th><th>そうりょ</th><th>まほうつかい</th>
</tr>
<tr><td>HP 153</td><td>HP 198</td><td>HP 101</td><td>HP 77</td></tr>
<tr><td>MP 25</td><td>MP 0</td><td>MP 35</td><td>MP 58</td></tr>
</table>
<div id="monster">
<img class="dark-knight" src="img/dark-knight.png">
<img class="dark-lord" src="img/dark-lord.png">
<img class="demon-priest" src="img/demon-priest.png">
</div>
<div id="message">
まおうがあらわれた。
</div>
</body>
</html>
/* battle.css folded */
body {
background-color: rgb(34, 34, 34);
color: white;
font-family: sans-serif;
}
table#status {
border: solid 2px white;
border-collapse: collapse;
margin: 10px auto 0 auto;
width: 640px;
}
table#status tr:first-child {
border-bottom: solid 1px white;
}
table#status td {
text-align: center;
}
#monster {
text-align: center;
margin-top: 40px;
}
#monster .dark-lord {
width: 400px;
}
#monster .dark-knight {
width: 150px;
}
#monster .demon-priest {
width: 150px;
}
#message {
border: solid 2px white;
border-radius: 4px;
padding: 10px;
width: 720px;
margin: 30px auto;
}
// battle.js
function sleep() {
return new Promise(resolve => setTimeout(resolve, 1000));
}
function calculateDamage(attack, defense) {
let damage = Math.floor((attack - defense) / 2);
if (damage < 0) {
damage = 0;
}
return damage;
}
function calculateHp(hp, damage) {
hp = hp - damage;
if (hp < 0) {
hp = 0;
}
return hp;
}
function displayMessage(message) {
document.getElementById("message").textContent = message;
}
function displayDamageMessage(name, damage) {
if (damage === 0) {
displayMessage(`${name}にダメージをあたえられない。`);
} else {
displayMessage(`${name}に${damage}のダメージ。`);
}
}
async function main() {
const hero = {
name: "ゆうしゃ",
maxHp: 153,
attack: 162,
defense: 97
};
hero.hp = hero.maxHp;
const darkLord = {
name: "まおう",
maxHp: 999,
attack: 186,
defense: 58
};
darkLord.hp = darkLord.maxHp;
while (hero.hp > 0 && darkLord.hp > 0) {
displayMessage(`${hero.name}のこうげき。`);
await sleep();
let damage = calculateDamage(hero.attack, darkLord.defense);
darkLord.hp = calculateHp(darkLord.hp, damage);
displayDamageMessage(darkLord.name, damage);
await sleep();
if (darkLord.hp <= 0) {
document.querySelector("#monster img:nth-child(2)").style.visibility = "hidden";
displayMessage(`${darkLord.name}をやっつけた。`);
break;
}
displayMessage(`${darkLord.name}のこうげき。`);
await sleep();
damage = calculateDamage(darkLord.attack, hero.defense);
hero.hp = calculateHp(hero.hp, damage);
displayDamageMessage(hero.name, damage);
await sleep();
document.querySelector("#status tr:nth-child(2) td:nth-child(1)").textContent = `HP ${hero.hp}`;
if (hero.hp <= 0) {
displayMessage(`${hero.name}はしんでしまった。`);
}
}
}
main();