Draggable piece

main
Mathieu GROUSSEAU 2 weeks ago
parent 060517b736
commit 53f9506de8

@ -19,6 +19,7 @@
F0143C2F2DF96B690086CAAA /* InGameVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C2E2DF96B690086CAAA /* InGameVM.swift */; }; F0143C2F2DF96B690086CAAA /* InGameVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C2E2DF96B690086CAAA /* InGameVM.swift */; };
F0143C312DF9813C0086CAAA /* PieceNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C302DF9813C0086CAAA /* PieceNode.swift */; }; F0143C312DF9813C0086CAAA /* PieceNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C302DF9813C0086CAAA /* PieceNode.swift */; };
F0143C342DF987490086CAAA /* PlayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C332DF987490086CAAA /* PlayerType.swift */; }; F0143C342DF987490086CAAA /* PlayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C332DF987490086CAAA /* PlayerType.swift */; };
F0143C362DFA9A000086CAAA /* RulesType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C352DFA9A000086CAAA /* RulesType.swift */; };
F0F59E492DD4958800BE32D6 /* C4.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E432DD492B400BE32D6 /* C4.xcframework */; }; F0F59E492DD4958800BE32D6 /* C4.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E432DD492B400BE32D6 /* C4.xcframework */; };
F0F59E4A2DD4958800BE32D6 /* C4Persistance.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E412DD492B400BE32D6 /* C4Persistance.xcframework */; }; F0F59E4A2DD4958800BE32D6 /* C4Persistance.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E412DD492B400BE32D6 /* C4Persistance.xcframework */; };
F0F59E4B2DD4958800BE32D6 /* C4Players.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E422DD492B400BE32D6 /* C4Players.xcframework */; }; F0F59E4B2DD4958800BE32D6 /* C4Players.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E422DD492B400BE32D6 /* C4Players.xcframework */; };
@ -66,6 +67,7 @@
F0143C2E2DF96B690086CAAA /* InGameVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InGameVM.swift; sourceTree = "<group>"; }; F0143C2E2DF96B690086CAAA /* InGameVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InGameVM.swift; sourceTree = "<group>"; };
F0143C302DF9813C0086CAAA /* PieceNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PieceNode.swift; sourceTree = "<group>"; }; F0143C302DF9813C0086CAAA /* PieceNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PieceNode.swift; sourceTree = "<group>"; };
F0143C332DF987490086CAAA /* PlayerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerType.swift; sourceTree = "<group>"; }; F0143C332DF987490086CAAA /* PlayerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerType.swift; sourceTree = "<group>"; };
F0143C352DFA9A000086CAAA /* RulesType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RulesType.swift; sourceTree = "<group>"; };
F0F59E412DD492B400BE32D6 /* C4Persistance.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4Persistance.xcframework; path = ../precompiled/xcframeworks/C4Persistance.xcframework; sourceTree = "<group>"; }; F0F59E412DD492B400BE32D6 /* C4Persistance.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4Persistance.xcframework; path = ../precompiled/xcframeworks/C4Persistance.xcframework; sourceTree = "<group>"; };
F0F59E422DD492B400BE32D6 /* C4Players.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4Players.xcframework; path = ../precompiled/xcframeworks/C4Players.xcframework; sourceTree = "<group>"; }; F0F59E422DD492B400BE32D6 /* C4Players.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4Players.xcframework; path = ../precompiled/xcframeworks/C4Players.xcframework; sourceTree = "<group>"; };
F0F59E432DD492B400BE32D6 /* C4.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4.xcframework; path = ../precompiled/xcframeworks/C4.xcframework; sourceTree = "<group>"; }; F0F59E432DD492B400BE32D6 /* C4.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = C4.xcframework; path = ../precompiled/xcframeworks/C4.xcframework; sourceTree = "<group>"; };
@ -204,6 +206,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F0143C332DF987490086CAAA /* PlayerType.swift */, F0143C332DF987490086CAAA /* PlayerType.swift */,
F0143C352DFA9A000086CAAA /* RulesType.swift */,
); );
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -359,6 +362,7 @@
F001A04E2DD48FAB00809561 /* MainMenuView.swift in Sources */, F001A04E2DD48FAB00809561 /* MainMenuView.swift in Sources */,
F001A04C2DD48FAB00809561 /* AppApp.swift in Sources */, F001A04C2DD48FAB00809561 /* AppApp.swift in Sources */,
F0143C2D2DF01E120086CAAA /* NewGameVM.swift in Sources */, F0143C2D2DF01E120086CAAA /* NewGameVM.swift in Sources */,
F0143C362DFA9A000086CAAA /* RulesType.swift in Sources */,
F0F59E532DDDC35100BE32D6 /* NewGameView.swift in Sources */, F0F59E532DDDC35100BE32D6 /* NewGameView.swift in Sources */,
F0143C2F2DF96B690086CAAA /* InGameVM.swift in Sources */, F0143C2F2DF96B690086CAAA /* InGameVM.swift in Sources */,
F0F59E5B2DE6F68800BE32D6 /* ScoreboardView.swift in Sources */, F0F59E5B2DE6F68800BE32D6 /* ScoreboardView.swift in Sources */,
@ -527,6 +531,7 @@
DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\"";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.board-games";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
@ -563,6 +568,7 @@
DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\"";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.board-games";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;

@ -1,21 +1,29 @@
import SpriteKit import SpriteKit
public class GameScene: SKScene { public class GameScene: SKScene {
public static let boardTopMargin: CGFloat = PieceNode.pieceSize / 2
private var boardNode: SKShapeNode private var boardNode: SKShapeNode
private var cells: [PieceNode] private var cells: [PieceNode]
private var draggablePiece: PieceNode
public init(s: (w: Int, h: Int)) { public init(s: (w: Int, h: Int)) {
let boardHeight = CGFloat(s.h) * PieceNode.pieceSize let boardHeight = CGFloat(s.h) * PieceNode.pieceSize
let gWidth = CGFloat(s.w) * PieceNode.pieceSize let gWidth = CGFloat(s.w) * PieceNode.pieceSize
let gHeight = boardHeight + PieceNode.pieceSize let gHeight = boardHeight + Self.boardTopMargin + PieceNode.pieceSize
boardNode = SKShapeNode(rect: CGRect(x: 0, y: 0, width: gWidth, height: boardHeight)) boardNode = SKShapeNode(rect: CGRect(x: 0, y: 0, width: gWidth, height: boardHeight))
boardNode.fillColor = .blue boardNode.fillColor = .blue
boardNode.strokeColor = .black boardNode.strokeColor = .black
draggablePiece = PieceNode()
draggablePiece.piece = .init(withOwner: .noOne)
draggablePiece.draggable = true
draggablePiece.position = CGPoint(x: 0, y: boardHeight + Self.boardTopMargin + PieceNode.pieceSize / 2)
cells = (0..<(s.w * s.h)).map({ index in cells = (0..<(s.w * s.h)).map({ index in
let piece = PieceNode() let piece = PieceNode()
piece.intPosition = (x: index % s.w, y: index / s.w) piece.boardPosition = (x: index % s.w, y: index / s.w)
piece.piece = switch(index % 3) { piece.piece = switch(index % 3) {
case 1: .init(withOwner: .player1) case 1: .init(withOwner: .player1)
case 2: .init(withOwner: .player2) case 2: .init(withOwner: .player2)
@ -27,11 +35,15 @@ public class GameScene: SKScene {
super.init(size: CGSize(width: gWidth, height: gHeight)) super.init(size: CGSize(width: gWidth, height: gHeight))
// anchorPoint = CGPoint(x: 0.5, y: 0.5) // anchorPoint = CGPoint(x: 0.5, y: 0.5)
backgroundColor = .clear
addChild(boardNode) addChild(boardNode)
addChild(draggablePiece)
for cell in cells { for cell in cells {
addChild(cell) addChild(cell)
} }
draggablePiece.resetDragPosition()
} }
public required init?(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {

@ -7,6 +7,11 @@ class PieceNode : SKNode {
private var circle: SKShapeNode private var circle: SKShapeNode
override var isUserInteractionEnabled: Bool {
get { true }
set { /* fatalError("Unsupported operation: set") */ }
}
var piece: Piece = .init(withOwner: .noOne) { var piece: Piece = .init(withOwner: .noOne) {
didSet { didSet {
switch (piece.owner) { switch (piece.owner) {
@ -23,14 +28,23 @@ class PieceNode : SKNode {
} }
} }
} }
var intPosition: (x: Int, y: Int) = (0, 0) { var boardPosition: (x: Int, y: Int)? = nil {
didSet { didSet {
if let pos = boardPosition {
position = CGPoint( position = CGPoint(
x: CGFloat(intPosition.x) * Self.pieceSize + Self.pieceSize / 2, x: CGFloat(pos.x) * Self.pieceSize + Self.pieceSize / 2,
y: CGFloat(intPosition.y) * Self.pieceSize + Self.pieceSize / 2 y: CGFloat(pos.y) * Self.pieceSize + Self.pieceSize / 2
) )
zPosition = 10
} else {
zPosition = 100
} }
} }
}
var draggable: Bool = false
private var gameScene: GameScene { self.scene as! GameScene }
override init() { override init() {
circle = SKShapeNode(circleOfRadius: Self.pieceSize / 2 * 0.90) circle = SKShapeNode(circleOfRadius: Self.pieceSize / 2 * 0.90)
@ -38,9 +52,95 @@ class PieceNode : SKNode {
super.init() super.init()
addChild(circle) addChild(circle)
zPosition = 100
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
public func resetDragPosition() {
let sc = self.scene!.size
self.position = CGPoint(x: sc.width / 2, y: sc.height - Self.pieceSize / 2)
}
private func dragTo(position pos: CGPoint) {
self.position = pos
}
private func releaseAt(position pos: CGPoint) {
resetDragPosition()
// TODO
}
#if os(macOS)
override public func touchesBegan(with event: NSEvent) {
let holder: NSView? = self.parent?.parent as? NSView
guard
self.draggable,
let touch = event.touches(matching: .began, in: holder).first
else { return }
let pos = touch.location(in: holder)
self.position = CGPoint(x: pos.y, y: pos.y)
}
override public func touchesMoved(with event: NSEvent) {
let holder: NSView? = self.parent?.parent as? NSView
guard
self.draggable,
let touch = event.touches(matching: .moved, in: holder).first
else { return }
let pos = touch.location(in: holder)
self.position = CGPoint(x: pos.y, y: pos.y)
}
override public func touchesEnded(with event: NSEvent) {
let holder: NSView? = self.parent?.parent as? NSView
guard
self.draggable,
let touch = event.touches(matching: .ended, in: holder).first
else { return }
let pos = touch.location(in: holder)
self.position = CGPoint(x: pos.y, y: pos.y)
}
#elseif os(iOS)
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard
self.draggable,
let touch = touches.first,
let holder = self.parent
else { return }
let pos = touch.location(in: holder)
self.dragTo(position: CGPoint(x: pos.x, y: pos.y))
}
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard
self.draggable,
let touch = touches.first,
let holder = self.parent
else { return }
let pos = touch.location(in: holder)
self.dragTo(position: CGPoint(x: pos.x, y: pos.y))
}
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard
self.draggable,
let touch = touches.first,
let holder = self.parent
else { return }
let pos = touch.location(in: holder)
self.releaseAt(position: CGPoint(x: pos.x, y: pos.y))
}
#endif
} }

@ -0,0 +1,3 @@
enum RulesType {
case Classic
}

@ -22,12 +22,12 @@ struct IngameView: View {
Spacer() Spacer()
VStack(alignment: .center) { VStack(alignment: .center) {
SpriteView(scene: self.scene) SpriteView(scene: self.scene, options: .allowsTransparency)
.aspectRatio( .aspectRatio(
self.scene.size.width / self.scene.size.height, self.scene.size.width / self.scene.size.height,
contentMode: .fit contentMode: .fit
) )
} }.safeAreaPadding()
Spacer() Spacer()
@ -51,14 +51,14 @@ private struct PlayerView: View {
let isLocal: Bool let isLocal: Bool
var body: some View { var body: some View {
VStack {
HStack {
let textAlignment: HorizontalAlignment = if self.isLocal { let textAlignment: HorizontalAlignment = if self.isLocal {
.leading .leading
} else { } else {
.trailing .trailing
} }
VStack {
HStack {
if self.isLocal { if self.isLocal {
Circle().frame(width: 50, height: 50) Circle().frame(width: 50, height: 50)
} }

@ -1,5 +1,45 @@
import Foundation import Foundation
import Connect4Core
import Connect4Players
class InGameVM: ObservableObject { class InGameVM: ObservableObject {
private var game: Game
init?(settings: NewGameVM, player1: PlayerSettingsVM, player2: PlayerSettingsVM) {
guard let rules = switch (settings.rulesType) {
case .Classic:
Connect4Rules(
nbRows: Int(settings.width), nbColumns: Int(settings.height),
nbPiecesToAlign: Int(settings.alignedTokens)
)
} else { return nil }
guard let player1 = Self.playerOf(settings: player1, id: .player1) else { return nil }
guard let player2 = Self.playerOf(settings: player2, id: .player2) else { return nil }
self.game = try! Game(withRules: rules, andPlayer1: player1, andPlayer2: player2)
for player in [player1, player2] {
(player as? HumanPlayer)?.changeInput(input: self.onMove)
}
}
private func onMove(player: HumanPlayer) -> Move? {
fatalError("TODO")
}
private static func playerOf(settings: PlayerSettingsVM, id: Owner) -> Player? {
return switch (settings.type) {
case .Human:
HumanPlayer(withName: settings.name, andId: id, andInputMethod: {
_ in fatalError("unreachable: \"uninitialized\" closure should be temporary")
})
case .AIRandom:
RandomPlayer(withName: settings.name, andId: id)
case .AIFinnishHim:
FinnishHimPlayer(withName: settings.name, andId: id)
case .AISimpleNegaMax:
SimpleNegaMaxPlayer(withName: settings.name, andId: id)
}
}
} }

@ -3,7 +3,7 @@ import Foundation
class NewGameVM : ObservableObject { class NewGameVM : ObservableObject {
@Published var rulesType: RulesType = .Classic @Published var rulesType: RulesType = .Classic
@Published var width: UInt = 7 { didSet { if width < 1 { width = 1 } } } @Published var width: UInt = 7 { didSet { if width < 1 { width = 1 } } }
@Published var height: UInt = 7 { didSet { if height < 1 { height = 1 } } } @Published var height: UInt = 6 { didSet { if height < 1 { height = 1 } } }
@Published var alignedTokens: UInt = 4 { didSet { if alignedTokens < 2 { alignedTokens = 2 } } } @Published var alignedTokens: UInt = 4 { didSet { if alignedTokens < 2 { alignedTokens = 2 } } }
} }
@ -16,7 +16,3 @@ class PlayerSettingsVM : ObservableObject, Identifiable {
self.type = type self.type = type
} }
} }
enum RulesType {
case Classic
}

Loading…
Cancel
Save