Compare commits

...

4 Commits

@ -10,53 +10,56 @@ import DouShouQiModel
import SpriteKit import SpriteKit
import SwiftUI import SwiftUI
// Class GameScene
// This class is used to display the game board and the pieces on it
class GameScene : SKScene{ class GameScene : SKScene{
// @ObservedObject var gameVm : GameVM
let imgBoard : SKSpriteNode = SKSpriteNode(imageNamed: "BoardImg") let imgBoard : SKSpriteNode = SKSpriteNode(imageNamed: "BoardImg")
let defaultSize : CGSize = CGSize(width: 120, height: 120) let defaultSize : CGSize = CGSize(width: 120, height: 120)
// var game : Game = try! Game(withRules: ClassicRules(), // Define constants
// andPlayer1: Player(withName: "Meruemu", andId: .player1)!, let meepleSizeWidth: CGFloat = 120
// andPlayer2: Player(withName: "Kumogi", andId: .player2)!) let meepleSizeHeight: CGFloat = 120
let pieces : [ Owner : [Animal : SpriteMeeple]] =
[
.player1 : [
.rat : SpriteMeeple(imageName: "RatMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
.cat : SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
.dog : SpriteMeeple(imageName: "DogoMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
.wolf : SpriteMeeple(imageName: "WolfMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
.leopard : SpriteMeeple(imageName: "LeoMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
.tiger : SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
.lion : SpriteMeeple(imageName: "LionMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
.elephant : SpriteMeeple(imageName: "ElphMeeple", size: CGSize(width: 120, height: 120), color: Color.player1),
],
.player2: [ // Players pieces
.rat : SpriteMeeple(imageName: "RatMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), let pieces: [Owner: [Animal: SpriteMeeple]] =
.cat : SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), [
.dog : SpriteMeeple(imageName: "DogoMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), .player1: [
.wolf : SpriteMeeple(imageName: "WolfMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), .rat: SpriteMeeple(imageName: "RatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.leopard : SpriteMeeple(imageName: "LeoMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), .cat: SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.tiger : SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), .dog: SpriteMeeple(imageName: "DogoMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.lion : SpriteMeeple(imageName: "LionMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), .wolf: SpriteMeeple(imageName: "WolfMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.elephant : SpriteMeeple(imageName: "ElphMeeple", size: CGSize(width: 120, height: 120), color: Color.player2), .leopard: SpriteMeeple(imageName: "LeoMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.tiger: SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.lion: SpriteMeeple(imageName: "LionMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.elephant: SpriteMeeple(imageName: "ElphMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
],
.player2: [
.rat: SpriteMeeple(imageName: "RatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
.cat: SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
.dog: SpriteMeeple(imageName: "DogoMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
.wolf: SpriteMeeple(imageName: "WolfMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
.leopard: SpriteMeeple(imageName: "LeoMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
.tiger: SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
.lion: SpriteMeeple(imageName: "LionMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
.elephant: SpriteMeeple(imageName: "ElphMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player2),
]
] ]
]
override init(size s: CGSize){ //, andVM gameVM : GameVM override init(size s: CGSize){
//self.gameVm = gameVM
super.init(size: s) super.init(size: s)
self.addChild(imgBoard) self.addChild(imgBoard)
//self.displayBoard(gameVm.game.board)
self.scaleMode = .aspectFit self.scaleMode = .aspectFit
self.anchorPoint = CGPoint(x: 0.5, y:0.5) self.anchorPoint = CGPoint(x: 0.5, y:0.5)
} }
/**
* Display the board
* @param board : DouShouQiModel.Board - The board to display
*/
func displayBoard(_ board : DouShouQiModel.Board){ func displayBoard(_ board : DouShouQiModel.Board){
//Nettoyage des fils //Nettoyage des fils
@ -67,7 +70,7 @@ class GameScene : SKScene{
for col in 0..<board.nbColumns{ for col in 0..<board.nbColumns{
if let p = board.grid[row][col].piece{ if let p = board.grid[row][col].piece{
pieces[p.owner]?[p.animal]?.cellPosition = CGPoint(x: row, y: col); pieces[p.owner]?[p.animal]?.cellPosition = CGPoint(x: row, y: col);
pieces[p.owner]?[p.animal]?.imageNode.size = CGSize(width: 120, height: 120) pieces[p.owner]?[p.animal]?.imageNode.size = CGSize(width: meepleSizeWidth, height: meepleSizeHeight)
self.addChild(pieces[p.owner]![p.animal]!) self.addChild(pieces[p.owner]![p.animal]!)
} }
} }
@ -75,7 +78,6 @@ class GameScene : SKScene{
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

@ -9,11 +9,11 @@ import Foundation
import DouShouQiModel import DouShouQiModel
import SpriteKit import SpriteKit
import SwiftUI import SwiftUI
// Gestion de la boucle de jeu par la création d'une tache async<
// On s'abonne aux événements, notament game.addPlayerNotifiedListeneren.
// Les joueurs n'ont besoin que de nom
/**
* GameVM
* Cette classe est utilisée pour gérer la logique du jeu
*/
class GameVM : ObservableObject, Identifiable { class GameVM : ObservableObject, Identifiable {
@Published var game: Game @Published var game: Game
@ -21,21 +21,12 @@ class GameVM : ObservableObject, Identifiable {
@Published var gameScene: GameScene @Published var gameScene: GameScene
@Published var isGameOver: Bool = false @Published var isGameOver: Bool = false
///Players (-game -> todelete)
// let player1 : Player
// let player2 : Player
// var pieces : [ Owner : [Animal : SpriteMeeple]]
// let actualRules : Rules
///Error ///Error
@Published var hasError : Bool = false @Published var hasError : Bool = false
///Message ///Message
@Published var displayMessage : String = "" @Published var displayMessage : String = ""
//On donne directement la scene et la game à la vm
public init(withGame _game : Game, andScene _gameScene : GameScene) { public init(withGame _game : Game, andScene _gameScene : GameScene) {
self.game = _game self.game = _game
@ -52,23 +43,22 @@ class GameVM : ObservableObject, Identifiable {
self.subscribesToMeeple() self.subscribesToMeeple()
///Lancement du jeu
Task{ Task{
try! await self.game.start() try! await self.game.start()
} }
} }
/// Début du jeu
func onGameStart(board : Board){ func onGameStart(board : Board){
displayMessage = " ==>> 🎉 GAME STARTS! 🎉 <<== " displayMessage = "==>> 🎉 GAME STARTS! 🎉 <<=="
self.gameScene.displayBoard(board) self.gameScene.displayBoard(board)
print("GAME STARTS")
} }
/// Notification du joueur
func onPlayerNotified(board : Board, player : Player) async{ func onPlayerNotified(board : Board, player : Player) async{
print("PLAYER NOTIFIED")
if player is HumanPlayer { if player is HumanPlayer {
displayMessage = "Player \(player.id == .player1 ? "🟡 1" : "🔴 2") - \(player.name), it's your turn!" displayMessage = "Player \(player.id == .player1 ? "🟡 1" : "🔴 2") - \(player.name), it's your turn!"
//try! await (player as! HumanPlayer).chooseMove(move)
} }
else { else {
do{ do{
@ -80,6 +70,7 @@ class GameVM : ObservableObject, Identifiable {
} }
} }
/// Mouvement choisi
func onMoveChosen(board : Board, move : Move, player : Player){ func onMoveChosen(board : Board, move : Move, player : Player){
///Récupération du Meeple : ///Récupération du Meeple :
let movedPiece = board.grid[move.rowOrigin][move.columnOrigin] let movedPiece = board.grid[move.rowOrigin][move.columnOrigin]
@ -98,16 +89,16 @@ class GameVM : ObservableObject, Identifiable {
if result { ///* invalidité terminante if result { ///* invalidité terminante
if let piece = board.grid[move.rowDestination][move.columnDestination].piece{ if let piece = board.grid[move.rowDestination][move.columnDestination].piece{
// Delete le meeple
let meeples = gameScene.pieces[player.id == .player1 ? .player2 : .player1] let meeples = gameScene.pieces[player.id == .player1 ? .player2 : .player1]
let meeple = meeples?.first(where: { let meeple = meeples?.first(where: {
$0.key == piece.animal $0.key == piece.animal
}) })
meeple?.value.parent?.removeChildren(in: [meeple!.value]) meeple?.value.parent?.removeChildren(in: [meeple!.value])
} }
return return
} }
///* invalidité non terminante
let piece = board.grid[move.rowOrigin][move.columnOrigin] let piece = board.grid[move.rowOrigin][move.columnOrigin]
let meeples = gameScene.pieces[move.owner] let meeples = gameScene.pieces[move.owner]
let meeple = meeples?.first(where: { let meeple = meeples?.first(where: {
@ -115,45 +106,21 @@ class GameVM : ObservableObject, Identifiable {
}) })
meeple?.value.cellPosition = CGPoint(x: move.rowOrigin, y: move.columnOrigin); meeple?.value.cellPosition = CGPoint(x: move.rowOrigin, y: move.columnOrigin);
print("INVALID")
} }
func invalideMoveChosen(board: Board, move: Move, player: Player, result: Bool) {
if result {
if let piece = board.grid[move.rowDestination][move.columnDestination].piece{
// Delete le meeple
let meeples = gameScene.pieces[player.id == .player1 ? .player2 : .player1]
let meeple = meeples?.first(where: {
$0.key == piece.animal
})
meeple?.value.parent?.removeChildren(in: [meeple!.value])
}
return
}
let piece = board.grid[move.rowOrigin][move.columnOrigin]
let meeples = gameScene.pieces[move.owner]
let meeple = meeples?.first(where: {
$0.key == piece.piece?.animal
})
meeple?.value.cellPosition = CGPoint(x: move.rowOrigin, y: move.columnOrigin);
}
///Changement de plateau
func onBoardChanged(board : Board){ func onBoardChanged(board : Board){
/// Bruit d'un placement de pion ? /// Bruit d'un placement de pion ?
} }
///Fin du jeu
func onGameOver(board : Board, result : Result, winner : Player?){ func onGameOver(board : Board, result : Result, winner : Player?){
displayMessage = "Game Over!!!" displayMessage = "Game Over!!!"
isGameOver = true ///Gestion par l'appelant isGameOver = true ///Gestion par l'appelant
} }
///*Meeples
///Meeples ///Abonnement aux mouvements des Meeples
func subscribesToMeeple(){ func subscribesToMeeple(){
for meeple in gameScene.pieces[.player1]!{ for meeple in gameScene.pieces[.player1]!{
meeple.value.observers.append(meepleMoved) meeple.value.observers.append(meepleMoved)
@ -163,13 +130,13 @@ class GameVM : ObservableObject, Identifiable {
} }
} }
///*Mouvement d'un Meeple
func meepleMoved(spriteMeeple: SpriteMeeple, startX: Int, startY: Int, endX: Int, endY: Int) async{ func meepleMoved(spriteMeeple: SpriteMeeple, startX: Int, startY: Int, endX: Int, endY: Int) async{
let owner : Owner = game.board.grid[startX][startY].piece!.owner let owner : Owner = game.board.grid[startX][startY].piece!.owner
let otherPlayer : Owner = self.game.rules.getNextPlayer() let otherPlayer : Owner = self.game.rules.getNextPlayer()
let move: Move = Move(of: owner, fromRow: startX, andFromColumn: startY, toRow: endX, andToColumn: endY) let move: Move = Move(of: owner, fromRow: startX, andFromColumn: startY, toRow: endX, andToColumn: endY)
if(otherPlayer != owner){ ///Mauvais Joueur pour ce jeton if(otherPlayer != owner){ ///Mauvais Joueur pour ce jeton
print("invalide de la part de meepleMoved")
onInvalidMove(board: game.board, move: move, player: game.players[otherPlayer]!, result: false) onInvalidMove(board: game.board, move: move, player: game.players[otherPlayer]!, result: false)
return return
} }
@ -177,12 +144,10 @@ class GameVM : ObservableObject, Identifiable {
if let player: HumanPlayer = game.players[owner] as? HumanPlayer{ if let player: HumanPlayer = game.players[owner] as? HumanPlayer{
try! await player.chooseMove(move) try! await player.chooseMove(move)
} }
// else { ///IMPOSSIBLE DE CAST LE JOUEUR HUMAIN // else { ///! IMPOSSIBLE DE CAST LE JOUEUR HUMAIN ==> SIGABRT
// let player: HumanPlayer = game.players[.player1] as! HumanPlayer // let player: HumanPlayer = game.players[.player1] as! HumanPlayer
// try! await player.chooseMove(move) // try! await player.chooseMove(move)
// } // }
print("il a bougé")
} }
} }

@ -7,6 +7,8 @@
import SwiftUI import SwiftUI
/// ImagePicker
/// Classe récupéré d'internet pour permettre de choisir une image dans la galerie
struct ImagePicker: UIViewControllerRepresentable { struct ImagePicker: UIViewControllerRepresentable {
@Environment(\.presentationMode) private var presentationMode @Environment(\.presentationMode) private var presentationMode
var sourceType: UIImagePickerController.SourceType = .photoLibrary var sourceType: UIImagePickerController.SourceType = .photoLibrary

@ -7,6 +7,7 @@
import Foundation import Foundation
///! Ancienne classe, supprimée suite à la mise en place de la librairie DouShouQiModel
/// Représente un joueur /// Représente un joueur
/* /*

@ -8,6 +8,8 @@
import Foundation import Foundation
import DouShouQiModel import DouShouQiModel
/// PlayerVM
/// Cette classe est utilisée pour gérer la logique du joueur
class PlayerVM : ObservableObject, Identifiable { class PlayerVM : ObservableObject, Identifiable {
@Published var player : Player @Published var player : Player
@Published var data : Player.Data @Published var data : Player.Data
@ -19,11 +21,13 @@ class PlayerVM : ObservableObject, Identifiable {
self.isEditing = false self.isEditing = false
} }
/// Début de l'édition
func onEditing(){ func onEditing(){
self.data = player.data self.data = player.data
self.isEditing = true self.isEditing = true
} }
/// Fin de l'édition
func onEdited(isCanceled cancel : Bool = false){ func onEdited(isCanceled cancel : Bool = false){
if !cancel { if !cancel {
//save //save

@ -9,7 +9,11 @@ import Foundation
import SpriteKit import SpriteKit
import SwiftUI import SwiftUI
/// SpriteMeeple
/// Cette classe est utilisée pour gérer les pièces des joueurs
class SpriteMeeple : SKNode, ObservableObject{ class SpriteMeeple : SKNode, ObservableObject{
let imageNode : SKSpriteNode let imageNode : SKSpriteNode
let ellipseNode : SKShapeNode let ellipseNode : SKShapeNode
@ -37,7 +41,6 @@ class SpriteMeeple : SKNode, ObservableObject{
ellipseNode = SKShapeNode(ellipseOf: CGSize(width: 100, height: 100)) ellipseNode = SKShapeNode(ellipseOf: CGSize(width: 100, height: 100))
ellipseNode.fillColor = UIColor(meepleColor); ellipseNode.fillColor = UIColor(meepleColor);
self.cellPosition = CGPoint(x: 0, y: 0) self.cellPosition = CGPoint(x: 0, y: 0)
@ -54,46 +57,58 @@ class SpriteMeeple : SKNode, ObservableObject{
get {true} get {true}
} }
///TouchesBegan
/// Cette fonction est appelée lorsque l'utilisateur commence à toucher l'écran
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
self.position = touches.first?.location(in: parent!) ?? CGPoint(x: 0, y: 0) self.position = touches.first?.location(in: parent!) ?? CGPoint(x: 0, y: 0)
} }
///TouchesEnded
/// Cette fonction est appelée lorsque l'utilisateur a terminé de toucher l'écran
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
//* Contantes
///Limites du board
static let rightLimit: CGFloat = 400
static let leftLimit: CGFloat = -400
static let topLimit: CGFloat = 300
static let bottomLimit: CGFloat = -300
static let gridSize: CGFloat = 100
/// Bordures du board /// Bordures du board
///Right ///Right
if (self.position.x < -400){ if (self.position.x < leftLimit){
self.position.x = -400; self.position.x = leftLimit;
} }
///Left ///Left
if (self.position.x > 400){ if (self.position.x > rightLimit){
self.position.x = 400; self.position.x = rightLimit;
} }
///Bottom ///Bottom
if (self.position.y < -300){ if (self.position.y < bottomLimit){
self.position.y = -300; self.position.y = bottomLimit;
} }
///Top ///Top
if (self.position.y > 300){ if (self.position.y > topLimit){
self.position.y = 300; self.position.y = topLimit;
} }
if (!Int(self.position.x).isMultiple(of: 100)){ if (!Int(self.position.x).isMultiple(of: gridSize)){
let calcx = self.position.x/100.0; let calcx = self.position.x/gridSize;
self.position.x = (calcx.rounded(.toNearestOrAwayFromZero))*100; self.position.x = (calcx.rounded(.toNearestOrAwayFromZero))*gridSize;
} }
if (!Int(self.position.y).isMultiple(of: 100)){ if (!Int(self.position.y).isMultiple(of: gridSize)){
let calcy = self.position.y/100.0; let calcy = self.position.y/gridSize;
self.position.y = (calcy.rounded(.toNearestOrAwayFromZero))*100; self.position.y = (calcy.rounded(.toNearestOrAwayFromZero))*gridSize;
} }
///Envoi au observers d'une notif ///Envoi aux observers d'une notif
///* Exectution des Observeurs ///* Execution des Observeurs
Task { Task {
for observer in observers { for observer in observers {
await observer(self, Int(cellPosition.x), Int(cellPosition.y), Int(position.x), Int(position.y)) await observer(self, Int(cellPosition.x), Int(cellPosition.y), Int(position.x), Int(position.y))

@ -7,6 +7,8 @@
import SwiftUI import SwiftUI
/// ClassicTextDisplay
/// Affichage classique d'un texte récurrent
struct ClassicTextDisplay: View { struct ClassicTextDisplay: View {
let text : String let text : String

@ -7,6 +7,7 @@
import SwiftUI import SwiftUI
/// Affichage d'une texte en deux parties
struct DoubleTextDisplay: View { struct DoubleTextDisplay: View {
let textL : String let textL : String
let textR : String let textR : String

@ -7,6 +7,7 @@
import SwiftUI import SwiftUI
/// Bouton de navigation récurrent
struct NavButton<D1:View>: View { struct NavButton<D1:View>: View {
let text : String let text : String

@ -8,6 +8,8 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
/// Extension de la classe Color pour ajouter des couleurs personnalisées
//! classe DEPRECATED : utilisée avant la mise en place des thèmes via XCode
public extension Color { public extension Color {
init(hex: String) { init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)

@ -8,6 +8,8 @@
import Foundation import Foundation
import DouShouQiModel import DouShouQiModel
/// Extension de Player
//! DEPRECIATED : Créé pour la modification de la classe Player, abandonné car nécéssite l'abandon de la librairie DouShouQiModel
extension Player { extension Player {
//var age : Int{get{return 0} set{age = newValue}} //var age : Int{get{return 0} set{age = newValue}}

@ -8,6 +8,8 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
/// FullButtonStyle
/// Utilisé pour définir le style d'un bouton
struct FullButtonStyle: ButtonStyle { struct FullButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View { func makeBody(configuration: Configuration) -> some View {
configuration.label configuration.label

@ -8,8 +8,10 @@
import Foundation import Foundation
import DouShouQiModel import DouShouQiModel
// Liste de joueurs fictifs afin de tester l'application.
struct StubbedPlayers{ struct StubbedPlayers{
//(Joueurs populaires de Gun-gi)//
let meruem : PlayerVM = PlayerVM(with: Player(withName: "Meruemu", andId: .player1)!) let meruem : PlayerVM = PlayerVM(with: Player(withName: "Meruemu", andId: .player1)!)
let kumogi : PlayerVM = PlayerVM(with: Player(withName: "Kumogi", andId: .player2)!) let kumogi : PlayerVM = PlayerVM(with: Player(withName: "Kumogi", andId: .player2)!)

@ -8,6 +8,8 @@
import SwiftUI import SwiftUI
import DouShouQiModel import DouShouQiModel
/// EditModalView
/// Cette vue modale est utilisée pour éditer un joueur
struct EditModalView: View { struct EditModalView: View {
var playerVM : PlayerVM var playerVM : PlayerVM

@ -9,13 +9,15 @@ import SwiftUI
import SpriteKit import SpriteKit
import DouShouQiModel import DouShouQiModel
/// Vue principale du jeu
struct GameView: View { struct GameView: View {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@State private var turnNumber = 1 @State private var turnNumber = 1
@State private var gameTime = 0.0 // Temps en secondes @State private var gameTime = 0.0 // Temps en secondes
//@State private var statusMessage = "C'est votre tour !"
//Nombre de pièces des joueurs
@State private var player1Pieces = 8 @State private var player1Pieces = 8
@State private var player2Pieces = 8 @State private var player2Pieces = 8
@ -25,20 +27,10 @@ struct GameView: View {
///VM ///VM
var gameVM : GameVM var gameVM : GameVM
///SK
//var gs : GameScene
init (gameVM : GameVM){ init (gameVM : GameVM){
//self.gameVM = GameVM(andPlayer1: Player(withName: "Meruemu", andId: .player1)!, andPlayer2: Player(withName: "Kumogi", andId: .player2)!)
self.gameVM = gameVM self.gameVM = gameVM
// self.gs = GameScene(size: CGSizeMake(940,740), andVM: gameVM)
//self.gs = GameScene(size: CGSizeMake(940,740))
} }
var body: some View { var body: some View {
@ -61,25 +53,7 @@ struct GameView: View {
} }
.padding([.leading, .trailing], 20) .padding([.leading, .trailing], 20)
} }
/*
///Old Grid Support
LazyVGrid(columns: gridItems, spacing: 5) {
ForEach(0..<64) { index in
Rectangle()
.foregroundColor((index / 8 + index % 8) % 2 == 0 ? .orange : .gray.opacity(0.2))
.frame(height: 35)
.cornerRadius(10)
}
}
.padding(10)
.background(Color.yellow)
.cornerRadius(15)
.padding(20)
*/
SpriteView(scene: gameVM.gameScene) SpriteView(scene: gameVM.gameScene)
.frame(width: 350, height: 275) .frame(width: 350, height: 275)
.padding(10) .padding(10)

@ -7,7 +7,7 @@
import SwiftUI import SwiftUI
/// Vue de l'historique d'un joueur
struct HistoryView: View { struct HistoryView: View {
var playerName: String var playerName: String
var matches: [Match] // Modèle des parties du joueur var matches: [Match] // Modèle des parties du joueur
@ -68,7 +68,6 @@ struct Match: Identifiable {
var opponent: String var opponent: String
var selfScore: Int var selfScore: Int
var opScore: Int var opScore: Int
//var result: String
} }
struct HistoryView_Previews: PreviewProvider { struct HistoryView_Previews: PreviewProvider {

@ -8,14 +8,14 @@
import SwiftUI import SwiftUI
import DouShouQiModel import DouShouQiModel
/// Vue principale de l'application
struct MainMenuView: View { struct MainMenuView: View {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
var gamevm : GameVM = GameVM(withGame: try! Game(withRules: ClassicRules(), var gamevm : GameVM = GameVM(withGame: try! Game(withRules: ClassicRules(),
andPlayer1: Player(withName: "Meruemu", andId: .player1)!, andPlayer1: Player(withName: "Meruemu", andId: .player1)!,
andPlayer2: Player(withName: "Kumogi", andId: .player2)!), andPlayer2: Player(withName: "Kumogi", andId: .player2)!),
andScene: GameScene(size: CGSize(width: 940, height: 740))) andScene: GameScene(size: CGSize(width: 940, height: 740)))
var body: some View { var body: some View {

@ -8,16 +8,14 @@
import SwiftUI import SwiftUI
import DouShouQiModel import DouShouQiModel
/// Liste des joueurs
struct PlayerListView: View { struct PlayerListView: View {
var playerStub : StubbedPlayers = StubbedPlayers() var playerStub : StubbedPlayers = StubbedPlayers()
var body: some View { var body: some View {
//NavigationStack {
ZStack { ZStack {
Rectangle().fill(Color.bgColor).ignoresSafeArea() Rectangle().fill(Color.bgColor).ignoresSafeArea()
VStack { VStack {
/*Text("Classement des joueurs")
.scaleEffect(1.75)*/
List(playerStub.getStubbedPlayer()) { player in List(playerStub.getStubbedPlayer()) { player in
NavigationLink { NavigationLink {
PlayerView(age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [ PlayerView(age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [
@ -25,7 +23,7 @@ struct PlayerListView: View {
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3), Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3),
Match(gameEndScreen: "opponent3", opponent: "Opponent 3", selfScore: 2, opScore: 2) Match(gameEndScreen: "opponent3", opponent: "Opponent 3", selfScore: 2, opScore: 2)
], ],
playerVm: player) //mettre player plus tard playerVm: player)
} label:{Text("pif")} } label:{Text("pif")}
}.navigationDestination(for: String.self) { name in }.navigationDestination(for: String.self) { name in
PlayerView(age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [ PlayerView(age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [
@ -33,37 +31,10 @@ struct PlayerListView: View {
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3), Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3),
Match(gameEndScreen: "opponent3", opponent: "Opponent 3", selfScore: 2, opScore: 2) Match(gameEndScreen: "opponent3", opponent: "Opponent 3", selfScore: 2, opScore: 2)
], ],
playerVm: PlayerVM(with: Player(withName: "Toto", andId: .player2)!)) //mettre player plus tard playerVm: PlayerVM(with: Player(withName: "Toto", andId: .player2)!))
} }
.navigationTitle("Leaderboard") .navigationTitle("Leaderboard")
List {
NavigationLink{
PlayerView(age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [
Match(gameEndScreen: "opponent1", opponent: "Opponent 1", selfScore: 3, opScore: 2),
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3),
Match(gameEndScreen: "opponent3", opponent: "Opponent 3", selfScore: 2, opScore: 2)
],
playerVm: PlayerVM(with: Player(withName: "Billy", andId: .player2)!)) //mettre player plus tard
} label:{Text("pif")}
NavigationLink("Billy", value: "Billyname")
NavigationLink("Bob", value: "Bob")
NavigationLink("Bromingo", value: "Bromingo")
}
.navigationDestination(for: String.self) { name in
PlayerView(age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [
Match(gameEndScreen: "opponent1", opponent: "Opponent 1", selfScore: 3, opScore: 2),
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3),
Match(gameEndScreen: "opponent3", opponent: "Opponent 3", selfScore: 2, opScore: 2)
],
playerVm: PlayerVM(with: Player(withName: "Toto", andId: .player2)!)) //mettre player plus tard
}
.navigationTitle("Leaderboard")
} }
VStack { VStack {

@ -7,6 +7,8 @@
import SwiftUI import SwiftUI
/// PlayerSelect
/// Cette vue est utilisée pour sélectionner un joueur
struct PlayerSelect: View { struct PlayerSelect: View {
@State private var image = UIImage() @State private var image = UIImage()
@State private var showSheet = false @State private var showSheet = false
@ -19,6 +21,26 @@ struct PlayerSelect: View {
VStack { VStack {
VStack{ VStack{
Text("Joueur 1") Text("Joueur 1")
HStack {
Image(uiImage: self.image)
.resizable()
.cornerRadius(50)
.frame(width: 100, height: 100)
.background(Color.black.opacity(0.2))
.aspectRatio(contentMode: .fill)
.clipShape(Circle())
ClassicTextDisplay(text: "Change photo")
.onTapGesture {
showSheet = true
}
}
.padding(.horizontal, 20)
.sheet(isPresented: $showSheet) {
ImagePicker(sourceType: .photoLibrary, selectedImage: self.$image)
}
TextField("Entrez un nom ici",text: .constant("")) TextField("Entrez un nom ici",text: .constant(""))
}.foregroundStyle(.primary) }.foregroundStyle(.primary)
.padding(20) .padding(20)
@ -49,11 +71,7 @@ struct PlayerSelect: View {
} }
.padding(.horizontal, 20) .padding(.horizontal, 20)
.sheet(isPresented: $showSheet) { .sheet(isPresented: $showSheet) {
// Pick an image from the photo library:
ImagePicker(sourceType: .photoLibrary, selectedImage: self.$image) ImagePicker(sourceType: .photoLibrary, selectedImage: self.$image)
// If you wish to take a photo from camera instead:
// ImagePicker(sourceType: .camera, selectedImage: self.$image)
} }
TextField("Entrez un nom ici",text: .constant("")) TextField("Entrez un nom ici",text: .constant(""))

@ -8,10 +8,8 @@
import SwiftUI import SwiftUI
import DouShouQiModel import DouShouQiModel
/// Affiche les statistiques d'un joueur
struct PlayerView: View { struct PlayerView: View {
//var Player : Player;
//var name : String
var age: Int var age: Int
var winLossRatio: Double var winLossRatio: Double
var gamesPlayed: Int var gamesPlayed: Int
@ -21,11 +19,8 @@ struct PlayerView: View {
@State var isEdited : Bool = false @State var isEdited : Bool = false
@ObservedObject var playerVm : PlayerVM //TODO @ObservedObject var playerVm : PlayerVM //TODO
//@Published var data : Data
var body: some View { var body: some View {
//ZStack{
//Rectangle().fill(Color.bgColor).ignoresSafeArea()
VStack{ VStack{
VStack{ VStack{
Image("Blob") Image("Blob")
@ -98,21 +93,17 @@ struct PlayerView: View {
.padding(.top, 20) .padding(.top, 20)
} }
.toolbar { .toolbar {
//ToolbarItemGroup(placement: .bottomBar) {
Button(action: {isEdited.toggle()}, label: {Text("Edit")}) Button(action: {isEdited.toggle()}, label: {Text("Edit")})
//}
} }
.sheet(isPresented: $isEdited, content: { .sheet(isPresented: $isEdited, content: {
EditModalView(playerVM: playerVm, isEdited: $isEdited) EditModalView(playerVM: playerVm, isEdited: $isEdited)
}) })
//}//Zstack
} }
} }
struct PlayerView_Previews: PreviewProvider { struct PlayerView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
/*var player1 = new Player(Player(withName: "Meruemu", andSName: "Roi", andId: 2, andAge: 1)*/
let stubedMatches = [ let stubedMatches = [
Match(gameEndScreen: "opponent1", opponent: "Opponent 1", selfScore: 3, opScore: 2), Match(gameEndScreen: "opponent1", opponent: "Opponent 1", selfScore: 3, opScore: 2),
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3), Match(gameEndScreen: "opponent2", opponent: "Opponent 2", selfScore: 1, opScore: 3),

@ -8,14 +8,11 @@
import SwiftUI import SwiftUI
import SpriteKit import SpriteKit
///! TODELETE
struct SpriteKitView: View { struct SpriteKitView: View {
///Sprite Kit
//var gs : GameScene = GameScene(size: CGSizeMake(940,740))
var body: some View { var body: some View {
VStack{ VStack{
//SpriteView(scene: gs)
} }
} }
} }

@ -1,2 +1,64 @@
# SwiftUiTp # SwiftUiTp
Projet de DouShouQi avec SwiftUI-SpriteKit et ARKit
## Overview
Ce projet est une implémentation du jeu de société traditionnel chinois Dou Shou Qi, également connu sous le nom de Jeu de la Jungle, pour les appareils iOS. Il s'agit d'un jeu stratégique à deux joueurs dans lequel chaque joueur contrôle huit pions représentants différents animaux ayant des rangs et des pouvoirs variés. L'objectif est de capturer la tanière de l'adversaire ou d'éliminer toutes les pièces adverses.
## Technologies utilisées
- **Swift** : Le principal langage de programmation pour le développement d'applications iOS.
- **SpriteKit** : Une puissante infrastructure de rendu graphique et d'animation intégrée à iOS pour la création de jeux en 2D.
- **Xcode** : L'environnement de développement intégré (IDE) pour macOS, utilisé pour le développement d'applications iOS.
## Features
- Gestion d'un thème sombre en fonction du thème du device
- Menu
- Affichage d'une historique dynamique en fonction de données stubées
- ARKIT : Affichage d'une component Board dans un WorldPannel, sans la logique de jeu
- (SpriteKit) Affichage d'une board dynamique en 2D, mais lié qu'avec une partie des règles de jeu
- Jetons personnalisés
- Gestion des mouvements invalides
## Featuresn't
- L'applications n'a pas d'options multilingue
- Aucune persistance de données
- Création de joueur non géré
- Impossibilité de lancer une partie
- Impossibilité de lancer le jeu contre une IA
- Modification des joueurs commencée, mais abandonnée car j'ai perdu trop de temps dessus, et qu'il aurait fallu recoder le modèle pour cette simple fonctionnalité (cela m'a permis quand même de toucher au Binding)
## Erreur
Une erreur SIGABRT est levée lors d'une cast d'un ``Player?`` en ``HumainPlayer``, je n'ai pas réussi à régler ce problème.
Possible problèmes dû au nettoyage car aucuns test n'a été effectué ensuite.
# :construction_worker: Développeur
- Pierre FERREIRA : pierre.ferreira@etu.uca.fr
<div align="center">
<a href = "https://codefirst.iut.uca.fr/git/pierre.ferreira">
<img src="https://codefirst.iut.uca.fr/git/avatars/edbacace5f621ae77077f206ebdcee27?size=870" width="50" >
</a>
© IUT - Auvergne
</div>
---
<div style="display: flex; align-items: center;">
<p><i><strong>While writing this README, I was listening to...</strong></i></p>
<a href="https://www.youtube.com/watch?v=TaRleMb7eH0" target="_blank" style="text-decoration: none;">
<img src="https://img.youtube.com/vi/TaRleMb7eH0/0.jpg" alt="YouTube Thumbnail" style="width: 120px; margin-right: 10px;">
</a>
<div>
<p><strong>Pure Vessel - Hollow Knight OST Extended</strong></p>
<p><em>Team Cherry - (2017)</em></p>
</div>
</div>
Loading…
Cancel
Save