Five Steps to Create a Chess App 4/5
Step 4: drawing pieces on board
Summary of previous 3 steps:
Step 1: created chess app from scratch
Step 2: added a view for our chess board
Step 3: drew 8x8 chess board
We need 12 piece images, 6 white and 6 black, to represent 32 pieces on board. Try to search whatever images you like and download them into, e.g. “Downloads” folder. The image files used in this tutorial can be found here.
Go back to Xcode and click “Assets.xcassets” on the left navigation panel. We’ll drag and drop all 12 piece images onto the big white area under “AppIcon”.
You can drag pieces one by one, or select all and drag them all together.
When finished the pieces look like the following in Xcode. We’ll use the name of each image to create UIImage object shortly. E.g, the name of black bishop image is “Bishop-black”, case sensitive.
Time for writing some code. We’ll create a struct representing a piece. Right click the yellow “Chess” folder on the left navigation panel and select “New File…”. Select “Swift File” under “iOS”. Then click “Next” button to continue.
Input the very creative name “ChessPiece” and click “Create” button.
Here is what it looks like when created.
We need two integers “col” and “row” for the location of a piece on logical chess board. Each piece also keep a field “imageName” for its own image name, e.g. “Bishop-black”. “Hashable” is needed when we use “Set” to contain objects of type ChessPiece.
import Foundationstruct ChessPiece: Hashable {
let col: Int
let row: Int
let imageName: String
}
Move on to create another struct for our game engine, which will hold all the pieces on board. If you ever heard of MVC design pattern, this is the M, meaning Model.
Key in “ChessEngine” and click “Create” button to continue.
ChessEngine.swift is created but still empty.
ChessEngine keeps a set of pieces. We create a convenient function to insert all 32 initial pieces into property “pieces”.
Here is the complete code of ChessEngine.swift:
import Foundationstruct ChessEngine {
var pieces = Set<ChessPiece>()
mutating func initializeGame() {
pieces.removeAll()
// ...
}
}
Put the following code in the place of “// …” above.
pieces.insert(ChessPiece(col: 0, row: 0, imageName: "Rook-black"))
pieces.insert(ChessPiece(col: 7, row: 0, imageName: "Rook-black"))
pieces.insert(ChessPiece(col: 0, row: 7, imageName: "Rook-white"))
pieces.insert(ChessPiece(col: 7, row: 7, imageName: "Rook-white"))pieces.insert(ChessPiece(col: 1, row: 0, imageName: "Knight-black"))
pieces.insert(ChessPiece(col: 6, row: 0, imageName: "Knight-black"))
pieces.insert(ChessPiece(col: 1, row: 7, imageName: "Knight-white"))
pieces.insert(ChessPiece(col: 6, row: 7, imageName: "Knight-white"))pieces.insert(ChessPiece(col: 2, row: 0, imageName: "Bishop-black"))
pieces.insert(ChessPiece(col: 5, row: 0, imageName: "Bishop-black"))
pieces.insert(ChessPiece(col: 2, row: 7, imageName: "Bishop-white"))
pieces.insert(ChessPiece(col: 5, row: 7, imageName: "Bishop-white"))pieces.insert(ChessPiece(col: 3, row: 0, imageName: "Queen-black"))
pieces.insert(ChessPiece(col: 3, row: 7, imageName: "Queen-white"))pieces.insert(ChessPiece(col: 4, row: 0, imageName: "King-black"))
pieces.insert(ChessPiece(col: 4, row: 7, imageName: "King-white"))pieces.insert(ChessPiece(col: 0, row: 1, imageName: "Pawn-black"))
pieces.insert(ChessPiece(col: 1, row: 1, imageName: "Pawn-black"))
pieces.insert(ChessPiece(col: 2, row: 1, imageName: "Pawn-black"))
pieces.insert(ChessPiece(col: 3, row: 1, imageName: "Pawn-black"))
pieces.insert(ChessPiece(col: 4, row: 1, imageName: "Pawn-black"))
pieces.insert(ChessPiece(col: 5, row: 1, imageName: "Pawn-black"))
pieces.insert(ChessPiece(col: 6, row: 1, imageName: "Pawn-black"))
pieces.insert(ChessPiece(col: 7, row: 1, imageName: "Pawn-black"))pieces.insert(ChessPiece(col: 0, row: 6, imageName: "Pawn-white"))
pieces.insert(ChessPiece(col: 1, row: 6, imageName: "Pawn-white"))
pieces.insert(ChessPiece(col: 2, row: 6, imageName: "Pawn-white"))
pieces.insert(ChessPiece(col: 3, row: 6, imageName: "Pawn-white"))
pieces.insert(ChessPiece(col: 4, row: 6, imageName: "Pawn-white"))
pieces.insert(ChessPiece(col: 5, row: 6, imageName: "Pawn-white"))
pieces.insert(ChessPiece(col: 6, row: 6, imageName: "Pawn-white"))
pieces.insert(ChessPiece(col: 7, row: 6, imageName: "Pawn-white"))
Let’s move on to add 2 lines of code in ViewController.swift. To create an instance of ChessEngine, add the following code above “override func viewDidLoad() {”.
var chessEngine = ChessEngine()
And add the following code under “super.viewDidLoad()” to initalize our chess game, i.e. deploying all 32 pieces in place.
chessEngine.initializeGame()
Now we arrive at the trickiest and most interesting part of this assignment. We need to build the connection between the screen element and the code. In our case it means ctrl dragging from the white board view to somewhere inside file ViewController.swift. To make that happen, both files have to show up side by side. On left navigation panel, click ViewController.swift first, then hold on “option” key and click Main.storyboard.
Click the white board view on the right to select it. With control key being held, start dragging from board view to the empty area above “override func viewDidLoad() {”. When you release mouse, A dialog box pops up.
Give the connection its name “boardView”. Then click “Connect” button to hide the dialog.
Xcode inserts the following line of code. This variable “boardView” represents the white view on screen.
@IBOutlet weak var boardView: BoardView!
Now let’s go back to file BoardView.swift to add code for pieces drawing. We create an instance variable pieces of type Set<ChessPiece>. Think it as the shadow pieces box of the one in ChessEngine, which is the variable pieces defined in ChessEngine. We also create a function to draw all pieces.
The complete code in BoardView.swift follows.
import UIKitclass BoardView: UIView {
var pieces = Set<ChessPiece>() override func draw(_ rect: CGRect) {
drawBoard()
drawPieces()
}
func drawPieces() {
for piece in pieces {
let pieceImage = UIImage(named: piece.imageName)
pieceImage?.draw(in: CGRect(x: piece.col * 80, y: piece.row * 80, width: 80, height: 80))
}
} func drawBoard() {
drawTwoRowsAt(y: 0 * 80)
drawTwoRowsAt(y: 2 * 80)
drawTwoRowsAt(y: 4 * 80)
drawTwoRowsAt(y: 6 * 80)
}
func drawTwoRowsAt(y: CGFloat) {
drawSquareAt(x: 1 * 80, y: y)
drawSquareAt(x: 3 * 80, y: y)
drawSquareAt(x: 5 * 80, y: y)
drawSquareAt(x: 7 * 80, y: y)
drawSquareAt(x: 0 * 80, y: y + 80)
drawSquareAt(x: 2 * 80, y: y + 80)
drawSquareAt(x: 4 * 80, y: y + 80)
drawSquareAt(x: 6 * 80, y: y + 80)
}
func drawSquareAt(x: CGFloat, y: CGFloat) {
let path = UIBezierPath(rect: CGRect(x: x, y: y, width: 80, height: 80))
UIColor.lightGray.setFill()
path.fill()
}
}
Go back to file ViewController.swift to add 2 lines of code. Line 21 updates the shadow pieces (box) in boardView from the main pieces (box) in chessEngine. Line 22 tells the system to redraw everything on screen.
Here is the complete code of ViewController.swift.
import UIKitclass ViewController: UIViewController {
var chessEngine = ChessEngine() @IBOutlet weak var boardView: BoardView!
override func viewDidLoad() {
super.viewDidLoad()
chessEngine.initializeGame()
boardView.pieces = chessEngine.pieces
boardView.setNeedsDisplay()
}
}
Run the app to enjoy these lovely pieces.