Tetris/Tetris/ContentView.swift

168 lines
6.1 KiB
Swift
Raw Permalink Normal View History

2019-10-15 16:24:58 +00:00
//
// 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
2019-10-15 21:26:06 +00:00
@State var initialXPosition: Int?
2019-10-15 16:24:58 +00:00
var body: some View {
GeometryReader { (geometry) in
VStack {
2019-10-16 18:39:18 +00:00
Spacer()
HStack(alignment: .top, spacing: 8) {
VStack {
Text("Held")
if self.controller.heldTetromino != nil {
Group {
TetrominoView(size: 50 / 4, tetromino: self.controller.heldTetromino!)
2019-10-16 18:39:18 +00:00
}.frame(width: 50, height: 50, alignment: .center)
} else {
Rectangle().foregroundColor(.clear).frame(width: 50, height: 50)
}
2019-10-16 21:49:16 +00:00
Text("Score")
Text(verbatim: self.controller.score.description)
2019-10-16 18:39:18 +00:00
}
.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)
2019-10-16 23:57:56 +00:00
.frame(width: 50)
2019-10-16 03:17:47 +00:00
}
2019-10-16 18:39:18 +00:00
.padding(.trailing, 8)
2019-10-16 03:17:47 +00:00
}
.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))
2019-10-16 18:39:18 +00:00
Spacer()
2019-10-15 16:24:58 +00:00
HStack {
2019-10-16 18:23:12 +00:00
Button(action: { self.controller.rotate(direction: .counterClockwise) }) {
Image(systemName: "gobackward").resizable().frame(width: 50, height: 50)
}
.padding(.leading, 25)
2019-10-15 16:24:58 +00:00
Button(action: self.onTap) {
Image(systemName: "goforward").resizable().frame(width: 50, height: 50)
}
2019-10-16 18:23:12 +00:00
.padding(.leading, 25)
2019-10-16 03:17:47 +00:00
Spacer()
DPadView(up: self.controller.hold, down: self.controller.drop, left: self.controller.left, right: self.controller.right)
.frame(width: 150, height: 150)
2019-10-16 18:23:12 +00:00
.padding(.trailing, 25)
2019-10-15 16:24:58 +00:00
}
2019-10-16 18:39:18 +00:00
Spacer()
2019-10-15 16:24:58 +00:00
}
}
}
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 }
2019-10-15 21:26:06 +00:00
if self.initialXPosition == nil {
self.initialXPosition = currentPiece.topLeft.0
2019-10-15 16:24:58 +00:00
}
2019-10-15 21:26:06 +00:00
var moved = currentPiece
let xPosition = self.initialXPosition! + Int((state.translation.width / (geometry.size.width / 10)).rounded())
if xPosition != self.initialXPosition {
self.gestureState = .horizontal
}
2019-10-15 21:26:06 +00:00
moved.topLeft = (xPosition, currentPiece.topLeft.1)
2019-10-15 16:24:58 +00:00
if !self.controller.overlapsAny(moved) {
self.controller.currentPiece = moved
}
}.onEnded { (state) in
2019-10-15 21:26:06 +00:00
self.initialXPosition = nil
if self.gestureState == .horizontal {
self.gestureState = .none
}
2019-10-15 16:24:58 +00:00
}
}
var verticalDragGesture: some Gesture {
DragGesture()
.onChanged({ (state) in
guard self.gestureState != .horizontal else { return }
})
2019-10-15 16:24:58 +00:00
.onEnded { (state) in
guard self.gestureState != .horizontal else { return }
2019-10-15 21:26:06 +00:00
if abs(state.translation.height) > 80 {
self.gestureState = .none
2019-10-15 21:26:06 +00:00
if state.translation.height > 0 {
2019-10-15 16:24:58 +00:00
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
}
2019-10-15 16:24:58 +00:00
self.controller.step()
}
}
func stopTimer() {
2019-10-16 23:57:56 +00:00
self.timer?.invalidate()
2019-10-15 16:24:58 +00:00
}
func onTap() {
self.controller.rotate(direction: .clockwise)
}
func onSwipeLeft() {
self.controller.left()
}
func onSwipeRight() {
self.controller.right()
}
func onSwipeUp() {
2019-10-16 03:17:47 +00:00
self.controller.hold()
2019-10-15 16:24:58 +00:00
}
func onSwipeDown() {
self.controller.drop()
}
}
enum GestureState: Equatable {
case none, horizontal, vertical
}
2019-10-15 16:24:58 +00:00
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}