carrot-game/carrot_game/carrot_game.ino

422 lines
10 KiB
Arduino
Raw Permalink Normal View History

2024-02-11 12:17:08 +00:00
#include <Arduino.h>
#include <avr/io.h>
2024-02-11 12:17:08 +00:00
#include <SPI.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <ArduinoNunchuk.h>
#include <Tone.h>
#include "bitmap.h"
#include "sound.h"
const bool SOUND_ENABLE = false;
2024-02-17 21:39:15 +00:00
const short INIT_X = 0;
const short INIT_Y = 48;
2024-02-18 12:36:21 +00:00
const short MAX_X = 90;
2024-02-11 12:17:08 +00:00
2024-02-18 12:36:21 +00:00
// number of milliseconds we refresh screen
2024-02-11 12:17:08 +00:00
const unsigned long display_interval = 100;
2024-02-18 12:36:21 +00:00
// number of milliseconds we refresh nunchuck info
2024-02-11 12:17:08 +00:00
const unsigned long nunchuk_interval = 100;
const unsigned int sound_tempo = 10;
2024-03-10 21:01:15 +00:00
const int dataPin = 11;
const int clockPin = 5;
const int latchPin = 6;
2024-02-11 12:17:08 +00:00
U8G2_ST7920_128X64_2_SW_SPI u8g2(U8G2_R0, /* clock=*/ 2, /* data=*/ 10, /* cs=*/ 7);
//A5 A4 A3 A2
ArduinoNunchuk nunchuk = ArduinoNunchuk();
Tone tone1;
2024-02-18 12:36:21 +00:00
// Carrot position
2024-02-17 21:39:15 +00:00
short posX = INIT_X;
short posY = INIT_Y;
unsigned int nextLaneY = INIT_Y - 16;
2024-02-18 12:36:21 +00:00
2024-02-11 12:17:08 +00:00
unsigned long last_display = millis();
unsigned long last_nunchuck = millis();
unsigned long last_note = millis();
unsigned long next_note = millis();
short notes_index = 0;
short intro_size = sizeof(notes_intro)/sizeof(notes_intro[0]);
2024-03-09 17:40:32 +00:00
enum FishType { FT_NORMAL, FT_LIFE, FT_BOMB};
2024-03-09 13:29:09 +00:00
const short NUM_FISHES = 12;
2024-03-09 17:40:32 +00:00
short fishes[NUM_FISHES][3] = {
{0,1,FT_NORMAL},{0,1, FT_NORMAL},{0,1,FT_NORMAL},
{0,16,FT_NORMAL},{0,16,FT_NORMAL},{0,16,FT_NORMAL},
{0,32,FT_NORMAL},{0,32,FT_NORMAL},{0,32,FT_NORMAL},
{0,48,FT_NORMAL},{0,48,FT_LIFE},{0,48,FT_NORMAL}
2024-02-18 12:36:21 +00:00
};
2024-02-11 12:17:08 +00:00
bool game_init = true;
bool jump = false;
bool jumpBack = false;
bool jumpInProgress = false;
2024-03-10 21:01:15 +00:00
int score = 0;
int max_score = 0;
2024-02-18 20:42:31 +00:00
short lives = 5;
2024-03-10 21:01:15 +00:00
int lives_leds[] = {
0b00000000,
0b00000001,
0b00000011,
0b00000111,
0b00001111,
0b00011111,
0b00111111,
0b00111111,
0b00111111,
0b00111111
};
2024-02-11 12:17:08 +00:00
//set A3 and A2 has +5V / GND
void nunchuck_setpowerpins()
{
#define pwrpin PORTC3
#define gndpin PORTC2
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |= _BV(pwrpin);
delay(100); // wait for things to stabilize
}
void setup(void) {
//init serial for debug
2024-02-11 12:17:08 +00:00
Serial.begin(115200);
2024-03-10 21:01:15 +00:00
//init leds
pinMode( dataPin, OUTPUT );
pinMode( clockPin, OUTPUT );
pinMode( latchPin, OUTPUT );
//init nunchuck
2024-02-11 12:17:08 +00:00
nunchuck_setpowerpins();
nunchuk.init();
//init lcd
2024-02-11 12:17:08 +00:00
u8g2.begin();
//init sound
2024-02-11 12:17:08 +00:00
tone1.begin(8);
2024-03-10 21:01:15 +00:00
//test leds
for(int i = 0; i <= 9; i++){
update_lives_leds(lives_leds[i]);
delay(200);
}
2024-02-11 12:17:08 +00:00
}
2024-03-10 21:01:15 +00:00
void update_lives_leds( int value ){
digitalWrite( latchPin, LOW );
shiftOut( dataPin, clockPin, MSBFIRST,value );
digitalWrite( latchPin, HIGH );
}
2024-02-11 12:17:08 +00:00
void play_intro(){
if(SOUND_ENABLE){
if(millis() >= next_note){
next_note = millis() + interval_intro[notes_index];
tone1.play(notes_intro[notes_index], interval_intro[notes_index]);
if(notes_index < intro_size - 1){
notes_index++;
}
2024-02-11 12:17:08 +00:00
}
}
}
void play_sound(uint16_t _note, uint32_t _duration){
if(SOUND_ENABLE){
tone1.play(_note, _duration);
2024-02-11 12:17:08 +00:00
}
}
void display_welcome_page() {
u8g2.setFont(u8g2_font_5x7_mf);
u8g2.drawStr(0, 7, "Welcome to Carrot game!");
u8g2.drawStr(15, 25, "Press Z to start.");
2024-02-18 20:42:31 +00:00
u8g2.drawXBMP(INIT_X, INIT_Y, 16, 16, fish);
if(max_score != 0){
char num_str[] = "000";
sprintf(num_str, "%03d", max_score);
u8g2.drawStr(50, 50, "Last score: ");
u8g2.drawStr(110, 50, num_str);
}
2024-02-11 12:17:08 +00:00
}
void display_game() {
2024-02-18 12:36:21 +00:00
u8g2.setFont(u8g2_font_4x6_tf);
u8g2.drawStr(108, 50, "SCORE:");
2024-02-19 21:06:19 +00:00
//u8g2.drawStr(108, 30, "LIVES:");
2024-02-18 12:36:21 +00:00
u8g2.drawStr(108, 10, "MAX:");
//draw score
2024-02-11 12:17:08 +00:00
u8g2.setFont(u8g2_font_5x7_mf);
2024-02-18 20:42:31 +00:00
char num_str[] = "000";
sprintf(num_str, "%03d", score);
u8g2.drawStr(110, 62, num_str);
sprintf(num_str, "%03d", max_score);
u8g2.drawStr(110, 20, num_str);
2024-02-19 21:06:19 +00:00
short y = 0;
short x = 0;
for(int i = 0; i < lives; i++){
2024-03-09 13:29:09 +00:00
//display 3 hearts per line
2024-02-19 21:06:19 +00:00
if (i % 3 == 0){
y = y +1;
x = 0;
}
u8g2.drawXBMP(110 + 6 * x, 20 + 6 * y, 5, 5, heart);
x = x + 1;
}
2024-02-18 12:36:21 +00:00
//draw fishes
2024-03-09 13:29:09 +00:00
for (int i = 0; i < NUM_FISHES; i++) {
2024-02-11 12:17:08 +00:00
if(fishes[i][0] != NULL){
2024-02-18 12:36:21 +00:00
if(fishes[i][0] < MAX_X){
2024-03-09 17:40:32 +00:00
if(fishes[i][2] == FT_NORMAL){
u8g2.drawXBMP(fishes[i][0], fishes[i][1], 16, 16, fish_reverse);
}
else if(fishes[i][2] == FT_LIFE){
u8g2.drawXBMP(fishes[i][0], fishes[i][1], 16, 16, fish_life);
}
2024-03-09 20:53:47 +00:00
else if(fishes[i][2] == FT_BOMB){
2024-03-11 19:52:11 +00:00
u8g2.drawXBMP(fishes[i][0], fishes[i][1], 16, 16, fish_bomb);
2024-03-09 20:53:47 +00:00
}
2024-02-18 12:36:21 +00:00
}
2024-02-11 12:17:08 +00:00
}
}
2024-02-18 12:36:21 +00:00
//draw carrot
2024-02-11 12:17:08 +00:00
u8g2.drawXBMP(posX, posY, 16, 16, carrot);
//swimelanes
2024-02-18 12:36:21 +00:00
u8g2.drawHLine(0, 0, MAX_X + 16); //top
u8g2.drawHLine(0, 16, MAX_X + 16);
u8g2.drawHLine(0, 32, MAX_X + 16);
u8g2.drawHLine(0, 48, MAX_X + 16);
u8g2.drawHLine(0, 63, MAX_X + 16); //bottom
u8g2.drawVLine(MAX_X + 16, 0, 48);
}
void init_game(){
2024-03-09 13:29:09 +00:00
Serial.print("Number of fishes: ");
Serial.println(sizeof(fishes));
2024-02-18 12:36:21 +00:00
score = 0;
2024-02-18 13:04:17 +00:00
max_score = 0;
2024-02-18 20:42:31 +00:00
lives = 5;
2024-03-10 21:01:15 +00:00
update_lives_leds(lives_leds[lives]);
2024-02-18 12:36:21 +00:00
posX = INIT_X;
posY = INIT_Y;
2024-03-09 13:29:09 +00:00
for ( short i = 0 ; i < NUM_FISHES; i++){
2024-02-18 12:36:21 +00:00
fishes[i][0]= random(MAX_X + 1,255);
}
2024-02-11 12:17:08 +00:00
}
2024-03-09 20:53:47 +00:00
void missed_or_bomb(){
score = score - 10;
lives = lives - 1;
2024-03-10 21:01:15 +00:00
update_lives_leds(lives_leds[lives]);
2024-03-09 20:53:47 +00:00
play_sound(NOTE_B1,300);
if (score <= 0 || lives == 0){
play_sound(NOTE_G1,300);
play_sound(NOTE_F1,300);
play_sound(NOTE_E1,300);
play_sound(NOTE_D1,300);
play_sound(NOTE_C1,300);
game_init = true;
}
}
2024-02-11 12:17:08 +00:00
void compute_fishes_position(){
2024-03-09 13:29:09 +00:00
short fish_step = (200 + score)/200;
for (int i = 0; i < NUM_FISHES; i++) {
2024-02-11 12:17:08 +00:00
if(fishes[i][0] != NULL){
2024-02-18 12:36:21 +00:00
//fish alive
2024-02-17 21:39:15 +00:00
if(fishes[i][0] <= fish_step)
2024-02-11 12:17:08 +00:00
{
2024-02-18 12:36:21 +00:00
fishes[i][0] = random(MAX_X+1,255);
2024-03-10 21:01:15 +00:00
// do not remove lives if it was a bomb
if(fishes[i][2] != FT_BOMB){
missed_or_bomb();
}
2024-02-11 12:17:08 +00:00
}
2024-02-18 12:36:21 +00:00
//move fish to the left
2024-02-11 12:17:08 +00:00
else{
2024-02-17 21:39:15 +00:00
fishes[i][0] = fishes[i][0] - fish_step;
2024-02-11 12:17:08 +00:00
}
//does carrot eat the fish
2024-02-18 12:36:21 +00:00
if( (posX > (fishes[i][0] - 8) && posX < (fishes[i][0] + 8) )
&& (posY > (fishes[i][1] - 8) && posY < (fishes[i][1] + 8) )
){
2024-03-09 20:53:47 +00:00
if(fishes[i][2] == FT_BOMB){
missed_or_bomb();
}
else{
play_sound(NOTE_B5,100);
score = score + 10;
if (score > max_score){
max_score = score;
}
}
2024-03-09 17:40:32 +00:00
if(fishes[i][2] == FT_LIFE){
2024-03-10 21:01:15 +00:00
if(lives < 6){
lives = lives + 1;
update_lives_leds(lives_leds[lives]);
}
2024-03-09 17:40:32 +00:00
}
2024-03-09 20:53:47 +00:00
fishes[i][0] = random(MAX_X+1,255);
2024-03-09 17:40:32 +00:00
//random type
short rdm_type = random(1,100);
2024-03-09 20:53:47 +00:00
if(rdm_type < 80){
2024-03-09 17:40:32 +00:00
fishes[i][2] = FT_NORMAL;
}
2024-03-09 20:53:47 +00:00
else if (rdm_type >= 80 and rdm_type < 90){
2024-03-09 17:40:32 +00:00
fishes[i][2] = FT_LIFE;
}
2024-03-09 20:53:47 +00:00
else{
fishes[i][2] = FT_BOMB;
2024-02-18 12:36:21 +00:00
}
2024-03-09 20:53:47 +00:00
2024-02-11 12:17:08 +00:00
}
}
}
}
void compute_cat_position() {
if (game_init) {
2024-02-18 12:36:21 +00:00
if (posX < MAX_X - 16) {
2024-02-11 12:17:08 +00:00
posX = posX + 2;
}
else {
2024-02-17 21:39:15 +00:00
posX = INIT_X;
2024-02-11 12:17:08 +00:00
if (posY < 48) {
posY = posY + 16;
} else {
2024-02-17 21:39:15 +00:00
posY = INIT_Y;
2024-02-11 12:17:08 +00:00
}
}
}
//game
else {
//compute X pos
if (nunchuk.analogX < 120 && posX >= 4) {
if (nunchuk.analogX < 80) {
if(posX >= 8){
posX = posX - 8;
}else{
posX = posX - 4;
}
} else {
posX = posX - 4;
}
}
2024-03-09 13:29:09 +00:00
if (nunchuk.analogX > 140 && posX < (MAX_X - 8)) {
2024-02-11 12:17:08 +00:00
if (nunchuk.analogX > 180) {
posX = posX + 8;
} else {
posX = posX + 4;
}
}
//compute jump
//detect a new jump
2024-02-18 13:04:17 +00:00
if((nunchuk.zButton == 1 || nunchuk.analogY > 160) && !jump && !jumpBack && posY > 0){
2024-02-11 12:17:08 +00:00
Serial.println("jump!");
//tone1.play(NOTE_B4,500);
jump = true;
jumpInProgress = true;
nextLaneY = posY - 16;
posY = posY - 4;
Serial.println(nextLaneY);
}
else {
//continue a current jump
if (jump) {
if (posY > nextLaneY && jumpInProgress) {
posY = posY - 4;
} else {
if (posY != nextLaneY){
jumpInProgress = false;
posY = posY + 4;
}
else{
jump = false;
}
}
}
}
2024-02-18 13:04:17 +00:00
if((nunchuk.cButton == 1 || nunchuk.analogY < 100) && !jump && !jumpBack && posY < 48){
2024-02-11 12:17:08 +00:00
Serial.println("jump bottom!");
//tone1.play(NOTE_B2,500);
jumpBack = true;
jumpInProgress = true;
nextLaneY = posY + 16;
posY = posY + 4;
Serial.println(nextLaneY);
}
else {
//continue a current jump
if (jumpBack) {
if (posY < nextLaneY && jumpInProgress) {
posY = posY + 4;
} else {
if (posY != nextLaneY){
jumpInProgress = false;
posY = posY - 4;
}
else{
jumpBack = false;
}
}
}
}
}
}
void loop(void) {
if(millis() - last_display >= display_interval){
u8g2.firstPage();
do {
if(game_init){
display_welcome_page();
} else{
display_game();
}
} while ( u8g2.nextPage() );
last_display = millis();
}
if(millis() - last_nunchuck >= nunchuk_interval){
nunchuk.update();
if(init){
if(nunchuk.zButton == 1){
if(game_init){
2024-02-18 12:36:21 +00:00
init_game();
2024-02-11 12:17:08 +00:00
game_init = false;
}
}
}
last_nunchuck = millis();
if(game_init){
play_intro();
}
else{
compute_cat_position();
compute_fishes_position();
}
}
// Serial.print(posX);
// Serial.print(' ');
// Serial.println(posY);
// Serial.print(nunchuk.analogX, DEC);
// Serial.print(' ');
// Serial.println(nunchuk.analogY, DEC);
2024-02-18 13:04:17 +00:00
// Serial.print(' ');
2024-02-11 12:17:08 +00:00
// Serial.print(nunchuk.accelX, DEC);
// Serial.print(' ');
// Serial.print(nunchuk.accelY, DEC);
// Serial.print(' ');
// Serial.print(nunchuk.accelZ, DEC);
// Serial.print(' ');
// Serial.print(nunchuk.zButton, DEC);
// Serial.print(' ');
// Serial.println(nunchuk.cButton, DEC);
//delay(interval);
}