2024-02-11 12:17:08 +00:00
|
|
|
#include <Arduino.h>
|
2024-02-17 21:29:08 +00:00
|
|
|
#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"
|
|
|
|
|
2024-02-17 21:29:08 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
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-02-18 12:36:21 +00:00
|
|
|
short fishes[8][2] = {
|
|
|
|
{0,1},{0,1},{0,16},{0,16},
|
|
|
|
{0,32},{0,32},{0,48},{0,48}
|
|
|
|
};
|
2024-02-11 12:17:08 +00:00
|
|
|
bool game_init = true;
|
|
|
|
bool jump = false;
|
|
|
|
bool jumpBack = false;
|
|
|
|
bool jumpInProgress = false;
|
2024-02-18 12:36:21 +00:00
|
|
|
unsigned int score = 0;
|
|
|
|
unsigned int max_score = 0;
|
2024-02-18 20:42:31 +00:00
|
|
|
short lives = 5;
|
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) {
|
|
|
|
|
2024-02-17 21:29:08 +00:00
|
|
|
//init serial for debug
|
2024-02-11 12:17:08 +00:00
|
|
|
Serial.begin(115200);
|
2024-02-17 21:29:08 +00:00
|
|
|
//init nunchuck
|
2024-02-11 12:17:08 +00:00
|
|
|
nunchuck_setpowerpins();
|
|
|
|
nunchuk.init();
|
2024-02-17 21:29:08 +00:00
|
|
|
//init lcd
|
2024-02-11 12:17:08 +00:00
|
|
|
u8g2.begin();
|
2024-02-17 21:29:08 +00:00
|
|
|
//init sound
|
2024-02-11 12:17:08 +00:00
|
|
|
tone1.begin(8);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void play_intro(){
|
2024-02-17 21:29:08 +00:00
|
|
|
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
|
|
|
}
|
2024-02-17 21:29: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-18 20:42:31 +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);
|
|
|
|
sprintf(num_str, "%03d", lives);
|
|
|
|
u8g2.drawStr(110, 40, num_str);
|
2024-02-18 12:36:21 +00:00
|
|
|
//draw fishes
|
2024-02-11 12:17:08 +00:00
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
if(fishes[i][0] != NULL){
|
2024-02-18 12:36:21 +00:00
|
|
|
if(fishes[i][0] < MAX_X){
|
|
|
|
u8g2.drawXBMP(fishes[i][0], fishes[i][1], 16, 16, fish_reverse);
|
|
|
|
}
|
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(){
|
|
|
|
score = 0;
|
2024-02-18 13:04:17 +00:00
|
|
|
max_score = 0;
|
2024-02-18 20:42:31 +00:00
|
|
|
lives = 5;
|
2024-02-18 12:36:21 +00:00
|
|
|
posX = INIT_X;
|
|
|
|
posY = INIT_Y;
|
|
|
|
for ( short i = 0 ; i < 8; i++){
|
|
|
|
fishes[i][0]= random(MAX_X + 1,255);
|
|
|
|
}
|
2024-02-11 12:17:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void compute_fishes_position(){
|
2024-02-17 21:39:15 +00:00
|
|
|
short fish_step = (200 + score)/100;
|
2024-02-11 12:17:08 +00:00
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
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-02-11 12:17:08 +00:00
|
|
|
score = score - 10;
|
2024-02-18 20:42:31 +00:00
|
|
|
lives = lives - 1;
|
2024-02-17 21:29:08 +00:00
|
|
|
|
|
|
|
play_sound(NOTE_B1,300);
|
2024-02-18 12:36:21 +00:00
|
|
|
//end game
|
2024-02-18 20:42:31 +00:00
|
|
|
if (score <= 0 || lives == 0){
|
2024-02-17 21:29:08 +00:00
|
|
|
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);
|
2024-02-11 12:17:08 +00:00
|
|
|
game_init = true;
|
|
|
|
}
|
|
|
|
}
|
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-02-11 12:17:08 +00:00
|
|
|
fishes[i][0] = random(128,255);
|
2024-02-17 21:29:08 +00:00
|
|
|
play_sound(NOTE_B5,100);
|
2024-02-11 12:17:08 +00:00
|
|
|
score = score + 10;
|
2024-02-18 12:36:21 +00:00
|
|
|
if (score > max_score){
|
|
|
|
max_score = score;
|
|
|
|
}
|
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-02-18 12:36:21 +00:00
|
|
|
if (nunchuk.analogX > 140 && posX < (MAX_X - 16)) {
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|