Compare commits

...

19 Commits

Author SHA1 Message Date
Pierre FERREIRA 91a6fec4ec Mise à jour de 'README.md'
10 months ago
Pierre FERREIRA d2b7c640f0 Mise à jour de 'README.md'
10 months ago
Pierre FERREIRA 7c07ad7d98 Mise à jour de 'README.md'
10 months ago
Pierre Ferreira b494c21a19 clean code : suppression de magic number, ajout de commentaire et relecture des codes morts 🔥
10 months ago
Pierre Ferreira 2b0d70d9db Merge branch 'master' of https://codefirst.iut.uca.fr/git/pierre.ferreira/SwiftUiTp into gameBranch
10 months ago
Pierre Ferreira bce2fd695d Merge branch 'firstTest' of https://codefirst.iut.uca.fr/git/pierre.ferreira/SwiftUiTp into gameBranch
10 months ago
Pierre FERREIRA b0ccb6ba85 liaison avec les evenement, interuption par une erreur SIGABRT sur un cast
10 months ago
Pierre FERREIRA d8736beacb ajout de événements (non fonctionnel) 🍱
10 months ago
étudiant2 fb33a8416c premier affichage du board, il faut remettre les objets un par un
10 months ago
Pierre FERREIRA 8f78239046 ajout de la page de sélection de personnage pour la création d'un jeu 🍱
10 months ago
Pierre FERREIRA 78c8260d91 poursuite de la liaisona vec le jeu, coomment recup le status des meeples pour le readmove avec cette archi ? 🍻
10 months ago
Pierre FERREIRA 81be263b3a debut de l'assimilation au jeu :wheel_chair:
10 months ago
Pierre FERREIRA 3f1b5618ba Début de l'assignation au grègles de jeu ! 🍱
11 months ago
Pierre FERREIRA 9158760e89 création du stub et changement de la liste + abandon de l'edit du joueur car impossible avec la lib donnée => go faire le jeu 💥
11 months ago
Pierre FERREIRA 272612da24 ajout de la view pour le jeu
11 months ago
Pierre FERREIRA 95efe097dd Ajout de l'historique, du menu principale et de style un peu partout 💥
11 months ago
Pierre FERREIRA ade74defe5 ajout de style et de la classe player 💄
12 months ago
Pierre FERREIRA 7017ff9285 🎉 premier master detail
12 months ago
Pierre FERREIRA fd3561e7fc Initial commit
12 months ago

@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
7B236FB62C29B371008E9CA7 /* DSQ.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B6426F12C00B61500575E16 /* DSQ.xcframework */; };
7B2597B82C203AFE0095F010 /* PlayerSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2597B72C203AFE0095F010 /* PlayerSelect.swift */; };
7B2597BA2C203FCB0095F010 /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2597B92C203FCB0095F010 /* ImagePicker.swift */; };
7B3B17642BF24B32002BC817 /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3B17632BF24B32002BC817 /* Player.swift */; };
7B3B17672BF24ED6002BC817 /* FullButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3B17662BF24ED6002BC817 /* FullButtonStyle.swift */; };
7B4508CD2BF206AF0027E1EF /* DouShouQiIOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4508CC2BF206AF0027E1EF /* DouShouQiIOSApp.swift */; };
@ -18,7 +21,6 @@
7B4508EA2BF206B10027E1EF /* DouShouQiIOSUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4508E92BF206B10027E1EF /* DouShouQiIOSUITestsLaunchTests.swift */; };
7B4508F72BF2084B0027E1EF /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4508F62BF2084B0027E1EF /* PlayerView.swift */; };
7B4508FA2BF214F50027E1EF /* PlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4508F92BF214F50027E1EF /* PlayerListView.swift */; };
7B6426F22C00B61500575E16 /* DSQ.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B6426F12C00B61500575E16 /* DSQ.xcframework */; };
7B6426F72C00B81400575E16 /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6426F62C00B81400575E16 /* GameScene.swift */; };
7B6426F92C00BDEA00575E16 /* SpriteKitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6426F82C00BDEA00575E16 /* SpriteKitView.swift */; };
7B6426FB2C00BFF500575E16 /* SpriteMeeple.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6426FA2C00BFF500575E16 /* SpriteMeeple.swift */; };
@ -29,6 +31,8 @@
7BDE976D2C0E13160079F2CD /* EditModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDE976C2C0E13160079F2CD /* EditModalView.swift */; };
7BDE976F2C0E18160079F2CD /* PlayerVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDE976E2C0E18160079F2CD /* PlayerVM.swift */; };
7BE8C5652C09A29A00A6E8C9 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE8C5642C09A29A00A6E8C9 /* Color.swift */; };
7BEAB78B2C19A26000DF39C9 /* StubbedPlayers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEAB78A2C19A26000DF39C9 /* StubbedPlayers.swift */; };
7BEAB78D2C19A94100DF39C9 /* GameVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEAB78C2C19A94100DF39C9 /* GameVM.swift */; };
BA5AFD332C09C7470056D332 /* ClassicTextDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5AFD322C09C7470056D332 /* ClassicTextDisplay.swift */; };
BA5AFD352C09CAC10056D332 /* NavButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5AFD342C09CAC10056D332 /* NavButton.swift */; };
BA5AFD392C09D1020056D332 /* DoubleTextDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5AFD382C09D1020056D332 /* DoubleTextDisplay.swift */; };
@ -52,6 +56,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
7B2597B72C203AFE0095F010 /* PlayerSelect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSelect.swift; sourceTree = "<group>"; };
7B2597B92C203FCB0095F010 /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = "<group>"; };
7B3B17632BF24B32002BC817 /* Player.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = "<group>"; };
7B3B17662BF24ED6002BC817 /* FullButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullButtonStyle.swift; sourceTree = "<group>"; };
7B4508C92BF206AF0027E1EF /* DouShouQiIOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DouShouQiIOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -78,6 +84,8 @@
7BDE976C2C0E13160079F2CD /* EditModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditModalView.swift; sourceTree = "<group>"; };
7BDE976E2C0E18160079F2CD /* PlayerVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerVM.swift; sourceTree = "<group>"; };
7BE8C5642C09A29A00A6E8C9 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
7BEAB78A2C19A26000DF39C9 /* StubbedPlayers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubbedPlayers.swift; sourceTree = "<group>"; };
7BEAB78C2C19A94100DF39C9 /* GameVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameVM.swift; sourceTree = "<group>"; };
BA5AFD322C09C7470056D332 /* ClassicTextDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicTextDisplay.swift; sourceTree = "<group>"; };
BA5AFD342C09CAC10056D332 /* NavButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavButton.swift; sourceTree = "<group>"; };
BA5AFD382C09D1020056D332 /* DoubleTextDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleTextDisplay.swift; sourceTree = "<group>"; };
@ -88,7 +96,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7B6426F22C00B61500575E16 /* DSQ.xcframework in Frameworks */,
7B236FB62C29B371008E9CA7 /* DSQ.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -116,6 +124,8 @@
7B6426F62C00B81400575E16 /* GameScene.swift */,
7B6426FA2C00BFF500575E16 /* SpriteMeeple.swift */,
7BDE976E2C0E18160079F2CD /* PlayerVM.swift */,
7BEAB78C2C19A94100DF39C9 /* GameVM.swift */,
7B2597B92C203FCB0095F010 /* ImagePicker.swift */,
);
path = Class;
sourceTree = "<group>";
@ -152,6 +162,7 @@
7B4508CB2BF206AF0027E1EF /* DouShouQiIOS */ = {
isa = PBXGroup;
children = (
7BEAB7892C19A24800DF39C9 /* Stub */,
BA5AFD312C09C6E90056D332 /* ContentView */,
7BE8C5632C09A27600A6E8C9 /* Extension */,
7B3B17652BF24EB1002BC817 /* Struct */,
@ -200,6 +211,7 @@
7BB279882BFDF79E00491801 /* GameView.swift */,
7B6426F82C00BDEA00575E16 /* SpriteKitView.swift */,
7BDE976C2C0E13160079F2CD /* EditModalView.swift */,
7B2597B72C203AFE0095F010 /* PlayerSelect.swift */,
);
path = View;
sourceTree = "<group>";
@ -222,6 +234,14 @@
path = Extension;
sourceTree = "<group>";
};
7BEAB7892C19A24800DF39C9 /* Stub */ = {
isa = PBXGroup;
children = (
7BEAB78A2C19A26000DF39C9 /* StubbedPlayers.swift */,
);
path = Stub;
sourceTree = "<group>";
};
BA5AFD312C09C6E90056D332 /* ContentView */ = {
isa = PBXGroup;
children = (
@ -368,11 +388,14 @@
7BDD43C72BFCC927003984FB /* MainMenuView.swift in Sources */,
7B4508CF2BF206AF0027E1EF /* ContentView.swift in Sources */,
7BE8C5652C09A29A00A6E8C9 /* Color.swift in Sources */,
7B2597B82C203AFE0095F010 /* PlayerSelect.swift in Sources */,
7B6426FB2C00BFF500575E16 /* SpriteMeeple.swift in Sources */,
7B4508CD2BF206AF0027E1EF /* DouShouQiIOSApp.swift in Sources */,
7BEAB78B2C19A26000DF39C9 /* StubbedPlayers.swift in Sources */,
7B6426F92C00BDEA00575E16 /* SpriteKitView.swift in Sources */,
7BDE976D2C0E13160079F2CD /* EditModalView.swift in Sources */,
7BDE976F2C0E18160079F2CD /* PlayerVM.swift in Sources */,
7B2597BA2C203FCB0095F010 /* ImagePicker.swift in Sources */,
BA5AFD392C09D1020056D332 /* DoubleTextDisplay.swift in Sources */,
BA5AFD332C09C7470056D332 /* ClassicTextDisplay.swift in Sources */,
BA5AFD352C09CAC10056D332 /* NavButton.swift in Sources */,
@ -380,6 +403,7 @@
7BDD43C92BFCD2C4003984FB /* HistoryView.swift in Sources */,
7B3B17642BF24B32002BC817 /* Player.swift in Sources */,
7B3B17672BF24ED6002BC817 /* FullButtonStyle.swift in Sources */,
7BEAB78D2C19A94100DF39C9 /* GameVM.swift in Sources */,
7B4508FA2BF214F50027E1EF /* PlayerListView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

@ -10,92 +10,68 @@ import DouShouQiModel
import SpriteKit
import SwiftUI
// Class GameScene
// This class is used to display the game board and the pieces on it
class GameScene : SKScene{
let imgBoard : SKSpriteNode = SKSpriteNode(imageNamed: "BoardImg")
let defaultSize : CGSize = CGSize(width: 120, height: 120)
var game : Game = try! Game(withRules: ClassicRules(),
andPlayer1: Player(withName: "Meruemu", andId: .player1)!,
andPlayer2: Player(withName: "Kumogi", andId: .player2)!)
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: [
.rat : SpriteMeeple(imageName: "RatMeeple", size: CGSize(width: 120, height: 120), color: Color.player2),
.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),
.wolf : SpriteMeeple(imageName: "WolfMeeple", size: CGSize(width: 120, height: 120), color: Color.player2),
.leopard : SpriteMeeple(imageName: "LeoMeeple", size: CGSize(width: 120, height: 120), color: Color.player2),
.tiger : SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: 120, height: 120), color: Color.player2),
.lion : SpriteMeeple(imageName: "LionMeeple", size: CGSize(width: 120, height: 120), color: Color.player2),
.elephant : SpriteMeeple(imageName: "ElphMeeple", size: CGSize(width: 120, height: 120), color: Color.player2),
// Define constants
let meepleSizeWidth: CGFloat = 120
let meepleSizeHeight: CGFloat = 120
// Players pieces
let pieces: [Owner: [Animal: SpriteMeeple]] =
[
.player1: [
.rat: SpriteMeeple(imageName: "RatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.cat: SpriteMeeple(imageName: "CatMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.dog: SpriteMeeple(imageName: "DogoMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.wolf: SpriteMeeple(imageName: "WolfMeeple", size: CGSize(width: meepleSizeWidth, height: meepleSizeHeight), color: Color.player1),
.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){
super.init(size: s)
self.addChild(imgBoard)
self.displayBoard(game.board)
/*
let cat = SpriteMeeple(imageName: "CatMeeple")
cat.position = CGPoint(x: 300, y: 200)
cat.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(cat)
let rat = SpriteMeeple(imageName: "RatMeeple")
rat.position = CGPoint(x: 200, y: 100)
rat.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(rat)
let dog = SpriteMeeple(imageName: "DogoMeeple")
dog.position = CGPoint(x: 200, y: 300)
dog.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(dog)
let cat2 = SpriteMeeple(imageName: "CatMeeple")
cat2.position = CGPoint(x: 300, y: -200)
cat2.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(cat2)
let rat2 = SpriteMeeple(imageName: "RatMeeple")
rat2.position = CGPoint(x: 200, y: -100)
rat2.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(rat2)
let dog2 = SpriteMeeple(imageName: "DogoMeeple")
dog2.position = CGPoint(x: 200, y: -300)
dog2.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(dog2)
let wolf = SpriteMeeple(imageName: "WolfMeeple")
wolf.position = CGPoint(x: 300, y: 0)
wolf.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(wolf)
*/
self.addChild(imgBoard)
self.scaleMode = .aspectFit
self.anchorPoint = CGPoint(x: 0.5, y:0.5)
}
private func displayBoard(_ board : DouShouQiModel.Board){
/**
* Display the board
* @param board : DouShouQiModel.Board - The board to display
*/
func displayBoard(_ board : DouShouQiModel.Board){
//Nettoyage des fils
self.removeAllChildren()
self.addChild(imgBoard)
for row in 0..<board.nbRows{
for col in 0..<board.nbColumns{
if let p = board.grid[row][col].piece{
pieces[p.owner]?[p.animal]?.cellPosition = CGPoint(x: row, y: col);
pieces[p.owner]?[p.animal]?.imageNode.size = CGSize(width: 120, height: 120)
self.addChild(pieces[p.owner]![p.animal]!);
pieces[p.owner]?[p.animal]?.imageNode.size = CGSize(width: meepleSizeWidth, height: meepleSizeHeight)
self.addChild(pieces[p.owner]![p.animal]!)
}
}
}

@ -0,0 +1,153 @@
//
// GameVM.swift
// DouShouQiIOS
//
// Created by Pierre FERREIRA on 12/06/2024.
//
import Foundation
import DouShouQiModel
import SpriteKit
import SwiftUI
/**
* GameVM
* Cette classe est utilisée pour gérer la logique du jeu
*/
class GameVM : ObservableObject, Identifiable {
@Published var game: Game
@Published var gameScene: GameScene
@Published var isGameOver: Bool = false
///Error
@Published var hasError : Bool = false
///Message
@Published var displayMessage : String = ""
public init(withGame _game : Game, andScene _gameScene : GameScene) {
self.game = _game
self.gameScene = _gameScene
///Abonnement aux événements
self.game.addGameStartedListener(onGameStart)
self.game.addPlayerNotifiedListener(onPlayerNotified)
self.game.addMoveChosenCallbacksListener(onMoveChosen)
self.game.addInvalidMoveCallbacksListener(onInvalidMove)
self.game.addBoardChangedListener(onBoardChanged)
self.game.addGameOverListener(onGameOver)
//self.game?.addGameChangedListener({}) - //? cas persistance
self.subscribesToMeeple()
///Lancement du jeu
Task{
try! await self.game.start()
}
}
/// Début du jeu
func onGameStart(board : Board){
displayMessage = "==>> 🎉 GAME STARTS! 🎉 <<=="
self.gameScene.displayBoard(board)
}
/// Notification du joueur
func onPlayerNotified(board : Board, player : Player) async{
if player is HumanPlayer {
displayMessage = "Player \(player.id == .player1 ? "🟡 1" : "🔴 2") - \(player.name), it's your turn!"
}
else {
do{
_ = try await player.chooseMove(in: board, with: self.game.rules)
}
catch{
hasError = true
}
}
}
/// Mouvement choisi
func onMoveChosen(board : Board, move : Move, player : Player){
///Récupération du Meeple :
let movedPiece = board.grid[move.rowOrigin][move.columnOrigin]
let meeples = gameScene.pieces[move.owner]
let meeple = meeples?.first(where: {
$0.key == movedPiece.piece?.animal
})
meeple?.value.cellPosition = CGPoint(x: move.rowDestination, y: move.columnDestination)
}
///Mouvement invalide
func onInvalidMove(board : Board, move : Move, player : Player, result : Bool){
displayMessage = "⚠️⚠️⚠️⚠️ Invalid Move detected: \(move) by \(player.name) (\(player.id))"
if result { ///* invalidité terminante
if let piece = board.grid[move.rowDestination][move.columnDestination].piece{
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
}
///* invalidité non terminante
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){
/// Bruit d'un placement de pion ?
}
///Fin du jeu
func onGameOver(board : Board, result : Result, winner : Player?){
displayMessage = "Game Over!!!"
isGameOver = true ///Gestion par l'appelant
}
///*Meeples
///Abonnement aux mouvements des Meeples
func subscribesToMeeple(){
for meeple in gameScene.pieces[.player1]!{
meeple.value.observers.append(meepleMoved)
}
for meeple in gameScene.pieces[.player2]!{
meeple.value.observers.append(meepleMoved)
}
}
///*Mouvement d'un Meeple
func meepleMoved(spriteMeeple: SpriteMeeple, startX: Int, startY: Int, endX: Int, endY: Int) async{
let owner : Owner = game.board.grid[startX][startY].piece!.owner
let otherPlayer : Owner = self.game.rules.getNextPlayer()
let move: Move = Move(of: owner, fromRow: startX, andFromColumn: startY, toRow: endX, andToColumn: endY)
if(otherPlayer != owner){ ///Mauvais Joueur pour ce jeton
onInvalidMove(board: game.board, move: move, player: game.players[otherPlayer]!, result: false)
return
}
if let player: HumanPlayer = game.players[owner] as? HumanPlayer{
try! await player.chooseMove(move)
}
// else { ///! IMPOSSIBLE DE CAST LE JOUEUR HUMAIN ==> SIGABRT
// let player: HumanPlayer = game.players[.player1] as! HumanPlayer
// try! await player.chooseMove(move)
// }
}
}

@ -0,0 +1,53 @@
//
// ImagePicker.swift
// DouShouQiIOS
//
// Created by Pierre FERREIRA on 17/06/2024.
//
import SwiftUI
/// ImagePicker
/// Classe récupéré d'internet pour permettre de choisir une image dans la galerie
struct ImagePicker: UIViewControllerRepresentable {
@Environment(\.presentationMode) private var presentationMode
var sourceType: UIImagePickerController.SourceType = .photoLibrary
@Binding var selectedImage: UIImage
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = false
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.selectedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}

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

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

@ -9,7 +9,11 @@ import Foundation
import SpriteKit
import SwiftUI
class SpriteMeeple : SKNode{
/// SpriteMeeple
/// Cette classe est utilisée pour gérer les pièces des joueurs
class SpriteMeeple : SKNode, ObservableObject{
let imageNode : SKSpriteNode
let ellipseNode : SKShapeNode
@ -24,6 +28,11 @@ class SpriteMeeple : SKNode{
static let offset = CGPoint(x:-400, y:-300)
static let direction = CGVector(dx:100, dy:100);
//@Published var observers : [ any ObservableObject ] = []
@Published var observers : [ (SpriteMeeple, Int, Int, Int, Int) async ->() ] = []
public init(imageName imgN : String,size meepleSize : CGSize,color meepleColor : Color){
imageNode = SKSpriteNode(imageNamed: imgN)
imageNode.size = meepleSize;
@ -32,7 +41,6 @@ class SpriteMeeple : SKNode{
ellipseNode = SKShapeNode(ellipseOf: CGSize(width: 100, height: 100))
ellipseNode.fillColor = UIColor(meepleColor);
self.cellPosition = CGPoint(x: 0, y: 0)
@ -49,43 +57,63 @@ class SpriteMeeple : SKNode{
get {true}
}
///TouchesBegan
/// Cette fonction est appelée lorsque l'utilisateur commence à toucher l'écran
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
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?) {
//* 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
///Right
if (self.position.x < -400){
self.position.x = -400;
if (self.position.x < leftLimit){
self.position.x = leftLimit;
}
///Left
if (self.position.x > 400){
self.position.x = 400;
if (self.position.x > rightLimit){
self.position.x = rightLimit;
}
///Bottom
if (self.position.y < -300){
self.position.y = -300;
if (self.position.y < bottomLimit){
self.position.y = bottomLimit;
}
///Top
if (self.position.y > 300){
self.position.y = 300;
if (self.position.y > topLimit){
self.position.y = topLimit;
}
if (!Int(self.position.x).isMultiple(of: 100)){
let calcx = self.position.x/100.0;
self.position.x = (calcx.rounded(.toNearestOrAwayFromZero))*100;
if (!Int(self.position.x).isMultiple(of: gridSize)){
let calcx = self.position.x/gridSize;
self.position.x = (calcx.rounded(.toNearestOrAwayFromZero))*gridSize;
}
if (!Int(self.position.y).isMultiple(of: 100)){
let calcy = self.position.y/100.0;
self.position.y = (calcy.rounded(.toNearestOrAwayFromZero))*100;
if (!Int(self.position.y).isMultiple(of: gridSize)){
let calcy = self.position.y/gridSize;
self.position.y = (calcy.rounded(.toNearestOrAwayFromZero))*gridSize;
}
///Envoi aux observers d'une notif
///* Execution des Observeurs
Task {
for observer in observers {
await observer(self, Int(cellPosition.x), Int(cellPosition.y), Int(position.x), Int(position.y))
}
}
}
required init?(coder aDecoder: NSCoder) {

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

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

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

@ -8,6 +8,8 @@
import Foundation
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 {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)

@ -8,9 +8,21 @@
import Foundation
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 {
//var age : Int{get{return 0} set{age = newValue}}
var matches : [Match]{
return [Match(gameEndScreen: "DouShouQi", opponent: "BadBro", selfScore: 1, opScore: 1)]
}
/*public convenience init?(withId id : Owner, andName name : String, andAge _age : Int){
self.init(withName: name, andId: id)
//self.age = _age
}
*/
struct Data : Identifiable {
//var dataId: ObjectIdentifier = ObjectIdentifier()
@ -25,11 +37,13 @@ extension Player {
func update (from data : Data){
///Test de l'id
if (data.id == .noOne){
if (data.id == .noOne){ // gestion de l'id en fonction de l'utilisation.
print("Problème de mise a jour des données")
return
}
///Assignations
//self.id = data.id
//self.name = data.name
}
}

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

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

@ -0,0 +1,21 @@
//
// StubbedPlayers.swift
// DouShouQiIOS
//
// Created by Pierre FERREIRA on 12/06/2024.
//
import Foundation
import DouShouQiModel
// Liste de joueurs fictifs afin de tester l'application.
struct StubbedPlayers{
//(Joueurs populaires de Gun-gi)//
let meruem : PlayerVM = PlayerVM(with: Player(withName: "Meruemu", andId: .player1)!)
let kumogi : PlayerVM = PlayerVM(with: Player(withName: "Kumogi", andId: .player2)!)
func getStubbedPlayer() -> [PlayerVM] {
return [meruem, kumogi]
}
}

@ -8,9 +8,11 @@
import SwiftUI
import DouShouQiModel
/// EditModalView
/// Cette vue modale est utilisée pour éditer un joueur
struct EditModalView: View {
@Binding var playerVM : PlayerVM
var playerVM : PlayerVM
@Binding var isEdited : Bool
var name : String = ""
var body: some View {
@ -27,6 +29,6 @@ struct EditModalView: View {
struct EditModalView_Previews: PreviewProvider {
static var previews: some View {
EditModalView(playerVM: .constant(PlayerVM(with:Player(withName: "toto", andId: .player2)! )), isEdited: .constant(true))
EditModalView(playerVM: PlayerVM(with:Player(withName: "toto", andId: .player2)!), isEdited: .constant(true))
}
}

@ -7,32 +7,39 @@
import SwiftUI
import SpriteKit
import DouShouQiModel
/// Vue principale du jeu
struct GameView: View {
@Environment(\.colorScheme) var colorScheme
@State private var turnNumber = 1
@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 player2Pieces = 8
var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
let gridItems = Array(repeating: GridItem(.flexible()), count: 8)
///SK
var gs : GameScene = GameScene(size: CGSizeMake(940,740))
///VM
var gameVM : GameVM
init (gameVM : GameVM){
self.gameVM = gameVM
}
var body: some View {
ZStack{
Rectangle().fill(Color.bgColor).edgesIgnoringSafeArea(.all)
VStack {
VStack {
Text(statusMessage)
.font(.title)
Text(gameVM.displayMessage)
.font(.title3)
.padding(.top, 20)
HStack {
@ -46,26 +53,8 @@ struct GameView: View {
}
.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: gs)
SpriteView(scene: gameVM.gameScene)
.frame(width: 350, height: 275)
.padding(10)
.background(Color.primaryColor)
@ -110,6 +99,6 @@ struct GameView: View {
struct GameView_Previews: PreviewProvider {
static var previews: some View {
GameView()
GameView(gameVM: GameVM(withGame: try! Game(withRules: ClassicRules(), andPlayer1: Player(withName: "toto", andId: .player1)!, andPlayer2: Player(withName: "tata", andId: .player2)!), andScene: GameScene(size: CGSize(width: 120, height: 120))))
}
}

@ -7,7 +7,7 @@
import SwiftUI
/// Vue de l'historique d'un joueur
struct HistoryView: View {
var playerName: String
var matches: [Match] // Modèle des parties du joueur
@ -37,19 +37,19 @@ struct HistoryView: View {
VStack(alignment: .leading) {
Text(match.opponent)
.font(.headline)
Text("Score: \(match.score)")
Text("Score: \(match.selfScore) - \(match.opScore)")
.font(.subheadline)
}
.padding(.leading, 10)
Spacer()
Text(match.result)
.foregroundColor(match.result == "Victoire" ? .green : .red)
Text(match.selfScore > match.opScore ? "Victoire" : (match.selfScore == match.opScore ? "Nul" : "Défaite"))
.foregroundColor(match.selfScore > match.opScore ? .green : .red)
.padding(.trailing, 10)
}.listRowBackground(Color.yellow.opacity(0.1))
.padding()
.background(match.result == "Victoire" ? Color.green.opacity(0.3) : Color.red.opacity(0.3))
.background(match.selfScore > match.opScore ? Color.green.opacity(0.3) : Color.red.opacity(0.3))
.cornerRadius(10)
@ -66,16 +66,16 @@ struct Match: Identifiable {
var id = UUID()
var gameEndScreen: String
var opponent: String
var score: String
var result: String
var selfScore: Int
var opScore: Int
}
struct HistoryView_Previews: PreviewProvider {
static var previews: some View {
let sampleMatches = [
Match(gameEndScreen: "DouShouQi", opponent: "Opponent 1", score: "3-0", result: "Victoire"),
Match(gameEndScreen: "DouShouQi", opponent: "Opponent 2", score: "0-3", result: "Défaite"),
Match(gameEndScreen: "DouShouQi", opponent: "Opponent 3", score: "2-2", result: "Nul")
Match(gameEndScreen: "DouShouQi", opponent: "Opponent 1", selfScore: 3, opScore: 0),//result: "Victoire""
Match(gameEndScreen: "DouShouQi", opponent: "Opponent 2", selfScore: 0, opScore: 3),// result: "Défaite"),
Match(gameEndScreen: "DouShouQi", opponent: "Opponent 3", selfScore: 2, opScore: 2),// result: "Nul")
]
HistoryView(playerName: "bro", matches: sampleMatches)
}

@ -6,10 +6,18 @@
//
import SwiftUI
import DouShouQiModel
/// Vue principale de l'application
struct MainMenuView: View {
@Environment(\.colorScheme) var colorScheme
var gamevm : GameVM = GameVM(withGame: try! Game(withRules: ClassicRules(),
andPlayer1: Player(withName: "Meruemu", andId: .player1)!,
andPlayer2: Player(withName: "Kumogi", andId: .player2)!),
andScene: GameScene(size: CGSize(width: 940, height: 740)))
var body: some View {
NavigationStack {
@ -27,10 +35,11 @@ struct MainMenuView: View {
.bold()
.padding()
NavButton("Jouer Seul", destinationView: {GameView()})
//NavButton("Jouer Seul", destinationView: {PlayerSelect(versus: false)})
NavButton("Jouer Seul", destinationView: {GameView(gameVM: gamevm)})
.padding(.top, 10)
NavButton("Jouer en Multi", destinationView: {GameView()})
NavButton("Jouer en Multi", destinationView: {PlayerSelect(versus: true)})
NavButton("Leaderboard", destinationView: {PlayerListView()})

@ -6,38 +6,35 @@
//
import SwiftUI
import DouShouQiModel
/// Liste des joueurs
struct PlayerListView: View {
var playerStub : StubbedPlayers = StubbedPlayers()
var body: some View {
//NavigationStack {
ZStack {
Rectangle().fill(Color.bgColor).ignoresSafeArea()
VStack {
/*Text("Classement des joueurs")
.scaleEffect(1.75)*/
List {
NavigationLink{
PlayerView(name: "Billy", age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [
Match(gameEndScreen: "opponent1", opponent: "Opponent 1", score: "3-2", result: "Victoire"),
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", score: "1-3", result: "Défaite"),
Match(gameEndScreen: "opponent3", opponent: "Opponent 3", score: "2-2", result: "Nul")
]) //mettre player plus tard
List(playerStub.getStubbedPlayer()) { player in
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: player)
} label:{Text("pif")}
NavigationLink("Billy", value: "Billyname")
NavigationLink("Bob", value: "Bob")
NavigationLink("Bromingo", value: "Bromingo")
}
.navigationDestination(for: String.self) { name in
PlayerView(name: name, age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches: [
Match(gameEndScreen: "opponent1", opponent: "Opponent 1", score: "3-2", result: "Victoire"),
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", score: "1-3", result: "Défaite"),
Match(gameEndScreen: "opponent3", opponent: "Opponent 3", score: "2-2", result: "Nul")
]) //mettre player plus tard
}.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)!))
}
.navigationTitle("Leaderboard")
}
VStack {

@ -0,0 +1,95 @@
//
// PlayerSelect.swift
// DouShouQiIOS
//
// Created by Pierre FERREIRA on 17/06/2024.
//
import SwiftUI
/// PlayerSelect
/// Cette vue est utilisée pour sélectionner un joueur
struct PlayerSelect: View {
@State private var image = UIImage()
@State private var showSheet = false
var versus : Bool
var body: some View {
ZStack {
Rectangle().fill(Color.bgColor).ignoresSafeArea()
VStack {
VStack{
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(""))
}.foregroundStyle(.primary)
.padding(20)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.player1, lineWidth: 5)
)
.padding(40)
if (versus){
VStack{
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(""))
}.foregroundStyle(.primary)
.padding(20)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.player2, lineWidth: 5)
)
.padding(40)
}
}
}
}
}
struct PlayerSelect_Previews: PreviewProvider {
static var previews: some View {
PlayerSelect(versus: true)
}
}

@ -8,10 +8,8 @@
import SwiftUI
import DouShouQiModel
/// Affiche les statistiques d'un joueur
struct PlayerView: View {
//var Player : Player;
var name : String
var age: Int
var winLossRatio: Double
var gamesPlayed: Int
@ -20,12 +18,9 @@ struct PlayerView: View {
var histoMatches : [ Match ]
@State var isEdited : Bool = false
//@ObservedObject var player : PlayerVM //TODO
//@Published var data : Data
@ObservedObject var playerVm : PlayerVM //TODO
var body: some View {
//ZStack{
//Rectangle().fill(Color.bgColor).ignoresSafeArea()
VStack{
VStack{
Image("Blob")
@ -39,7 +34,7 @@ struct PlayerView: View {
Spacer()
VStack(alignment: .center){
Text("\(name) Sname")
Text("\(playerVm.player.name) Sname")
.font(.largeTitle)
.foregroundColor(Color.white)
@ -94,31 +89,27 @@ struct PlayerView: View {
Spacer()
NavButton("Historique", destinationView: {HistoryView(playerName: name, matches: histoMatches)})
NavButton("Historique", destinationView: {HistoryView(playerName: playerVm.player.name, matches: histoMatches)})
.padding(.top, 20)
}
.toolbar {
//ToolbarItemGroup(placement: .bottomBar) {
Button(action: {isEdited.toggle()}, label: {Text("Edit")})
//}
}
.sheet(isPresented: $isEdited, content: {
Text("CouCou")
EditModalView(playerVM: playerVm, isEdited: $isEdited)
})
//}//Zstack
}
}
struct PlayerView_Previews: PreviewProvider {
static var previews: some View {
/*var player1 = new Player(Player(withName: "Meruemu", andSName: "Roi", andId: 2, andAge: 1)*/
let stubedMatches = [
Match(gameEndScreen: "opponent1", opponent: "Opponent 1", score: "3-2", result: "Victoire"),
Match(gameEndScreen: "opponent2", opponent: "Opponent 2", score: "1-3", result: "Défaite"),
Match(gameEndScreen: "opponent3", opponent: "Opponent 3", score: "2-2", result: "Nul")
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)
]
PlayerView(name: "bro", age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches : stubedMatches)
PlayerView(age: 18, winLossRatio: 1.0, gamesPlayed: 10, wins: 5, losses: 5, histoMatches : stubedMatches, playerVm: PlayerVM(with: Player(withName: "toto", andId: .player2)!))
}
}

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

@ -1,2 +1,64 @@
# 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>

@ -17,7 +17,7 @@
7B6A02982C18930400877E3F /* SwiftARKitUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6A02972C18930400877E3F /* SwiftARKitUITestsLaunchTests.swift */; };
7B6A02A82C18966400877E3F /* MySwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6A02A72C18966400877E3F /* MySwiftUIView.swift */; };
7B6A02AC2C18A1B000877E3F /* MyARViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6A02AB2C18A1B000877E3F /* MyARViewRepresentable.swift */; };
7BB48C802C256C370090AA60 /* objects in Resources */ = {isa = PBXBuildFile; fileRef = 7BB48C7F2C256C370090AA60 /* objects */; };
F0A2577F2C297CDF006C9826 /* board.usdz in Resources */ = {isa = PBXBuildFile; fileRef = F0A2577E2C297CDF006C9826 /* board.usdz */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -52,7 +52,7 @@
7B6A02A72C18966400877E3F /* MySwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySwiftUIView.swift; sourceTree = "<group>"; };
7B6A02A92C189ED700877E3F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
7B6A02AB2C18A1B000877E3F /* MyARViewRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyARViewRepresentable.swift; sourceTree = "<group>"; };
7BB48C7F2C256C370090AA60 /* objects */ = {isa = PBXFileReference; lastKnownFileType = folder; name = objects; path = ../../../SwiftUI_ARKit_2024/objects; sourceTree = "<group>"; };
F0A2577E2C297CDF006C9826 /* board.usdz */ = {isa = PBXFileReference; lastKnownFileType = file.usdz; name = board.usdz; path = objects/board.usdz; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -103,7 +103,7 @@
7B6A02772C18930200877E3F /* SwiftARKit */ = {
isa = PBXGroup;
children = (
7BB48C7F2C256C370090AA60 /* objects */,
F0A2577E2C297CDF006C9826 /* board.usdz */,
7B6A02AA2C18A19C00877E3F /* Struct */,
7B6A02A92C189ED700877E3F /* Info.plist */,
7B6A02A62C18964F00877E3F /* View */,
@ -261,7 +261,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7BB48C802C256C370090AA60 /* objects in Resources */,
F0A2577F2C297CDF006C9826 /* board.usdz in Resources */,
7B6A02822C18930400877E3F /* Preview Assets.xcassets in Resources */,
7B6A027F2C18930400877E3F /* Assets.xcassets in Resources */,
);
@ -448,9 +448,12 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"SwiftARKit/Preview Content\"";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 67U885533Q;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SwiftARKit/Info.plist;
@ -466,8 +469,10 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = PierreFerreira.SwiftARKit;
PRODUCT_BUNDLE_IDENTIFIER = fr.uca.iut.SwiftARKit;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = tpiOS_2024_06_21_off;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -479,9 +484,12 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"SwiftARKit/Preview Content\"";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 67U885533Q;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SwiftARKit/Info.plist;
@ -497,8 +505,10 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = PierreFerreira.SwiftARKit;
PRODUCT_BUNDLE_IDENTIFIER = fr.uca.iut.SwiftARKit;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = tpiOS_2024_06_21_off;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B6A02742C18930200877E3F"
BuildableName = "SwiftARKit.app"
BlueprintName = "SwiftARKit"
ReferencedContainer = "container:SwiftARKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B6A02862C18930400877E3F"
BuildableName = "SwiftARKitTests.xctest"
BlueprintName = "SwiftARKitTests"
ReferencedContainer = "container:SwiftARKit.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B6A02902C18930400877E3F"
BuildableName = "SwiftARKitUITests.xctest"
BlueprintName = "SwiftARKitUITests"
ReferencedContainer = "container:SwiftARKit.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B6A02742C18930200877E3F"
BuildableName = "SwiftARKit.app"
BlueprintName = "SwiftARKit"
ReferencedContainer = "container:SwiftARKit.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B6A02742C18930200877E3F"
BuildableName = "SwiftARKit.app"
BlueprintName = "SwiftARKit"
ReferencedContainer = "container:SwiftARKit.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

@ -12,9 +12,11 @@ import RealityKit
struct MyARViewRepresentable : UIViewRepresentable{
public func makeUIView(context: Context) -> MyARView {
// let arView = ARView(frame: .zero)
// let mesh = MeshResource.generateBox(width: 150, height: 500, depth: 50)
//
// let arView = MyARView(frame: .zero)
// let mesh = MeshResource.generateBox(size: 0.1, cornerRadius: 0.005)
//
// //let mesh = MeshResource.generateBox(width: 150, height: 500, depth: 50)
// let material = SimpleMaterial(color: .red, isMetallic: true)
// let model = ModelEntity(mesh: mesh, materials: [material]) //, materials: material
// model.transform.translation.y = 0.05
@ -23,7 +25,7 @@ struct MyARViewRepresentable : UIViewRepresentable{
//
// arView.scene.anchors.append(anchor)
// return arView
//
return MyARView()
}

@ -41,8 +41,8 @@ class MyARView : ARView{
}
func addBoardToTheFloor() {
let configuration = ARWorldTrackingConfiguration()
session.run(configuration)
//let configuration = ARWorldTrackingConfiguration()
//session.run(configuration)
let anchor = AnchorEntity(.plane(.horizontal, classification: .any, minimumBounds: SIMD2<Float>(0.2, 0.2)))
scene.addAnchor(anchor)
@ -54,8 +54,8 @@ class MyARView : ARView{
}
func addMeepleToBoard(){ //boardAnchor : AnchorEntity
let configuration = ARWorldTrackingConfiguration()
session.run(configuration)
//let configuration = ARWorldTrackingConfiguration()
//session.run(configuration)
let objectsName = [ "rat", "cat", "dog", "wolf", "leopard", "tiger", "lion", "elephant"]
var iterator : Int = 0

Loading…
Cancel
Save