added config screen, added game
This commit is contained in:
@@ -1,48 +1,39 @@
|
||||
#include "display.h"
|
||||
|
||||
e_displayState CurrentScreen;
|
||||
|
||||
|
||||
void handleDisplayGui(void)
|
||||
{
|
||||
clearDisplay();
|
||||
switch (CurrentScreen)
|
||||
switch (getDisplayState())
|
||||
{
|
||||
case mainscreen:
|
||||
{
|
||||
e_displayState nextScreen = ScreenMainHandle();
|
||||
if(nextScreen != CurrentScreen)
|
||||
{
|
||||
setDisplayState(nextScreen);
|
||||
}
|
||||
ScreenMainDrawValues();
|
||||
ScreenMainDrawButtons();
|
||||
ScreenMainHandle();
|
||||
}
|
||||
break;
|
||||
|
||||
case setupscreen:
|
||||
{
|
||||
e_displayState nextScreen = ScreenSetupHandle();
|
||||
if(nextScreen != CurrentScreen)
|
||||
{
|
||||
setDisplayState(nextScreen);
|
||||
}
|
||||
ScreenSetupDrawButtons();
|
||||
ScreenSetupHandle();
|
||||
}
|
||||
break;
|
||||
|
||||
case gamescreen:
|
||||
{
|
||||
handleScreenGame();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setDisplayState(e_displayState newstate)
|
||||
{
|
||||
CurrentScreen = newstate;
|
||||
}
|
||||
|
||||
|
||||
void initDisplay(void)
|
||||
{
|
||||
initDisplayHall();
|
||||
initDisplayMain();
|
||||
initSetupScreen();
|
||||
initScreenGame();
|
||||
setDisplayState(e_displayState::mainscreen);
|
||||
}
|
||||
|
||||
void handleDisplay(void)
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
#include "display_buttons.h"
|
||||
#include "display_hall.h"
|
||||
#include "display_draw.h"
|
||||
#include "display_screen.h"
|
||||
#include "screen_main.h"
|
||||
#include "screen_setup.h"
|
||||
#include "screen_game.h"
|
||||
#include "measure.h"
|
||||
#include "measure_mode.h"
|
||||
|
||||
@@ -14,5 +16,4 @@ void initDisplay(void);
|
||||
void handleDisplay(void);
|
||||
|
||||
e_displayState getDisplayState(void);
|
||||
void setDisplayState(e_displayState newstate);
|
||||
|
||||
|
||||
@@ -67,21 +67,28 @@ void c_onScreenButton::handle()
|
||||
{
|
||||
if (_physButton.isValid())
|
||||
{
|
||||
_pressed = _physButton.read();
|
||||
_physButton.read();
|
||||
_pressed = _physButton.pressedFor(200);
|
||||
if (_pressed)
|
||||
{
|
||||
if(_mode != e_measureMode::mNone)
|
||||
if(_actionFn != NULL && (actionHandled == false))
|
||||
{
|
||||
setMeasureMode(_mode);
|
||||
_actionFn();
|
||||
actionHandled = true;
|
||||
}
|
||||
}
|
||||
else if (_physButton.releasedFor(500))
|
||||
{
|
||||
actionHandled = false;
|
||||
}
|
||||
|
||||
_state = _pressed;
|
||||
}
|
||||
else if (_stateFn != NULL)
|
||||
{
|
||||
_state = _stateFn();
|
||||
}
|
||||
log_i("item(%s)=%d",_name.c_str(), _state);
|
||||
log_d("item(%s)=%d",_name.c_str(), _state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define INDICATORWIDTH 29
|
||||
#define INDICATORHEIGHT 13
|
||||
#define INDICATORRADIUS 3
|
||||
#define HORIZONTALBUTTONS 5
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -31,6 +32,8 @@ class c_onScreenButton
|
||||
const String _name;
|
||||
const uint8_t _index;
|
||||
bool (*const _stateFn)();
|
||||
void (*const _actionFn)();
|
||||
bool actionHandled = false;
|
||||
c_button _physButton;
|
||||
|
||||
|
||||
@@ -54,10 +57,12 @@ class c_onScreenButton
|
||||
void setState(bool state) { _state = state; }
|
||||
|
||||
public:
|
||||
c_onScreenButton(String name, e_measureMode mode, e_buttonLoc location, uint8_t pin) : _name(name),
|
||||
_index((uint8_t)mode),
|
||||
_stateFn(NULL),
|
||||
_physButton(pin, (uint8_t)mode)
|
||||
c_onScreenButton(String name, e_measureMode mode, e_buttonLoc location, uint8_t pin,void (*const action)() ) :
|
||||
_name(name),
|
||||
_index((uint8_t)mode),
|
||||
_stateFn(NULL),
|
||||
_actionFn(action),
|
||||
_physButton(pin, (uint8_t)mode)
|
||||
{
|
||||
_xpos = 1;
|
||||
_ypos = 1;
|
||||
@@ -69,10 +74,12 @@ public:
|
||||
_location = location;
|
||||
}
|
||||
|
||||
c_onScreenButton(String name, uint8_t index, e_buttonLoc location, uint8_t pin) : _name(name),
|
||||
_index(index),
|
||||
_stateFn(NULL),
|
||||
_physButton(pin, index)
|
||||
c_onScreenButton(String name, uint8_t index, e_buttonLoc location, uint8_t pin, void (*const action)()) :
|
||||
_name(name),
|
||||
_index(index),
|
||||
_stateFn(NULL),
|
||||
_actionFn(action),
|
||||
_physButton(pin, index)
|
||||
{
|
||||
_xpos = 1;
|
||||
_ypos = 1;
|
||||
@@ -87,6 +94,7 @@ public:
|
||||
c_onScreenButton(String name, uint8_t index, e_buttonLoc location, bool (*stateFn)()) : _name(name),
|
||||
_index(index),
|
||||
_stateFn(stateFn),
|
||||
_actionFn(NULL),
|
||||
_physButton(NOBUTTON, index)
|
||||
{
|
||||
_xpos = 1;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
U8G2_SSD1322 display(U8G2_R2, OLED_MOSI, OLED_SCK, OLED_CS, OLED_DC, OLED_RST); // Enable U8G2_16BIT in u8g2.h
|
||||
uint64_t lastDisplayTime = 0;
|
||||
bool maxFPS = false;
|
||||
|
||||
U8G2_SSD1322 *getDisplay(void)
|
||||
{
|
||||
@@ -21,7 +22,7 @@ void initDisplayHall()
|
||||
void handleDisplayHall()
|
||||
{
|
||||
uint64_t currentmillis = millis();
|
||||
if (currentmillis - lastDisplayTime > SCREENREFRESH)
|
||||
if ((currentmillis - lastDisplayTime > SCREENREFRESH) || maxFPS)
|
||||
{
|
||||
//display.clearBuffer(); // clear the internal memory
|
||||
//display.drawStr(0, 10, "Hello World!"); // write something to the internal memory
|
||||
@@ -30,6 +31,11 @@ void handleDisplayHall()
|
||||
}
|
||||
}
|
||||
|
||||
void SetFPSMax(void)
|
||||
{
|
||||
maxFPS = true;
|
||||
}
|
||||
|
||||
void clearDisplay(void)
|
||||
{
|
||||
display.clearBuffer();
|
||||
|
||||
@@ -43,3 +43,5 @@ uint16_t getDisplayHeight(void);
|
||||
|
||||
U8G2_SSD1322* getDisplay(void);
|
||||
|
||||
void SetFPSMax(void);
|
||||
|
||||
|
||||
83
MilliOhmMeter_FW/src/display_screen.cpp
Normal file
83
MilliOhmMeter_FW/src/display_screen.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "display_screen.h"
|
||||
|
||||
e_displayState CurrentScreen = e_displayState::mainscreen;
|
||||
uint32_t lastScreenchange = 0;
|
||||
|
||||
void setDisplayState(e_displayState newstate)
|
||||
{
|
||||
log_i("setDisplayState to %d", newstate);
|
||||
uint32_t timeNow = millis();
|
||||
// if(timeNow - lastScreenchange > MINSCREENSWITCHDELAY)
|
||||
// {
|
||||
CurrentScreen = newstate;
|
||||
lastScreenchange = timeNow;
|
||||
//}
|
||||
}
|
||||
|
||||
e_displayState getDisplayState()
|
||||
{
|
||||
return CurrentScreen;
|
||||
}
|
||||
|
||||
// class functions
|
||||
|
||||
void screen_c::begin(void)
|
||||
{
|
||||
uint16_t IndicatorYpos = 0;
|
||||
uint16_t buttonXpos = 0;
|
||||
uint16_t buttonwidth = ((getDisplay()->getDisplayWidth() / _buttonCount) - CONTROLLOFFSET * 2 + 1);
|
||||
|
||||
for (auto &&item : _items)
|
||||
{
|
||||
if (item->getLocation() == LocBottom)
|
||||
{
|
||||
uint16_t ypos = getDisplay()->getDisplayHeight() - 1 - CONTROLSLINE_H + 2;
|
||||
item->begin(buttonXpos, ypos, buttonwidth, CONTROLSLINE_H + 6, CONTROLRADIUS);
|
||||
buttonXpos+=buttonwidth-1;
|
||||
item->setVisible(true);
|
||||
}
|
||||
else if (item->getLocation() == LocRight)
|
||||
{
|
||||
uint16_t IndicatorXpos = getDisplay()->getDisplayWidth() - INDICATORWIDTH;
|
||||
item->begin(IndicatorXpos, IndicatorYpos, INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
|
||||
item->setVisible(true);
|
||||
IndicatorYpos += INDICATORHEIGHT - 1;
|
||||
log_d("indicators: indicW=%d, indicH=%d ypos,%d", INDICATORWIDTH, INDICATORHEIGHT, IndicatorXpos);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_e("location not implemented (%d)", item->getLocation());
|
||||
}
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void screen_c::handle(void)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
log_e("screen not initialized! %d", _thisScreen);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &&i : _items)
|
||||
{
|
||||
i->handle();
|
||||
}
|
||||
}
|
||||
|
||||
void screen_c::draw(void)
|
||||
{
|
||||
for (auto &&item : _items)
|
||||
{
|
||||
item->drawButton();
|
||||
}
|
||||
}
|
||||
|
||||
void screen_c::activateModeButton(void)
|
||||
{
|
||||
for (auto &&item : _items)
|
||||
{
|
||||
item->setDisplayState((item->getIndex() == (uint8_t)getMeasureMode()));
|
||||
}
|
||||
}
|
||||
40
MilliOhmMeter_FW/src/display_screen.h
Normal file
40
MilliOhmMeter_FW/src/display_screen.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <vector>
|
||||
|
||||
#include "display_types.h"
|
||||
#include "display_draw.h"
|
||||
#include "display_buttons.h"
|
||||
#include "measure_mode.h"
|
||||
|
||||
#define MINSCREENSWITCHDELAY 1000
|
||||
|
||||
void setDisplayState(e_displayState newstate);
|
||||
e_displayState getDisplayState(void);
|
||||
|
||||
|
||||
class screen_c
|
||||
{
|
||||
std::vector<c_onScreenButton*> _items;
|
||||
const e_displayState _thisScreen;
|
||||
const uint8_t _buttonCount;
|
||||
bool _initialized = false;
|
||||
|
||||
|
||||
public:
|
||||
screen_c(e_displayState thisScreen, uint8_t buttonCount):_thisScreen(thisScreen), _buttonCount(buttonCount)
|
||||
{}
|
||||
|
||||
void addItem(c_onScreenButton* pButton)
|
||||
{
|
||||
_items.push_back(pButton);
|
||||
}
|
||||
|
||||
void begin(void);
|
||||
void handle(void);
|
||||
void draw(void);
|
||||
void activateModeButton(void);
|
||||
|
||||
};
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
typedef enum
|
||||
{
|
||||
mainscreen,
|
||||
setupscreen
|
||||
setupscreen,
|
||||
gamescreen
|
||||
} e_displayState;
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
enum e_measureMode
|
||||
{
|
||||
mNone,
|
||||
mA20,
|
||||
mA200,
|
||||
mA1000,
|
||||
mAuto,
|
||||
mSetup,
|
||||
mLast,
|
||||
mNone
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -38,13 +38,11 @@ void initMeasureState(void)
|
||||
void handleMeasureState(void)
|
||||
{
|
||||
uint32_t currentTime = millis();
|
||||
bool newState = false;
|
||||
if (currentTime - lastMeasureTime > MEASURESTATEINTERVAL)
|
||||
{
|
||||
if (currentMeasureState != lastMeasureState)
|
||||
{
|
||||
log_i("measureState: %s", s_measureState[currentMeasureState]);
|
||||
newState = true;
|
||||
log_d("measureState: %s", s_measureState[currentMeasureState]);
|
||||
}
|
||||
|
||||
switch (currentMeasureState)
|
||||
|
||||
398
MilliOhmMeter_FW/src/screen_game.cpp
Normal file
398
MilliOhmMeter_FW/src/screen_game.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
#include "screen_game.h"
|
||||
|
||||
#define SCREEN_WIDTH 256 // OLED _display width, in pixels
|
||||
#define SCREEN_HEIGHT 64 // OLED _display height, in pixels
|
||||
|
||||
const char STR_LOADSCREEN_CREATOR[] = "Makeability Lab";
|
||||
const char STR_LOADSCREEN_APP_NAME_LINE1[] = "Flappy";
|
||||
const char STR_LOADSCREEN_APP_NAME_LINE2[] = "Bird!";
|
||||
const char STR_PRESS_FLAP_TO_PLAY[] = "Press flap to play";
|
||||
const char STR_PRESS_FLAP_TO_EXIT[] = "Press flap to exit";
|
||||
|
||||
const char STR_GAME_OVER[] = "Game Over!";
|
||||
|
||||
// Define I/O pins
|
||||
const int FLAP_BUTTON_INPUT_PIN = BUTTON1;
|
||||
const int TONE_OUTPUT_PIN = BUZZER1;
|
||||
const int VIBROMOTOR_OUTPUT_PIN = BUZZER1;
|
||||
|
||||
// for tracking fps
|
||||
unsigned long _frameCount = 0;
|
||||
float _fps = 0;
|
||||
unsigned long _fpsStartTimeStamp = 0;
|
||||
|
||||
uint32_t lastGameUpdate = 0;
|
||||
|
||||
// status bar
|
||||
const boolean _drawFrameCount = false; // change to show/hide frame count
|
||||
const int DELAY_LOOP_MS = 5;
|
||||
const int LOAD_SCREEN_SHOW_MS = 750;
|
||||
|
||||
class Bird : public Rectangle
|
||||
{
|
||||
public:
|
||||
Bird(int x, int y, int width, int height) : Rectangle(x, y, width, height)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Pipe : public Rectangle
|
||||
{
|
||||
protected:
|
||||
bool _hasPassedBird = false;
|
||||
|
||||
public:
|
||||
Pipe(int x, int y, int width, int height) : Rectangle(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
bool getHasPassedBird()
|
||||
{
|
||||
return _hasPassedBird;
|
||||
}
|
||||
|
||||
bool setHasPassedBird(bool hasPassedBird)
|
||||
{
|
||||
return _hasPassedBird = hasPassedBird;
|
||||
}
|
||||
};
|
||||
|
||||
const int BIRD_HEIGHT = 5;
|
||||
const int BIRD_WIDTH = 10;
|
||||
const int NUM_PIPES = 3;
|
||||
|
||||
const int MIN_PIPE_WIDTH = 8;
|
||||
const int MAX_PIPE_WIDTH = 18; // in pixels
|
||||
const int MIN_PIPE_X_SPACING_DISTANCE = BIRD_WIDTH * 3; // in pixels
|
||||
const int MAX_PIPE_X_SPACING_DISTANCE = 100; // in pixels
|
||||
const int MIN_PIPE_Y_SPACE = BIRD_HEIGHT * 3;
|
||||
const int MAX_PIPE_Y_SPACE = SCREEN_HEIGHT - BIRD_HEIGHT * 2;
|
||||
|
||||
int _pipeSpeed = 2;
|
||||
int _gravity = 1; // can't apply gravity every frame, apply every X time
|
||||
int _points = 0;
|
||||
unsigned long _gameOverTimestamp = 0;
|
||||
|
||||
const int IGNORE_INPUT_AFTER_GAME_OVER_MS = 500; // ignores input for 500ms after game over
|
||||
|
||||
// Initialize top pipe and bottom pipe arrays. The location/sizes don't matter
|
||||
// at this point as we'll set them in setup()
|
||||
Pipe _topPipes[NUM_PIPES] = {Pipe(0, 0, 0, 0),
|
||||
Pipe(0, 0, 0, 0),
|
||||
Pipe(0, 0, 0, 0)};
|
||||
|
||||
Pipe _bottomPipes[NUM_PIPES] = {Pipe(0, 0, 0, 0),
|
||||
Pipe(0, 0, 0, 0),
|
||||
Pipe(0, 0, 0, 0)};
|
||||
|
||||
Bird _bird(5, SCREEN_HEIGHT / 2, BIRD_WIDTH, BIRD_HEIGHT);
|
||||
|
||||
enum GameState
|
||||
{
|
||||
NEW_GAME,
|
||||
PLAYING,
|
||||
GAME_OVER,
|
||||
};
|
||||
|
||||
GameState _gameState = NEW_GAME;
|
||||
|
||||
// This is necessary for the game to work on the ESP32
|
||||
// See:
|
||||
// - https://github.com/espressif/arduino-esp32/issues/1734
|
||||
// - https://github.com/Bodmer/TFT_eSPI/issues/189
|
||||
#ifndef max
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
void showLoadScreen();
|
||||
void initializeGameEntities();
|
||||
void drawStatusBar();
|
||||
void nonGamePlayLoop();
|
||||
void gamePlayLoop();
|
||||
void calcFrameRate();
|
||||
|
||||
void initScreenGame()
|
||||
{
|
||||
// if analog input pin 5 is unconnected, random analog
|
||||
// noise will cause the call to randomSeed() to generate
|
||||
// different seed numbers each time the sketch runs.
|
||||
// randomSeed() will then shuffle the random function.
|
||||
randomSeed(125);
|
||||
|
||||
// Show load screen
|
||||
showLoadScreen();
|
||||
|
||||
// Setup pipes
|
||||
initializeGameEntities();
|
||||
|
||||
_fpsStartTimeStamp = millis();
|
||||
_gameState = NEW_GAME;
|
||||
}
|
||||
|
||||
void handleScreenGame()
|
||||
{
|
||||
uint32_t timeNow = millis();
|
||||
if (timeNow - lastGameUpdate > DELAY_LOOP_MS)
|
||||
{
|
||||
//getDisplay()->clearDisplay();
|
||||
|
||||
drawStatusBar();
|
||||
if (_gameState == NEW_GAME || _gameState == GAME_OVER)
|
||||
{
|
||||
nonGamePlayLoop();
|
||||
}
|
||||
else if (_gameState == PLAYING)
|
||||
{
|
||||
gamePlayLoop();
|
||||
}
|
||||
calcFrameRate();
|
||||
}
|
||||
}
|
||||
|
||||
void drawText(int xText, int yText, const char* s, const uint8_t *font)
|
||||
{
|
||||
getDisplay()->setFont(font);
|
||||
getDisplay()->setCursor(xText, yText);
|
||||
getDisplay()->print(s);
|
||||
}
|
||||
|
||||
void drawCenterText(int yText, const char* s, const uint8_t *font)
|
||||
{
|
||||
int w = getDisplay()->getStrWidth(s);
|
||||
drawText(getDisplay()->getDisplayWidth() / 2 - w / 2, yText, s, font);
|
||||
}
|
||||
|
||||
void nonGamePlayLoop()
|
||||
{
|
||||
for (int i = 0; i < NUM_PIPES; i++)
|
||||
{
|
||||
_topPipes[i].draw();
|
||||
_bottomPipes[i].draw();
|
||||
}
|
||||
|
||||
int flapButtonVal = digitalRead(FLAP_BUTTON_INPUT_PIN);
|
||||
if (_gameState == NEW_GAME)
|
||||
{
|
||||
drawCenterText(15,STR_PRESS_FLAP_TO_PLAY,FONT16);
|
||||
|
||||
if (flapButtonVal == LOW)
|
||||
{
|
||||
_gameState = PLAYING;
|
||||
}
|
||||
}
|
||||
else if (_gameState == GAME_OVER)
|
||||
{
|
||||
int yText = 15;
|
||||
drawCenterText(yText,STR_GAME_OVER,FONT16);
|
||||
|
||||
yText = yText + 16 + 2;
|
||||
drawCenterText(yText,STR_PRESS_FLAP_TO_EXIT,FONT16);
|
||||
|
||||
// We ignore input a bit after game over so that user can see end game screen
|
||||
// and not accidentally start a new game
|
||||
if (flapButtonVal == LOW && millis() - _gameOverTimestamp >= IGNORE_INPUT_AFTER_GAME_OVER_MS)
|
||||
{
|
||||
// if the current state is game over, need to reset
|
||||
setDisplayState(mainscreen);
|
||||
}
|
||||
}
|
||||
|
||||
_bird.draw();
|
||||
}
|
||||
|
||||
void initializeGameEntities()
|
||||
{
|
||||
_points = 0;
|
||||
|
||||
_bird.setY(getDisplay()->getDisplayHeight() / 2 - _bird.getHeight() / 2);
|
||||
_bird.setDrawFill(true);
|
||||
|
||||
const int minStartXPipeLocation = getDisplay()->getDisplayWidth() / 2;
|
||||
int lastPipeX = minStartXPipeLocation;
|
||||
for (int i = 0; i < NUM_PIPES; i++)
|
||||
{
|
||||
|
||||
int pipeX = lastPipeX + random(MIN_PIPE_X_SPACING_DISTANCE, MAX_PIPE_X_SPACING_DISTANCE);
|
||||
int pipeWidth = random(MIN_PIPE_WIDTH, MAX_PIPE_WIDTH);
|
||||
|
||||
int yGapBetweenPipes = random(MIN_PIPE_Y_SPACE, MAX_PIPE_Y_SPACE);
|
||||
|
||||
int topPipeY = 0;
|
||||
int topPipeHeight = random(0, SCREEN_HEIGHT - yGapBetweenPipes);
|
||||
|
||||
int bottomPipeY = topPipeHeight + yGapBetweenPipes;
|
||||
int bottomPipeHeight = SCREEN_HEIGHT - bottomPipeY;
|
||||
|
||||
_topPipes[i].setLocation(pipeX, topPipeY);
|
||||
_topPipes[i].setDimensions(pipeWidth, topPipeHeight);
|
||||
_topPipes[i].setDrawFill(false);
|
||||
|
||||
_bottomPipes[i].setLocation(pipeX, bottomPipeY);
|
||||
_bottomPipes[i].setDimensions(pipeWidth, bottomPipeHeight);
|
||||
_topPipes[i].setDrawFill(false);
|
||||
|
||||
lastPipeX = _topPipes[i].getRight();
|
||||
}
|
||||
}
|
||||
|
||||
void gamePlayLoop()
|
||||
{
|
||||
int flapButtonVal = digitalRead(FLAP_BUTTON_INPUT_PIN);
|
||||
_bird.setY(_bird.getY() + _gravity);
|
||||
|
||||
if (flapButtonVal == LOW)
|
||||
{
|
||||
_bird.setY(_bird.getY() - 3);
|
||||
}
|
||||
_bird.forceInside(0, 0, getDisplay()->getDisplayWidth(), getDisplay()->getDisplayHeight());
|
||||
|
||||
// xMaxRight tracks the furthest right pixel of the furthest right pipe
|
||||
// which we will use to reposition pipes that go off the left part of screen
|
||||
int xMaxRight = 0;
|
||||
|
||||
// Iterate through pipes and check for collisions and scoring
|
||||
for (int i = 0; i < NUM_PIPES; i++)
|
||||
{
|
||||
|
||||
_topPipes[i].setX(_topPipes[i].getX() - _pipeSpeed);
|
||||
_bottomPipes[i].setX(_bottomPipes[i].getX() - _pipeSpeed);
|
||||
|
||||
_topPipes[i].draw();
|
||||
_bottomPipes[i].draw();
|
||||
|
||||
Serial.println(_topPipes[i].toString());
|
||||
|
||||
// Check if the bird passed by the pipe
|
||||
if (_topPipes[i].getRight() < _bird.getLeft())
|
||||
{
|
||||
|
||||
// If we're here, the bird has passed the pipe. Check to see
|
||||
// if we've marked it as passed yet. If not, then increment the score!
|
||||
if (_topPipes[i].getHasPassedBird() == false)
|
||||
{
|
||||
_points++;
|
||||
_topPipes[i].setHasPassedBird(true);
|
||||
_bottomPipes[i].setHasPassedBird(true);
|
||||
}
|
||||
}
|
||||
|
||||
// xMaxRight is used to track future placements of pipes once
|
||||
// they go off the left part of the screen
|
||||
if (xMaxRight < _topPipes[i].getRight())
|
||||
{
|
||||
xMaxRight = _topPipes[i].getRight();
|
||||
}
|
||||
|
||||
// Check for collisions and end of game
|
||||
if (_topPipes[i].overlaps(_bird))
|
||||
{
|
||||
_topPipes[i].setDrawFill(true);
|
||||
_gameState = GAME_OVER;
|
||||
_gameOverTimestamp = millis();
|
||||
}
|
||||
else
|
||||
{
|
||||
_topPipes[i].setDrawFill(false);
|
||||
}
|
||||
|
||||
if (_bottomPipes[i].overlaps(_bird))
|
||||
{
|
||||
_bottomPipes[i].setDrawFill(true);
|
||||
_gameState = GAME_OVER;
|
||||
_gameOverTimestamp = millis();
|
||||
}
|
||||
else
|
||||
{
|
||||
_bottomPipes[i].setDrawFill(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for pipes that have gone off the screen to the left
|
||||
// and reset them to off the screen on the right
|
||||
xMaxRight = max(xMaxRight, getDisplay()->getDisplayWidth());
|
||||
for (int i = 0; i < NUM_PIPES; i++)
|
||||
{
|
||||
if (_topPipes[i].getRight() < 0)
|
||||
{
|
||||
int pipeX = xMaxRight + random(MIN_PIPE_X_SPACING_DISTANCE, MAX_PIPE_X_SPACING_DISTANCE);
|
||||
int pipeWidth = random(MIN_PIPE_WIDTH, MAX_PIPE_WIDTH);
|
||||
|
||||
int yGapBetweenPipes = random(MIN_PIPE_Y_SPACE, MAX_PIPE_Y_SPACE);
|
||||
|
||||
int topPipeY = 0;
|
||||
int topPipeHeight = random(0, SCREEN_HEIGHT - yGapBetweenPipes);
|
||||
|
||||
int bottomPipeY = topPipeHeight + yGapBetweenPipes;
|
||||
int bottomPipeHeight = SCREEN_HEIGHT - bottomPipeY;
|
||||
|
||||
_topPipes[i].setLocation(pipeX, topPipeY);
|
||||
_topPipes[i].setDimensions(pipeWidth, topPipeHeight);
|
||||
_topPipes[i].setHasPassedBird(false);
|
||||
|
||||
_bottomPipes[i].setLocation(pipeX, bottomPipeY);
|
||||
_bottomPipes[i].setDimensions(pipeWidth, bottomPipeHeight);
|
||||
_bottomPipes[i].setHasPassedBird(false);
|
||||
|
||||
xMaxRight = _topPipes[i].getRight();
|
||||
}
|
||||
}
|
||||
|
||||
_bird.draw();
|
||||
}
|
||||
|
||||
void showLoadScreen()
|
||||
{
|
||||
// Clear the buffer
|
||||
getDisplay()->clearDisplay();
|
||||
|
||||
// Show load screen
|
||||
int yText = 10;
|
||||
|
||||
drawCenterText(yText,STR_LOADSCREEN_CREATOR,FONT8);
|
||||
yText = yText + 8 + 1;
|
||||
drawCenterText(yText,STR_LOADSCREEN_APP_NAME_LINE1,FONT16);
|
||||
yText = yText + 16 + 1;
|
||||
drawCenterText(yText,STR_LOADSCREEN_APP_NAME_LINE2,FONT8 );
|
||||
|
||||
delay(LOAD_SCREEN_SHOW_MS);
|
||||
getDisplay()->clearDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this every frame to calculate frame rate
|
||||
*/
|
||||
void calcFrameRate()
|
||||
{
|
||||
unsigned long elapsedTime = millis() - _fpsStartTimeStamp;
|
||||
_frameCount++;
|
||||
if (elapsedTime > 1000)
|
||||
{
|
||||
_fps = _frameCount / (elapsedTime / 1000.0);
|
||||
_fpsStartTimeStamp = millis();
|
||||
_frameCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the status bar at top of screen with points and fps
|
||||
*/
|
||||
void drawStatusBar()
|
||||
{
|
||||
// Draw accumulated points
|
||||
getDisplay()->setFont(FONT8);
|
||||
|
||||
getDisplay()->setCursor(0, 0); // draw points
|
||||
getDisplay()->print(_points);
|
||||
|
||||
// Draw frame count
|
||||
if (_drawFrameCount)
|
||||
{
|
||||
int16_t x1;
|
||||
getDisplay()->setFont(FONT8);
|
||||
x1 = getDisplay()->getDisplayWidth() - getDisplay()->getStrWidth("XX.XX fps");
|
||||
getDisplay()->setCursor(x1,0);
|
||||
getDisplay()->print(_fps);
|
||||
getDisplay()->print(" fps");
|
||||
|
||||
}
|
||||
}
|
||||
9
MilliOhmMeter_FW/src/screen_game.h
Normal file
9
MilliOhmMeter_FW/src/screen_game.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "shape.hpp"
|
||||
#include "display_screen.h"
|
||||
|
||||
|
||||
void initScreenGame(void);
|
||||
void handleScreenGame(void);
|
||||
@@ -1,76 +1,66 @@
|
||||
#include "screen_main.h"
|
||||
|
||||
c_onScreenButton ma20("20m", mA20, LocBottom, BUTTON1);
|
||||
c_onScreenButton ma200("200m", mA200, LocBottom, BUTTON2);
|
||||
c_onScreenButton ma1000("1A", mA1000, LocBottom, BUTTON3);
|
||||
c_onScreenButton mauto("Auto", mAuto, LocBottom, BUTTON4);
|
||||
c_onScreenButton bsetup("Conf", 5, LocBottom, BUTTON5);
|
||||
void setma20(void)
|
||||
{
|
||||
setMeasureMode(e_measureMode::mA20);
|
||||
}
|
||||
|
||||
void setma200(void)
|
||||
{
|
||||
setMeasureMode(e_measureMode::mA200);
|
||||
}
|
||||
|
||||
void setma1000(void)
|
||||
{
|
||||
setMeasureMode(e_measureMode::mA1000);
|
||||
}
|
||||
|
||||
void setmAuto(void)
|
||||
{
|
||||
setMeasureMode(e_measureMode::mAuto);
|
||||
}
|
||||
|
||||
void buttonSetup(void)
|
||||
{
|
||||
setDisplayState(e_displayState::setupscreen);
|
||||
log_i("Conf button pressed, go to setupscreen");
|
||||
}
|
||||
|
||||
c_onScreenButton ma20("20m", mA20, LocBottom, BUTTON1, &setma20);
|
||||
c_onScreenButton ma200("200m", mA200, LocBottom, BUTTON2, &setma200);
|
||||
c_onScreenButton ma1000("1A", mA1000, LocBottom, BUTTON3, &setma1000);
|
||||
c_onScreenButton mauto("Auto", mAuto, LocBottom, BUTTON4, &setmAuto);
|
||||
c_onScreenButton bsetup("Conf", 5, LocBottom, BUTTON5, &buttonSetup);
|
||||
c_onScreenButton errorState("ER", 6, LocRight, &getErrorState);
|
||||
c_onScreenButton okState("OK", 7, LocRight, &getOkState);
|
||||
c_onScreenButton openState("Open", 8, LocRight, &getOpenState);
|
||||
c_onScreenButton wifiState("Wifi", 9, LocRight, &getWifiState);
|
||||
|
||||
std::vector<c_onScreenButton*> MainScreen;
|
||||
//std::vector<c_onScreenButton*> MainScreen;
|
||||
screen_c mainScreen(e_displayState::mainscreen, 5);
|
||||
|
||||
const e_displayState thisScreen = e_displayState::mainscreen;
|
||||
|
||||
void initDisplayMain(void)
|
||||
{
|
||||
log_i("Setup main screen : ");
|
||||
uint16_t screenwidth = getDisplay()->getDisplayWidth();
|
||||
uint16_t buttonwidth = ((screenwidth / mLast) - CONTROLLOFFSET * 2 + 1);
|
||||
uint16_t currentWidth = 0;
|
||||
uint16_t ypos = getDisplay()->getDisplayHeight() - 1 - CONTROLSLINE_H + 2;
|
||||
|
||||
log_i("buttons: screenW=%d, buttonW=%d, ypos=%d", screenwidth, buttonwidth, ypos);
|
||||
// setup bottom buttons
|
||||
ma20.begin(currentWidth, ypos, buttonwidth, CONTROLSLINE_H + 6, CONTROLRADIUS);
|
||||
ma200.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
ma1000.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
mauto.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
bsetup.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
|
||||
// setup right side indicators
|
||||
uint16_t currentYpos = 0;
|
||||
uint16_t IndicatorXpos = screenwidth - INDICATORWIDTH;
|
||||
|
||||
log_i("indicators: indicW=%d, indicH=%d ypos,%d", INDICATORWIDTH, INDICATORHEIGHT, IndicatorXpos);
|
||||
|
||||
errorState.begin(IndicatorXpos, currentYpos, INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
|
||||
okState.begin(IndicatorXpos, currentYpos += (INDICATORHEIGHT - 1), INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
|
||||
openState.begin(IndicatorXpos, currentYpos += (INDICATORHEIGHT - 1), INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
|
||||
wifiState.begin(IndicatorXpos, currentYpos += (INDICATORHEIGHT - 1), INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
|
||||
|
||||
// fill vector
|
||||
log_i("Store main screen items");
|
||||
MainScreen.push_back(&ma20);
|
||||
MainScreen.push_back(&ma200);
|
||||
MainScreen.push_back(&ma1000);
|
||||
MainScreen.push_back(&mauto);
|
||||
MainScreen.push_back(&bsetup);
|
||||
mainScreen.addItem(&ma20);
|
||||
mainScreen.addItem(&ma200);
|
||||
mainScreen.addItem(&ma1000);
|
||||
mainScreen.addItem(&mauto);
|
||||
mainScreen.addItem(&bsetup);
|
||||
|
||||
MainScreen.push_back(&errorState);
|
||||
MainScreen.push_back(&okState);
|
||||
MainScreen.push_back(&openState);
|
||||
MainScreen.push_back(&wifiState);
|
||||
mainScreen.addItem(&errorState);
|
||||
mainScreen.addItem(&okState);
|
||||
mainScreen.addItem(&openState);
|
||||
mainScreen.addItem(&wifiState);
|
||||
|
||||
for (auto &&button : MainScreen)
|
||||
{
|
||||
button->setVisible(true);
|
||||
}
|
||||
mainScreen.begin();
|
||||
|
||||
log_i("mainscreen OK");
|
||||
}
|
||||
|
||||
void ScreenMainDrawButtons(void)
|
||||
{
|
||||
// draw controlstrip indicators
|
||||
for (auto &&thismode : MainScreen)
|
||||
{
|
||||
thismode->setDisplayState((thismode->getIndex() == (uint8_t)getMeasureMode()));
|
||||
thismode->drawButton();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenMainDrawValues(void)
|
||||
{
|
||||
if (getDisplay() == NULL)
|
||||
@@ -92,16 +82,10 @@ void ScreenMainDrawValues(void)
|
||||
getDisplay()->drawUTF8(60 + stringwidth + 3, 43, "mΩ");
|
||||
}
|
||||
|
||||
e_displayState ScreenMainHandle(void)
|
||||
void ScreenMainHandle(void)
|
||||
{
|
||||
for (auto &&i : MainScreen)
|
||||
{
|
||||
i->handle();
|
||||
}
|
||||
|
||||
if(bsetup.getState())
|
||||
{
|
||||
return e_displayState::setupscreen;
|
||||
}
|
||||
return thisScreen;
|
||||
mainScreen.activateModeButton();
|
||||
mainScreen.handle();
|
||||
mainScreen.draw();
|
||||
ScreenMainDrawValues();
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "display_types.h"
|
||||
#include "display_draw.h"
|
||||
#include "display_buttons.h"
|
||||
#include "display_screen.h"
|
||||
|
||||
|
||||
void initDisplayMain(void);
|
||||
void ScreenMainDrawValues(void);
|
||||
void ScreenMainDrawButtons(void);
|
||||
e_displayState ScreenMainHandle(void);
|
||||
void ScreenMainHandle(void);
|
||||
|
||||
@@ -1,53 +1,42 @@
|
||||
#include "screen_setup.h"
|
||||
|
||||
c_onScreenButton bback("<", mA20, LocBottom, BUTTON1);
|
||||
c_onScreenButton bforw(">", mA200, LocBottom, BUTTON2);
|
||||
c_onScreenButton bOK("OK", mA1000, LocBottom, BUTTON3);
|
||||
c_onScreenButton bGame("Game", mAuto, LocBottom, BUTTON4);
|
||||
c_onScreenButton bExit("Exit", 5, LocBottom, BUTTON5);
|
||||
void buttonExit(void)
|
||||
{
|
||||
setDisplayState(e_displayState::mainscreen);
|
||||
log_i("Exit button pressed, go to mainscreen");
|
||||
}
|
||||
|
||||
std::vector<c_onScreenButton *> SetupScreen;
|
||||
const e_displayState thisScreen = e_displayState::setupscreen;
|
||||
void buttonGame(void)
|
||||
{
|
||||
setDisplayState(e_displayState::gamescreen);
|
||||
SetFPSMax();
|
||||
initScreenGame();
|
||||
log_i("game button pressed, go to gamescreen");
|
||||
}
|
||||
|
||||
|
||||
|
||||
c_onScreenButton bback("<", 1, LocBottom, BUTTON1, NULL);
|
||||
c_onScreenButton bforw(">", 2, LocBottom, BUTTON2, NULL);
|
||||
c_onScreenButton bOK("OK", 3, LocBottom, BUTTON3, NULL);
|
||||
c_onScreenButton bGame("Game", 4, LocBottom, BUTTON4, &buttonGame);
|
||||
c_onScreenButton bExit("Exit", 5, LocBottom, BUTTON5, &buttonExit);
|
||||
|
||||
screen_c setupScreen(e_displayState::setupscreen, 5);
|
||||
|
||||
void initSetupScreen(void)
|
||||
{
|
||||
uint16_t screenwidth = getDisplay()->getDisplayWidth();
|
||||
uint16_t buttonwidth = ((screenwidth / mLast) - CONTROLLOFFSET * 2 + 1);
|
||||
uint16_t currentWidth = 0;
|
||||
uint16_t ypos = getDisplay()->getDisplayHeight() - 1 - CONTROLSLINE_H + 2;
|
||||
setupScreen.addItem(&bback);
|
||||
setupScreen.addItem(&bforw);
|
||||
setupScreen.addItem(&bOK);
|
||||
setupScreen.addItem(&bGame);
|
||||
setupScreen.addItem(&bExit);
|
||||
|
||||
bback.begin(currentWidth, ypos, buttonwidth, CONTROLSLINE_H + 6, CONTROLRADIUS);
|
||||
bforw.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
bOK.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
bGame.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
bExit.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
|
||||
|
||||
SetupScreen.push_back(&bback);
|
||||
SetupScreen.push_back(&bforw);
|
||||
SetupScreen.push_back(&bOK);
|
||||
SetupScreen.push_back(&bGame);
|
||||
SetupScreen.push_back(&bExit);
|
||||
setupScreen.begin();
|
||||
}
|
||||
|
||||
void ScreenSetupDrawButtons(void)
|
||||
void ScreenSetupHandle(void)
|
||||
{
|
||||
// draw controlstrip indicators
|
||||
for (auto &&thismode : SetupScreen)
|
||||
{
|
||||
thismode->drawButton();
|
||||
}
|
||||
}
|
||||
|
||||
e_displayState ScreenSetupHandle(void)
|
||||
{
|
||||
for (auto &&i : SetupScreen)
|
||||
{
|
||||
i->handle();
|
||||
}
|
||||
|
||||
if(bExit.getState())
|
||||
{
|
||||
return e_displayState::mainscreen;
|
||||
}
|
||||
return thisScreen;
|
||||
setupScreen.handle();
|
||||
setupScreen.draw();
|
||||
}
|
||||
@@ -3,8 +3,9 @@
|
||||
#include "Arduino.h"
|
||||
#include "display_types.h"
|
||||
#include "display_buttons.h"
|
||||
#include "display_screen.h"
|
||||
#include "screen_game.h"
|
||||
|
||||
void initSetupScreen(void);
|
||||
|
||||
e_displayState ScreenSetupHandle(void);
|
||||
void ScreenSetupDrawButtons(void);
|
||||
void ScreenSetupHandle(void);
|
||||
|
||||
493
MilliOhmMeter_FW/src/shape.hpp
Normal file
493
MilliOhmMeter_FW/src/shape.hpp
Normal file
@@ -0,0 +1,493 @@
|
||||
#include "display_hall.h"
|
||||
|
||||
|
||||
class Shape {
|
||||
|
||||
protected:
|
||||
int _x;
|
||||
int _y;
|
||||
int _width;
|
||||
int _height;
|
||||
bool _drawFill = DEFAULT_DRAW_FILL;
|
||||
bool _drawBoundingBox = false;
|
||||
|
||||
public:
|
||||
const bool DEFAULT_DRAW_FILL = false;
|
||||
|
||||
Shape(int x, int y, int width, int height)
|
||||
: Shape(x, y, width, height, DEFAULT_DRAW_FILL){
|
||||
// purposefully empty
|
||||
}
|
||||
|
||||
Shape(int x, int y, int width, int height, bool drawFillOn) {
|
||||
_x = x;
|
||||
_y = y;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_drawFill = drawFillOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggles whether to draw the bounding box around the shape
|
||||
*
|
||||
* @param drawBoundingBox If true, then a bounding box will be drawn around the shape
|
||||
*/
|
||||
void setDrawBoundingBox(bool drawBoundingBox){
|
||||
_drawBoundingBox = drawBoundingBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggles whether to draw the object as filled or just an outline
|
||||
*
|
||||
* @param drawFill If true, then the shape will be drawn filled (solid)
|
||||
*/
|
||||
void setDrawFill(bool drawFill){
|
||||
_drawFill = drawFill;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convenience function to set location of shape. Simply calls setX(x) and setY(y)
|
||||
*
|
||||
* @param x The x location of the shape
|
||||
* @param y The y location of the shape
|
||||
*/
|
||||
void setLocation(int x, int y){
|
||||
setX(x);
|
||||
setY(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the y location of the shape
|
||||
*
|
||||
* @param y The y location of the shape
|
||||
*/
|
||||
void setY(int y){
|
||||
_y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the x location of the shape
|
||||
*
|
||||
* @param x The x location of the shape
|
||||
*/
|
||||
void setX(int x){
|
||||
_x = x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the x location of the shape
|
||||
*
|
||||
* @return int x location of the shape
|
||||
*/
|
||||
int getX() const{
|
||||
return _x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the y location of the shape
|
||||
*
|
||||
* @return int y locaiton of the shape
|
||||
*/
|
||||
int getY() const{
|
||||
return _y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A virtual function that should be overloaded by sub-classes. Deriving
|
||||
* classes should call
|
||||
*
|
||||
* @param display A reference to the Adafruit_SSD1306 object
|
||||
*/
|
||||
virtual void draw( void) {
|
||||
if(_drawBoundingBox){
|
||||
getDisplay()->drawFrame(_x, _y, _width, _height); //drawRect(_x, _y, _width, _height, SSD1306_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the size (width, height) of the shape. This is a virtual
|
||||
* function. Deriving classes may want to handle setDimensions differently.
|
||||
* For example, the Circle class forces only uses the 'width' parameter
|
||||
* and sets height = width.
|
||||
*
|
||||
* @param width The width of the shape
|
||||
* @param height The height of the shape
|
||||
*/
|
||||
virtual void setDimensions(int width, int height){
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the width of the shape
|
||||
*
|
||||
* @return int Width of the shape in pixels
|
||||
*/
|
||||
int getWidth() const{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the height of the shape
|
||||
*
|
||||
* @return int Height of the shape in pixels
|
||||
*/
|
||||
int getHeight() const{
|
||||
return _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the left (x) location of the shape, which is just getX()
|
||||
*
|
||||
* @return int The left location of the shape
|
||||
*/
|
||||
int getLeft() const{
|
||||
return _x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the right (x) location, which is getX() + getWidth()
|
||||
*
|
||||
* @return int The right location (x) of the shape
|
||||
*/
|
||||
int getRight() const{
|
||||
return _x + _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the bottom of the shape (y), which is getY() + getHeight()
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getBottom() const{
|
||||
return _y + _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the top of the shape, which is just getY()
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getTop() const{
|
||||
return _y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempts to force the x,y location of the shape into the
|
||||
* provided rectangular boundary (x, y, width, height)
|
||||
*
|
||||
* @param x X location of boundary
|
||||
* @param y Y location of boundary
|
||||
* @param width Width of boundary
|
||||
* @param height Height of boundary
|
||||
*/
|
||||
void forceInside(int x, int y, int width, int height){
|
||||
if(getTop() <= y){
|
||||
setY(y);
|
||||
}else if(getBottom() >= y + height){
|
||||
setY((y + height) - getHeight() - 1);
|
||||
}
|
||||
|
||||
if(getLeft() <= x){
|
||||
setX(x);
|
||||
}else if(getRight() >= x + width){
|
||||
setX((x + width) - getWidth() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks to see if the current shape overlaps with the provided shape.
|
||||
* Overlap is defined as any pixel between this shape touching (equaling)
|
||||
* the passed in shape
|
||||
*
|
||||
* @param shape The shape to check for overlap
|
||||
* @return true If the shape overlaps
|
||||
* @return false If the shape does not overlap
|
||||
*/
|
||||
virtual bool overlaps(const Shape& shape) const{
|
||||
//Serial.println("We are in overlaps shape!");
|
||||
|
||||
// based on https://stackoverflow.com/a/4098512
|
||||
return !(getRight() < shape._x ||
|
||||
getBottom() < shape._y ||
|
||||
_x > shape.getRight() ||
|
||||
_y > shape.getBottom());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if this shape contains the x,y location. If so, returns true.
|
||||
*
|
||||
* @param x The X location to check
|
||||
* @param y The Y location to check
|
||||
* @return true If this shape contains the x,y
|
||||
* @return false If this shape does NOT contain the x,y
|
||||
*/
|
||||
virtual bool contains(int x, int y) const {
|
||||
return x >= _x && // check within left edge
|
||||
x <= (_x + _width) && // check within right edge
|
||||
y >= _y && // check within top edge
|
||||
y <= (_y + _height); // check within bottom edge
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the name of this object. Sub-classes should override this.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
virtual String getName() const{
|
||||
return "Shape";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a basic toString() for the object. Sub-classes should override
|
||||
* to get a more fitting description
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
virtual String toString() const{
|
||||
return (String)"x: " + _x + " y: " + _y + " width: " + _width + " height: " + _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Static function that calculates the Euclidean distance between two points
|
||||
*
|
||||
* @param x1
|
||||
* @param y1
|
||||
* @param x2
|
||||
* @param y2
|
||||
* @return float
|
||||
*/
|
||||
static float distance(int x1, int y1, int x2, int y2){
|
||||
return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A Rectangle shape class that extends Shape.
|
||||
*
|
||||
*/
|
||||
class Rectangle : public Shape {
|
||||
public:
|
||||
Rectangle(int x, int y, int width, int height)
|
||||
: Rectangle(x, y, width, height, DEFAULT_DRAW_FILL)
|
||||
{
|
||||
// purposefully empty
|
||||
}
|
||||
|
||||
Rectangle(int x, int y, int width, int height, bool drawFillOn)
|
||||
: Shape(x, y, width, height)
|
||||
{
|
||||
// purposefully empty
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draw the rectangle
|
||||
*
|
||||
* @param display
|
||||
*/
|
||||
void draw (void) override{
|
||||
// Draw rectangle takes in (xTop, yTop, width, height)
|
||||
// https://learn.adafruit.com/adafruit-gfx-graphics-library/graphics-primitives#rectangles-2002784-10
|
||||
if(_drawFill){
|
||||
getDisplay()->drawFrame(_x, _y, _width, _height);
|
||||
}else{
|
||||
getDisplay()->drawBox(_x, _y, _width, _height);
|
||||
}
|
||||
|
||||
// We don't call the parent class draw() call because
|
||||
// the bounding box is simple another rectangle
|
||||
// Shape::draw(disp);
|
||||
}
|
||||
|
||||
String getName() const override{
|
||||
return "Rectangle";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A circular shape class that extends Shape
|
||||
*
|
||||
*/
|
||||
class Circle : public Shape {
|
||||
|
||||
public:
|
||||
Circle(int xCenter, int yCenter, int radius) :
|
||||
Circle(xCenter, yCenter, radius, DEFAULT_DRAW_FILL)
|
||||
{
|
||||
// purposefully empty
|
||||
}
|
||||
|
||||
Circle(int xCenter, int yCenter, int radius, bool drawFillOn) :
|
||||
Shape(xCenter - radius, yCenter - radius, (radius * 2) + 1, (radius * 2) + 1, drawFillOn)
|
||||
{
|
||||
// purposefully empty
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draw the circle
|
||||
*
|
||||
* @param display
|
||||
*/
|
||||
void draw() override{
|
||||
// Draw circle takes in (xCenter, yCenter, radius)
|
||||
// https://learn.adafruit.com/adafruit-gfx-graphics-library/graphics-primitives#circles-2002788-14
|
||||
int radius = getRadius();
|
||||
if(_drawFill){
|
||||
getDisplay()->drawDisc(_x + radius, _y + radius, radius);
|
||||
}else{
|
||||
getDisplay()->drawCircle(_x + radius, _y + radius, radius);
|
||||
}
|
||||
|
||||
// Call super method
|
||||
Shape::draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the overlap between two circular objects
|
||||
*
|
||||
* @param circle
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool overlaps(const Circle& circle) const {
|
||||
int distanceFromCenterPoints = Shape::distance(getCenterX(), getCenterY(), circle.getCenterX(), circle.getCenterY());
|
||||
return distanceFromCenterPoints <= getRadius() + circle.getRadius();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the overlap between two shapes
|
||||
*
|
||||
* @param shape
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool overlaps(const Shape& shape) const override{
|
||||
if(getName().equals(shape.getName())){
|
||||
return this->overlaps((Circle&)shape);
|
||||
}
|
||||
|
||||
// Default to parent overlaps function
|
||||
return Shape::overlaps(shape);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Uses the width value to set the circle diameter. The height parameter is ignored.
|
||||
*
|
||||
* @param width sets the circle diameter
|
||||
* @param height ignored
|
||||
*/
|
||||
void setDimensions(int width, int height) override{
|
||||
Shape::setDimensions(width, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the X center of the circle
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getCenterX() const{
|
||||
return _x + getWidth() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the Y center of the circle
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getCenterY() const{
|
||||
return _y + getWidth() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Moves the location of the object so that the center is at x,y
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return int
|
||||
*/
|
||||
void setCenter(int x, int y){
|
||||
int radius = getRadius();
|
||||
setLocation(x - radius, y - radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the radius of the circle
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getRadius() const{
|
||||
return getWidth() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the radius of the circle
|
||||
*
|
||||
* @param radius
|
||||
*/
|
||||
void setRadius(int radius){
|
||||
int size = 2 * radius;
|
||||
setDimensions(size, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the circle
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String getName() const override{
|
||||
return "Circle";
|
||||
}
|
||||
};
|
||||
|
||||
class Ball : public Circle{
|
||||
|
||||
protected:
|
||||
int _xSpeed = 0;
|
||||
int _ySpeed = 0;
|
||||
|
||||
public:
|
||||
Ball(int xCenter, int yCenter, int radius) : Circle(xCenter, yCenter, radius)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
boolean checkYBounce(int yMin, int yMax){
|
||||
return getTop() <= yMin || getBottom() >= yMax;
|
||||
}
|
||||
|
||||
boolean checkXBounce(int xMin, int xMax){
|
||||
return getLeft() <= xMin || getRight() >= xMax;
|
||||
}
|
||||
|
||||
void setSpeed(int xSpeed, int ySpeed){
|
||||
_xSpeed = xSpeed;
|
||||
_ySpeed = ySpeed;
|
||||
}
|
||||
|
||||
int getXSpeed() const{
|
||||
return _xSpeed;
|
||||
}
|
||||
|
||||
int getYSpeed() const{
|
||||
return _ySpeed;
|
||||
}
|
||||
|
||||
void update(){
|
||||
_x += _xSpeed;
|
||||
_y += _ySpeed;
|
||||
}
|
||||
|
||||
int reverseYSpeed(){
|
||||
_ySpeed = _ySpeed * -1;
|
||||
return _ySpeed;
|
||||
}
|
||||
|
||||
int reverseXSpeed(){
|
||||
_xSpeed = _xSpeed * -1;
|
||||
return _xSpeed;
|
||||
}
|
||||
|
||||
String getName() const override{
|
||||
return "Ball";
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user