🏃‍♂️달리기

1) 파일

2) main.js

const STATE_INIT = 3000;
const STATE_READY = 3001;
const STATE_PLAYING = 3002;
const STATE_JUDGE = 3004;
const STATE_END = 3005;
const STATE_INTRO = 3006;

let _players = App.players; 
let _state = STATE_INIT; 
let _start = false; 
let _stateTimer = 0; 
let _countDown = 10; 
let _finishCountDown = false;
let _finishCount = 30;
let _finishTimer = 0; 
let _delayTimer = 0; 
let _playerBaseSpeed = 120; 
let _rank = 1; 
let _rankList = []; 

//모든 플레이어가 결승점을 통과한 경우 처리  
function finishCheck(){
	
	let allPlayer = 0; 
	let finishedPlayer = 0; 

	for(let p of _players){

		if(!p)
			continue; 

		if(p.tag.isNotPlayer)
			continue; 

		if(p.tag.isFinish)
			finishedPlayer++;
		
		allPlayer++; 
	}

	if(finishedPlayer == allPlayer)
		return true 
	
	return false; 
}

//달리기 맵 설정이 정상적으로 되어있는 지 확인하고, 아닌 경우 에러메세지를 리턴한다. 
function checkSetting(){

	let warningMsg = '';

	if (!Map.hasLocation("race_start_point")) {
		warningMsg = 'race_start_point';

	}
	if (!Map.hasLocation("race_end_point")) {
		warningMsg = 'race_end_point';

	}
	if (!Map.hasLocation("race_finish_point")) {
		warningMsg = 'race_finish_point';

	}
	
	return warningMsg; 
}

function startState(state){

	_state = state; 
  	_stateTimer = 0; 

  	switch (state) {
		
    	case STATE_INIT:
     		for(let p of _players){
				//달리기 앱을 실행한 유저를 확인한다.
				if(p.id == App.creatorID){
					if(p.isMobile)
					//유저의 환경이 모바일인 경우의 위젯 
						p.tag.widget = p.showWidget("setting.html", "bottom", 440, 340);
					else
					//유저의 환경이 pc인 경우의 위젯
						p.tag.widget = p.showWidget("setting.html", "middle", 440, 340);
					
					p.tag.widget.sendMessage({ 
						str_title         : 'Run',
						str_title_text1   : "A race to see who reaches the finish line the fastest",
						str_title_text2   : 'Click the Start Running button to start the game',
						str_title_start   : "Start",
						str_title_how     : "How to set up a map"
					});
					
					p.tag.widget.onMessage.Add(function (sender, msg) {
						//게임을 취소하는 경우, 나타난 위젯을 제거한다 
						if (msg.type == "cancle") {
							if(p.tag.widget_warning){
								p.tag.widget_warning.destroy();
								p.tag.widget_warning = null;
							}

							if(p.tag.widget){
								p.tag.widget.destroy();
								p.tag.widget = null;
							}
						
						} else {
							// 맵에 필요한 설정 체크							
							let warning = checkSetting();
		
							if(warning !== ''){
								if(p.tag.widget_warning){
									p.tag.widget_warning.destroy();
									p.tag.widget_warning = null;
								}
		
								if(p.isMobile)
									p.tag.widget_warning = App.showWidget("warning.html", "top", 440, 60);
								else 
									p.tag.widget_warning = App.showWidget("warning.html", "bottom", 440, 250);
								
								p.tag.widget_warning.sendMessage({
									str_warningText :  'Race track needs to be setup',
									str_warningText2 : ' needed)',
									str_warningText3 : 'Run',
									warningMsg: warning,
								})
							} else {
								if(p.tag.widget){
									p.tag.widget.destroy();
									p.tag.widget = null;
								}
								
								startState(STATE_INTRO);
								_start = true;
							}
						}
					})  
				}
			}
    		break;

		case STATE_INTRO: 
			App.showCenterLabel("\n minigame - RUN \n\n", 0xffffff, 0x000000, 120); ;
			break; 
		
		case STATE_READY:
			for(let p of _players){
				if(p.tag.start)
				   continue; 
				
				//모든 플레이어가 "race_start_point" 로케이션으로 이동한다 
				p.spawnAtLocation("race_start_point"); 

				p.moveSpeed = 0; 
			   	p.sendUpdated();
			}
			break;
		
		case STATE_PLAYING:	
			
			App.showCenterLabel("Start!!", 0xffffff, 0x000000, 120); ;
			
			for(let p of _players){
				if(p.tag.start)
				continue; 
				p.moveSpeed = _playerBaseSpeed; 
				p.sendUpdated();
			}
			break

		// 게임 결과 처리 
		case STATE_JUDGE:
			for(let i =0 ; i <_rankList.length; i++){
				let rank = ''; 
				if(i == 0)
					rank = '1st'; 
				else if(i == 1)
					rank = '2nd';
				else if(i == 2)
					rank = '3rd'; 
				else
					rank = `${i + 1}th`;
				//게임종료 후 등수 표시 
				App.sayToAll(`${rank} : ${_rankList[i].name} !`);
			}
			for(let p of _players){
				p.moveSpeed = 80; 
				p.sendUpdated();
			}
			_start = false;

			break

		// 게임 종료 처리 
		case STATE_END:
			for(let p of _players){
				//게임이 종료되면 모든 플레이어는 "race_end_point" 로케이션으로 이동한다 
				p.spawnAtLocation("race_end_point"); 
				
				if(p.tag.isNotPlayer)
					p.tag.isNotPlayer = false;
			}
			_start = false;
			
			break
  }
}

App.onLeavePlayer.add(function(p){
	_players = App.players;

	p.moveSpeed = 80; 
	p.sendUpdated();

})

// 플레이어가 해당 맵에 들어왔을 때 처리
App.onJoinPlayer.Add(function (p) {
	p.tag = {};
  
	//게임시작 후 들어온 플레이어 처리 
	if (_start) {
		p.tag.isNotPlayer = true;  

	} else {
		p.tag.isNotPlayer = false; 
		p.tag.speedTimer = 0;
		p.tag.isFinish = false;  
		p.tag.speedChnage = false; 
	  	p.sendUpdated();
	}
	//게임이 시작된 후 들어온 플레이어는 'race_end_point' 로케이션으로 이동시킨다 
	if(p.tag.isNotPlayer)
		p.spawnAtLocation("race_end_point"); 

	_players = App.players;
  });

App.onStart.Add(function(){
	startState(_state);

	//"speed_set_40", "speed_set_60", "speed_set_140", "speed_set_160" 으로 지정한 로케이션을 밟으면 
	//2초간 해당 로케이션 이름의 숫자와 같이 플레이어의 속도가 변한다. (2초 후 플레이어의 속도는 _playerBaseSpeed 가 된다 )
	//ex) "speed_set_40"를 밟으면 속도가 40으로 변함 
	let speed = [40,60,140,160]; 

	for(let i =0; i<speed.length; i++){
		App.addOnLocationTouched(`speed_set_${speed[i]}`, function(p){

			if(_state !== STATE_PLAYING)
				return; 

			p.moveSpeed = speed[i]; 
			p.tag.speedTimer = 0; 
			p.tag.speedChnage = true;

			//로케이션에 설정된 속도에 따라 "속도가 빨라졌습니다", "속도가 느려졌습니다" 등의 라벨이 해당 플레이어에게 띄워진다
			let str = '';
			if(i == 0)
				str = 'Speed greatly reduced';
			else if(i == 1)
				str = 'Speed reduced';
			else if(i == 2)
				str = 'Speed increased';
			else
				str = 'Speed greatly increased';

			p.showCenterLabel(`${str}`, 0xffffff, 0x000000, 120); 
			p.sendUpdated();	
		})
	}

	//"speed_set_random" 로케이션을 밟는 경우 플레이어의 속도가 40, 60, 140, 160 중 하나의 속도로 2초간 바뀐다 (2초 후 플레이어의 속도는 _playerBaseSpeed 가 된다 )
	App.addOnLocationTouched(`speed_set_random`, function(p){
		if(_state !== STATE_PLAYING)
			return; 

		p.moveSpeed = speed[Math.floor(Math.random() * 4)]; 
		//로케이션에 설정된 속도에 따라 "속도가 빨라졌습니다", "속도가 느려졌습니다" 등의 라벨이 해당 플레이어에게 띄워진다
		let str = '';
		if(p.moveSpeed == 40)
			str = 'Speed greatly reduced';
		else if(p.moveSpeed == 60)
			str = 'Speed reduced';
		else if(p.moveSpeed == 140)
			str = 'Speed increased';
		else
			str = 'Speed greatly increased';

		p.showCenterLabel(`${str}`, 0xffffff, 0x000000, 120); 
		p.tag.speedTimer = 0; 
		p.tag.speedChnage = true;
		p.sendUpdated();
	})

	//플레이어가 결승선을 통과하는 경우
	App.addOnLocationTouched("race_finish_point", function(p){
		if(p.tag.isFinish)
			return; 

		if(p.tag.isNotPlayer)
			return;

		_delayTimer = 0; 

		// 처음으로 플레이어가 결승선을 통과하는 경우 레이스 종료 카운트 다운을 시작한다(디폴트 30초)
		if(!_finishCountDown)
			_finishCountDown = true;

		// 플레이어가 결승선을 최초에 통과한 경우를 체크한다 
		p.tag.isFinish = true; 
		_rankList.push(p); 
		_rank++;
	})
});
 
App.onUpdate.Add(function(dt){
	_stateTimer += dt; 

	if(_start){
		for(let p of _players){
			if(p.tag.isNotPlayer)
				p.showCenterLabel("game is in progress, please wait..",0xffffff, 0x000000, 120);
		}
	}

	switch (_state) {
		case STATE_INIT:
		break;
		case STATE_INTRO: 
			if(_stateTimer >= 3){
				startState(STATE_READY);
			}
			break; 
		case STATE_READY:
			//출발선에서 _countDown 초(디폴트 10초) 간 레이스 카운트 다운을 시작한다 
			App.showCenterLabel(`${_countDown}  seconds later the race will start. `,0xffffff, 0x000000, 120); 

			if(_stateTimer >= 1){
				_stateTimer = 0; 
				_countDown --; 
			} 
			
			//카운트가 종료되면 (_countDown 가 0이 되면) 레이스가 시작된다  
			if(_countDown <= 0)
				startState(STATE_PLAYING); 
			break;
		case STATE_PLAYING:
			
			for(let p of _players){

				if(finishCheck() && _rankList.length == 0){
					p.showCenterLabel("There is no winner",0xffffff, 0x000000, 120);
					p.spawnAtLocation("race_end_point"); 
					startState(STATE_END);
				}
				// else if(finishCheck())
				// 	startState(STATE_JUDGE);
				
				if(p.tag.isNotPlayer)
					continue; 
				if(p.tag.speedChnage)
					p.tag.speedTimer += dt; 
				
				//어떤 속도 지정 타일을 밟아도 2초 후 기본 속도(_playerBaseSpeed)로 돌아온다 
				if(p.tag.speedTimer >= 2 && p.tag.speedChnage){
					p.moveSpeed = _playerBaseSpeed; 
					p.tag.speedChnage = false; 
					p.sendUpdated(); 
				}
			}
			
			if(_finishCountDown)
				{	
					_delayTimer += dt; 
					//플레이어가 결승선을 통과하면 1.5초간 플레이어의 이름과 레이스 카운트다운을 라벨로 나타낸다 
					if(_delayTimer <=1.5)
						App.showCenterLabel(`${_rankList[_rankList.length -1].name}  ${_rank - 1} place ! \n\n ${_finishCount} seconds later the race will end`,0xffffff, 0x000000, 120);
					else
						App.showCenterLabel(`${_finishCount} seconds later the race will end`,0xffffff, 0x000000, 120);

					_finishTimer += dt; 

					if(_finishTimer >= 1){
						_finishCount--; 
						_finishTimer = 0; 
					}
					// 레이스 카운트다운이 종료(0초)되었거나, 모든 플레이어가 결승선을 통과한 경우, 다음 단계로 넘어간다  
					if(_finishCount == 0 || finishCheck()){
						startState(STATE_JUDGE); 
					}
				}
			break; 
		case STATE_JUDGE:
			//5초간 우승자(1등)의 이름을 라벨로 나타낸다 
			if(_stateTimer <= 5){
				App.showCenterLabel(`- Winner - \n\n ${_rankList[0].name}`,0xffffff, 0x000000, 120);
			} 
			//5초간 "잠시 후 대기실로 이동합니다"라는 라벨을 띄웁다 
			else if(_stateTimer > 5 && _stateTimer <= 10 ){
				App.showCenterLabel("You will soon be transported to the waiting room",0xffffff, 0x000000, 120);
			}
			else {
				startState(STATE_END);
			}
			break;
		case STATE_END:
			
			break;
	}
});

Last updated