The Strange Adventure

STM32 기반 장애물 회피 2D 게임 프로젝트

개요

프로젝트 기간

2025.04.25 ~ 2025.05.04

목적

STM32 M3 코어 기반 2D 플랫폼 게임을 설계·구현하여 UART 통신 및 GPIO 포트 활용 능력을 향상시키고,
타이머 인터럽트 기반 게임 루프 제어 및 사용자 입력 처리로 Event-Driven 프로그래밍 설계 역량을 강화하는 것을 목표로 함.

하드웨어

  • MCU : 72MHz ARM Cortex-M3 프로세서
  • 디스플레이 : 320x240 해상도 컬러 LCD 디스플레이
  • 입력 장치 : Jog 키, push button (KEY0, KEY1)
  • 통신 방식 : UART (보드 <-> 데스크탑)

주요 기능

  • Jog키를 사용하여 플레이어 이동 기능 구현
  • button을 사용하여 플레이어의 점프 기능 구현 (플랫폼 올라타기, 머리 박음 판정)
  • 움직이는 장애물 및 총알 발사 기능 구현
  • UART를 통해 Clear time, Death count를 Display

개발 결과

스테이지 구성

3개의 난이도(level 1, level 2, level 3)가 있고, 각 난이도마다 3개의 stage로 총 9개의 stage로 구성.

성능

  • 물리적 계산과 렌더링(화면 갱신) 분리를 통해 60FPS 속도로 부드러운 게임 플레이 가능
  • 델타 렌더링을 통해 Jittering 현상을 제거하여 게임의 실시간성을 잘 반영

게임 설명

시연 영상


핵심 기술 및 코드

1. 코드 흐름 (Main)


2. 점프 동작

  • 점프를 했을 때 발생하는 모든 경우들을 미리 계산한 후, 렌더링을 통해 반영
  • 중력이 작용하는 것 처럼 초기 점프속도를 설정 후, 중력 가속도를 설정하여 속도가 점차 감소

3. 스테이지 구성 (구조체)

  • 데이터 코드 분리 : 스테이지는 StageCfg 데이터만 바꾸면 생성·동작. 엔진 코드를 수정하지 않고도 레벨을 추가/수정 가능.
  • 일관된 업데이트 파이프라인 : 플랫폼/장애물/발사체(슈터) 이동·충돌·렌더링을 공통 루프에서 처리.
  • 확장 용이성 : PlatformCfg, ObstacleCfg, ShooterObstacleCfg로 개별 행동 규칙을 주입.
typedef struct {
    u8           	       platform_count;                // 발판 개수
    QUERY_DRAW    	       platforms[MAX_PLATFORMS];      // 발판 정보 배열
    PlatformCfg                plat_cfg[MAX_PLATFORMS];       // 발판 이동 정보

    u8           	       obstacle_count;                // 장애물 개수
    QUERY_DRAW    	       obstacles[MAX_OBSTACLES];      // 장애물 정보 배열
    ObstacleCfg   	       obs_cfg[MAX_OBSTACLES];        // 장애물별 이동 설정 배열

    u8                         shooter_count;
    ShooterObstacleCfg         shooters[MAX_SHOOTERS];

    QUERY_DRAW                 goal;                          // 목표 위치 추가

    s16                        char_start_x, char_start_y;    // 캐릭터 x, y축 위치
} StageCfg;


4. object 동작

  • platform, obstacle 동작 : x축과 y축으로 이동하는 속도를 반영하여 계산. 최대 범위를 만났을 때는 반전.
  • bullet 동작 : 화면 밖으로 나가거나 캐릭터에 맞았을 때 총알 비활성화. cos, sin함수를 사용하여 발사 각도 계산
  • 충돌 감지 함수 : 박스가 겹치면 1을 return.
static int IsCollide(QUERY_DRAW *a, QUERY_DRAW *b) { 	
    return (a->x < b->x + b->w) &&						
           (a->x + a->w > b->x) &&						
           (a->y < b->y + b->h) &&						
           (a->y + a->h > b->y);						
}


5. Delta rendering

  • Jittering 문제 원인 : Physics (물리적 계산)는 매우 빠르게 진행되고, object를 그리는 rendering은 매우 느림. (Rendering은 픽셀로 큰 정보를 넘겨야 하기 때문.)
  • 해결 방법 : delta rendering 방법 고안.
  • 기존 방식인 Full render는 20 x 20 px의 캐릭터를 2px 이동시킬 때, 20 x 20 영역을 지우고 20 x 20 영역을 다시 그림. 총 400px + 400px = 800px이 소요.
  • 새로운 방식인 delta render는 20 x 20 px의 캐릭터를 2px 이동시킬 때, 겹치는 20 x 2 영역만 지우고 20 x 2 영역만큼만 다시 그림. 총 40px + 40px = 80px이 소요. 즉, 훨씬 더 빠르게 rendering 가능.

6. BGM (PWM)

  • 원리: TIM3 PWM 주파수로 버저 음 생성, 음계는 12평균율 f = 130 × 2^(st/12)로 변환.
  • 구현: bgmTheme[(tone,dur)] 테이블을 SysTick에서 순환 → 타이머가 0되면 ARR만 갱신(PSC 고정, duty 50%)하여 다음 음 재생.
  • 결과: 게임 루프와 논블로킹으로 동작, 반복 재생 안정적. 음계 / 템포 변경은 테이블 교체만으로 처리.
unsigned short SemitoneToFreq(unsigned char st) {       // 주파수 계산 함수
    float f = 130.0f * powf(2.0f, st/12.0f);
    return (unsigned short)(f + 0.5f);
}

enum tone {
    C3=0,   C3_=1, D3=2,  D3_=3, E3=4,  F3=5,  F3_=6,
    G3=7,   G3_=8, A3=9,  A3_=10,B3=11,
    C4=12,  C4_=13,D4=14, D4_=15,E4=16, F4=17, F4_=18,
    G4=19,  G4_=20,A4=21, A4_=22,B4=23,
    C5=24,  C5_=25,D5=26, D5_=27,E5=28, F5=29, F5_=30,
    G5=31,  G5_=32,A5=33, A5_=34,B5=35
};

enum note{N16=BASE/4, N8=BASE/2, N4=BASE, N2=BASE*2, N1=BASE*4};

const Note bgmTheme[] = {
    { C5_, N8 }, { D5, N8 },  { E5, N8 }, { D5, N8 }, { G5, N4 }, { D5, N16 }, 
	{ B4, N8 },  { G4, N8 },  { B4, N8 }, { A4, N8 }, { G4_, N8}, { A4, N8 },
	{ E5, N4 },  { E5, N8 },  { F5_, N8}, { F5, N8 }, { E5, N8 }, { D5_, N8},
	{ D5, N8 },  { C5_, N16}, { D5, N16}, { E5, N8 }, { C5, N8 }, { B4, N8},
	{ A4_, N8},  { B4, N8 },  { E5, N8 }, { D5, N2 }
};

트러블 슈팅

1. 점프 동작 개선

  • 문제 : 입력 타이머 기반 점프 -> 에어 컨트롤(점프 후 좌우이동) 부자연스러움.
  • 접근 : Systic 타이머 기반 고정 dt physics 업데이트 + FSM 으로 수직, 수평 순으로 충돌 해소.
  • 처리 : 이동 플랫폼 상대속도 반영, 천장/벽 충돌 시 속도 클램핑 + 위치 보정.
  • 결과 : 점프 중 좌우 제어 일관성·응답성 향상, 머리 박음·벽 끼임 제거, 안정적 착지.

2. rendering 개선

  • 문제 : 전체 refresh 기반 rendering -> object 증가 시 jittering 발생.
  • 접근 : Delta rendering 적용 -> 이전 / 현재 위치의 차이 영역 (Dirty Rect)만 갱신.
  • 결과 : 불필요한 지우기 / 그리기 최소화 -> jittering 해소, 프레임 안정성 개선.

3. RAM overflow

  • 문제 : C:\CodeSourcery\Sourcery G++ Lite/bin/arm-none-eabi-ld: region RAM' overflowed by 10544 bytes 오류로 bin 파일 생성 실패.
  • 접근 : 메모리 사용량을 줄이기 위해 구조체 타입 및 매크로 상수 최적화
  • 결과 : 동일 기능 유지 상태에서 RAM 사용량 대폭 감소 -> RAM overflow 해소
// 매크로 상수 최적화
#define MAX_STAGE               (9)
#define MAX_PLATFORMS           (11)
#define MAX_OBSTACLES           (6)
#define MAX_SHOOTERS            (1)
#define MAX_BULLETS_PER_SHOOTER (5)

// 구조체 타입 최적화
typedef signed char s8; typedef unsigned char u8; typedef signed short u8; typedef unsigned short u16;

typedef struct {
  u16 x, y;
  u16 w, h;
  u8 ci;
} QUERY_DRAW;

typedef struct {
  s8  vx, vy;
  s16 min_x, max_x;
  s16 min_y, max_y;
} PlatformCfg;

결론 및 고찰

성과

  • 60 FPS를 안정적으로 유지하며 실시간 응답성을 확보함.
  • 전체 지우기 -> 다시 그리기 방식에서 delta renering으로 전환하여 jittering 해소.

한계

  • 수직 이동하는 플랫폼 탑승 동작은 미완성. 플랫폼 상대 속도 반영과 수직→수평 순서의 충돌 해소가 추가로 필요.
  • 시각적 완성도가 아쉬움. 배경, 스프라이트 애니메이션, 파티클/효과 등 그래픽 요소가 부족해 화면이 단조롭게 보임.

개선 방향

  • 이동 플랫폼 상호작용 고도화: 플랫폼 속도/가속도 상대좌표 적용, 머리·벽 충돌에 대한 연속 충돌 처리(continuous) 및 위치 보정 로직 보강.
  • 비주얼 업그레이드: 레이어드 배경(패럴랙스), 스프라이트 시트/프레임 애니메이션, 파티클/피드백 이펙트 추가.

GitHub Source