How to make an escape game using swiftUI part 3
Tags: swift, swiftui, ios, gamedev
Published: 2021-04-27 02:02:54

## About zoom In

The next task is the zoomIn zoomOut technique. This is a method where you can click on a location on the screen. The screen will zoom in on that location to get more information about the room. For example, you can look under a desk to see if a key has left on carpet, look up something on a computer screen, or talk to a person.
It may seem like a simple function, but it is very important. Although it seems simple, it is a bit tricky to implement correctly. 
This is in reference to the method of placing items. 
I call this invisible button a void button. When you press this button, the screen will switch to a different dimension from the east, west, north, and south as before. 
So after zooming in, we need to disable the left and right window buttons once they were always visible.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/876v4cowcbykrt4xt25v.png)

The canvasUI is the file that draws the blueprint for all the game's screen transitions. It is the trunk that binds a number of windows together. 
the zoomChainIndex can disable the window buttons. 
As I will explain in detail later, we will create a separate Index model for each of these flags, and write a large number of if statements for each flag value in the center of the canvasUI.

```swift
CanvasUI.swift

import SwiftUI

struct CanvasUI: View {
    
    @ObservedObject var itemData = ItemObject.shared
    @ObservedObject var cycle = CycleChain.shared
    @ObservedObject var scriptData = ScriptObject.shared
    @ObservedObject var windowObject = WindowObject.shared
    @ObservedObject var selectTabView = WindowSelect()
    
    var body: some View {
        ZStack {
            // move to window to window
            if cycle.windowChainIndex == 0 {
                // ZoomIn and ZoomOut
                if cycle.zoomChainIndex == 0 {
                    WindowButton(WWindow: AnyView(W_Window()), NWindow: AnyView(N_Window()), EWindow:  AnyView(E_Window()), SWindow: AnyView(S_Window()))
                } // disable WindowButton
                if cycle.zoomChainIndex == 1 {
                    if windowObject.window.isZoom == true {
                        N_Zoom_Window()
                    }
                    if windowObject.window1.isZoom == true {
                        E_Zoom_Window()
                    }
                }
            }
            // Second Floor
            if cycle.windowChainIndex == 1 {
                
            }
            // to one way move
            if cycle.windowChainIndex == 2 {
                // scene
            }
            VStack {
                Spacer()
                ItemDock()
            }
        }
    }


```

It's a bit noisy, but it can't be helped. Also, there is a bug that when you come back from the default zoomOut, you can only return from a certain direction, northward, no matter which direction you came back from, but again, I don't care. If you care about that, you won't be able to play the game for any length of time.

## About subtitles

We are slowly finishing up. Since we want to add a textual way of expression in addition to the image-based way, we decided to reserve a place for subtitles to be displayed. So, we will create a script dock above the item dock where the subtitles will be displayed. Eventually, we want to be able to create a text game here. By the way, let's take a look at the bottom half of the screen in the Tools tab. You will notice that the item dock is not exactly glued to the script dock. That's a small detail.

```swift
import SwiftUI

struct CanvasUI: View {
    
    @ObservedObject var itemData = ItemObject.shared
    @ObservedObject var cycle = CycleChain.shared
    @ObservedObject var scriptData = ScriptObject.shared
    @ObservedObject var windowObject = WindowObject.shared
    @ObservedObject var selectTabView = WindowSelect()
    
    var body: some View {
        ZStack {
            // move to window to window
            if cycle.windowChainIndex == 0 {
                // ZoomIn and ZoomOut
                if cycle.zoomChainIndex == 0 {
                    WindowButton(WWindow: AnyView(W_Window()), NWindow: AnyView(N_Window()), EWindow:  AnyView(E_Window()), SWindow: AnyView(S_Window()))
                } // disable WindowButton
                if cycle.zoomChainIndex == 1 {
                    if windowObject.window.isZoom == true {
                        N_Zoom_Window()
                    }
                    if windowObject.window1.isZoom == true {
                        E_Zoom_Window()
                    }
                }
            }
            // Second Floor
            if cycle.windowChainIndex == 1 {
                
            }
            // to one way move
            if cycle.windowChainIndex == 2 {
                // scene
            }
            // add to spacing
            VStack(spacing: 0) {
                Spacer()
                ItemDock()
            }
        }
    }


```

This is due to Apple's default settings. It is to prevent people from creating flat design apps so that they won't mistake it for windows phone. If you want to make this snug and close, you can write the following. Now it is stored properly.
If anyone cares about this, they are either a Microsoft employee or a Nokia employee.

```swift
import Foundation


struct ScriptModel : Identifiable {
    var id : Int
    var text : String
    var isAppear : Bool
}

class ScriptObject : ObservableObject {
    static let shared = ScriptObject()
    private init(){}
    
    @Published var script = ScriptModel(id: 0, text: "This is my 0 script.", isAppear: false)
    @Published var script1 = ScriptModel(id: 1, text: "This is my 1 script.", isAppear: false)
    @Published var script2 = ScriptModel(id: 2, text: "This is my 2 script.", isAppear: false)
    @Published var script3 = ScriptModel(id: 3, text: "This is my 3 script.", isAppear: false)
    @Published var script4 = ScriptModel(id: 4, text: "This is my 4 script.", isAppear: false)
    @Published var script5 = ScriptModel(id: 5, text: "This is my 5 script.", isAppear: false)
    @Published var script6 = ScriptModel(id: 6, text: "This is my 6 script.", isAppear: false)
    @Published var script7 = ScriptModel(id: 7, text: "This is my 7 script.", isAppear: false)
    @Published var script8 = ScriptModel(id: 8, text: "This is my 8 script.", isAppear: false)
    @Published var script9 = ScriptModel(id: 9, text: "This is my 9 script.", isAppear: false)
    @Published var script10 = ScriptModel(id: 10, text: "This is my 10 script.", isAppear: false)
    @Published var script11 = ScriptModel(id: 11, text: "This is my 11 script.", isAppear: false)
    @Published var script12 = ScriptModel(id: 12, text: "This is my 12 script.", isAppear: false)
    @Published var script13 = ScriptModel(id: 13, text: "This is my 13 script.", isAppear: false)
}

```
```swift

import SwiftUI



struct ScriptDock: View {
    @State var script : ScriptModel
    var body: some View {
        ZStack{
            Rectangle()
                .fill(Color.green.opacity(0.3))
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 50, maxHeight: 70)
            ScriptText(script: $script)
        }
    }
}


// This view is for script book
struct ScriptText : View {
    @State var flg = true
    @Binding var script : ScriptModel
    @ObservedObject var cycle = CycleChain.shared
    
    var body: some View {
        if self.flg {
            Text(script.text)
                .onTapGesture {
//                    cycle.scriptChainIndex = script.id
                    self.flg.toggle()
            }
        } else {
            
        }
    }
}




```

Subtitles are also modeled and managed separately. 
This is because subtitles often need to be modified or reordered afterwards. 
If you've ever created subtitles for a video, you'll understand this well. For the nature of subtitles, you will notice that there is a pattern to how this appears. 
As shown in the figure below, a script appears in the script dock right after an event, and it can serve as a description of the situation, a line from a character, or a heavenly voice (hint) to reduce the difficulty of the game. 
Each script should be tied to a script index number for each event, and either disappear after a certain amount of time or can be removed by tapping the script itself. 
There are two types of scripts: those that work by themselves and those that display multiple scripts in succession. 
In the case of multiple scripts, you can link the index number to the script number and have it increment by itself. In this case, it is essential to set the default number, i.e. 0, when the long script is finished.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2d1qxnswr95l3qgmxuu.png)

## About chainIndex

The same floor with only four views (east, west, north, south, and west) seemed boring to me. So, let's make it so that the user can go up to the upper floor if certain conditions are met. In this case, we need to create a new index that we used for zooming. There are two main types of indexes: multiple and one-way. The first is the window index. This is the index that connects the floor to the floor above. It looks like the figure below.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccck9yc1dndu3oz2nggu.png)


This is a multiple. Naturally, you can go back and forth between floors, or you can make it one-way. It can also be used as a time trip. from 1980's to 2020's : from Reagan to Trump. The zoomChianIndex we mentioned earlier is also a multiple, because it goes back and forth between In and Out.

Only the last one is one-way. It is a keyChainIndex.
What is the purpose of getting an item in the first place? It is to solve the mystery in front of you. Look at the diagram below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zjq251uafizjkfe072pp.png)

This is an illustration of how to use Key1 to open a safe, which contains a new key, Key2, to open another safe. It is assumed that the key used cannot be used to open any more safes. Once you have solved the problem, it will make it impossible to go back on the stage. If you don't do this, there may be glitches that the game creator didn't anticipate. This is the most important method throughout the game to guide the player through the story. Although it is important, it is merely an Int-type variable that is allowed to be incremented only.


```swift

import Foundation


class CycleChain : ObservableObject {
    static let shared = CycleChain()
    private init(){}
    
    @Published var windowChainIndex : Int = 0
    @Published var zoomChainIndex : Int = 0
    @Published var animeChainIndex : Int = 0
    // One Way
    
    @Published var keyChainIndex : Int = 0
    @Published var scriptChainIndex : Int = 0
}


```