LCD 힐 런 v2 – 16x2 LCD와 부저를 활용한 인터랙티브 아두이노 러너 게임
부품 및 소모품
| 부품 |
수량 |
비고 |
| 아두이노 메가 2560 |
1 |
아두이노 우노로 대체 가능 (코드에서 DUCK_PIN을 3으로 변경하고 배선 수정 필요) |
| 점퍼 와이어 (일반) |
약 20개 |
|
| 스파크펀 푸시버튼 스위치 12mm |
2 |
점프 및 숙이기 버튼용 |
| 저항 10k 옴 |
2 |
버튼 풀다운용 |
| 아다프루트 표준 LCD - 16x2 백색/청색 |
1 |
|
| 로터리 포텐셔미터 10k 옴 |
1 |
LCD 대비 조절용 |
| 부저 (패시브 권장) |
1 |
필수는 아니나 효과음용으로 추천 |
| 브레드보드 (풀사이즈 권장) |
1 |
|
앱 및 온라인 서비스
프로젝트 소개
아두이노를 막 배운 12살 학생이 만든 재미있는 러너 게임입니다. 여러 프로젝트 허브 글을 참고하여 영감을 얻었고, 디버깅도 그리 오래 걸리지 않았습니다. 코드는 나름 깔끔하게 작성되었으니 즐겨주세요! 질문이나 피드백은 댓글로 남겨주세요.
라이브러리 설치: GitHub 저장소에서 라이브러리를 다운로드하여 아두이노 라이브러리 폴더에 넣으세요. 라이브러리를 찾을 수 없다는 오류가 나면 스케치 > 라이브러리 포함 메뉴를 이용하세요.
회로도
회로도 파일: hillrun2_zp0SI4wns3.fzz

우노 사용 시: 코드에서 const int DUCK_PIN = 18;을 3으로 변경하고, 숙이기 버튼을 핀 18에서 디지털 핀 3으로 다시 배선하세요.
코드 설명
게임 로직은 인터럽트를 사용하여 버튼 입력을 처리합니다. 주요 기능:
- 점프 버튼 (핀 2): 상승 에지 인터럽트로 감지, 장애물(언덕) 넘기
- 숙이기 버튼 (핀 18/3): 변화 인터럽트로 감지, 까마귀 피하기
- 부저 (핀 5): 점프/숙이기/게임오버 효과음 재생
- LCD (핀 7~12): 16x2 화면에 캐릭터, 장애물, 점수 표시
- 커스텀 문자: 플레이어(걷기/점프/숙이기), 언덕, 까마귀(날개짓 애니메이션) 정의
핵심 게임 루프
void loop() {
// 플레이어 Y 위치 결정 (점프 중이면 1행, 아니면 0행)
// 스프라이트 그리기
// 언덕 이동 및 충돌 체크
// 까마귀 이동 및 충돌 체크
// 점수 증가, 게임오버 처리
delay(TICKSPEED); // 90ms 간격
}
인터럽트 서비스 루틴
void seeJumping() { // 점프 버튼 눌림
if (jumpPhase > JUMP_LENGTH + 2 && !ducking) {
jumpPhase = 0;
tone(BUZZER_PIN, JUMP_PITCH, JUMP_PITCH_DURATION);
}
}
void seeDucking() { // 숙이기 버튼 상태 변화
ducking = digitalRead(DUCK_PIN);
if (ducking) {
jumpPhase = JUMP_LENGTH; // 점프 취소
tone(BUZZER_PIN, DUCK_PITCH, DUCK_PITCH_DURATION);
}
}
전체 소스 코드
아래는 완전한 아두이노 스케치입니다. pitches.h 라이브러리가 필요합니다.
/*
* Copyright (c) 2020 by Miles C.
* 인터럽트를 활용해 개선된 힐 런 게임.
* 주의: DUCK_PIN은 인터럽트 가능 핀인 18번(메가) 또는 3번(우노)으로 설정!
* 배선: 아두이노 5V -> 브레드보드 전원 버스, GND -> 그라운드 버스
* 점프 버튼: 10k 풀다운, 핀 2, 반대편 5V
* 숙이기 버튼: 10k 풀다운, 핀 18(또는 3), 반대편 5V
* 패시브 부저: 한 쪽 GND, 다른 쪽 PWM 핀 5
* 16x2 LCD: 표준 배선 (D4-D7: 9-12, E: 8, RW: GND, RS: 7, V0: 포텐셔미터)
*/
#include
#include "pitches.h"
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
const int JUMP_PIN = 2;
const int BUZZER_PIN = 5;
const int DUCK_PIN = 18; // 우노 사용 시 3으로 변경
const int JUMP_PITCH = 2700;
const int JUMP_PITCH_DURATION = 50;
const int DUCK_PITCH = 1350;
const int DUCK_PITCH_DURATION = 50;
const int DIE_PITCH = 200;
const int DIE_PITCH_DURATION = 500;
const int TICKSPEED = 90;
const int JUMP_LENGTH = 3;
// 커스텀 문자 비트맵 정의 (생략)...
volatile int jumpPhase = JUMP_LENGTH + 1;
int gameTick = 0;
int crowX = 40;
int hillX = 25;
bool playerY = 0;
volatile bool ducking = LOW;
bool loopBreaker = 1;
bool crowGo = 0;
int score = 0;
void setup() {
pinMode(JUMP_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
lcd.begin(16, 2);
// 커스텀 문자 등록 (0:언덕, 1:걷기1, 2:걷기2, 3:점프, 4:숙이기, 5:까마귀1, 6:까마귀2)
lcd.createChar(0, hill);
lcd.createChar(1, stickStep1);
lcd.createChar(2, stickStep2);
lcd.createChar(3, stickJump);
lcd.createChar(4, stickDuck);
lcd.createChar(5, crow1);
lcd.createChar(6, crow2);
attachInterrupt(digitalPinToInterrupt(JUMP_PIN), seeJumping, RISING);
attachInterrupt(digitalPinToInterrupt(DUCK_PIN), seeDucking, CHANGE);
}
void loop() {
playerY = 0;
if (jumpPhase < JUMP_LENGTH) playerY = 1;
drawSprites();
loopBreaker = 1;
// 언덕 및 까마귀 충돌 로직 (생략)...
delay(TICKSPEED);
}
// 나머지 함수들 (endGame, drawSprites, seeJumping, seeDucking) 생략...