메뉴 전체보기

회원메뉴

MatTris 아케이드 > 게시판

본문 바로가기

쇼핑몰 검색

NO. 3570
제목 : MatTris 아케이드
2026-10-14 10:37

소개: MatTris 아케이드


소개: MatTris 아케이드


소개: MatTris 아케이드


소개: MatTris 아케이드


완전히 처음부터 직접 제작한 DIY 테트리스 콘솔, ‘MatTris’를 소개합니다.

그 옛날 휴대용 게임기들에는 여전히 제가 정말 좋아하는 무언가가 있습니다.

저는 1998년에 태어났는데, 테트리스를 처음 접한 건 50개, 100개, 때로는 999개나 되는 게임이 내장된 작은 단색 LCD 휴대용 게임기를 통해서였습니다. 대부분은 꽤 단순한 게임들이었지만, 왠지 모르게 몇 시간이고 빠져들어 놀곤 했습니다.

몇 년 전, 저는 현재 제가 다루고 있는 하드웨어를 활용해 그 느낌을 재현해 보기로 결심했습니다. 그 프로젝트가 바로 WS2812B LED 매트릭스와 Waveshare RP2040 Tiny를 사용해 제작한 소형 테트리스 기계인 ‘MatTris’가 되었습니다.

원본 프로젝트는 여기에서 확인해 보실 수 있습니다: MatTris – LED 매트릭스 위의 테트리스.

결과물이 꽤 마음에 들었지만, 한동안 사용해 보니 다른 방식으로 할 수도 있었을 부분들이 떠올랐습니다. 외장 디자인이 그중 하나였죠. 게다가 테트리스 게임만 들어 있는 건 기회를 좀 놓친 것 같은 느낌도 들었습니다.

그래서 전 전부 다시 만들기로 했습니다.

새 버전에서는 기본 개념은 그대로 유지하되, 소형 아케이드 콘솔 같은 느낌을 더 살리고 싶었습니다. 완전히 새로운 케이스를 디자인하고, 전체적인 구조를 일부 수정했으며, 동일한 LED 매트릭스로 구동되는 게임을 몇 가지 더 추가했습니다.

전자 부품은 여전히 WS2812B 미니 LED 매트릭스와 Waveshare RP2040 Tiny를 기반으로 합니다. 맞춤형 PCB가 LED 매트릭스, 버튼 및 전원 관리를 담당하며, 전체 시스템은 충전식 리튬폴리머(LiPo) 배터리로 구동됩니다.

사실 가장 큰 어려움은 이렇게 작은 화면에서 다양한 게임을 제대로 작동하게 만드는 것이었습니다. 14×20 매트릭스밖에 되지 않아 활용할 수 있는 공간이 많지 않았기 때문에, 게임을 재미있게 만들면서도 게임 자체와 조작 방식을 단순하게 유지해야 했습니다.

이 프로젝트는 기본적으로 두 번째 버전이지만, 훨씬 더 많은 내용이 담겨 있습니다.

그럼 제가 새로운 MatTris Arcade를 어떻게 만들었는지 한번 살펴보겠습니다.

그럼 시작해 볼까요.




STEP 1. 준비물


이 프로젝트에는 다음과 같은 자료가 사용되었습니다:

  1. 맞춤형 PCB (HQ NextPCB 제공)
  2. RP2040 Tiny Waveshare 보드
  3. WS2812B 2020 패키지 LED
  4. 100 nF 커패시터, 0603 패키지
  5. 10 uF 커패시터, 1206 패키지
  6. IP5306
  7. 10 uH 인덕터
  8. 표시 LED 0805 패키지
  9. USB Type-C 포트
  10. 6x6 SMD 푸시 버튼
  11. 2 옴, 1206 패키지
  12. RP2040 타이니 프로그래머
  13. 3D 프린팅으로 제작된 케이스




STEP 2. 하드웨어


하드웨어


MatTris Arcade의 메인 컨트롤러는 Waveshare RP2040 Tiny입니다. RP2040은 이러한 소규모 게임에 충분한 처리 능력을 갖추고 있을 뿐만 아니라 크기도 꽤 작기 때문에, 이전 버전에서 사용하던 것과 동일한 컨트롤러를 그대로 사용했습니다.

RP2040은 다음을 기반으로 합니다. 라즈베리 파이 RP2040 듀얼 코어 ARM Cortex-M0+, 최대 133 MHz로 작동합니다. 이 프로젝트에서는 코어 중 하나가 그다지 높은 부하를 받지 않지만, 여분의 처리 능력을 확보해 두었기 때문에 LED 업데이트, 버튼 입력, 게임 로직 등을 꽤 수월하게 처리할 수 있습니다.

또한 RP2040 Tiny는 PCB 전체 크기를 작게 유지해 주는데, 14 × 20 LED 매트릭스를 위해 가능한 한 많은 공간을 확보하고 싶었기 때문에 이 점이 매우 중요했습니다.

컨트롤러는 WS2812B LED를 제어하고, 모든 게임 버튼을 인식하며, 전체 게임 로직을 실행합니다. 코드와 6가지 게임이 모두 RP2040에 직접 저장되어 있으므로, 게임을 플레이하는 동안 휴대폰이나 외부 장치가 필요하지 않습니다.




STEP 3. PCB 설계


PCB 설계


PCB 설계


PCB 설계


PCB 설계


PCB 설계


PCB 설계 과정은 프로젝트의 회로도를 준비하는 것부터 시작됩니다. 이번 프로젝트의 경우, 저희는 이전에 WS2812B 2020 패키지 LED를 사용해 본 경험이 있습니다. 이 LED들은 크기가 작아 다루기가 그리 쉽지는 않지만, 바로 그 콤팩트한 폼 팩터 덕분에 매트릭스 기반 프로젝트에 이상적입니다.

저희는 이전 8×8 Tiny Matrix 프로젝트의 회로도를출발점으로 재사용했습니다. 8×8 레이아웃 대신, LED를 더 추가하여 설계를 14×20 매트릭스로 확장했습니다.

그런 다음 RP2040 Tiny 보드의 회로도에 6개의 푸시 버튼이 연결된 또 다른 섹션을 추가했습니다. 모든 버튼은 GND와 개별 GPIO 핀에 연결되어 있습니다. 버튼을 누르면 해당 GPIO 핀이 로우(Low) 상태가 되고, 마이크로컨트롤러는 이를 버튼 누름으로 인식합니다.

저희는 Waveshare의 RP2040 Tiny를 사용했습니다. 이 보드는 ESP-12F나 ESP32 모듈과 마찬가지로 PCB에 모듈 형태로 직접 장착되도록 설계된 RP2040 기반 SMD 개발 보드입니다. RP2040 Tiny는 캐슬레이트 패드를 갖추고 있어 PCB 뒷면에 납땜할 수 있으며, 전체 설계에 깔끔하게 통합될 수 있습니다.

자세한 내용은 해당 위키를 확인해 보세요—https://www.waveshare.com/wiki/RP2040-Tiny

전원 공급을 위해, 저희는 신뢰할 수 있는 IP5306—많은 이전 프로젝트에서 사용해 온 전원 관리 IC를 포함했습니다. 이 IC는 3.7V 리튬 전지에서 안정적인 5V 출력을 제공하며, 충전 표시, 배터리 부족 경고, 고전압/저전압 차단 기능 등 리튬 전지의 안전한 작동에 필수적인 기능을 모두 갖추고 있습니다.




STEP 4. NextPCB PCB 서비스


NextPCB PCB 서비스


NextPCB PCB 서비스


NextPCB PCB 서비스


NextPCB PCB 서비스


PCB 설계를 완료한 후, 거버 데이터를 다음으로 전송했습니다. HQ NextPCB, 그리고 흰색 실크스크린이 적용된 녹색 솔더 마스크를 주문했고, 이번에는 스텐실까지 주문했습니다.

주문을 한 후 일주일도 채 되지 않아 PCB와 스텐실을 모두 받았는데, PCB 품질이 꽤 훌륭했습니다.

또한, 제가 여러 프로젝트를 진행하는 데 큰 도움이 되었던 HQDFM에 대해서도 소개해 드리고 싶습니다. Huaqiu의 사내 엔지니어들이 개발한 무료 제조 설계(Design for Manufacturing) 소프트웨어인 HQDFM은 PCB 설계자들이 설계를 시각화하고 검증하는 방식에 혁명을 일으켰습니다.

NextPCB의 ‘Accelerator’ 캠페인을 활용하여 혁신적인 프로젝트를 위해 RP2040 기반 조립 완료된 PCB 2개를 무료로 받아보세요.

https://www.nextpcb.com/blog/rp2040-free-pcba-prototypes-nextpcb-accelerator

이 제안에는 물류 비용을 포함한 모든 비용이 포함되어 있어, 여러분의 아이디어를 현실로 구현하는 과정을 더욱 쉽고 경제적으로 만들어 드립니다. SMT 서비스는 비용이 많이 들 수 있지만, NextPCB가 그 어려움을 극복할 수 있도록 도와드립니다. 관련 프로젝트를 공유해 주시면 나머지는 NextPCB가 모두 처리해 드립니다. 여러분의 기술 창작물을 한 단계 발전시킬 수 있는 이 놀라운 기회를 놓치지 마세요!




STEP 5. 스텐실 공정


스텐실 공정


스텐실 공정


스텐실 공정


우리는 PCB에 솔더 페이스트를 도포하는 것으로 회로 조립 공정을 시작했지만, 약간의 문제가 발생했습니다. 이 프로젝트에 사용되는 부품의 수가 상당히 많아서, 패드의 수도 매우 많습니다. 이 때문에 솔더 페이스트 디스펜싱 주사기를 사용하는 것은 비실용적이었습니다. 대신, 이 회로 전용으로 제작된 맞춤형 스텐실을 사용했습니다.

스텐실은 기본적으로 PCB상의 부품 패드와 일치하도록 레이저로 절단된 슬롯과 구멍이 뚫린 얇은 금속판입니다. 스텐실을 사용하면 모든 패드에 솔더 페이스트를 고르게 도포할 수 있습니다. 회로를 대량 생산하는 산업 현장에서는 이러한 목적으로 스텐실이 일반적으로 사용됩니다.

  1. 먼저 여분의 PCB를 이용해 임시 PCB 및 스텐실 홀더를 제작했습니다. 이 장치를 통해 메인 PCB를 고정시켜 공정 중 움직이지 않도록 할 수 있었습니다.
  2. 스텐실의 개구부를 PCB 부품 패드와 정렬한 후, 스텐실 위에 솔더 페이스트를 도포하고 스크레이퍼를 사용하여 슬롯 전체에 고르게 펴 바랐습니다. 이 과정을 통해 각 부품 패드에 얇고 균일한 솔더 페이스트 층이 형성되어, 부품 실장을 위한 기판 준비가 완료되었습니다.




STEP 6. SMD 부품 실장 공정


SMD 부품 실장 공정


SMD 부품 실장 공정


SMD 부품 실장 공정


이제 또 다른 지루한 과정이 남아 있습니다. 바로 SMD 부품 실장입니다. 이 프로젝트에 사용된 LED의 수가 엄청나게 많기 때문에 이 과정이 지루한 것입니다. 총 280개의 LED와 그 밖의 여러 SMD 부품을 사용했으므로, 전체 부품 수는 300개가 넘습니다. 이 모든 부품을 일일이 수작업으로 실장해야 했습니다.

  1. 먼저 100 nF 디커플링 커패시터를 지정된 위치에 배치하는 것으로 작업을 시작했습니다. 픽 앤 플레이스 공정에는 ESD 방지 핀셋을 사용했습니다.
  2. 다음으로, 280개의 SMD RGB LED를 모두 패드에 장착하면서 각 LED의 방향이 올바른지 확인했습니다. 방향이 잘못되면 LED가 작동하지 않기 때문입니다.
  3. LED를 배치한 후, SMD 푸시 버튼과 전원 관리 회로와 관련된 모든 부품을 포함한 나머지 SMD 부품을 장착했습니다.
  4. 모든 부품을 배치한 후, 기판 전체를 조심스럽게 들어 올려 SMD 리플로우 핫플레이트 위에 올려놓았습니다. 핫플레이트는 PCB를 아래쪽에서 가열하여 솔더 페이스트의 용융 온도인 약 200°C까지 올립니다. 그 결과, 모든 부품이 패드에 단단히 납땜됩니다.




STEP 7. SMD LED 테스트


SMD LED 테스트


SMD LED 테스트


PCB 뒷면에 RP2040 보드를 장착하기 전에, 먼저 LED가 작동하는지 여부를 테스트했습니다. 만약 LED가 작동하지 않는 상태에서 RP2040이 이미 PCB 뒷면에 장착되어 있다면, 핫플레이트를 사용하여 LED 문제를 해결할 수 없게 될 것이기 때문입니다.

RP2040의 5V, GND, GPIO0 단자에 점퍼선을 연결했습니다. 그런 다음 이 점퍼선들을 XIAO SAMD21 M0 개발 보드에 연결했습니다.

아래 스케치를 XIAO에 업로드했는데, 이 스케치는 체인에 연결된 각 LED를 차례로 녹색으로 켜줍니다. 이 스케치는 Adafruit NeoPixel 라이브러리의 예제 스케치에서 가져온 것입니다.

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#endif
#define PIN 0
#define NUMPIXELS 280
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 50
void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
pixels.begin();
}
void loop() {
pixels.clear();
for(int i=0; i<NUMPIXELS; i++) { // For each pixel...
pixels.setPixelColor(i, pixels.Color(0, 150, 0));
pixels.show();
delay(DELAYVAL);
}
}




STEP 8. RP2040 조립


RP2040 조립


LED가 정상적으로 작동하는지 확인한 후, RP2040 조립 과정으로 넘어갔습니다. 이 과정은 먼저 기판을 뒤집고, 2040 Tiny를 제자리에 배치한 다음, 납땜 인두를 사용하여 캐슬레이트 단자가 있는 모든 패드를 납땜하는 것으로 시작됩니다.




STEP 9. 전원


전원


전원


전원


이전 버전에서 개선하고 싶었던 점 중 하나는 배터리 사용 시간이었습니다. 초기 MatTris 모델은 훨씬 작은 배터리를 사용했는데, 솔직히 말해 사용 시간이 그리 길지 않았습니다. 특히 LED가 계속 켜져 있을 때는 더욱 그랬죠.

MatTris Arcade에서는 2200mAh 리포 셀 2개를 대신 선택했습니다. 덕분에 콘솔의 배터리 용량이 훨씬 더 커져, 장시간 게임을 즐길 때 실제로 사용하기에 훨씬 더 실용적입니다.

이 두 개의 셀은 전원 관리 회로에 연결되어 있으며, 이 회로는 충전을 담당하고 시스템의 나머지 부분에 전원을 공급합니다.




STEP 10. 인클로저 설계


인클로저 설계


인클로저 설계


인클로저 설계


인클로저 설계


인클로저 설계


인클로저 설계


인클로저 설계


인클로저 설계


이번 버전에서는 Fusion 360으로 케이스를 완전히 새롭게 설계하기로 했습니다. 일반적인 3D 프린팅 전자기기 프로젝트보다는 소형 아케이드 콘솔에 더 가까운 느낌을 주고 싶었기 때문입니다.

먼저 PCB와 주요 부품을 모델링하여 실제 하드웨어를 기준으로 케이스를 설계할 수 있도록 했습니다. 케이스는 전자 부품을 수용하는 3D 프린팅 본체와 별도의 전면 패널, 이렇게 두 가지 주요 부분으로 구성됩니다.

전면 패널에는 검은색 착색 투명 아크릴 시트를 사용했습니다. 이 시트를 필요한 모양으로 레이저 커팅하고 모든 버튼이 들어갈 구멍을 만들었습니다. LED 매트릭스는 이 패널 바로 뒤에 배치되므로 기기의 전원이 꺼져 있을 때는 전면이 거의 완전히 검은색으로 보입니다. LED가 켜지면 착색된 시트 너머로 매트릭스가 비쳐 보여 정말 멋진 외관을 연출합니다.

버튼도 전면 패널을 관통하여 장착되므로 복잡한 장착 시스템 없이도 모든 부품이 제자리에 고정됩니다.

패널과 버튼, PCB가 제대로 맞도록 몇 가지 사소한 조정을 거친 후, 완성된 케이스의 모습이 마음에 들었습니다. 결과물은 재설계를 시작할 때 구상했던 아케이드 스타일의 작은 기기와 훨씬 더 비슷한 모습이 되었습니다.




STEP 11. 3D 프린팅


3D 프린팅


3D 프린팅


3D 프린팅


전자 부품 준비가 끝나자 케이스를 만들 차례가 되었습니다.

저는 본체를 블루 하이퍼 PLA 를 사용해 Anycubic Kobra S1에서 출력했습니다. 본체와 어울리도록 버튼도 같은 소재로 출력했습니다.

출력 결과는 꽤 깔끔했고 후처리를 많이 할 필요도 없었습니다. 출력물의 작은 잔여물을 정리한 뒤, 다음 작업인 전면 패널로 넘어가기 전에 PCB와 버튼을 대고 간단히 맞춰 보았습니다. adahi




STEP 12. 전면 패널 레이저 절단


전면 패널 레이저 절단


전면 패널 레이저 절단


전면 패널 레이저 절단


전면 패널에는 검은색 착색 투명 아크릴 시트를 사용했습니다. Fusion 360에서 디자인을 내보낸 뒤, 레이저 커터로 패널과 모든 버튼 구멍을 함께 절단했습니다.

저는 약 15 mm/s에서 70% 출력을 사용했으며, 한 번만 통과시켜도 시트를 깔끔하게 절단하기에 충분했습니다.

절단 후 보호 필름을 제거하고 인클로저에 패널을 대어 확인했습니다. 색상이 꽤 옅어서 LED 매트릭스와 그 뒤의 일부 부품이 여전히 보이지만, LED를 완전히 노출된 상태로 두는 것보다 전면이 훨씬 깔끔해 보입니다. 또한 버튼이 패널을 관통해 배치되어 있어 전면이 꽤 단순해 보입니다.




STEP 13. 최종 조립


최종 조립


최종 조립


최종 조립


최종 조립


최종 조립


모든 부품이 준비되자, 나는 모든 부품을 케이스 안에 넣기 시작했다.

PCB는 나사 몇 개로 고정되어 있어 케이스 안에 제대로 자리 잡고 있습니다. 배터리는 이미 전원 회로에 연결되어 있었고, 저는 양면 테이프를 사용하여 두 개의 전지를 PCB 아래쪽에 고정했습니다.

전면 패널의 경우, 가장자리를 따라 얇은 양면 테이프를 붙이고 착색 아크릴 시트를 케이스에 부착했습니다. 버튼들은 이미 패널에 장착되어 있었기 때문에, 패널을 제자리에 고정한 후에는 전면 작업이 거의 끝난 셈이었습니다.

케이스를 조립한 후에는 전원을 켜고 모든 것을 마지막으로 한 번 더 확인하는 것 외에는 할 일이 별로 남지 않았습니다.




STEP 14. 코드 부분


소프트웨어의 경우, 메뉴, 버튼, 화면 표시 및 게임 전환을 담당하는 메인 스케치를 그대로 유지했습니다. 실제 게임 로직은 별도의 .h 파일에 분리해 두었기 때문에, 메인 파일이 너무 복잡해지지 않으면서도 각 게임을 훨씬 수월하게 개발할 수 있었습니다.

이 버전에는 총 6개의 게임이 포함되어 있습니다: 테트리스, 스네이크, 퐁, 브레이크아웃, 카 도지, 스페이스 인베이더. 각 게임에는 세 가지 난이도가 있습니다.

버튼은 INPUT_PULLUP을 사용하므로, 일반적으로 HIGH 상태이며 눌리면 LOW로 바뀝니다. 또한, 이를 적용하지 않으면 한 번 누른 것이 여러 번 감지되는 경우가 있을 수 있으므로 간단한 디바운스 루틴도 추가했습니다.

struct Button {
int pin;
bool raw = false;
bool down = false;
bool pressed = false;
uint32_t changed = 0;

Button(int p): pin(p) {}

void begin() {
pinMode(pin, INPUT_PULLUP);
}

void poll(uint32_t now) {

pressed = false;

bool v = digitalRead(pin) == LOW;

if (v != raw) {
raw = v;
changed = now;
}

if (v != down && uint32_t(now - changed) >= 25) {
down = v;
pressed = v;
}
}
};




STEP 15. 주요 코드 및 메뉴


주요 코드 및 메뉴


메인 메뉴는 꽤 간결하게 구성되어 있습니다. 왼쪽 및 오른쪽 버튼으로 6개의 게임을 순차적으로 이동할 수 있으며, A 버튼을 누르면 난이도 선택 화면이 열립니다. B 버튼을 사용하면 실행 중이던 게임을 다시 시작할 수 있습니다.

또한 다음을 추가했습니다. A + B 단축키. 두 버튼을 3초 동안 누르고 있으면 메뉴가 열리는데, 이는 이미 게임 중에 있을 때 유용합니다.

const int MENU = 0;
const int MODES = 1;
const int PLAY = 2;
const int OVER = 3;

int screen = MENU;
int selected = 0;
int mode = 1;
int game = 0;
int difficulty = 1;

const char* labels[] = {
"TET",
"SNK",
"PON",
"BRK",
"CAR",
"INV"
};

const char* modes[] = {
"EZ",
"MED",
"HI"
};

게임 자체는 작은 디스패처를 통해 선택됩니다. 덕분에 메인 루프가 모든 게임에 대한 방대한 if 문 모음이 되는 것을 막을 수 있습니다.

void updateGame(uint32_t now) {

switch (game) {

case 0:
tetris(now);
break;

case 1:
snake(now);
break;

case 2:
pong(now);
break;

case 3:
breakout(now);
break;

case 4:
car(now);
break;

case 5:
invaders(now);
break;
}
}

게임을 시작하면, 선택한 게임에 따라 해당 게임의 변수들도 초기화됩니다.

void startGame(uint32_t now) {

game = selected;
difficulty = mode;
score = 0;
lives = 3;
won = false;

tick = moveAt = shotAt = now;

enter(PLAY);
hasGame = true;

if (game == 0) {
memset(board, 0, sizeof(board));
lines = 0;
spawn();
}

else if (game == 1) {
length = 4;
dx = nextDx = 1;
dy = nextDy = 0;
turnQueued = false;

for (int i = 0; i < 4; i++) {
sx[i] = 6 - i;
sy[i] = 10;
}

food();
}

else if (game == 2) {
paddle = enemy = 5;
bx = 7;
by = 10;
vx = vy = 1;
}

else if (game == 3) {
paddle = 5;
memset(bricks, 1, sizeof(bricks));
resetBall();
}

else if (game == 4) {
lane = 1;
roadStep = 0;

for (int i = 0; i < 6; i++)
carY[i] = -99;
}

else {
ship = 6;
wave = 0;
newWave(now);
}
}





STEP 16. 테트리스


테트리스


테트리스는 여전히 이 프로젝트에서 가장 복잡한 게임입니다. 각 테트로미노는 4×4 비트 패턴으로 저장됩니다. 조각을 이동하거나 회전하기 전에, fits() 함수는 새로운 위치가 보드의 가장자리나 이미 배치된 블록과 충돌하는지 확인합니다.

const uint16_t shapes[7] = {
0x00F0,
0x0066,
0x0072,
0x0036,
0x0063,
0x0071,
0x0074
};

bool block(int p, int rot, int x, int y) {

for (int i = 0; i < rot; i++) {
int old = x;
x = y;
y = 3 - old;
}

return (
shapes[p] &
(uint16_t(1) << (y * 4 + x))
) != 0;
}

조작 기능은 낙하 타이머와 별도로 처리됩니다. 즉, 선택한 난이도에 따라 자동 낙하 속도가 조절되는 동안에도 조각을 이동할 수 있습니다.

if (L.pressed && fits(px - 1, py, rotation))
px--;

if (R.pressed && fits(px + 1, py, rotation))
px++;

if (A.pressed || U.pressed) {

int r = (rotation + 1) % 4;

if (fits(px, py, r))
rotation = r;
}

if (B.pressed) {

while (fits(px, py + 1, rotation))
py++;

lockPiece();
}

조각이 잠기면, 프로그램은 각 행을 검사하여 완성된 줄을 찾아내어 제거합니다.

for (int y = 19; y >= 0; y--) {

bool full = true;

for (int x = 0; x < 14; x++)
if (!board[y][x])
full = false;

if (full) {

for (int j = y; j > 0; j--)
memcpy(board[j], board[j - 1], 14);

memset(board[0], 0, 14);

score += 100;
lines++;

y++;
}
}





STEP 17. 뱀


뱀


뱀의 경우, 뱀의 각 부분의 X, Y 좌표를 저장하기 위해 두 개의 배열을 사용했습니다. 움직일 때마다 몸통의 위치를 하나씩 이동시킨 다음 머리를 움직입니다.

int sx[280];
int sy[280];

int length = 4;

int dx = 1;
int dy = 0;

int nextDx = 1;
int nextDy = 0;

또한 두 방향 버튼을 매우 빠르게 연달아 누를 때 뱀이 즉시 자기 자신과 맞닿아 돌아오지 않도록 작은 회전 대기열을 추가했습니다.

if (U.pressed && dy != 1) {
nextDx = 0;
nextDy = -1;
turnQueued = true;
}

else if (D.pressed && dy != -1) {
nextDx = 0;
nextDy = 1;
turnQueued = true;
}

else if (L.pressed && dx != 1) {
nextDx = -1;
nextDy = 0;
turnQueued = true;
}

else if (R.pressed && dx != -1) {
nextDx = 1;
nextDy = 0;
turnQueued = true;
}

뱀이 먹이를 먹으면 몸길이가 늘어나고, 다음 먹이를 놓을 또 다른 빈 위치가 선택됩니다.

if (eat) {

length++;
score += 10;

food();
}

for (int i = length - 1; i > 0; i--) {
sx[i] = sx[i - 1];
sy[i] = sy[i - 1];
}

sx[0] = x;
sy[0] = y;




STEP 18. 퐁


퐁


Pong과 Breakout은 공과 패들에 관한 기본 로직 중 일부가 공통되므로, 공통되는 부분을 BallGamesCommon.h로 옮겼습니다.

'퐁' 게임에서 아래쪽 패들은 좌우 방향키로 조작되며, 위쪽 패들은 간단한 AI에 의해 움직입니다.

if (L.pressed && paddle > 0)
paddle--;

if (R.pressed && paddle < 10)
paddle++;

if (bx > enemy + 1 && enemy < 10)
enemy++;

else if (bx < enemy + 1 && enemy > 0)
enemy--;

공의 위치는 X 및 Y 방향의 속도를 사용하여 업데이트됩니다. 공이 측면에 닿으면 X 방향이 반전됩니다.

int nx = bx + vx;
int ny = by + vy;

if (nx < 0 || nx > 13) {
vx = -vx;
nx = bx + vx;
}

패들을 맞히면 Y 방향이 반전되고 점수가 증가합니다.





STEP 19. 브레이크아웃


브레이크아웃


브레이크아웃은 동일한 패들과 공 시스템을 사용하지만, AI 패들 대신 화면 상단에는 벽돌 28개가 있습니다.

각 벽돌은 ‘존재’ 또는 ‘지워짐’ 상태로만 저장되므로, 로직이 상당히 간결해집니다.

uint8_t bricks[4][7];

...

if (
ny >= 2 &&
ny <= 5 &&
bricks[ny - 2][nx / 2]
) {

bricks[ny - 2][nx / 2] = 0;

score += 10;

vy = -vy;
ny = by + vy;
}

공을 칠 때마다 프로그램은 벽돌이 남아 있는지 확인합니다. 남아 있는 벽돌이 없으면 게임이 승리하며 종료됩니다.




STEP 20. 로드 파이터


로드 파이터


'Car Dodge'는 세 개의 차선을 사용합니다. 플레이어의 차량은 맨 아래쪽에 머무르는 반면, 다른 차량들은 위쪽에서 등장하여 아래로 내려옵니다.

if (L.pressed && lane > 0)
lane--;

if (R.pressed && lane < 2)
lane++;

게임 내에 장애물 슬롯을 6개 남겨두었습니다. 슬롯 중 하나가 비면, 상단에 다른 차량을 생성할 수 있습니다.

for (int i = 0; i < 6; i++) {

if (carY[i] != -99) {

carY[i]++;

if (
carLane[i] == lane &&
carY[i] + 2 >= 16 &&
carY[i] <= 18
) {
finish(false);
return;
}

if (carY[i] > 19) {
carY[i] = -99;
score++;
}
}
}

난이도는 주로 차량의 이동 속도에 영향을 미칩니다.




STEP 21. 스페이스 인베이더


스페이스 인베이더


'스페이스 인베이더'의 경우, 이 게임을 14 × 20 행렬에서 작동하게 하려면 원래의 아이디어를 상당히 단순화해야만 했습니다. 외계인들은 3 × 3 배열로 배치되어 좌우로 움직입니다.

bool aliens[3][3];

int alienX = 1;
int alienY = 2;
int alienDir = 1;

int ship = 6;

int bulletX = 0;
int bulletY = -1;

int enemyX = 0;
int enemyY = -1;

플레이어는 우주선을 가로로 이동시키며, 한 번에 하나의 탄환만 활성화할 수 있습니다.

if (L.pressed && ship > 1)
ship--;

if (R.pressed && ship < 12)
ship++;

if (
A.down &&
bulletY < 0 &&
uint32_t(now - shotAt) >= 220
) {

bulletX = ship;
bulletY = 17;

shotAt = now;
}

외계인들은 타이머가 만료될 때마다 이동합니다. 가장자리에 도달하면 전체 편대가 방향을 바꾸고 아래로 이동합니다.

if (
alienX + alienDir < 0 ||
alienX + alienDir + 10 > 13
) {

alienDir = -alienDir;
alienY++;
}

else {
alienX += alienDir;
}

탄환이 외계인과 겹치면 해당 외계인은 제거되고 점수가 올라갑니다. 9마리가 모두 사라지면 새로운 웨이브가 시작됩니다.

if (
bulletX >= xx &&
bulletX < xx + 3 &&
bulletY >= yy &&
bulletY < yy + 2
) {

aliens[y][x] = false;
bulletY = -1;

score += 10;

return;
}

이를 통해 독자는 각 게임을 어떻게 제작했는지 이해할 수 있을 만큼 실제 구현 내용을 알 수 있으며, 전체 .ino 및 .h 파일은 프로젝트

좋아요 0
게시판

회사명 공방유니언 주소 각 공방 주소 참조
사업자 등록번호 0000 대표 공방유니언 전화 019-60105846 팩스 0000 이메일 gongbangunion@gmail.com
통신판매업신고번호 0000 개인정보관리책임자 공방유니언
Copyright © 2017 공방유니언. All Rights Reserved.