【cocos2d-x】タッチした場所にエフェクトを入れる

よくスマホゲームって、タッチした場所にアニメーションを入れてエフェクトかけてるじゃないですか。

あれを実装して見たいと思います。

今回もフリー素材のぴぽや倉庫さんから素材を拝借しました。

すでに、タッチ処理のやり方もアニメーションのやり方も知っているので、これを組み合わせます。

コードはこうなりました。

bool HelloWorld::init()
{

  中略

    auto listener1 = EventListenerTouchOneByOne::create();
    listener1->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

    return true;
}

bool HelloWorld::onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event)
{
    auto anime = Sprite::create("pipo-btleffect007.png");
    if (anime == nullptr)
    {
        problemLoading("'pipo-btleffect007.png'");
    }
    else
    {
        this->addChild(anime, 0);
    }
    float frameHeight = anime->getContentSize().height;
    float frameWidth = anime->getContentSize().width / 14.0;
    Vector<SpriteFrame*> animFrames;
    animFrames.reserve(14);
    for(int i = 0; i < 14; i++) {
        animFrames.pushBack(SpriteFrame::create("pipo-btleffect007.png", Rect(frameWidth * i,0,frameHeight,frameWidth)));
    }
    Animation* animation = Animation::createWithSpriteFrames(animFrames, 0.02f);
    Animate* animate = Animate::create(animation);

    anime->setPosition(touch->getLocation().x, touch->getLocation().y);
    anime->runAction(animate);

    return true;
}

前回のタッチ処理でラムダ式を使いましたが、

C++のラムダ式は思った以上に使いづらいので

普通にコールバックとして関数を定義し、マクロで登録する形にしました。

アニメーション画像は14枚横に並んでいるので、この画像を横に14等分割にクリッピングして使用しています。

あとはアニメーション画像の位置をタップ位置に設定してアニメーションを実行します。

前回はanimateをforever(無限ループ)していましたが、今回は1回きりのアニメーションなので、animateをそのまま使用しています。

どちらも親クラスがActionクラスなので、問題ありません。

【テクテクライフ】モエレ沼公園

モエレ沼公園は札幌市の北部にある、中島公園よりも広い公園です。

中心部から歩いて行くのは無謀で、一般的にバスか自家用車を使用します。

バスで行く場合は、地下鉄東西線バスセンター駅からバスセンターでモエレ沼公園行き(期間限定)に乗るか、地下鉄南北線麻生駅または、地下鉄東豊線栄町駅からモエレ沼公園行きのバスに乗ると入り口まで行けます。

入り口には自転車レンタルのお店があります。

はい、自転車移動推奨のとてつもなく広い公園なんです。

だいぶ葉っぱが色づいてきたね。

公園の中央には噴水広場がありまして、時間になると噴水ショーが見られます。

もちろん無料。

15分のショートバージョンと40分のロングバージョンがあります。

そして、モエレ沼の象徴と言えばこれでは無かろうか。

高いね。

昔1回登ったことあるけど、スゴイ大変だった記憶がある。

キノコ。

晴れている日はピクニックしている人達が多いね。

みんなテント張ってランチ食べてる。(公園内は火気厳禁であるのでバーベキューはできない)

そういう優雅な休日も過ごしてみたいなぁ。

【北海道大戦】戦闘結果をメインシーンへ反映

たぶん、これが最後の仕上げ。

まずはじゃんけんの結果を判定します。

    class BattleScene : asd.Scene
    {
        private void onClickMouseShowActionResult(asd.Vector2DF pos)
        {
            var result = janken(selectedAttack, selectedDeffece);
            if(result == BattleResult.win)
            {
                _deffencePower -= _attackPower;
                if(_deffencePower <= 0)
                {
                    Singleton.GameData.BattleResultUpdate(BattleResult.win);
                    var scene = new MainScene();
                    asd.Engine.ChangeScene(scene);
                }
                _deffenceParam.Text = "戦闘力:" + _deffencePower;
            }
            else if(result == BattleResult.lose)
            {
                _attackPower -= _deffencePower;
                if (_attackPower <= 0)
                {
                    Singleton.GameData.BattleResultUpdate(BattleResult.lose);
                    var scene = new MainScene();
                    asd.Engine.ChangeScene(scene);
                }
                _attackParam.Text = "戦闘力:" + _attackPower;
            }
            _attackResult.Hide();
            _deffenceResult.Hide();
            _status = GameStatus.SelectDeffenceAction;
        }

        private BattleResult janken(BattleIcon.Icon attack, BattleIcon.Icon deffence)
        {
            switch (attack)
            {
                case BattleIcon.Icon.Gu:
                    switch (deffence)
                    {
                        case BattleIcon.Icon.Gu:
                            return BattleResult.draw;
                        case BattleIcon.Icon.Choki:
                            return BattleResult.win;
                        case BattleIcon.Icon.Par:
                            return BattleResult.lose;
                        default:
                            return BattleResult.draw;
                    }
                case BattleIcon.Icon.Choki:
                    switch (deffence)
                    {
                        case BattleIcon.Icon.Gu:
                            return BattleResult.lose;
                        case BattleIcon.Icon.Choki:
                            return BattleResult.draw;
                        case BattleIcon.Icon.Par:
                            return BattleResult.win;
                        default:
                            return BattleResult.draw;
                    }
                case BattleIcon.Icon.Par:
                    switch (deffence)
                    {
                        case BattleIcon.Icon.Gu:
                            return BattleResult.win;
                        case BattleIcon.Icon.Choki:
                            return BattleResult.lose;
                        case BattleIcon.Icon.Par:
                            return BattleResult.draw;
                        default:
                            return BattleResult.draw;
                    }
                default:
                    return BattleResult.draw;
            }
        }

今回のじゃんけん判定はベタな方法で実装しました。

上手い方法が思い浮かばなかった。

janken()でじゃんけんの結果を判定し、その結果を反映、勝負が決まったら、その情報をGameData.BattleResultUpdate()に反映し、MainSceneに遷移します。

    class GameData
    {
        public void BattleResultUpdate(BattleResult result)
        {
            if (gameStatus == GameStatus.ActionEnemy)
            {
                Battle.EnemyTurnEnd(result);
            }
            else if (gameStatus == GameStatus.ActionPlayer)
            {
                Battle.MyTurnEnd(result);
                gameStatus = GameStatus.ActionEnemy;
            }
        }

状態が敵のターンか、自分のターンかで分岐させます。

    class Battle
    {
        public void EnemyTurnEnd(BattleResult result)
        {
            if(result == BattleResult.win)
            {
                lastAttack.CombinationCity(lastDeffece);
                lastDeffece.Lose();
                aliveCities.Remove(lastDeffece);
                lastDeffece = null;
            }
        }

        public void MyTurnEnd(BattleResult result)
        {
            if (result == BattleResult.win)
            {
                lastAttack.CombinationCity(lastDeffece);
                lastDeffece.Lose();
                aliveCities.Remove(lastDeffece);
                lastDeffece.ClearPaint();
                lastDeffece = null;
            }

            lastAttack.ClearPaint();
            lastAttack = null;

            cityCnt++;
            if (cityCnt >= _cities.Count)
            {
                _cities = cityRandomReplace(aliveCities);
                aliveCities = copyCity(_cities);
                cityCnt = 0;
                turn++;
            }
        }

これでデータの反映もできたはず。

あとはもう少しブラッシュアップさせて行きます。

【ぼっち】七輪焼肉 安安 札幌南3条店

障害年金が入ったんだ。少しぐらい贅沢しても良いじゃ無いか。

今回は安安コース2980円+アルコール飲み放題980円を選択。

七輪で焼く焼肉は美味しい。

炭水化物を先に食べることで、食べ過ぎを防ぐという高度なテクニック。

食べ放題には向いていないけど。

しかし、美味しければ問題無い。

さて、今日、コレステロール値が高いと言われたので、少しお肉を控えて、運動するように心がけたいと思う。

はい。

ビジモネットからビッグローブへ乗り換え

俺の夜のマッタリ時間を返して欲しい。

ビジモネット回線設置から半年が過ぎたので、今の回線より安い値段で他のプロバイダに乗り換えできますよ、

という連絡を、夜8時に受けました。

焼肉から帰ってきて、良い感じに酔っ払っているこのタイミングに、である。

酔っ払っているときにそんな重要な話やめて。

どうやら話を聞くと、このままビジモネットを使い続けていても良いけど、高いっすよ。このタイミングで他のプロバイダに乗り換えた方が安くなりますよ、ということである。

ちなみに、これを言っているのは、引越しの時に回線引越しの手引きをしてくれた業者である。どうやらこの人はビジモネットの人ではないらしい。

で、そのあとにやったこと。

1.引越し前の回線解約料分のキャッシュバックを受けられるので、料金明細をFAXして欲しい。

2.NTT回線の転用手続きをして欲しい。今使用している回線は切り替え先プロバイダに引き継ぐための、その手続き。

完了したら案内番号を連絡。これは電話を受けたその場でやった。(もちろん酔っ払ってる状態)

ちなみに、回線工事料金は全て業者持ちでこちらの負担は無しとのこと。

3.ビッグローブから回線切り替え手続きの連絡が来るので、対応して欲しい。

これは今日やった。手続き完了。

4.ビッグローブからIDが発行されて、書類が自宅に届いたら、すでにビッグローブの回線が使えるので、モデムの設定を済ませて、繋がることを確認したら、ビジモネットに解約の連絡を入れる。

ネットが繋がらなくなる期間はないらしい。良き良き。

5.ビジモネットの解約手続きが完了したら、それを業者に連絡。

なお、ビジモネットの解約手数料も業者が負担してくれるらしい。

ちなみに、この回線切り替えでどれくらいコストが安くなるかというと、

今まで:NTT4925円+ビジモネット878円=5803円

切り替え後:ビッグローブ3980円(初回3000円、3年更新、解約手数料2万円)

1900円ぐらい安くなるみたいです。

ちなみに、ビッグローブを選択した理由は、特にありません。

@niftyが切り替え後の選択肢になかったので、適当に決めました。

いや、だから、酔っ払ってるときに重要な話しないでって言ったでしょ。

まぁ、また何か進展があったらご報告します。

【プロジェクトセカイ】しばらくプレイし続けて思ったこと。

どこか行きたい。

なんとかmasterも普通にクリアできるレベルになりました。

EXもフルコンできた曲増えてきたし。

いきなりこんな譜面出てきたときは、確実に初心者を殺しに来てると思いました。

難しそうで、慣れちゃえば、意外と簡単に行けるもんすよ。

指をクロスしなくちゃいけないけど。

親指じゃ厳しいかもしれない。

そして、とにかく最初はデッキの強化に努めるしかない。

スコアランクがA以上になればレアアイテム獲得できる確率が高くなる。

それまでは、ありったけのリソースを使ってデッキを強化するしかない。

ベテランルームに入れることができれば、また変わってくるかもしれない(←まだ入れない)

ライブボーナスの使用量を調整できるのは良いですね。

ライブボーナス残っていても、消費しないでプレイすることも可能。

練習し放題。

メルト選曲すると切断されがち。

【ラズパイ】【カメラ】Webからカメラの画像を保存する。

前回はカメラのプレビュー画面をWebに表示させましたが、

今回はWebからシャッターボタンを設置して、カメラの画像を保存させます。

すでにPOSTリクエストを処理する方法も知っていますし、カメラの画像を保存する方法も知っているので、これらを組み合わせればできるはずです。

まずはサーバ(ラズパイ)の処理。


    def do_POST(self):
        content_len = int(self.headers.get('content-length'))
        requestBody = json.loads(self.rfile.read(content_len).decode('utf-8'))

        if requestBody['contents']['command'] == 1:
            _, img = cap.read()
            dt_now = datetime.datetime.now()
            filename = dt_now.strftime('%Y%m%d_%H%M%S')+".jpg"
            cv2.imwrite(filename, img)

            response = {
                'status' : 200,
                'path': "http://pi4.local:8000/" + filename
            }
        else:
            response = {
                'status' : 200
            }

        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        responseBody = json.dumps(response)

        self.wfile.write(responseBody.encode('utf-8'))

POST処理を追加しました。

カメラの画像をファイルに保存し、そのファイルパスをレスポンスで返すようなイメージです。

次はWeb側。

<!DOCTYPE html>
<html>
<head>
  <title>My first Vue app</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script src="vue.min.js"></script>
  <script src="jquery-3.5.1.slim.min.js"></script>
</head>
<body>
  <div id="app">
      <image id="camera" src="" /><br />
      <button @click="onShutter">Shutter</button><br />
      <a id="picture" href="" target="_blank">{{ path }}</a>
  </div>

  <script>
    var app = new Vue({
      el: '#app',
      data: {
        timer: null,
        param: {},
        contents: {
          command: 1,
        },
        path: "",
      },
      created: function() {
        self = this;
        this.timer = setInterval(function() {self.onLoad()}, 50)
      },
      methods: {
        onLoad: function() {
            axios.get('http://pi4.local:8000/Streaming').then(function(response){
                $("#camera").attr('src', response.data.image);
            }).catch(function(error){
            });
        },
        onShutter: function() {
          self = this;
          this.param.contents = this.contents;
          axios.post('http://pi4.local:8000/', this.param).then(function(response){
                $("#picture").attr('href', response.data.path);
                self.path = response.data.path;
            }).catch(function(error){
            });
        }
      }
    })
  </script>
</body>
</html>

ボタンとリンクを追加しました。

ボタンをクリックすると、onShutter処理が実行され、ラズパイ側にPOSTリクエストを送信します。

そのレスポンスから画像のファイルパスを取得し、リンクに反映させます。

同じ仕組みで動画の撮影もできそう。

次回やります。

【ダイエット支援】【入力履歴機能】入力履歴にデータを記入

ここもサクッと作成できると思う。

namespace App\Repository;

use App\Model\EatingHistoryItem;

class EatingManagementRepository
{
    private $templateParamNames = ['item', 'protein', 'liqid', 'carbo', 'calorie'];

    /**
     * ヒストリにデータを1件追加する
     */
    public function addHistory($param, $user)
    {
        $model = new EatingHistoryItem();
        foreach($this->templateParamNames as $name)
        {
            $model->$name = $param[$name];
        }
        $model->save();

        $this->attachToUser($model, $user);
    }
class ApiController extends Controller
{
    /**
     * データを1件登録する
     */
    public function add(Request $request)
    {
        $paramNames = $this->eatingManagement->getParam();

        $param = [];
        foreach($paramNames as $name) {
            $param[$name] = $request->contents[$name];
        }

        $this->eatingManagement->add($param, Auth::user(), $request->contents['timezone']);
        $this->eatingManagement->addHistory($param, Auth::user());
        
        return response()->json();
    }

入力したデータをそのまま履歴にも記入する、という処理ですな。

ここまではサクッとできたけど、次回からはかなりヘビーになると思う。

【ぼっち】いろはにほへと白石店

1人でもGoToイートが利用できることに重点を置いています。

居酒屋チェーン店ですが、LINEのクーポンを使えば、飲み放題が安くなります。

中トロの刺身が期間限定メニューで500円。

これでも刺身では一番安いです。

牛串。

間に挟まっているのはニンニクでした。

いや、美味しかった。

【COCOS2D-X】背景画像を設置。

画像はWeb検索して、見つけたフリー素材を使用しました。

で、これ多分、普通に表示するだけだと、画面からはみ出してしまうので。

これを上手く画面に収まるようにしたい。

見た感じ、画像の高さがはみ出ているので、高さの表示を画像に合うように拡大率を計算して、画像を縮小させたいと思います。

    auto sprite = Sprite::create("ID003_Western-Castle_noon.jpg");
    if (sprite == nullptr)
    {
        problemLoading("'ID003_Western-Castle_noon.jpg'");
    }
    else
    {
        // position the sprite on the center of the screen
        sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

        // add the sprite as a child to this layer
        this->addChild(sprite, 0);
    }
    auto scaleRate = visibleSize.height / sprite->getContentSize().height;
    sprite->setScale(scaleRate);

    auto str = String();
    str.appendWithFormat("width %f height %f", sprite->getContentSize().width, sprite->getContentSize().height);
    label->setString(str.getCString());

この画像の範囲をベースにして、画面を作っていきましょうか。

あ、画面をタップしたときにエフェクトかかるようにしたいなぁ。

次回やります。

自分、ぼっちですが何か?