자바스크립트를 이용하여
자스스톤 게임을 구현하는 예제(ver.4)입니다.
- 게임 공격 세팅 -
자스스톤 게임을 구현하는 예제(ver.4)입니다.
- 게임 공격 세팅 -
예제는 인프런의 제로초, "조현영"님의 강의를 들으면서 공부한 내용입니다.
구현내용
· 상대방의 영웅 또는 쫄병을 한번씩 공격하기
· 내 턴일때 카드 클릭 불가능 -> 클릭 가능하도록 변경
· 공격하여 상대방 카드 공격하여 제거하기
완성 코드
html
<div><small>초록: 체력, 주황: 공격력, 적 영웅의 체력이 0이되면 영웅을 보호하기 위해
<br />쫄병들을 코스트(우드색)내에서 뽑는다.</small></div>
<div id="rival">
<div>코스트:<span id="rival-cost">10</span>/<span>10</span></div>
<div id="rival-hero"></div>
<div id="rival-cards" style="height: 100px">
</div>
</div>
<div id="rival-deck"></div>
<hr />
<button id="turn-btn">턴 교체</button>
<div class="turn-box">현재 턴: <span id="turn-type">나의 턴</span></div>
<div id="my">
<div id="my-cards" style="height: 100px"></div>
<div id="my-hero"></div>
<div>코스트:<span id="my-cost">10</span>/<span>10</span></div>
</div>
<div id="my-deck"></div>
<div class="card-hidden">
<div class="card">
<div class="card-cost"></div>
<div class="card-att"></div>
<div class="card-hp"></div>
</div>
</div>
css
#my,
#rival {
display: inline-block;
vertical-align: top;
margin-right: 50px;
}
#my {
margin-bottom: 10px;
}
#rival {
vertical-align: bottom;
margin-top: 10px;
}
#my-deck,
#rival-deck {
display: inline-block;
vertical-align: top;
width: 200px;
background-color: antiquewhite;
}
#rival-deck,
#rival-hero,
#rival-cards,
#my-cards,
#my-deck,
#my-hero {
text-align: center;
}
#rival-cards>.card,
#my-cards .card {
width: 40px;
height: 55px;
}
.card {
width: 50px;
height: 75px;
display: inline-block;
position: relative;
border: 1px solid #dedede;
margin: 10px 0;
font-size: 12px;
margin-right: 10px;
background-color: aliceblue;
}
.card-att,
.card-hp,
.card-cost {
width: 15px;
height: 15px;
border-radius: 50%;
position: absolute;
bottom: 0;
border: 1px solid #828282;
font-size: 12px;
line-height: 15px;
text-align: center;
font-weight: bold;
}
.card-cost {
bottom: auto;
right: 0;
background-color: burlywood;
}
.card-att {
left: 0;
background-color: coral;
}
.card-hp {
right: 0;
background-color: darkseagreen;
}
.card-turnover {
background: gray;
}
.card-hidden {
display: none;
}
.card-selected {
border: 2px solid navy;
}
#turn-btn {
float: right;
position: relative;
top: -20px;
}
.turn-box {
text-align: center;
margin: 15px 0;
font-weight: bold;
}
script
//변수를 딕셔너리 구조로 세팅
const 상대 = {
영웅: document.getElementById('rival-hero'),
덱: document.getElementById('rival-deck'),
필드: document.getElementById('rival-cards'),
코스트: document.getElementById('rival-cost'),
덱data: [],
영웅data: [],
필드data: [],
선택카드: null,
선택카드data: null,
}
const 나 = {
영웅: document.getElementById('my-hero'),
덱: document.getElementById('my-deck'),
필드: document.getElementById('my-cards'),
코스트: document.getElementById('my-cost'),
덱data: [],
영웅data: [],
필드data: [],
선택카드: null,
선택카드data: null,
}
const 턴버튼 = document.getElementById('turn-btn');
const 턴타입 = document.getElementById('turn-type');
let 턴 = true; //true는 나의 턴, false는 상대 턴
//리팩토링 함수
function 덱에서필드로(데이터, 내턴) {
//스코프안에 데이터가 없음 -> 카드돔연결(데이터, 돔, 영웅)에서 데이터 선언을 가져와야함
let 객체 = 내턴 ? 나 : 상대; //삼항연산자 나 = true, 상대 = false
let 현재코스트 = Number(객체.코스트.textContent);
if (현재코스트 < 데이터.cost) {
return 'end';
}
let idx = 객체.덱data.indexOf(데이터);
객체.덱data.splice(idx, 1);
객체.필드data.push(데이터);
//화면 업데이트 구현이 어려워 전체 데이터 지웠다가 다시 필드, 덱 출력
객체.덱.innerHTML = '';
객체.필드.innerHTML = '';
객체.필드data.forEach(function(data) { //뽑은 카드 올라감
카드돔연결(data, 객체.필드);
});
객체.덱data.forEach(function(data) { //뽑은 카드는 배열에서 사라짐
카드돔연결(data, 객체.덱);
});
데이터.field = true; //필드에 올라간 카드(위에서 재클릭 방지)
객체.코스트.textContent = 현재코스트 - 데이터.cost;
}
function 화면다시그리기(내화면) {
let 객체 = 내화면 ? 나 : 상대;
객체.덱.innerHTML = '';
객체.필드.innerHTML = '';
객체.영웅.innerHTML = '';
객체.필드data.forEach(function(data) { //뽑은 카드 올라감
카드돔연결(data, 객체.필드);
});
객체.덱data.forEach(function(data) { //뽑은 카드는 배열에서 사라짐
카드돔연결(data, 객체.덱);
});
카드돔연결(객체.영웅data, 객체.영웅, true);
}
//카드 화면 출력
function 카드돔연결(데이터, 돔, 영웅) {
let 카드 = document.querySelector('.card-hidden .card').cloneNode(true);
카드.querySelector('.card-cost').textContent = 데이터.cost;
카드.querySelector('.card-att').textContent = 데이터.att;
카드.querySelector('.card-hp').textContent = 데이터.hp;
//영웅일때 cost 감추기
if (영웅) {
카드.querySelector('.card-cost').style.display = 'none';
let 이름 = document.createElement('div');
이름.textContent = '영웅',
카드.appendChild(이름);
}
//클릭한 카드 덱에서 필드로 그대로 옮기기
카드.addEventListener('click', function(card) {
if (턴) { //내 필드, 내턴
// if (!데이터.mine || 데이터.field) {
//상대카드를 뽑을 경우, 데이터가 field에 있으면(필드에 있는 카드 클릭 방지)
// return;
// }
if (카드.classList.contains('card-turnover')) { //card-turnover아닌 상태
return;
}
if (!데이터.mine && 나.선택카드) {
// 내카드가 card-selected 된 상태에서 상대방 클릭하면 공격
데이터.hp = 데이터.hp - 나.선택카드data.att;
if (데이터.hp <= 0) { //키드가 죽었을 때 카드 제거하기
let 인덱스 = 상대.필드data.indexOf(데이터);
if (인덱스 > -1) { //쫄병이 죽었을때
상대.필드data.splice(인덱스, 1);
} else { //영웅이 죽었을때
alert('승리하였습니다.');
초기세팅();
}
}
화면다시그리기(false);
나.선택카드.classList.remove('card-selected'); //카드 선택해제
나.선택카드.classList.add('card-turnover'); //카드 선택해제
나.선택카드 = null; //초기화
나.선택카드data = null; //초기화
return;
} else if (!데이터.mine) { //상대 카드면
return;
}
if (데이터.field) { //카드가 필드에 있으면
//중요 클릭한 한개의 카드만 선택이 되어야 하므로
//상위 태그 전체를 querySelectorAll로 지정 후
//forEach 반복문으로 전체를 다 class명을 제거한 후 클래스를 다시 줌 =>
//한개만 선택하게 됨
카드.parentNode.querySelectorAll('.card').forEach((item) =>
item.classList.remove('card-selected'));
카드.classList.add('card-selected');
나.선택카드 = 카드;
나.선택카드data = 데이터;
} else { //카드가 필드에 없으면(덱에 있으면)
if (덱에서필드로(데이터, true) !== 'end') { //true = 내턴
내덱생성(1); //카드 한개 더 뽑기
}
}
} else { //상대방 필드 상대턴
if (카드.classList.contains('card-turnover')) { //card-turnover아닌 상태
return;
}
if (데이터.mine && 상대.선택카드) { // 내카드가 card-selected 된 상태에서
//상대방 클릭하면 공격
데이터.hp = 데이터.hp - 상대.선택카드data.att;
if (데이터.hp <= 0) { //키드가 죽었을 때 카드 제거하기
let 인덱스 = 나.필드data.indexOf(데이터);
if (인덱스 > -1) { //쫄병이 죽었을때
나.필드data.splice(인덱스, 1);
} else { //영웅이 죽었을때
alert('패배하였습니다.');
초기세팅();
}
}
화면다시그리기(true);
상대.선택카드.classList.remove('card-selected'); //카드 선택해제
상대.선택카드.classList.add('card-turnover'); //카드 선택해제
상대.선택카드 = null; //초기화
상대.선택카드data = null; //초기화
return;
} else if (데이터.mine) {
return;
}
if (데이터.field) {
카드.parentNode.querySelectorAll('.card').forEach((item) =>
item.classList.remove('card-selected'));
카드.classList.add('card-selected');
상대.선택카드 = 카드;
상대.선택카드data = 데이터;
} else {
if (덱에서필드로(데이터, false) !== 'end') { //false = 상대턴
상대덱생성(1);
}
}
}
});
돔.appendChild(카드);
}
function 상대덱생성(개수) {
for (let i = 0; i < 개수; i++) {
상대.덱data.push(카드공장());
}
//자바스크립트로 바뀐 데이터만 화면 출력시키는 것이 힘들기 때문에 다 지웠다가 출력
상대.덱.innerHTML = '';
상대.덱data.forEach(function(data) {
카드돔연결(data, 상대.덱);
})
};
function 내덱생성(개수) {
for (let i = 0; i < 개수; i++) {
나.덱data.push(카드공장(false, true)); //영웅 X, 내카드 O
}
//다 지웠다가 출력
나.덱.innerHTML = '';
나.덱data.forEach(function(data) {
카드돔연결(data, 나.덱);
});
};
function 내영웅생성() {
나.영웅data = 카드공장(true, true); //영웅 O, 내카드 O
카드돔연결(나.영웅data, 나.영웅, true);
};
function 상대영웅생성() {
상대.영웅data = 카드공장(true);
카드돔연결(상대.영웅data, 상대.영웅, true);
};
function 초기세팅() {
상대덱생성(5);
내덱생성(5);
내영웅생성();
상대영웅생성();
화면다시그리기(true); //내화면
화면다시그리기(false); //상대화면
}
function Card(영웅, 내카드) { //문제해결 내카드 추가
if (영웅) {
this.att = Math.ceil(Math.random() * 2);
this.hp = Math.ceil(Math.random() * 5) + 25;
this.hero = true;
this.field = true;
} else {
this.att = Math.ceil(Math.random() * 5);
this.hp = Math.ceil(Math.random() * 5);
this.cost = Math.floor((this.att + this.hp) / 2);
}
if (내카드) {
this.mine = true;
}
}
function 카드공장(영웅, 내카드) {
return new Card(영웅, 내카드);
}
//턴 넘기기
턴버튼.addEventListener('click', function() {
//턴 넘길때 card-turnover 된 카드들 초기화
let 객체 = 턴 ? 나 : 상대;
객체.필드.innerHTML = '';
객체.영웅.innerHTML = '';
객체.필드data.forEach(function(data) { //뽑은 카드 올라감
카드돔연결(data, 객체.필드);
});
카드돔연결(객체.영웅data, 객체.영웅, true);
턴 = !턴; //true는 false로, false는 true로
if (턴) {
나.코스트.textContent = 10; //코스트 10으로 채우기
턴타입.innerHTML = '나의 턴'
} else {
상대.코스트.textContent = 10;
턴타입.innerHTML = '상대방 턴'
}
});
초기세팅();
화면 결과
반응형
'개발 > Javascript' 카테고리의 다른 글
[js] 글자 타이핑 효과 라이브러리 소개 (2) | 2021.05.08 |
---|---|
[js] react로 구구단 게임 만들기(ft. 클래스 방식, cdn 설치) (0) | 2021.03.14 |
[js] 카드세팅 리팩토링 딕셔너리 구조 (ft. 자바스크립트로 자스스톤 게임 구현 ver.3) (0) | 2021.03.02 |
[js] 생성자, 카드세팅 (ft. 자바스크립트로 자스스톤 게임 구현 ver.2) (0) | 2021.03.02 |
[js] 팩토리, 생성자 패턴, 카드세팅 (ft. 자바스크립트 자스스톤 게임 구현 ver.1) (0) | 2021.02.28 |
댓글