본문 바로가기

Around Runner Game - HTML5

Around Run Game

동그란 원에서 장애물을 피하며 살아남기 게임

전체화면으로 게임하기: https://pythonblog.co.kr/example/around_run/

기본설정

<script>
let game;
window.onload = function() {
    let config = {
        type: Phaser.CANVAS,
        backgroundColor: 0x444444,
        scale: {
            mode: Phaser.Scale.FIT,
            autoCenter: Phaser.Scale.CENTER_BOTH,
            parent: "theAroundGame",
            width: 800,
            height: 800
        },
        scene: playGame
    }
    game = new Phaser.Game(config);
    window.focus();
}

class playGame extends Phaser.Scene{
    preload(){}    
    create(){}
    update(){}
}
</script>

게임옵션 정의 및 원 그리기

    ...
    ...
let gameOptions = {    
    bigCircleRadius: 300, 
    bigCircleThickness: 20,// 큰 원의 두께(픽셀)
    playerRadius: 25, // 플레이어의 반지름(픽셀)
    playerSpeed: 0.6, // 플레이어 속도, 프레임당 각도
    worldGravity: 0.8, //중력
    jumpForce: [12, 8] // 싱글, 2단뛰기 점프파워
}
    create(){
        // 원 그리기
        this.bigCircle = this.add.graphics();
        this.bigCircle.lineStyle(gameOptions.bigCircleThickness, 0xffffff);
        this.bigCircle.strokeCircle(game.config.width / 2, game.config.height / 2, gameOptions.bigCircleRadius);        
    }

플레이어 추가

    preload(){
        this.load.image("player", "https://actioncall.github.io/actioncall/assets/img/around_eyes.png");
    }   
    create(){
        ...
        ...
           //플레이어 추가
        this.distanceFromCenter = gameOptions.bigCircleRadius - gameOptions.playerRadius - gameOptions.bigCircleThickness / 2; //원 중앙과의 거리
        this.player = this.add.sprite(game.config.width / 2, game.config.height / 2 - this.distanceFromCenter, "player"); 

        //플레이어 셋팅
        this.player.currentAngle = -90;   // 플레이어 현재 각도
        this.player.previousAngle = this.player.currentAngle; // 플레이어 이전 각도         
        this.player.jumpOffset = 0; // 점프 오프셋, 점프 중 지면과 거리
        this.player.jumps = 0; //점프 횟수                 
        this.player.jumpForce = 0; // 현재 점프포스파워

    }

점프하기

create(){
    ...
    ...
        //마우스 클릭 리스너
         this.input.on("pointerdown", function(){
            // 2회 미만 점프
            if(this.player.jumps < 2){                                 
                this.player.jumps ++;                                
                this.player.jumpForce = gameOptions.jumpForce[this.player.jumps - 1]; //jumpForce: [12, 8] 1회,2회

            }

        }, this);       
    }

update(){
        if(this.player.jumps > 0){
            console.log("this.player.jumpForce : "+this.player.jumpOffset);             
            this.player.jumpOffset += this.player.jumpForce;  //점프오프셋에 점프파워 추가      
            this.player.jumpForce -= gameOptions.worldGravity; //중력을 통해 점프력 감소

            // 점프오프셋이 다 감소하여 0보다 작으면 착지 상태
            if(this.player.jumpOffset < 0){                
                //점프 오프셋, 점프 횟수, 점프 포스 초기화
                this.player.jumpOffset = this.player.jumps = this.player.jumpForce =  0; 
            }
        }

        // 점프 오프셋으로 플레이어 위치 결정 
        let distanceFromCenter = this.distanceFromCenter - this.player.jumpOffset;        

        // 삼각법을 사용하여 플레이어 위치 지정
        let radians = Phaser.Math.DegToRad(this.player.currentAngle); //라디안으로 변환
        this.player.x = game.config.width / 2 + distanceFromCenter * Math.cos(radians);
        this.player.y = game.config.height / 2 + distanceFromCenter * Math.sin(radians);

    }

플레이어 이동 및 회전

update(){ ... ... ...

// 이전 각도를 현재 각도로 업데이트
    this.player.previousAngle = this.player.currentAngle;                   
    // 현재 각도에 플레이어 속도 추가
    this.player.currentAngle = Phaser.Math.Angle.WrapDegrees(this.player.currentAngle + gameOptions.playerSpeed);

    //회전 시키기
    let revolutions = gameOptions.bigCircleRadius / gameOptions.playerRadius + 1;
    this.player.angle = -this.player.currentAngle * revolutions;

}

플레이어 이동 자리 색 칠하기

create(){
    ...        
    ...        
    ...        
        // 그려진 모든 호를 저장할 배열
        this.paintedArcs = [];     
        //지나간 원을 그릴 그래픽
        this.highlightCircle = this.add.graphics();

}
  update(){       
    ...
    ...
    ...

        //점프하지 않을때
        if(this.player.jumpOffset == 0){
            this.paintedRatio = 0;
            //Phaser 각도를 더 읽기 쉬운 각도로 변환합니다. 0:위,90:오른, 180:아래, 270:왼
            let currentAngle = this.getGameAngle(this.player.currentAngle);
            let previousAngle = this.getGameAngle(this.player.previousAngle);


            if(currentAngle >= previousAngle){
                // paintedArcs 배열에 추가
                this.paintedArcs.push([previousAngle, currentAngle]);
            }else{                 
                // 플레이어가 360보다 작은 값에서 360보다 큰 값, 즉 0으로 전달된 경우                
                this.paintedArcs.push([previousAngle, 360]);
                this.paintedArcs.push([0, currentAngle]);
            }

            // 하이라이트 처리 준비
            this.highlightCircle.clear();
            this.highlightCircle.lineStyle(gameOptions.bigCircleThickness, 0xaa00aa);

            // 겹치는 원을 병합
            this.paintedArcs = this.mergeIntervals(this.paintedArcs);

            // 배열에 저장된..
            this.paintedArcs.forEach(function(i){
                // 원 길이에 따라 도장 비율 값 증가
                this.paintedRatio += (i[1] - i[0]);
                console.log("i : "+i+" ---------"+this.paintedRatio);

                // 지나간 자리의 색을 칠한다.
                this.highlightCircle.beginPath();
                this.highlightCircle.arc(game.config.width / 2, game.config.height / 2, gameOptions.bigCircleRadius, Phaser.Math.DegToRad(i[0] - 90), Phaser.Math.DegToRad(i[1] - 90), false);
                this.highlightCircle.strokePath();
            }.bind(this));
        }
    }
    // Phaser 각도 변환
    getGameAngle(angle){
        let gameAngle = angle + 90;
        if(gameAngle < 0){
            gameAngle = 360 + gameAngle
        }
        return gameAngle;
    }
    mergeIntervals(intervals){
        if(intervals.length <= 1){
            return intervals;
        }
        let stack = [];
        let top = null;
        intervals = intervals.sort(function(a, b){
            return a[0] - b[0]
        });
        stack.push(intervals[0]);
        for(let i = 1; i < intervals.length; i++){
            top = stack[stack.length - 1];
            if(top[1] < intervals[i][0]){
                stack.push(intervals[i]);
            }
            else{
                if (top[1] < intervals[i][1]){
                    top[1] = intervals[i][1];
                    stack.pop();
                    stack.push(top);
                }
            }
        }
        return stack;
    }

텍스트 표시 및 재시작

    ...
    ...
create(){
    constructor(){
        super("PlayGame");
    }
    ...
    ...
    ...

      // 플레이어 진행 상황을 표시하는 텍스트
      this.levelText = this.add.text(game.config.width / 2, game.config.height / 2, "", {
          fontFamily: "Arial",
          fontSize: 96,
          color: "#00ff00"
      });
      this.levelText.setOrigin(0.5);
}
update(){
    ... 
    ...
 //점프하지 않을때
        if(this.player.jumpOffset == 0){
           ...
           ...
           ...

            // 모든 원 길이의 합을 0 -> 100의 값으로 변환
            this.paintedRatio = Math.round(this.paintedRatio * 100 / 360);


            // 플레이어 진행률 텍스트 업데이트
            this.levelText.setText(this.paintedRatio + "%");

            // 만약 플레이어가 원 전체를 칠했다면...
            if(this.paintedRatio == 100){                
                // 2초 후에 게임을 다시 시작합니다.
                this.time.addEvent({
                    delay: 2000,
                    callbackScope: this,
                    callback: function(){
                        this.scene.start("PlayGame");
                    }
                });
            }

        }
}

점프시 audio 추가

    preload(){        
        this.load.audio("bong_001", "https://actioncall.github.io/actioncall/assets/audio/bong_001.ogg");
    }   
    create(){
    this.jumpOgg = this.sound.add('bong_001');
    ...
    ...
this.input.on("pointerdown", function(){
            // 2회 미만 점프
            if(this.player.jumps < 2){
                this.jumpOgg.play();           
             ...
        ...      

            }

        }, this);  
    ...
    ...

    }   

캐릭터 변경 및 애니메이션 효과

    preload(){
    ...
    ...
        //this.load.image("player", "https://actioncall.github.io/actioncall/assets/img/around_eyes.png");      
        this.load.spritesheet('player','https://actioncall.github.io/actioncall/assets/img/p_run.png', { frameWidth: 37, frameHeight: 46 });
    } 
create(){
    ...
    ...

        this.anims.create({
            key: 'run',
            frames: this.anims.generateFrameNumbers('player', { start: 0, end: 5 }),
            frameRate: 10,
            repeat: -1
        });
        this.player.anims.play('run');

        this.anims.create({
            key: 'jump',
            frames: this.anims.generateFrameNumbers('player', { start: 6, end: 8 }),
            frameRate: 10,
            repeat: -1
        });
    ...
    ...

         this.input.on("pointerdown", function(){
            // 2회 미만 점프
            if(this.player.jumps < 2){
                this.player.anims.play('jump');
        ...
        ...                
            }

        }, this);  
    ...
    ...

    //현재 각도에 맞춰 캐릭터 회전
        this.player.angle = this.getGameAngle(this.player.currentAngle)-150;

    //회전 시키기(삼각법)
        //let revolutions = gameOptions.bigCircleRadius / gameOptions.playerRadius + 1;
        //this.player.angle = -this.player.currentAngle * revolutions;

}   
  • 기존 플레이어의 회원시키기 기능 주석 처리

배경 변경

 preload(){
    ...
    ...
        this.load.image("bg1", "https://actioncall.github.io/actioncall/assets/img/bg1.png");        
    }   
    create(){
    ...
    ...
        this.add.image(game.config.width / 2, game.config.height / 2,'bg1');

        // 큰 원 그리기
        //this.bigCircle = this.add.graphics();
        //this.bigCircle.lineStyle(gameOptions.bigCircleThickness, 0xffffff);
        //this.bigCircle.strokeCircle(game.config.width / 2, game.config.height / 2, gameOptions.bigCircleRadius); 

    }
  • 기존 원 그리기 부분 주석 처리

적 추가하기

let gameOptions = {    
    ...
    ...

    mobSpeed:0.1,
    mobRadius: 10,
}

    preload(){
    ...
    ...
    this.load.image("mob1", "https://actioncall.github.io/actioncall/assets/img/Rectangle.png"); |
    }   
    create(){
    ...
    ...
    ...


        this.distanceFromCenterMob = gameOptions.bigCircleRadius - gameOptions.mobRadius - gameOptions.bigCircleThickness / 2;
        this.mob1 = this.add.image(game.config.width / 2, game.config.height / 2 + this.distanceFromCenterMob,'mob1');
        this.mob1.currentAngle = -90;
    }
    update(){
    ...
    ...
    ...

        //위치 지정 이동
        this.mob1.currentAngle = Phaser.Math.Angle.WrapDegrees(this.mob1.currentAngle + gameOptions.mobSpeed);                
        let mobXY = this.getXY(this.mob1.currentAngle,this.distanceFromCenterMob);
        this.mob1.y = mobXY.x;
        this.mob1.x = mobXY.y;
        //radians = this.getRadians(this.mob1.currentAngle); //라디안으로 변환
        //this.mob1.y = game.config.width / 2 + mobDist * Math.cos(radians);
        //this.mob1.x = game.config.height / 2 + mobDist * Math.sin(radians);     

    }
    getXY(currentAngle,dist){
        let radians = this.getRadians(currentAngle); //라디안으로 변환        
        return {x: game.config.width / 2 + dist * Math.cos(radians),y:game.config.height / 2 + dist * Math.sin(radians)}
    }
    ...
    ...
    ...
  • 기존 이동 기능 getXY 함수로 변경

적과 충돌 감지

let gameState = {
    state:'run'
}
class playGame extends Phaser.Scene{
    create(){
    ...
    ...

     //this.player = this.add.sprite(game.config.width / 2, game.config.height / 2 - this.distanceFromCenter, "player"); 
     this.player = this.physics.add.sprite(game.config.width / 2, game.config.height / 2 - this.distanceFromCenter, "player"); 
    ...
    ...
    //this.mob1 = this.add.image(game.config.width / 2, game.config.height / 2 + this.distanceFromCenterMob,'mob1');
        this.mob1 = this.physics.add.image(game.config.width / 2, game.config.height / 2 +  this.distanceFromCenterMob,'mob1');

    this.physics.add.overlap(this.player, this.mob1, this.playerOverlap, null, this);
    ...
    ...
    }   

    playerOverlap(player, mob){
            if(gameState.state != 'death'){
                this.deathOgg.play({volume:0.3});
            }
            gameState.state = 'death';        
      }
}

시작버튼 추가하기

      ...
      ...

let orignalGameOptions = gameOptions;

class playGame extends Phaser.Scene{
    constructor(){
        super("PlayGame");
    }

    preload(){
      ...
      ...

        this.load.image("startBtn", "https://actioncall.github.io/actioncall/assets/img/start_btn.png"); 



    }  
    create(){
        this.startBtn = this.add.sprite(100, 100, 'startBtn').setInteractive();
        this.startBtn.on('pointerdown', function () { this.startGame(); },this ); // Start game on click
        ...
        ...
    }
    startGame(){
        stage=1;
        gameOptions = orignalGameOptions;
        this.scene.start("PlayGame") 
    }

전체 소스

let game;
let stage = 1;
let score = 0;
let mob_cnt = 0;
let GAMESTATE = { state:'run'}
let BUFF = {speed:1,speedTime:0, jumpPower:1,jumpPowerTime:0,recovery:0,recoveryTime:0}

let gameOptions = { 
    gameVolume: 0.3,  
    bigCircleRadius: 300, //원의 반지름
    bigCircleThickness: 20,//원의 두께(픽셀)
    playerRadius: 18, // 플레이어의 반지름(픽셀)
    playerSpeed: 120, // 플레이어 속도, 프레임당 각도
    playerHp:100,
    playerRecovery:0,
    worldGravity: 0.8, //중력
    jumpForce: [12, 7], // 싱글, 2단뛰기 점프파워

    mob:{atk:10,speed:60,radius:20},    
    mobSpeed:50,
    mobRadius: 20,
}

window.onload = function() {
    let config = {
        type: Phaser.CANVAS,
        backgroundColor: 0x000000,
        scale: {
            mode: Phaser.Scale.FIT,
            autoCenter: Phaser.Scale.CENTER_BOTH,
            parent: "theGame",
            width: 800,
            height: 800
        },
        physics: {default: 'arcade',
              arcade: {gravity: { y: 0 },
                       debug: false}
        },
        scene: playGame
    }
    game = new Phaser.Game(config);
    window.focus();
}


class playGame extends Phaser.Scene{
    constructor(){ 
        super("PlayGame");        
    }
    preload(){
        //this.load.image("player", "https://actioncall.github.io/actioncall/assets/img/around_eyes.png");
        this.load.audio("bong_001", "https://actioncall.github.io/actioncall/assets/audio/bong_001.ogg");
        this.load.audio('death', 'https://actioncall.github.io/actioncall/assets/audio/death.ogg');
        this.load.audio('gameOverAudio', 'https://actioncall.github.io/actioncall/assets/audio/GameOver.mp3');
        this.load.audio('forest', 'https://actioncall.github.io/actioncall/assets/audio/forest.mp3');
        this.load.audio('powerUp1', 'https://actioncall.github.io/actioncall/assets/audio/powerUp11.ogg');

        this.load.spritesheet('player','https://actioncall.github.io/actioncall/assets/img/player_run.png', { frameWidth: 37, frameHeight: 46 });        
        //this.load.image("particle1", "https://actioncall.github.io/actioncall/assets/img/blue.png");
        this.load.image("bg1", "https://actioncall.github.io/actioncall/assets/img/bg1.png"); 
        this.load.image("mob1", "https://actioncall.github.io/actioncall/assets/img/Rectangle.png"); 
        this.load.image("mob2", "https://actioncall.github.io/actioncall/assets/img/Triangle.png"); 

        this.load.image("startBtn", "https://actioncall.github.io/actioncall/assets/img/start_btn.png"); 

        //item
        this.load.image("potionRoundIndigo", "https://actioncall.github.io/actioncall/assets/img/PotionRoundIndigo.png"); 
        this.load.image("potionRoundPink", "https://actioncall.github.io/actioncall/assets/img/PotionRoundPink.png"); 
        this.load.image("potionRoundPurple", "https://actioncall.github.io/actioncall/assets/img/PotionRoundPurple.png"); 
        this.load.image("potionRoundRed", "https://actioncall.github.io/actioncall/assets/img/PotionRoundRed.png"); 
        this.potionNames = ['potionRoundIndigo','potionRoundPink','potionRoundPurple','potionRoundRed'];

    }   
    startGame(){
        stage = 1;       
        score = 0;       
        mob_cnt = 0;          
        gameOptions.playerSpeed = 120;    
        gameOptions.jumpForce[0]= 12;
        gameOptions.playerRecovery=0;
        //this.player.hp=0;                
        this.scene.start("PlayGame")         
    }
    create(){

        GAMESTATE.state="run";
        // 그려진 모든 원를 저장할 배열
        this.paintedArcs = []; 
        this.mobs = []; 
        this.potion = [];
        this.init_audio();
        this.init_btn();

        this.add.image(game.config.width / 2, game.config.height / 2,'bg1');

        //플레이어 추가
        this.distanceFromCenter = gameOptions.bigCircleRadius - gameOptions.playerRadius - gameOptions.bigCircleThickness / 2; //원 중앙과의 거리        
        this.player = this.physics.add.sprite(game.config.width / 2, game.config.height / 2 - this.distanceFromCenter, "player"); 

        this.anims.create({
            key: 'run',
            frames: this.anims.generateFrameNumbers('player', { start: 0, end: 5 }),
            frameRate: 10,
            repeat: -1
        });
        this.player.anims.play('run');
        this.anims.create({
            key: 'jump',
            frames: this.anims.generateFrameNumbers('player', { start: 6, end: 8 }),
            frameRate: 10,
            repeat: -1
        });

        //플레이어 셋팅   
        this.init_player();     

        this.input.on("pointerdown", function(){this.jumping();}, this);  
        this.input.keyboard.on('keydown-' + 'UP', function () { this.jumping();}, this);      

        this.init_display();

        //포션 생성
        this.createPotion();

        if(stage == 1 || stage % 2 == 0){mob_cnt+=1;}

        for(var i=0;i<mob_cnt;i++){
            let mob            = this.createMob(this.getMinus_or_Plus()==-1?'mob1':'mob2');
            mob.currentAngle   = (this.player.previousAngle*-1)-this.getRandom(1,180);        
            mob.speed          = this.getRandom(5, gameOptions.mob.speed);
            mob.atk            = this.getRandom(5, gameOptions.mob.atk); 
            mob.radius         = this.getRandom(5, gameOptions.mob.radius);
            this.mobs.push(mob);

            this.physics.add.overlap(this.player, mob, this.playerOverlap, null, this);       
        }        


        //지나간 원을 샐칠할 객체
        this.highlightCircle = this.add.graphics();
        if(stage > 1){
            this.playerStatUp();
        }
    }
    createPotion(){
        for(var i=0;i<this.potionNames.length;i++){
            let potion = this.physics.add.sprite(game.config.width / 2, game.config.height / 2 - this.distanceFromCenter, this.potionNames[i]); 
            potion.currentAngle = this.getRandom(Math.abs(this.player.currentAngle),360);

            if(this.getRandom(1,100) > 22){
                potion.disableBody(true, true);
            }
            potion.setScale(0.1).refreshBody();        
            let potionXy = this.getXY(potion.currentAngle, this.distanceFromCenter)
            potion.x = potionXy.x;
            potion.y = potionXy.y;
            potion.angle = this.getGameAngle(potion.currentAngle)-180;
            potion.name = this.potionNames[i]


            this.physics.add.overlap(this.player, potion, this.potionProcess, null, this);

        }
    }
    potionProcess(player, potion){
        let potionName = potion.name;        
        let objText;
        let colorText;
        potion.disableBody(true, true);
        if("potionRoundIndigo" == potionName){
            BUFF.jumpPower = 1.2;
            BUFF.jumpPowerTime = 500;
            objText = "JumpPower X "+BUFF.jumpPower;
            colorText ="#6610f2";
        }else if("potionRoundPink" == potionName){
            BUFF.speed = 2;
            BUFF.speedTime = 300;
            objText = "SpeedUP X "+BUFF.speed;
            colorText ="#e83e8c";
        }else if("potionRoundPurple" == potionName){            
            BUFF.recovery = 2;
            BUFF.recoveryTime = 1000;
            objText = "recovery X "+BUFF.recovery;
            colorText ="#6610f2";
        }else if("potionRoundRed" == potionName){
            player.hp+=200;
            objText = "HP +500";
            colorText ="#e71a11"; 
        }        
        this.noticeTween(objText, game.config.width / 2, game.config.height / 2+50, {fontFamily: "Arial",fontSize: 30,color:colorText })
        this.setDisplay();
        if(player.hp <= 0){
            GAMESTATE.state = 'death'
            this.gameOver();
        }
    }
    createMob(obj_name){
        this.distanceFromCenterMob = gameOptions.bigCircleRadius - gameOptions.mobRadius - gameOptions.bigCircleThickness / 2;
        return  this.physics.add.image(game.config.width / 2, game.config.height / 2 + this.distanceFromCenterMob,obj_name);
    }





    update(){           
        if(GAMESTATE.state != 'death'){
            if(this.player.jumps > 0) GAMESTATE.state = 'jump'; 
            if(GAMESTATE.state == 'jump') this.jump();
            if(GAMESTATE.state == 'run') this.run();
            this.move();

            this.player.hp= this.player.hp+1+gameOptions.playerRecovery+BUFF.recovery;
            score+=1;
            this.player.timer+=1;

            this.setDisplay();    
        }
        if(BUFF.speedTime > 0){ 
            BUFF.speedTime -= 1;       
        }else{
            BUFF.speed = 1;        
        } 
        if(BUFF.jumpPowerTime > 0){ 
            BUFF.jumpPowerTime -= 1;       
        }else{
            BUFF.jumpPower = 1;        
        } 
        if(BUFF.recoveryTime > 0){ 
            BUFF.recoveryTime -= 1;       
        }else{
            BUFF.recovery = 0;        
        } 


        //if(BUFF.speedTime

    }

    jump(){
        this.player.jumpOffset += this.player.jumpForce;  //점프오프셋에 점프파워 추가      
        this.player.jumpForce -= gameOptions.worldGravity; //중력을 통해 점프력 감소

        // 점프오프셋이 다 감소하여 0보다 작으면 착지 상태
        if(this.player.jumpOffset < 0){                
            //점프 오프셋, 점프 횟수, 점프 포스 초기화
            this.player.jumpOffset = this.player.jumps = this.player.jumpForce =  0;
            this.player.anims.play('run');    
            GAMESTATE.state = 'run';              
        }
    }

    run(){               
        this.paintedRatio = 0;
        //Phaser 각도를 더 읽기 쉬운 각도로 변환합니다. 0:위,90:오른, 180:아래, 270:왼
        let currentAngle = this.getGameAngle(this.player.currentAngle);
        let previousAngle = this.getGameAngle(this.player.previousAngle);


        if(currentAngle >= previousAngle){
            // paintedArcs 배열에 추가
            this.paintedArcs.push([previousAngle, currentAngle]);
        }else{                 
            // 플레이어가 360보다 작은 값에서 360보다 큰 값, 즉 0으로 전달된 경우                
            this.paintedArcs.push([previousAngle, 360]);
            this.paintedArcs.push([0, currentAngle]);
        }

        // 색칠할 준비
        this.highlightCircle.clear();
        this.highlightCircle.lineStyle(gameOptions.bigCircleThickness, 0xffffff);

        // 겹치는 원을 병합
        this.paintedArcs = this.mergeIntervals(this.paintedArcs);

        // 배열에 저장된..
        this.paintedArcs.forEach(function(i){
            // 원 길이에 따라 도장 비율 값 증가
            this.paintedRatio += (i[1] - i[0]);                

            // 지나간 자리의 색을 칠한다.
            this.highlightCircle.beginPath();
            this.highlightCircle.arc(game.config.width / 2, game.config.height / 2, gameOptions.bigCircleRadius, Phaser.Math.DegToRad(i[0] - 90), Phaser.Math.DegToRad(i[1] - 90), false);
            this.highlightCircle.strokePath();
        }.bind(this));

        // 모든 원 길이의 합을 0 -> 100 값으로 변환
        this.paintedRatio = Math.round(this.paintedRatio * 100 / 360);

        // 플레이어 진행률 텍스트 업데이트
        //this.levelText.setText(this.paintedRatio + "%");

        // 만약 플레이어가 원 전체를 칠함.
        if(this.paintedRatio == 100){          
           this.success();

        }
    }
    move(){
        // 이전 각도를 현재 각도로 업데이트
        this.player.previousAngle = this.player.currentAngle;

        // 현재 각도에 플레이어 속도 추가        

        this.player.currentAngle = Phaser.Math.Angle.WrapDegrees(this.player.currentAngle + ((this.player.speed*BUFF.speed)*0.01));

        // 점프 오프셋으로 플레이어 위치 결정 
        let distanceFromCenter = this.distanceFromCenter - this.player.jumpOffset;

        // 삼각법을 사용하여 플레이어 위치 지정
        let playerXY = this.getXY(this.player.currentAngle, distanceFromCenter)
        this.player.x = playerXY.x;
        this.player.y = playerXY.y;

        //현재 각도에 맞춰 캐릭터 회전
        this.player.angle = this.getGameAngle(this.player.currentAngle)-150;



        this.moveMob();

    }
    moveMob(){        
        this.mobs.forEach(function(mob){            
            mob.currentAngle = Phaser.Math.Angle.WrapDegrees(mob.currentAngle + (mob.speed*0.01));        
            let mobXY = this.getXY(mob.currentAngle, this.distanceFromCenterMob);
            mob.y = mobXY.x;
            mob.x = mobXY.y;

            mob.angle= mob.currentAngle * (gameOptions.bigCircleRadius / mob.radius + 1)*mob.speed*0.01;                        
        },this);


    }
    success(){
        stage +=1;
        if(this.player.timer < 600){
            score =score+(this.player.timer*3);
        }
        this.scene.start("PlayGame");
        //this.time.addEvent({delay: 300,callbackScope: this,callback: function(){this.playerStatUp();}});

    }
    gameOver(){        
        // this.musicAudio.stop();
        this.gameOverAudio.play({volume:gameOptions.gameVolume});

        GAMESTATE.state = 'death';                
        this.player.anims.stop();   

        var gameOverText = this.add.text(game.config.width / 2, game.config.height / 2,"GAME OVER",{fontFamily: "Arial",fontSize: 60,color: "#3a3b45"});
        gameOverText.setOrigin(0.5);                               
        this.tweens.add({targets: gameOverText,duration: 300,y:game.config.height / 2-200}, this);

        this.time.addEvent({
            delay: 5000,
            callbackScope: this,
            callback: function(){
                this.startGame();
            }
        });
    }

    setDisplay(){
        this.stageText.setText("STAGE "+stage);
        // 플레이어 진행률 텍스트 업데이트
        this.levelText.setText(this.paintedRatio + "%");
        this.jumpPowerText.setText("JumpPower:"+gameOptions.jumpForce[0]+(BUFF.jumpPowerTime > 0?" X"+BUFF.jumpPower:""));
        this.speedText.setText("Speed:"+this.player.speed+(BUFF.speedTime > 0?" X"+BUFF.speed:""));
        this.hpText.setText("Hp:"+Math.round(this.player.hp));
        //this.pointTitle.setText('Point:'+player_point);
        this.scoreText.setText('Score:'+score)

        // this.gameInfoText.setText("JumpPower:"+gameOptions.jumpForce[0]+
        //                           "\nSpeed:"+this.player.speed+
        //                           "\nHp:"+this.player.hp
        // )

    }
    playerOverlap(player, mob){        
        if(GAMESTATE.state != 'death'){            
            player.hp  = player.hp - mob.atk;
            this.hpText.setColor("#ff0000");
            this.time.addEvent({delay: 500,callbackScope: this,callback: function(){this.hpText.setColor("#ffffff");}});

            if(GAMESTATE.state != 'death' && player.hp  <= 0 ){
                this.gameOver();
            }          
        }     
    }

    jumping(){       
        // 2회 미만 점프
        if(this.player.jumps < 2){
            this.player.anims.play('jump');
            this.jumpAudio.play({volume:gameOptions.gameVolume});
            this.player.jumps ++;                                
            this.player.jumpForce = gameOptions.jumpForce[this.player.jumps - 1] * BUFF.jumpPower; //jumpForce: [12, 8] 1회,2회
            this.player.hp-=20;
        }
    }
    getXY(currentAngle, dist){
        let radians = this.getRadians(currentAngle); //라디안으로 변환        
        return {x: game.config.width / 2 + dist * Math.cos(radians),y:game.config.height / 2 + dist * Math.sin(radians)}
    }

    getRadians(currentAngle){
        return Phaser.Math.DegToRad(currentAngle);
    }

    // 페이저 각도를 보다 읽기 쉬운 각도로 변환하는 방법
    getGameAngle(angle){
        let gameAngle = angle + 90;
        if(gameAngle < 0){
            gameAngle = 360 + gameAngle
        }
        return gameAngle;
    }
    mergeIntervals(intervals){
        if(intervals.length <= 1){
            return intervals;
        }
        let stack = [];
        let top = null;
        intervals = intervals.sort(function(a, b){
            return a[0] - b[0]
        });
        stack.push(intervals[0]);
        for(let i = 1; i < intervals.length; i++){
            top = stack[stack.length - 1];
            if(top[1] < intervals[i][0]){
                stack.push(intervals[i]);
            }
            else{
                if (top[1] < intervals[i][1]){
                    top[1] = intervals[i][1];
                    stack.pop();
                    stack.push(top);
                }
            }
        }
        return stack;
    }

    init_btn(){
        let sc_width  = game.config.width / 2+300
        let sc_height = (game.config.height / 2);

        this.startBtn       = this.add.sprite(sc_width, 50, 'startBtn').setInteractive();        
        this.startBtn.on('pointerdown', function () { this.startGame(); },this ); // Start game on click                     
    }
    playerStatUp(){
        let num = this.getRandom(1,3)
        let numText;
        let numUp;
        if(num == 1){
            numUp=10;
            this.player.speed+=numUp;         
            gameOptions.playerSpeed+=numUp;
            numText = "Speed +"+numUp
        }else if(num == 2 ){
            numUp=0.5;        
            gameOptions.jumpForce[0]= gameOptions.jumpForce[0]+0.5;    
            numText = "Jump Power +"+numUp      
        }else if(num == 3 ){
            numUp=0.1;
            gameOptions.playerRecovery+=0.3;
            numText = "HP Recovery +"+numUp      
        }
        let sc_width = game.config.width / 2;
        let sc_height = (game.config.height / 2);


        this.powerUp1Audio.play({volume:gameOptions.gameVolume});
        this.noticeTween(numText+' UP',sc_width, sc_height,{fontFamily: "Arial",fontSize: 33,color: "#1cc88a"});

    }

    noticeTween(message, sc_width, sc_height, config){
        var noticeObj = this.add.text(sc_width, sc_height+160,message,config);
        noticeObj.setOrigin(0.5);  
        var timeline = this.tweens.timeline({
            tweens: [{
                targets: noticeObj,
                y:sc_height-200,
                duration: 100,
                ease: 'Cubic.easeInOut'
            },
            {
                targets: noticeObj,
                y:-100,
                duration: 300,
                delay: 1000
            }]
        });
    }


    init_audio(){
        // this.musicAudio = this.sound.add('forest');        
        // this.musicAudio.play({volume:volume:gameOptions.gameVolume});
        // this.musicAD.setLoop(true); 
        this.jumpAudio = this.sound.add('bong_001');
        this.deathAudio = this.sound.add('death');
        this.gameOverAudio = this.sound.add('gameOverAudio');
        this.powerUp1Audio = this.sound.add('powerUp1');
    }    

    init_display(){
        let sc_width = game.config.width / 2;
        let sc_height = (game.config.height / 2);
        let fontConfig = {fontFamily: "Arial",fontSize: 50,color: "#ffffff"}


        // 플레이어 진행 상황을 표시하는 텍스트
        this.stageText = this.add.text(sc_width, 50, "STAGE "+stage, fontConfig);

        fontConfig.fontSize = 20;
        this.scoreText = this.add.text(50, 25,'Score:'+score,fontConfig);

        fontConfig.fontSize = 69;
        this.levelText = this.add.text(sc_width, sc_height-75, "", fontConfig);

        fontConfig.fontSize = 25;
        //this.gameInfoText = this.add.text(sc_width, sc_height,'',fontConfig);
        this.jumpPowerText = this.add.text(sc_width, sc_height,'',fontConfig);
        this.speedText = this.add.text(sc_width, sc_height+30,'',fontConfig);
        this.hpText = this.add.text(sc_width, sc_height+60,'',fontConfig);


        this.stageText.setOrigin(0.5);       
        this.levelText.setOrigin(0.5);
        //this.gameInfoText.setOrigin(0.5);
        this.jumpPowerText.setOrigin(0.5);
        this.speedText.setOrigin(0.5);
        this.hpText.setOrigin(0.5);

    }
    init_player(){
        this.player.currentAngle = this.getRandom(1,90) * this.getMinus_or_Plus();   // 플레이어 현재 각도
        this.player.previousAngle = this.player.currentAngle; // 플레이어 이전 각도         
        this.player.jumpOffset = 0; // 점프 오프셋, 점프 중 지면과 거리
        this.player.jumps = 0; //점프 횟수                 
        this.player.jumpForce = 0; // 현재 점프포스파워
        this.player.speed = gameOptions.playerSpeed;
        this.player.hp = gameOptions.playerHp;
        this.player.timer = 0;


    }

    getRandom(idx1, idx2){
        return (Math.floor(Math.random() *idx2)+idx1)
    }
    getMinus_or_Plus(){
        return (Math.floor(Math.random() * 2)==0?-1:1)
    }
    //회전
    mobAngle(currentAngle){
        return currentAngle* (gameOptions.bigCircleRadius / gameOptions.mobRadius + 1)*0.1;
    }

}
현재글 : Around Runner Game - HTML5
Comments
Login:

Copyright © PythonBlog 2021 - 2022 All rights reserved
Mail : PYTHONBLOG