Use GamePieceView to encapsulate displaying the current piece

This commit is contained in:
Shadowfacts 2019-10-20 23:52:15 -04:00
parent 18c2938470
commit 907d92b3b8
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
9 changed files with 93 additions and 111 deletions

View File

@ -27,7 +27,7 @@ struct ContentView: View {
.fontWeight(.bold)
if self.controller.heldTetromino != nil {
Group {
TetrominoView(tetromino: self.controller.heldTetromino!)
TetrominoView(size: 100 / 4, tetromino: self.controller.heldTetromino!)
}.frame(width: 100, height: 100, alignment: .center)
} else {
Rectangle().foregroundColor(.clear).frame(width: 100, height: 100)
@ -49,7 +49,7 @@ struct ContentView: View {
Text("Next")
.font(.title)
.fontWeight(.bold)
NextTetromioesView(tetrominoes: self.controller.nextTetrominoes)
NextTetromioesView(size: 100, tetrominoes: self.controller.nextTetrominoes)
.frame(width: 100)
}
.padding(.trailing, 8)

View File

@ -60,8 +60,9 @@
D608564723551602005BE4BC /* BoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D608564523551602005BE4BC /* BoardView.swift */; };
D608564923551BAC005BE4BC /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D608564823551BAC005BE4BC /* GridView.swift */; };
D608564A23551BAC005BE4BC /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D608564823551BAC005BE4BC /* GridView.swift */; };
D6D84DED23551E5E002968FB /* CurrentPieceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D84DEB23551E37002968FB /* CurrentPieceView.swift */; };
D6D84DEE23551E5E002968FB /* CurrentPieceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D84DEB23551E37002968FB /* CurrentPieceView.swift */; };
D6AA0AE6235D273E00D2FFE8 /* GamePieceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AA0AE4235D272200D2FFE8 /* GamePieceView.swift */; };
D6AA0AE7235D273F00D2FFE8 /* GamePieceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AA0AE4235D272200D2FFE8 /* GamePieceView.swift */; };
D6AA0AE8235D273F00D2FFE8 /* GamePieceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AA0AE4235D272200D2FFE8 /* GamePieceView.swift */; };
D6D84DF523567415002968FB /* Tetromino+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D84DF323567231002968FB /* Tetromino+Color.swift */; };
D6D84DF623567415002968FB /* Tetromino+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D84DF323567231002968FB /* Tetromino+Color.swift */; };
D6D84DF92356B903002968FB /* TetrominoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D84DF72356B8F0002968FB /* TetrominoView.swift */; };
@ -87,7 +88,6 @@
D6E5606C2357D65700BF9ACF /* TetrisUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D608560723550D3A005BE4BC /* TetrisUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
D6E5606D2357D65700BF9ACF /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D608564823551BAC005BE4BC /* GridView.swift */; };
D6E5606E2357D65700BF9ACF /* TilesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D608562F23551269005BE4BC /* TilesView.swift */; };
D6E5606F2357D65700BF9ACF /* CurrentPieceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D84DEB23551E37002968FB /* CurrentPieceView.swift */; };
D6E560702357D65700BF9ACF /* BoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D608564523551602005BE4BC /* BoardView.swift */; };
D6E560712357D65700BF9ACF /* TetrominoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D84DF72356B8F0002968FB /* TetrominoView.swift */; };
D6E560722357D65700BF9ACF /* NextTetrominoesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC0E9823579A4D008A0A98 /* NextTetrominoesView.swift */; };
@ -305,7 +305,7 @@
D608562F23551269005BE4BC /* TilesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TilesView.swift; sourceTree = "<group>"; };
D608564523551602005BE4BC /* BoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardView.swift; sourceTree = "<group>"; };
D608564823551BAC005BE4BC /* GridView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridView.swift; sourceTree = "<group>"; };
D6D84DEB23551E37002968FB /* CurrentPieceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentPieceView.swift; sourceTree = "<group>"; };
D6AA0AE4235D272200D2FFE8 /* GamePieceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamePieceView.swift; sourceTree = "<group>"; };
D6D84DF323567231002968FB /* Tetromino+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tetromino+Color.swift"; sourceTree = "<group>"; };
D6D84DF72356B8F0002968FB /* TetrominoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TetrominoView.swift; sourceTree = "<group>"; };
D6DC0E9323575D7C008A0A98 /* DPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DPadView.swift; sourceTree = "<group>"; };
@ -524,9 +524,9 @@
D6E5609F2358C68C00BF9ACF /* BundleHelper.swift */,
D608564823551BAC005BE4BC /* GridView.swift */,
D608562F23551269005BE4BC /* TilesView.swift */,
D6D84DEB23551E37002968FB /* CurrentPieceView.swift */,
D608564523551602005BE4BC /* BoardView.swift */,
D6D84DF72356B8F0002968FB /* TetrominoView.swift */,
D6AA0AE4235D272200D2FFE8 /* GamePieceView.swift */,
D6DC0E9823579A4D008A0A98 /* NextTetrominoesView.swift */,
D6D84DF323567231002968FB /* Tetromino+Color.swift */,
D6E5609B2358C50A00BF9ACF /* Media.xcassets */,
@ -1056,9 +1056,9 @@
D608564623551602005BE4BC /* BoardView.swift in Sources */,
D608564923551BAC005BE4BC /* GridView.swift in Sources */,
D6DC0E9923579A4D008A0A98 /* NextTetrominoesView.swift in Sources */,
D6D84DED23551E5E002968FB /* CurrentPieceView.swift in Sources */,
D60856332355128F005BE4BC /* TilesView.swift in Sources */,
D6D84DF523567415002968FB /* Tetromino+Color.swift in Sources */,
D6AA0AE8235D273F00D2FFE8 /* GamePieceView.swift in Sources */,
D6E560A02358C68C00BF9ACF /* BundleHelper.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1071,9 +1071,9 @@
D608564723551602005BE4BC /* BoardView.swift in Sources */,
D608564A23551BAC005BE4BC /* GridView.swift in Sources */,
D6DC0E9A23579A4D008A0A98 /* NextTetrominoesView.swift in Sources */,
D6D84DEE23551E5E002968FB /* CurrentPieceView.swift in Sources */,
D60856342355128F005BE4BC /* TilesView.swift in Sources */,
D6D84DF623567415002968FB /* Tetromino+Color.swift in Sources */,
D6AA0AE7235D273F00D2FFE8 /* GamePieceView.swift in Sources */,
D6E560A12358C68C00BF9ACF /* BundleHelper.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1110,8 +1110,8 @@
D6E560732357D65700BF9ACF /* Tetromino+Color.swift in Sources */,
D6E560702357D65700BF9ACF /* BoardView.swift in Sources */,
D6E560712357D65700BF9ACF /* TetrominoView.swift in Sources */,
D6E5606F2357D65700BF9ACF /* CurrentPieceView.swift in Sources */,
D6E5606D2357D65700BF9ACF /* GridView.swift in Sources */,
D6AA0AE6235D273E00D2FFE8 /* GamePieceView.swift in Sources */,
D6E560A22358C68C00BF9ACF /* BundleHelper.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -29,7 +29,7 @@ struct ContentView: View {
Text("Held")
if self.controller.heldTetromino != nil {
Group {
TetrominoView(tetromino: self.controller.heldTetromino!)
TetrominoView(size: 50 / 4, tetromino: self.controller.heldTetromino!)
}.frame(width: 50, height: 50, alignment: .center)
} else {
Rectangle().foregroundColor(.clear).frame(width: 50, height: 50)
@ -48,7 +48,7 @@ struct ContentView: View {
VStack {
Text("Next")
NextTetromioesView(tetrominoes: self.controller.nextTetrominoes)
NextTetromioesView(size: 50, tetrominoes: self.controller.nextTetrominoes)
.frame(width: 50)
}
.padding(.trailing, 8)

View File

@ -11,8 +11,9 @@ import Foundation
public struct GamePiece {
public let tetromino: Tetromino
public var topLeft: (Int, Int)
public internal(set) var rotation: Double = 0
public internal(set) var tiles: [[Bool]]
public init(tetromino: Tetromino, topLeft: (Int, Int) = (0, 0)) {
self.tetromino = tetromino
self.tiles = tetromino.shape
@ -23,8 +24,10 @@ public struct GamePiece {
switch direction {
case .clockwise:
self.tiles.rotateClockwise()
rotation += 90
case .counterClockwise:
self.tiles.rotateCounterclockwise()
rotation -= 90
}
}
}

View File

@ -21,10 +21,17 @@ public struct BoardView: View {
}
public var body: some View {
ZStack {
TilesView(board: $board)
CurrentPieceView(boardWidth: board.width, boardHeight: board.height, currentPiece: $currentPiece, droppedPiece: $droppedPiece)
GeometryReader { (geometry) in
ZStack(alignment: .topLeading) {
TilesView(board: self.$board)
if self.currentPiece != nil {
GamePieceView(size: min(geometry.size.width / CGFloat(self.board.width), geometry.size.height / CGFloat(self.board.height)), piece: self.currentPiece!)
}
if self.droppedPiece != nil {
GamePieceView(size: min(geometry.size.width / CGFloat(self.board.width), geometry.size.height / CGFloat(self.board.height)), piece: self.droppedPiece!, color: .gray, border: false)
}
}
}
}
}

View File

@ -1,64 +0,0 @@
//
// CurrentPieceView.swift
// Tetris
//
// Created by Shadowfacts on 10/14/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import SwiftUI
import TetrisKit
struct CurrentPieceView: View {
let boardWidth: Int
let boardHeight: Int
@Binding var currentPiece: GamePiece?
@Binding var droppedPiece: GamePiece?
var body: some View {
GridView(rows: self.boardHeight, columns: self.boardWidth) { (col, row, size) in
if self.currentPieceAt(col, row) {
Rectangle()
.foregroundColor(self.currentPiece!.tetromino.color)
.frame(width: size, height: size)
.border(self.currentPiece!.tetromino.borderColor, width: 6)
} else if self.droppedPieceAt(col, row) {
Rectangle()
.foregroundColor(.gray)
.frame(width: size, height: size)
} else {
Rectangle()
.foregroundColor(.clear)
.frame(width: size, height: size)
}
}
}
func currentPieceAt(_ col: Int, _ row: Int) -> Bool {
guard let currentPiece = self.currentPiece else { return false }
let (left, top) = currentPiece.topLeft
let pieceHeight = currentPiece.tiles.count
let pieceWidth = currentPiece.tiles.first!.count
return col - left >= 0 && col - left < pieceWidth && row - top >= 0 && row - top < pieceHeight && currentPiece.tiles[row - top][col - left]
}
func droppedPieceAt(_ col: Int, _ row: Int) -> Bool {
guard let droppedPiece = self.droppedPiece else { return false }
let (left, top) = droppedPiece.topLeft
let pieceHeight = droppedPiece.tiles.count
let pieceWidth = droppedPiece.tiles.first!.count
return col - left >= 0 && col - left < pieceWidth && row - top >= 0 && row - top < pieceHeight && droppedPiece.tiles[row - top][col - left]
}
}
struct CurrentPieceView_Previews: PreviewProvider {
@State static var currentPiece: GamePiece? = GamePiece(tetromino: .t)
@State static var droppedPiece: GamePiece? = {
var piece = GamePiece(tetromino: .t)
return piece.moved(by: (0, 16 - piece.tiles.count))
}()
static var previews: some View {
CurrentPieceView(boardWidth: 10, boardHeight: 16, currentPiece: $currentPiece, droppedPiece: $droppedPiece)
}
}

View File

@ -0,0 +1,38 @@
//
// GamePieceView.swift
// Tetris
//
// Created by Shadowfacts on 10/20/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import SwiftUI
import TetrisKit
struct GamePieceView: View {
let size: CGFloat
let piece: GamePiece
let color: Color
let border: Bool
init(size: CGFloat, piece: GamePiece, color: Color? = nil, border: Bool = true) {
self.size = size
self.piece = piece
self.color = color ?? piece.tetromino.color
self.border = border
}
var body: some View {
return TetrominoView(size: size, tetromino: piece.tetromino, color: color, border: border)
.rotationEffect(.degrees(piece.rotation))
.offset(x: CGFloat(piece.topLeft.0) * size, y: CGFloat(piece.topLeft.1) * size)
.frame(width: size * CGFloat(piece.tiles.count), height: size * CGFloat(piece.tiles.count))
}
}
struct GamePieceView_Previews: PreviewProvider {
static var previews: some View {
GamePieceView(size: 100, piece: GamePiece(tetromino: .t))
}
}

View File

@ -10,16 +10,18 @@ import SwiftUI
import TetrisKit
public struct NextTetromioesView: View {
let size: CGFloat
let tetrominoes: [Tetromino]
public init(tetrominoes: [Tetromino]) {
public init(size: CGFloat, tetrominoes: [Tetromino]) {
self.size = size
self.tetrominoes = tetrominoes
}
public var body: some View {
VStack {
ForEach(0..<self.tetrominoes.count, id: \.self) { (index) in
TetrominoView(tetromino: self.tetrominoes[index]).aspectRatio(1, contentMode: .fit)
TetrominoView(size: self.size / 4, tetromino: self.tetrominoes[index]).aspectRatio(1, contentMode: .fit)
}
}
}
@ -27,6 +29,6 @@ public struct NextTetromioesView: View {
struct NextTetrominoesView_Previews: PreviewProvider {
static var previews: some View {
NextTetromioesView(tetrominoes: [.t, .l, .z])
NextTetromioesView(size: 50, tetrominoes: [.t, .l, .z])
}
}

View File

@ -10,51 +10,47 @@ import SwiftUI
import TetrisKit
public struct TetrominoView: View {
let size: CGFloat
let tetromino: Tetromino
let color: Color
let border: Bool
public init(tetromino: Tetromino) {
public init(size: CGFloat, tetromino: Tetromino, color: Color? = nil, border: Bool = true) {
self.size = size
self.tetromino = tetromino
self.color = color ?? tetromino.color
self.border = border
}
public var body: some View {
GridView(rows: self.rows, columns: self.columns) { (col, row, size) in
GridView(rows: tetromino.shape.count, columns: tetromino.shape.first!.count) { (col, row, _) in
if row < self.tetromino.shape.count && col < self.tetromino.shape[row].count && self.tetromino.shape[row][col] {
Rectangle()
.foregroundColor(self.tetromino.color)
.frame(width: size, height: size)
.border(self.tetromino.borderColor, width: 6)
if self.border {
Rectangle()
.foregroundColor(self.color)
.frame(width: self.size, height: self.size)
.border(self.tetromino.borderColor, width: 6)
} else {
Rectangle()
.foregroundColor(self.color)
.frame(width: self.size, height: self.size)
}
} else {
Rectangle()
.foregroundColor(.clear)
.frame(width: size, height: size)
.frame(width: self.size, height: self.size)
}
}
}
var rows: Int {
self.tetromino.shape.firstIndex(where: { row in
row.allSatisfy({ el in
!el
})
}) ?? self.tetromino.shape.count
}
var columns: Int {
self.tetromino.shape.map { row in
(row.firstIndex(where: { el in
!el
}) ?? row.count - 1) + 1
}.max() ?? self.tetromino.shape.first!.count
}
}
struct TetrominoView_Previews: PreviewProvider {
static var previews: some View {
Group {
TetrominoView(tetromino: .t)
TetrominoView(tetromino: .i)
TetrominoView(tetromino: .z)
TetrominoView(tetromino: .j)
TetrominoView(size: 50, tetromino: .t)
TetrominoView(size: 50, tetromino: .i)
TetrominoView(size: 50, tetromino: .z)
TetrominoView(size: 50, tetromino: .j)
}
}
}