vue 기본기를 활용해서 간단한 rpg 게임 만들기 실습을 진행했다.
처음에 지금 배운 기본기로만 rpg 게임을 어떻게 구현을 시킬지 상상이 안갔는데
정말 단순하게 기능에만 집중해서 구현하는 방식으로 진행된 것 같다.
(버튼을 눌러 몬스터 생성, 몬스터 삭제, 몬스터 때리기 등등..)
참 그리고 vue의 장점을 한 가지 더 배웠다.
template 태그 안쪽에서는 이벤트 매핑만 시키며 화면에 어떤 것들을 보여줄지에 대한 내용만 있다.
이렇게 컴포넌트가 분리되면서 필요한 부분에만 집중할 수 있다는 것!
rpg 게임에 필요한 다양한 기능들을 구현했는데
실제로 작업할 때는 기능별로 컴포넌트를 하나하나 분리해서 만들어야 하지만
실습의 목적이 vue의 기능을 사용해보고 익숙해지는 것이기 때문에
컴포넌트 분리는 하지 않고 한군데에 모든 기능과 데이터를 담았음.
컴포넌트 분리해서 배우는 건 앞으로 배울 것이다.
배열, 버튼을 활용해서 몬스터 만들기
<template>
<!-- 화면에 보이는 몬스터 추가 버튼 -->
<button v-on:click="addRandomMonster">랜덤 몬스터 추가하기</button>
</template>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Test",
data() {
return {
// 몬스터 도감
monsterBooks: [
{ monsterId: 1, name: '슬라임', hp: 50, exp: 10, dropMoney: 5 },
{ monsterId: 2, name: '칡', hp: 100, exp: 20, dropMoney: 10 },
{ monsterId: 3, name: '멧돼지', hp: 200, exp: 40, dropMoney: 20 },
{ monsterId: 4, name: '고라니', hp: 400, exp: 100, dropMoney: 20 },
{ monsterId: 5, name: '너굴맨', hp: 350, exp: 90, dropMoney: 10 },
]
}
},
methods: {
// 현재 맵에 있는 몬스터 수 세기(?)
findCurrentMonsterListsMax() {
return this.monsterLists.reduce(
(a, b) => {
console.log("a: " + a + ", b.id: " + b.id);
return a > b.id ? a : b.id
},
0)
},
// mosterLists에 랜덤 몬스터 추가
addRandomMonster() {
let max = this.findCurrentMonsterListsMax()
//Math.floor 하면 소수점 밑 절삭
let randomMonsterBookIdx = Math.floor(Math.random() * this.monsterBooks.length)
this.monsterLists.push({
id: max + 1,
name: this.monsterBooks[randomMonsterBookIdx].name,
hp: this.monsterBooks[randomMonsterBookIdx].hp
})
},
}
beforeUpdate() {
console.log("나는 VDOM의 변화를 감지하면 무조건 동작해!")
let i
// 몬스터 hp가 0이 되면 죽으면서 캐릭터에게 보상 지급(경험치, 돈)
for (i = 0; i < this.monsterLists.length; i++) {
if (this.monsterLists[i].hp <= 0) {
for (let j = 0; j < this.monsterBooks.length; j++) {
if (this.monsterLists[i].name === this.monsterBooks[j].name) {
this.characterStatus.currentLevelBar += this.monsterBooks[j].exp
this.characterStatus.money += this.monsterBooks[j].dropMoney
}
}
// splice를 쓰면 list에서 i번째 데이터부터 인자값 만큼(1)삭제됨.
this.monsterLists.splice(i, 1)
}
}
}
</script>
beforeUpdate 쓸 때는 methods 밖에서 선언되어야 활성화된다.
첨에 methods 안에다가 선언해서 안먹어가지고 당..황..^^;;;;
캐릭터 관련 기능들
<templates>
<!-- 화면에 보여줄 캐릭터 상태창 -->
<p>캐릭터 상태 창</p>
<p>HP: {{ characterStatus.currentHp }} / {{ characterStatus.hp }} MP: {{ characterStatus.currentMp }} / {{ characterStatus.mp }} </p>
<p>ATK: {{ characterStatus.atk }} Lv: {{ characterStatus.level }} 직업: {{ characterStatus.currentJob }}</p>
<p>STR: {{ characterStatus.str }} INT: {{ characterStatus.intelligence }} DEX: {{ characterStatus.dex }}
VIT: {{ characterStatus.vit }} DEF: {{ characterStatus.def }} MEN: {{ characterStatus.men }}</p>
<p>경험치: {{ characterStatus.currentLevelBar }} / {{ characterStatus.totalLevelBar }}</p>
<p>소지금: {{ characterStatus.money }}</p>
<!-- 몬스터 공격 버튼 -->
<button v-on:click="iceBlade">광역기 발동!</button>
<br/>
</templates>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Test",
data() {
return {
characterStatus: {
level: 1,
currentHp: 50,
hp: 50,
currentMp: 30,
mp: 30,
itemAtk: 0,
defaultAtk: 10,
atk: 10,
str: 10,
intelligence: 10,
dex: 10,
vit: 10,
def: 10,
men: 0,
totalLevelBar: 10,
currentLevelBar: 0,
money: 0,
currentJob: '모험가'
}
}
},
methods: {
iceBlade() {
console.log("아이스 블레이드...!")
// 맵에 있는 몬스터들을 모두 공격
for (let i = 0; i < this.monsterLists.length; i++) {
this.monsterLists[i].hp -= 30 * this.characterStatus.atk
}
},
},
beforeUpdate() {
//캐릭터 레벨업 기능(상태가 바뀌면 자동 업데이트 됨)
while (this.characterStatus.currentLevelBar >= this.characterStatus.totalLevelBar) {
// 레벨업 시 넘치는 경험치를 현재 경험치로 넘겨주기
this.characterStatus.currentLevelBar =
parseInt(this.characterStatus.currentLevelBar - this.characterStatus.totalLevelBar)
// 레벨업 및 각종 스탯 상승
this.characterStatus.level += 1
this.characterStatus.currentHp = parseInt(this.characterStatus.hp * 1.05)
this.characterStatus.hp = parseInt(this.characterStatus.hp * 1.05)
this.characterStatus.currentMp = parseInt(this.characterStatus.mp * 1.02)
this.characterStatus.mp = parseInt(this.characterStatus.mp * 1.02)
this.characterStatus.defaultAtk += 4
this.characterStatus.atk += 4
this.characterStatus.def += 1
this.characterStatus.str += 3
this.characterStatus.intelligence += 1
this.characterStatus.dex += 2
this.characterStatus.vit += 3
this.characterStatus.men += 1
// 레벨에 때라 레벨업 밸런스 조정
if (this.characterStatus.level < 10) {
this.characterStatus.totalLevelBar = this.characterStatus.totalLevelBar = parseInt(this.characterStatus.totalLevelBar * 1.2)
} else if (this.characterStatus.level < 30) {
this.characterStatus.totalLevelBar = this.characterStatus.totalLevelBar = parseInt(this.characterStatus.totalLevelBar * 1.3)
} else if (this.characterStatus.level < 50) {
this.characterStatus.totalLevelBar = this.characterStatus.totalLevelBar = parseInt(this.characterStatus.totalLevelBar * 1.1)
} else if (this.characterStatus.level < 70) {
this.characterStatus.totalLevelBar = this.characterStatus.totalLevelBar = parseInt(this.characterStatus.totalLevelBar * 1.3)
} else if (this.characterStatus.level < 100) {
this.characterStatus.totalLevelBar = this.characterStatus.totalLevelBar = parseInt(this.characterStatus.totalLevelBar * 1.1)
}
}
}
}
</script>
아이템 관련 기능
<templates>
<h3>상점</h3>
<label>
<!-- checkbox의 값을 v-model로 양방향 맵핑. 초기 설정은 false(shopView가 false임) -->
<!-- 클릭할 때마다 shuffleShopList()이 동작해 판매목록이 랜덤하게 나옴 -->
<input type="checkbox" v-model="shopView" v-on:click="shuffleShopList()">
판매 목록
</label>
<button v-on:click="calcBuyList()">구매 확정</button>
// showView가 true이면 table이 보임
<table border="1" v-if="shopView">
<tr>
<th align="center" width="40">번호</th>
<th align="center" width="120">아이템명</th>
<th align="center" width="160">가격</th>
<th align="center" width="320">아이템 설명</th>
<th align="center" width="40">구매</th>
</tr>
<!-- v-for문이 돌면서 shopList의 데이터를 하나씩 출력 -->
<tr v-for="(item, index) in shopList" :key="index">
<th align="center" width="40">{{ index }}</th>
<th align="center" width="120">{{ item.name }}</th>
<th align="center" width="160">{{ item.price }}</th>
<th align="center" width="320">{{ item.effect.description }}</th>
<th align="center" width="40">
<label>
<!-- v-model로 양방향 맵핑, checkbox에 체크된 열의 index 값이 shopListValue에 저장됨 -->
<input type="checkbox" v-model="shopListValue" :value="index">
</label>
</th>
</tr>
</table><br/><br/>
<h3>인벤토리</h3>
<label>
<input type="checkbox" v-model="inventoryView">
소지품 보기
</label>
<button v-on:click="equipItem()">아이템 장착!</button>
<table border="1" v-if="inventoryView">
<tr>
<th align="center" width="40">번호</th>
<th align="center" width="120">아이템명</th>
<th align="center" width="320">아이템 설명</th>
<th align="center" width="40">장착</th>
</tr>
<tr v-for="(itemList, idx) in myInventory" :key="idx">
<th align="center" width="40">{{ idx + 1 }}</th>
<th align="center" width="120">{{ itemList.name }}</th>
<th align="center" width="320">{{ itemList.effect.description }}</th>
<th align="center" width="40">
<label>
<input type="checkbox" v-model="myInventoryValue" :value="idx">
</label>
</th>
</tr>
</table><br/><br/>
</templates>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Test",
data() {
return {
inventoryView: false,
myInventory: [],
myInventoryValue: [],
shopView: false,
shopList: [],
// 상점에서 구매에 체크된 아이템의 index값들을 담는 리스트
shopListValue: [],
itemBooks: [
{ name: 'HP 포션 I', price: 50, effect: { description: 'hp 200 회복', amount: 200 }},
{ name: 'HP 포션 II', price: 200, effect: { description: 'hp 600 회복', amount: 600 }},
{ name: '낡은 검', price: 5000000, effect: { description: '무기 공격력 100', atk: 100 }},
{ name: '검', price: 50000000, effect: { description: '무기 공격력 200', atk: 200 }},
]
}
},
methods: {
// 아이템 장착(atk만)
equipItem () {
// 장착 아이템의 atk를 합산하는 변수 tmpSum 선언
let tmpSum = 0
// myInventoryValue길이만큼 돌면서
for (let i = 0; i < this.myInventoryValue.length; i++) {
// i번째 myInventoryValue 값이 myInvetory index(j)와 일치하면 atk 스탯 올리기
for (let j = 0; j < this.myInventory.length; j++) {
if (this.myInventoryValue[i] === j) {
tmpSum += this.myInventory[j].effect.atk
break
}
}
}
this.characterStatus.itemAtk = tmpSum
this.characterStatus.atk = this.characterStatus.defaultAtk + tmpSum
},
// 아이템 상점에 랜덤 아이템 표시하기
shuffleShopList () {
if (!this.shopView) {
this.shopListValue = []
}
for (let i = 0; i < 10; i++) {
let randIdx = Math.floor(Math.random() * this.itemBooks.length)
this.shopList[i] = this.itemBooks[randIdx]
}
},
// 아이템 구매 하기
calcBuyList () {
let tmpSum = 0
for (let i = 0; i < this.shopListValue.length; i++) {
for (let j = 0; j < this.shopList.length; j++) {
if (this.shopListValue[i] === j) {
tmpSum += this.shopList[j].price
break;
}
}
}
// 고른 아이템 가격만큼 돈이 있는지 검사해서 구입 가능 여부 결정
if (this.characterStatus.money - tmpSum >= 0) {
this.characterStatus.money -= tmpSum
for (let i = 0; i < this.shopListValue.length; i++) {
// 구매 아이템 인벤토리에 추가
this.myInventory.push({
name: this.shopList[this.shopListValue[i]].name,
effect: this.shopList[this.shopListValue[i]].effect
})
}
alert("물품 구매 완료!")
} else {
alert("돈없음. 돈벌어와!!")
}
}
},
beforeUpdate() {
}
}
</script>
const를 써서 만든 기능도 있지만 귀찮아서 스킵..ㅎ..
const가 상수라는 것만 알면 되지 뭐^^!!
'국비지원 > 시즌2' 카테고리의 다른 글
Vue.js 기초를 배우는 중: 국비 웹개발 과정 41일차 (0) | 2022.08.25 |
---|---|
7차 과제: 로또게임 예제코드 & 애자일보드 비교분석 (0) | 2022.08.20 |
다시 자바 복습.. (0) | 2022.08.19 |
과제 3: 학생들 점수를 임의로 배치하고 평균값 구하기. (0) | 2022.07.22 |
/homework1 URL을 요청하면 주사위를 굴려서 주사위 값 화면에 출력 되게 만들기 (0) | 2022.07.21 |