산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

부품 및 소모품

부품 수량
마이크로칩 ATtiny85 1
그래픽 OLED 128x64 1
스파크펀 마이크로B USB 브레이크아웃 1
아두이노 나노 R3 (프로그래밍용) 1

필요한 도구 및 장비

앱 및 온라인 서비스

프로젝트 개요

영감과 이전 프로젝트

2017년 12월, 아두이노 나노와 OLED, 버튼 2개로 휴대용 퐁(Pong) 콘솔을 만들었습니다.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

당시엔 괜찮았지만 크고 번거로웠습니다. 최근 과거 프로젝트를 재구성하며 스네이크 게임이 돌아가는 초소형 콘솔을 만들기로 했습니다.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

선정된 부품

소형화를 위해 아두이노 개발 보드 대신 ATtiny85를 선택했습니다.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

충분한 I/O(ADC 2개, I2C 포트, GPIO 1개)를 제공합니다. 조작은 심플한 2축 조이스틱/스위치 모듈로, 신호 핀 3개만 필요합니다.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

디스플레이는 DF로봇의 128x64 OLED로, 크기는 작지만 해상도가 충분합니다.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

시스템 납땜 조립

ATtiny85를 SOP-8 to DIP-8 브레이크아웃 PCB에 장착해 소형 퍼프보드에 납땜. 프로그래밍용 헤더와 화면용 헤더를 달고, 조이스틱 핀을 연결한 뒤 마이크로 USB 브레이크아웃 보드로 전원 공급선을 연결했습니다.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기
ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

인클로저 설계 (퓨전 360)

퓨전 360에서 실제 부품들을 배치하고 주변을 감싸는 인클로저를 설계했습니다. 80년대 아케이드 박스 느낌을 내면서도 초소형 크기 유지.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기
ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기
ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기
ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

게임 프로그래밍

스네이크는 프로그래밍하기 쉬운 게임입니다. RAM 절약을 위해 최대 뱀 길이를 30으로 제한(먹이 29개 먹으면 승리). 뱀 몸통 좌표를 저장하는 2차원 배열 사용.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

머리가 이동하면 이전 위치들이 연쇄적으로 아래로 전달됩니다. 먹이 섭취 시 새 세그먼트가 랜덤 위치에 생성. 충돌 검사는 각 세그먼트 좌표와 머리 좌표 비교, 벽 충돌도 감지.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

스네이크 플레이

전원 켜고 화면 로드 대기 후 조이스틱으로 뱀 조종하며 먹이 먹기. 작은 패키지에 담긴 재미있는 시간 때우기 게임입니다.

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

코드

ATTiny85 Code C/C++
// U8g2lib 라이브러리 설치 필요
//#include <TinyWireM.h>
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g(U8G2_R0, /* clock=*/ 2, /* data=*/ 0, /* reset=*/ U8X8_PIN_NONE);
#define MAX_LENGTH 30
#define X 0
#define Y 1
#define JOYSTICK_X 2
#define JOYSTICK_Y 3
#define DIR_THRESH 300
uint8_t segmentPositions[MAX_LENGTH][2];
uint8_t headPosition[2] = {63, 31};
uint8_t foodPosition[2];
uint8_t tempPosition0[2];
uint8_t tempPosition1[2];
int segmentLength = 1;
void gameUpdate();
enum DIRECTIONS { RIGHT, DOWN, LEFT, UP } currentDirection;

void setup() {
  //TinyWireM.begin();
  u8g.begin();
  u8g.setPowerSave(0);
  pinMode(JOYSTICK_X, INPUT);
  pinMode(JOYSTICK_Y, INPUT);
  randomSeed(analogRead(0));
  beginGame();
}

void loop() {
  u8g.firstPage();
  do {
    gameUpdate();
    u8g.setColorIndex(1);
  } while(u8g.nextPage());
}

void beginGame(){
  currentDirection = RIGHT;
  spawnFood();
  delay(1000);
}

bool checkCollisions(){
  for(int i=1; i<segmentLength; i++){
    if(segmentPositions[i][X] == headPosition[X]){
      if(segmentPositions[i][Y] == headPosition[Y]) return 1;
    }
  }
  if(headPosition[X] <= 0 || headPosition[X] >= 128) return 1;
  else if(headPosition[Y] <= 0 || headPosition[Y] >= 64) return 1;
  return false;
}

void spawnFood(){
  int randomX = random(5, 123);
  int randomY = random(5, 60);
  foodPosition[X] = randomX;
  foodPosition[Y] = randomY;
}

void checkFoodEaten(){
  if(headPosition[X] == foodPosition[X] || headPosition[Y] == foodPosition[Y]){
    segmentLength += 1;
    spawnFood();
  }
}

void updateDirection(){
  int joy_x_val = analogRead(JOYSTICK_X);
  int joy_y_val = analogRead(JOYSTICK_Y);
  if(joy_x_val <= DIR_THRESH) currentDirection = LEFT;
  else if(joy_x_val >= 1023-DIR_THRESH) currentDirection = RIGHT;
  else if(joy_y_val <= DIR_THRESH) currentDirection = UP;
  else if(joy_y_val >= 1023-DIR_THRESH) currentDirection = DOWN;
}

void displaySegments(){
  for(int segment=0; segment < segmentLength; segment++){
    u8g.drawPixel(segmentPositions[segment][X], segmentPositions[segment][Y]);
  }
  u8g.drawPixel(foodPosition[X], foodPosition[Y]);
}

void gameUpdate(){
  updateDirection();
  tempPosition0[X] = headPosition[X];
  tempPosition0[Y] = headPosition[Y];
  switch(currentDirection){
    case UP: headPosition[Y] -= 1; break;
    case DOWN: headPosition[Y] += 1; break;
    case LEFT: headPosition[X] -= 1; break;
    case RIGHT: headPosition[X] += 1; break;
  }
  for(int segment=1; segment < segmentLength; segment+= 2){
    tempPosition1[X] = segmentPositions[segment][X];
    tempPosition1[Y] = segmentPositions[segment][Y];
    segmentPositions[segment][X] = tempPosition0[X];
    segmentPositions[segment][Y] = tempPosition0[Y];
    tempPosition0[X] = segmentPositions[segment+1][X];
    tempPosition0[Y] = segmentPositions[segment+1][Y];
    segmentPositions[segment+1][X] = tempPosition1[X];
    segmentPositions[segment+1][Y] = tempPosition1[Y];
  }
  displaySegments();
  checkFoodEaten();
  if(checkCollisions()) endGame();
  if(segmentLength>= MAX_LENGTH) endGame();
  delay(50);
}

void endGame(){
  segmentLength = 1;
  headPosition[0] = 63;
  headPosition[1] = 31;
  beginGame();
}

커스텀 파트 및 인클로저

(파일 별도 제공)

회로도

ATtiny85 미니 아케이드: 스네이크 게임 - 휴대용 OLED 게임기 만들기

제조공정

  1. 아두이노 나노 33 BLE 센스에서 Edge Impulse로 구현하는 TinyML 언어 감지기
  2. 아두이노 나노와 MPU-6050으로 실시간 자이로스코프 게임 만들기
  3. 아두이노 디지털 주사위: LCD 기반 랜덤 넘버 제너레이터 만들기
  4. 아두이노 나노로 37개 LED 룰렛 게임 만들기
  5. 휴대용 거리 감지기: HC-SR04, 18650 배터리, 3D 프린트 케이스의 아두이노 나노 DIY 키트
  6. 아두이노 나노로 만드는 오디오 반응형 책상 무드등 (주소 지정 LED)
  7. 구리 도금 프로젝트: 아두이노 우노로 만드는 스마트 전기분해 시스템
  8. 아두이노 나노와 네오픽셀 매트릭스로 만드는 네온 퐁 게임
  9. 아두이노 시프트 레지스터로 화려한 라이트 시퀀스 만들기
  10. 스마트 플러그: 120V 아두이노 기반 스마트 콘센트 (RTC 내장)