Examples > TFT

TFT Pong

This sketch is a basic implementation of pong for the TFT screen with an Arduino Uno.

This version of the game creates a rectangular platform that can move in two directions, and a ball that bounces against the edges of the screen as well as the movable platform. Two potentiometers (or other analog sensor) control the position of the platform.

The example demonstrates collision detection between objects on the screen, as well as how to quickly update images without erasing the entire screen every loop()

Hardware Required

  • Arduino Uno
  • Arduino TFT screen
  • breadboard
  • hookup wire
  • two 10-kilohm potentiometers

Circuit

Connect power and ground to the breadboard.
Show the circuitShow the circuit

Place the potentiometers on the breadboard. On each pot, connect one side to ground, and the other to power. Connect the middle pin of one potentiometer to A0, the other one to A1.
Show the circuitShow the circuit

Connect the TFT screen to the breadboard. The headers on the side of the screen with the small blue tab and arrow should be the ones that attach to the board. Pay attention to the orientation of the screen, in these images, it is upside down.
Show the circuitShow the circuit

Connect the BL and +5V pins to power, and GND to ground. Connect CS-LD to pin 10, DC to pin 9, RESET to pin 8, MOSI to pin 11, and SCK to pin 13. If uyou're using a Leonardo, you'll be using different pins. see the getting started page for more details.
Show the circuitShow the circuit

Code

To use the screen you must first include the SPI and TFT libraries.

#include <SPI.h>
#include <TFT.h>

Define the pins you're going to use for controlling the screen, and create an instance of the TFT library named TFTscreen. You'll reference that object whenever you're working with the screen.

#define cs   10
#define dc   9
#define rst  8  

TFT TFTscreen = TFT(cs, dc, rst);

Set up the variables for the ball and paddle x & y positions, the ball's direction, and the previous locations of the ball and paddle.

int paddleX = 0;
int paddleY = 0;
int oldPaddleX, oldPaddleY;
int ballDirectionX = 1;
int ballDirectionY = 1;

int ballX, ballY, oldBallX, oldBallY;

In setup(), initialize the display and clear the screen's background.

void setup() {
  TFTscreen.begin();
  TFTscreen.background(0,0,0);
}

loop() starts by storing the width and height of the screen, an reading the values of the potentiometers, before mapping them to a useful range.

void loop() {
  int myWidth = TFTscreen.width();
  int myHeight = TFTscreen.height();

  paddleX = map(analogRead(A0), 0, 1023, 0, myWidth) - 20/2;
  paddleY = map(analogRead(A1), 0, 1023, 0, myHeight) - 5/2;

Set the fill color to black, and erase the previous location of the paddle if it has moved.

TFTscreen.fill(0,0,0);

  if (oldPaddleX != paddleX || oldPaddleY != paddleY) {
    TFTscreen.rect(oldPaddleX, oldPaddleY, 20, 5);
  }

Set the fill color to white, and draw the paddle.

TFTscreen.fill(255,255,255);
  TFTscreen.rect(paddleX, paddleY, 20, 5);

Save the paddle's current location as the previous location, so the next time through you can check if it has moved.

oldPaddleX = paddleX;
  oldPaddleY = paddleY;

At the end of loop(), use the value of the ballSpeed variable to determine how quickly the display will update. Once you've finished with the example, you could add another potentiometer and change the speed dynamically by changing the value of ballSpeed.

You'll call a custom function named moveBall() to update the ball's position.

if (millis() % ballSpeed < 2) {
  moveBall();
  }
}

moveBall() will update the ball's position, erase its previous location, and draw it in the new spot. It will also check to make sure it does not go off the screen, reversing direction when it hits the sides. This also calls a second custom function named inPaddle() which checks for intersections of the ball and paddle.

void moveBall() {
  if (ballX > TFTscreen.width() || ballX < 0) {
    ballDirectionX = -ballDirectionX;
  }
  if (ballY > TFTscreen.height() || ballY < 0) {
    ballDirectionY = -ballDirectionY;
  }  
  if (inPaddle(ballX, ballY, paddleX, paddleY, 20, 5)) {
    ballDirectionY = -ballDirectionY;
  }

  ballX += ballDirectionX;
  ballY += ballDirectionY;

  TFTscreen.fill(0,0,0);

  if (oldBallX != ballX || oldBallY != ballY) {
    TFTscreen.rect(oldBallX, oldBallY, 5, 5);
  }

  TFTscreen.fill(255,255,255);
  TFTscreen.rect(ballX, ballY, 5, 5);

  oldBallX = ballX;
  oldBallY = ballY;
}

inPaddle() checks to see if the paddle and ball occupy the same space. If so, it returns TRUE, which reverses the ball's direction in moveBall().

boolean inPaddle(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
  boolean result = false;

  if ((x >= rectX && x <= (rectX + rectWidth)) &&
    (y >= rectY && y <= (rectY + rectHeight))) {
    result = true;
  }
  return result;  
}

The complete sketch is below :

/*

 TFT Pong

 This example for the Arduino screen reads the values
 of 2 potentiometers to move a rectangular platform
 on the x and y axes. The platform can intersect
 with a ball causing it to bounce.

 This example code is in the public domain.

 Created by Tom Igoe December 2012
 Modified 15 April 2013 by Scott Fitzgerald

 http://www.arduino.cc/en/Tutorial/TFTPong

 */


#include <TFT.h>  // Arduino LCD library
#include <SPI.h>

// pin definition for the Uno
#define cs   10
#define dc   9
#define rst  8

// pin definition for the Leonardo
// #define cs   7
// #define dc   0
// #define rst  1

TFT TFTscreen = TFT(cs, dc, rst);

// variables for the position of the ball and paddle
int paddleX = 0;
int paddleY = 0;
int oldPaddleX, oldPaddleY;
int ballDirectionX = 1;
int ballDirectionY = 1;

int ballSpeed = 10; // lower numbers are faster

int ballX, ballY, oldBallX, oldBallY;

void setup() {
  // initialize the display
  TFTscreen.begin();
  // black background
  TFTscreen.background(0, 0, 0);
}

void loop() {

  // save the width and height of the screen
  int myWidth = TFTscreen.width();
  int myHeight = TFTscreen.height();

  // map the paddle's location to the position of the potentiometers
  paddleX = map(analogRead(A0), 512, -512, 0, myWidth) - 20 / 2;
  paddleY = map(analogRead(A1), 512, -512, 0, myHeight) - 5 / 2;

  // set the fill color to black and erase the previous
  // position of the paddle if different from present
  TFTscreen.fill(0, 0, 0);

  if (oldPaddleX != paddleX || oldPaddleY != paddleY) {
    TFTscreen.rect(oldPaddleX, oldPaddleY, 20, 5);
  }

  // draw the paddle on screen, save the current position
  // as the previous.
  TFTscreen.fill(255, 255, 255);

  TFTscreen.rect(paddleX, paddleY, 20, 5);
  oldPaddleX = paddleX;
  oldPaddleY = paddleY;

  // update the ball's position and draw it on screen
  if (millis() % ballSpeed < 2) {
    moveBall();
  }
}

// this function determines the ball's position on screen
void moveBall() {
  // if the ball goes offscreen, reverse the direction:
  if (ballX > TFTscreen.width() || ballX < 0) {
    ballDirectionX = -ballDirectionX;
  }

  if (ballY > TFTscreen.height() || ballY < 0) {
    ballDirectionY = -ballDirectionY;
  }

  // check if the ball and the paddle occupy the same space on screen
  if (inPaddle(ballX, ballY, paddleX, paddleY, 20, 5)) {
    ballDirectionX = -ballDirectionX;
    ballDirectionY = -ballDirectionY;
  }

  // update the ball's position
  ballX += ballDirectionX;
  ballY += ballDirectionY;

  // erase the ball's previous position
  TFTscreen.fill(0, 0, 0);

  if (oldBallX != ballX || oldBallY != ballY) {
    TFTscreen.rect(oldBallX, oldBallY, 5, 5);
  }


  // draw the ball's current position
  TFTscreen.fill(255, 255, 255);
  TFTscreen.rect(ballX, ballY, 5, 5);

  oldBallX = ballX;
  oldBallY = ballY;

}

// this function checks the position of the ball
// to see if it intersects with the paddle
bool inPaddle(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
  bool result = false;

  if ((x >= rectX && x <= (rectX + rectWidth)) &&
      (y >= rectY && y <= (rectY + rectHeight))) {
    result = true;
  }

  return result;
}