チャプター1:クリッカーゲームを作る

このプロジェクトでは、クリッカーゲーム を組み合わせて、Odoo と完全に統合します。 このゲームでは、多数のクリックを蓄積し、システムを自動化することが目標です。 興味深い点は、私たちの遊び場としてOdooユーザーインターフェイスを使用することです。 例えば、ウェブクライアントのランダムな部分でボーナスを非表示にします。

始めるには、実行中の Odoo サーバーと開発環境が必要です。 練習問題に入る前に、この :ref:`tutorial <tutorials/master_odoo_web_framework/setup> `で説明されているすべてのステップに従っていることを確認してください。

目標

../../../_images/final.png

章の各演習の解決策は、 official Odoo tutorials repository にホストされています。

1. システムのアイテムを作成

開始するには、systray にカウンタを表示します。

  1. clicker_systray_item.js (および xml) ファイルを hello ワールドコンポーネントとして作成します。

  2. systrayレジストリに登録し、それが表示されていることを確認してください。

  3. 次の文字列を表示するようにアイテムの内容を更新します: Clicks: 0 をクリックして、値を増分するボタンを追加します。

../../../_images/systray.png

そして、出来上がり、私たちは完全に動作するクリッカーゲームを持っています!

2. 外部クリック数をカウント

まあ、正直言って、それはまだあまり楽しいことではありません。 そこで、新しい機能を追加しましょう。ユーザーインターフェイス内のすべてのクリックをカウントします。 ユーザーはできるだけOdooを使用するようにインセンティブを与えられます! しかし、明らかに、メインカウンター上の意図的なクリックはまだよりカウントする必要があります。

  1. document.body をすべてクリックするのをリッスンするには、useExternalListener を使用します。

  2. これらのクリックごとにカウンタ値を1ずつ増やす必要があります。

  3. カウンターをクリックするたびに値が10増加するようにコードを変更します

  4. カウンターをクリックすると、値が11増えないことを確認してください!

  5. また、追加の課題: 外部リスナーがイベントをキャプチャすることを確認してください。クリックを逃さないようにします。

関連項目

3. クライアントアクションを作成

現在、現在のユーザーインターフェイスは非常に小さいです: それは単なるsystrayアイテムです。私たちは確かに私たちのゲームのより多くを表示するために多くのスペースを必要とします。 これを行うには、クライアントアクションを作成しましょう。クライアントアクションは、コンポーネントを表示するWebクライアントによって管理されるメインアクションです。

  1. client_action.js (と xml) ファイルを作成します。hello world コンポーネントです。

  2. `awesome_clicker.client_action`という名前のアクションレジストリにクライアントアクションを登録します。

  3. Open`という文字列を使って、sysstrayアイテムにボタンを追加します。 これをクリックすると、クライアントアクション `awesome_clicker.client_action が開きます(アクションサービスを使用してこれを行います)。

  4. 従業員のワークフローを乱すことを避けるために、フルスクリーンモードではなく、ポップオーバー内でクライアントアクションを開くことを好みます。 `doAction`コールを変更して、ポップオーバーで開きます。

    ちなみに

    doAction の中で `target: "new"`を使って、ポップオーバーでアクションを開くことができます。

    {
       type: "ir.actions.client",
       tag: "awesome_clicker.client_action",
       target: "new",
       name: "Clicker"
    }
    
../../../_images/client_action.png

4. 状態をサービスに移動

今のところ、私たちのクライアントアクションはただのhello worldコンポーネントです。 ゲームの状態を表示させたいのですが、その状態は現在システムのアイテムでのみ利用できます。 つまり、すべてのコンポーネントで利用できるようにするために、状態の位置を変更する必要があるということです。 これはサービスにとって完璧なユースケースです。

  1. 対応するサービスで clicker_service.js ファイルを作成します。

  2. このサービスはリアクティブな値(クリック数)といくつかの機能をエクスポートして更新する必要があります。

    const state = reactive({ clicks: 0 });
    ...
    return {
       state,
       increment(inc) {
          state.clicks += inc
       }
    };
    
  3. systrayアイテムとクライアントアクションの両方の状態にアクセスします (useState を忘れないでください)。 systray項目を変更してローカルの状態を削除し、使用します。また、+10 clicks ボタンを削除できます。

  4. クライアントアクションに状態を表示し、その中に +10 クリックボタンを追加します。

../../../_images/increment_button.png

5. カスタム フックを使用

クリッカーサービスを使用する必要があるコードのすべての部分は、 useServiceuseState をインポートする必要があります。 かなり一般的なので、カスタムフックを使用しましょう。 clicker 部分をより強調し、service 部分を強調することも便利です。

  1. useClicker フックをエクスポートします。

  2. クリックサービスの現在の使用状況をすべて新しいフックに更新します:

    this.clicker = useClicker();
    

6. 表示される値をヒューマニズム化する

将来的には多数を表示するので、その準備をさせていただきます。 `humanNumber`関数があり、数字をよりわかりやすく書式設定できます。例えば、`1234`は`1.2k`と書式設定できます。

  1. カウンターを表示するために使用します(systray アイテムとクライアントアクションの両方)。

  2. 値を表示する ClickValue コンポーネントを作成します。

    注釈

    Owl はテキストノードだけを含むコンポーネントを許可します。

../../../_images/humanized_number.png

関連項目

7. `ClickValue`コンポーネントにツールチップを追加

`humanNumber`関数を使うと、インターフェースで正確さが失われました。ツールチップとして実数を表示しましょう。

  1. Tooltipにはhtml要素が必要です。`<span/>`要素で値をラップするには、`ClickValue`を変更してください。

  2. 正確な値を表示するために動的な`data-tooltip`属性を追加します。

../../../_images/humanized_tooltip.png

8. ClickBotを購入

私たちのゲームをさらに面白くしましょう。プレイヤーが初めて1000クリックになったら。 ゲームは新しい機能を解放する必要があります:プレイヤーは1000クリックでロボットを購入することができます。 これらのロボットは、10秒ごとに10クリックを生成します。

  1. 状態に「レベル」の数字を追加します。いくつかのマイルストーンで増加し、新機能を開く数字です。

  2. 「clickBots」番号を追加してください。購入したロボットの数を表します。

  3. クライアントアクションをクリックボットの数を表示するように変更します(level >= 1`の場合のみ) `クリック>= 1000 の場合に有効になる Buy ボタンを使用します。 Buy ボタンはクリックボットの数を 1 ずつ増やします。

  4. 10秒間隔を設定して、クリック数を 10*clickBots 増分します。

  5. プレーヤーが十分なクリックを持っていない場合は、購入ボタンが無効になっていることを確認してください。

../../../_images/clickbot.png

9. クラスモデルにリファクタリングする

現在のコードは、やや関数型スタイルで書かれています。 しかし、そのためには、state とそのすべての更新機能をクリッカーオブジェクトにエクスポートする必要があります。 このプロジェクトが成長するにつれて、これはますます複雑になるかもしれません。 簡単にするために、私たちのビジネスロジックをサービスから分割し、クラスにしましょう。

  1. リアクティブなクラスをエクスポートする clicker_model ファイルを作成します。すべての状態を移動し、サービスの関数をモデルに更新します。

    ちなみに

    @web/core/utils/reactiveReactive クラスで ClickerModel を拡張できます。Reactive クラスはモデルをリアクティブなプロキシにラップします。

  2. クリッカーサービスを書き換えて、クリッカーモデルクラスをインスタンス化してエクスポートします。

10. マイルストーンに達したときに通知する

1kクリックに達したときに何かが変わったというフィードバックはあまりありません。その情報を明確に伝えるために、effect サービスを使ってみましょう。 問題は、クリックモデルがサービスにアクセスできないことです。 また、できるだけUIの関心をモデルから外しておきたいと考えています。 そこで、私たちはコミュニケーションのための新しい戦略を探求することができます:イベントバス。

  1. クリッカーモデルを更新して、バスをインスタンス化し、初めて1000クリックに達したときに MILESTNE_1k イベントをトリガーします。

  2. クリッカーサービスをモデルバスで同じイベントをリッスンするように変更します。

  3. それが起こったら、`エフェクト`サービスを使って虹色の男を表示します。

  4. ユーザーがクリックボットを購入できるようになったことを説明するテキストを追加します。

../../../_images/milestone.png

11. BigBotを追加

明らかに、プレイヤーにもっと多くの選択肢を提供する方法が必要です。 新しい種類のクリックボットを追加しましょう: BigBots 10秒ごとに100回クリックしますが5000回クリックするだけです

  1. 5kになったときに`レベル`を増やします(2)

  2. bigbotsを追跡するために状態を更新します

  3. bigbotsは`レベル > =2`で利用できます。

  4. クライアントアクションで対応する情報を表示します

ちなみに

JavaScriptの式としてテンプレート内で`<または>`を使用する必要がある場合は、xmlパーサでクラス化する可能性があるので注意してください。 これを解決するには、gt, gte, lt または lte のいずれかの特殊エイリアスを使用できます。 テンプレート式 <https://github.com/odoo/owl/blob/master/doc/reference/templates.md#expression-evaluation>`_ の `Owl ドキュメントページを参照してください。

../../../_images/bigbot.png

12. 新しいタイプのリソースを追加: 電力

次に、別のスケーリングポイントを追加するために、新しいタイプのリソースを追加しましょう。パワー乗数です。 これは、`level >= 3`で増やすことができる数値で、Botの動作を乗算します。 クリックを1回ではなく、クリックボットが「multilier」クリックを提供してくれるようになりました。

  1. 100kになると`レベル`を増やします(3にする必要があります)。

  2. 電力を追跡するために状態を更新します(初期値は1です)。

  3. 乗数として使うボットを変更します。

  4. ユーザーインターフェイスを更新して、ユーザーが新しいパワーレベルを購入できるようにします(コスト:50k)。

../../../_images/bigbot.png

13.ランダム報酬を定義する

私たちは、ユーザーが時々ボーナスを取得し、Odooを使用して報酬を得ることを望んでいます。

  1. click_rewards.js`で報酬のリストを定義します。報酬は次のオブジェクトです: `description`文字列。 - ゲームの状態を引数に取り、変更できる `apply 関数です。 - ボーナスが利用可能なロック解除レベルを示す「minLevel」番号(任意)。 - ボーナスが利用できなくなったロック解除レベルを記述する maxLevel 番号 (任意)

    例:

    export const rewards = [
       {
          description: "Get 1 click bot",
          apply(clicker) {
                clicker.increment(1);
          },
          maxLevel: 3,
       },
       {
          description: "Get 10 click bot",
          apply(clicker) {
                clicker.increment(10);
          },
          minLevel: 3,
          maxLevel: 4,
       },
       {
          description: "Increase bot power!",
          apply(clicker) {
                clicker.multipler += 1;
          },
          minLevel: 3,
       },
    ];
    

    そのリストに何でも追加できます!

  2. 関数`getReward`を定義すると、現在のロック解除レベルに一致する報酬のリストからランダムな報酬を選択できます。

  3. `choose`関数の中で、別の`utils.js`ファイルに移動することができる配列からランダムに選択するコードを抽出します。

14. フォームビューを開くときに報酬を提供する

  1. フォームコントローラにパッチを適用します。フォームコントローラが作成されるたびに、報酬が与えられるかどうかをランダムに決定します(1%の確率)。

  2. 答えが「はい」の場合は、モデルのメソッド「getReward」を呼び出します。

  3. その方法は報酬を選択し、通知を送信する必要があります。 報酬が適用されるボタン Collect を使用すると、最後に clicker クライアントアクションが開きます。

../../../_images/reward.png

15. コマンドパレットにコマンドを追加

  1. コマンドパレットに`Clicker Gameを開く`コマンドを追加します。

  2. `Buy 1 click bot`というコマンドを追加します。

../../../_images/command_palette.png

16. さらに別のリソースを追加: 木

今、完全に新しい種類のリソースを紹介する時間です. ここではあまりにも論争されるべきではないものです: 木. これで、ユーザーは果物の木を植える(収集する?)ことができます。 木は100万クリックの費用がかかりますが、果物(梨またはチェリーのいずれか)が提供されます。

  1. さまざまな種類の木:梨/チェリーとその果物を追跡するために状態を更新します。

  2. 木と果物の合計数を計算する関数を追加します。

  3. 新しいロック解除レベルを clicks >= 1000 000 で定義します。

  4. クライアントのユーザー インターフェイスを更新して、木や果物の数を表示し、木を購入することもできます。

  5. 30秒ごとに果実番号を1ずつ増やします。

../../../_images/trees.png

17. systray項目にドロップダウンメニューを使用する

私たちのゲームは面白くなり始めます. しかし、今のところ, systray は、クリック数の合計を表示します. 私たちは、詳細情報を参照したい:木や果物の合計数。 また、いくつかのコマンドへの迅速なアクセスと詳細情報を持つことが便利です。ドロップダウンメニューを使用してみましょう!

  1. systray項目をドロップダウンメニューに置き換えます。

  2. それは素敵なアイコンを持つクリック、木、果物の数を表示する必要があります。

  3. それをクリックすると、より詳細な情報を表示するドロップダウンメニューが開きます:木や果物の各種類。

  4. また、いくつかのコマンドでいくつかのドロップダウンアイテム:クリッカーゲームを開き、クリックボットを購入、...

../../../_images/dropdown.png

18. ノートブックコンポーネントを使用する

私たちは今、より多くの情報を追跡します。 Notebook コンポーネントを使用して、さまざまなタブの情報と機能を整理することで、クライアントインターフェースを改善しましょう。

  1. Notebook コンポーネントを使用します。

  2. すべての click コンテンツは 1 つのタブに表示されます。

  3. すべての tree/fruits コンテンツは別のタブに表示されます。

../../../_images/notebook.png

19. ゲームの状態を保持

あなたは確かに私たちのゲームで大きな欠陥に気づいた:それは一時的です。 ユーザーがブラウザのタブを閉じるたびにゲームの状態が失われます。 それを修正しましょう。ローカルストレージを使用して状態を保持します。

  1. browser@web/core/browser/browser からインポートしてローカルストレージにアクセスします。

  2. 10秒ごとに状態をシリアル化し、ローカルストレージに保存します。

  3. clicker サービスが開始されると、ローカルストレージから状態をロードするか、そうでなければ自分自身を初期化する必要があります。

20.状態移行システムを導入する

どこかで状態を保持すると、新しい問題が発生します:コードを更新すると何が起こるか。 状態の形状が変化しユーザーがブラウザを開いて 古いバージョンで作られた状態で? 移行問題の世界へようこそ!

その問題に早期に取り組むのは賢明だろう。 ここでは、バージョン番号を州に追加します。 最新でない場合は自動的に州を更新するシステムを導入します。

  1. 状態にバージョン番号を追加します。

  2. マイグレーションのリストを定義します。マイグレーションは、fromVersion 番号、toVersion 番号、および apply 関数を持つオブジェクトです。

  3. コードがローカルストレージから状態をロードするたびに、バージョン番号を確認する必要があります。 状態が最新でない場合は、必要な移行をすべて適用する必要があります。

21.別の種類の木を追加

移行システムをテストするために、新しいタイプの木を追加しましょう: 桃。

  1. `桃`の木を追加します。

  2. 状態のバージョン番号を増分します。

  3. 移行を定義します

../../../_images/peach_tree.png