RMIT University

RMIT Casino Game (Part 1) Step by Step Guide

This guide provides a detailed walkthrough for building the RMIT Casino game from scratch. You'll learn to set up a SwiftUI project, manage assets, build a user interface with ZStack and VStack, create reusable ViewModifiers, and implement game logic with @State.

🛠️ 1. Project Setup & File Organization

Step 1: Create a New Xcode Project

Instruction: Launch Xcode. On the welcome screen, click "Create a new Xcode project". Select the iOS platform and the App template. Click Next.

On the next screen, enter the following options:

  • Product Name: RMITCasino
  • Interface: SwiftUI
  • Language: Swift
Click Next, choose a location to save your project, and click Create.

Explanation: This process creates a new, blank SwiftUI project. Xcode generates the essential files, including ContentView.swift, which is where we will start building our user interface.

Create New Xcode Project

Step 2: Prepare Assets (Images & Colors)

Instruction: First, you will need to gather the image assets for the game. For this tutorial, all required images can be found in the Asset Gallery and also on Canvas. You will need to find the following files:

  • Logos: rmit-casino-welcome-logo
  • UI Elements: reel, spin, casino-chips
  • Slot Icons: apple, bar, cherry, clover, diamond, grape, heart, horseshoe, lemon, melon, money, orange
Once you have located the images, add them to your Xcode project:
  1. Open Assets.xcassets in the Xcode project navigator.
  2. Create two new folders by right-clicking in the assets list and selecting "New Folder". Name them Graphics and RMIT-Logos.
  3. Drag the rmit-casino-welcome-logo image into the RMIT-Logos folder.
  4. Drag all other images into the Graphics folder.
Next, let's add our custom colors:
  1. In Assets.xcassets, create another new folder named Colors.
  2. Inside the Colors folder, right-click and select "New Color Set". Create six color sets with the following names:
    • ColorBlackTransparentRMIT
    • ColorBlueRMIT
    • ColorPurpleRMIT
    • ColorRedRMIT
    • ColorWhiteRMIT
    • ColorYellowRMIT
  3. For each color set, select it, open the Attributes Inspector on the right, and set the "Appearances" to "Any, Dark".
  4. Click on the color swatch and set the RGB values as follows:
    • ColorBlackTransparentRMIT: Red: 0, Green: 0, Blue: 0, Opacity: 35%
    • ColorBlueRMIT: Red: 0, Green: 0, Blue: 84
    • ColorPurpleRMIT: Red: 184, Green: 24, Blue: 121
    • ColorRedRMIT: Red: 230, Green: 30, Blue: 42
    • ColorWhiteRMIT: Red: 227, Green: 229, Blue: 224
    • ColorYellowRMIT: Red: 250, Green: 200, Blue: 0

You can also use a color palette tool like Paletton to help you choose a set of colors that go well together. When selecting colors, consider using color theory to create a harmonious scheme based on your primary colors (e.g., RMIT Blue and Red):

  • Adjacent/Analogous Colors: These are colors next to each other on the color wheel. They create a serene and comfortable design. For example, with RMIT Red, you could use adjacent oranges and purples.
  • Triad: This scheme uses three colors that are evenly spaced around the color wheel, forming a triangle. It offers high contrast while retaining harmony. For example, a triad with RMIT Red might include a yellow and a blue.
  • Tetrad (Rectangular): This scheme uses four colors arranged into two complementary pairs. It offers plenty of possibilities for variation but can be difficult to balance. For example, with RMIT Red and Blue, you could find their complementary colors (a green and an orange) to form a tetrad.

Explanation: The Asset Catalog (Assets.xcassets) is where we manage all our visual assets like images and colors. By defining custom colors here, we can easily reference them throughout our app by name (e.g., Color("ColorRedRMIT")), making our code cleaner and easier to maintain. Organizing images into folders keeps the project tidy.

Prepare Assets

🎨 2. Building the User Interface

Step 3: Add the Background Color

Instruction: Open ContentView.swift. Replace the default content inside the body with a ZStack containing a LinearGradient.

Explanation: A ZStack is a container that layers its child views on top of each other. We'll use it to place a gradient background behind all other UI elements. LinearGradient creates a smooth transition between multiple colors. In this case, it blends from ColorRedRMIT at the top (.top) to ColorPurpleRMIT at the bottom (.bottom), creating a vibrant background. The .edgesIgnoringSafeArea(.all) modifier ensures the gradient extends to the very edges of the screen, including the notch and home indicator areas.

// ContentView.swift
struct ContentView: View {
    var body: some View {
        ZStack {
            // MARK: - Background
            LinearGradient(gradient: Gradient(colors: [Color("ColorRedRMIT"), Color("ColorPurpleRMIT")]), startPoint: .top, endPoint: .bottom)
                .edgesIgnoringSafeArea(.all)
        }
    }
}

#Preview {
    ContentView()
}
Background Gradient

Step 4: Add LogoView

Instruction:

  1. Create a new file for the logo. Right-click on the RMITCasino folder in the Project Navigator, select "New File...", choose "SwiftUI View", and name it LogoView.swift.
  2. In LogoView.swift, add the code to display the logo image with some basic sizing.
  3. Go back to ContentView.swift and add a VStack inside the ZStack. Inside the VStack, add your new LogoView.

Explanation: Breaking down the UI into smaller, reusable views like LogoView is a core concept in SwiftUI. It keeps our ContentView clean and makes the LogoView easy to reuse or modify later.

// LogoView.swift
import SwiftUI

struct LogoView: View {
    let logoFileName: String

    var body: some View {
        Image(logoFileName)
            .resizable()
            .scaledToFit()
            .frame(minWidth: 250, idealWidth: 280, maxWidth: 320, alignment: .center)
    }
}

#Preview {
    LogoView(logoFileName: "rmit-casino-welcome-logo")
}
// ContentView.swift
struct ContentView: View {
    var body: some View {
        ZStack {
            // MARK: - Background
            LinearGradient(gradient: Gradient(colors: [Color("ColorRedRMIT"), Color("ColorPurpleRMIT")]), startPoint: .top, endPoint: .bottom)
                .edgesIgnoringSafeArea(.all)
            
            VStack {
                // MARK: - Logo
                LogoView(logoFileName: "rmit-casino-welcome-logo")
                
                Spacer() // Add a spacer to push content to the top and bottom
            }
            .padding()
        }
    }
}

#Preview {
    ContentView()
}
Add LogoView

✨ 3. Creating Reusable Modifiers

Step 5: Create the ShadowModifier

Instruction:

  1. Create another new file. Right-click on the RMITCasino folder, select "New File...", choose "Swift File", and name it Modifiers.swift.
  2. Inside Modifiers.swift, add the code for ShadowModifier.

Explanation: A ViewModifier is a reusable set of modifiers. We will apply a specific shadow style to many UI elements. Creating a custom ShadowModifier saves us from repeating the same .shadow(...) code everywhere and ensures a consistent look.

// Modifiers.swift
import Foundation
import SwiftUI

struct ShadowModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .shadow(color: Color("ColorBlackTransparentRMIT"), radius: 10)
    }
}
ShadowModifier Code

Step 6: Apply ShadowModifier to LogoView

Instruction: Open LogoView.swift and apply your new ShadowModifier to the Image.

Explanation: We apply a custom modifier using the .modifier() syntax. This attaches the shadow effect defined in ShadowModifier to our logo.

// LogoView.swift
struct LogoView: View {
    let logoFileName: String

    var body: some View {
        Image(logoFileName)
            .resizable()
            .scaledToFit()
            .frame(minWidth: 250, idealWidth: 280, maxWidth: 320, alignment: .center)
            .modifier(ShadowModifier()) // <-- ADDED: Applies our custom shadow style
    }
}
Apply ShadowModifier

🏗️ 4. Building the Static UI

Step 7: Add Score Boards

Instruction: In ContentView.swift, below the LogoView, add an HStack to create the layout for the player's money and high score boards. Use hardcoded text for now.

Explanation: We use an HStack to arrange the two scoreboards horizontally. Each scoreboard is itself an HStack containing a label and a score. Spacer() is a flexible view that expands to fill available space, pushing the two scoreboards to opposite sides of the screen.

// Inside the VStack, after LogoView
// MARK: - Score boards
HStack {
    // Left-side score board
    HStack{
        Text("Your\nMoney".uppercased())
            .multilineTextAlignment(.leading)
        
        Text("100")
    }
    
    Spacer() // Pushes the two HStacks apart
    
    // Right-side score board
    HStack{
        Text("200")
        
        Text("High\nScore".uppercased())
            .multilineTextAlignment(.trailing)
    }
}
Add Score Boards

Step 8: Create and Apply Score Board Modifiers

Instruction:

  1. Open Modifiers.swift and add the three new ViewModifier structs: scoreLabelStyle, scoreNumberStyle, and scoreCapsuleStyle.
  2. Open ContentView.swift and apply these new modifiers to the score board views as shown in the code snippet.

Explanation: We are creating three distinct styles: one for the text labels ("Your Money"), one for the numbers ("100"), and one for the capsule-shaped background. This approach keeps styling logic separate from the layout code in ContentView, making it much cleaner.

// Add these structs to Modifiers.swift
// Styles the text labels like "Your Money"
struct scoreLabelStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .foregroundColor(Color("ColorWhiteRMIT"))
            .font(.system(size: 10, weight: .bold,design: .rounded))
    }
}

// Styles the score numbers like "100"
struct scoreNumberStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .foregroundColor(Color("ColorWhiteRMIT"))
            .font(.system(size: 20, weight: .heavy,design: .rounded))
    }
}

// Creates the capsule background for the score boards
struct scoreCapsuleStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding(.vertical,10)
            .padding(.horizontal,16)
            .background(
                Capsule()
                    .foregroundColor(Color("ColorBlackTransparentRMIT"))
            )
    }
}
// Replace the previous score board HStack with this
HStack {
    HStack{
        Text("Your\nMoney".uppercased())
            .modifier(scoreLabelStyle()) // <-- APPLY MODIFIER
            .multilineTextAlignment(.leading)
        
        Text("100")
            .modifier(scoreNumberStyle()) // <-- APPLY MODIFIER
    }
    .modifier(scoreCapsuleStyle()) // <-- APPLY MODIFIER
    
    Spacer()
    
    HStack{
        Text("200")
            .modifier(scoreNumberStyle()) // <-- APPLY MODIFIER
        
        Text("High\nScore".uppercased())
            .modifier(scoreLabelStyle()) // <-- APPLY MODIFIER
            .multilineTextAlignment(.trailing)
    }
    .modifier(scoreCapsuleStyle()) // <-- APPLY MODIFIER
}
Apply Score Modifiers

Step 9: Add Slot Machine and Reel Modifiers

Instruction:

  1. First, add the ReelImageModifier and IconImageModifier to your Modifiers.swift file.
  2. Then, in ContentView.swift, inside the main VStack and below the score boards, add the code for the slot machine reels. Use ZStack to place an icon image on top of a reel background image.
  3. Apply the new modifiers to the images as you create them.

Explanation: We are building the visual structure of the slot machine. Each reel is a ZStack containing the background reel image and the icon. The ReelImageModifier and IconImageModifier ensure all images are sized correctly and have our standard shadow. For now, we'll use hardcoded icon images like "apple" and "cherry".

// Add these structs to Modifiers.swift
// Styles the background reel images
struct ReelImageModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .scaledToFit()
            .frame(minWidth: 140, idealWidth: 200, maxWidth: 220, alignment: .center)
            .modifier(ShadowModifier())
    }
}

// Styles the icon images (apple, cherry, etc.)
struct IconImageModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .scaledToFit()
            .frame(minWidth: 50, idealWidth: 60, maxWidth: 70, alignment: .center)
            .modifier(ShadowModifier())
    }
}
// Inside the main VStack, after the score boards
// MARK: - Slot Machine
VStack {
    
    // MARK: - First Reel
    ZStack {
        Image("reel")
            .resizable()
            .modifier(ReelImageModifier())
        Image("apple") // Hardcoded for now
            .resizable()
            .modifier(IconImageModifier())
    }
    
    HStack {
        // MARK: - Second Reel
        ZStack {
            Image("reel")
                .resizable()
                .modifier(ReelImageModifier())
            Image("cherry") // Hardcoded for now
                .resizable()
                .modifier(IconImageModifier())
        }
        
        Spacer()
        
        // MARK: - Third Reel
        ZStack {
            Image("reel")
                .resizable()
                .modifier(ReelImageModifier())
            Image("bar") // Hardcoded for now
                .resizable()
                .modifier(IconImageModifier())
        }
    }
}
Add Slot Machine

Step 10: Add the Spin Button

Instruction: In ContentView.swift, at the end of the slot machine VStack, add a Button with the "spin" image. The action can just print a message for now.

Explanation: This adds the primary interactive element to our game. We use a Button view, and for its label, we provide our "spin" image, styled with the ReelImageModifier to match the reels above it. The `action` is a closure—a piece of code that will run when the button is tapped.

// Inside the slot machine VStack, after the HStack for the reels
// MARK: - Spin Button
Button(action: {
    // This code runs when the button is tapped
    print("Spin!!!")
}, label: {
    Image("spin")
        .resizable()
        .modifier(ReelImageModifier())
})
Add Spin Button

Step 11: Add the Footer and its Modifiers

Instruction:

  1. Add the BetCapsuleModifier and CasinoChipModifier to your Modifiers.swift file.
  2. In ContentView.swift, at the bottom of the main VStack, add an HStack for the footer.
  3. Build the footer UI using Text and Image views, and apply the new modifiers.

Explanation: This step builds the static UI for the betting controls. The BetCapsuleModifier creates a stylish, gradient-filled capsule for the bet amounts, and the CasinoChipModifier sizes the chip images appropriately.

// Add these structs to Modifiers.swift
// Styles the bet amount text ("10", "20")
struct BetCapsuleModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .foregroundColor(Color("ColorWhiteRMIT"))
            .font(.system(size: 25, weight: .heavy, design: .rounded))
            .modifier(ShadowModifier())
            .background(
                // The capsule is filled with a gradient for a nice visual effect
                Capsule()
                    .fill(
                        LinearGradient(gradient: Gradient(colors: [Color("ColorYellowRMIT"), Color("ColorRedRMIT")]), startPoint: .top, endPoint: .bottom)
                    )
                    .frame(width: 80, height: 50, alignment: .center)
            )
    }
}

// Styles the casino chip images
struct CasinoChipModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .scaledToFit()
            .frame(height: 70)
            .modifier(ShadowModifier())
    }
}
// At the end of the main VStack, after the slot machine VStack
// MARK: - Footer
HStack {
    
    // MARK: - Bet 20
    HStack(spacing: 30) {
        Text("20")
            .modifier(BetCapsuleModifier())
        
        Image("casino-chips")
            .resizable()
            .modifier(CasinoChipModifier())
    }
    
    Spacer()
    
    // MARK: - Bet 10
    HStack(spacing: 30) {
        Image("casino-chips")
            .resizable()
            .modifier(CasinoChipModifier())
            .opacity(0) // Hidden for now
        Text("10")
            .modifier(BetCapsuleModifier())
    }
}
.padding(.horizontal, 20)
Add Footer

🧠 5. Adding Game Logic

Step 12: Add Game Logic

Instruction: Now we will make the game interactive.

  1. Add Icon Array: In ContentView.swift, at the top of the ContentView struct, add a constant array icons that holds the names of all your slot icon images.
  2. Add State: Below the icons array, add a @State variable named reels to store the current state of the three reels.
  3. Update Reels: Go to the slot machine UI code and replace the hardcoded image names ("apple", "cherry", "bar") with references to your new state variable, like icons[reels[0]].
  4. Create Spin Function: Add a spinReels() function inside the ContentView struct. This function will assign a new random index to each item in the reels array.
  5. Connect Button: Find the "spin" Button and change its action to call your new spinReels() function.

Explanation: This is where the magic happens.

  • The icons array gives us a single source for all possible outcomes.
  • The @State property wrapper tells SwiftUI to watch the reels variable for any changes.
  • When the user taps the spin button, the spinReels() function is called, which changes the values in the reels array.
  • Because reels is a @State variable, SwiftUI automatically detects this change and re-draws the parts of the UI that depend on it (the icon images), showing the new random combination.

// ContentView.swift
import SwiftUI

struct ContentView: View {
    // MARK: - PROPERTIES
    // This array holds the names of all the icon images.
    let icons = ["apple","bar","cherry","clover","diamond", "grape", "heart", "horseshoe","lemon","melon","money","orange"]
    
    // @State tells SwiftUI to watch this variable for changes.
    // When it changes, the UI will automatically update.
    // It stores the index of the icon for each of the three reels.
    @State private var reels = [0,1,2]

    // MARK: - FUNCTIONS
    func spinReels(){
        // Randomize the icon for each reel.
        reels[0] = Int.random(in: 0...icons.count - 1)
        reels[1] = Int.random(in: 0...icons.count - 1)
        reels[2] = Int.random(in: 0...icons.count - 1)
    }
    
    var body: some View {
        ZStack {
            // ... (Background code is unchanged)
            
            VStack {
                // ... (Logo and Score boards are unchanged)
                
                // MARK: - Slot Machine
                VStack {
                    // MARK: - First Reel
                    ZStack {
                        Image("reel")
                            .resizable()
                            .modifier(ReelImageModifier())
                        // CHANGED: Display the icon from our state array
                        Image(icons[reels[0]])
                            .resizable()
                            .modifier(IconImageModifier())
                    }
                    
                    HStack {
                        // MARK: - Second Reel
                        ZStack {
                            Image("reel")
                                .resizable()
                                .modifier(ReelImageModifier())
                            // CHANGED: Display the icon from our state array
                            Image(icons[reels[1]])
                                .resizable()
                                .modifier(IconImageModifier())
                        }
                        
                        Spacer()
                        
                        // MARK: - Third Reel
                        ZStack {
                            Image("reel")
                                .resizable()
                                .modifier(ReelImageModifier())
                            // CHANGED: Display the icon from our state array
                            Image(icons[reels[2]])
                                .resizable()
                                .modifier(IconImageModifier())
                        }
                    }
                    
                    // MARK: - Spin Button
                    Button(action: {
                        // CHANGED: Call the spinReels function
                        spinReels()
                    }, label: {
                        Image("spin")
                            .resizable()
                            .modifier(ReelImageModifier())
                    })
                }
                
                // ... (Footer is unchanged)
            }
            .padding()
        }
    }
}

#Preview {
    ContentView()
}
Add Game Logic

🥳 Congratulations!

Awesome job! You've just brought the RMIT Casino app to life. Fire it up in the simulator, hit that spin button, and see your creation in action as the reels whirl and land on a random combination. This is the foundation of your game—and it's already looking great!

🚀 Level Up Your Skills: Bonus Challenges!

Challenge 1: Apple Dice Game 🎲

To warm up, let’s create a fun app to roll virtual dice with the functionality to increase or decrease the number of dice on the screen to play different kinds of games. Please follow Apple SwiftUI tutorial here: https://developer.apple.com/tutorials/develop-in-swift/update-the-ui-with-state

Apple Dice Game Challenge

Challenge 2: RMIT Red & Blue Dice Game 🎲

You play blue dice. The AI opponent plays red dice. When you press the “Play” button, dice rolls are randomized. If your total sum of dice is greater than your opponent then you will win and see the winning message like in the demo screenshot below. Otherwise, you lose and see the lost message. Add some sound effects when you win or you lose. You can select free game sound effects here:

All image assets of this dice game can be downloaded on Canvas. You can also find the dice images in the Asset Gallery. However you are encouraged to choose your own image assets and colorsets.

Extra challenge: Add a total score text on the top of the game. Add +100 points for any winning game and -100 points for any losing game.

RMIT Red & Blue Dice Game Challenge

🎉 What's Next?

You've Mastered the Basics!

You've successfully built the foundation of the RMIT Casino app. Along the way, you've learned key SwiftUI concepts:

  • Setting up a project and managing assets in the Asset Catalog.
  • Building complex layouts with ZStack, VStack, and HStack.
  • Creating reusable styles with custom ViewModifiers to keep your code clean and consistent.
  • Making your UI interactive with @State variables and Button actions.

Now that you have a working game, why not take on the Bonus Challenges? They are a great way to test your skills and learn even more.

Ready for more? In Part 2 of this guide, we will take this game to the next level by adding betting logic, calculating winnings, managing player scores, and adding sound effects to create a fully immersive casino experience. Stay tuned!