Electgpl

ARDUINO - Arkanoid MEGA

Oct 11th, 2015
147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.36 KB | None | 0 0
  1. #include "LedControl.h"
  2. //game orientation
  3. //0 == regular
  4. //1 == paddle on the rightmost column
  5. //2 == upside-down
  6. //3 == paddle on the leftmost column
  7. //change the orientation on the fly using the button
  8. //S6 and the optional wiring described from line #223 on.
  9. byte Orientation = 0;
  10.  
  11. //milliseconds to wait after a ball got lost
  12. const unsigned long respawn_duration = 1500;
  13.  
  14. //the speed setting heavily depends on the used processor type
  15. //on my Arduino Nano, speed_max = 10 and speed_min = 60 seem reasonable values.
  16. //if you're having a faster machine, it makes sense to increase those values
  17. const int speed_max = 10;                      //amount of screen refresh cycles that are wasted...
  18. const int speed_min = 60;                      //...until the ball moves on one pixel
  19. int Speed;
  20.  
  21. int Paddle = 0;                                //current x-position of paddle
  22. byte Paddle_Old = Paddle;
  23. byte Intensity = 8;                            //brightness of the display
  24. byte Intensity_Old = Intensity;                
  25.  
  26. //current ball position
  27. byte BallX = 5;                                
  28. byte BallY = 4;
  29. byte BallX_Old = BallX;
  30. byte BallY_Old = BallY;
  31.  
  32. //current ball speed in x and y direction
  33. //note: the "char" variable can be negative, so -1 in BallDX means,
  34. //that it moves to the left
  35. char BallDX = 0;
  36. char BallDY = 0;
  37. char BallDX_tbs = 0;
  38. char BallDY_tbs = 0;
  39.  
  40. //maximum amount of balls and current balls left
  41. const byte Balls_max = 3;
  42. byte Balls = 3;
  43.  
  44. //the timer is used to de-couple the paddle movement from the ball-speed
  45. int timer = 0;
  46.  
  47. //the respawn_timer is used to wait until the next ball appears after you lose one
  48. unsigned long respawn_timer = 0;
  49.  
  50. //used for measuring a quick paddle movement right before the ball hits the paddle
  51. //to give the ball an extra spin; this is needed in situations, where you otherwise
  52. //would not be able to clear all "bricks".
  53. byte LastPaddlePos = 0;
  54. int RelativePaddleSpeed = 0;
  55. byte PaddleSpeedThreshold = 1;
  56.  
  57. //debounce the optional button S6 that can be used for changeing the orientation
  58. int ButtonPressedInterval = 0;
  59. const int ButtonPressedMinimumDuration = 10; //how long needs the button to be pressed to be recognized?
  60.  
  61. //this is the level pattern of the "bricks"
  62. //modify to create tougher or easier levels
  63. byte bricks[3][8] =
  64. {
  65.     {1, 1, 1, 1, 1, 1, 1, 1},
  66.     {1, 1, 1, 1, 1, 1, 1, 1},
  67.     {1, 1, 1, 1, 1, 1, 1, 1}
  68. };
  69.  
  70. // :-) smiley shown, if you win the game
  71. byte smiley_won[8][8] =
  72. {
  73.     {0, 0, 1, 1, 1, 1, 0, 0},
  74.     {0, 1, 0, 0, 0, 0, 1, 0},
  75.     {1, 0, 1, 0, 0, 1, 0, 1},
  76.     {1, 0, 0, 0, 0, 0, 0, 1},
  77.     {1, 0, 1, 0, 0, 1, 0, 1},
  78.     {1, 0, 0, 1, 1, 0, 0, 1},
  79.     {0, 1, 0, 0, 0, 0, 1, 0},
  80.     {0, 0, 1, 1, 1, 1, 0, 0}    
  81. };
  82.  
  83. // :-| smiley shown, if you loose the game
  84. byte smiley_lost[8][8] =
  85. {
  86.     {0, 0, 1, 1, 1, 1, 0, 0},
  87.     {0, 1, 0, 0, 0, 0, 1, 0},
  88.     {1, 0, 1, 0, 0, 1, 0, 1},
  89.     {1, 0, 0, 0, 0, 0, 0, 1},
  90.     {1, 0, 1, 1, 1, 1, 0, 1},
  91.     {1, 0, 0, 0, 0, 0, 0, 1},
  92.     {0, 1, 0, 0, 0, 0, 1, 0},
  93.     {0, 0, 1, 1, 1, 1, 0, 0}    
  94. };
  95.  
  96.  
  97.  
  98. /* ******************************************
  99.     MAX7221 connections to 8x8 LED matrix
  100.  
  101.     DP => ROW1        DG1 => COL1
  102.     A  => ROW2        DG2 => COL2
  103.     B  => ROW3        DG3 => COL3
  104.       ...                ...    
  105.     G  => ROW8        DG8 => COL8
  106.    
  107.     Additionally:
  108.    
  109.     1. Connect DIN, CLK, CS as shown below to digital inputs of the Arduino Nano
  110.     2. Connect VCC to 5V and GND to GND of the Arduino Nano
  111. */
  112.  
  113. const byte DataIn = 2;    //D2 => DIN
  114. const byte CLK    = 3;    //D3 => CLK
  115. const byte LOAD   = 4;    //D4 => CS
  116.  
  117. //create a new object to control the 8x8 LED matrix
  118. LedControl Matrix = LedControl(DataIn, CLK, LOAD, 1);
  119.  
  120. /* ****************************************** */
  121.  
  122. /* *************************************************
  123.     1st 10k potentiometer to A0 (brightness)
  124.     2nd 10k potentiometer to A1 (paddle)
  125.     unused analog pin for random seed
  126.     PWM enabled digital pin for audio
  127.     3 digital pins for remaining ball LEDs
  128.    
  129.     Wiring hints:
  130.    
  131.     1. In the middle of your DuinoKit (Essentials)
  132.        you find "copies" of the Arduino ports, e.g.
  133.        A0, A1, GND and 5V. Use those "copies" to
  134.        connect to the potentiometers as they are in
  135.        close proximity.
  136.        
  137.     2. Connect A0 to the middle pin of the first
  138.        potentiometer. It will be used for
  139.        controlling the speed and the brightness
  140.        
  141.     3. Connect A1 to the middle pin of the second
  142.        potentiometer. It will be used for playing
  143.        the game (i.e. controlling the paddle)
  144.        
  145.     4. Create a "5V current column" by connecting
  146.        5V from "the middle" with the first PIN of
  147.        a row of the white breadboard (have a look
  148.        at the video, minute 4:25 an onwards).
  149.        Also create a "GND" (ground) column.
  150.        This dramatically simplifies the further
  151.        wiring of the potentiometers and the LEDs.
  152.        
  153.     5. Wire the top pin of the two potentiometers
  154.        with your GND column on the breadboard.
  155.        
  156.     6. Wire the bottom pin of the two
  157.        potentiometers with the 5V column.
  158.        
  159.     7. Wire the left side of the topmost green LED
  160.        (called LED23) with your GND column.
  161.        
  162.     8. Wire from here (GND LED23) directly to
  163.        GND LED24 and from there to GND LED 25.
  164.        (Always on the left side, see video at the
  165.        above-mentioned position and following.)
  166.        
  167.     9. Wire D5, D6 and D7 with the right side of
  168.        the green LEDs: D5 => LED23
  169.                        D6 => LED24
  170.                        D7 => LED25
  171.                        
  172.    10. If you're using the original DuinoKit,
  173.        wire D9 with the "+" pin of the Speaker.
  174.        Wire the "-" pin with your GND column.
  175.        
  176.    11. If you're using DuinoKit Essentials, wire
  177.        D9 with the "+" pin of the Buzzer.
  178.        Wire the "-" pin with your GND column.
  179.        
  180.    12. Optional bonus wiring (tested on the original
  181.        DuinoKit only): Wire the top left pin of
  182.        switch S6 with D12. Wire the top right pin
  183.        of switch S6 to GND using one of the free
  184.        GND pins "in the middle" of your DuinoKit.
  185.    ************************************************* */
  186.    
  187. const byte potBrightness = A0;
  188. const byte potPaddle     = A1;
  189. const byte UnusedAnalog  = A6;
  190.  
  191. const byte PWM_Audio     = 9;
  192.  
  193. const byte BallDisplay[Balls_max] = {5, 6, 7};
  194.  
  195. const byte switchOrientation =12; //D12
  196.  
  197. /* ******************************************
  198.     SETUP
  199.    ****************************************** */
  200.    
  201. void setup()
  202. {  
  203.     //initialize sees with floating analog number
  204.     randomSeed(analogRead(UnusedAnalog));
  205.        
  206.     //The MAX72XX is in power-saving mode on startup, we have to do a wakeup call
  207.     Matrix.shutdown(0, false);
  208.  
  209.     //Set the brightness to a medium values
  210.     Matrix.setIntensity(0, 8);
  211.  
  212.     //clear display
  213.     Matrix.clearDisplay(0);
  214.    
  215.     //set the digital ports of the Arduino
  216.     //that we use for audio output and for
  217.     //driving the LEDs to OUTPUT
  218.     pinMode(PWM_Audio, OUTPUT);
  219.     for (int i = 0; i < Balls_max; i++)
  220.         pinMode(BallDisplay[i], OUTPUT);
  221.        
  222.     //digital input for switch S6 (orientation)
  223.     //activating the pullup means and wiring as described
  224.     //above means, that the input will read HIGH when
  225.     //the switch is open and LOW when the switch is pressed
  226.     pinMode(switchOrientation, INPUT_PULLUP);
  227.    
  228.    
  229.     //random ball start position and direction
  230.     randomBall();
  231.     BallDY_tbs = 1;
  232.     respawn_timer = millis() + (2 * respawn_duration);
  233.    
  234.     LastPaddlePos = analogRead(potPaddle);
  235. }
  236.  
  237. /* ******************************************
  238.     Handle the orientation
  239.    ****************************************** */
  240.  
  241. void handleOrientation()
  242. {  
  243.     if (digitalRead(switchOrientation) == LOW)
  244.         ButtonPressedInterval++;
  245.     else
  246.     {
  247.         if (ButtonPressedInterval != 0)
  248.         {
  249.             if (ButtonPressedInterval >= ButtonPressedMinimumDuration)
  250.             {
  251.                 Matrix.clearDisplay(0);
  252.                 Orientation = (Orientation + 1) % 4;
  253.             }
  254.            
  255.             ButtonPressedInterval = 0;
  256.         }
  257.     }
  258.    
  259.     //support "upside-down" orientations by inverting the Paddle
  260.     if (Orientation >= 2)
  261.         Paddle = 6 - Paddle;
  262. }
  263.  
  264. //draws a pixel while respecting the orientation
  265. void putPixel(byte x, byte y, boolean on)
  266. {
  267.     switch (Orientation)
  268.     {
  269.         case 0: Matrix.setLed(0, x, y, on); break;
  270.         case 1: Matrix.setLed(0, y, 7 - x, on); break;
  271.         case 2: Matrix.setLed(0, 7 - x, 7 - y, on); break;
  272.         case 3: Matrix.setLed(0, 7 - y, x, on); break;
  273.     }
  274. }
  275.  
  276. /* ******************************************
  277.     GAME
  278.    ****************************************** */
  279.    
  280. //draw a two pixel wide paddle
  281. void drawPaddle(byte x, boolean on)
  282. {
  283.     putPixel(x, 7, on);
  284.     putPixel(x + 1, 7, on);
  285. }
  286.  
  287. //draw the new ball and remove it at its old position
  288. void drawBall()
  289. {
  290.     if ((BallX != BallX_Old) || (BallY != BallY_Old))
  291.         putPixel(BallX_Old, BallY_Old, false);
  292.     putPixel(BallX, BallY, true);
  293. }
  294.  
  295. //draw a pattern, e.g. the playfield and the smileys
  296. void drawPattern(byte pattern[8][8], byte y_count)
  297. {
  298.     for (byte y = 0; y < y_count; y++)
  299.         for (byte x = 0; x < 8; x++)
  300.             putPixel(x, y, pattern[y][x] == 1);
  301. }
  302.  
  303. //random ball position and random x-movement
  304. void randomBall()
  305. {
  306.     BallX = random(1, 7);
  307.     BallY = random(3, 5);
  308.     BallDX_tbs = random(10) < 5 ? 1 : -1;
  309.     BallDY_tbs = 1;
  310. }
  311.  
  312. //show the remaining balls in a row of LEDs
  313. void displayBallsLeft()
  314. {
  315.     for (int i = 0; i < Balls_max; i++)
  316.         digitalWrite(BallDisplay[i], i < Balls ? HIGH : LOW);
  317. }
  318.  
  319. //play various noisy sounds
  320. void noise(int freq1, int freq2, int duration)
  321. {
  322.     while (duration--)
  323.     {
  324.         digitalWrite(PWM_Audio, HIGH);
  325.         delayMicroseconds(random(freq1) / 2);
  326.         digitalWrite(PWM_Audio, LOW);
  327.         delayMicroseconds(random(freq2) / 2);
  328.     }
  329. }
  330.  
  331.  
  332. /* ******************************************
  333.     MAIN LOOP
  334.    ****************************************** */
  335.  
  336. void loop()
  337. {    
  338.     //sample analog data for Paddle, 8x8 display Intensity
  339.     //and speed; only go to 1020 to avoid fibrillation
  340.     Paddle = map(analogRead(potPaddle), 0, 1020, 0, 6);  
  341.     int poti = analogRead(potBrightness);
  342.     Intensity = map(poti, 0, 1020, 0, 15);
  343.     Speed = map(poti, 0, 1020, speed_min, speed_max);
  344.  
  345.     //handle the orientation switching (optional, you can comment it out if not needed)
  346.     handleOrientation();    
  347.        
  348.     //draw all playfield elements
  349.     drawPattern(bricks, 3);
  350.     drawBall();
  351.     if (Paddle != Paddle_Old)
  352.         drawPaddle(Paddle_Old, false);
  353.     drawPaddle(Paddle, true);
  354.     Paddle_Old = Paddle;
  355.    
  356.     //collision detection with playfield
  357.     if (BallY < 3) //performance opt., unclear if lazy eval works, so two statements
  358.         if (bricks[BallY][BallX] == 1)
  359.         {
  360.             noise(500, 500, 3);
  361.  
  362.             bricks[BallY][BallX] = 0;    //the hit brick disappears
  363.             BallDY = 1;                  //ball reflects and starts to move down again
  364.            
  365.             //x-reflection of the ball while respecting the playfield boundaries
  366.             if (BallX == 0)
  367.                 BallDX = 1;
  368.             else if (BallX == 7)
  369.                 BallDX = -1;
  370.             else
  371.                 BallDX = random(10) < 5 ? 1 : -1;
  372.                
  373.             //check if the game is won
  374.             boolean won = true; //asume the game is won
  375.             byte wy = 0;
  376.             while (won && wy < 3)
  377.             {
  378.                 byte wx = 0;                    
  379.                 while (won && wx < 8)
  380.                 {
  381.                     if (bricks[wy][wx] == 1)
  382.                         won = false; //a single remaining bricks leads to continued playing
  383.                     wx++;
  384.                 }
  385.                 wy++;
  386.             }
  387.            
  388.             //end screen and "applause"
  389.             //endless loop: game can be restarted only by a reset
  390.             if (won)
  391.             {
  392.                 drawPattern(smiley_won, 8);
  393.                 while (true)
  394.                     noise(300, 500, 1000);
  395.             }
  396.         }
  397.        
  398.     //calculate ball movement
  399.     //the ball movement is de-coupled from the paddle movement, i.e. a lower ball speed
  400.     //does NOT lower the paddle movement speed
  401.     if (timer++ >= Speed)
  402.     {
  403.         //RelativePaddleSpeed is important, if you need to give the ball a special
  404.         //"spin" either to left or to right in cases it moves to uniformly and you
  405.         //cannot reach certain bricks
  406.         RelativePaddleSpeed = Paddle - LastPaddlePos;
  407.         LastPaddlePos = Paddle;
  408.        
  409.         //reset the timer
  410.         timer = 0;
  411.        
  412.         //calculate ball movement
  413.         BallX_Old = BallX;
  414.         BallY_Old = BallY;
  415.         BallX += BallDX;
  416.         BallY += BallDY;
  417.        
  418.         //reflection at the LEFT playfield side
  419.         if (BallX == 0)
  420.         {
  421.             BallDX = 1;
  422.             noise(100, 100, 2);
  423.         }
  424.  
  425.         //reflection at the RIGHT playfield side            
  426.         if (BallX == 7)
  427.         {
  428.             BallDX = -1;
  429.             noise(100, 100, 2);
  430.         }
  431.            
  432.         //reflection at the TOP of the playfield
  433.         if (BallY == 0)
  434.         {
  435.             BallDY = 1;
  436.             noise(100, 100, 2);
  437.         }
  438.                
  439.         //ball hits the paddle
  440.         if (BallY == 6)
  441.         {
  442.             //if the ball is coming from the left (and moving to the right)...
  443.             if (BallDX > 0)
  444.             {
  445.                 //... and if the ball hits the paddle
  446.                 if (BallX + 1 == Paddle || BallX  == Paddle || BallX - 1 == Paddle)
  447.                 {
  448.                     BallDY = -1; // .. then reflect it upwards
  449.                    
  450.                     //and if it hit the paddle at the very left, then also reflect it left
  451.                     if (BallX + 1 == Paddle && BallX != 0)
  452.                         BallDX = -BallDX;
  453.                        
  454.                     //make a short clicking noise when the paddle was hit
  455.                     noise(200, 200, 6);
  456.                 }
  457.             }
  458.            
  459.             //the same game as shown above - but now, the ball is coming from the right
  460.             else
  461.             {
  462.                 if (BallX - 2 == Paddle || BallX - 1 == Paddle || BallX == Paddle)
  463.                 {
  464.                     BallDY = -1;
  465.                     if (BallX - 2 == Paddle && BallX != 7)
  466.                         BallDX = -BallDX;
  467.                     noise(200, 200, 6);
  468.                 }
  469.             }
  470.            
  471.             //possibility to influence the ball with a speedy paddle movement
  472.             //you can give it a "spin" either to the left or to the right
  473.             if (abs(RelativePaddleSpeed) >= PaddleSpeedThreshold)
  474.             {
  475.                 if (BallX < 6 && RelativePaddleSpeed > 0)
  476.                     BallX++;
  477.                
  478.                 if (BallX > 1 && RelativePaddleSpeed < 0)
  479.                     BallX--;
  480.             }
  481.         }
  482.        
  483.         //lost ball
  484.         if (BallY == 8)
  485.         {
  486.             noise(7000, 10000, 100);
  487.             Balls--;
  488.            
  489.             //if there are balls left: respawn
  490.             if (Balls)
  491.             {
  492.                 randomBall();
  493.                 BallDX = 0;
  494.                 BallDY = 0;
  495.                 respawn_timer = millis() + respawn_duration;
  496.             }
  497.            
  498.             //otherwise: show the sad smiley and go to an
  499.             //endless loop that can be only left by resetting the system
  500.             else
  501.             {
  502.                 displayBallsLeft();
  503.                 drawPattern(smiley_lost, 8);                
  504.                 while (true) ;
  505.             }
  506.         }
  507.     }
  508.    
  509.     //respawn management
  510.     if (BallDX_tbs != 0)
  511.     {
  512.         if (millis() >= respawn_timer)
  513.         {
  514.             BallDX = BallDX_tbs;
  515.             BallDY = BallDY_tbs;
  516.             BallDX_tbs = BallDY_tbs = respawn_timer = 0;
  517.         }
  518.     }
  519.    
  520.     //change the brightness of the LEDs
  521.     if (Intensity != Intensity_Old)
  522.     {
  523.         Matrix.setIntensity(0, Intensity);
  524.         Intensity_Old = Intensity;
  525.     }
  526.  
  527.     //show remaining balls using LEDs  
  528.     displayBallsLeft();
  529. }
Add Comment
Please, Sign In to add comment