diff --git a/Tetris Mac/ContentView.swift b/Tetris Mac/ContentView.swift index b151a17..d1fce50 100644 --- a/Tetris Mac/ContentView.swift +++ b/Tetris Mac/ContentView.swift @@ -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) diff --git a/Tetris.xcodeproj/project.pbxproj b/Tetris.xcodeproj/project.pbxproj index 572f683..8d61bda 100644 --- a/Tetris.xcodeproj/project.pbxproj +++ b/Tetris.xcodeproj/project.pbxproj @@ -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 = ""; }; D608564523551602005BE4BC /* BoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardView.swift; sourceTree = ""; }; D608564823551BAC005BE4BC /* GridView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridView.swift; sourceTree = ""; }; - D6D84DEB23551E37002968FB /* CurrentPieceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentPieceView.swift; sourceTree = ""; }; + D6AA0AE4235D272200D2FFE8 /* GamePieceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamePieceView.swift; sourceTree = ""; }; D6D84DF323567231002968FB /* Tetromino+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tetromino+Color.swift"; sourceTree = ""; }; D6D84DF72356B8F0002968FB /* TetrominoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TetrominoView.swift; sourceTree = ""; }; D6DC0E9323575D7C008A0A98 /* DPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DPadView.swift; sourceTree = ""; }; @@ -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; diff --git a/Tetris/ContentView.swift b/Tetris/ContentView.swift index 496fe1d..1151bea 100644 --- a/Tetris/ContentView.swift +++ b/Tetris/ContentView.swift @@ -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) diff --git a/TetrisKit/GamePiece.swift b/TetrisKit/GamePiece.swift index 05c4833..28cd286 100644 --- a/TetrisKit/GamePiece.swift +++ b/TetrisKit/GamePiece.swift @@ -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 } } } diff --git a/TetrisUI/BoardView.swift b/TetrisUI/BoardView.swift index 0c72c6b..4553948 100644 --- a/TetrisUI/BoardView.swift +++ b/TetrisUI/BoardView.swift @@ -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) + } + } } } } diff --git a/TetrisUI/CurrentPieceView.swift b/TetrisUI/CurrentPieceView.swift deleted file mode 100644 index 404b63c..0000000 --- a/TetrisUI/CurrentPieceView.swift +++ /dev/null @@ -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) - } -} diff --git a/TetrisUI/GamePieceView.swift b/TetrisUI/GamePieceView.swift new file mode 100644 index 0000000..14ee2ef --- /dev/null +++ b/TetrisUI/GamePieceView.swift @@ -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)) + } +} diff --git a/TetrisUI/NextTetrominoesView.swift b/TetrisUI/NextTetrominoesView.swift index 8c4cbc4..10fbdec 100644 --- a/TetrisUI/NextTetrominoesView.swift +++ b/TetrisUI/NextTetrominoesView.swift @@ -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..