Javascript - 탈출게임(2)

정종찬·2022년 4월 26일
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
const statuscanvas = document.getElementById('statusCanvas');
const ctx = statuscanvas.getContext('2d');


document.addEventListener('keydown', keyPush);

let gameState = 0; // 0 플레이 가능 / 1 전투모드 / 2 클리어
const fieldWidth = 40;
const fieldHeight = 40;
const fieldColumn = 10;
const fieldRow = 10;
const fields = [];
const charRadius = 20;
let charPosX = charRadius;
let charPosY = canvas.height - charRadius;
let charMoveDirX = -1;
let charMoveDirY = -1;
let charMoveSpd = 2;

let fieldColor = ["lightpink", "lightsalmon", "gold", "lightgreen", "mediumslateblue", "powderblue", "violet"];
// class Field {
//     constructor(left, top, right, bottom) {
//         this.left = left; 
//         this.top = top;
//         this.right = right;
//         this.bottom = bottom;

//     }
// }
class Field {
    constructor(left, top, right, bottom, column, row, color, type, ismob) {
        this.left = left; 
        this.top = top;
        this.right = right;
        this.bottom = bottom;
        this.column = column;
        this.row = row;
        this.color = color;
        this.type = type;
        this.ismob = ismob;
    }
    draw() {
        context.beginPath();
        context.rect(this.left, this.top, fieldWidth, fieldHeight);
        context.fillStyle = fieldColor[this.color%7];
        context.fill();
        context.closePath();
    }
}

let mobname = ["slime", "goblin", "kobold", "orc", "golem", "spider", "gnoll"]

class Mob {
    constructor(left, top, right, bottom, ismob, money, name) {
        this.left = left; 
        this.top = top;
        this.right = right;
        this.bottom = bottom;
        this.ismob = ismob;
        this.money = money;
        this.name = name;
    }
}

class Exit extends Field {
}
let exit = new Exit(
    380 - (fieldWidth) / 2 ,        // left 380 - 20    360
    20 - (fieldHeight) / 2,          // top 20 - 20     0
    380 + (fieldWidth) / 2,        // right 380 + 20    400
    20 + (fieldHeight) / 2,       // bottom 20 + 20     40
    1,                        
    1,
)

class Shop extends Field {
}
let shop = new Shop(
    220 - (fieldWidth) / 2 ,        // left 380 - 20    360
    220 - (fieldHeight) / 2,          // top 20 - 20     0
    380 + (fieldWidth) / 2,        // right 380 + 20    400
    20 + (fieldHeight) / 2,       // bottom 20 + 20     40
    1,                        
    1,
)

class Char extends Field{
    constructor(left, top, right, bottom, hp, money) {
        super(left, top, right, bottom)
        this.hp = hp;
        this.money = money;
    }
        drawChar()
        {
            let img_char = new Image();
            img_char.src = "char.png";
            img_char.onload = function() 
            {
                        context.drawImage(img_char, char.left, char.top, fieldWidth, fieldHeight)
            } 
            
        }
}
let char = new Char(
    charPosX-20,       // left: ;           20 -20
    charPosY-20,       // top: ;            400 - 20 - 20
    charPosX+20,       // right             20 + 20
    charPosY+20,       // bottom            400 - 20 + 20 
    100,               // hp
    0,                 // money
)



function keyPush(evt){
    switch(evt.keyCode) {
        case 37: // 왼쪽
            if(charPosX - 20 > 0) {
                charPosX += -40;
                charPosY += 0;
                char.left = charPosX - charRadius;
                char.right = charPosX + charRadius ;
            }
            break;
        case 38: // 윗키
            if(charPosY - 20 > 0) {
                charPosX += 0;
                charPosY += -40;
                char.top = charPosY - charRadius;
                char.bottom = charPosY + charRadius;
            }            
            break;
        case 39: // 오른쪽
            if(charPosX + 20 < canvas.width){
                charPosX += 40;
                charPosY += 0;
                char.left = charPosX - charRadius;
                char.right = charPosX + charRadius;
            }            
            break;
        case 40: // 아래
            if(charPosY + 20 < canvas.height){
                charPosX += 0;
                charPosY += 40;
                char.top = charPosY - charRadius;
                char.bottom = charPosY + charRadius;
            }            
            break;
        default:
            break;
    }
}

function charStat()
{
    ctx.font = '18px serif'
    ctx.clearRect(0, 0, statuscanvas.width, statuscanvas.height);
    ctx.fillText("HP", 10, 30)
    ctx.fillText(`${char.hp}`, 10, 55)
    ctx.fillText("CowNow", 10, 150)
    ctx.fillText(`${char.money}`, 10, 125)
}

function draw()
{
    //  // 화면 클리어    
     context.clearRect(0, 0, canvas.width-40, canvas.height);
     context.clearRect(360, 40, 40, canvas.height-40);
    //  drawCanavs(); 
     // 그리기     
     drawFields();
     char.drawChar();
     charStat();
}

function drawExit(){
    let img_exit = new Image();
    img_exit.src = "exit.png";
    img_exit.onload = function() 
    {
        context.drawImage(img_exit, exit.left, exit.top, fieldWidth, fieldHeight)
    } 
}

function drawShop(){
    let img_shop = new Image();
    img_shop.src = "shop.png";
    img_shop.onload = function() 
    {
        context.drawImage(img_shop, shop.left, shop.top, fieldWidth, fieldHeight)
    } 
}

function drawFields()
{   
    // let gaming = false;    
    for(let i = 0; i < fieldRow; i++) 
    { 
        for(let j = 0; j < fieldColumn; j++) 
        {              
            if((i != 9 || j != 0) && (i != 5 || j != 5)) fields[i][j].draw(); 
                    
            // if(fields[i][j].charstat > 0) gaming = true;
        }
    }
    // if(!gaming){
    //    /*  clear(); */
    // }
}

function update()
{    
    meetMob();
    // 충돌확인 어떤경우에도 안부딪히는 상황    
    if(isCollisionExitToChar(char, exit)) // right left top bottom 
    {   
        console.log("충돌!")
        gameState = 2; 
        setTimeout(() => {
        window.location.reload();
        alert("clear")
        }, 300);
        
    }

    if(isCollisionShopToChar(char, shop)) // right left top bottom 
    {   
        console.log("상점")
        gameState = 2; 
        setTimeout(() => {
        alert("회복완료")
        }, 300);
        
    }
}

function isCollisionExitToChar(charA, exitB)
{
    // a의 완쪽과 b의 오른쪽
    // a의 오른쪽과 b의 왼쪽
    // a의 아래쪽과 b의 위쪽
    // a의 위쪽과 b의 아래쪽
    if (charA.left >= exitB.right ||     // a의 왼쪽이 더 클때
        charA.right <= exitB.left ||     // b의 왼쪽이 더 클때
        charA.top >= exitB.bottom ||     // a의 탑이 더 클때
        charA.bottom <= exitB.top )      // b의 탑이 더 클때
        {
            return false;
        }
    return true;
}
function isCollisionShopToChar(charA, shopB)
{
    // a의 완쪽과 b의 오른쪽
    // a의 오른쪽과 b의 왼쪽
    // a의 아래쪽과 b의 위쪽
    // a의 위쪽과 b의 아래쪽
    if (charA.left >= shopB.right ||     // a의 왼쪽이 더 클때
        charA.right <= shopB.left ||     // b의 왼쪽이 더 클때
        charA.top >= shopB.bottom ||     // a의 탑이 더 클때
        charA.bottom <= shopB.top )      // b의 탑이 더 클때
        {
            return false;
        }
    return true;
}

function setFields()
{       
    for(let i = 0; i < fieldRow; i++) 
    { 
        fields[i] = [];
        for(let j = 0; j < fieldColumn; j++) 
        {              
            fields[i][j] = new Field(
                0 + i * 40,
                0 + j * 40,
                0 + (1+i) * 40,
                0 + (1+j) * 40,
                i,
                j,
                Math.floor((Math.random()*7)+1),
                Math.round(Math.random()),
                Math.round(Math.random())               
            )     
            // console.log(fields[i][j].ismob)       
        }
    }
}
setFields();

let mobs = [];
function setMob() {    
    for(let i = 0; i < fieldRow; i++) 
    { 
        for(let j = 0; j < fieldColumn; j++) 
        {   
            // console.log(fields[i][j].ismob)
            if(fields[i][j].ismob == 1 && (i != 0 && j !=0)) {
                mobs.push(new Mob(
                    fields[i][j].left, 
                    fields[i][j].top,
                    fields[i][j].right, 
                    fields[i][j].bottom,
                    true,
                    Math.floor(Math.random()*100), 
                    mobname[fields[i][j].color%7]))
                console.log(fields[i][j].ismob)
            } 
        }
    }
    
}

setMob();
function meetMob() {
    for(let i = 0; i < mobs.length; i++) {
        // console.log(mobs[i])
        if(mobs[i].left == char.left 
            && mobs[i].top == char.top
/*             && mobs[i].right == char.right
            && mobs[i].bottom == char.bottom */
            && mobs[i].ismob == true
            ) {
            console.log(mobs[i].ismob)
            console.log("몹 만남");
            console.log(mobs[i].name);
            gameState = 1;       
            alert(`${mobs[i].name} 과 전투!!`)            
        }
    }
}

function setExit()
{
    for(let i = 0; i < fieldRow; i++) 
    { 
        exit[i] = [];
        for(let j = 0; j < fieldColumn; j++) 
        {              
            exit[i][j] = new Exit(
                0 + i * 30,
                0 + j * 30,
                0 + (1+i) * 30,
                0 + (1+j) * 30,
                i,
                j,                
            )            
        }
    }
}

function setShop()
{
    for(let i = 0; i < fieldRow; i++) 
    { 
        shop[i] = [];
        for(let j = 0; j < fieldColumn; j++) 
        {              
            shop[i][j] = new Shop(
                0 + i * 30,
                0 + j * 30,
                0 + (1+i) * 30,
                0 + (1+j) * 30,
                i,
                j,                
            )            
        }
    }
}

function battle(i)
{
    let mobVal = Math.round(Math.random()*2)
    let charVal = i     
 
    if(((charVal - mobVal)+3)%3 == 0){
        alert("비김")
        for(let i = 0; i < mobs.length; i++)
        {
            if(char.left == mobs[i].left && char.top == mobs[i].top){
                mobs[i].ismob = false;
            }
        }
    } else if(((charVal - mobVal)+3)%3 == 1){
        alert("이김")
        for(let i = 0; i < mobs.length; i++)
        {
            if(char.left == mobs[i].left && char.top == mobs[i].top){
                char.money += mobs[i].money;
                mobs[i].ismob = false;
            }
        }
    }else { 
        alert("짐")

        for(let i = 0; i < mobs.length; i++)
        {
            if(char.left == mobs[i].left && char.top == mobs[i].top){
                mobs[i].ismob = false;
                char.hp--;
            }
        }
    }
    gameState = 0; 
}
// 몹이 012 중 값 하나를 낸다 // 내 값을 낸다 012  
// 두 값의 빼서 기영+1(양수) 하고 %3 한다 0무승부 , 1이김 , 2짐 
// 결과 이기면 골드 + , 지면 HP -
// 0 1 2 
// 0 1 2


setInterval(()=>{if(gameState==0){draw()}}, 150);
setInterval(()=>{if(gameState==0){update()}}, 30);
setInterval(()=>{if(gameState==0){
    drawShop()}}, 30);
setExit();
drawExit();
setShop();
drawShop();
// setInterval(draw, 150);
// setInterval(update, 30);

1차 버전
1. 맵 제작 - 이차원 배열 10 X 10
2. 맵타일 - 오브젝트 or 클래스
3. 플레이어, 탈출구 - 오브젝트 - 클래스
4. 키 입력 처리 - 상하좌우 1칸씩 이동 가능
5. 플레이어가 탈출구에 도착하면 게임 클리어

2차 버전
1. 몬스터 등장 - 플레이어가 이동할 때 마다 일정 확률로 만날 수 있다.
2. 전투 - 몬스터와 만나면 가위, 바위, 보 버튼이 생기고 가위, 바위, 보 시스템을 사용해서 전투가 벌어진다.
3. 전투 - 이기면 0 ~ 100골드 사이에서 랜덤하게 보상 획득
4. 전투 - 지면 HP 1감소

3차 버전
1. 맵타일 종류 추가 - 땅, 숲, 늪 등등
2. 맵타일 종류에 따라 다른 표현 (색, 모양)
3. 몬스터 종류 추가 - 맵타일 종류에 따라 등장하는 몬스터 변경
4. 상점 추가 - 맵에 상점 1개 존재

5. 상점 기능 - 골드를 소모해서 HP회복

4차 버전
1. 맵 표현 추가(시야 개념)
1-1. 안가본 영역 - 검은색
1-2. 기본 영역 - 회색 (탈출구, 상점 표시)
1-3. 현재 시야 - 밝은 회색 (탈출구, 상점 표시)
2. 전투시 몬스터 프로필 추가 - 몬스터 종류에 따라 이미지 출력

profile
dalssenger

0개의 댓글