NXTChuck pong game

Here is a pong game I made for the NXT. It uses the NXTChuck to read a Wii Nunchuk, and uses the values as the user input to the game.

In the video I was using two NXTChucks and Nunchuks, in two-player game mode. If you want to play single-player, you can change just one line of the program so that it compiles for single-player mode.

You need to have the NXC NXTChuck library, available from Dexter Industries’ downloads page.

Lines 3 through 16 are constants that determine the characteristics of the game. Most of them are fairly self-explanatory. Try changing them, and don’t worry, I have listed all the original values in the program, so you can always change it back really easily. Line 16 tells the compiler to compile for 1 or for 2 controllers.

#include "NXTChuck lib.nxc"

#define SpeedUpAmount      5  // 5
#define InitialSpeed       50 // 50
#define InitialBallSize    4  // 4
#define InitialPaddleWidth 10 // 10

#define MinX 1                // 1
#define MaxX 98               // 98
#define MinY 1                // 0
#define MaxY 55               // 55

#define NXTChuck_1_PORT S1    // S1
#define NXTChuck_2_PORT S2    // S2

#define Controllers 2         // 2

byte N1_SX, N1_SY;
int N1_AX, N1_AY, N1_AZ;
byte N1_B;

#if Controllers == 2
byte N2_SX, N2_SY;
int N2_AX, N2_AY, N2_AZ;
byte N2_B;
#endif

long LeftScore, RightScore, TotalScore;     // scores

float TotalSpeed;                      // ball speed
byte BallRadius;                       // ball size
byte PaddleHalfWidth;                  // paddle size

int PaddleL, PaddleR;                  // paddle positions

int BallXspeedBase, BallYspeedBase;    // base speed, expenced to be in the range of 0-100
char BallXdir, BallYdir;               // ball travle direction
float BallXPos, BallYPos;              // current position
int BallXlast, BallYlast;    // was a byte, and should be fine as such         // last position, for erasing the last frame

unsigned long LastTick, ThisTick;

#define Clip(in,min,max) (in<min?min:(in>max?max:in))

inline long Round(float Value){
  long Result = Value > 0 ? Value + 0.5 : Value - 0.5 ;
  return Result;
}

float ScaleRange(float Value, float ValueMin, float ValueMax, float DesiredMin, float DesiredMax)
{
  return (DesiredMax - DesiredMin) * (Value - ValueMin) / (ValueMax - ValueMin) + DesiredMin;
}

int ScaleRangeInt(float Value, float ValueMin, float ValueMax, float DesiredMin, float DesiredMax)
{
  return (DesiredMax - DesiredMin) * (Value - ValueMin) / (ValueMax - ValueMin) + DesiredMin;
}

void InitializeValues(bool First){
  LeftScore = 0;
  RightScore = 0;
  TotalScore = 0;

  TotalSpeed = InitialSpeed;
  BallRadius = InitialBallSize;
  PaddleHalfWidth = InitialPaddleWidth;

  BallXspeedBase = ScaleRange(Random(), -32768, 32767, 45, 55);
  BallYspeedBase = 100 - BallXspeedBase;

  BallXdir = Random()&1?1:-1;
  Wait(Random(25));
  BallYdir = Random()&1?1:-1;

  BallXPos = (MaxX - MinX) / 2;
  BallYPos = (MaxY - MinY) / 2;

  if(First){
    ClearScreen();
    TextOut(12, LCD_LINE1, "Initializing");
  
    until(NXTChuckInit(NXTChuck_1_PORT, false) == NXTChuck_COM_SUCCESS);
    until(NXTChuckIdent(NXTChuck_1_PORT) == NXTChuck_DEVICE_NUNCHUK);

#if Controllers == 2
    until(NXTChuckInit(NXTChuck_2_PORT, false) == NXTChuck_COM_SUCCESS);
    until(NXTChuckIdent(NXTChuck_2_PORT) == NXTChuck_DEVICE_NUNCHUK);
#endif

    ClearScreen();
    TextOut(23, LCD_LINE1, "Press z");
    TextOut(23, LCD_LINE2, "to start.");
  }
  else{
    TextOut(17, LCD_LINE7, "Press z to");
    TextOut(23, LCD_LINE8, "restart.");
  }

#if Controllers == 2
  while(!(N1_B&N2_B&NXTChuck_N_BTN_Z)){
    NXTChuckReadNunchuk(NXTChuck_2_PORT, N2_SX, N2_SY, N2_AX, N2_AY, N2_AZ, N2_B);
#else
  while(!(N1_B&NXTChuck_N_BTN_Z)){
#endif
    NXTChuckReadNunchuk(NXTChuck_1_PORT, N1_SX, N1_SY, N1_AX, N1_AY, N1_AZ, N1_B);

    Wait(10);
  }
  
  ClearScreen();
  LastTick = CurrentTick();
}

void UpdateDisplay(){
  CircleOut(BallXlast, BallYlast, BallRadius, 0x36);
  BallXlast = Round(BallXPos);
  BallYlast = Round(BallYPos);
  CircleOut(BallXlast, BallYlast, BallRadius, 0x32);

  LineOut(MinX - 1, PaddleL-PaddleHalfWidth, MinX - 1, PaddleL + PaddleHalfWidth);
  LineOut(MaxX + 1, PaddleR-PaddleHalfWidth, MaxX + 1, PaddleR + PaddleHalfWidth);
  
  LineOut(0, 55, 99, 55);
  LineOut(0, 0, 99, 0);

  ClearLine(LCD_LINE1);
  NumOut(37, LCD_LINE1, TotalScore);
  NumOut(0, LCD_LINE1, LeftScore);
  NumOut(75, LCD_LINE1, RightScore);
}

void ChangeBaseSpeeds(){
  char OffsetFromEven = BallXspeedBase - 50;
  BallXspeedBase += Round(ScaleRange(Random(), -32768, 32767, OffsetFromEven>0?-5:-4, OffsetFromEven<0?5:4));
  BallYspeedBase = 100 - BallXspeedBase;

  if(BallXspeedBase > 90){
    BallXspeedBase = 90;
    BallYspeedBase = 10;
  }
  else if(BallXspeedBase < 10){
    BallXspeedBase = 10;
    BallYspeedBase = 90;
  }
}

char CheckEdge(){
  if(BallYPos < (MinY + BallRadius)){
    BallYdir = -BallYdir;
    BallYPos = MinY + BallRadius;
    ChangeBaseSpeeds();
  }
  if(BallYPos > (MaxY - BallRadius)){
    BallYdir = -BallYdir;
    BallYPos = MaxY - BallRadius;
    ChangeBaseSpeeds();
  }

  char result = 0;
  
  if(BallXPos < (MinX + BallRadius)){
    if(Round(BallYPos) > (PaddleL + PaddleHalfWidth) || Round(BallYPos) < (PaddleL - PaddleHalfWidth)){
      result = -1;
    }
    else{
      result = 1;
    }
    BallXdir = -BallXdir;
    BallXPos = MinX + BallRadius;
    ChangeBaseSpeeds();
  }
  
  if(BallXPos > (MaxX - BallRadius)){
    if(Round(BallYPos) > (PaddleR + PaddleHalfWidth) || Round(BallYPos) < (PaddleR - PaddleHalfWidth)){
      result = -2;
    }
    else{
      result = 2;
    }
    BallXdir = -BallXdir;
    BallXPos = MaxX - BallRadius;
    ChangeBaseSpeeds();
  }
  
  return result;
}

char Edge;

task Game(){
  InitializeValues(true);
  while(true){

    NXTChuckReadNunchuk(NXTChuck_1_PORT, N1_SX, N1_SY, N1_AX, N1_AY, N1_AZ, N1_B);
    PaddleL = Clip(ScaleRangeInt(N1_SY, 0, 255, MinY + PaddleHalfWidth, MaxY - PaddleHalfWidth), MinY + PaddleHalfWidth, MaxY - PaddleHalfWidth);

#if Controllers == 2
    NXTChuckReadNunchuk(NXTChuck_2_PORT, N2_SX, N2_SY, N2_AX, N2_AY, N2_AZ, N1_B);
    PaddleR = Clip(ScaleRangeInt(N2_SY, 0, 255, MinY + PaddleHalfWidth, MaxY - PaddleHalfWidth), MinY + PaddleHalfWidth, MaxY - PaddleHalfWidth);
#else
    PaddleR = PaddleL;
#endif

    ThisTick = CurrentTick();
    
    BallXPos += (ScaleRange((BallXspeedBase * BallXdir), -100, 100, -TotalSpeed, TotalSpeed) * (ThisTick - LastTick) / 1000 );
    BallYPos += (ScaleRange((BallYspeedBase * BallYdir), -100, 100, -TotalSpeed, TotalSpeed) * (ThisTick - LastTick) / 1000 );
    
    LastTick = ThisTick;

    Edge = CheckEdge();
    if(Edge == 0){
    
    }
    else if(Edge == 1){
      LeftScore++;
      TotalScore++;
      TotalSpeed += SpeedUpAmount;
    }
    else if(Edge == 2){
      RightScore++;
      TotalScore++;
      TotalSpeed += SpeedUpAmount;
    }
    else if(Edge == -1 || Edge == -2){
      ClearScreen();
      NumOut(37, LCD_LINE1, TotalScore);
      NumOut(0, LCD_LINE1, LeftScore);
      NumOut(75, LCD_LINE1, RightScore);
      TextOut(18, LCD_LINE2, "Game Over,");
      if(Edge == -1){
        TextOut(18, LCD_LINE3, "Left Lost.");
      }
      if(Edge == -2){
        TextOut(15, LCD_LINE3, "Right Lost.");
      }
      TextOut(0, LCD_LINE5, "Speed Was =");
      NumOut(72, LCD_LINE5, TotalSpeed);

      InitializeValues(false);
    }

    UpdateDisplay();
    
    Wait(10);
  }
}

task main(){
  start Game;
}
About these ads
This entry was posted in Dexter Industries, Mindstorms, NXC, NXT and tagged , , , , . Bookmark the permalink.

3 Responses to NXTChuck pong game

  1. Pingback: Pong for the LEGO MINDSTORMSDexter Industries Blog

  2. Pingback: LEGO MINDSTORMS NXT Pong Game using Wii Nunchucks - Hacked Gadgets – DIY Tech Blog

  3. Pingback: LEGO MINDSTORMS NXT Pong Game using Wii Nunchucks | Price Gadget Reviews

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s