정보나눔

오픈소스하드웨어 프로젝트에 대한 다양한 정보를 나누는 공간입니다.

아두이노 문의 Klant님 답변 부탁드립니다!!!
장호근 | 2016-12-09

안녕하세요 제가 이번에 학교에서 하는 프로젝트로 아두이노를 배우면서 하고있습니다.

제가 테트리스 게임을 만들고있는데 그냥 게임만하는 것이아니라 시간이 지날수록 점점 빠르게 내려가게 하고

싶은데 코드를 어디부분을 어떻게 손봐줘야하는지 힘든부분이 있어 문의드려봅니다! 부탁드립니다!

 

#define GRID_W           (8)
#define GRID_H           (8)
// max size of each tetris piece
#define PIECE_W          (4)
#define PIECE_H          (4)
#define JOYSTICK_DEAD_ZONE  (10)
// a list of anode pins, sorted by top to bottom of the grid
const int anode[8] = { 8, 13, 7, 11, 0, 6, 1, 4 };
// a list of cathode pins, sorted by left to right of the grid
const int cathode[8] = { 12, 2, 3, 9, 5, 10, 14, 15 };
// translate the pins on the LED panel to pins on the Arduino
const int arduino_to_grid[16] = {5, 4, 3, 2, 14, 15, 16, 17, 13, 12, 11, 10, 9, 8, 7, 6};

// 1 color drawings of each piece in each rotation.
// Each piece is max 4 wide, 4 tall, and 4 rotations.
const char piece_I[] = {
  0, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  1, 1, 1, 1,
  0, 1, 0, 0,
  0, 1, 0, 0,
  0, 1, 0, 0,
  0, 1, 0, 0,

  0, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  1, 1, 1, 1,
  0, 1, 0, 0,
  0, 1, 0, 0,
  0, 1, 0, 0,
  0, 1, 0, 0,
};
const char piece_L1[] = {
  0, 1, 0, 0,
  0, 1, 0, 0,
  0, 1, 1, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  1, 1, 1, 0,
  1, 0, 0, 0,
  0, 0, 0, 0,

  1, 1, 0, 0,
  0, 1, 0, 0,
  0, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 1, 0,
  1, 1, 1, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
};
const char piece_L2[] = {
  0, 1, 0, 0,
  0, 1, 0, 0,
  1, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  1, 0, 0, 0,
  1, 1, 1, 0,
  0, 0, 0, 0,

  0, 1, 1, 0,
  0, 1, 0, 0,
  0, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  1, 1, 1, 0,
  0, 0, 1, 0,
  0, 0, 0, 0,
};
const char piece_T[] = {
  0, 0, 0, 0,
  1, 1, 1, 0,
  0, 1, 0, 0,
  0, 0, 0, 0,
  0, 1, 0, 0,
  1, 1, 0, 0,
  0, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  0, 1, 0, 0,
  1, 1, 1, 0,
  0, 0, 0, 0,
  0, 1, 0, 0,
  0, 1, 1, 0,
  0, 1, 0, 0,
  0, 0, 0, 0,
};
const char piece_S1[] = {
  1, 0, 0, 0,
  1, 1, 0, 0,
  0, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  0, 1, 1, 0,
  1, 1, 0, 0,
  0, 0, 0, 0,
  1, 0, 0, 0,
  1, 1, 0, 0,
  0, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  0, 1, 1, 0,
  1, 1, 0, 0,
  0, 0, 0, 0,
};
const char piece_S2[] = {
  0, 1, 0, 0,
  1, 1, 0, 0,
  1, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  1, 1, 0, 0,
  0, 1, 1, 0,
  0, 0, 0, 0,

  0, 1, 0, 0,
  1, 1, 0, 0,
  1, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  1, 1, 0, 0,
  0, 1, 1, 0,
  0, 0, 0, 0,
};
const char piece_O[] = {
  1, 1, 0, 0,
  1, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,

  1, 1, 0, 0,
  1, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,

  1, 1, 0, 0,
  1, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,

  1, 1, 0, 0,
  1, 1, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
};

// how many kinds of pieces
#define NUM_PIECE_TYPES  (7)
// An array of pointers!
const char *pieces[NUM_PIECE_TYPES] = {
  piece_S1,
  piece_S2,
  piece_L1,
  piece_L2,
  piece_O,
  piece_T,
  piece_I,
};
// length of the chances array
#define MAX_CHANCES (13)
// chances[12] is 6,
// pieces[chances[12]] is piece_I,
// pieces[chances[12]][8] is 1.
const char chances[MAX_CHANCES] = {
  0, 0,
  1, 1,
  2, 2,
  3, 3,
  4, 4,
  5, 5,
  6,  // 1 in 13 chance of an I piece
};
//--------------------------------------------------------------------------------
// GLOBALS
//--------------------------------------------------------------------------------
// this is how arduino remembers what the button was doing in the past,
// so arduino can tell when it changes.
int old_button = 0;
// so arduino can tell when user moves sideways
int old_px = 0;
// so arduino can tell when user tries to turn
int old_i_want_to_turn = 0;
// this is how arduino remembers the falling piece.
int piece_id;
int piece_rotation;
int piece_x;
int piece_y;
// this controls how fast the player can move.
long last_move;
long move_delay = 100; // 100ms = 5 times a second
// this controls when the piece automatically falls.
long last_drop;
long drop_delay = 1500; // 500ms = 2 times a second
// this is how arduino remembers where pieces are on the grid.
char grid[8 * 8];
//--------------------------------------------------------------------------------
// METHODS
//--------------------------------------------------------------------------------
// I want to turn on the column N from the left.
// this figures out which pin on the LED that is,
// then figures out which pin on the arduino matches that LED pin.
// two translations!
int out(int x) {
  return arduino_to_grid[anode[x]];
}
// I want to turn on the row N from the top.
// this figures out which pin on the LED that is,
// then figures out which pin on the arduino matches that LED pin.
// two translations!
int in(int y) {
  return arduino_to_grid[cathode[y]];
}
// I want to turn on point P(x,y), which is X from the left and Y from the top.
// I might also want to hold it on for us microseconds.
void p(int x, int y, int us) {
  // don't try to turn on a light that doesn't exist
  //if(x<0 || x>GRID_W) return;
  //if(y<0 || y>GRID_H) return;

  // now light it
  digitalWrite(out(x), HIGH);
  digitalWrite(in(y), LOW);
  delayMicroseconds(us);
  digitalWrite(in(y), HIGH);
  digitalWrite(out(x), LOW);
}
// grid contains the arduino's memory of the game board, including the piece that is falling.
void draw_grid() {
  int x, y;
  for (y = 0; y < GRID_H; ++y) {
    for (x = 0; x < GRID_W; ++x) {
      if ( grid[y * GRID_W + x] != 0 ) {
        p(x, y, 150);
      }
    }
  }
}
void choose_new_piece() {
  // make the chances array longer to change the odds of different pieces appearing.
  piece_id = chances[rand() % MAX_CHANCES];
  // always start the piece top center.
  piece_y = -4; // -4 squares off the top of the screen.
  piece_x = 3;
  // always start in the same orientation.
  piece_rotation = 0;
}
void erase_piece_from_grid() {
  int x, y;

  const char *piece = pieces[piece_id] + (piece_rotation * PIECE_H * PIECE_W);

  for (y = 0; y < PIECE_H; ++y) {
    for (x = 0; x < PIECE_W; ++x) {
      int nx = piece_x + x;
      int ny = piece_y + y;
      if (ny < 0 || ny > GRID_H) continue;
      if (nx < 0 || nx > GRID_W) continue;
      if (piece[y * PIECE_W + x] == 1) {
        grid[ny * GRID_W + nx] = 0; // zero erases the grid location.
      }
    }
  }
}
void add_piece_to_grid() {
  int x, y;

  const char *piece = pieces[piece_id] + (piece_rotation * PIECE_H * PIECE_W);

  for (y = 0; y < PIECE_H; ++y) {
    for (x = 0; x < PIECE_W; ++x) {
      int nx = piece_x + x;
      int ny = piece_y + y;
      if (ny < 0 || ny > GRID_H) continue;
      if (nx < 0 || nx > GRID_W) continue;
      if (piece[y * PIECE_W + x] == 1) {
        grid[ny * GRID_W + nx] = 1; // zero erases the grid location.
      }
    }
  }
}
// Move everything down 1 space, destroying the old row number y in the process.
void delete_row(int y) {
  int x;
  for (; y > 0; --y) {
    for (x = 0; x < GRID_W; ++x) {
      grid[y * GRID_W + x] = grid[(y - 1) * GRID_W + x];
    }
  }
  // everything moved down 1, so the top row must be empty or the game would be over.
  for (x = 0; x < GRID_W; ++x) {
    grid[x] = 0;
  }
}
void remove_full_rows() {
  int x, y, c;
  for (y = 0; y < GRID_H; ++y) {
    // count the full spaces in this row
    c = 0;
    for (x = 0; x < GRID_W; ++x) {
      if ( grid[y * GRID_W + x] > 0 ) c++;
    }
    if (c == GRID_W) {
      // row full!
      delete_row(y);
    }
  }
}
void try_to_move_piece_sideways() {
  // what does the joystick angle say
  int dx = map(analogRead(4), 0, 1023, 500, -500);
  Serial.print(dx);
  int new_px = 0;
  // is the joystick really being pushed?
  if (dx > JOYSTICK_DEAD_ZONE) {
    new_px = 1;
  }
  if (dx < -JOYSTICK_DEAD_ZONE) {
    new_px = -1;
  }
  if (new_px != old_px && piece_can_fit(piece_x + new_px, piece_y, piece_rotation) == 1) {
    piece_x += new_px;
  }
  old_px = new_px;
}
void try_to_rotate_piece() {
  int i_want_to_turn = 0;

  // what does the joystick button say
  int new_button = digitalRead(1);
  // if the button state has just changed AND it is being let go,
  if ( new_button > 0 && old_button != new_button ) {
    i_want_to_turn = 1;
  }
  old_button = new_button;

  // up on joystick to rotate
  int dy = map(analogRead(5), 0, 1023, -500, 500);
  if (dy < -JOYSTICK_DEAD_ZONE) i_want_to_turn = 1;

  if (i_want_to_turn == 1 && i_want_to_turn != old_i_want_to_turn) {
    // figure out what it will look like at that new angle
    int new_pr = ( piece_rotation + 1 ) % 4;
    // if it can fit at that new angle (doesn't bump anything)
    if (piece_can_fit(piece_x, piece_y, new_pr)) {
      // then make the turn.
      piece_rotation = new_pr;
    }
  }
  old_i_want_to_turn = i_want_to_turn;
}
// can the piece fit in this new location?
int piece_can_fit(int px, int py, int pr) {
  if ( piece_off_edge(px, py, pr) ) return 0;
  if ( piece_hits_rubble(px, py, pr) ) return 0;
  return 1;
}
int piece_off_edge(int px, int py, int pr) {
  int x, y;
  const char *piece = pieces[piece_id] + (pr * PIECE_H * PIECE_W);

  for (y = 0; y < PIECE_H; ++y) {
    for (x = 0; x < PIECE_W; ++x) {
      int nx = px + x;
      int ny = py + y;
      if (ny < 0) continue; // off top, don't care
      if (piece[y * PIECE_W + x] > 0) {
        if (nx < 0) return 1; // yes: off left side
        if (nx >= GRID_W ) return 1; // yes: off right side
      }
    }
  }

  return 0;  // inside limits
}
int piece_hits_rubble(int px, int py, int pr) {
  int x, y;
  const char *piece = pieces[piece_id] + (pr * PIECE_H * PIECE_W);

  for (y = 0; y < PIECE_H; ++y) {
    int ny = py + y;
    if (ny < 0) continue;
    for (x = 0; x < PIECE_W; ++x) {
      int nx = px + x;
      if (piece[y * PIECE_W + x] > 0) {
        if (ny >= GRID_H ) return 1; // yes: goes off bottom of grid
        if (grid[ny * GRID_W + nx] == 1 ) return 1; // yes: grid already full in this space
      }
    }
  }

  return 0;  // doesn't hit
}
void game_over() {
  int x, y;
  while (1) {
    // Your homework: add a 'game over' animation here, then film it and tweet it to @marginallyc.
    for (x = 0; x < GRID_W; ++x) {
      for (y = 0; y < GRID_H; ++y) {
        p(x, y, 150);
      }
    }

    // click the button?
    if (digitalRead(1) == 0) {
      // restart!
      setup();
      return;
    }
  }
}
void try_to_drop_piece() {
  erase_piece_from_grid();
  if (piece_can_fit(piece_x, piece_y + 1, piece_rotation)) {
    piece_y++;  // move piece down
    add_piece_to_grid();
  } else {
    // hit something!
    // put it back
    add_piece_to_grid();
    remove_full_rows();
    if (game_is_over() == 1) {
      game_over();
    }
    // game isn't over, choose a new piece
    choose_new_piece();
  }
}

void try_to_drop_faster() {
  int y = analogRead(5);

  if(y == 0){
    try_to_drop_piece();
  }
  /*
  int y = map(analogRead(5), 1023, 0, -500, 500);
  if (y < JOYSTICK_DEAD_ZONE) {
    // player is holding joystick down, drop a little faster.
    try_to_drop_piece();
  }
  */
}

void react_to_player() {
  erase_piece_from_grid();
  try_to_move_piece_sideways();
  try_to_rotate_piece();
  add_piece_to_grid();

  try_to_drop_faster();
}
// can the piece fit in this new location
int game_is_over() {
  int x, y;
  const char *piece = pieces[piece_id] + (piece_rotation * PIECE_H * PIECE_W);

  for (y = 0; y < PIECE_H; ++y) {
    for (x = 0; x < PIECE_W; ++x) {
      int ny = piece_y + y;
      int nx = piece_x + x;
      if (piece[y * PIECE_W + x] > 0) {
        if (ny < 0) return 1; // yes: off the top!
      }
    }
  }

  return 0;  // not over yet...
}
// called once when arduino reboots
void setup() {
  int i;
  // set all the pins to output.
  for (i = 0; i < 16; ++i) {
    pinMode(arduino_to_grid[i], OUTPUT);
  }
  // turn on all resistors, should produce no light
  for (i = 0; i < 8; ++i) {
    digitalWrite(out(i), LOW);
    digitalWrite(in(i), HIGH);
  }

  // set up joystick button
  pinMode(1, INPUT);
  digitalWrite(1, HIGH);


  // make sure arduino knows the grid is empty.
  for (i = 0; i < GRID_W * GRID_H; ++i) {
    grid[i] = 0;
  }

  // make the game a bit more random - pull a number from space and use it to 'seed' a crop of random numbers.
  randomSeed(analogRead(4));

  // get ready to start the game.
  choose_new_piece();

  // start the game clock after everything else is ready.
  last_move = millis();
  last_drop = last_move;
Serial.begin(9600);
// called over and over after setup()
}
void loop() {
  Serial.println(analogRead(5));

  // the game plays at one speed,
  if (millis() - last_move > move_delay ) {
    last_move = millis();
    react_to_player();
  }

  // ...and drops the falling block at a different speed.
  if (millis() - last_drop > drop_delay ) {
    last_drop = millis();
    try_to_drop_piece();
  }

  // when it isn't doing those two things, it's redrawing the grid.
  draw_grid();
}

프로필사진

Klant 2016-12-09 11:01:16

안녕하십니까? 장호근님.

우선 위의 소스에서 테트리스가 내려오는 속도를 조절하는 변수는 move_delay입니다. 

move_delay의 값을 줄여주실 수록 속도는 높아지겠죠.

다만 위의 소스 자체에는 게임이 시간에 지남에 따라 move_delay 값이 줄어드는 코드는 있지 않습니다. 

즉 이 부분을 작성해주시면 말씀해주신 기능 구현이 가능할 것 같네요.

프로필사진

장호근 2016-12-10 23:22:13

답변정말 감사드립니다!

이전글   |    무선충전 모듈을 이용한 리튬 폴리머 충전에 관하여... 2016-12-08
다음글   |    수박쨈님 아두이노 2016-12-09