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 */; };
F0143C312DF9813C0086CAAA /* PieceNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0143C302DF9813C0086CAAA /* PieceNode.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 */; };
F0F59E4A2DD4958800BE32D6 /* C4Persistance.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F59E412DD492B400BE32D6 /* C4Persistance.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>"; };
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>"; };
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>"; };
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>"; };
@ -204,6 +206,7 @@
isa = PBXGroup;
children = (
F0143C332DF987490086CAAA /* PlayerType.swift */,
F0143C352DFA9A000086CAAA /* RulesType.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -359,6 +362,7 @@
F001A04E2DD48FAB00809561 /* MainMenuView.swift in Sources */,
F001A04C2DD48FAB00809561 /* AppApp.swift in Sources */,
F0143C2D2DF01E120086CAAA /* NewGameVM.swift in Sources */,
F0143C362DFA9A000086CAAA /* RulesType.swift in Sources */,
F0F59E532DDDC35100BE32D6 /* NewGameView.swift in Sources */,
F0143C2F2DF96B690086CAAA /* InGameVM.swift in Sources */,
F0F59E5B2DE6F68800BE32D6 /* ScoreboardView.swift in Sources */,
@ -527,6 +531,7 @@
DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\"";
ENABLE_PREVIEWS = 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=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
@ -563,6 +568,7 @@
DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\"";
ENABLE_PREVIEWS = 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=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;

@ -1,21 +1,29 @@
import SpriteKit
public class GameScene: SKScene {
public static let boardTopMargin: CGFloat = PieceNode.pieceSize / 2
private var boardNode: SKShapeNode
private var cells: [PieceNode]
private var draggablePiece: PieceNode
public init(s: (w: Int, h: Int)) {
let boardHeight = CGFloat(s.h) * 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.fillColor = .blue
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
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) {
case 1: .init(withOwner: .player1)
case 2: .init(withOwner: .player2)
@ -27,11 +35,15 @@ public class GameScene: SKScene {
super.init(size: CGSize(width: gWidth, height: gHeight))
// anchorPoint = CGPoint(x: 0.5, y: 0.5)
backgroundColor = .clear
addChild(boardNode)
addChild(draggablePiece)
for cell in cells {
addChild(cell)
}
draggablePiece.resetDragPosition()
}
public required init?(coder aDecoder: NSCoder) {

@ -7,6 +7,11 @@ class PieceNode : SKNode {
private var circle: SKShapeNode
override var isUserInteractionEnabled: Bool {
get { true }
set { /* fatalError("Unsupported operation: set") */ }
}
var piece: Piece = .init(withOwner: .noOne) {
didSet {
switch (piece.owner) {
@ -23,24 +28,119 @@ class PieceNode : SKNode {
}
}
}
var intPosition: (x: Int, y: Int) = (0, 0) {
var boardPosition: (x: Int, y: Int)? = nil {
didSet {
position = CGPoint(
x: CGFloat(intPosition.x) * Self.pieceSize + Self.pieceSize / 2,
y: CGFloat(intPosition.y) * Self.pieceSize + Self.pieceSize / 2
)
if let pos = boardPosition {
position = CGPoint(
x: CGFloat(pos.x) * 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() {
circle = SKShapeNode(circleOfRadius: Self.pieceSize / 2 * 0.90)
super.init()
addChild(circle)
zPosition = 100
}
required init?(coder aDecoder: NSCoder) {
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()
VStack(alignment: .center) {
SpriteView(scene: self.scene)
SpriteView(scene: self.scene, options: .allowsTransparency)
.aspectRatio(
self.scene.size.width / self.scene.size.height,
contentMode: .fit
)
}
}.safeAreaPadding()
Spacer()
@ -51,14 +51,14 @@ private struct PlayerView: View {
let isLocal: Bool
var body: some View {
let textAlignment: HorizontalAlignment = if self.isLocal {
.leading
} else {
.trailing
}
VStack {
HStack {
let textAlignment: HorizontalAlignment = if self.isLocal {
.leading
} else {
.trailing
}
if self.isLocal {
Circle().frame(width: 50, height: 50)
}

@ -1,5 +1,45 @@
import Foundation
import Connect4Core
import Connect4Players
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 {
@Published var rulesType: RulesType = .Classic
@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 } } }
}
@ -16,7 +16,3 @@ class PlayerSettingsVM : ObservableObject, Identifiable {
self.type = type
}
}
enum RulesType {
case Classic
}

Loading…
Cancel
Save