?>

Hard Copy World

HCW

Arduino advanced

Home > Learning >

Arduino advanced

게임 메이커: 아두이노 게임기 만들기 PART7 - 총알과 해골 움직임, 마무리

페이지 정보

작성자 하드카피 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 작성일15-07-07 02:23 조회5,082회 댓글0건

본문

 

강좌 시리즈:

  1. 게임 메이커: 아두이노 게임기 만들기 Part1 - 프로젝트 소개, 하드웨어 구성, 테스트
  2. 게임 메이커: 아두이노 게임기 만들기 Part2 - 게임 프로그래밍 준비
  3. 게임 메이커: 아두이노 게임기 만들기 PART3 - 기획 및 기본 구조
  4. 게임 메이커: 아두이노 게임기 만들기 PART4 - 게임 화면 전환
  5. 게임 메이커: 아두이노 게임기 만들기 PART5 - 캐릭터 움직임 구현
  6. 게임 메이커: 아두이노 게임기 만들기 PART6 - 장애물 움직임과 충돌 구현
  7. 게임 메이커: 아두이노 게임기 만들기 PART7 - 총알과 해골 움직임, 마무리

 

지난 강좌에서 장애물 만들어서 이동시키는 루틴과 캐릭터와의 충돌을 검사하는 루틴을 추가했습니다. 이번 강좌에서 추가할 해골의 움직임과 총알, 캐릭터와의 충돌 검사도 거의 유사하게 작성하면 됩니다.

이번 강좌에서 사용되는 소스는 GitHub에서 받을 수 있습니다. [Template/Template5] 폴더를 확인하시면 됩니다.

https://github.com/godstale/game-maker

 

 

이미지 리소스 추가

 

이번 강좌에서는 해골의 움직임을 넣기 위해 bitmaps.cpp, bitmaps.h 파일에 IMG_enemy[], IMG_enemy_die[] 배열을 추가했습니다.

enemy   enemy_die

이 두 배열은 해골의 bitmap 이미지를 코드로 바꾼 것입니다. 그리고 사용하기 편하도록 두 배열을 다시 enemy_anim[] 배열에 넣었습니다.

PROGMEM const unsigned char* enemy_anim[] = {
  IMG_enemy, IMG_enemy_die
};

 

 

해골과 총알의 움직임

 

updateMove() 함수가 화면상의 오브젝트 움직임을 업데이트 하는 함수입니다. 이 함수에 해골 생성과 움직임 업데이트 코드를 추가로 넣었습니다. 변경된 코드만 추려보면 아래와 같습니다.

	// Make enemy
	if(enemyCount < ENEMY_MAX && millis() > enemyTime) {
		// Make enemy
		for(int i=0; i 0) {
		for(int i=0; i 0) {
				enemyX[i] -= ENEMY_MOVE;
				if(enemyX[i] < OBSTACLE_DEL_THRESHOLD) {
					// clear last drawing
					display.fillRect(enemyX[i] + ENEMY_MOVE, ENEMY_POS_Y, ENEMY_WIDTH, ENEMY_HEIGHT, BLACK);
					
					// delete enemy
					enemyX[i] = 0;
					enemyCount--;
				}
			}
		}
	}

현재 생성된 해골이 없으면 getRandTime() 함수를 이용해 일정 시간 뒤에 해골을 만들도록 예약합니다. 시간이 흘러 해골 생성 시간이 되면 해골을 만들어 enemyX[] 배열에 위치 값을 업데이트 합니다. 해골이 캐릭터와 닿거나 총알에 파괴되면 해골이 화면에서 삭제됩니다.

매 프레임 업데이트 할 때 마다 생성된 해골의 움직임도 업데이트 해줍니다. 해골의 위치를 나타내는 enemyX[] 배열의 값이 ENEMY_MOVE 만큼 작아집니다. 그래서 마치 왼쪽으로 움직이는 것처럼 보이게 됩니다. 당연하게도 해골의 움직임을 매번 업데이트하기 위해서는 앞서 그렸던 해골 영역을 지우고 변경된 위치에 다시 그려야 합니다.

 

 

해골과 총알, 캐릭터의 충돌 검사

 

해골과 총알, 캐릭터의 충돌했는지 검사하는 함수는 checkCollision()입니다. 이미 총알과 해골의 충돌을 검사하는 루틴은 지난 강좌에서 작성 했습니다. 여기서는 추가로 해골과 캐릭터의 충돌을 검사해주면 됩니다.

	// check bullet touch
	if(bulletCount > 0) {
		for(int i=0; i 0) {
		for(int i=0; i 0) {
				if(enemyX[i] <= prevPosX + CHAR_WIDTH) {
					// Character touched enemy. End game
					charStatus = CHAR_DIE;
					break;
				}
			}
		}
	}

캐릭터의 X 위치는 고정이므로 해골의 움직임을 나타내는 enemyX[] 배열의 값이 캐릭터 위치까지 도달했는지만 검사하면 됩니다. 충돌한다면 캐릭터의 상태를 나타내는 charStatus 변수에 CHAR_DIE 값을 넣어줍니다. 그러면 캐릭터 이미지가 변경되면서 게임이 종료됩니다.

 

 

draw() 함수 수정

 

draw() 함수에 아직 포함되지 않은 캐릭터 총쏘기 이미지, 해골 이미지, 총알 이미지 등을 넣어줍니다. 변경된 부분은 아래와 같습니다.

	} else if(charStatus == CHAR_FIRE) {
		prevPosY = CHAR_POS_Y;
		display.fillRect(CHAR_POS_X, prevPosY, CHAR_WIDTH, CHAR_HEIGHT, BLACK);
		display.drawBitmap(CHAR_POS_X, prevPosY, (const unsigned char*)pgm_read_word(&(char_anim[FIRE_IMAGE_INDEX])), CHAR_WIDTH, CHAR_HEIGHT, WHITE);
		charStatus = CHAR_RUN;
	}

	...

	// draw enemy
	if(enemyCount > 0) {
		for(int i=0; i 0) {
				// Enemy running image
				display.fillRect(prevEnemyPosX[i], ENEMY_POS_Y, ENEMY_WIDTH, ENEMY_HEIGHT, BLACK);	// clear previous drawing
				display.drawBitmap(enemyX[i], ENEMY_POS_Y, (const unsigned char*)pgm_read_word(&(enemy_anim[ENEMY_RUN_IMAGE_INDEX])), ENEMY_WIDTH, ENEMY_HEIGHT, WHITE);
				prevEnemyPosX[i] = enemyX[i];
			}
		}
	}
	
	// Draw bullet
	if(bulletCount > 0) {
		for(int i=0; i BULLET_START_X) {
				// Delete previous drawing
				display.drawLine(prevBulletPosX[i], BULLET_POS_Y, bulletX[i], BULLET_POS_Y, BLACK);
			}
			display.drawLine(bulletX[i], BULLET_POS_Y, bulletX[i] + BULLET_WIDTH, BULLET_POS_Y, WHITE);
			prevBulletPosX[i] = bulletX[i];
		}
	}

해골 이미지는 enemyX[] 배열이 가진 값에 따라 그릴 이미지가 틀려집니다. 총알과 충돌했을 때 파괴되는 이미지를 그려야 할 때가 있기 때문입니다. 즉, 해골의 움직임 뿐 아니라 총알과의 충돌 상태를 모두 enemyX[] 배열로 표시합니다.

총알의 움직임은 bulletX[] 배열로 표시됩니다.

추가로 draw() 함수에서는 점수도 표시하도록 수정되어 있습니다. 아래 코드가 점수를 게임 화면에 표시하는 코드입니다. 장애물을 넘거나 해골을 파괴하면 점수가 올라가는데 이때 점수 영역도 업데이트 됩니다.

	// Draw score
	if(prevGameScore != gameScore) {
		int tempS = gameScore+(int)((millis() - startTime) / 1000);
		int margin = 120 - getOffset(tempS);
		display.fillRect(margin, 1, 127, 9, BLACK);
		display.setCursor(margin, 1);
		display.print(tempS);
		prevGameScore = gameScore;
	}

 

 

게임 점수 기록하기

 

게임 점수가 최고점을 기록하면 EEPROM에 기록해서 재부팅 후에도 유지되도록 해줄 수 있습니다. 먼저 아두이노에 전원이 들어올 때 처음 실행되는 setup() 함수에서 EEPROM으로부터 최고 점수를 읽어오는 코드를 추가해야 합니다.

	// Read high score from eeprom
	while (!eeprom_is_ready()); // Wait for EEPROM to be ready
	cli();
	gameHighScore = eeprom_read_word((uint16_t*)eepromAddr);
	sei();
	if(gameHighScore < 0 || gameHighScore > 65500) gameHighScore = 0;

그리고 게임이 종료되고 게임 결과 화면이 표시될 때 최고 점수와 현재 점수를 비교합니다. 최고 점수를 갱신한 경우는 EEPROM에 기록합니다.

		else if (gameState == STATUS_RESULT) {  // Draw a Game Over screen w/ score
			gameScore += (int)((millis() - startTime) / 1000);
			if (gameScore > gameHighScore) { 
				gameHighScore = gameScore; 
				cli();
				eeprom_write_word((uint16_t*)eepromAddr, gameHighScore);
				sei();
			}  // Update game score

EEPROM 기록을 위해 아래와 같은 코드가 Template5.ino 파일 최상단 영역에 추가되었습니다. EEPROM 제어를 위해 필요한 헤더 파일과 전역 변수들입니다.

#include 
#include 

...
// System
int eepromAddr = 0;
...
int prevGameScore = 0;

 

 

테스트

 

앞서 설명한 총알과 해골의 움직임, 충돌, 점수와 관련된 코드 외에도 작게 수정한 부분들이 존재합니다. 마음에 안드시는 부분이 있다면 직접 여기저기 수정해 보시길 바랍니다.

이 소스코드를 아두이노에 올려서 테스트 해보세요. 정상적으로 게임이 진행되며 얼추 횡스크롤 액션 게임같은 느낌이 날 것입니다. 에러가 발생하는 부분 없이 잘 동작하는지 충분히 확인해보세요.

runningman07

이상으로 아두이노에 올릴 8비트 게임 만들기 강좌를 마치겠습니다.

 

 

마치며

 

어릴 적 즐겼던 8비트 게임을 아두이노로 다시 즐길 수 있다는 것은 정말 즐거운 일입니다. PC/모바일 게임과는 비할 수 없을 정도로 단순하지만 그게 8비트 게임의 매력이기도 합니다. 그리고 어릴 적 추억을 담겨 더 끌리기도 합니다.

비록 이런 게임들을 직접 만드는 작업이 쉽진 않지만 앞선 강좌들과 예제코드를 활용하면 직접 8비트 게임을 만드는데 적지 않게 도움이 될 것입니다. 게임기도 직접 만들어보시고 게임도 직접 제작해서 즐겨보세요. 값비싼 전용 게임기가 부럽지 않습니다.

 

 

강좌 시리즈:

  1. 게임 메이커: 아두이노 게임기 만들기 Part1 - 프로젝트 소개, 하드웨어 구성, 테스트
  2. 게임 메이커: 아두이노 게임기 만들기 Part2 - 게임 프로그래밍 준비
  3. 게임 메이커: 아두이노 게임기 만들기 PART3 - 기획 및 기본 구조
  4. 게임 메이커: 아두이노 게임기 만들기 PART4 - 게임 화면 전환
  5. 게임 메이커: 아두이노 게임기 만들기 PART5 - 캐릭터 움직임 구현
  6. 게임 메이커: 아두이노 게임기 만들기 PART6 - 장애물 움직임과 충돌 구현
  7. 게임 메이커: 아두이노 게임기 만들기 PART7 - 총알과 해골 움직임, 마무리

 

 


하드카피 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물

모든 전통이 한 때는 오해를 면치 못했다. 마찬가지로 모든 아이디어는 한 때는 비웃음을 면치 못했다. -홀브룩 잭슨 ::::: 느닷없이 떠오르는 생각이 가장 귀중한 것이며, 보관해야할 가치가 있는 것이다. -베이컨 ::::: 미리 숙고하면 힘든 일도 그저 실행에 옮기는 순간 쉬워진다. - 로버트 M. 피어식

추천 0
  • 페이스북으로 보내기
  • 트위터로 보내기
  • 구글플러스로 보내기

댓글목록

등록된 댓글이 없습니다.