КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Игра Avalanche на Arduino/Piranha UNO

Общие сведения

В этом проекте мы соберём видеоигру на Arduino. Управлять мы будем при помощи Trema модуля Потенциометр, а вывод будет осуществятся на матрицу 8x8 Flash-I2C. Игрок управляет бригадой, которая отлавливает падающие сверху камни оползня и выглядит эта бригада как платформа из трёх пикселей внизу игрового поля. При пропуске каждого камня ниже платформы игрок теряет одну жизнь и платформа уменьшается. При пропуске трёх камней игра заканчивается. Каждый пойманный камень - сто очков, очки мы будем выводить в последовательный порт. Каждую 1000 очков игрок получает бонус, если не полное количество жизней - жизнь добавляется.

Видео

Нам понадобится

Подключение

Устанавливаем Trema Shield на Piranha UNO

Подключаем модули

Скетч проекта

Необходимо установить библиотеку iarduino_I2C_Matrix_8x8. Если Вы не знаете как устанавливать библиотеки в Arduino IDE - Вы можете узнать по этой ссылке

#include <iarduino_I2C_Matrix_8x8.h>

// Определяем количество жизней
#define LIFES 3
// Определяем интервал смены игрового поля
#define SET_INTERVAL 500
// Определяем вывод зуммера
#define ZUM 2
// Определяем сложность игры, меньше - сложнее
#define SET_DIFICULTY 20

iarduino_I2C_Matrix_8x8 disp;

// Переменная для хранения очков
unsigned long score;
// Переменная для хранения millis начала звука зуммера
unsigned long sound_millis;

// Сложность игры (растёт с каждой 1000 очков)
uint8_t difficulty = 0;

// Насколько быстрее становится игра при смене уровня
const byte increase_rate = 10;
// Флаг работы зуммера
bool sound_flag;
// Переменные интервалов
unsigned long interval;
unsigned long old_millis;
unsigned long sound_interval;

// Текущий ряд (если 7, то остаётся 7 до конца игры)
uint8_t row;

// Переменная жизней игрока
byte life_insurance;

// Состояние игры
byte game_state;

// Возможные состояния игры
enum {
    wait,
    play,
    game_over
};

// Массивы дисплея и игрового поля
byte game_field[8];
uint8_t matrix[8];

// Положение игрока
byte player_pos;

void setup()
{

    Serial.begin(9600);
    Serial.println("start");

    pinMode(ZUM, INPUT);

    disp.begin();
    // Сбрасываем количество жизней и игровую матрицу
    reset();

    // Инициируем переменные
    sound_millis = 0;
    sound_interval = 100;
    sound_flag = false;
    old_millis = 0;
    game_state = wait;
}

void loop()
{
    // Считываем потенциометр
    byte old_pot = readPot();

    // Если игра ожидает, просто ортисовываем игрока
    if (game_state == wait)
        drawPlayer();

    // Если состояние в игре - запускаем игру
    if (game_state == play)
        game();

    // Если игра окончена - сбрасываем всё в изначальное состояние
    if (game_state == game_over) {
        delay(2000);
        reset();
        delay(1000);
        old_pot = readPot();
        game_state = wait;
    }

    // Выводим картинку на модуль матрицы
    disp.drawImage(matrix);

    // Небольшая задержка для устранения джиттера потенциометра
    delay(50);

    // Елси потенциометр был повёрнут - переходим в режим игры
    if (old_pot != readPot() && game_state == wait)
        game_state = play;
}

void reset()
{
    disp.clrScr();

    for (auto& i:game_field)
        i = 0;
    row = 0;

    for (auto& i:matrix)
        i = 0;

    score = 0;
    interval = SET_INTERVAL;
    life_insurance = LIFES;
}
void game()
{
    // Если пришло время обновлять матрицу
    if (millis() - old_millis > interval) {
        // Запоминаем это время
        old_millis = millis();

        // Вызываем функцию измененя игрового поля
        progress(row);
        // Издаём звук
        advanceSound();

        // Елси игрок не поймал пиксель
        if (!checkCollision()) {
            // Отнимаем одну жизнь
            life_insurance--;
            // Проигрываем звук
            missedSound();
        }
        // Иначе, если в последнем ряду есть пиксель врага
        else if (row == 7 && game_field[7] != 0) {
            // прибавляем очки
            score += 100;

            // Если количество очков кратно 1000
            if (!(score % 1000)) {
                // Усложняем игру
                if (difficulty < SET_DIFICULTY - 7)
                    difficulty++;

                rewardAndPunish();
                levelUpSound();
            }

            Serial.println((String)"Score:\t" + score);
        }

        // Если жизни закончились - конец игры
        if (!life_insurance)
            gameOver();

        // Приращиваем ряды
        if (row < 7)
            row++;
    }

    // Обновляем матрицу
    for (auto i = 0; i < sizeof(matrix); i++) {
        matrix[i] = game_field[i];
    }
    // Выводим пиксели игрока на матрицу
    drawPlayer();
    // Прерываем зуммер, если подошло время
    shutUp();
}

// Добавляем жизнь, если меньше 3 и увеличиваем скорость игры.
void rewardAndPunish()
{
    interval -= increase_rate;
    if (life_insurance < 3)
        life_insurance++;
}

void drawPlayer()
{
    player_pos = readPot();
    matrix[7] = player_pos;
}

byte readPot()
{
    uint8_t pot_value = map(analogRead(A0), 0, 1023, 7, 0);
    byte _player_pos;

    _player_pos ^= _player_pos;

    bitSet(_player_pos, pot_value);

    if (life_insurance > 2)
        bitSet(_player_pos, pot_value+1);
    if (life_insurance > 1)
        bitSet(_player_pos, pot_value-1);

    return _player_pos;
}

uint8_t generate()
{
    uint8_t rnd = random(SET_DIFICULTY - difficulty);

    if (rnd > 8)
        return 0;

    uint8_t prj;
    prj ^= prj;
    bitSet(prj, rnd);
    return prj;
}

void progress(uint8_t n)
{
    if (n == 0) {
        game_field[n] = generate();
        return;
    }
    else {
        game_field[n] = game_field[n-1];
        progress(--n);
    }
}

bool checkCollision()
{

    byte test = 0xFF;

    if (row == 7 && game_field[7] != 0)
        test = player_pos & game_field[7];

    return !!test;
}

void gameOver()
{
    game_state = game_over;
    gameoverSound();
}

void makeNoise()
{
    pinMode(ZUM, OUTPUT);
    digitalWrite(ZUM, HIGH);
}

void shutUp()
{
    if (millis() - sound_millis > sound_interval) {
        pinMode(ZUM, INPUT);
        sound_flag = false;
    }
}

void advanceSound()
{
    if (!sound_flag) {
        makeNoise();
        sound_millis = millis();
        sound_interval = 100;
        sound_flag = true;
    }
}

void missedSound()
{
    if (!sound_flag) {
        makeNoise();
        sound_millis = millis();
        sound_interval = 300;
        sound_flag = true;
    }
}

void gameoverSound()
{
    for (int i = 0; i < 4; i++) {
        missedSound();
        delay(300);
    }
}

void levelUpSound()
{
    for (int i = 0; i < 2; i++) {
        advanceSound();
        if (i)
            break;
        delay(50);
    }
}

Ссылки




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями