//DISPLAY INI start #include #include #include #include #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); #define NUMFLAKES 10 //probably not needed leftovers from OLED demo #define XPOS 0 #define YPOS 1 #define DELTAY 2 #define LOGO16_GLCD_HEIGHT 64 #define LOGO16_GLCD_WIDTH 128 static const unsigned char PROGMEM logo16_glcd_bmp[] = //Here I make the initial splash screen { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x3F, 0xC0, 0xFF, 0x80, 0x1F, 0xF0, 0x03, 0xF8, 0x7E, 0x00, 0x00, 0xFE, 0x03, 0xF8, 0x07, 0xC0, 0xFF, 0xC3, 0xFF, 0xC0, 0x7F, 0xFC, 0x03, 0xF8, 0xFE, 0x00, 0x03, 0xFF, 0x87, 0xF8, 0x07, 0xC3, 0xFF, 0xC7, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xF8, 0xFE, 0x00, 0x07, 0xFF, 0xC7, 0xF8, 0x07, 0xC3, 0xFF, 0xCF, 0xFF, 0xC3, 0xFF, 0xFF, 0x83, 0xF9, 0xFC, 0x00, 0x0F, 0xFF, 0xE7, 0xF8, 0x07, 0xC7, 0xFF, 0xDF, 0xFF, 0xC3, 0xFF, 0xFF, 0x03, 0xF9, 0xFC, 0x00, 0x0F, 0xFF, 0xF7, 0xF8, 0x07, 0xC7, 0xFF, 0xDF, 0xFF, 0xC7, 0xFF, 0xFE, 0x03, 0xF9, 0xFC, 0x00, 0x0F, 0xFF, 0xF7, 0xF8, 0x07, 0xC7, 0xFF, 0xDF, 0xFF, 0x87, 0xFF, 0xFC, 0x03, 0xFB, 0xFC, 0x00, 0x0F, 0xE7, 0xF3, 0xF8, 0x07, 0xC7, 0xF0, 0x1F, 0xE0, 0x0F, 0xF8, 0x18, 0x03, 0xFB, 0xF8, 0x00, 0x1F, 0xC3, 0xFB, 0xF8, 0x07, 0xC7, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x1F, 0xC3, 0xFB, 0xF8, 0x07, 0xC7, 0xF0, 0x07, 0xF8, 0x0F, 0xE1, 0xFF, 0xF3, 0xFF, 0xF0, 0x00, 0x1F, 0xC7, 0xF3, 0xF8, 0x07, 0xC7, 0xFF, 0xC7, 0xFE, 0x0F, 0xE3, 0xFF, 0xF3, 0xFF, 0xF0, 0x00, 0x1F, 0xCF, 0xF7, 0xF8, 0x07, 0xC7, 0xFF, 0xC3, 0xFF, 0x0F, 0xE3, 0xFF, 0xF3, 0xFF, 0xFC, 0x00, 0x1F, 0xDF, 0xF7, 0xF8, 0x07, 0xC7, 0xFF, 0xC0, 0xFF, 0x8F, 0xE3, 0xFF, 0xE3, 0xFF, 0xFC, 0x00, 0x1F, 0xDF, 0xE7, 0xF8, 0x07, 0xC7, 0xFF, 0xC0, 0x7F, 0xCF, 0xE3, 0xFF, 0xE3, 0xFF, 0xFE, 0x08, 0x1F, 0xDF, 0xE7, 0xF8, 0x07, 0xC7, 0xFF, 0xC0, 0x3F, 0xCF, 0xE1, 0xFF, 0xE3, 0xF8, 0xFE, 0x01, 0x1F, 0xDF, 0xC7, 0xF8, 0x07, 0xC7, 0xF0, 0x00, 0x3F, 0xCF, 0xF0, 0x1F, 0xE3, 0xF8, 0xFE, 0x00, 0x5F, 0xCF, 0x07, 0xF8, 0x07, 0xC7, 0xF0, 0x00, 0x7F, 0xC7, 0xFC, 0x3F, 0xC3, 0xF8, 0xFE, 0x03, 0xFF, 0xC0, 0x03, 0xFC, 0x07, 0xC7, 0xF0, 0x1F, 0xFF, 0xC7, 0xFF, 0xFF, 0xC3, 0xF8, 0xFE, 0x23, 0xFF, 0xC0, 0x03, 0xFF, 0xE7, 0xC7, 0xF0, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, 0x83, 0xF8, 0xFE, 0x7F, 0xFF, 0xC0, 0x03, 0xFF, 0xE7, 0xC7, 0xF0, 0x1F, 0xFF, 0xC1, 0xFF, 0xFF, 0x03, 0xF8, 0xFE, 0x7F, 0xDF, 0xC0, 0x03, 0xFF, 0xE7, 0xC7, 0xF0, 0x1F, 0xFF, 0x80, 0xFF, 0xFE, 0x03, 0xF8, 0xFE, 0x3F, 0x1F, 0xC0, 0x01, 0xFF, 0xE7, 0xC7, 0xF0, 0x1F, 0xFF, 0x00, 0x7F, 0xFC, 0x03, 0xF8, 0xFE, 0x06, 0x1F, 0xC0, 0x00, 0xFF, 0xE7, 0xC7, 0xF0, 0x1F, 0xFC, 0x00, 0x1F, 0xF8, 0x03, 0xF8, 0xFE, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0xE7, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0x8E, 0x3F, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xDB, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0x7F, 0xFB, 0xDF, 0xFF, 0xFF, 0xFF, 0xDB, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0x7F, 0xFB, 0xDF, 0xFF, 0xFF, 0xFF, 0xDB, 0x18, 0x34, 0xD3, 0x1C, 0x73, 0xF7, 0xF8, 0x3F, 0xDF, 0x7C, 0x71, 0xC7, 0x3D, 0x31, 0x97, 0xC6, 0xEB, 0xB5, 0xEE, 0xEB, 0xAD, 0xE7, 0xFB, 0xBF, 0xDF, 0x7B, 0xBB, 0xDA, 0xDE, 0xEE, 0xAB, 0xCE, 0xEE, 0x73, 0xEE, 0x0B, 0xF1, 0xF7, 0xFE, 0x7F, 0xDF, 0x7B, 0xBB, 0xDB, 0x1E, 0xE0, 0xAB, 0xD6, 0xED, 0xF3, 0xEE, 0xFB, 0xED, 0xF7, 0xFD, 0xFF, 0xDF, 0x7B, 0xBB, 0xDA, 0xDE, 0xEF, 0xAB, 0xD6, 0xEB, 0xB5, 0xEE, 0xEB, 0xAD, 0xF7, 0xFB, 0xBF, 0xDB, 0x6B, 0xBA, 0xDA, 0xDE, 0xEE, 0xAB, 0x93, 0x18, 0x24, 0xC7, 0x1C, 0x72, 0xF7, 0xF8, 0x3F, 0x82, 0x0C, 0x7D, 0xDB, 0x2C, 0x71, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; #if (SSD1306_LCDHEIGHT != 64) //probably not needed #error("Height incorrect, please fix Adafruit_SSD1306.h!"); #endif //DISPLAY INI END //bullet byte rounds = 1; //round counter byte led = 13; //default LED byte speaker = 11; //speaker pin byte knob = 0; //analog knob byte landscape[128]; //landscape is an array containing height data. This is a byte, not int, as I am running out of memory. Adafruit takes 82% of RAM so no much left for the game itself byte tankAX = 15; //hard coded tank A position in X axis byte tankBX = 113; //hard coded tank B position in X axis bool playerA = true; //PlayerA starts. When false that means player B is playing void GenerateLandscape(){ //function to generate landscape - landscape[0] = 63; //very left is at zero. Note that adafruit library has 0,0 on the top left corner, thats why it is 63, so pixel no.64 landscape[31] = random(32,63); // 1/4 from the left edge, random height from a range landscape[63] = random(20,63); // middle of the screen, again a random height. landscape[95] = random(32,63); // 3/4 towards right edge, random height. landscape[127] = 63; //right edge, at zero float increment = landscape[31] - landscape[0]; //this + below function generates slopes between the randomly generated heights. This should be a loop, but there are just 3 gaps to fill and I was lazy. increment = increment/32; for (byte i = 1; i <= 31; i++){ landscape[0+i] = landscape[0]+increment*i; } increment = landscape[63] - landscape[31]; increment = increment/32; for (byte i = 1; i <= 31; i++){ landscape[31+i] = landscape[31]+increment*i; } increment = landscape[95] - landscape[63]; increment = increment/32; for (byte i = 1; i <= 31; i++){ landscape[63+i] = landscape[63]+increment*i; } increment = landscape[127] - landscape[95]; increment = increment/32; for (byte i = 1; i <= 31; i++){ landscape[95+i] = landscape[95]+increment*i; } } void draw_terrain(){ //for drawing terrain. I use fast line drawing. for (int i = 0; i < 129; i++) { display.drawFastVLine(i,landscape[i],63, 1); } } void draw_tanks(){ //tanks are just circles of a set radius. X position fixed, Y position taken from the landscape array. display.drawCircle(tankAX, landscape[tankAX], 4, 1); display.drawCircle(tankBX, landscape[tankBX], 4, 1); display.setCursor(80,0); //drawing the rounds number display.print("Round "); display.print(rounds); } void shoot(){ //this really is the main loop of the game. Everything takes place here. int tankX; //tank is the current tank Variable for X position int tankY; int targetX; //target is the enemy tank int targetY; float bulletX; //variable for bullets This is position on X axis float bulletY; //variable for bullets This is position on Y axis float bulletXX; //convention is that XX is the velocity of the bullet in the X direction float bulletYY; // float angle = 1; //angle in radians. All calculations are in radians by default float power = 45; bool aim1 = true; //aim1 is for angle bool aim2 = true; //aim2 is for power int button; if (playerA == true){ //assign correct coordinates for tank (current player) and target (enemy) tankX = tankAX; tankY = landscape[tankAX]; targetX = tankBX; targetY = landscape[tankBX]; } if (playerA ==false){ tankX = tankBX; tankY = landscape[tankBX]; targetX = tankAX; targetY = landscape[tankAX]; } while (aim1 == true){ //in this loop you make angle aim angle = analogRead(knob); //read the analog input angle = angle / 670 * 3.1416; //convert analog input to radians. I divide by 670 and not 1023 because I used 3.3V as reference. I just had it on the board for the OLED and was easier than dragging 5V just for potentiometer. Zero to Pi is 0 to 180 degrees. display.setCursor(0,0); //writing the current angle value display.print("Angle:"); display.println(angle); display.drawLine(tankX, tankY, tankX + cos(angle)*15, tankY - sin(angle)*15,1); //this draws the turret. draw_terrain(); draw_tanks(); display.display(); display.clearDisplay(); delay(10); //delay so I won't work at crazy framerates (can cause screen flickering in OLED). ~100FPS should be fast enough. button = digitalRead(2); //read button state if (button == 0){ //detect click. delay(50); //wait 50ms and see if it was really pressed. Lame button noise detection. if (button ==0){ aim1= false; } } } delay(100); //I give you 100ms to get your finger off that button. Kinda Lame as well, and should be easy to fix with few lines. while (aim2 == true){//now power selection power = analogRead(knob); power = power / 670; //again, divide by 670 due to 3.3v as reference. This makes power to be a range of 0 to 1. I use floats so that's cool. display.setCursor(0,0); display.print("Angle:"); display.println(angle); //display the angle display.print("Power:"); display.println(power); //and current power display.drawLine(tankX, tankY, tankX + cos(angle)*15, tankY - sin(angle)*15,1); //draw turret Would be cool to modify it so it gets larger when the power is higher ;) draw_terrain(); draw_tanks(); display.display(); display.clearDisplay(); delay(10); //100FPS limiter here as well button = digitalRead(2); //read button state if (button == 0){ //detect click, lame way again, sorry delay(50); if (button ==0){ aim2= false; } } } //now angle and power is set. Let's get this bullet going! bool hit = false; //hit is true when you hit. bulletXX = cos(angle)*5*power; //calculating bullet velocity. I multiply by 5 to get a nice distance between calculations. This was trial end error. I do not need to calculate sub-pixel movement of the bullet, so if at high power I get movement by ~3 pixels that's ok. bulletYY = -sin(angle)*5*power; bulletX = tankX; //bullet starts at current player position bulletY = tankY; float distance; //distance is used for hit-miss calculations. I wanted to have doppler effect driven sounds, but it turned out to be weird. It worked and all, but was not ear-pleasing. float distanceX; float distanceY; while(hit == false){ //loop for the bullet to fly display.drawLine(tankX, tankY, tankX + cos(angle)*15, tankY - sin(angle)*15,1); //draw turret draw_terrain(); draw_tanks(); display.drawPixel(bulletX, bulletY, WHITE); //this is the bullet trace. I do not clear the screen inside of this while-loop, so I do not need an array to store the old bullet positions. distanceX = targetX - bulletX; //calculat the distance between the bullet and the target distanceY = targetY - bulletY; distance = sqrt(distanceX*distanceX+distanceY*distanceY); display.display(); if (distance < 5){ //if you are closer than 5 pixels - consider this a hit display.setCursor(10,30); display.print("Player "); //some congratulations if (playerA == true){ display.print("1 WINS!!!"); }else{ display.print("2 WINS!!!"); } display.display(); tone(speaker,440,2000); //a monotone noise for the winner! hit = true; //hit! leave that while loop! delay(5000); //5 sec delay after hitting a target } int intbulletX = bulletX; // convert the bullet X position to int. if (bulletY > landscape[intbulletX] || bulletY > 63 ){ //detect hitting the ground or going below the screen. hit = true; //yeah, I make it hit=true, but you are hitting the ground buddy ;) } bulletYY = bulletYY + 0.07; //bullet Y velocity changes due to gravity. I add a number (got by trial and error) so that velocity will "increase", but increase is in "down" direction - remember the adafruit having 0.0 at top left. bulletX = bulletX + bulletXX; //position X affected by velocity bulletY = bulletY + bulletYY; //position Y affected by velocity tone(speaker,440+bulletY); //tone o 440Hz plus the height of the bullet } //this part is when you leave the while loop of the current bullet flight. noTone(speaker); aim1= true; aim2 = true; } void setup() { Serial.begin(9600); // by default, we'll generate the high voltage from the 3.3v line internally! (neat!) display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64) // init done display.clearDisplay(); // Clear the buffer. // initialize the digital pin as an output. pinMode(led, OUTPUT); pinMode(speaker, OUTPUT); pinMode(knob, INPUT); pinMode(2, INPUT); //button digitalWrite(2, HIGH); //enabled pull-up, so no resistor needed for button operations. Neat-o. randomSeed(analogRead(0)); //random seed for landscape generation display.drawBitmap(0, 0, logo16_glcd_bmp, 128, 64, 1); //splash screen display.display(); delay(3000); display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); GenerateLandscape(); } // the loop routine runs over and over again forever: void loop() { shoot(); //call shooting display.display(); display.clearDisplay(); delay(1000); //one second delay between shots if (playerA == true){ //switch between player A and B playerA = false;}else{ playerA = true; rounds++;} //increment round count after switch from B to A }