// // ContentView.swift // Tetris // // Created by Shadowfacts on 10/14/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import SwiftUI import TetrisKit import TetrisUI struct ContentView: View { @ObservedObject var controller: GameController = { let c = GameController() c.start() return c }() @State var timer: Timer? @State var gestureState: GestureState = .none @State var initialXPosition: Int? var body: some View { GeometryReader { (geometry) in VStack { Spacer() HStack(alignment: .top, spacing: 8) { VStack { Text("Held") if self.controller.heldTetromino != nil { Group { TetrominoView(size: 50 / 4, tetromino: self.controller.heldTetromino!) }.frame(width: 50, height: 50, alignment: .center) } else { Rectangle().foregroundColor(.clear).frame(width: 50, height: 50) } Text("Score") Text(verbatim: self.controller.score.description) } .padding(.leading, 8) BoardView(board: self.$controller.board, currentPiece: self.$controller.currentPiece, droppedPiece: self.$controller.currentPieceAtDropPoint) .aspectRatio(CGSize(width: self.controller.width, height: self.controller.height), contentMode: .fit) .onAppear(perform: self.startTimer) .onDisappear(perform: self.stopTimer) .onTapGesture(perform: self.onTap) // .gesture(ExclusiveGesture(horizDragGesture, verticalDragGesture)) // .gesture(horizDragGesture.simultaneously(with: verticalDragGesture)) VStack { Text("Next") NextTetromioesView(size: 50, tetrominoes: self.controller.nextTetrominoes) .frame(width: 50) } .padding(.trailing, 8) } .background(Color("TempBackground")) // visually does nothing but lets the gesture work outside of the board, see FB7385742 .gesture(self.horizDragGesture(geometry: geometry).simultaneously(with: self.verticalDragGesture)) Spacer() HStack { Button(action: { self.controller.rotate(direction: .counterClockwise) }) { Image(systemName: "gobackward").resizable().frame(width: 50, height: 50) } .padding(.leading, 25) Button(action: self.onTap) { Image(systemName: "goforward").resizable().frame(width: 50, height: 50) } .padding(.leading, 25) Spacer() DPadView(up: self.controller.hold, down: self.controller.drop, left: self.controller.left, right: self.controller.right) .frame(width: 150, height: 150) .padding(.trailing, 25) } Spacer() } } } func horizDragGesture(geometry: GeometryProxy) -> some Gesture { DragGesture(coordinateSpace: .global) .onChanged { (state) in guard self.gestureState != .vertical, case .playing(.normal) = self.controller.state, let currentPiece = self.controller.currentPiece else { return } if self.initialXPosition == nil { self.initialXPosition = currentPiece.topLeft.0 } var moved = currentPiece let xPosition = self.initialXPosition! + Int((state.translation.width / (geometry.size.width / 10)).rounded()) if xPosition != self.initialXPosition { self.gestureState = .horizontal } moved.topLeft = (xPosition, currentPiece.topLeft.1) if !self.controller.overlapsAny(moved) { self.controller.currentPiece = moved } }.onEnded { (state) in self.initialXPosition = nil if self.gestureState == .horizontal { self.gestureState = .none } } } var verticalDragGesture: some Gesture { DragGesture() .onChanged({ (state) in guard self.gestureState != .horizontal else { return } }) .onEnded { (state) in guard self.gestureState != .horizontal else { return } if abs(state.translation.height) > 80 { self.gestureState = .none if state.translation.height > 0 { self.onSwipeDown() } else { self.onSwipeUp() } } } } func startTimer() { self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { (_) in guard case .playing(_) = self.controller.state else { self.stopTimer() return } self.controller.step() } } func stopTimer() { self.timer?.invalidate() } func onTap() { self.controller.rotate(direction: .clockwise) } func onSwipeLeft() { self.controller.left() } func onSwipeRight() { self.controller.right() } func onSwipeUp() { self.controller.hold() } func onSwipeDown() { self.controller.drop() } } enum GestureState: Equatable { case none, horizontal, vertical } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }