From 6129a2bf00e77afd4c2c70b26e957e0c05e8a308 Mon Sep 17 00:00:00 2001
From: tobias <thinkdifferent055@gmail.com>
Date: Sat, 15 Jul 2017 22:05:06 +0200
Subject: [PATCH] Add minimap

---
 Pacman3D-iOS.xcodeproj/project.pbxproj |  20 +++++--
 Pacman3D-iOS/GameViewController.swift  |  69 +++++++++++++++++++++++--
 Pacman3D-iOS/Monster.swift             |  18 ++++---
 Pacman3D-iOS/MotionInput.swift         |   2 +-
 Pacman3D-iOS/pause.png                 | Bin 0 -> 7188 bytes
 5 files changed, 93 insertions(+), 16 deletions(-)
 create mode 100644 Pacman3D-iOS/pause.png

diff --git a/Pacman3D-iOS.xcodeproj/project.pbxproj b/Pacman3D-iOS.xcodeproj/project.pbxproj
index eac038b..41a9bbe 100644
--- a/Pacman3D-iOS.xcodeproj/project.pbxproj
+++ b/Pacman3D-iOS.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		F6090F531F1AA9270003F688 /* pause.png in Resources */ = {isa = PBXBuildFile; fileRef = F6090F521F1AA9270003F688 /* pause.png */; };
 		F6621DF41F12B09800F9E486 /* TouchInputHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6621DF31F12B09800F9E486 /* TouchInputHandler.swift */; };
 		F6621DF61F12B82200F9E486 /* MotionInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6621DF51F12B82200F9E486 /* MotionInput.swift */; };
 		F6BEB8071EF7F99B00EDAA66 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6BEB8061EF7F99B00EDAA66 /* AppDelegate.swift */; };
@@ -47,6 +48,7 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+		F6090F521F1AA9270003F688 /* pause.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pause.png; sourceTree = "<group>"; };
 		F6621DF31F12B09800F9E486 /* TouchInputHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchInputHandler.swift; sourceTree = "<group>"; };
 		F6621DF51F12B82200F9E486 /* MotionInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionInput.swift; sourceTree = "<group>"; };
 		F6BEB8031EF7F99B00EDAA66 /* Pacman3D-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Pacman3D-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -100,6 +102,18 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		F6090F541F1AA92D0003F688 /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				F6FEE71A1EF8135200C1DE35 /* level.plv */,
+				F6E39CE11F1A56E500A3E7D1 /* wall.jpg */,
+				F6E39CE31F1A583B00A3E7D1 /* floor.jpg */,
+				F6090F521F1AA9270003F688 /* pause.png */,
+				F6E39CE91F1A7C2C00A3E7D1 /* background.jpg */,
+			);
+			name = Resources;
+			sourceTree = "<group>";
+		};
 		F6BEB7FA1EF7F99B00EDAA66 = {
 			isa = PBXGroup;
 			children = (
@@ -135,10 +149,7 @@
 				F6BEB80C1EF7F99B00EDAA66 /* Main.storyboard */,
 				F6BEB8111EF7F99B00EDAA66 /* LaunchScreen.storyboard */,
 				F6BEB8141EF7F99B00EDAA66 /* Info.plist */,
-				F6FEE71A1EF8135200C1DE35 /* level.plv */,
-				F6E39CE11F1A56E500A3E7D1 /* wall.jpg */,
-				F6E39CE31F1A583B00A3E7D1 /* floor.jpg */,
-				F6E39CE91F1A7C2C00A3E7D1 /* background.jpg */,
+				F6090F541F1AA92D0003F688 /* Resources */,
 			);
 			path = "Pacman3D-iOS";
 			sourceTree = "<group>";
@@ -295,6 +306,7 @@
 				F6BEB8131EF7F99B00EDAA66 /* LaunchScreen.storyboard in Resources */,
 				F6BEB8101EF7F99B00EDAA66 /* Assets.xcassets in Resources */,
 				F6BEB80E1EF7F99B00EDAA66 /* Main.storyboard in Resources */,
+				F6090F531F1AA9270003F688 /* pause.png in Resources */,
 				F6FEE71B1EF8135200C1DE35 /* level.plv in Resources */,
 				F6E39CEA1F1A7C2C00A3E7D1 /* background.jpg in Resources */,
 				F6E39CE21F1A56E500A3E7D1 /* wall.jpg in Resources */,
diff --git a/Pacman3D-iOS/GameViewController.swift b/Pacman3D-iOS/GameViewController.swift
index 502f403..dc91c2a 100644
--- a/Pacman3D-iOS/GameViewController.swift
+++ b/Pacman3D-iOS/GameViewController.swift
@@ -28,14 +28,17 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
     var lifeLabel: SKLabelNode!
     var backButton: SKSpriteNode!
     
+    var minimap: [[SKSpriteNode]] = []
+    
     var restartButton: SKLabelNode!
     
     // MARK: - Game Objects
     
     let monsterCount = 6
     
+    var level: Level!
     var player: Player!
-    var monsters: [Monster] = []
+    var monsters: [Monster]!
 
     // MARK: - Input Handler
     private var touchInputHandler: TouchInputHandler!
@@ -44,7 +47,9 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
     override func viewDidLoad() {
         super.viewDidLoad()
         
-        let level = Level(named: "level")
+        monsters = []
+        
+        level = Level(named: "level")
         
         // create a new scene
         scene = SCNScene(named: "art.scnassets/ship.scn")!
@@ -88,6 +93,10 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
         }
         
         // add a tap gesture recognizer
+        if touchInputHandler != nil {
+            scnView.removeGestureRecognizer(touchInputHandler)
+        }
+        
         touchInputHandler = TouchInputHandler(target: self, action: #selector(handleTap(_:)))
         touchInputHandler.scene = (self.view as? SCNView)?.overlaySKScene
         touchInputHandler.gameController = self
@@ -138,7 +147,6 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
         if contact.nodeA.name ?? "" == "Monster" && contact.nodeB.name ?? "" == "Monster" {
             return
         }
-        let pacman = self.scene.rootNode.childNode(withName: "Pacman", recursively: true)!
         
         if contact.nodeA.name ?? "" == "Monster" && contact.nodeB.name ?? "" == "Pacman" ||
             contact.nodeB.name ?? "" == "Monster" && contact.nodeA.name ?? "" == "Pacman" {
@@ -150,6 +158,15 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
             let monster = contact.nodeA.name ?? "" == "Monster" ? contact.nodeA : contact.nodeB
             monster.removeFromParentNode()
             
+            for m in monsters {
+                if let n =  m.node {
+                    if n == monster {
+                        monsters.remove(object: m)
+                        break
+                    }
+                }
+            }
+            
             if player.life <= 0 {
                 self.scene.isPaused = true
                 restartButton.isHidden = false
@@ -181,6 +198,8 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
                 }
                 lastTime = time
             }
+            
+            updateMinimap()
         }
     }
     
@@ -199,7 +218,8 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
         lifeLabel.position = CGPoint(x: self.view.frame.width - 50, y: 5)
         scene.addChild(lifeLabel)
         
-        backButton = SKSpriteNode(color: UIColor.purple, size: CGSize(width: 40, height: 40))
+        backButton = SKSpriteNode(imageNamed: "pause.png")
+        backButton.size = CGSize(width: 60, height: 60)
         backButton.position = CGPoint(x: backButton.size.width / 2, y: backButton.size.height / 2)
         backButton.name = "Back"
         scene.addChild(backButton)
@@ -213,9 +233,50 @@ class GameViewController: UIViewController, SKSceneDelegate, SCNPhysicsContactDe
         restartButton.name = "Restart"
         scene.addChild(restartButton)
         
+        let frame = self.view.frame
+        
+        minimap = []
+        for (x, _) in level.data.enumerated() {
+            var nodes: [SKSpriteNode] = []
+            for (z, _) in level.data[x].enumerated() {
+                let node = SKSpriteNode(color: UIColor.magenta, size: CGSize(width: 5, height: 5))
+                let posX = frame.width - CGFloat(level.data[x].count * 5) + CGFloat(z * 5) + 2.5
+                let posZ = frame.height - CGFloat(level.data.count * 5) + CGFloat(x * 5) + 2.5
+                node.position = CGPoint(x: posX, y: posZ)
+                nodes.append(node)
+                scene.addChild(node)
+            }
+            minimap.append(nodes)
+        }
+        
         return scene
     }
     
+    func updateMinimap() {
+        for (x, _) in level.data.enumerated() {
+            for (z, data) in level.data[x].enumerated() {
+                let node = minimap[x][z]
+                switch data {
+                case .blank:
+                    node.color = UIColor.black.withAlphaComponent(0.6)
+                case .point:
+                    node.color = UIColor.blue.withAlphaComponent(0.6)
+                case .wall:
+                    node.color = UIColor.gray.withAlphaComponent(0.6)
+                }
+            }
+        }
+
+        for monster in monsters {
+            let x: Int = Int(monster.position.x / 5)
+            let z: Int = Int(monster.position.z / 5)
+            minimap[x][z].color = UIColor.red.withAlphaComponent(0.6)
+        }
+        
+        let x: Int = Int(player.position.x / 5)
+        let z: Int = Int(player.position.z / 5)
+        minimap[x][z].color = UIColor.yellow.withAlphaComponent(0.6)
+    }
     
     func restart() {
         viewDidLoad()
diff --git a/Pacman3D-iOS/Monster.swift b/Pacman3D-iOS/Monster.swift
index 30a1113..039c011 100644
--- a/Pacman3D-iOS/Monster.swift
+++ b/Pacman3D-iOS/Monster.swift
@@ -9,14 +9,14 @@
 import Foundation
 import SceneKit
 
-class Monster {
+class Monster: Equatable {
     
-    private var position: SCNVector3
+    private (set) var position: SCNVector3
     private var direction: Direction = .north
     
     private let level: Level
     private let scene: SCNScene
-    private var node: SCNNode?
+    private (set) var node: SCNNode?
     
     init(position: SCNVector3, level: Level, scene: SCNScene) {
         self.position = position
@@ -67,13 +67,13 @@ class Monster {
             }
             
             if direction == .north {
-                position = SCNVector3(x: node.position.x + 0.15, y: node.position.y, z: Float(z * 5))
+                position = SCNVector3(x: node.position.x + 0.1, y: node.position.y, z: Float(z * 5))
             } else if direction == .east {
-                position = SCNVector3(x: Float(x * 5), y: node.position.y, z: node.position.z + 0.15)
+                position = SCNVector3(x: Float(x * 5), y: node.position.y, z: node.position.z + 0.1)
             } else if direction == .south {
-                position = SCNVector3(x: node.position.x - 0.15, y: node.position.y, z: Float(z * 5))
+                position = SCNVector3(x: node.position.x - 0.1, y: node.position.y, z: Float(z * 5))
             } else if direction == .west {
-                position = SCNVector3(x: Float(x * 5), y: node.position.y, z: node.position.z - 0.15)
+                position = SCNVector3(x: Float(x * 5), y: node.position.y, z: node.position.z - 0.1)
             }
             node.position = position
         }
@@ -102,4 +102,8 @@ class Monster {
         
         return directions
     }
+    
+    public static func ==(lhs: Monster, rhs: Monster) -> Bool {
+        return lhs.node == rhs.node
+    }
 }
diff --git a/Pacman3D-iOS/MotionInput.swift b/Pacman3D-iOS/MotionInput.swift
index 822bdaf..bd56d8f 100644
--- a/Pacman3D-iOS/MotionInput.swift
+++ b/Pacman3D-iOS/MotionInput.swift
@@ -26,7 +26,7 @@ class MotionInput {
         let direction: Float = rotate < 0 ? 1.0 : -1.0
         if abs(rotate) > 0.3 {
             if !self.isRotating {
-                let action = SCNAction.rotateBy(x: 0, y: CGFloat(direction * Float.pi * 0.5), z: 0, duration: 0.25)
+                let action = SCNAction.rotateBy(x: 0, y: CGFloat(direction * Float.pi * 0.5), z: 0, duration: 0.15)
                 pacman.runAction(action, completionHandler: nil)
                 self.isRotating = true
                 
diff --git a/Pacman3D-iOS/pause.png b/Pacman3D-iOS/pause.png
new file mode 100644
index 0000000000000000000000000000000000000000..28b37804f7fefa1fdd7688fd35580a5a6967471f
GIT binary patch
literal 7188
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

literal 0
HcmV?d00001

-- 
GitLab