이번에는 저번에 구현했던 비트맵에서 더 나아가 여러가지 이미지를 번갈아가며 화면에 나타내어 움직이는 듯한 모습을 구현하고자 합니다.
저번에 사용했던 툴을 이용해서 공룡이 달리는 듯한 두 이미지를 생성하고, 이를 배열로 변환한 뒤 다음과 같이 코드를 작성해서 공룡이 뛰는 모션을 구현했습니다.
더보기
void Dino_Run() {
OLED_ClearBuffer(&myDevice);
OLED_MoveTo(&myDevice, 0, 0); // (0,0)에서 출력 시작
OLED_Update(&myDevice); // OLED 화면 업데이트
u8 *pat;
char c;
xil_printf("print dinosaur and cactus");
c = 1;
while(1){
xil_printf("Dinosaur running\n");
OLED_MoveTo(&myDevice, 0, 13); // (0,0) 좌표에서 비트맵 출력 시작 //MAX 128,32
//OLED_GetBmp(&myDevice, 128, 128, dinoncactus);
OLED_PutBmp(&myDevice, 19, 19, dinorun1);
usleep(100000);
OLED_Update(&myDevice); // OLED 화면 업데이트
OLED_PutBmp(&myDevice, 19, 19, dinorun2);
usleep(100000);
//OLED_PutBmp(&myDevice, DINONCACTUS_WIDTH, DINONCACTUS_HEIGHT, dino);
OLED_Update(&myDevice); // OLED 화면 업데이트
}
}
위 코드를 실행하면 공룡이 발을 번갈아가며 들어올리며 걷는듯한 애니메이션을 구현할 수 있습니다.
이후 선인장이 우측 끝에서 좌측 끝으로 이동하는 것을 구현하기 위해 아래와 같이 코드를 작성했습니다.
더보기
void CactusMove() {
int cactus_x = 128; // initial cactus position
int cactus_width = CACTUS_WIDTH;
int draw_x, draw_width;
char c;
while (1) {
OLED_ClearBuffer(&myDevice);
// 1️. print cactus on current position
OLED_MoveTo(&myDevice, cactus_x, 16);
OLED_PutBmp(&myDevice, CACTUS_WIDTH, CACTUS_HEIGHT, cactus);
// 2️. move cactus to left
cactus_x -= 1;
// 3️. when cactus reaches left edge, start from the right again
if (cactus_x < 0) {
cactus_x = 128; // 오른쪽 끝으로 이동
}
OLED_Update(&myDevice);
// 4️. speed control
usleep(10000); // 100ms 대기*/
}
}
이제 두 모션을 합쳐 좌측에는 공룡이 계속 뛰고 있고 선인장이 움직이는 애니메이션이 구현되도록 했습니다.
이후에는 장애물을 선인장2개, 바위를 추가하여 총 장애물 3개를 만든 뒤, 난수를 발생시켜 랜덤으로 장애물을 선택하고, 그 간격도 무작위로 나타나도록 코드를 최종 수정했습니다. 추가로 우측 상단에는 프레임에 따라 증가하는 점수판이 표시되도록 했습니다.
더보기
// 난수를 직접 생성하는 함수 (XORSHIFT 사용)
int get_random_offset() {
static uint32_t seed = 0xACE1;
seed ^= seed << 13;
seed ^= seed >> 17;
seed ^= seed << 5;
return (seed % 40) + 20; // 20~60 범위의 난수 반환 (장애물 간격 조절)
}
// 장애물 종류 선택 함수
const uint8_t* get_random_obstacle(int* width, int* height) {
static uint32_t seed = 0xBEEF;
seed ^= seed << 11;
seed ^= seed >> 7;
seed ^= seed << 3;
int obstacle_type = (seed % 3); // 0, 1, 2 중 하나 선택
if (obstacle_type == 0) {
*width = CACTUS_WIDTH;
*height = CACTUS_HEIGHT;
return cactus;
} else if (obstacle_type == 1) {
*width = CACTUS2_WIDTH;
*height = CACTUS2_HEIGHT;
return cactus2;
} else {
*width = ROCK_WIDTH;
*height = ROCK_HEIGHT;
return rock;
}
}
void DinoCactus_Run() {
int cactus_x = 128;
int frame = 0;
int dino_y = 15;
int dino_x = 15;
int is_jumping = 0;
int jump_height = 0;
int delay_time = 30000;
int min_delay = 3000;
int speed_increment_interval = 500;
int anim_speed = 4;
int score = 0;
char score_str[5];
// 첫 번째 장애물 초기화
int obstacle_width, obstacle_height;
const uint8_t* current_obstacle = get_random_obstacle(&obstacle_width, &obstacle_height);
int next_obstacle_distance = get_random_offset();
while (1) {
OLED_ClearBuffer(&myDevice);
// 점프 조건 확인
if (!is_jumping && (cactus_x > (dino_x + DINO_WIDTH)) && (cactus_x - (dino_x + DINO_WIDTH) <= 5)) {
is_jumping = 1;
jump_height = 0;
}
// 점프 로직
if (is_jumping) {
if (jump_height < 5) {
dino_y -= 2;
} else if (jump_height < 18) {
dino_y -= 1;
} else if (jump_height < 22) {
dino_y += 0;
} else if (jump_height < 35) {
dino_y += 1;
} else if (jump_height < 40) {
dino_y += 2;
} else {
is_jumping = 0;
jump_height = 0;
dino_y = 15;
}
jump_height++;
}
// 공룡 애니메이션
OLED_MoveTo(&myDevice, dino_x, dino_y);
if ((frame / anim_speed) % 2 == 0) {
OLED_PutBmp(&myDevice, DINO_WIDTH, DINO_HEIGHT, dinorun1);
} else {
OLED_PutBmp(&myDevice, DINO_WIDTH, DINO_HEIGHT, dinorun2);
}
// 장애물 이동
if (cactus_x >= 0) {
OLED_MoveTo(&myDevice, cactus_x, 32-obstacle_height);
OLED_PutBmp(&myDevice, obstacle_width, obstacle_height, current_obstacle);
}
// 충돌 감지
if (cactus_x >= dino_x && cactus_x <= (dino_x + DINO_WIDTH) && dino_y >= 13) {
OLED_ClearBuffer(&myDevice);
OLED_MoveTo(&myDevice, 30, 10);
OLED_DrawString(&myDevice, "GAME OVER");
OLED_Update(&myDevice);
xil_printf("GAME OVER!\n");
while (1) { usleep(100000); }
}
// 장애물 이동 (1픽셀씩)
cactus_x -= 1;
// 장애물이 왼쪽 끝에 도달하면 새로운 장애물 생성
if (cactus_x < -obstacle_width) {
cactus_x = 128 + next_obstacle_distance;
current_obstacle = get_random_obstacle(&obstacle_width, &obstacle_height);
next_obstacle_distance = get_random_offset();
}
// 속도 증가 로직
if (frame % speed_increment_interval == 0 && delay_time > min_delay) {
delay_time = (int)(delay_time * 0.9);
}
// 점수 증가 (50 프레임마다 1점 증가)
if (frame % 15 == 0 && score < 9999) {
score += 1;
}
// 점수를 네 자리로 표시
sprintf(score_str, "%04d", score);
OLED_MoveTo(&myDevice, 90, 0);
OLED_DrawString(&myDevice, score_str);
OLED_Update(&myDevice);
usleep(delay_time);
frame++;
}
}
이제 이전에 사용했던 키패드나 버튼을 활용하여 직접 공룡을 조작할 수 있도록 설계하고 공룡 게임 구현 프로젝트를 마무리할 예정입니다.
'자습시간 > Verilog' 카테고리의 다른 글
Pmod OLED 공룡 게임 만들기(3) - 최종 (0) | 2025.02.20 |
---|---|
Pmod OLED 공룡 게임 만들기(1) - 비트맵 이미지 구현 (0) | 2025.02.17 |
Vivado와 Vitis를 이용한 Pmod OLED 제어 [Pmod IP 이용] (0) | 2025.02.14 |
Verilog 복습 프로젝트(3) - Decoder 설계 [Pmod KYPD 이용] (0) | 2025.02.12 |