子ども向け はじめてのiPhoneアプリ開発ワークショップ

# ワクワク！はじめてのiPhoneアプリ作り大冒険！ ✨
## 〜君だけのジャンプゲームを作ろう〜 

## ようこそ、プログラミングの世界へ！ 👋
やぁ、みんな！今日は特別な冒険にでかけるよ！
なんと、自分だけのiPhoneアプリを作っちゃうんだ！
ゲームを作りながら、プログラミングの楽しさを体験していこう！

## 今日の冒険でできるもの 🎮
- タップでピョンピョン跳ねるかわいい主人公
- ジャンプで障害物をよけるドキドキゲーム
- スコアがどんどん上がっていく楽しさ
- ハイスコアに挑戦できるやりこみ要素

## 冒険の準備をしよう！ 🎒
### 必要なもの
- Macパソコン（macOS Monterey 12.0以降）
  - 魔法の道具みたいなすごいパソコンだよ！
- Apple ID（無料）
  - 宝物を入れておける特別なカギみたいなもの
- Xcode（無料）
  - アプリを作る魔法の道具箱だよ
- やる気とワクワク感！
  - これが一番大切！君の想像力が必要だ！

## 大切な約束！ 🌟
- 分からないことがあったら、すぐに質問してね
  - 質問するのは賢い証拠だよ！
- 間違えても全然平気！
  - 失敗は成功のもと。何度でもチャレンジしよう！
- パソコンは優しく扱おう
  - 大切な道具は優しく使おうね

## お城（パソコン）の準備をしよう！ 🏰

### Step 1：魔法のカギを作ろう（Apple ID作成）
※この部分は魔法使い（先生や大人の人）と一緒にやろう！
1. パソコンでインターネットの世界へ
2. Apple IDの作成ページに行く
3. 必要な情報を入れていこう
   - ここは大人の人に手伝ってもらおう！

### Step 2：魔法の道具箱を手に入れよう（Xcodeインストール）
1. App Storeという宝物庫を開く
2. 「Xcode」という魔法の道具を探す
3. インストールボタンを押す
   - とっても大きな魔法の道具箱だから
   - ダウンロードには時間がかかるよ
   - 気長に待とう！

### Step 3：魔法の試験場を準備しよう（iOSシミュレーター設定）
1. はじめてXcodeを開くと「iOS 18.2 not installed」というメッセージが出るよ
   - これは「iPhone の試験場がまだ準備できてないよ」という意味
   - 心配しないで！簡単に解決できるよ

2. 準備の方法その1（簡単）：
   - 青い警告メッセージをクリック
   - 「Download iOS 18.2」をクリック
   - ダウンロードが終わるまで待とう

3. 準備の方法その2：
   - 画面上部の「Xcode」メニューをクリック
   - 「Settings...」を選ぶ
   - 左側の「Platforms」をクリック
   - iOS 18.2の横にある「Download」ボタンをクリック

4. インストールが終わるまで待とう
   - ダウンロードには少し時間がかかるよ
   - 「Done」や「Finished」と表示されたら完了！
   - Xcodeを一度閉じて、開き直してみよう

※ダウンロードはWi-Fiがつながっているところでしてね
※分からないことがあったら、先生や大人の人に聞いてね！

# いよいよ冒険のスタート！ 🎮

## Step 1：魔法の道具箱を開こう（Xcodeを起動）
1. 画面下の Dock から Xcode を探そう
   - 青い背景にハンマーの形をした魔法の箱だよ
2. 見つからないときは：
   - 右上の虫眼鏡をクリック
   - 「Xcode」って入力してみよう
   - 出てきた青いアイコンをクリック

## Step 2：新しい冒険の地図を作ろう（プロジェクト作成）
1. 「Create a new Xcode project」をクリック
   - 新しい冒険の始まりだ！
2. 上の方にある「iOS」をクリック
   - iPhoneアプリを作る世界だよ
3. 「App」を選んでクリック
   - これが基本の冒険セット！
4. 「Next」を押す

## Step 3：冒険に名前をつけよう（プロジェクト設定）
1. Product Name に「JumpGame」と入力
   - 半角英語で書くのを忘れずに
   - 大文字と小文字をまちがえないように
   - これが君のゲームの名前になるよ！

2. チーム設定をしよう
   - Team のところを探してみよう
   - 「Add Account...」をクリック
   - Apple ID でサインイン
   - サインインできたら「Personal Team」を選ぼう

3. Organization Identifier を設定
   - 「com.example」と入力
   - これは魔法使いたちの決まり事だよ！

4. 魔法の設定をしよう
   - Interface: 「SwiftUI」にチェック
   - Language: 「Swift」にチェック
   - 「Create Git repository on my Mac」のチェックを外す
   - 他のチェックも外しておこう

5. 全部入力できたら「Next」を押そう
   - 保存する場所はデスクトップを選んでね
   - 最後に「Create」をクリック

## Step 4：魔法の巻物を開こう（コードを書く準備）
1. 左側のファイル一覧から「ContentView.swift」を探そう
   - 青いアイコンの巻物みたいなファイルだよ
2. クリックして開いてみよう
3. 中に書いてある文字を消して、新しい魔法を書く準備をしよう
   - Command + A で全部選択
   - Deleteキーで消してスッキリ！

## Step 5：魔法の呪文を唱えよう（プログラムを書く）
1. プログラムをまるごとコピーしよう

これが僕たちの作るゲームの魔法の呪文だよ！
下のプログラムをまるごとコピーしてXcodeに貼り付けてね！

```swift
import SwiftUI

struct ContentView: View {
    @State private var characterPosition = CGPoint(x: 100, y: 300)
    @State private var isJumping = false
    @State private var obstaclePosition = CGPoint(x: 400, y: 300)
    @State private var gameTimer: Timer?
    @State private var score = 0
    @State private var isGameOver = false
    @State private var highScore = UserDefaults.standard.integer(forKey: "highScore")
    @State private var showStartScreen = true
    
    let backgroundGradient = LinearGradient(
        colors: [.blue.opacity(0.3), .purple.opacity(0.2)],
        startPoint: .top,
        endPoint: .bottom
    )
    
    var body: some View {
        ZStack {
            // きれいな背景
            backgroundGradient
                .ignoresSafeArea()
            
            if showStartScreen {
                // スタート画面
                VStack(spacing: 20) {
                    Text("ジャンプゲーム")
                        .font(.system(size: 50, weight: .bold))
                        .foregroundColor(.blue)
                    
                    Text("画面タップでジャンプ！")
                        .font(.title2)
                    
                    Text("障害物をよけよう！")
                        .font(.title2)
                    
                    Text("ハイスコア: \(highScore)")
                        .font(.title3)
                        .foregroundColor(.gray)
                        .padding(.top)
                    
                    Button {
                        withAnimation {
                            showStartScreen = false
                            startGame()
                        }
                    } label: {
                        Text("ゲームスタート")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                            .padding()
                            .background(Color.blue)
                            .cornerRadius(10)
                    }
                    .padding(.top)
                }
                .padding()
            } else {
                // ゲーム画面
                Group {
                    // スコア表示
                    VStack {
                        Text("スコア: \(score)")
                            .font(.title2)
                            .bold()
                            .padding()
                            .background(Color.white.opacity(0.8))
                            .cornerRadius(10)
                        
                        if score > 0 {
                            Text("がんばれ！")
                                .font(.title3)
                                .foregroundColor(.blue)
                                .bold()
                        }
                    }
                    .position(x: 200, y: 50)
                    
                    // キャラクター
                    ZStack {
                        // 体
                        Circle()
                            .fill(Color.red)
                            .frame(width: 50, height: 50)
                        // 目
                        Circle()
                            .fill(Color.white)
                            .frame(width: 20, height: 20)
                            .offset(x: 10, y: -5)
                        Circle()
                            .fill(Color.black)
                            .frame(width: 10, height: 10)
                            .offset(x: 12, y: -5)
                    }
                    .position(characterPosition)
                    
                    // 障害物
                    ZStack {
                        Rectangle()
                            .fill(
                                LinearGradient(
                                    colors: [.green, .green.opacity(0.7)],
                                    startPoint: .top,
                                    endPoint: .bottom
                                )
                            )
                            .frame(width: 30, height: 60)
                        // キラキラ
                        Circle()
                            .fill(Color.white)
                            .frame(width: 10, height: 10)
                            .opacity(0.6)
                            .offset(x: -5, y: -20)
                    }
                    .position(obstaclePosition)
                }
                
                // ゲームオーバー画面
                if isGameOver {
                    VStack(spacing: 20) {
                        Text("ゲームオーバー")
                            .font(.system(size: 40, weight: .bold))
                            .foregroundColor(.red)
                        
                        Text("スコア: \(score)")
                            .font(.title2)
                            .bold()
                        
                        if score > highScore {
                            Text("新記録！")
                                .font(.title2)
                                .foregroundColor(.orange)
                                .bold()
                        }
                        
                        Button {
                            resetGame()
                        } label: {
                            Text("もう一度チャレンジ")
                                .font(.title3)
                                .bold()
                                .foregroundColor(.white)
                                .padding()
                                .background(Color.blue)
                                .cornerRadius(10)
                        }
                    }
                    .padding()
                    .background(Color.white.opacity(0.9))
                    .cornerRadius(20)
                }
                
                // タップ判定用の透明ボタン
                Button {
                    if !isGameOver {
                        jump()
                    }
                } label: {
                    Color.clear
                }
            }
        }
    }
    
    func jump() {
        guard !isJumping else { return }
        isJumping = true
        
        withAnimation(.easeOut(duration: 0.5)) {
            characterPosition.y -= 100
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            withAnimation(.easeIn(duration: 0.5)) {
                characterPosition.y += 100
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                isJumping = false
            }
        }
    }
    
    func startGame() {
        score = 0
        isGameOver = false
        characterPosition = CGPoint(x: 100, y: 300)
        obstaclePosition = CGPoint(x: 400, y: 300)
        
        gameTimer = Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { _ in
            withAnimation {
                obstaclePosition.x -= 2
                
                if obstaclePosition.x < -20 {
                    obstaclePosition.x = 420
                    score += 1
                }
                
                if checkCollision() {
                    gameOver()
                }
            }
        }
    }
    
    func gameOver() {
        gameTimer?.invalidate()
        gameTimer = nil
        isGameOver = true
        
        if score > highScore {
            highScore = score
            UserDefaults.standard.set(highScore, forKey: "highScore")
        }
    }
    
    func resetGame() {
        withAnimation {
            showStartScreen = true
            // ゲーム状態をリセット
            score = 0
            isGameOver = false
            characterPosition = CGPoint(x: 100, y: 300)
            obstaclePosition = CGPoint(x: 400, y: 300)
            gameTimer?.invalidate()
            gameTimer = nil
        }
    }
    
    func checkCollision() -> Bool {
        let characterFrame = CGRect(
            x: characterPosition.x - 25,
            y: characterPosition.y - 25,
            width: 50,
            height: 50
        )
        
        let obstacleFrame = CGRect(
            x: obstaclePosition.x - 15,
            y: obstaclePosition.y - 30,
            width: 30,
            height: 60
        )
        
        return characterFrame.intersects(obstacleFrame)
    }
}
```

2. コピーしたプログラムをXcodeに貼り付けよう
   - Command + V でペッ！と貼り付け
3. 魔法を保存しよう
   - Command + S で魔法を封印完了！

## Step 6：シミュレーターで遊んでみよう
1. まずシミュレーターを選ぼう！
   - Xcodeの上部にある再生ボタン（▶）の左隣を見てね
   - 「Any iOS Device (arm64)」と書いてある部分をクリック
   - メニューが開くので、その中から：
     - 「iPhone 11 Simulator」や
     - 「iPhone 15 Simulator」などを選んでね
   - 必ず「Simulator」という文字が付いているものを選ぼう！

2. シミュレーターを追加したい時は：
   - 上部メニューから「Window」→「Devices and Simulators」を選ぶ
   - 上部の「Simulators」タブをクリック
   - 左下の「+」ボタンをクリック
   - Device Type: 使いたいiPhone（例：iPhone 11）を選ぶ
   - iOS Version: 一番上にある最新のものを選ぶ
   - 「Create」ボタンを押して完成！

3. 上の方にある再生ボタン（▶）を押す
   - シミュレーターが開くまで少し待ってね
   - はじめは時間がかかることもあるよ

## Step 7：実際のiPhoneで遊んでみよう
※この方法は先生や大人の人と一緒にやってね！

1. iPhoneをMacにつなごう
   - iPhoneの充電ケーブルを使ってね
   - パスコードを入力して、iPhoneのロックを解除してね

2. iPhoneを信頼設定しよう
   - iPhoneに「このコンピュータを信頼しますか？」と出てきたら
   - 「信頼」を選んでね
   - もう一度パスコードの入力が必要かも

3. Xcodeでデバイスを選ぼう
   - 上部の再生ボタン（▶）の左隣にある「iPhone ○○ Simulator」と書いてある部分をクリック
   - 開いたメニューの中から、「Simulator」が付いていない方のリストから
   - つないだiPhoneの名前（例：「John's iPhone」など）を選ぼう

4. 開発者の設定をしよう
   - エラーが出たら「Register Device」をクリック
   - チームの設定が求められたら、Apple IDで設定してね
   - この作業は最初の1回だけでOK！

5. iPhoneで承認しよう
   - iPhoneの「設定」アプリを開く
   - 「一般」→「デバイス管理」を選ぶ
   - Apple IDが表示されているところをタップ
   - 「信頼」を選んでね

6. 実行ボタン（▶）を押そう
   - iPhoneにゲームが表示されるよ！
   - 実際に触って遊んでみよう！

※注意点：
- 開発者登録をしていない場合、7日間だけゲームが動くよ
- 7日過ぎたら、もう一度Macとつないで実行する必要があるよ
- Wi-Fiがつながっている場所で作業してね

## 困ったときは？
### 画面が真っ白になったら
1. まずプログラムを確認しよう
   - 左側の「ContentView.swift」を開いてみる
   - プログラムがちゃんと入っているか確認
   - 空っぽになっていたら、もう一度プログラムをコピー＆ペースト

2. もう一度プログラムを入れ直してみよう
   - Command + A で全部選択
   - Deleteキーで消す
   - プログラムをもう一度貼り付け
   - Command + S で必ず保存！

3. それでも動かない時は
   - 上のメニューから「Product」→「Clean Build Folder」を選ぶ
   - 少し待つ
   - もう一度再生ボタン（▶）を押してみる

4. まだ動かない時は
   - Xcodeを一度終了して、開き直す
   - プロジェクトを開いて、もう一度試してみる

### Could not attach to pid というエラーが出たら
1. シミュレーターを終了しよう
   - シミュレーターの上部メニューから「Simulator」→「Quit Simulator」
   - または Command + Q で閉じてね
2. Xcodeも一度終了しよう
   - Xcodeメニューから「Xcode」→「Quit Xcode」
   - または Command + Q で閉じてね
3. もう一度挑戦！
   - Xcodeを開き直す
   - プロジェクトを開く
   - シミュレーターを選んで再生ボタン（▶）を押す

### A build only device cannot be used とエラーが出たら
- シミュレーターが正しく選べていないよ
- 上のStep 6の手順1に戻って、ちゃんとシミュレーターを選び直してね
- 「Simulator」という文字が付いているものを選ぶのが大切！

## ゲームの遊び方 🎮
1. 「ゲームスタート」ボタンを押してスタート！
2. 画面をタップするとキャラクターがジャンプ
3. 緑の障害物を避けながら進もう
4. 障害物にぶつかるとゲームオーバー
5. スコアを競って、ハイスコアを目指そう！

## カスタマイズしてみよう！ 🎨

プログラムの中の以下の部分を変えると、ゲームをカスタマイズできるよ：

1. キャラクターの色を変えよう
```swift
Circle()
    .fill(Color.red)  // ここの .red を変えてみよう！
```
使える色：
- `.blue` (青)
- `.green` (緑)
- `.yellow` (黄色)
- `.orange` (オレンジ)
- `.purple` (紫)
- `.pink` (ピンク)

2. ジャンプの高さを変えよう
```swift
characterPosition.y -= 100  // この 100 を変えてみよう！
```
- 大きくすると高くジャンプ
- 小さくすると低くジャンプ

3. 障害物の速さを変えよう
```swift
obstaclePosition.x -= 2  // この 2 を変えてみよう！
```
- 大きくすると速く
- 小さくすると遅く

4. 背景の色を変えよう
```swift
colors: [.blue.opacity(0.3), .purple.opacity(0.2)]  // この色を変えてみよう！
```
- 好きな色の組み合わせを試してみよう
- `opacity` は 0 から 1 の間で設定してね（透明度）

がんばってカスタマイズしてみよう！ 👍