Flash CS4 + ActionScript 3.0로 탱크 게임 만들기 테스트.

* 키보드 상하좌우, 혹은 wsad키로 탱크 조종하기.
* 마우스 클릭하면 그쪽 방향으로 탱크 포탄 발사.
* 에너지 게이지를 화면에 추가하고 주인공 탱크 타격시 에너지 한 칸씩 줄어들도록 처리.
* 주인공 탱크가 4번 적 총탄에 맞으면 게임 오버.
* 'ReStart' 버튼을 클릭하면 게임이 처음부터 재시작된다.

이전 마우스 클릭으로부터 5초 이내이면 주인공 탱크 포탄 발사하게 처리.
키보드 상하좌우, wsad키로 탱크 움직이게 처리.
마우스 클릭하는 곳으로 탱크 터렛이 향하게 하고 탱크 터렛에서 마우스 클릭한 곳까지 라인 그어지게 처리.
총알이 마우스 클릭한 곳을 향해 발사되고 화면을 나가면 제거되게 하고 특정시간까지 화면내에 있으면 폭발효과를 보이고 사라지게 처리.
적 등장하게 하고 주인공 총알과 적 충돌시 적 폭파효과 이후 제거되게 처리.
적이 일정시간마다 총알을 발사하게 만들고 적탄과 주인공 충돌시 주인공 타격 입는 모습 보이게 처리.
적탄 발사시 적탄이 발사 방향을 향하게 만드는데는 성공했는데 적 탱크 터렛을 발사방향으로 돌리는 것이 잘 안되어 헤메다. -적탄 방향에서 적 몸체의 방향을 빼는 것으로 해결.
적탄 발사는 10~170도 사이에서 무작위로 정하고 싶어 Math.random()*160+10;로 처리.
주인공이 적탄을 맞을 때 마다 에너지 줄어들게 하여 에너지가 0이 되면 게임오버 되게 하고 재시작 버튼 띄우기. 재시작 버튼 클릭시 게임 재시작하기 추가.
한데 재시작을 두번 하면 적들이 등장하지 않는 문제가 발생 -적 탱크 생성 타이머 발동시 화면내 적 숫자가 3 이하일 때 새로 적을 만들게 했는데 재시작시 화면내 적 숫자를 초기화 하지 않아 생긴 문제, 재시작시 초기화 시켜주게 하여 해결.

15/6/15 월

* 올해 들어선 처음으로 지네에게 물렸다. 물린 곳은 엄지 발가락, 물로 물린 곳을 씻어냈다. 벌에 쏘인 듯한 통증이 15~20분 정도 지속 됐었다.

화면에 'tM'이란 이름의 주인공 탱크(무비클립 내에는 'top'이란 이름의 탱크위 터렛 무비클립, 'pointM'이란 이름의 충돌체크용 사각형 무비클립을 만들어 두었다)를 배치해 두고 'eTankM'이란 익스포트명의 적 탱크 무비클립(무비클립 내에는 'boom'이란 name 뒤에 간단한 폭파 애니를 만들어 두었다), 'tBulletM', 'eBulletM'이란 이름의 주인공 포탄, 적 포탄 무비클립, 'screenM'이란 이름의 설명용 화면 무비클립(내부에 'restartBtn'이란 이름의 재시작 버튼 무비클립을 담고 있다)을 라이브러리에 만들어 두다.

var screen:MovieClip = new screenM(); //설명화면 무비클립
var player:MovieClip = tM; //주인공 탱크
var pSpeed = 2; //탱크 속도
var pEnergy; //탱크 목숨
var shootInterval = 500; //주인공 발사 간격 0.5초
var startTime = 0;
var elapsedTime = 0;
var enemyNum = 0; //화면상 적 탱크의 수

var allObjM:MovieClip = new MovieClip(); //화면내 모든 무비 담기용
var pBulletArr:Array = new Array(); //주인공 탄 저장 배열
var pBulletBoomArr:Array = new Array(); //주인공 탄 폭파효과 저장 배열
var enemyArr:Array = new Array(); //적 탱크 저장 배열
var eBulletArr:Array = new Array(); //적탄 저장 배열
var enemyBoomArr:Array = new Array(); //적 폭파효과 저장 배열

var eSpawnTimer:Timer = new Timer(3000); // 적탱크 생성 타이머 3초

var left, right, up, down, jump; //주인공 조종 관련 방향변수
var left0,right0,up0,down0,released=true; //주인공 이전 방향변수
var sx0=0, sx1=stage.stageWidth; //화면 상하좌우 변수
var sy0=0, sy1=stage.stageHeight;

function init(){
	gameInit();
}
//************************************************************
// 게임 초기화, 종료, 재시작 처리 등.
//************************************************************
function gameInit(){
	player.gotoAndStop(1);
	left = false, right = false, up = false, right = false;
	pEnergy = 4;
	enemyNum = 0;
	pEnergyM.gotoAndStop(pEnergy);
	stage.addChild(allObjM);
	
	stage.addEventListener(Event.ENTER_FRAME,loop);
	stage.addEventListener(MouseEvent.CLICK,mouseClick);
	stage.addEventListener(KeyboardEvent.KEY_UP,keyUps);
	stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDowns);
	
	eSpawnTimer.addEventListener(TimerEvent.TIMER,esTimeOn);
	eSpawnTimer.reset();
	eSpawnTimer.start();
}
function gameOver(){ //게임 오버 처리
	//배열들 비우기
	pBulletArr = []; 
	pBulletBoomArr = [];
	enemyArr = [];
	eBulletArr = [];
	enemyBoomArr = [];
	//이벤트 핸들러들 지우기
	stage.removeEventListener(Event.ENTER_FRAME,loop);
	stage.removeEventListener(MouseEvent.CLICK,mouseClick);
	stage.removeEventListener(KeyboardEvent.KEY_UP,keyUps);
	stage.removeEventListener(KeyboardEvent.KEY_DOWN,keyDowns);
	eSpawnTimer.removeEventListener(TimerEvent.TIMER,esTimeOn);
	
	stage.addChild(screen);
	screen.gotoAndStop("gameOver");
	screen.restartBtn.addEventListener(MouseEvent.CLICK,gameRestartH);
}
function gameRestartH(e:MouseEvent){ gameRestart(); }
function gameRestart(){
	//화면내 무비클립들 지우기
	while(allObjM.numChildren) allObjM.removeChildAt(0);
	stage.removeChild(allObjM);
	
	screen.restartBtn.removeEventListener(MouseEvent.CLICK,gameRestartH);
	stage.removeChild(screen);
	gameInit();
}
//************************************************************
//************************************************************
// 메인 루프**************************************************
//************************************************************
//************************************************************
function loop(e:Event){ //매 프레임 실행
	keyProcessing(); //키보드 처리
	pBulletMove(); //주인공 총알 움직임 처리
	pBulletBoomProcess(); //주인공 총알 폭발효과 처리
	enemyMove(); //적 이동 처리
	eBulletMove(); //적탄 이동 처리
	enemyBoomProcess(); //적 폭발효과 처리
	collisionCheck(); //충돌처리
}
//************************************************************
// 충돌 처리 관련
//************************************************************
function collisionCheck(){ //충돌처리
	eBulletArr = eBulletArr.filter(eraseNull);//eBulletArr내 null 제거
	for(var i=0; i < eBulletArr.length; ++i){ //적탄과 주인공 충돌 체크
		var eb = eBulletArr[i];
		if(eb.hitTestObject(player.pointM)){ //적탄과 주인공 충돌시
			playerDamage(); //주인공 충돌처리
			eBulletDestroy(eb, i); //적탄 제거 함수 호출
			continue; //다음으로 넘어가기
		}
	}
	enemyArr = enemyArr.filter(eraseNull);//enemyArr내 null 제거
	for(var j=0; j < enemyArr.length; ++j){
		var enemy = enemyArr[j];
		pBulletArr = pBulletArr.filter(eraseNull);//pBulletArr내 null 제거
		for(var k=0; k < pBulletArr.length; ++k){ //주인공 총알과 적 충돌 체크
			var pb = pBulletArr[k];
			if(pb.hitTestObject(enemy)){ //총알과 적 충돌시
				//trace("enemy Hit!!!!!!!!");
				enemyBoom(enemy.x, enemy.y, enemy.rotation);//폭발효과 붙이기
				enemyDestroy(enemy, j); //적 제거 함수 호출
				pBulletDestroy(pb, k); //총알 제거 함수 호출
				continue; //다음으로 넘어가기
			}
		}
	}
}
//************************************************************
// 타이머 처리 관련
//************************************************************
function esTimeOn(e:TimerEvent){ //타이머 작동
	if(enemyNum < 3){ //화면상에 적 탱크가 3대 이하일 경우
		enemyInit(); //적 생성, 초기화 함수 호출
		enemyNum++;
	}
}
//************************************************************
// 적 관련
//************************************************************
function enemyInit(angle=90, speed=2){ //적 탱크 생성, 초기화
	//매개변수(각도(도), 이동속도)
	var em = new eTankM();
	em.x = Math.random()*(sx1-em.width)+em.width; //적 위치 초기화
	em.y = 100; //-em.height; 
	em.vx = 0, em.vy = 0; //탄 속도벡터 초기화
	em.rotation = angle; //탄 각도 발사방향으로 회전
	em.sTimer = 60; //총알 발사 타이머
	//em.life = 130;
	
	angle = Math.PI/180 * angle; //도에서 라디안으로
	//탄 속도벡터 구하기
	em.vx = Math.cos(angle)*speed;
	em.vy = Math.sin(angle)*speed;
	
	allObjM.addChild(em); //적 화면에 붙이기
	enemyArr.push(em); //적 배열에 추가
}
function eBulletInit(enemy, angle=90, speed=2){ //적탄 생성, 초기화
	//매개변수(적 무비클립,)
	var eb = new eBulletM();
	eb.x = enemy.x; //적탄 위치 초기화
	eb.y = enemy.y;  
	eb.vx = 0, eb.vy = 0; //탄 속도벡터 초기화
	eb.rotation = angle; //탄 각도 발사방향으로 회전
	//eb.life = 130;
	
	angle = Math.PI/180 * angle; //도에서 라디안으로
	//탄 속도벡터 구하기
	eb.vx = Math.cos(angle)*speed;
	eb.vy = Math.sin(angle)*speed;
	
	allObjM.addChild(eb); //적탄 화면에 붙이기
	eBulletArr.push(eb); //적 배열에 추가
}
function enemyMove(){ //적 이동 처리
	//enemyArr내 null값 요소 제거
	enemyArr = enemyArr.filter(eraseNull);
	for(var i=0; i < enemyArr.length; ++i){
		var em = enemyArr[i];
		if(em.x > -20 && em.x-em.width < stage.stageWidth
		   && em.y > -20 && em.y-em.height < stage.stageHeight){
			//적이 화면내 존재시
			em.x += em.vx; //기존 탄 위치 더하기 속도 벡터
			em.y += em.vy;
			if(em.sTimer <= 0){ //총알 발사 간격이 지났으면
				var ebAngle = Math.random()*160+10; //총알 발사각도
				em.top.rotation = ebAngle - em.rotation;
				eBulletInit(em, ebAngle); //적 총알 발사
				em.sTimer = 60; // 타이머 시간 복원
			}
			em.sTimer--; //타이머 시간 줄이기
		}else{ //적이 화면 밖으로 나갔을 경우
			enemyDestroy(em, i); //적 제거
			continue; //다음으로 넘어가기
          }
     }
}
function eBulletMove(){ //적탄 이동 처리
	//eBulletArr내 null값 요소 제거
	eBulletArr = eBulletArr.filter(eraseNull);
	for(var i=0; i < eBulletArr.length; ++i){
		var eb = eBulletArr[i];
		if(eb.x > -20 && eb.x-eb.width < stage.stageWidth
		   && eb.y > -20 && eb.y-eb.height < stage.stageHeight){
			//적이 화면내 존재시
			eb.x += eb.vx; //기존 탄 위치 더하기 속도 벡터
			eb.y += eb.vy;
		}else{ //적이 화면 밖으로 나갔을 경우
			eBulletDestroy(eb, i); //적 제거
			continue; //다음으로 넘어가기
          }
     }
}
function enemyDestroy(em, id){ //적 제거 처리
	arrDestroy(em, id, enemyArr);
	enemyNum--;
}
function eBulletDestroy(eb, id){ //적탄 제거 처리
	arrDestroy(eb, id, eBulletArr);
}
function enemyBoom(bx, by, angle=0){ //적 폭발효과 붙이기
	//매개변수(x위치, y위치)
	var emb = new eTankM();
	emb.gotoAndPlay("boom");
	emb.x = bx, emb.y = by; //위치 초기화
	emb.rotation = angle; //탄 각도 발사방향으로 회전
	emb.life = 30;
	
	allObjM.addChild(emb); //화면에 붙이기
	enemyBoomArr.push(emb); //배열에 추가
}
function enemyBoomProcess(){ //적 탱크 폭발효과 처리
	//Arr내 null값 요소 제거
	enemyBoomArr = enemyBoomArr.filter(eraseNull);
	for(var i=0; i < enemyBoomArr.length; ++i){
		var emb = enemyBoomArr[i];
		if(emb.life){ //life 요소를 갖고 있으면
			emb.life--; //매 프레임 life 줄이기
			if(emb.life <= 1){ //life가 1이하가 되면
				enemyBoomDestroy(emb, i); //폭발효과 제거
				continue; //다음으로 넘어가기
			}
		}
	}
}
function enemyBoomDestroy(emb, id){ arrDestroy(emb, id, enemyBoomArr); }
//************************************************************
// 마우스 처리 관련
//************************************************************
function mouseClick(e:MouseEvent){ //마우스 클릭시
	elapsedTime = getTimer() - startTime;
	if(elapsedTime >= shootInterval){ 
	//이전 샷 이후 경과시간이 총알 발사제한 인터벌 통과시
		elapsedTime = 0; //경과시간 초기화
		rayCastTest(mouseX, mouseY);
	}
	startTime = getTimer(); //마지막 클릭한 시간 기억
}
function rayCastTest(mx, my){
	graphics.clear();
	lineDraw01(player.x, player.y, mx,my, 1, 0xFFFFFF); //마우스 클릭한 곳까지 선긋기
	var dx = mx - player.x;
	var dy = my - player.y;
	var tangle = Math.atan2(dy, dx); //탱크 중앙과 마우스 클릭한 곳 각 구하기
	player.top.rotation = tangle * 180/Math.PI; //탱크 위 터렛 회전
	pbDirectInit(tangle); //총알 생성, 초기화
}
//************************************************************
// 주인공 관련
//************************************************************
function pbDirectInit(angle=0, speed=3){ //주인공 방향탄 생성 및 초기화
	//매개변수(각도, 이동속도)
	var pb = new tBulletM();
	pb.x = player.x, pb.y = player.y; //탄 위치 초기화
	pb.vx = 0, pb.vy = 0; //탄 속도벡터 초기화
	pb.rotation = angle * 180/Math.PI; //탄 각도 발사방향으로 회전
	pb.life = 130;
	
	//angle += angle * Math.PI/180; //도에서 라디안으로
	//탄 속도벡터 구하기
	pb.vx = Math.cos(angle)*speed;
	pb.vy = Math.sin(angle)*speed;
	
	allObjM.addChild(pb); //탄 화면에 붙이기
	pBulletArr.push(pb); //탄 배열에 추가
}
function pBulletMove(){ //주인공 총탄 이동 처리
	//pBulletArr내 null값 요소 제거
	pBulletArr = pBulletArr.filter(eraseNull);
	for(var i=0; i < pBulletArr.length; ++i){
		var pb = pBulletArr[i];
		if(pb.x > -20 && pb.x+pb.width < stage.stageWidth
		   && pb.y > -20 && pb.y+pb.height < stage.stageHeight){
			//탄이 화면내 존재시
			pb.x += pb.vx; //기존 탄 위치 더하기 속도 벡터
			pb.y += pb.vy;
			
			if(pb.life){ //life 요소를 갖고 있으면
				pb.life--; //매 프레임 life 줄이기
				if(pb.life <= 1){ //life가 1이하가 되면
					pBulletDestroy(pb, i); //탄 제거
					pBulletBoom(pb.x, pb.y, pb.rotation);//폭발효과 붙이기
				}
			}
		}else{ //탄이 화면 밖으로 나갔을 경우
			pBulletDestroy(pb, i); //탄 제거
			continue; //다음으로 넘어가기
          }
     }
}
function pBulletDestroy(pb, id){ //주인공 총탄 제거 처리
	arrDestroy(pb, id, pBulletArr);
}
function pBulletBoom(bx, by, angle=0){ //총탄 폭발효과 붙이기
	//매개변수(x위치, y위치)
	var pbb = new tBulletM();
	pbb.gotoAndPlay("boom");
	pbb.x = bx, pbb.y = by; //위치 초기화
	pbb.rotation = angle; //탄 각도 발사방향으로 회전
	pbb.life = 30;
	
	allObjM.addChild(pbb); //화면에 붙이기
	pBulletBoomArr.push(pbb); //배열에 추가
}
function pBulletBoomProcess(){ //주인공 총탄 폭발효과 처리
	//pBulletArr내 null값 요소 제거
	pBulletBoomArr = pBulletBoomArr.filter(eraseNull);
	for(var i=0; i < pBulletBoomArr.length; ++i){
		var pbb = pBulletBoomArr[i];
		if(pbb.life){ //life 요소를 갖고 있으면
			pbb.life--; //매 프레임 life 줄이기
			if(pbb.life <= 1){ //life가 1이하가 되면
				pBulletBoomDestroy(pbb, i); //폭발효과 제거
				continue; //다음으로 넘어가기
			}
		}
	}
}
function pBulletBoomDestroy(pbb, id){ arrDestroy(pbb, id, pBulletBoomArr); }
function playerDamage(){ //주인공 충돌 처리
	trace("Player Hit!!!!!!!!!!");
	player.gotoAndPlay("hit"); //주인공 충격 그래픽
	if(pEnergy > 1){
		pEnergy--; //주인공 목숨 수 줄이기
		pEnergyM.gotoAndStop(pEnergy); //에너지 게이지 줄이기
	}else{ 
		trace("player Dead!!!!!!!!!"); 
		player.gotoAndStop("hit");
		gameOver(); //게임 오버 처리.
	}
}
//************************************************************
// 키보드 처리 관련
//************************************************************
function keyProcessing(){ //키보드 처리
	if(left && sx0 <= player.x-player.width/2){ player.x -= pSpeed; }
	if(right && player.x+player.width/2 <= sx1){ player.x += pSpeed; }
	if(up && sy0 <= player.y-player.height/2){ player.y -= pSpeed; }
	if(down && player.y+player.height/2 <= sy1){ player.y += pSpeed; }
}
function keyDowns(e:KeyboardEvent){ //키보드 눌릴때
	switch(e.keyCode){
		case(37): case(65): left = true; break; //left, a
		case(39): case(68): right = true; break; //right, d
		case(38): case(87): up = true; break; //up, w
		case(40): case(83): down = true; break; //down, s
		case(17): jump = true; break; //left ctrl 점프
		case(90): break; //z
		case(88): break; //x 
		default: break;
	}
}
function keyUps(e:KeyboardEvent){ //키보드 뗄 때
	switch(e.keyCode){
		case(37): case(65): left = false; break; //left, a
		case(39): case(68): right = false; break; //right, d
		case(38): case(87): up = false; break; //up, w
		case(40): case(83): down = false; break; //down, s
		case(107): break; //Numpad +
		//case(109): break; //Numpad -
		case(96): break; //Numpad 0
		case(32): break; //space
		case(90): break; //z
		case(88): break; //x
		case(67): break; //c
		default: break;
	}
}
//************************************************************
// 기타 도우미 함수들
//************************************************************
function arrDestroy(item, id, arr:Array){ //배열내 해당요소 제거 처리
	try{
		allObjM.removeChild(item); //해당 요소 화면에서 제거
	}catch(er:Error){ trace("pBullet removeChild error"); }
	arr[id] = null; //해당요소 배열에서 비우기 위해 null 넣어두기
}
function lineDraw01(mX,mY,tX,tY,style1=1,style2=0){ //선그리기 함수
	//(mX,mY)위치로 이동, (tX,tY)까지 style(굵기,색)대로 선그리기
	graphics.lineStyle(style1, style2);
	graphics.moveTo(mX, mY);
	graphics.lineTo(tX, tY);
}
function eraseNull(element:*,index:uint,array:Array):Boolean{
     //배열 내부의 null 제거용 함수
     return (element != null);
}
init();