프로젝트

나도 메이커! 메이커스 여러분들의 작품/프로젝트를 공유하는 공간입니다.

아두이노 핑퐁게임기 만들기

2014-09-11 10:49:05

개요

1950년대 아날로그 시대에 나타난 최초의 게임들은 어떤 형태였는지 아시나요 ?
그중 하나를 소개하자면, 

출처 : <Tennis for Two>/1958 - 위키백과

1958년 미국의 물리학자 윌리엄 히긴보덤이 오실로스코프 상에서 개발한 게임<테니스 포 투>가 있습니다.
<테니스 포 투>는 전기적 신호를 계측하는 데 사용되었던 5인치 아날로그 오실로스코프와
간단한 전자회로를 조합하고 다이얼과 버튼으로 이루어진 간이 컨트롤러를 연결하여 만들어 졌습니다.

당시 '첨단 과학기술을 즐거운 유희의 형태로 이용할 수 있지 않을까'라는 목적만으로 개발하였으며,
자신의 연구소를 방문하는 손님들의 지루함을 달래는데에 사용되어졌다고 합니다.

출처 : 당시 테니스 포 투가 시연된 모습 - 위키백과

그후 1970년대 게임회사 '아타리'에서 '퐁'이라는 게임기를 출시하여 
엄청난 '대박'을 치게되고,  '아케이드 게임'시장을 형성하는데 큰 영향을 주게 됩니다.

출처 : 아케이드 게임 '퐁' - 위키백과

이번 프로젝트에서는 아두이노를 이용하여  '퐁'을 재현해 보고,
'아케이드 게임'의 향수를 느껴봅시다.


미리보기 동영상

시작 전 개념 이해하기

부품목록


NO 부품명 수량 상세설명
1 오렌지 보드 1 아두이노 호환보드
2 브레드보드 1 브레드보드
3 10kΩ 가변저항 2 가변저항
4 점퍼 케이블 15 점퍼 케이블
5 노키아 5110  LCD 1 LCD
부품명 오렌지 보드 브레드보드 점퍼케이블 10kΩ 가변저항 노키아 5110  LCD
파트 x1 x1 x15  x2 x1

하드웨어 Making

회로도

브레드보드 레이아웃

소프트웨어 Coding

아래의 코드를 아두이노에 업로드 합니다.
#define PIN_SCE   7 //노키아 5110의 핀연결 
#define PIN_RESET 6
#define PIN_DC    5
#define PIN_SDIN  4
#define PIN_SCLK  3

#define LCD_C     LOW
#define LCD_D     HIGH

#define LCD_X     84
#define LCD_Y     6

int barWidth = 16;//화면에서'바'의 길이를 설정합니다.
int barHeight = 4;//화면에서'바'의 높이를 설정합니다.
int ballPerimeter = 4;// 화면에서 공의 크기를 설정합니다.
unsigned int bar1X = 0;
unsigned int bar1Y = 0;
unsigned int bar2X = 0;
unsigned int bar2Y = LCD_Y * 8 - barHeight;
int ballX = 0;
int ballY = 0;
boolean isBallUp = false;
boolean isBallRight = true;

byte pixels[LCD_X][LCD_Y];
unsigned long lastRefreshTime;
const int refreshInterval = 150;
byte gameState = 1;
byte ballSpeed = 2;
byte player1WinCount = 0;
byte player2WinCount = 0;
byte hitCount = 0;

void setup(){
  //Serial.begin(9600);
  LcdInitialise();
  restartGame();
}

void loop(){
  unsigned long now = millis();
  if(now - lastRefreshTime > refreshInterval){
      update();
      refreshScreen();
      lastRefreshTime = now;  
  }  
}

void restartGame(){
   ballSpeed = 1;
   gameState = 1;
   ballX = random(0, 60);
   ballY = 20;
   isBallUp = false; 
   isBallRight = true;
   hitCount = 0;
}

void refreshScreen(){
  if(gameState == 1){
    for(int y = 0; y < LCD_Y; y++){
        for(int x = 0; x < LCD_X; x++){
           byte pixel = 0x00;
           int realY = y * 8;
           // draw ball if in the frame
           if(x >= ballX && x <= ballX + ballPerimeter -1 && ballY + ballPerimeter > realY && ballY < realY + 8 ){
             byte ballMask = 0x00;
             for(int i = 0; i < realY + 8 - ballY; i++){
               ballMask = ballMask >> 1;
               if(i < ballPerimeter)
                 ballMask = 0x80 | ballMask;
             }
             pixel = pixel | ballMask;
           }
           
           // '바'를 프레임안에 그려넣습니다.
           if(x >= bar1X && x <= bar1X + barWidth -1 && bar1Y + barHeight > realY && bar1Y < realY + 8 ){
             byte barMask = 0x00;
             for(int i = 0; i < realY + 8 - bar1Y; i++){
               barMask = barMask >> 1;
               if(i < barHeight)
                 barMask = 0x80 | barMask;
             }
             pixel = pixel | barMask;
           }
           
           if(x >= bar2X && x <= bar2X + barWidth -1 && bar2Y + barHeight > realY && bar2Y < realY + 8 ){
             byte barMask = 0x00;
             for(int i = 0; i < realY + 8 - bar2Y; i++){
               barMask = barMask >> 1;
               if(i < barHeight)
                 barMask = 0x80 | barMask;
             }
             pixel = pixel | barMask;
           }
           LcdWrite(LCD_D, pixel);
         }
    }
  } else if(gameState == 2){
      
  }
}

void update(){
  if(gameState == 1){
     int barMargin = LCD_X - barWidth;
     int pot1 = analogRead(A0); //가변저항의 값으로 '바'의 위치값을 지정합니다.
     int pot2 = analogRead(A1);
     bar1X = pot1 / 2 * LCD_X / 512;
     bar2X = pot2 / 2 * LCD_X / 512;
    
     if(bar1X > barMargin) bar1X = barMargin;
     if(bar2X > barMargin) bar2X = barMargin;
     
     //공이 움직이기 시작하면,
     if(isBallUp)
       ballY -= ballSpeed;
     else
       ballY += ballSpeed;
     if(isBallRight)
       ballX += ballSpeed;
     else
       ballX -= ballSpeed;
     //부딪힌 상태를 체크합니다.  
     if(ballX < 1){
       isBallRight = true;
       ballX = 0;
     }
     else if(ballX > LCD_X - ballPerimeter - 1){
       isBallRight = false;
       ballX = LCD_X - ballPerimeter;
     }
     if(ballY < barHeight){
       if(ballX + ballPerimeter >= bar1X && ballX <= bar1X + barWidth){ // ball bounces from bar1
         isBallUp = false;
         if(ballX + ballPerimeter/2 < bar1X + barWidth/2)
           isBallRight = false;
         else
           isBallRight = true;
         ballY = barHeight;
         if(++hitCount % 10 == 0 && ballSpeed < 5) 
           ballSpeed++;
       }else{ //플레이어2가 이긴경우
         gameState = 2;
         player2WinCount++;
       }
     }
     if(ballY + ballPerimeter > LCD_Y * 8 - barHeight){
       if(ballX + ballPerimeter >= bar2X && ballX <= bar2X + barWidth){ //ball bounces from bar2
         isBallUp = true; 
         if(ballX + ballPerimeter/2 < bar2X + barWidth/2)
           isBallRight = false;
         else
           isBallRight = true;
         ballY = LCD_Y * 8 - barHeight - ballPerimeter;
         if(++hitCount % 10 == 0 && ballSpeed < 5) 
           ballSpeed++;
       }else{ // 플레이어1이 이긴경우
         gameState = 2;
         player1WinCount++;
       }
     }
  }else if(gameState == 2){
      for(int i =0; i < 4; i++){
        LcdWrite(LCD_C, 0x0D );  //LCD반전
        delay(300);
        LcdWrite(LCD_C, 0x0C );  // LCD반전
        delay(300);
      }
      restartGame();
  }  
}

void LcdInitialise(void){
  pinMode(PIN_SCE, OUTPUT);
  pinMode(PIN_RESET, OUTPUT);
  pinMode(PIN_DC, OUTPUT);
  pinMode(PIN_SDIN, OUTPUT);
  pinMode(PIN_SCLK, OUTPUT);
  delay(200);
  digitalWrite(PIN_RESET, LOW);
  delay(500);
  digitalWrite(PIN_RESET, HIGH);
  LcdWrite(LCD_C, 0x21 );  // LCD 확장 명령
  LcdWrite(LCD_C, 0xB1 );  // LCD의 콘트라스트설정 
  LcdWrite(LCD_C, 0x04 );  // Set Temp coefficent. //0x04
  LcdWrite(LCD_C, 0x14 );  // LCD편향mode 1:48. //0x13
  LcdWrite(LCD_C, 0x0C );  // LCD노말 모드
  LcdWrite(LCD_C, 0x20 );
  LcdWrite(LCD_C, 0x80 ); //x값주소설정
  LcdWrite(LCD_C, 0x40 ); //y값주소설정 
  LcdWrite(LCD_C, 0x0C );
}

void LcdWrite(byte dc, byte data){
  digitalWrite(PIN_DC, dc);
  digitalWrite(PIN_SCE, LOW);
  shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
  digitalWrite(PIN_SCE, HIGH);
}


hihyo

아두이노,퐁,아타리,아케이드게임,가변저항,노키아5110,PCD8544
profile

김수현 2016-02-13 15:15:00

재밌겠네요 ㅎㅎ

profile

전홍연 2020-12-08 20:53:01

브레드보드 레이아웃대로 연결하고 스케치 그대로 설정해서 업로드해봤는데 디스플레이가 반짝거리기만 하는데 어떡하죠ㅠㅜㅠ