본문 바로가기

아두이노우노 R4

MPU6050, MAX7219 8x8 Dot Matrix 아두이노 모래시계 소스코드 2

반응형

구현 계획 - 물리 엔진 정밀 고도화

이 계획은 상단 매트릭스의 비워지는 순서를 자연스럽게 개선하고, 하단 매트릭스의 쌓임 현상을 평탄화하는 데 집중합니다.

주요 개선 사항

1. 상단 매트릭스: 무작위 표면 제거 (Random Surface Removal)

  • 로직:
    • 현재 상단 매트릭스에서 가장 위에 위치한 픽셀 그룹(r+c 합이 최소인 모든 픽셀들)을 찾습니다.
    • 해당 그룹 내에서 무작위로 하나를 선택하여 떨어뜨립니다.
  • 효과: 규칙적으로 대각선 방향으로 비워지던 현상이 사라지고, 상단 표면이 불규칙하고 자연스럽게 낮아집니다.

2. 하단 매트릭스: 평탄화 구르는 효과 (Leveling & Rolling)

  • 로직:
    • 모래알이 직선 낙하
       
      (r+1, c+1)가 가능하더라도 20%의 확률로 옆
       
      (r+1, c)이나 
       
      (r, c+1)로 구르게 합니다.
    • 모래알이 쌓인 더미의 경사가 급할 경우, 주변 빈 공간으로 미끄러져 내려가는 연출을 강화합니다.
  • 효과: 중심부가 솟아오르는 현상이 해결되고, 완만한 곡선을 그리며 모래가 쌓입니다.

 

헤더파일  분리

 

define.h

#ifndef DEFINE_H
#define DEFINE_H

#include <MD_MAX72xx.h>

// 하드웨어 설정
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

// 상단 매트릭스 핀 설정
#define CLK_PIN_TOP 13
#define DATA_PIN_TOP 11
#define CS_PIN_TOP 10

// 하단 매트릭스 핀 설정
#define CLK_PIN_BOT 7
#define DATA_PIN_BOT 9
#define CS_PIN_BOT 8

// 물리 및 시스템 설정
const int frameDelay = 200; // 프레임 지연 (속도 조절)

#endif

 

소스코드 mpu-max-sandclock.ino

 

#include <MD_MAX72xx.h>
#include <SPI.h>
#include "define.h"

MD_MAX72XX topMatrix = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN_TOP, CLK_PIN_TOP, CS_PIN_TOP, 1);
MD_MAX72XX botMatrix = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN_BOT, CLK_PIN_BOT, CS_PIN_BOT, 1);

bool topGrid[8][8];
bool botGrid[8][8];

// 업데이트 순서를 무작위로 섞기 위한 배열
int pixelOrder[64];

// 유효 좌표 확인
bool isValid(int row, int col) {
  return row >= 0 && row < 8 && col >= 0 && col < 8;
}

// 매트릭스 초기화
void initMatrices() {
  memset(botGrid, 0, sizeof(botGrid));
  botMatrix.clear();
  topMatrix.clear();
  for (int r = 0; r < 8; r++) {
    for (int c = 0; c < 8; c++) {
      topGrid[r][c] = true;
      topMatrix.setPoint(r, c, true);
    }
  }
  // 업데이트 순서 배열 초기화 (0~63)
  for (int i = 0; i < 64; i++) pixelOrder[i] = i;
}

// 화면 출력 (깜빡임 방지를 위해 버퍼 업데이트 사용)
void displayGrids() {
  topMatrix.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
  botMatrix.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
  
  for (int r = 0; r < 8; r++) {
    for (int c = 0; c < 8; c++) {
      topMatrix.setPoint(r, c, topGrid[r][c]);
      botMatrix.setPoint(r, c, botGrid[r][c]);
    }
  }
  
  topMatrix.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
  botMatrix.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}

// 배열 무작위 셔플 (Fisher-Yates) - 하단 매트릭스용
void shuffleOrder() {
  for (int i = 63; i > 0; i--) {
    int j = random(i + 1);
    int temp = pixelOrder[i];
    pixelOrder[i] = pixelOrder[j];
    pixelOrder[j] = temp;
  }
}

// 물리 엔진 업데이트
void updatePhysics() {
  // 1. 하단 매트릭스 업데이트 (기존 방식 유지)
  shuffleOrder();
  for (int i = 0; i < 64; i++) {
    int r = pixelOrder[i] / 8;
    int c = pixelOrder[i] % 8;
    
    if (botGrid[r][c]) {
      // 대각선 아래(7,7 방향) 우선 이동하되, 20% 확률로 옆으로 비껴가게 하여 평탄화 유도
      bool forceSlide = (random(100) < 20);
      
      if (!forceSlide && r < 7 && c < 7 && !botGrid[r + 1][c + 1]) {
        botGrid[r][c] = false;
        botGrid[r + 1][c + 1] = true;
      } else {
        // 막혔거나 확률에 의해 옆으로 미끄러짐 (구르는 효과)
        int dir = random(2);
        if (dir == 0) {
          if (r < 7 && !botGrid[r + 1][c]) { botGrid[r][c] = false; botGrid[r + 1][c] = true; }
          else if (c < 7 && !botGrid[r][c + 1]) { botGrid[r][c] = false; botGrid[r][c + 1] = true; }
        } else {
          if (c < 7 && !botGrid[r][c + 1]) { botGrid[r][c] = false; botGrid[r][c + 1] = true; }
          else if (r < 7 && !botGrid[r + 1][c]) { botGrid[r][c] = false; botGrid[r + 1][c] = true; }
        }
      }
    }
  }

  // 2. 위 -> 아래 전배 (상단 표면 무작위 선택 로직)
  static int spawnDelay = 0;
  if (spawnDelay > 0) {
    spawnDelay--;
  } else if (!botGrid[0][0]) {
    int minS = -1;
    // 1단계: 가장 높은 층(최소 r+c) 찾기
    for (int s = 0; s <= 14; s++) {
      for (int r = 0; r <= s; r++) {
        int c = s - r;
        if (isValid(r, c) && topGrid[r][c]) {
          minS = s;
          break;
        }
      }
      if (minS != -1) break;
    }

    if (minS != -1) {
      // 2단계: 해당 층에 있는 모래알 개수 확인
      int count = 0;
      for (int r = 0; r <= minS; r++) {
        int c = minS - r;
        if (isValid(r, c) && topGrid[r][c]) count++;
      }

      // 3단계: 무작위로 하나 선택하여 떨어뜨리기
      int targetIndex = random(count);
      int current = 0;
      bool found = false;
      for (int r = 0; r <= minS; r++) {
        int c = minS - r;
        if (isValid(r, c) && topGrid[r][c]) {
          if (current == targetIndex) {
            topGrid[r][c] = false;
            botGrid[0][0] = true;
            spawnDelay = 2; // 한 알씩 떨어지는 간격 유지
            found = true;
            break;
          }
          current++;
        }
      }
    }
  }

  // 3. 상단 매트릭스 내부 물리는 불필요하므로 비활성화 (깜빡임의 원인)
}

// 상단 매트릭스가 비었는지 확인
bool isTopEmpty() {
  for (int r = 0; r < 8; r++) {
    for (int c = 0; c < 8; c++) {
      if (topGrid[r][c]) return false;
    }
  }
  return true;
}

void setup() {
  randomSeed(analogRead(0));
  topMatrix.begin();
  botMatrix.begin();
  topMatrix.control(MD_MAX72XX::INTENSITY, 2);
  botMatrix.control(MD_MAX72XX::INTENSITY, 2);
  initMatrices();
}

unsigned long lastUpdate = 0;

void loop() {
  if (millis() - lastUpdate >= frameDelay) {
    updatePhysics();
    displayGrids();
    lastUpdate = millis();

    // 상단이 비워지면 3초 대기 후 초기화
    if (isTopEmpty()) {
      delay(3000);
      initMatrices();
    }
  }
}

 

 

반응형

캐어랩 고객 지원

취업, 창업의 막막함, 외주 관리, 제품 부재!

당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약, 아이디어는 있지만 구현할 기술이 없는 막막함.

우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.

이제 고민을 멈추고, 캐어랩을 만나세요!

코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.

제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!

귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.

지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.

카카오 채널 추가하기

카톡 채팅방에서 무엇이든 물어보세요

당신의 성공을 위해 캐어랩과 함께 하세요.

캐어랩 온라인 채널 바로가기

캐어랩