Advertisement
Guest User

BreakOut on 8x8 Matrix V2.0 for DuinoKit

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