Project Description
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