리듬킹2(개인 프로젝트)
Start버튼을 누르면 로딩화면이 2.5초간 나타난 후 선택된 곡을 연주할 수 있다. 이전에 버튼들과 마찬가지로, 마우스가 Start버튼에 위치하면 버튼 크기가 작아지고, 커서가 손가락 모양으로 변한다.
Menu17.js
song_list = [],
...
song_list =
[
"Beethoven Virus(Easy)",
"Beethoven Virus(Normal)",
"Fantaisie(Easy)",
"Fantaisie(Normal)",
"Piano Conceto1(Easy)",
"Piano Conceto1(Normal)",
"Swan:Black(Easy)",
"Swan:Black(Normal)",
"Swan:White(Easy)",
"Swan:White(Normal)",
"The BumbleBee(Easy)",
"The BumbleBee(Normal)",
"Toccata and Fuga(Easy)",
"Toccata and Fuga(Normal)"
];
...
//캔버스에서 마우스 누르는것 감지
canvas.addEventListener('mousedown', function(e)
{
X_loc=e.offsetX;
Y_loc=e.offsetY;
if(cur_status == 0)
{
...
//start버튼
if(260 <= X_loc
&& X_loc <=500)
{
if(700 <= Y_loc && Y_loc <= 800)
{
context.drawImage(start_button,260,700,240,100);
//연주할 곡에 관한 정보를 담고있는 js파일의 경로를 module_path에 지정해 준다.
module_path = './songs_info/'+ song_list[cur_num+(cur_page-1)*8] +'.js';
cur_status = 1;
pic_path = pic_list[cur_num+(cur_page-1)*8];
pic.src = pic_path;
pic.onload = function()
{
context.drawImage(pic,0, 0, 565, 810);
loading_sound.play();
}
setTimeout(
function ()
{
playing();
},
2500);
}
}
}
...
}
로딩화면
Menu17.js
...
image = new Image(),
image.src = 'shared/images/Frame7.png';
...
function playing()
{
//플레이시 게임화면을 보여준다.
context.drawImage(image,0,0);
//score 및 max_combo콤보의 변화시 가릴 초기 이미지를 저장해둔다.
max_combo_Default_img = context.getImageData(91,733,182,15);
score_Default_img = context.getImageData(141,750,132,15);
...
}
...
buttom_line_drawer함수를 이용해 하단에 검붉은 판정바를 그려준다.
Menu17.js
...
function playing()
{
...
buttom_line_drawer();
}
//하단의 판정바를 그려준다.
function buttom_line_drawer()
{
context.save();
context.fillStyle='rgba(200,0,0,0.4)';
context.fillRect(30,context.canvas.height-185,440,20);
context.restore();
}
...
배경이미지와 라이프바를 그려준다.
Menu17.js
...
background = new Image(),
life_bar = new Image(),
life_bar_Default_img = new Image(),
life_bar_percentage = 0.7,
life_bar_width = 37,
life_bar_height = 392,
life_bar_blank_x_loc = 495,
life_bar_blank_y_loc = 250,
life_bar_blank_width = 40,
life_bar_blank_height = 380,
life_bar_down,
life_bar_edit,
life_bar.src = 'shared/images/life_bar.png';
...
function playing()
{
//module_path에 저장된 곡에 대한 정보들을 가져온다.
import(module_path)
.then( (module) =>
{
...
background.src = module.background_src;
...
});
background.onload = function()
{
...
//context.canvas.width-125 = 565-125 = 440
//context.canvas.height-180 = 810-180 = 630
context.drawImage(background,30, 0, context.canvas.width-125, context.canvas.height-185);
//라이프바 채워지기진 이미지 저장
life_bar_Default_img = context.getImageData(life_bar_blank_x_loc,life_bar_blank_y_loc,
life_bar_blank_width,life_bar_blank_height+10);
//맨처음 실행시 라이프바 그리기(70%)
life_bar_down = (life_bar_height * life_bar_percentage); //274
life_bar_edit = life_bar_height -life_bar_down ; //392-274=117
context.drawImage(life_bar
,0,life_bar_edit
,life_bar_width, life_bar_height -life_bar_edit
,life_bar_blank_x_loc, life_bar_blank_y_loc+life_bar_edit
,life_bar_blank_width, life_bar_blank_height-life_bar_edit);
...
}
}
Menu17.js
...
b_Default_img = [],
b_Pressed_img = [],
...
function playing()
{
background.onload = function()
{
...
context.save();
context.fillStyle='rgba(200,0,0,1)';
for(var i=0; i<5; i++)
{
//버튼이 눌리기전 이미지 저장
b_Default_img[i] = context.getImageData(x_y[i][0],x_y[i][1]-40,x_y[i][2],x_y[i][3]+40);
//버튼이 눌렸을때 보여줄 이미지 저장
b_Pressed_img[i] = context.getImageData(x_y[i][0],x_y[i][1]+5,x_y[i][2],x_y[i][3]-15);
}
context.restore();
//노래 제목을 입력한다.
context.save();
context.fillStyle='rgba(255,255,255,1)';
context.font = "italic 14px palatino";
context.fillText(song_name,396,749);
context.restore();
...
}
}
버튼이 눌리기전 이미지 b_Default_img[]에 저장
버튼이 눌렸을 때 쓰여질 이미지 b_Pressed_img[]에 저장
노래 제목을 보여준다.
노래를 재생한다. 16m초마다 각 라인에 맞게 들어갈 노트가 있는지 확인하고, 조건에 맞다면 lines배열에 push함수를 이용해 넣어준다.
Menu17.js
...
function playing()
{
background.onload = function()
{
...
iterable = 0;
sound_track.play();
run = setInterval
(
function()
{
//노트를 넣어준다.
//63/5 == 126(맨 위에서 판정바까지 노트가 내려가는데 걸리는 프레임)
//1000/16 == 62.5(초당 프레임수)
//126/62.5 == 악 2초(맨 위에서 판정바까지 노트가 내려가는데 걸리는 시간)
try
{
temp = Math.round(notes[cur_idx][0] / 16 - 126);
if(temp == iterable)
{
temp_length = notes[cur_idx].length;
//숏노트를 lines배열에 넣어준다.
if(temp_length == 1)
{
console.log("인덱스 : " + order);
for(var i of locs[cur_idx])
{
lines[i].push([0,15, 0, 1]);
}
}
//롱노트를 lines배열에 넣어준다.
else
{
console.log("인덱스 : " + order);
for(var i of locs[cur_idx])
{
lines[i].push([0,25*notes[cur_idx][1],0,notes[cur_idx][1]-1]);
}
}
cur_idx +=1;
order +=1;
}
effect_drawer();
}
catch(e)
{
...
}
iterable +=1;
},
16);
}
}
16ms마다 effect_drawer함수를 실행되고, 키들(s,d,g,j,k)을 눌렀을 때 화면의 변화를 보여준다.
Menu17.js
...
hitting_effect = [],
gradation_img = [],
judges = [],
numbers = [];
cur_judges = [-1,-1,-1,-1,-1],
keys_pressed = [false,false,false,false,false],
...
for(var i=0; i<10; i++)
{
if(i<5)
{
hitting_effect[i] = new Image();
hitting_effect[i].src = 'shared/images/hitting_effect'+(i+1)+'.png';
gradation_img[i] = new Image();
gradation_img[i].src = 'shared/images/'+(i+1)+'.png';
//판정 나타내는 그림 가져오기
if(i<4)
{
judges[i] = new Image();
judges[i].src = 'shared/images/judge_'+(i)+'.png';
}
}
numbers[i] = new Image();
numbers[i].src = 'shared/images/num_'+(i)+'.png';
}
...
function effect_drawer()
{
//배경이미지 초기화
//context.canvas.width-125 = 565-125 = 440
//context.canvas.height-180 = 810-180 = 630
context.drawImage(background,30,0,context.canvas.width-125,context.canvas.height-185);
buttom_line_drawer();
for(var i=0; i<5; i++)
{
if(keys_pressed[i])
{
//버튼이 눌렸을때 이미지를 위에 그린다.
context.save();
context.fillStyle='rgba(0,0,0,1)';
context.fillRect(x_y[i][0],x_y[i][1],x_y[i][2],20);
context.restore();
context.putImageData(b_Pressed_img[i],x_y[i][0],x_y[i][1]+15);
//그라데이션 그리기
context.save();
context.globalAlpha=0.6;
context.drawImage(gradation_img[i],x_y[i][0],0,x_y[i][2],context.canvas.height-185);
context.restore();
...
}
else
{
//버튼이 안눌리면 어둡게, 눌리면 밝은 빨간색으로 판정라인이 보이게 되는것은
//바로 아랫줄의 b_Default_img[i]와 buttom_line_drawer();의
//상호 작용의 결과이다.
context.putImageData(b_Default_img[i],x_y[i][0],x_y[i][1]-40);
itrs[i] = 0;
}
...
}
}
...
/*
키코드값
...
s => 83
d => 68
g => 71
j => 74
k => 75
...
참고 : keypress이벤트의 경우 keydown,keyup이벤트와 달리 ASCII코드를 받는다.
*/
k_code = [83,68,71,74,75],
//키를 때는것 감지
window.addEventListener('keyup', function(e)
{
if(-1<k_code.indexOf(e.keyCode) && k_code.indexOf(e.keyCode)<5)
{
keys_pressed[k_code.indexOf(e.keyCode)] = false;
keep_pressing[k_code.indexOf(e.keyCode)] = false;
hitting[k_code.indexOf(e.keyCode)] = 0;
}
});
키보드서 I를 누른경우
16ms마다 effect_drawer함수를 실행되서, playing함수에서 lines에 업력했던 정보를 이용해 노트들을 화면에 보여준다.
Menu17.js
...
function effect_drawer()
{
//배경이미지 초기화
//context.canvas.width-125 = 565-125 = 440
//context.canvas.height-180 = 810-180 = 630
context.drawImage(background,30,0,context.canvas.width-125,context.canvas.height-185);
buttom_line_drawer();
for(var i=0; i<5; i++)
{
...
for(var j=0; j<lines[i].length; j++)
{
cur_loc = lines[i][j][0];
leng = lines[i][j][1];
comp = (lines[i][j][2] >= 645) ? 645 : lines[i][j][2];
//leng값은 안변한다.
//숏노트
if(leng == 15)
{
j=single_note(i,j,cur_loc);
}
//(25<=leng<645)
//롱노트
else if(leng<645)
{
j=under_645(i,j,cur_loc,leng,comp);
}
//(645<=leng)
//롱노트
else
{
j=more_645(i,j,cur_loc,leng,comp);
}
}
...
}
}
lines에 업력된 정보가 숏노트에 해당하는 경우 j=single_note(i,j,cur_loc);가 실행된다. single_note에서는 숏노트를 화면에 보여주고, 조건에 따라 히트처리 여부를 결정한다.
Menu17.js
...
pre_pressing = [false,false,false,false,false],
notes_img = [new Image(),new Image(),new Image(),new Image(),new Image()],
notes_img[0].src = 'shared/images/note_b.png';
notes_img[1].src = 'shared/images/note_s.png';
notes_img[2].src = 'shared/images/note_g.png';
notes_img[3].src = 'shared/images/note_s.png';
notes_img[4].src = 'shared/images/note_b.png';
...
function single_note(i,j,cur_loc)
{
//판정바 밖에 노트가 위치한 경우
if(cur_loc < 610)
{
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],15);
lines[i][j][0] = cur_loc+5;
}
//판정바 안에 노트가 위치한 경우
else
{
//노트가 아래 빨간바에 닿기 전 미리 버튼을 눌렀을때 BAD로 판정되는 것을 막기위한
//바로 아래의 조건문을 코딩했다.
if(cur_loc == 610)
{
if(keys_pressed[i])
{
pre_pressing[i] = true;
}
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],15);
lines[i][j][0] = cur_loc+5;
}
/* 작동 예시
keys_pressed[i]=true, pre_pressing[i]=true == 계속 버튼 누르고 있다
=> 히트처리 안한다.
keys_pressed[i]=true, pre_pressing[i]=false == 버튼을(눌렀다가 땐후 다시) 눌렀다
=> 히트처리 한다
keys_pressed[i]=false, pre_pressing[i]=true == 버튼 눌렀다가 땟다.
=> pre_pressing[i]=false로 바꾼다.
keys_pressed[i]=flase, pre_pressing[i]=false== 버튼 누르지 않았다
=> 히트처리 안한다.
*/
else if(cur_loc < 660)
{
//console.log("cur_loc <= 630 실행");
//노트가 판정바를 지나가기전 조금 걸쳐있는 상황
if(keys_pressed[i])
{
//keys_pressed[i]=true, pre_pressing[i]=false
//== 버튼을(눌렀다가 땐후 다시) 눌렀다 => 히트처리 한다
if(!pre_pressing[i])
{
cur_judge=hitting_judge_s(cur_loc);
hitting[i] += 1;
lines[i].splice(j, 1);
j --;
}
//keys_pressed[2]=true, pre_pressing[i]=true
//== 계속 버튼 누르고 있다 => 히트처리 안한다.
else
{
if(cur_loc < 630)
{
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],15);
}
else if(cur_loc < 645)
{
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],645-cur_loc);
}
lines[i][j][0] = cur_loc+5;
}
}
//keys_pressed[2]=flase == 버튼이 안눌렸다 => 히트처리 안한다.
else
{
//keys_pressed[2]=false, pre_pressing[i]=true
//== 버튼 눌렀다가 땟다.=> pre_pressing[i]=false로 바꾼다.
if(pre_pressing[i])
{
pre_pressing[i]=false;
}
if(cur_loc < 630)
{
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],15);
}
else if(cur_loc < 645)
{
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],645-cur_loc);
}
lines[i][j][0] = cur_loc+5;
}
}
//노트가 판정바를 완전히 지나간 경우
else
{
//console.log("645 < cur_loc 실행");
//console.log("cur_judge="+ 3);
cur_judge = 3;
subtracting = true;
hitting[i] = 0;
lines[i].splice(j, 1);
j --;
pre_pressing[i]=false;
}
}
return j;
}
...
//숏 노트의cool,good,bad,miss등을 판단
function hitting_judge_s(iterable)
{
if((Math.abs(635 - iterable)/20) <= 0.25)
{
cool +=1;
//console.log("cool = " + cool);
score += 100;
return 0;
}
else if((Math.abs(635 - iterable)/20) <= 0.75)
{
good +=1;
//console.log("good = " + good);
score += 70;
return 1;
}
else if((Math.abs(635 - iterable)/20) <= 1.0)
{
bad +=1;
//console.log("bad = " + bad);
score += 40;
return 2;
}
}
lines에 업력된 정보가 길이 645px 미만의 노트에 해당하는 경우 j=under_645(i,j,cur_loc,leng,comp);가 실행된다. under_645에서는 롱노트를 화면에 보여주고, 조건에 따라 히트처리 여부를 결정한다.
Menu17.js
...
notes_img = [new Image(),new Image(),new Image(),new Image(),new Image()],
notes_img[0].src = 'shared/images/note_b.png';
notes_img[1].src = 'shared/images/note_s.png';
notes_img[2].src = 'shared/images/note_g.png';
notes_img[3].src = 'shared/images/note_s.png';
notes_img[4].src = 'shared/images/note_b.png';
...
function under_645(i,j,cur_loc,leng,comp)
{
//롱노트가 상하단서 짤린 모습
if(lines[i][j][2] < leng)
{
//롱노트가 상단에서 내려오고 있다(아직 롱노트 전부가 화면에 나오진 않은 상황)
//아래 if문 실행시 lines[i][j][2]가 leng까지 커짐
if(cur_loc == 0)
{
//롱 노트가 판정바에 닿기전 미리 버튼을 누르고 있었는지 판단
if(625 == comp)
{
if(keys_pressed[i])
{
pre_pressing[i] = true;
}
}
//롱 노트의 하단이 판정바의 영역에 들어온 경우 판단
else if(625 < comp)
{
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
hitting_processing_L(i, comp);
}
context.drawImage(notes_img[i],x_y[i][0],0,x_y[i][2],comp);
lines[i][j][2] += 5;
}
//롱노트가 하단의 판장바 아랫변에 닿은후 점점 사라지는 상황
//아래 if문 실행시 lines[i][j][2]가 0까지 점점 작아짐
else
{
//롱 노트의 하단이 판정바의 영역에 들어온 경우 판단
if(leng-comp < 20)
{
hitting_processing_L(i, cur_loc+leng);
}
//롱노트의 하단 부가 15px이상 벗어나가 시작하는 지점
else
{
missing_processing_L(i);
}
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],comp);
lines[i][j][0] += 5;
lines[i][j][2] -= 5;
//하단바를 롱노트가 완전히 지난 경우 노트 관련 정보를 지워준다.
//if(lines[i][j][0] == 645)
if(lines[i][j][2] == 0)
{
if(cur_judges[i] == 3)
{
subtracting = true;
}
lines[i].splice(j, 1);
j--;
pre_pressing[i] = false;
keep_pressing[i] = false;
hitting[i] = 0;
cur_judges[i] = -1;
once[i] = 1;
}
}
}
else
{
//롱노트가 화면에 완전히 나타난 상황
if(cur_loc+leng < 645)
{
//롱 노트가 판정바에 닿기전 미리 버튼을 누르고 있었는지 판단
if(625 == cur_loc+leng)
{
if(keys_pressed[i])
{
pre_pressing[i] = true;
}
}
//롱 노트의 하단이 판정바의 영역에 들어온 경우 판단
else if(625 < cur_loc+leng )
{
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
hitting_processing_L(i, cur_loc+leng);
}
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],leng);
lines[i][j][0] += 5;
}
//롱노트의 하단이 판정바 아랫변에 닿은 상황
//else if(cur_loc+leng == 645)
else
{
//comp = (leng >= 645) ? 645 : lines[i][j][2];
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
hitting_processing_L(i, cur_loc+comp);
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],comp);
lines[i][j][0] = cur_loc+5;
lines[i][j][2] = comp-5;
}
}
return j;
}
...
function hitting_processing_L(i, loc)
{
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
if(keys_pressed[i])
{
if(!pre_pressing[i])
{
if(cur_judges[i] ==-1)
{
cur_judges[i] = hitting_judge(loc);
keep_pressing[i] = true;
hitting[i] += lines[i][0][3];
}
}
}
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 안눌린경우
else
{
pre_pressing[i] = false;
keep_pressing[i] = false;
}
}
function missing_processing_L(i)
{
if(cur_judges[i] ==-1)
{
if(once[i])
{
cur_judge = 3;
once[i] -=1;
}
cur_judges[i] = 3;
pre_pressing[i] = false;
keep_pressing[i] = false;
hitting[i] = 0;
}
else
{
if(keep_pressing[i])
{
cur_judge = cur_judges[i];
}
else
{
if(once[i])
{
cur_judge = 3;
once[i] -=1;
}
cur_judges[i] = 3;
pre_pressing[i] = false;
keep_pressing[i] = false;
hitting[i] = 0;
}
}
}
//롱노트의 cool,good,bad,miss등을 판단
function hitting_judge(iterable)
{
if((Math.abs(650 - iterable)/20) <= 0.25)
{
return 0;
}
else if((Math.abs(650 - iterable)/20) <= 0.75)
{
return 1;
}
else if((Math.abs(650 - iterable)/20) <= 1.0)
{
return 2;
}
}
lines에 업력된 정보가 길이 645px 이상의 노트에 해당하는 경우j=more_645(i,j,cur_loc,leng,comp);가 실행된다. more_645에서는 롱노트를 화면에 보여주고, 조건에 따라 히트처리 여부를 결정한다.
Menu17.js
...
notes_img = [new Image(),new Image(),new Image(),new Image(),new Image()],
notes_img[0].src = 'shared/images/note_b.png';
notes_img[1].src = 'shared/images/note_s.png';
notes_img[2].src = 'shared/images/note_g.png';
notes_img[3].src = 'shared/images/note_s.png';
notes_img[4].src = 'shared/images/note_b.png';
...
function more_645(i,j,cur_loc,leng,comp)
{
//롱노트가 상하단서 짤린 모습
if(lines[i][j][2] < leng)
{
//롱노트가 내려오고 있는 중이다
if(cur_loc == 0)
{
//롱 노트가 판정바에 닿기전 미리 버튼을 누르고 있었는지 판단
if(lines[i][j][2] < 625)
{
}
else if(625 == lines[i][j][2])
{
if(keys_pressed[i])
{
pre_pressing[i] = true;
}
}
//롱 노트의 하단이 판정바의 영역에 들어온 경우 판단
else if(625 < lines[i][j][2] && lines[i][j][2] < 660)
{
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
hitting_processing_L(i, lines[i][j][2]);
}
else
{
missing_processing_L(i);
}
context.drawImage(notes_img[i],x_y[i][0],0,x_y[i][2],comp);
lines[i][j][2] += 5;
}
//롱노트가 내려오고 점점 사라지는 중이다
else
{
//if(cur_loc+645 < 660)
if(cur_loc < 20)
{
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
hitting_processing_L(i, cur_loc+645);
}
else
{
missing_processing_L(i);
}
context.drawImage(notes_img[i],x_y[i][0],cur_loc,x_y[i][2],comp);
lines[i][j][2] -= 5;
lines[i][j][0] += 5;
//하단바를 롱노트가 완전히 지난 경우 노트 관련 정보를 지워준다.
//if(lines[i][j][0] == 645)
if(lines[i][j][2] == 0)
{
if(cur_judges[i] == 3)
{
subtracting = true;
}
lines[i].splice(j, 1);
j--;
pre_pressing[i] = false;
keep_pressing[i] = false;
hitting[i] = 0;
cur_judges[i] = -1;
once[i] = 1;
}
}
}
else
{
if(lines[i][j][2] < 660)
{
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
hitting_processing_L(i, lines[i][j][2]);
}
else
{
missing_processing_L(i);
}
context.drawImage(notes_img[i],x_y[i][0],0,x_y[i][2],645);
lines[i][j][2] = 640;
lines[i][j][0] += 5;
}
return j;
}
function hitting_processing_L(i, loc)
{
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 눌린경우
if(keys_pressed[i])
{
if(!pre_pressing[i])
{
if(cur_judges[i] ==-1)
{
cur_judges[i] = hitting_judge(loc);
keep_pressing[i] = true;
hitting[i] += lines[i][0][3];
}
}
}
//롱 노트의 하단이 판정바 영역에 들어왔을 때, 버튼이 안눌린경우
else
{
pre_pressing[i] = false;
keep_pressing[i] = false;
}
}
function missing_processing_L(i)
{
if(cur_judges[i] ==-1)
{
if(once[i])
{
cur_judge = 3;
once[i] -=1;
}
cur_judges[i] = 3;
pre_pressing[i] = false;
keep_pressing[i] = false;
hitting[i] = 0;
}
else
{
if(keep_pressing[i])
{
cur_judge = cur_judges[i];
}
else
{
if(once[i])
{
cur_judge = 3;
once[i] -=1;
}
cur_judges[i] = 3;
pre_pressing[i] = false;
keep_pressing[i] = false;
hitting[i] = 0;
}
}
}
//롱노트의 cool,good,bad,miss등을 판단
function hitting_judge(iterable)
{
if((Math.abs(650 - iterable)/20) <= 0.25)
{
return 0;
}
else if((Math.abs(650 - iterable)/20) <= 0.75)
{
return 1;
}
else if((Math.abs(650 - iterable)/20) <= 1.0)
{
return 2;
}
}
16ms마다 effect_drawer함수를 실행되서, scroe, maxcombo, life, combo숫자등 에 변동된 상태를 반영해 준다.
Menu17.js
...
combo_marker = new Image(),
combo_marker.src = 'shared/images/combo.png';
judges = [],
for(var i=0; i<10; i++)
{
if(i<5)
{
...
//판정(cool, good, bad, miss)나타내는 그림 가져오기
if(i<4)
{
judges[i] = new Image();
judges[i].src = 'shared/images/judge_'+(i)+'.png';
}
}
//콤보 숫자를 나타낼 0~9까지 이미지 가져오기
numbers[i] = new Image();
numbers[i].src = 'shared/images/num_'+(i)+'.png';
}
...
function effect_drawer()
{
for(var i=0; i<5; i++)
{
if(keys_pressed[i])
{
...
//하단의 판정바에 노트가 있고, 버튼을 눌룬 상태
if(hitting[i])
{
itrs[i] +=1;
if(itrs[i]==5)
{
hitting[i] -= 1;
itrs[i] = 0;
combo +=1;
max_combo = (combo>max_combo) ? combo : max_combo;
//롱노트에서 히트했을 때 처리하기 위한 코딩
switch(cur_judges[i])
{
case 0:
cool +=1;
score += 100;
//console.log("cool = " + cool);
break;
case 1:
good +=1;
score += 70;
//console.log("good = " + good);
break;
case 2:
bad +=1;
score += 40;
//console.log("bad = " + bad);
break;
}
//cool,good,bad등이 뜨면 라이프 게이지 1%씩 증가
life_bar_percentage = ((life_bar_percentage+0.01)>= 1.0) ? 1.0
: life_bar_percentage+0.01;
if(life_bar_percentage <= 1.0)
{
context.putImageData(life_bar_Default_img,495,250);
life_bar_down = Math.floor(life_bar_height * life_bar_percentage);
life_bar_edit = life_bar_height -life_bar_down ;
context.drawImage(life_bar,0,life_bar_edit ,
life_bar_width, life_bar_height -life_bar_edit, life_bar_blank_x_loc,life_bar_blank_y_loc+life_bar_edit,
life_bar_blank_width,life_bar_blank_height-life_bar_edit);
}
/*score, max_combo값 보이기위한 코딩*/
//기존에 있던 값들을 지운다.
context.save();
context.fillStyle='rgba(0,0,0,1)';
context.fillRect(95,732,178,13);
context.fillRect(147,750,126,15);
context.restore();
//새로운 값들을 입력한다.
context.save();
context.fillStyle='rgba(255,255,255,1)';
context.font = "italic 14px palatino";
context.fillText(score,95,745);
context.fillText(max_combo,147,760);
context.restore();
}
}
}
...
if(cur_judge == 3)
{
combo=0;
}
...
//히트 효과 그리기
if(hitting[i])
{
context.drawImage(hitting_effect[itrs[i]],loc_x[i],context.canvas.height-205,60,60);
}
if(combo > 0)
{
//combo란 글자 화면에 그리기
context.drawImage(combo_marker,220,186);
//콤보수 화면에 그리기
combo_count(combo);
//console.log("cur_judge = " + cur_judge);
}
//cool,good,bad,miss란 글자 그리기
if(cur_judge != undefined)
{
context.drawImage(judges[cur_judge],200,400,100,100);
}
if(subtracting)
{
//miss뜨면 라이프 게이지 5%씩 감소
if(life_bar_percentage-0.05> 0.0)
{
context.putImageData(life_bar_Default_img,
life_bar_blank_x_loc,life_bar_blank_y_loc);
life_bar_percentage = (life_bar_percentage-0.05 < 0.0) ? 0.0
: life_bar_percentage - 0.05;
life_bar_down = Math.floor(life_bar_height * life_bar_percentage);
life_bar_edit = life_bar_height -life_bar_down ;
context.drawImage(life_bar,0,life_bar_edit ,
life_bar_width, life_bar_height -life_bar_edit, life_bar_blank_x_loc,life_bar_blank_y_loc+life_bar_edit,
life_bar_blank_width,life_bar_blank_height-life_bar_edit);
}
else
{
//challenge mode
if(C_P_MODE == 0)
{
...
}
//Practice mode
else
{
context.putImageData(life_bar_Default_img,
life_bar_blank_x_loc,life_bar_blank_y_loc);
}
}
subtracting = false;
}
}
}
메뉴 페이지에서 Challenge Mode버튼을 선택하고 플레이시, 라이프바가 전부 없어진다면 게임오버가 된다. 게임오버 이미지가 위에서 아래로 이동하고, 전체 화면을 꽉체웠다면, 0.5초 뒤에 Retry, Quit버튼이 화면에 나타난다.
Menu17.js
...
gameover= new Image(),
gameover_sound,
gameover.src = "shared/images/GAMEOVER.png",
gameover_sound = new Audio();
gameover_sound.src = 'sound/gameover_audio.mp4';
...
function effect_drawer()
{
for(var i=0; i<5; i++)
{
...
if(subtracting)
{
//miss뜨면 라이프 게이지 5%씩 감소
if(life_bar_percentage-0.05> 0.0)
{
...
}
else
{
//challenge mode
if(C_P_MODE == 0)
{
//challenge mode
if(C_P_MODE == 0)
{
clearInterval(run);
sound_track.pause();
var variant = 1;
gameover_sound.play();
//게임오버
var top_to_bottom = setInterval
(
function()
{
if(variant == 162)
{
clearInterval(top_to_bottom);
setTimeout(
function()
{
//초기화 작업
notes = [];
locs = [];
cur_judge = undefined;
cur_idx = 0;
cur_status = 2;
iterable = 0;
life_bar_percentage = 0.7;
score = 0;
cool = 0;
good = 0;
bad = 0;
miss = 0;
combo = 0;
max_combo = 0;
for(var k=0; k<5; k++)
{
//lines[k]에 들어있는 배열의 모든 원소를 제거한다.
lines[k].splice(0)
}
context.drawImage(retry,115,490,329,143);
context.drawImage(quit,115,640,329,143);
},
500);
}
//gameover이미지의 가로크기가 555다, 565로 만든줄 알았는데 착각했다.
context.drawImage(gameover,0,810-variant*5,555,variant*5,0,0,565,variant*5);
variant +=1;
},
10);
}
//Practice mode
else
{
...
}
}
subtracting = false;
}
}
}
게임오버가 되었을 때 , Retry를 클릭하면 같은곡을 재도전하게 되고, Quit을 클릭하면 메뉴화면으로 돌아간다.
Menu17.js
...
//canvas안에서 마우스를 움직였을 때 이벤트들이 발생한다.
canvas.addEventListener("mousemove" , function (e)
{
X_loc=e.offsetX;
Y_loc=e.offsetY;
context.canvas.style.cursor = "auto";
...
//context.canvas.style.cursor = "pointer";
//=> 마우스 커서를 화살표가 아닌 손가락 모양으로 바꾼다.
...
//gameover
if(cur_status == 2)
{
if(115 <= X_loc
&& X_loc <=444)
{
//retry
if(490 <= Y_loc
&& Y_loc <=633)
{
context.save();
context.fillStyle = "black"
context.fillRect(115,490,329,143);
context.restore();
context.drawImage(retry,120,495,319,133);
context.canvas.style.cursor = "pointer";
prev_button = 5;
}
//quit
else if(640 <= Y_loc
&& Y_loc <=783)
{
context.save();
context.fillStyle = "black"
context.fillRect(115,640,329,143);
context.restore();
context.drawImage(quit,120,645,319,133);
context.canvas.style.cursor = "pointer";
prev_button = 6;
}
}
}
...
});
...
//캔버스에서 마우스 누르는것 감지
canvas.addEventListener('mousedown', function(e)
{
X_loc=e.offsetX;
Y_loc=e.offsetY;
...
//gameover
if(cur_status == 2)
{
if(115 <= X_loc
&& X_loc <=444)
{
//retry
if(490 <= Y_loc
&& Y_loc <=633)
{
//console.log("retry 실행");
context.drawImage(retry,115,490,329,143);
module_path = './songs_info/'+ song_list[cur_num+(cur_page-1)*8] +'.js';
cur_status = 1;
playing();
}
//quit
else if(640 <= Y_loc
&& Y_loc <=783)
{
//console.log("quit 실행");
context.drawImage(quit,115,640,329,143);
cur_status = 0;
total_notes_number = 0;
click_sound.play();
showing_menu(false);
}
}
}
...
});
곡을 끝까지 연주하면 결과화면이 나온다(별로 표시된 부분이 실행된 결과). 그리고 게임결과가 오라클에 기록된다.
Menu17.js
...
function playing()
{
...
iterable = 0;
sound_track.play();
run = setInterval
(
function()
{
//노트를 넣어준다.
//63/5 == 126(맨 위에서 판정바까지 노트가 내려가는데 걸리는 프레임)
//1000/16 == 62.5(초당 프레임수)
//126/62.5 == 악 2초(맨 위에서 판정바까지 노트가 내려가는데 걸리는 시간)
try
{
temp = Math.round(notes[cur_idx][0] / 16 - 126);
...
}
//에러가 발생한다면, 노래가 끝났다는 의미이므로, 인터벌 및 타임아웃 함수를 중지한다.
catch(e)
{
if(sound_track.ended)
{
miss = total_notes_number -(cool + good + bad);
★ showing_result();
clearInterval(run);
}
else
{
effect_drawer();
}
}
iterable +=1;
},
16);
}
Menu17.js
...
function showing_result()
{
if(C_P_MODE == 0)
{
//결과를 오라클에 집어넣는다.
//rythm3.jsp에서 <script type="text/javascript"
//src="https://code.jquery.com/jquery-3.6.0.min.js"></script>라
//코딩했기에 현재의 페이지에서 바로 아작스 사용이 가능하다.
$.ajax
({
type: 'POST',
url: './Score_record',
data:
{
id : id,
gname : 'rhythm king',
gname_n : song_name,
score : score,
mod : 0,
},
success: function()
{
},
error: function()
{
alert('요청실패');
}
});
}
...
}
Score_record.java
...
package games;
...
@WebServlet("/Score_record")
public class Score_record extends HttpServlet
{
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
actionDo(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
actionDo(request, response);
}
private void actionDo(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
HttpSession session = request.getSession();
String id = (request.getParameter("id") != null)
? request.getParameter("id") : "";
String gname = (request.getParameter("gname") != null)
? request.getParameter("gname") : "";
String gname_n = (request.getParameter("gname_n") != null)
? request.getParameter("gname_n") : "";
String score_t = (request.getParameter("score") != null)
? request.getParameter("score") : "0";
int score = Integer.parseInt(score_t);
int mod = Integer.parseInt(request.getParameter("mod"));
int current_page = (request.getParameter("current_page") == null)?
1:Integer.parseInt(request.getParameter("current_page"));
RankingList_S list;
//새로새운 기록 올리기
if(mod == 0)
{
new Score_record_DAO().score_record_renewal(gname,gname_n,id,score);
}
else
{
...
}
session.setAttribute("GNAME_S", gname);
}
}
Score_record_DAO.java
...
package games;
...
public class Score_record_DAO
{
private Connection conn = null;
private PreparedStatement pstmt = null;
private ResultSet rs = null;
static String sql;
static int SCORE;
static int hour;
static int min;
static int sec;
static int id_rank;
static RankingList_S result_S;
public Score_record_DAO()
{
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@localhost:1521:xe";
conn = DriverManager.getConnection(url,"koreait","1011");
}
catch (Exception e)
{
e.printStackTrace();
}
}
//String GNAME, String GNAME_N, String ID, int SCORE
public int score_record_renewal(String GNAME, String GNAME_N, String ID, int SCORE)
{
Timestamp timestamp;
try
{
String sq1 = "select SCORE from SCORERECORD where ID=? and GNAME=? and GNAME_N=?";
pstmt = conn.prepareStatement(sq1);
pstmt.setString(1, ID);
pstmt.setString(2, GNAME);
pstmt.setString(3, GNAME_N);
rs = pstmt.executeQuery();
//이전의 게임기록이 존재하는 경우
if(rs.next())
{
int prev_SCORE_record = rs.getInt("SCORE");
//현재의 기록이 이전의 것보다 더 좋은경우
if(prev_SCORE_record < SCORE)
{
timestamp = new Timestamp(System.currentTimeMillis());
/*
System.out.println("timestamp =" + timestamp);
System.out.println("SCORE =" + SCORE);
System.out.println("GNAME =" + GNAME);
System.out.println("ID =" + ID);
System.out.println("GNAME_N =" + GNAME_N);
*/
String sql2 = "update SCORERECORD set SCORE=?, WRITEDATE=? where ID=?
and GNAME=? and GNAME_N=?";
pstmt = conn.prepareStatement(sql2);
pstmt.setInt(1, SCORE);
pstmt.setTimestamp(2,timestamp);
pstmt.setString(3, ID);
pstmt.setString(4, GNAME);
pstmt.setString(5, GNAME_N);
pstmt.executeUpdate();
}
}
//이전의 게임기록이 없는경우
else
{
String sql3 = "insert into SCORERECORD(ID,GNAME,SCORE,GNAME_N) values(?,?,?,?)";
pstmt = conn.prepareStatement(sql3);
pstmt.setString(1, ID);
pstmt.setString(2, GNAME);
pstmt.setInt(3, SCORE);
pstmt.setString(4, GNAME_N);
pstmt.executeUpdate();
}
conn.close();
pstmt.close();
rs.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
return 0;
}
...
}
Menu17.js
...
function showing_result()
{
if(C_P_MODE == 0)
{
...
}
offscreencontext.drawImage(result,0,0,565,810);
offscreencontext.drawImage(pic,160,100,240,215);
offscreencontext.save();
offscreencontext.fillStyle='rgba(255,255,255,1)';
offscreencontext.font = "normal 25px Elephant";
//id
offscreencontext.fillText(id,220,415);
//cool
offscreencontext.fillText(cool,205,485);
//good
offscreencontext.fillText(good,440,485);
//bad
offscreencontext.fillText(bad,205,535);
//miss
offscreencontext.fillText(miss,440,535);
//maxcombo
offscreencontext.fillText(max_combo,305,615);
//Score
offscreencontext.fillText(score,200,667);
offscreencontext.restore();
var variant = 1;
result_sound.play();
var top_to_bottom = setInterval
( function()
{
//context.drawImage(gameover,0,ini,565,810);
if(variant == 162)
{
clearInterval(top_to_bottom);
//초기화 작업
//notes = [];
//locs = [];
setTimeout
(
function()
{
for(var k=0; k<5; k++)
{
//lines[k]에 들어있는 배열의 모든 원소를 제거한다.
lines[k].splice(0)
}
cur_judge = undefined;
cur_idx = 0;
cur_status = 3;
iterable = 0;
life_bar_percentage = 0.7;
score = 0;
cool = 0;
good = 0;
bad = 0;
miss = 0;
combo = 0;
max_combo = 0;
context.drawImage(retry,20,720,260,75);
context.drawImage(quit,280,720,260,75);
},
1500);
}
context.drawImage(offscreencanvas,0,810-variant*5,565,variant*5,0,0,565,variant*5);
variant +=1;
},
10);
}
게임오버 화면에서와 마찬가지로 , Retry를 클릭하면 같은곡을 재도전하게 되고, Quit을 클릭하면 메뉴화면으로 돌아간다.
Menu17.js
...
//canvas안에서 마우스를 움직였을 때 이벤트들이 발생한다.
canvas.addEventListener("mousemove" , function (e)
{
X_loc=e.offsetX;
Y_loc=e.offsetY;
...
//context.canvas.style.cursor = "pointer";
//=> 마우스 커서를 화살표가 아닌 손가락 모양으로 바꾼다.
...
//result
if(cur_status == 3)
{
if(720 <= Y_loc
&& Y_loc <=795)
{
//retry
if(20 <= X_loc
&& X_loc <=280)
{
context.save();
context.fillStyle = "black"
context.fillRect(20,720,260,75);
context.restore();
context.drawImage(retry,25,725,250,65);
context.canvas.style.cursor = "pointer";
prev_button = 5;
}
//quit
else if(280 <= X_loc
&& X_loc <=540)
{
context.save();
context.fillStyle = "black"
context.fillRect(280,720,260,75);
context.restore();
context.drawImage(quit,285,725,250,65);
context.canvas.style.cursor = "pointer";
prev_button = 6;
}
}
}
});
...
//캔버스에서 마우스 누르는것 감지
canvas.addEventListener('mousedown', function(e)
{
X_loc=e.offsetX;
Y_loc=e.offsetY;
...
//result
if(cur_status == 3)
{
if(720 <= Y_loc
&& Y_loc <=795)
{
//retry
if(20 <= X_loc
&& X_loc <=280)
{
result_sound.pause();
result_sound.load();
context.drawImage(retry,25,725,250,65);
module_path = './songs_info/'+ song_list[cur_num+(cur_page-1)*8] +'.js';
cur_status = 1;
playing();
}
//quit
else if(280 <= X_loc
&& X_loc <=540)
{
result_sound.pause();
result_sound.load();
context.drawImage(quit,285,725,250,65);
cur_status = 0;
total_notes_number = 0;
click_sound.play();
showing_menu(false);
}
}
}
});
동작 영상
※전체 코드는 https://github.com/qlqlzh/Storage에서 my_project_mybatis.zip과 my_project_mybatis.z01를 다운받은 후 my_project_mybatis.zip의 압축을 풀으면 볼 수 있다.
댓글남기기