In this post, I will show how to create a Tic-Tac-Toe game all within Nextion Logic without the need for an external microprocessor. As we go through the process of creating this game, I will deal with a few techniques that may be useful in your projects.

So this is a relatively simple game, so let’s get started.

Materials Used

Resources:
– Enhanced Nextion 3.5″ Model NX4832K035_011
– 4 Picture Resources (blank, “O”,”X” and logo)
– 1 8×16 ZI font

Components:
– 9 Picture Components for the board
– 1 Variable Component for which player’s turn
– 1 Variable Component for play count
– 1 Hotspot Component to hold 2 functions
– 1 Text Component for play status
– 1 Picture Component for logo (game reset)

Techniques demonstrated:
– use of System Variables sys0, sys1, sys2
– use the Operational instruction click
– animate sliding a Graphic into place
– use of the b[.id] Component array
– how to make a function (code holder using a hotspot)
– pass a function parameter value using sys variables
– pass a function return value using a sys variable
– make use of formulas to select pictures dynamically
– retain a variables value while changing a page

Creating the HMI

1) Download the TicTacToe_resources.zip file.
2) Unpack the graphics _b.png, _o.png, _x.png, _z.png
3) Generate an 8×16 ZI Font (ASCII character set is sufficient)

Loading the Picture Resources
– load picture _b.png as Picture Resource #0
– load picture _o.png as Picture Resource #1
– load picture _x.png as Picture Resource #2
– load picture _z.png as Picture Resource #3

Loading the Font Resources
– add your generated 8×16 ZI font as font Resource #0

Set the page page0 background color
– click the attribute .bco value, type 25061 and press return
– in the Postinitialize Event, add the following

if(player.val==0)
{
  t0.txt="O's turn"
}else
{
  t0.txt="X's turn"
}

Prepping the game board

The game board playing area will be made of a 3×3 set of squares
(.id values: Top Row 1,2,3 Middle Row 4,5,6 Bottom Row 7,8,9)

Create a Picture Component p0
– double click the attribute .pic value and select picture 0
– place this component at x 148, y 10
– in the Component Touch Release Event, add the following

sys0=1
click m0,1

– where sys0 will hold the .id values of the picture components
– Copy component p0

Paste to create Components p2, p3, p4, p5, p6, p7 and p8
– change release event code sys0=2 and place p1 (248,10)
– change release event code sys0=3 and place p2 (348,10)
– change release event code sys0=4 and place p3 (148,110)
– change release event code sys0=5 and place p4 (248,110)
– change release event code sys0=6 and place p5 (348,110)
– change release event code sys0=7 and place p6 (148,210)
– change release event code sys0=8 and place p7 (248,210)
– change release event code sys0=9 and place p8 (348,210)

Adding the Variable Components

Create a numeric Variable player
– click the attribute .objname value, type player, and press return
– click the attribute .vscope value, select global, and press return
Setting the .vscope to global will retain its value on page re-entry.

Create a numeric Variable plays
– click the attribute .objname value, type plays, and press return

Adding our two main functions using a Hotspot

Create a hotspot component m0 and place at (25,25)
The first function make play will be placed
in the Touch Press Event by adding the following code

//make play
if(plays.val<10)
{
  if(b[sys0].pic==0)
  {
    if(player.val==0)
    {
      for(sys2=1;sys2<=100;sys2+=3)
      {
        sys1=100-sys2
        xpic b[sys0].x+sys1,b[sys0].y,sys2,b[sys0].h,0,0,player.val+1
        delay=8
      }
    }else
    {
      for(sys2=1;sys2<=100;sys2+=3)
      {
        sys1=100-sys2
        xpic b[sys0].x,b[sys0].y,sys2,b[sys0].h,sys1,0,player.val+1
        delay=8
      }
    }
    b[sys0].pic=player.val+1
    player.val=1-player.val
    plays.val+=1
    click m0,0
    if(sys2==5)
    {
      if(plays.val<9)
      {
        if(player.val==0)
        {
          t0.txt="O's turn"
        }else
        {
          t0.txt="X's turn"
        }
      }else
      {
        plays.val=10
        t0.txt="Tie Game"
      }
    }else
    {
      plays.val=10
      if(sys2==1)
      {
        t0.txt="O Wins"
      }
      if(sys2==2)
      {
        t0.txt="X Wins"
      }
    }
  }
}

Let’s review what is going on in this code
it’s not so complex – almost half of it is braces

line 2 – if plays is less than 10 – then game on
– this prevents moves being made after the game is over
– if game is over, then logo picture release starts new game
line 4 – only if .pic=0, this square has not been played
line 6 – if player is O’s do lines 8 to 13
– animates O piece coming in from left side
line 14 – if player is X’s do lines 16 to 21
– animates X piece coming in from right side
line 23 – finalize setting square to player’s picture resource
line 24 – toggles player (whose turn is next)
line 25 – increments the number of plays
line 26 – check if this is the game winning move
– on return from our release function check if game is won
line 27 – if sys2 equals 5, game wasn’t won
line 29 – if plays less than 9 – game is still on
line 31 – if player’s turn is O’s, line 33 sets to O’s turn
line 34 – if player’s turn is X’s, line 36 sets to X’s turn
line 38 – the game is over but wasn’t won
– line 40 – sets plays to 10 so logo tile will restart game
– line 41 – sets the status to a Tie Game
line 42 – the game has been won
– line 45 – sets the plays to 10 so logo tile will restart game
line 46 – checks if O was the winner
– line 48 – sets the status to show O’s victory
line 50 – checks if X was the winner
– line 52 – sets the status to show X’s victory

Our second function check for a win will be placed
in the Touch Release Event by adding the following code

// check for a win
sys2=5
if(b[1].pic>0)// Top row
{
  if(b[1].pic==b[2].pic)
  {
    if(b[2].pic==b[3].pic)
    {
      sys2=b[3].pic
    }
  }
}
if(b[4].pic>0)// Middle row
{
  if(b[4].pic==b[5].pic)
  {
    if(b[5].pic==b[6].pic)
    {
      sys2=b[6].pic
    }
  }
}
if(b[7].pic>0)// Bottom row
{
  if(b[7].pic==b[8].pic)
  {
    if(b[8].pic==b[9].pic)
    {
      sys2=b[9].pic
    }
  }
}
if(b[1].pic>0)// Left column
{
  if(b[1].pic==b[4].pic)
  {
    if(b[4].pic==b[7].pic)
    {
      sys2=b[7].pic
    }
  }
}
if(b[2].pic>0)// Center column
{
  if(b[2].pic==b[5].pic)
  {
    if(b[5].pic==b[8].pic)
    {
      sys2=b[8].pic
    }
  }
}
if(b[3].pic>0)// Right column
{
  if(b[3].pic==b[6].pic)
  {
    if(b[6].pic==b[9].pic)
    {
      sys2=b[9].pic
    }
  }
}
if(b[1].pic>0)// Diagonal top left to bottom right
{
  if(b[1].pic==b[5].pic)
  {
    if(b[5].pic==b[9].pic)
    {
      sys2=b[9].pic
    }
  }
}
if(b[3].pic>0)// Diagonal top right to bottom left 
{
  if(b[3].pic==b[5].pic)
  {
    if(b[5].pic==b[7].pic)
    {
      sys2=b[7].pic
    }
  }
}

The premise of checking if the game has been won is simple
– preinitialize sys2 to an unused value, in this case 5
– checking each possible line direction
– if A == B, and B == C, then set sys2 = which players piece
When this function returns
– sys2 either equals 5 (not won) or 1 (“O”) or 2 (“X”)

Each of these functions can be triggered using the click command

click m0,1// trigger Touch Press code with sys0 parameter
click m0,0// trigger Touch Release code with sys2 return value

Adding our game status Text

Create a Text Component t0 and place at (15,186)
– double click attribute .bco value, type 25601 and press return
– double click attribute .pco value, type 48107 and press return
This Text Components will hold our messages
– whose turn, or who has won the game.

Finally, adding the logo and game reset

Create the last Picture Component p9
– double click the attribute .pic value and select picture 3
– place this component at (16,16) covering our Hotspot m0
– in the Component Touch Release Event, add the following

if(plays.val>=10)
{
  page 0
}

This code
– checks if the game has ended and
– if ended, restarts the game by page re-entry
– the global player.vscope preserves whose turn to start.

Conclusion

So in this tutorial we have covered
– how to make a function (code holder) in Nextion using a hotspot
– how to trigger Press or Release code via the click command
– how to pass a function parameter value using sys variables
– how to pass a function return value using a sys variable
– how to animate a graphic sliding using the xpic command
– how to make use of formulas to select dynamic pictures
– how to make use of the b[.id] component array
– how to retain a variables value while changing a page

I hope this has been a learning experience and Enjoy

Patrick