Инструменты пользователя

Инструменты сайта


Это старая версия документа!


Электронный игральный кубик

Чем ближе друзья живут, тем их сложнее собрать. В ход идёт куча предлогов — передвинуть мебель на даче, починить машину в гаражно-оздоровительном комплексе. Или порубиться в тихие настольные игры — «Манчкин» или ещё какую-нибудь «Цивилизацию». И вот, момент настал, пенные напитки в холодильнике, все собрались и вдруг выясняется, кто-то потерял игральные кубики. Если кубики обычные, шестигранные, их можно поискать у младшего брата. Но как быть с четырёх-, двенадцати- или двадцатигранными?

Наши универсальные электронные кости работают в режиме эмуляции стандартных кубиков d4, d6, d12 и d20. Режим работы переключается потенциометром и выводятся на левую LED матрицу. Чтобы запустить генератор случайных чисел, возьмите гаджет в руки и встряхните его. Гироскоп запустит генератор случайных чисел и результат «броска» появится на правой матрице. Для четырёх- и шестигранных кубиков значение выводится крупными точками, как на настоящих костях, для остальных кубиков используется цифровая индикация.

LED-дисплее. Мы начнём с простейшего двухклеточного организма, к концу игры он займёт собой почти все 64 пикселя матрицы. На втором экране будут появляться его просьбы: пришельца нужно кормить, поить, говорить приятные слова и вовремя укладывать спать. Каждому из этих действий соответствует кнопка на четырёхкнопочной клавиатуре. Не забывайте о питомце, иначе он может погибнуть.

Что потребуется

Видеоинструкция

Как собрать

Скетч

Прошейте контроллер скетчем через Arduino IDE.

dice.ino
// библиотека для работы I²C
#include "Wire.h"
// библиотека для работы со светодиодной матрицей
#include "TroykaLedMatrix.h"
// библиотека для работы с модулями IMU
#include <TroykaIMU.h>
 
// создаём объекты matrix для работы с матрицами
// для каждой матрицы передаём свой адрес
// подробнее читайте на:
// http://wiki.amperka.ru/продукты:troyka-led-matrix
TroykaLedMatrix matrix1;
TroykaLedMatrix matrix2(0x61);
// создаём объект для работы с гироскопом
Gyroscope gyro;
 
// порог скорости вращения гироскопа
#define ROTATE_SPEED_TRESHOLD 500
 
enum {FOUR = 4, SIX = 6, TWELWE = 12, TWENTY = 20};
 
// массив закраски всей матрицы
const uint8_t allPixels[] {
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B11111111
};
 
// массив картнки цифры 4
const uint8_t four[] {
  B00000000,
  B00001000,
  B00011000,
  B00101000,
  B00111100,
  B00001000,
  B00001000,
  B00000000
};
 
// массив картнки цифры 6
const uint8_t six[] {
  B00000000,
  B00011000,
  B00100000,
  B00111000,
  B00100100,
  B00100100,
  B00011000,
  B00000000,
};
 
// массив картнки цифры 12
const uint8_t twelwe[] {
  B00000000,
  B00100100,
  B01101010,
  B00100010,
  B00100100,
  B00101000,
  B00101110,
  B00000000
};
 
// массив картнки цифры 20
const uint8_t twenty[] {
  B00000000,
  B01000110,
  B10101001,
  B00101001,
  B01001001,
  B10001001,
  B11100110,
  B00000000
};
 
// массив значений кубика для режима 4d
const uint8_t face4D[][8] {
  { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},
  { 0x00, 0x06, 0x06, 0x00, 0x00, 0x60, 0x60, 0x00},
  { 0x03, 0x03, 0x00, 0x18, 0x18, 0x00, 0xC0, 0xC0},
  { 0x18, 0x18, 0x00, 0xC3, 0xC3, 0x00, 0x18, 0x18}
};
 
// массив значений кубика для режима 6d
const uint8_t face6D[][8] {
  { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},
  { 0x00, 0x06, 0x06, 0x00, 0x00, 0x60, 0x60, 0x00},
  { 0x03, 0x03, 0x00, 0x18, 0x18, 0x00, 0xC0, 0xC0},
  { 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00},
  { 0xC3, 0xC3, 0x00, 0x18, 0x18, 0x00, 0xC3, 0xC3},
  { 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66}
};
 
// массив значений кубика для режима 12d и 20d
const uint8_t face20D[][8] {
  { 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x00},  // 1
  { 0x00, 0x18, 0x24, 0x04, 0x08, 0x10, 0x3C, 0x00},  // 2
  { 0x00, 0x38, 0x08, 0x10, 0x08, 0x08, 0x30, 0x00},  // 3
  { 0x00, 0x08, 0x18, 0x28, 0x3C, 0x08, 0x08, 0x00},  // 4
  { 0x00, 0x3C, 0x20, 0x38, 0x04, 0x24, 0x18, 0x00},  // 5
  { 0x00, 0x18, 0x20, 0x38, 0x24, 0x24, 0x18, 0x00},  // 6
  { 0x00, 0x3C, 0x04, 0x04, 0x08, 0x10, 0x10, 0x00},  // 7
  { 0x00, 0x18, 0x24, 0x18, 0x24, 0x24, 0x18, 0x00},  // 8
  { 0x00, 0x18, 0x24, 0x24, 0x1C, 0x04, 0x18, 0x00},  // 9
  { 0x00, 0x26, 0x69, 0x29, 0x29, 0x29, 0x26, 0x00},  // 10
  { 0x00, 0x22, 0x66, 0x22, 0x22, 0x22, 0x22, 0x00},  // 11
  { 0x00, 0x24, 0x6A, 0x22, 0x24, 0x28, 0x2E, 0x00},  // 12
  { 0x00, 0x2E, 0x62, 0x24, 0x22, 0x22, 0x2C, 0x00},  // 13
  { 0x00, 0x22, 0x66, 0x2A, 0x2F, 0x22, 0x22, 0x00},  // 14
  { 0x00, 0x2F, 0x68, 0x2E, 0x21, 0x29, 0x26, 0x00},  // 15
  { 0x00, 0x26, 0x68, 0x2E, 0x29, 0x29, 0x26, 0x00},  // 16
  { 0x00, 0x2F, 0x61, 0x21, 0x22, 0x24, 0x24, 0x00},  // 17
  { 0x00, 0x26, 0x69, 0x26, 0x29, 0x29, 0x26, 0x00},  // 18
  { 0x00, 0x26, 0x69, 0x29, 0x27, 0x21, 0x26, 0x00},  // 19
  { 0x00, 0x46, 0xA9, 0x29, 0x49, 0x89, 0xE6, 0x00}   // 20
};
 
void setup()
{
  // открываем последовательный порт
  Serial.begin(115200);
  // выводим сообщение о начале инициализации
  Serial.println("Gyroscope init...");
  // инициализация гироскопа
  gyro.begin();
  // устанавливаем чувствительность гироскопа
  // 250dps — по умолчанию, 500dps, 2000dps
  gyro.setRange(RANGE_250DPS);
  // выводим сообщение об удачной инициализации
  Serial.println("Initialization completed");
  // начало работы с матрицами
  matrix1.begin();
  matrix2.begin();
  // очищаем матрицы
  matrix1.clear();
  matrix2.clear();
}
 
void loop()
{
  // считываем количесвто граней
  int countFace = readCountFace();
  // выводим количество граней на первую матрицу
  switch(countFace) {
    case FOUR:
      matrix1.drawBitmap(four);
      break;
    case SIX:
      matrix1.drawBitmap(six);
      break;
    case TWELWE:
      matrix1.drawBitmap(twelwe);
      break;
    case TWENTY:
      matrix1.drawBitmap(twenty);
      break;
  }
  // считываем абсолютную скорость вращения гироскопа по трём осям
  float cubeRotate = abs(gyro.readDegPerSecX())
  + abs(gyro.readDegPerSecY())
  + abs(gyro.readDegPerSecZ());
  // если скорость вращения гироскопа превысила порог
  if (cubeRotate > ROTATE_SPEED_TRESHOLD) {
    // считаем это за бросок кубика
    // бросаем кубик и отображаем результат
    dropCubeAndPrintResult(countFace);
    delay(1000);
  }
}
 
// функция считывания количество граней
int readCountFace() {
  // считываем показания с потенциометра
  int sensorValue = analogRead(A1);
  if (sensorValue <= 255) {
    return TWENTY;
  } else if (sensorValue > 255 && sensorValue <= 512) {
    return TWELWE;
  } else if (sensorValue > 512 && sensorValue <= 767) {
    return SIX;
  } else if (sensorValue > 767 && sensorValue <= 1023) {
    return FOUR;
  }
}
 
// функция броска кубика и отображение результата
void dropCubeAndPrintResult(int countFace) {
  int number = random(1, countFace + 1);
  randomPixels();
  Serial.println(number);
  switch (countFace) {
    case FOUR:
      matrix2.drawBitmap(face4D[number - 1]);
      break;
    case SIX:
      matrix2.drawBitmap(face6D[number - 1]);
      break;
    case TWELWE:
      matrix2.drawBitmap(face20D[number - 1]);
      break;
    case TWENTY:
      matrix2.drawBitmap(face20D[number - 1]);
      break;
  }
}
 
void randomPixels() {
  matrix2.clear();
  for (int i = 0; i < 20; i++) {
    // заполняем матрицу случайным пикселем
    matrix2.drawPixel(random(8),random(8));
    // очищаем на матрице случайный пиксель
    matrix2.clearPixel(random(8),random(8));
    // ждём 50 мс
    delay(30);
  }
  matrix2.clear();
}

Что дальше?

Часто задаваемые вопросы