工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

必要な部品と材料

Microchip ATtiny85×1
グラフィックOLED 128×64×1
SparkFun microB USBブレークアウト×1
Arduino Nano R3(プログラミング用)×1

必要な工具と機械

使用するアプリとオンラインサービス

プロジェクト概要

着想と過去のプロジェクト

2017年12月、Arduino NanoとOLED画面、2ボタンで携帯型Pongコンソールを作りました。

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

当時は良かったものの、コンソールが大型で扱いにくかったです。最近、過去のプロジェクトを再構築しようと考え、今回はスネークゲームが遊べる極小コンソールを目指しました。

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

採用コンポーネント

小型化のためArduino開発ボードは不使用。ATtiny85を採用しました。

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

ゲーム運用に十分なI/Oを備え、2つのADCピン、I2Cポート、GPIOピンを持ちます。操作にはシンプルな2軸ジョイスティック/スイッチモジュールを採用。信号用に3ピンのみで接続簡単です。

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

ディスプレイはDFRobotの128×64 OLEDを選択。小型ながら十分な解像度です。

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

システムのハンダ付け

ATtiny85をSOP-8 to DIP-8変換基板に取り付け、小さなパーフボードにハンダ付け。プログラミング用と画面用の2つのヘッダーを実装。アナログジョイスティックのピンをパーフボードにハンダ付けして配線。最後にmicro USBブレークアウトボードを電源用に配線してシステム完成。

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作
ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

エンクロージャの設計

Fusion 360で設計。実装部品を配置し、それらを囲むエンクロージャを作成。

ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作
ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作

80年代のアーケード筐体を模しつつ、極小サイズを維持。以下はレンダリング画像:

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. TinyML言語検出器:Edge ImpulseとArduino Nano 33 BLE Senseで「はい」を多言語判別
  2. Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
  3. Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
  4. Arduino Nanoで37LEDルーレットゲームを自作:ヨーロピアンスタイル完全再現
  5. ポータブル距離検出器:Arduino Nano自作キット(HC-SR04、18650電源、3Dプリントケース付き)
  6. Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション
  7. 銅メッキプロジェクト:Arduino UNOで作るスマート電気メッキシステム
  8. Arduino Nanoで作るNeoPixelマトリックス版Pongゲーム
  9. Arduinoシフトレジスタで美しい光のシーケンスを作成
  10. Arduino 製スマートプラグ - 120V 対応、RTC 内蔵で時刻制御可能なインテリジェントコンセント
  11. Arduinoで作るセキュリティシステム:超音波センサーとサーボで広範囲監視・アラート