View in English

  • Apple Developer
    • 今すぐ始める

    「今すぐ始める」を詳しく見る

    • 概要
    • 学ぶ
    • Apple Developer Program

    最新情報

    • 最新ニュース
    • Hello Developer
    • プラットフォーム

    プラットフォームを詳しく見る

    • Appleプラットフォーム
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    特集

    • デザイン
    • 配信
    • ゲーム
    • アクセサリ
    • Web
    • Home
    • CarPlay
    • テクノロジー

    テクノロジーを詳しく見る

    • 概要
    • Xcode
    • Swift
    • SwiftUI

    特集

    • アクセシビリティ
    • App Intent
    • Apple Intelligence
    • ゲーム
    • 機械学習とAI
    • セキュリティ
    • Xcode Cloud
    • コミュニティ

    コミュニティを詳しく見る

    • 概要
    • 「Appleに相談」イベント
    • コミュニティによるイベント
    • デベロッパフォーラム
    • オープンソース

    特集

    • WWDC
    • Swift Student Challenge
    • デベロッパストーリー
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Center
    • ドキュメント

    ドキュメントを詳しく見る

    • ドキュメントライブラリ
    • テクノロジー概要
    • サンプルコード
    • ヒューマンインターフェイスガイドライン
    • ビデオ

    リリースノート

    • 注目のアップデート
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • ダウンロード

    ダウンロードを詳しく見る

    • すべてのダウンロード
    • オペレーティングシステム
    • アプリ
    • デザインリソース

    特集

    • Xcode
    • TestFlight
    • フォント
    • SF Symbols
    • Icon Composer
    • サポート

    サポートを詳しく見る

    • 概要
    • ヘルプガイド
    • デベロッパフォーラム
    • フィードバックアシスタント
    • お問い合わせ

    特集

    • アカウントヘルプ
    • App Reviewガイドライン
    • App Store Connectヘルプ
    • 近日導入予定の要件
    • 契約およびガイドライン
    • システムステータス
  • クイックリンク

    • イベント
    • ニュース
    • Forum
    • サンプルコード
    • ビデオ
 

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • すべてのビデオ
  • 利用方法

その他のビデオ

  • 概要
  • Summary
  • コード
  • 優れたタッチ操作によるゲーム体験の向上

    ゲームのタッチ体験を魅力的にするための効果的な手法を詳しく解説します。インディーゲームからAAAゲームまでを含む幅広いゲーム開発についての専門的なインサイトを共有します。直感的なタッチ操作を提供するためのベストプラクティスや、Touch ControllerフレームワークやMetalなどのAppleテクノロジーを活用して優れたパフォーマンスを実現する方法を学ぶことができます。

    関連する章

    • 0:00 - Introduction
    • 1:42 - Set up a touch controller
    • 4:52 - Design flexible layouts
    • 10:17 - Design fluid interactions
    • 21:16 - Provide rich feedback
    • 23:49 - Next steps

    リソース

      • HDビデオ
      • SDビデオ

    関連ビデオ

    Meet with Apple

    • Design great interfaces for handheld games
    • Level up with Apple game technologies
  • このビデオを検索
    • 2:04 - GCController polling vs. change handlers

      // Polling
      if (button.isPressed) {
          // ...
      }
      
      // Change handlers
      pressedInput.pressedDidChangeHandler = { (element: any GCPhysicalInputElement,
                                                 input: any GCPressedStateInput,
                                                 pressed: Bool)
          // ...
      }
    • 3:14 - Set up a TCTouchController

      // Set up a TCTouchController
      private(set) var touchController: TCTouchController?
      
      let descriptor = TCTouchControllerDescriptor(mtkView: mtkView)
      if TCTouchController.isSupported {
          touchController = TCTouchController(descriptor: descriptor)
      }
      touchController?.connect()
      touchController?.render(using: renderEncoder)
      
      override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
          for touch in touches {
              touchControls.handleTouchBegan(at: touch.location(in: view), index: touch.hash)
          }
      }
      
      buttonA?.valueChangedHandler = { (_ button: GCControllerButtonInput, _ value: Float,
                                        _ pressed: Bool) in
          // ...
      }
    • 8:33 - Create a standard circular button B

      // Create a standard circular button B
      let buttonBDesc = TCButtonDescriptor()
      buttonBDesc.label = TCControlLabel.buttonB
      buttonBDesc.anchor = .bottomRight
      buttonBDesc.offset = adjustedOffset(CGPoint(x: -35, y: -106), for: buttonBDesc.anchor)
      buttonBDesc.contents = .buttonContents(forSystemImageNamed: "b.circle",
                                             size: buttonBDesc.size, shape: .circle,
                                             controller: touchController)
      // Set other properties ...
      touchController.addButton(descriptor: buttonBDesc)
      
      func adjustedOffset(_ offset: CGPoint, for anchor: TCControlLayoutAnchor) -> CGPoint {
          // Adjust offset for other anchors ...
          case .bottomRight:
              x -= safeArea.right
              y -= safeArea.bottom
      }
    • 10:48 - Change icon image

      // Change icon image
      buttonBDesc.contents = .buttonContents(forSystemImageNamed: "figure.fencing",
                                             size: buttonBDesc.size,
                                             shape: .circle,
                                             controller: touchController)
    • 11:51 - Update contents for button B based on context

      // Update contents for button B based on context
      func setButtonBContents(symbolName: String) {
          for button in touchController.buttons {
              if button.label == TCControlLabel.buttonB {
                  button.contents = .buttonContents(forSystemImageNamed: symbolName, size: buttonSize,
                                                    shape: .circle, controller: touchController)
              }
          }
      }
      
      func cyclePower() {
          // Get the current power type ...
          switch currentPower {
              case .strike:       touchControls?.setButtonBContents(symbolName: "figure.fencing")
              case .fireball:     touchControls?.setButtonBContents(symbolName: "flame.fill")
              case .waterBlaster: touchControls?.setButtonBContents(symbolName: "drop.fill")
          }
      }
    • 13:01 - Hide left thumbstick when not touched

      // Hide left thumbstick when it is not touched
      let leftStickDesc = TCThumbstickDescriptor()
      leftStickDesc.hidesWhenNotPressed = true
      // Set other properties ...
      touchController.addThumbstick(descriptor: leftStickDesc)
    • 13:19 - Show/hide the pick-up button

      // Show pickup button when there's an item nearby
      func showPickupButton(at projectedPosition: CGPoint) {
          // Calculate the position(ptX, ptY) for pickup button ...
          descriptor.offset = CGPoint(x: ptX, y: ptY)
          // Set other properties ...
          touchController.addButton(descriptor: descriptor)
      }
      
      func hidePickupButton() {
          for button in touchController.buttons {
              if button.label == TCControlLabel.buttonY {
                  touchController.removeControl(button)
              }
          }
      }
    • 13:56 - Show power options as touch controls

      // Show power options as touch controls
      buttonX?.pressedChangedHandler = { (_ button: GCControllerButtonInput, _ value: Float,
                                          _ pressed: Bool) -> Void in
          if pressed {
              self.openPowerWheel()
          }
      }
      
      func openPowerWheel() {
          touchControls?.showPowerWheelButtons(fireballCount: fireballCount, has: hasWaterBlaster)
          wirePowerWheelHandlers()
          DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [weak self] in
              guard let self = self, self.powerWheelActive else { return }
              self.closePowerWheel()
          }
      }
    • 15:34 - Use the left half of the screen for character movement

      // Use the left half of the screen for character movement
      let leftStickDesc = TCThumbstickDescriptor()
      leftStickDesc.colliderShape = .leftSide // Don't set as .circle
      // Set other properties ...
      touchController.addThumbstick(descriptor: leftStickDesc)
    • 16:39 - Calculate thumbstick tilt magnitude to trigger sprint

      // Calculate left thumbstick's tilt magnitude to trigger sprint
      func pollInput() {
          if let gamePad = gameController.extendedGamepad {
              let gamePadLeft = gamePad.leftThumbstick
              var moveInput = simd_make_float2(gamePadLeft.xAxis.value, -gamePadLeft.yAxis.value)
              let magnitude = simd_length(moveInput)
              if magnitude > 0.8 {
                  self.runModifier = 1.3
              }
              self.characterDirection = moveInput
          }
      }
    • 17:36 - Replace right thumbstick with a touchpad

      // Replace right thumbstick with touchpad
      let touchpadDesc = TCTouchpadDescriptor()
      touchpadDesc.label = TCControlLabel.rightThumbstick
      touchpadDesc.colliderShape = .rightSide
      touchpadDesc.reportsRelativeValues = true
      // Set other properties ...
      touchController.addTouchpad(descriptor: touchpadDesc)
    • 19:30 - Collapse two QTE buttons into one

      // Collapse 2 QTE buttons into 1 single button
      func setupControls() {
          let desc = TCButtonDescriptor()
          desc.label = TCControlLabel(name: "escape_button", role: .button)
          // Set up other properties ...
          touchController.addButton(descriptor: desc)
      }
      
      func showEscapeButton() {
          // Find escape button in touchController ...
          escapeButton.isEnabled = true
      }
      
      func hideEscapeButton() {
          // Find escape button in touchController ...
          escapeButton.isEnabled = false
      }
    • 20:28 - Use button B to aim, move, and release power

      // Use button B to aim, move, and release power
      buttonB?.valueChangedHandler = { (_ button: GCControllerButtonInput, _ value: Float,
                                        _ pressed: Bool) -> Void in
          self.releasePower(pressed: pressed)
      }
      
      override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
          for touch in touches {
              let point = touch.location(in: metalView)
              // Handle touch input ...
              if let gc = gameController, gc.isAiming {
                  let prev = touch.previousLocation(in: metalView)
                  gc.aimTouchDelta += simd_float2(Float(point.x - prev.x), Float(point.y - prev.y))
              }
          }
      }
    • 21:52 - Add a halo effect with custom TCControlContents

      // Add a halo effect around left thumbstick with customized TCControlContents
      let haloLayer = TCControlImage(texture: haloTexture, size: haloSize, highlight: nil,
                                     offset: .zero, tintColor: tint)
      let normalBgImages = TCControlContents.thumbstickStickBackgroundContents(size: bgSize,
                                                                               controller: controller).images
      haloThumbstickBg = TCControlContents(images: [haloLayer] + normalBgImages)
      thumbstick.backgroundContents = active ? haloThumbstickBg : normalThumbstickBg
    • 0:00 - Introduction
    • An overview of why great touch controls are essential for games on iOS and iPadOS, using Dredge by Black Salt Games as an example, and a preview of the four areas covered: setup, flexible layouts, fluid interactions, and player feedback.

    • 1:42 - Set up a touch controller
    • How the Touch Controller framework extends existing GCController support to touch input. Covers creating a TCTouchController from a descriptor, enabling it, and how it appears as a standard GCController object so existing game input code requires minimal changes.

    • 4:52 - Design flexible layouts
    • How to place touch controls comfortably across all iOS and iPadOS screen sizes using the framework's nine anchor points and section grouping. Covers reading UIKit safe area insets and strategies for positioning controls — near thumbs for frequent actions, top of screen for less critical ones — to avoid obscuring gameplay.

    • 10:17 - Design fluid interactions
    • How to make touch controls feel native rather than like a direct controller overlay. Covers contextual icons that reflect current game state, hiding unavailable controls, replacing complex overlays with direct touch input, full-screen thumbstick collider shapes for easier character movement, sprint detection via thumbstick tilt magnitude, and using TCTouchpad for smooth camera control.

    • 21:16 - Provide rich feedback
    • How to give players clear feedback during touch interactions using built-in press states, custom visual effects like a glowing thumbstick halo during sprint, and strategies for simplifying complex multi-finger actions like QTEs and aim-to-release power throws into single, intuitive controls.

    • 23:49 - Next steps
    • Guidance on getting started: design your touch controls with the Touch Controller framework, test on multiple device sizes, and iterate based on player feedback to make your game feel brand new on iPhone and iPad.

Developer Footer

  • ビデオ
  • WWDC26
  • 優れたタッチ操作によるゲーム体験の向上
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • Apple Intelligence
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習とAI
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • Mini Apps Partner Program
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    最新ニュースを読む。
    Apple Developerアプリを入手する。
    Copyright © 2026 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン