「ポモドーロ」ってよく目にするけど、何だろう?

タスク管理で情報収集をしていると、「ポモドーロ」とか「ポモドーロ・テクニック」とか「ポモドーロタイマー」ってよく目にしませんか?

気になって調べると、これは時間管理のテクニックのようです。

まずは、分かりやすい記事があったので、こちらを紹介します。

作業効率が上がるポモドーロ・テクニックとは?その効果や実践方法を紹介!

https://mynavi-job20s.jp/howto/pomodoro_technique.html

ポモドーロとは?

ポモドーロは、イタリア語でトマトを意味です。

何分間の作業であれば人間の集中力が持つかを試行錯誤した際に利用したトマト型のキッチンタイマーが由来のようです。

ポモドーロ・テクニックとは?

仕事や勉強のタスクを25分ごとに分割し、5分間の休憩を間に入れて、タスクを実施していく時間管理のテクニックです。

ポモドーロ・テクニックの効果

  1. 集中力が持続できる

    • 長すぎない時間を設定することにより1つの事に集中ができるため。

  2. 時間の管理ができる

    • 予定通りいかない場合も一定時間で振り返れるため。

  3. タスク管理ができる

    • 時間内に達成する予定を立てる必要があるため。

ポモドーロ・テクニックのやり方

  1. 終わらせたいタスクを選択する

  2. 25分にタイマーをセットする

  3. 25分間、集中して作業する

  4. 25分が完了したら、タスクにチェックを入れる

  5. 5分間の休憩をとる

※後は、これを繰り返します。

まとめ

実際にやってみましたが効果がありそうです!

YouTubeで「ポモドーロ タイマー」でヒットする動画を流す方法もやってみると、とても効果を感じれました。

作業環境や内容によってはマッチしないところも出てきますが、ポモドーロ・テクニックの本質を理解して、作業効率のアップに繋げていければと思いました!!

5

Game Up Your Life.

Skimieアプリをダウンロードして、タスク管理をゲーム化しよう

Skimieは、仕事や勉強にゲーミフィケーションを取り入れ、モチベーションアップをサポートする情報管理サービスです。

新着記事

Widgetテストにおける認証系のテスト

Widgetテストでは、エミュレーターや実機を使えないので、Firebaseに問い合わせるユーザー認証の機能が使えません。なので、モックのFirebaseAuthやGoogleSignInのパッケージを使って認証系のテストをします。https://pub.dev/packages/firebase_auth_mockshttps://pub.dev/packages/google_sign_in_mocksテストの流れFirebaseAuthとGoogleSignInのインスタンスのProviderをoverrideして、それぞれのモックのインスタンスに置き換えるAuthenticator のメソッドがモック化されたので、そのメソッドを使ってテストをするpackages/skimie/test/presentation/pages/login_page/components/signup_buttons_test.dartGoogleログインのWidgetテストを解説します。まず始めに、setUpにて、mockUserやmockGoogleSignIn、mockFirebaseAuthのインスタンスを用意します。このときに、mockFirebaseAuthには、mockUserのインスタンスを渡しておきます。 setUp(() async { final MockUser mockUser = MockUser( initialDisplayName: 'skimie_test', ); mockGoogleSignIn = MockGoogleSignIn(); mockFirebaseAuth = MockFirebaseAuth(initCurrentUser: mockUser); await Firebase.initializeApp(); });Widgetテストを開始するときに、対象のProviderをoverrideしてモック化します。ここでは、Googleログインのテストをしているので、そのメソッドがあるGoogleAuthenticatorをモック化しています。 testWidgets('Googleログイン', (WidgetTester tester) async { await tester.pumpWidget( ProviderScope( overrides: [ googleAuthenticatorProvider.overrideWithValue( GoogleAuthenticator( auth: mockFirebaseAuth, googleSignIn: mockGoogleSignIn, ), ), ], child: const MaterialApp( home: Material( child: SignUpButtons(animating: false), ), ), ), );SignUpButtonsをタップしたときには、FirebaseAuthのsignInWithCredentialを使っているので、モッククラス内でこのメソッドを修正します。 @override Future<UserCredential> signInWithCredential( AuthCredential credential, ) async { // ログインしたプラットフォームを確認 Log.i('ログイン : ${credential.signInMethod}'); final userCredential = MockUserCredential(); // currentUserを更新 currentUser = userCredential.user; return userCredential; } setUp内でcurrentUserにMockUserをセットしていましたが、このメソッドを走らせると、別のユーザーにcurrentUserが置き換わる様に修正しました。メソッドが走ったことをユーザー名が変更されていることで確認します。 // signInWithGoogleが呼ばれたことを確認 expect(loggedInCurrentUser.displayName, 'test_login_success');参考:https://qiita.com/mogmet/items/ade07bd842495192922dhttps://github.com/atn832/firebase_auth_mocks/blob/master/test/firebase_auth_mocks_test.darthttps://riverpod.dev/ja/docs/cookbooks/testing#%E3%83%97%E3%83%AD%E3%83%90%E3%82%A4%E3%83%80%E3%81%AE%E6%8C%99%E5%8B%95%E3%82%92%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%A9%E3%82%A4%E3%83%89%E3%81%99%E3%82%8B

インテグレーションテストについて

Flutter公式のドキュメントhttps://docs.flutter.dev/testing/integration-testsはじめに、テスト対象とするOSのバージョンとデバイスを決めてください。デバイスやOSバージョンの普及率などから、テスト範囲を決められるといいと思います。https://developer.apple.com/jp/support/app-store/packages/skimie/integration_test/test_setting.dart上記のファイルのコメントアウトを外すことで、テスト対象とすることができます。iOSの場合XcodeのインストールされているPCで行ってください。テストの流れ利用可能なエミュレータの情報を取得し、Mapに変換するテストに必要なデバイスがインストールされているかをチェックし、なければ追加するエミュレーターを起動し、テストするエミュレーターを終了し、キャッシュを削除する3. に戻り、テスト対象のOSバージョンとデバイスが終わるまで繰り返す1. 利用可能なエミュレータの情報を取得し、Mapに変換するfinal result = await Process.run('xcrun', ['simctl', 'list']);上記のコードで、使用可能なエミュレーターの一覧の文字列が取得できるの、正規表現を使って、Mapに変換する2. テストに必要なデバイスがインストールされているかをチェックし、なければ追加するtest_setting.dart でテスト対象にしているOSバージョンのデバイスが 1. で作成したMapに存在するかをチェックする。ない場合は、インストールする必要があるので、final result = await Process.run( 'xcrun', [ 'simctl', 'create', targetDevice, targetDevice, 'com.apple.CoreSimulator.SimRuntime.iOS-$major-$miner' ], );上記のコードで、追加する。3. エミュレーターを起動し、テストする// エミュレーター起動 await Process.run('xcrun', ['simctl', 'boot', device.id]);上記のコードで、エミュレーターを起動する。// テスト開始 final result = await Process.run( 'flutter', [ 'test', 'integration_test/app_test.dart', '--dart-define=FLAVOR=stg', '--dart-define=INTEGRATION_TEST=true', '-d', device.id, ], );上記のコードで、テストを開始する。packages/skimie/integration_test/app_test.dart に書かれているテストが実行される。4. エミュレーターを終了し、キャッシュを削除する// シミュレーター終了 print('Test Device Shut Down'); await Process.run('xcrun', ['simctl', 'shutdown', device.id]); // キャッシュの削除 await Process.run('xcrun', ['--kill-cache']);上記のコードで、エミュレーターを終了し、念のため、キャッシュを削除しておく。5. 3. に戻り、テスト対象のOSバージョンとデバイスが終わるまで繰り返す3〜5の操作をFor in で回しているので、指定されたテスト範囲が終了するまで、テストを回す。Androidの場合Android Studioをインストールしてある、PCで行ってください。iOSの場合とテストの流れは、大きくは変わりません。テストの流れ利用可能なエミュレータの情報を取得し、Mapに変換するテストに使用するPCのCPUアーキテクチャを確認しますテストに必要なデバイスがインストールされているかをチェックし、なければ追加する必要なシステムイメージがインストールされているかチェックする、なければ追加するエミュレーターを起動し、テストするエミュレーターを終了する。5. に戻り、テスト対象のOSバージョンとデバイスが終わるまで繰り返す1. 利用可能なエミュレータの情報を取得し、Mapに変換する final result = await Process.run('emulator', ['-list-avds']);上記のコードで、使用可能なエミュレーターの一覧の文字列が取得できるの、正規表現を使って、Mapに変換する2. テストに使用するPCのCPUアーキテクチャを確認します// CPUアーキテクチャを判別して、インストールするsystem imageを選択 Future<String> getSystemStringImagesType() async { if (Platform.isMacOS) { final result = await Process.run('sysctl', ['-n', 'machdep.cpu.brand_string']); if ((result.stdout as String).contains('Apple')) { return 'arm64-v8a'; } return 'x86_64'; } return 'x86_64'; }Widndowの場合は、'x86_64'になり、Macは、Apple Silicon(M1など)の場合があるので確認が必要。エミュレーターをインストールするときに指定する必要がある。3. テストに必要なデバイスがインストールされているかをチェックし、なければ追加するテストの対象になっているOSバージョンのエミュレーターがない場合は、インストールする。await Process.run( 'avdmanager', [ 'create', 'avd', '-n', id, '-k', 'system-images;android-$targetApi;google_apis_playstore;$systemImageType', '-d', targetDevice.deviceSetup, ], );上記のコードで、エミュレーターがインストールされる。4. 必要なシステムイメージがインストールされているかチェックする、なければ追加するテストの対象になっているシステムイメージのチェックをするfinal result = await Process.run('sdkmanager', ['--list']);上記のコードで、インストールされているシステムイメージのリストが取得できる。正規表現を使用して、文字列を加工し、テストに必要なシステムイメージがあれば、インストールする // テストに必要なsystem-imageをインストール for (final systemImage in filteredLines) { await Process.run('sdkmanager', [systemImage]); }5. エミュレーターを起動し、テストする// エミュレーター起動 final process = await Process.start( 'emulator', ['-avd', device.id, '-no-snapshot'], );上記のコードで、エミュレーターを起動。// テスト開始 final result = await Process.run( 'flutter', [ 'test', 'integration_test/app_test.dart', '--dart-define=FLAVOR=stg', '--dart-define=INTEGRATION_TEST=true', '--device-id', deviceId ], );上記のコードで、テストを開始する。packages/skimie/integration_test/app_test.dart に書かれているテストが実行される。6. エミュレーターを終了するawait Process.run( 'adb', ['-s', deviceId, 'emu', 'kill'], );上記のコードで、エミュレーターを終了。7. 5. に戻り、テスト対象のOSバージョンとデバイスが終わるまで繰り返す5〜6の操作をFor in で回しているので、指定されたテスト範囲が終了するまで、テストを回す。インテグレーションテストの追加繰り返しているテストは、packages/skimie/integration_test/app_test.dart に書かれているので、テストはこのファイルに追加する。intro_test や email_test の様にそれぞれのテストをディレクトリで分けて、追加していくと、可読性が良いかと思います。import 'package:authenticator/authenticator.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:integration_test/integration_test.dart'; import 'package:skimie/main.dart' as app; import 'login_page/email_login_test.dart' as email_login; import 'intro_page/intro_test.dart' as intro_test; // Test実行 // dart integration_test/test.dart // 実機やエミュレーターでテストする場合は、localeに気をつけてください。 // エミュレーターの場合、'en'になっていることが多いかと思います。 void main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('Integration Test', ( WidgetTester tester, ) async { // アプリ起動 await app.main(); await tester.pumpAndSettle(); // ログアウトしておく final container = ProviderContainer(); final authenticator = container.read(authenticatorProvider); await authenticator.signOut(); await tester.pumpAndSettle(); // イントロ画面の動作確認 await intro_test.test(tester); // Emailログインのテスト await email_login.test(tester); }); }

Firebase Authを用いた認証機能について

FirebaseAuthについては、丁寧なドキュメントがありますので、まずこちらを参考にしてみてください。https://firebase.google.com/docs/auth?hl=jaFlutterでの導入方法も説明されています。https://firebase.google.com/docs/auth/flutter/start?hl=ja各種認証に関しては、Fireabseのドキュメントにコード例も記載してあり、そのコードを利用するだけで、実装可能です。参考:https://zenn.dev/kazutxt/books/flutter_practice_introduction/viewer/30_chapter4_authenticationhttps://zenn.dev/flutteruniv_dev/articles/5f05c75b070b38https://zenn.dev/flutteruniv_dev/articles/25ae47164a1d44

SNS共有機能について

https://zenn.dev/taminaryosuke/articles/fda7ad009a6c69SNS共有機能は、上記の記事を参考にしました。

Flutter おすすめナレッジ

Flutter開発で便利なナレッジを紹介します。カラー色が少ない場合は、Flutterにデフォルトで用意されているClorsクラスから指定すればいいのですが、細かな指定がある場合(カラーコードなど)は、その都度調べて書いていては、間違う可能性もあり、何より面倒です。なので、アプリ全体で色を管理するクラスを始めに作成することが多いです。import 'dart:ui'; class AppColor { const AppColor._(); static const defaultBlack = black900; static const red = Color(0xFFE00000); static const white = Color(0xFFFFFFFF); static const black100 = Color(0xFFE6E6E6); static const black200 = Color(0xFFCCCCCC); static const black300 = Color(0xFFB3B3B3); static const black400 = Color(0xFF999999); static const black500 = Color(0xFF808080); static const black600 = Color(0xFF666666); static const black700 = Color(0xFF4D4D4D); static const black800 = Color(0xFF333333); static const black900 = Color(0xFF1A1A1A); }テキストスタイルフォントの指定や、テキストサイズの指定などは、アプリ内でそれほど多くの種類があるわけではないので、こちらもカラーと同じように、アプリ全体でテキストスタイルを管理するクラスを作成した方が間違うことがなく、統一感も出ると思います。下のように定義しても、適宜、修正することも可能です。import 'package:flutter/material.dart'; import 'app_color.dart'; class AppTextStyle { const AppTextStyle._(); static TextStyle get _base => const TextStyle( color: AppColor.defaultBlack,                 // フォントの指定がある場合は、ここに追加する ); static TextStyle get style8 => const TextStyle( fontSize: 8, height: 16 / 8, fontWeight: FontWeight.w300, ).merge(_base); static TextStyle get style12 => const TextStyle( fontSize: 12, height: 20 / 12, fontWeight: FontWeight.w400, ).merge(_base); static TextStyle get style16 => const TextStyle( fontSize: 16, height: 24 / 16, fontWeight: FontWeight.w400, ).merge(_base); static TextStyle get style20 => const TextStyle( fontSize: 20, height: 28 / 20, fontWeight: FontWeight.w500, ).merge(_base); } Text( num.toString(), style: AppTextStyle.style20.copyWith( color: AppColor.red, ), ),

in_app_purchase を用いたアプリ内課金について

アプリ内課金に使用しているパッケージhttps://pub.dev/packages/in_app_purchase実装例https://github.com/flutter/packages/blob/main/packages/in_app_purchase/in_app_purchase/example/lib/main.dartSkimieでは、課金のメソッドをNotifierProviderで管理しています。アプリ内課金を実装するためには、Google Play Console とApp Store Connect(Apple)の設定が必要です。それぞれのプラットフォームの設定は、Google :https://developer.android.com/google/play/billing/getting-ready?hl=jahttps://www.revenuecat.com/docs/android-productsApple:https://www.revenuecat.com/docs/ios-productsアプリ内課金の流れアプリ内購入が利用可能な状態なのか確認する購入品の製品IDのSetを用意する。それぞれのストアに問い合わせて製品情報を取得する上記で取得した製品情報を使って購入処理を行う購入処理進行を監視して、購入が完了した後にバックエンドでレシートの検証を行う検証が成功した場合、ジェムをユーザーに付与する(課金の対価を付与する)返ってきた検証結果に応じて、通知を行う購入のトランザクションを完了させるNotifierProviderで持つ状態と上記の順番に沿って説明を進めます。全体のコードを確認するには、/skimie/lib/services/app_purchase/app_purchase_provider.dartを参照してください。Stateとしては、下記の状態を定義しました。@freezed class AppPurchaseState with _$AppPurchaseState { const AppPurchaseState._(); const factory AppPurchaseState({ // 課金利用の可否 @Default(false) bool isAvailable, // バックエンドから取得した課金商品情報(productIdや購入品Image) Items? purchaseItems, // AppleやGoogleのプラットフォームから取得した課金商品の情報 @Default([]) List<ProductDetails> products, // 課金処理の状態 @Default(false) bool purchasePending, }) = _AppPurchaseState; ProductDetails? getProductDetail(Item item) { final productId = item.code; return products.firstWhereOrNull((e) => e.id == productId); } }purchasePending 以外は、全てAPIから取得した値を定義しています。getProductDetailsのメソッドは、OpenAPIで定義されたItemの型の中にある製品IDに該当するproductDetailsをリストの中から取得するメソッドです。単回購入処理のときに使います。1. アプリ内購入が利用可能な状態なのか確認する // アプリ内購入が利用できるかを確認 final isAvailable = await _inAppPurchase.isAvailable(); // アプリ内購入が利用できない場合 if (!isAvailable) { Log.e('アプリ内課金が利用できません。'); state = state.copyWith( isAvailable: isAvailable, ); return; }ストアにアクセスできない、デバイスがGoogleにログインしていないなど、利用不可の場合は、この先の処理には進むことはないです。2. 購入品の製品IDのSetを用意する。 // 課金商品の情報を取得 final purchaseItems = await _purchaseService.getProductIds(); final productIds = purchaseItems.items.map((p0) => p0.code).toSet();バックエンドから、購入品のItemsのデータを取得し、製品IDのSetを作成します。3. それぞれのストアに問い合わせて製品情報を取得する // 課金処理に使用する商品一覧を取得 final response = await _inAppPurchase .queryProductDetails({...productIds, 'android.test.purchased'});このAPIは、1. で作成した製品IDに該当する購入品の情報を Google Play Console や App Store Connect から List<ProductDetails> という型で取得することができます。ProductDetailsからは、購入品のid や title、description、price などの情報が取得できます。4. 上記で取得した製品情報を使って購入処理を行うこの処理はUI側で行われる処理です。 // アプリ内購入品のリスト List<Widget> purchaseItemList = []; final purchaseItems = ref.watch(appPurchaseServiceProvider .select((s) => s.purchaseItems?.items.toList())) ?? []; final isAvailable = ref.watch(appPurchaseServiceProvider.select((s) => s.isAvailable)); for (final item in purchaseItems) { final product = ref.watch(appPurchaseServiceProvider).getProductDetail(item); if (product == null) continue; purchaseItemList.add( ActionBtn( label: "", disabled: !isAvailable, onPressed: () async { await ref.read(appPurchaseServiceProvider.notifier).buyProduct( context, product: product, ); }, color: Colors.amber, width: (width - marginValue * 2 - 80) / 3, height: (width - marginValue * 2 - 80) / 3, image: ProductImage( item: item, displayCount: product.title.getNumInTarget, ), ), ); }appPurchaseServiceProviderのStateから、purchaseItemsを取得し、for in で回します。各item に該当するProductDetailsを取得して、notifierのbuyProductに渡します。AppPurchaseServiceクラス内の単回購入のメソッド、 // 単回購入 Future<void> buyProduct( BuildContext context, { required ProductDetails product, }) async { _context = context; state = state.copyWith(purchasePending: true); final purchaseParam = PurchaseParam(productDetails: product); await _inAppPurchase.buyConsumable( purchaseParam: purchaseParam, autoConsume: _kAutoConsume, ); }各プラットフォームのアプリ内購入が実行されます。※ autoConsume について、デフォルトがtrueになっており、消耗型の購入は、購入と同時にトランザクションが閉じられます。https://techblog.booklista.co.jp/entry/2023/09/29/130633#%E8%B3%BC%E5%85%A5%E5%87%A6%E7%90%86%E3%81%AE%E6%9B%B4%E6%96%B0%E3%82%92listen%E3%81%99%E3%82%8B:~:text=%E3%81%99%E3%81%B9%E3%81%8D%E7%82%B9-,Android,-%E3%81%A7%E3%81%AF%E8%B3%BC%E5%85%A5%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A05. 購入処理進行を監視して、購入が完了した後にバックエンドでレシートの検証を行う /// 購入の処理を監視する Future<void> listenPurchaseUpdated() async { final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream; _subs = purchaseUpdated.listen( (List<PurchaseDetails> purchaseDetailsList) { // 購入などが行われた場合の処理 _listenToPurchaseUpdated(purchaseDetailsList); }, onError: (Object error) { Log.e('Error: ${error.toString()}'); errorNotification(msg: _l10n.errorPurchase); }, ); }Streamにて、購入が行われるとpurchaseDetailsList が自動で返される処理になります。返されたpurchaseDetailsListを_listenToPurchaseUpdatedに渡して、検証する流れになります。 // 購入処理が行われた場合にそのトランザクションを処理する void _listenToPurchaseUpdated( List<PurchaseDetails> purchaseDetailsList, ) { purchaseDetailsList.forEach( (PurchaseDetails purchaseDetails) async { if (purchaseDetails.status == PurchaseStatus.pending) { state = state.copyWith(purchasePending: true); } else { // 購入処理がエラーの場合 if (purchaseDetails.status == PurchaseStatus.error) { Log.e('Error: ${purchaseDetails.error}'); errorNotification(msg: _l10n.errorPurchase); } // 購入がキャンセルされた場合 if (purchaseDetails.status == PurchaseStatus.canceled) { successNotification(msg: _l10n.canceledPurchase); } // 購入処理が完了、または復元された場合 if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { // レシートの検証をする final receipt = purchaseDetails.verificationData.localVerificationData; final valid = await _purchaseService.postPurchaseReceipt( receipt: base64Encode(utf8.encode(json.encode(receipt))), itemMasterCode: purchaseDetails.productID, ); if (valid) { // レシートの検証が成功時の処理 successNotification(); // 購入完了の処理 if (purchaseDetails.pendingCompletePurchase) { await _inAppPurchase.completePurchase(purchaseDetails); } } else { // レシートの検証が失敗時の処理 errorNotification(msg: _l10n.errorPurchase); } state = state.copyWith(purchasePending: false); } } }, ); }受け取った、purchaseDetailsListは、forEachで回され、個別の状態に応じて処理をする。purchaseDetails.status == PurchaseStatus.purchased購入済みの状態であれば、  // レシートの検証をする   final receipt = purchaseDetails.verificationData.localVerificationData;レシートを取得することができるので、バックエンドに渡して、レシート検証をしてもらう。6. 検証が成功した場合、ジェムをユーザーに付与する(課金の対価を付与する)バックエンドの処理。7. 返ってきた検証結果に応じて、通知を行うレシート検証から返ってきた値(valid)によって、成功、失敗の通知を行う。8. 購入のトランザクションを完了させる // 購入完了の処理 if (purchaseDetails.pendingCompletePurchase) { await _inAppPurchase.completePurchase(purchaseDetails); }トランザクションを完了していないpurchaseDetailsは、完了の処理を行う。バックエンドの処理が失敗した時の対策https://techblog.booklista.co.jp/entry/2023/09/29/130633こちらの記事で紹介されている方法で実装。 // 未完了のレシートを検出して再検証する Future<void> reCheckPendingTransaction() async { // 未完了のレシートを格納するリスト final List<PurchaseDetails> pendingReceiptList = []; // iOSの場合 if (Platform.isIOS) { final paymentQueueWrapper = SKPaymentQueueWrapper(); final transactions = await paymentQueueWrapper.transactions(); for (final transaction in transactions) { // TODO:transactionから未完了のPurchaseDetailsを取得する } } // Androidの場合 if (Platform.isAndroid) { final androidAddition = _inAppPurchase .getPlatformAddition<InAppPurchaseAndroidPlatformAddition>(); // 過去の購入履歴を取得 final response = await androidAddition.queryPastPurchases(); for (final purchaseDetails in response.pastPurchases) { if (purchaseDetails.pendingCompletePurchase) { pendingReceiptList.add(purchaseDetails); } } } // 未完了の購入トランザクションがあったときにはレシートを再検証する if (pendingReceiptList.isNotEmpty) { pendingReceiptList.forEach( (PurchaseDetails purchaseDetails) async { // 購入処理が完了、または復元された場合 if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { // レシートの検証をする final receipt = purchaseDetails.verificationData.localVerificationData; final valid = await _purchaseService.postPurchaseReceipt( receipt: base64Encode(utf8.encode(json.encode(receipt))), itemMasterCode: purchaseDetails.productID, ); if (valid) { // 購入完了の処理 if (purchaseDetails.pendingCompletePurchase) { await _inAppPurchase.completePurchase(purchaseDetails); } } } }, ); } }リチェックの処理で参考になるレポジトリ:https://github.com/erase2004/InstantCoffee/blob/9278973eb13f70e04653076a2cf651dc13b0cce5/lib/helpers/iap_subscription_helper.dart#L33

Firebase Cloud Messaging について

Push通知を実装するために、Firebase Cloud Messaging が使われています。https://firebase.google.com/docs/cloud-messaging?hl=jaFlutterのドキュメントhttps://firebase.google.com/docs/cloud-messaging/flutter/client?hl=jaPush通知の実装では、主に4つの機能を実装する必要があります。通知の権限許可をとるFCMトークンを取得するフォアグラウンドでの通知を監視するバックグラウンドでの通知を監視するSkimieでは、Push通知関連の機能をStateNotifierで管理しています。packages/skimie/lib/services/messaging/messaging_provider.dart参考にしたレポジトリ:https://github.com/altive/flutter_app_template/tree/main/packages1. 通知の権限許可をとる /// 通知権限の確認 Future<void> requestPermission() async { // Androidの場合 if (Platform.isAndroid) { await _flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>()! .requestNotificationsPermission(); } // IOSの場合 if (Platform.isIOS) { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { return _messaging.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true, ); }); } // stateの更新 await refetchSettings(); }通知の権限を確認します。2. FCMトークンを取得する // デバイスに一意のFCMトークンを登録する // - ログイン後の画面で実施すること // - idToken は変わる場合があるらしいため、ウォッチして変わったら更新すること Future<void> saveFcmToken() async { final fcmToken = await _messaging.getToken(); Log.i('FCM Token: $fcmToken'); if (fcmToken == null) return; try { final deviceService = DeviceService(); await deviceService.save(fcmToken); } catch (e) { Log.e(e.toString()); } } // FCMトークンの変更を監視する void tokenRefreshListen() { final deviceService = DeviceService(); try { _tokenStream = _messaging.onTokenRefresh.listen( (fcmToken) async { await deviceService.save(fcmToken); }, ); } catch (e) { Log.e(e.toString()); } }FCMトークンを取得します。このトークンは変更される可能性があるので、変更を監視し、変更があった場合には、再度登録し直す様にしています。3. フォアグラウンドでの通知を監視する /// 通知を監視する Future<void> notificationListen(BuildContext context) async { FirebaseMessaging.onMessage.listen( (RemoteMessage message) { final text = message.notification?.body ?? ''; showNotificationToast( context, text: text, onPressed: () { debugPrint("OnPressed."); }, ); }, ); }フォアグラウンドでの、通知を監視している。通知を受け取った場合にlisten 以下が発火し、showNotificationToastに、通知のメッセージを渡して、実行している。4. バックグラウンドでの通知を監視する /// バックグランド設定 void onBackgroundMessage() { FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); } // バックグランドで受け取った通知の処理 @pragma('vm:entry-point') Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { final notification = message.notification; Log.i('PUSH Message(Back Ground): ${notification?.body}'); }アプリが閉じている時の通知を監視している。1〜4の処理は、通知を初期化する関数で、実行されるようになっている。プラットフォーム側の設定https://zenn.dev/flutteruniv_dev/articles/flutter_push_notification?redirected=1(古くなっている可能性もあります、最新を確認してください。)通知アイコンの変更https://qiita.com/mkosuke/items/45dd14c61b633efcfd17https://firebase.google.com/docs/cloud-messaging/android/topic-messaging?hl=ja#edit-the-app-manifest

Flutterの状態管理 riverpod について

参考になる資料としては、有料ですが、https://zenn.dev/riscait/books/flutter-riverpod-practical-introductionこちらの本が、ほぼ全てが網羅されています。Skimieでよく使われているのが、Provider と FutureProviderになります。あと、NotifierProvider (StateNotifierProviderとも呼ばれる)は、一番多用されるものになるので、最後に説明します。Provider(基本的には) 変更されない値(インスタンス)を持つことができます。final firebaseAuthProvider = Provider((ref) => FirebaseAuth.instance);上記のコードでは、FirebaseAuthクラスのインスタンスを持つProviderを作成しています。riverpod_generatorでは、省略形で書くことができます。(このナレッジでは省略形は書きません。)Providerは、単体で使うこともありますが、他のProviderと組み合わせることもできます。final authenticatorProvider = Provider<Authenticator>((ref) {   return Authenticator(     auth: ref.watch(firebaseAuthProvider),     appleAuthenticator: ref.watch(appleAuthenticatorProvider),     googleAuthenticator: ref.watch(googleAuthenticatorProvider),   ); });上記のコードのように、先ほどのFirebaseAuthのインスタンスのProviderを別のProviderの中で呼び出すことができます。FutureProvider変更されない値を非同期的に処理することができるProviderです。非同期処理を、Loading / error / data と処理の進行と結果に応じて返す値を変えてくれます。APIを叩く時や時間がかかる処理の時に、使われるProviderです。final resourceProvider = FutureProvider<Resources>((ref) async { return ResourceService().find(); })UIで使用するときには、final state = ref.watch(eventPageNotifierProvider); state.when( // ローディング時 loading: () { return const LoadingIndicator(); }, // エラー時 error: (error, __) { Log.e(error.toString()); // リロードアイコン return Container( margin: const EdgeInsets.all(kSkimieMargin), child: ReloadApiBtn( onPressed: () => ref.read(eventPageNotifierProvider.notifier).refresh(), ), ); },                 // データ取得時 data: (state) { final padding = MediaQuery.of(context).padding; final activeTab = state.activeTabType; List<Widget> children = [];非同期の処理中の状態に応じてUIを変えることができる。NotifierProviderState(状態)と、それを操作するNotifierを持つProviderです。非同期な初期化をする場合は、AsyncNotifierProviderを使用することもあります。状態を定義しますimport 'package:freezed_annotation/freezed_annotation.dart'; part 'state.freezed.dart'; @freezed class HomePageState with _$HomePageState { const factory HomePageState({ @Default(0) int num, }) = _HomePageState; }Freezedを使います。Freezed + StateNotifier はセットで使います。https://pub.dev/packages/freezedこの状態を持ったStateNotifierを継承したクラスを定義します。class HomePageNotifier extends StateNotifier<HomePageState> { HomePageNotifier() : super(const HomePageState()); void plus() async { state = state.copyWith(num: state.num + 1); } void minus() async { if ((state.num - 1) < 0) return; state = state.copyWith(num: state.num - 1); } }クラス内に、Stateを操作するメソッドを追加することができます。このクラスをAutoDisposeStateNotifierProviderに持たせます。final homePageNotifierProvider = AutoDisposeStateNotifierProvider<HomePageNotifier, HomePageState>((ref) { return HomePageNotifier(); });NotifierProviderは、使い方によって、autodisposeのあるなしを決めた方がいいです。あるUIのViewModelとして、使用する場合は、autodisposeをつけることで、参照されなくなったときに自動でインスタンスを破棄してくれます。autodisposeをつけない(一度参照されるとアプリを停止するまで、存在し続ける)例としては、user情報を保持するものや、Skimieだとジェムやコインの状態を保持するProviderは、アプリが動いている間は、いろいろなページで参照される可能性があるので、破棄しない方針の方が良いと思われます。レポジトリーを参照:https://github.com/HI-0123/riverpod_explanation/tree/main

人気ボードゲームが漫画化『ボルカノス』サンデーうぇぶりで連載開始!

ボードゲームが他のメディアに進出するというと、デジタルゲーム化がイメージしやすいかなと思います。小説化したり舞台化したのもあったりします。今回ご紹介するのは漫画化の話題です。「Kaiju on the Earth」シリーズの『ボルカノス』が漫画化、連載開始しました。電ファミニコゲーマーで記事が出ていますので、まずはそちらをどうぞ。「怪獣映画の登場人物になったかのような体験ができる」人気ボードゲーム『ボルカルス』マンガ版の連載がスタート。謎めいた第1話で今後の展開が気になるhttps://news.denfaminicogamer.jp/news/231225c『ボルカノス』どんなゲーム?漫画の内容の前に原作の『ボルカノス』がどんなゲームかご紹介。アークライトとドロッセルマイヤーズによって共同企画された怪獣戦略ボードゲームシリーズ「Kaiju on the Earth」の一作目が『ボルカノス』です。富士火口に突如現れた全身から溶岩を噴出する怪獣「ボルカノス」に対し日本政府は「怪獣災害緊急対策本部」を設置。市民の避難、火災消火、自衛隊を配備しての防衛を行いながら怪獣を調査し撃退方法を探します。1人の怪獣役と1~3人の人間チームとに分かれて遊ぶ非対称対戦ゲームとなっており、怪獣は破壊、人間チームは防衛によって獲得できるポイントを競います。そのターンの行動を事前にカード伏せて計画してから実行に移すという進行をするので、怪獣映画の作戦会議シーンのような会話が自然に発生するのが楽しい戦略ゲームです。Kaiju on the Earth 公式サイトhttps://kaijuontheearth.com/vulcanus/漫画ではどうなるの?ボードゲーム『ボルカノス』は「怪獣が出現!街を守れ!」というのがゲーム本編で、怪獣映画などで言われる「人間ドラマパート」のようなストーリーラインはありません。つまり、オリジナルストーリーになりますね。公開済みの第1話では、幼少の頃からボードゲームを作って今では売れないクリエイターとなった主人公が、友人の結婚式の帰りに渋谷で幼馴染と再会。そこに突如現れるボルカノスですが、幼馴染はその存在をなぜか事前に察知しており……といった具合で、謎めいたストーリーを予感させます。個人的には怪獣VS自衛隊みたいな漫画を創造していたので、第1話のラストには「こう来るの!?」と驚かされました。さりげなく描かれているボードゲームや行動の予測など、今後の展開によっては細かな要素で原作の雰囲気を出してきそうなところも気になりますね。サンデーうぇぶり『Kaiju on the Earth ボルカルス』https://www.sunday-webry.com/episode/14079602755571963643

鉄のリサイクルをボードゲームに『鉄の転生すごろく』本日22時に「積分サークル」とのコラボ動画公開

今回はちょっと変わったボードゲームの話題です。鉄のリサイクルをモチーフにしたボードゲーム『鉄の転生すごろく』が、株式会社カヤック、一般社団法人日本鉄鋼連盟、株式会社デイリースポーツ案内広告社がタッグによって制作されました。プロモーションの一環として、理数系の企画動画で人気のYouTubeチーム「積分サークル」とコラボした動画が本日22日19時30分に公開されるとのことです。PR TIMESにプレスリリース記事が出ていますのでまずはそちらをどうぞ。リサイクルのループを表現したボードゲーム「鉄の転生すごろく」誕生!面白法人カヤックが(一社)日本鉄鋼連盟とタッグを組み、遊びながら鉄のリサイクル優位性を啓蒙するアイテムを製作https://prtimes.jp/main/html/rd/p/000000694.000014685.html『鉄の転生すごろく』どんなゲーム?『鉄の転生すごろく』は、鉄がリサイクルによってさまざまな製品に生まれ変わることを表現したすごろくゲームです。各プレイヤーはゲーム開始時に目標とする鉄製品をカードでランダムに決めます。「∞」の形のコースをサイコロを振って進み、イベントマスに止まったらカードを引いて様々な効果を受けます。転生マスにたどり着くと転生カードを引き、目標の鉄製品に転生できれば勝利となります。転生した製品ごとにコマが用意されており、最初のコマであるスチール缶を含めて8種類のコマがあります。鉄は磁石を使えば選別しやすく、リサイクル率が高い素材であるという特徴を表現したゲームになっています。プレイ動画が積分サークルYouTubeで22日19時30分より公開されるので、ルールが気になる方はそちらもどうぞ。「鉄の転生すごろく」紹介映像https://www.youtube.com/watch?v=kiHnZ4XFqQY積分サークルYouTube(「鉄の転生すごろく」プレイ動画) https://youtu.be/aG1Oi4obHDoイベントや期間限定で遊べる。かも『鉄の転生すごろく』はすごろくのコースもコマもサイコロも鉄を使っているようで、一般販売はされないものと思われます。現在は一般社団法人日本鉄鋼連盟で1体のみ保管しているとのこと。一般人では触れないボドゲという訳ですが、その辺りは今後普及に取り組んでいくそうです。まだ具体的な情報は出ていませんが、将来的にはイベントや期間限定で出張して遊べるようにする予定とのことなので、一点ものの珍しいボドゲに触ってみたい方はサイトをチェックしてみてはいかがでしょうか。鉄くる(JISF) Webサイト(「鉄の転生すごろく」紹介) https://tetsukuru.com/sugoroku/

磁石を使った人気ゲーム『Kluster(クラスター)』がリニューアル+パワーアップ!『Kluster DUO(クラスター・デュオ)』先行予約開始

皆さんは、Amazonなどで磁石を使ったオモチャが規制されたという話を聞いたことがありますか?小さなボール状の磁石をくっつけて様々な形を作れるオモチャがあったのですが、なんでもこれを子供が誤飲した場合が厄介で、お腹の中で内蔵を挟んだ状態でくっついて取り出すために手術が必要になってしまうとか。そんな訳で磁石の取り扱いに制限がかかったらしいです。この影響はボードゲームにも出ており、紐で囲われたエリアの中に磁石を置いていくアクションゲーム『Kluster(クラスター)』が日本では販売禁止となっていました。そんな『Kluster(クラスター)』ですがこのたび誤飲対策に加えたパワーアップ版『Kluster DUO(クラスター・デュオ)』として販売再開するとのことです。PR TIMESにプレスリリース記事が出ていますのでまずはそちらをどうぞ。人気マグネットアクション・ボードゲーム「Kluster(クラスター)」が新たな魅力を追加しパワーアップして登場!https://prtimes.jp/main/html/rd/p/000000004.000060111.html『Kluster DUO(クラスター・デュオ)』どんなゲーム?『Kluster DUO』は『Kluster』同様に紐と磁石を使用したアクションゲームです。紐で囲われたエリア内に磁石を置いていき、手持ちを全て置けたら勝利というシンプルなゲームです。ただし、磁石同士がくっついてしまうとくっついた分は手持ちに加わってしまいます。ちなみに紐を動かしてエリアを変形させたり、既に置かれている磁石を移動させるというテクニックもアリらしいです。『DIO』では磁石の形が変更されて、釣りの浮きのような細長い楕円形になりました。この変更は最初に書いたように誤飲対策が理由のようです。ただ、この変更によって予測しづらい転がり方をするため、ゲームがさらにスリリングで戦略的になったとのこと。怪我の功名ですね。『Kluster(クラスター)』と混ぜても遊べる旧バージョンの『Kluster(クラスター)』を持っていれば、2つのバージョンを混ぜて遊ぶこともできるようです。異なる大きさ、異なる形の磁石が混ざった状態で遊ぶのでより一層複雑で奥深いプレイが可能になります。現在公式ショップにて予約販売が開始されており、2024年1月末までに手元に届く予定です。旧バージョンにハマったファンは新バージョンもゲットしたいところですね。販売サイトhttps://www.r-enterprise.jp/products/kluster-duo

”良い感じ”なデジタルゲームの話をしよう『Undying』

”良い感じ”なデジタルゲームの話をしよう第35回『Undying』ゲームの情報って発売決定とか開発決定って瞬間にワーっと流れるんですが、リリースまで間があると忘れてます。100%忘れます。そりゃ夏くらいに「発売は冬!」みたいに言われて半年近く期待を維持できる人の方が少ないでしょ。つまり、ニンテンドーダイレクト方式ができるニンテンドーは強い。という話ではなく、だいぶ前にゲームのコンセプトが発表されて「お、良いじゃん」と思ってたゲームが今月リリースされていたのでその話をしたいと思います。Steam『Undying』https://store.steampowered.com/app/638990/_/ゾンビに噛まれた母。息子に生きる術を託す今回は『Undying』です。ゾンビが蔓延るようになってしまった世界で一組の母子が辿る運命を描くストーリーです。プレイヤーは母親として息子を守りながら、彼に生き残る術を教えていきますが残された時間は長くありません。なぜならすでに自分はゾンビに噛まれてしまっているから。という、非常に追い詰められた状況のゲームです。ゾンビに囲まれて死んでもリスポーンできる雰囲気ではないですね。このコンセプトが息苦しくも母の強さも見えて良い緊張感がありますね。ゾンビ化するまでの時間を何とか遅らせつつサバイバル生活をする中、息子は資源の収集方法、戦い方、料理など生きる術を学んでいきます。早期アクセスが2021年から続いていましたが、正式リリースに伴いストーリー部分が追加されたようなので、個人的に一番楽しみな部分が揃ったという感じです。現在ローカライズに難ありただ、直近のレビューは不評気味。ゲーム性に関する不満では若干操作が重たいという声もあるようですが、それ以上に目立っているが日本語ローカライズの低品質さへの不満です。恐らく機械翻訳だろうと見られ、キャラクターの一人称が不安定だったり情感がなかったりと、ストーリーに魅力を感じて手にした人には致命的な状態のようです。来年春にはswitch版がリリースされる予定らしいので、それに向けて日本語版アップデートが行われるかも知れません。という訳で、日本語が直ってから評判を見て買おうかな……来年春か、忘れてそうだなー。

新作マーダーミステリー『赤ずきん、舞踏会で死体と出会う。』本日発売!ゲームマーケット2023秋で先行販売・完売した作品が正式リリース

今回は数年前にブームが来て以来、根強い人気を獲得しているマーダーミステリージャンルの新作の話題です。ボードゲーム・アナログゲームというとちょっと趣が違うと感じる人もいそうですが、ゲムマでも1つのジャンルとして売られています。本日リリースされる新作マーダーミステリー『赤ずきん、舞踏会で死体と出会う。』はゲムマ2023秋で先行販売され、完売した作品です。正式リリースに合わせてPR TIMESでプレスリリース記事が出ていますので、まずはそちらをどうぞ。ゲームマーケットにて完売したマーダーミステリーゲーム『赤ずきん、舞踏会で死体と出会う。』(双葉社)発売開始!https://prtimes.jp/main/html/rd/p/000000420.000014531.html『赤ずきん、舞踏会で死体と出会う。』どんなマダミス?『赤ずきん、舞踏会で死体と出会う。』はNetflixで実写映画化もされたミステリー作品です。今回ご紹介するマーダーミステリー版は原作小説の第一章を原案にしたオリジナルストーリーとのこと。小説版第一章『ガラスの靴の共犯者』で犯人を無事捕まえた赤ずきんは、その功績を認められ王子様に舞踏会へ招待されます。貴族、町の住人、使用人など多くの人が集まった舞踏会の日、城内で庭師の死体が見つかります。探偵として実績のある赤ずきんは、4人の容疑者たちと協力して犯人探しを行うことに。この4人の協力者というのをプレイヤーが担当するようです。詳しくは、公式サイトをご参照ください。『赤ずきん、舞踏会で死体と出会う。』公式サイトhttps://www.futabasha.co.jp/introduction/aoyagi/madamisu.html協力型シナリオも収録本作にはボーナスコンテンツとしてwebゲーム『ガラスの靴の共犯者』を楽しめる二次元コードが封入されているそうです。原作小説の第一章をモチーフにした内容のようで、こちらは協力型のゲームとなっています。マーダーミステリーと言えば、プレイヤーの中に犯人がいて秘密を探り合って、というのが一般的なイメージかと思います。実際は色々なモノがあって、中にはNPCが犯人だったり、犯人が存在しなかったりといった推理小説だったら怒られそうな変化球もあります。プレイヤー同士でコミュニケーションを取って謎に近づく部分がゲームの肝なので、そういうのもアリなんでしょうね。面白ければ何でもアリです。初心者も楽しめる難易度で原作未読でも大丈夫とのことなので、初めてマダミスをする原作ファンも原作は知らないけ童話モチーフのマダミスに興味が沸いた方にも安心ですね。

真四角で大容量!拡張機能付き「ボードゲーム専用多機能バッグ」クラウドファンディング実施中!

今回はボードゲーム関連のグッズのご紹介です。ボードゲームを持って出かける機会がある人は共感できそうな悩みですが、ボドゲは嵩張るんですよね。特に重たいゲームは箱がデカい。そんな大きな箱もまとめて持ち運べる大容量のバッグがクラウドファンディングで開発中とのこと。Gamerで紹介記事が出ていますので、まずはそちらをどうぞ。ボードゲーム専用バッグ「GAMER'S BAG」のクラウドファンディングがMakuakeで実施中!https://www.gamer.ne.jp/news/202312180042/大容量多機能 Gamer’sBagGamer’sBagは幅35.5cm、高さ56cmの大型バッグです。厚みは通常状態で20cm、一周ぐるっと取り付けられたファスナーを開くと35.5cmに拡張できます。ボードゲームの「大箱」と呼ばれるサイズが30cm×30cmくらいらしいので、このバッグのサイズだと余裕を持って入れられそうです。リュックスタイルでもショルダースタイルでも手提げでも使える他、キャリーバッグのハンドルを入れられるスリットもあるのでサークル参加する際の荷物搬入にも良さそうですね。小物入れが充実していたり、内部に衝撃対策のパットが入っていたり、防水仕様になっているなど細かな気遣いも嬉しい所。ボドゲに限らず、書籍を入れても良さそうですし、レジャーなどにも使えそうです。Makuake 圧倒的な収納力!より充実したボードゲームライフを。多機能なボードゲーム専用バッグhttps://www.makuake.com/project/gamers_bag/プロジェクト期間は来年1月末まで現在このバッグのプロジェクトは達成率82%で、未達成となっています。とはいえ、あと1人か2人参加したら達成できそう。プロジェクトの期間は1月30日までで、まだ時間はありますが気になった方はお早めにどうぞ。現在クラウドファンディング中のプランの中で最も安い、超早割プランなら23,990円。大容量バッグが欲しい方は検討してみてはいかがでしょうか。

「月が綺麗ですね」お題を手札で粋に翻訳『超翻訳ゲーム アイラブユーなんてゆー?』発売中!

夏目漱石が「I Love you」を何と和訳したらいいか聞かれて「月が綺麗ですね」とでも訳しなさいと答えた、というエピソードを聞いたことはありますか?このエピソード自体は後世の創作らしいですが、それはそれとしてなかなか粋な訳ですよね。今回ご紹介するのはそんな気の利いた和訳に挑戦する大喜利系のゲームです。タイトルは『超翻訳ゲーム アイラブユーなんてゆー?』。12月6日に販売され、全国の書店・玩具店・雑貨店にて販売中です。PR TIMESに記事が出ていますのでまずはそちらをどうぞ。言葉あそび×推理。お題の英文を、限られた手札を組み合わせて伝えよう!『超翻訳ゲーム アイラブユーなんてゆー?』12/6発売!!https://prtimes.jp/main/html/rd/p/000000115.000007567.html『超翻訳ゲーム アイラブユーなんてゆー?』『超翻訳ゲーム アイラブユーなんてゆー?』はお題の英文カードを各プレイヤーが6枚の日本語カードを組み合わせて和訳し、もっとも素晴らしい訳を出せた人が得点する大喜利系のゲームです。日本語カードには複数の単語が書かれており、どれを使うかで選択の幅があります。「わくわく」「因果」「喜び勇んで」「カブトムシ」「神」など使い所が難しそうな単語も含め、100単語が収録されています。お題となる英語カードは「I love you」や「You look like a million dollars today」などロマンティックな物や、「Help me」「Let me work here, please!」などシチュエーションが気になる物も含まれています。ちなみに英文が分からなくても翻訳例が書かれており、使われる場面についても補足があるとのことで遊びやすい工夫もされています。販売元は幻冬舎言葉を活かした大喜利系のゲームということでパーティーゲーム好きな方は好みにハマりそうですね。発売元の幻冬舎からは『はぁって言うゲーム』や 『57577 ゴーシチゴーシチシチ』など、遊びやすいボードゲームがいくつも出ていますので、気になった方は他のゲームも調べてみてはいかがでしょうか。

プレイヤーは敵側!?『呪術廻戦 呪霊逃走 -渋谷事変』人気漫画のボドゲが2024年春に発売

年の瀬も迫ってきて、もう新作ボードゲームの話題も来年の話が多くなっていますね。クリスマスセールが近づけばまた変わるかも知れませんけど。さて、今回は来年春に販売予定の新作ボードゲームの話題です。モチーフになっているのは週刊少年ジャンプの人気漫画『呪術廻戦』。人間の負の感情から生まれた呪霊を呪術で祓う呪術師の闘いを描いたバトル漫画で、独特の重たい雰囲気を纏ったダークファンタジーです。漫才したりもしてますが。このボドゲ、目を惹くポイントはプレイヤーが作中の敵側「呪霊」として、最強格の呪術師「五条悟」から逃げ回りながら目的を達成しようとするという所。4Gamerに記事が出ていますので、まずはそちらをどうぞ。呪霊となって五条 悟から逃げ回れ! ボードゲーム「呪術廻戦 呪霊逃走 -渋谷事変」2024年春発売https://www.4gamer.net/games/999/G999905/20231214059/『呪術廻戦 呪霊逃走 -渋谷事変』どんなゲーム?【特報】マンガボドゲ 呪術廻戦 呪霊逃走 -渋谷事変- 発売決定PVhttps://www.youtube.com/watch?v=6QO0UWh7wBg『呪術廻戦 呪霊逃走 -渋谷事変』は原作漫画『呪術廻戦』のエピソード「渋谷事変」をモチーフにしたボードゲームです。このエピソードでは特級呪詛師・夏油傑と特級呪霊(漏瑚、花御、真人)たちが首謀した呪術を用いたテロにより、渋谷周辺の半径400mが「帳」と呼ばれる結界で閉じられ、多数の一般人が犠牲になるという大事件が描かれます。この事件で「五条悟」も現場に登場し、最強の名に恥じない……というか、もう不条理を感じるレベルの強さを見せつけます。『呪術廻戦 呪霊逃走 -渋谷事変』の詳しいルールについてはまだ未公開のようですが、プレイヤーは特級呪霊として五条悟から逃げ回りながら目的を達成するというコンセプトは明らかにされています。原作を知らずとも、倒すとか戦うとかの次元にいない相手だということが伝わってきますよね。ステージギミックみたいな理不尽な強さの五条悟を相手に立ち回る戦略的なゲームになるようです。先行体験会来年春発売予定とのことですが、4月13日には世界最速体験会が開かれるようです。詳しい情報は以下の通り、元記事より転記します。○開催日:2024年4月13日(土) 予定 ○日時:11時~、13時~、15時~、17時~(各回90分程度) 予定○場所:東京都渋谷某所(当選者のみに通知)○料金:無料○主催:株式会社集英社ゲームズ○応募締切:12月28日23時59分 予定○参加応募フォーム: https://docs.google.com/forms/d/e/1FAIpQLSeBAqFHcVoBdwfMdB99-qqag1Y2qPSDkJBc34aN5kXBGnpXBQ/viewform