「技術」カテゴリーアーカイブ

【Cocos2d-x】フリック処理を行う

もう一つやりたいのはフリック処理。

使用するハンドラはonTouchBeganとonTouchEndedの二つ。

タッチ開始の座標(sx,sy)とタッチリリースの座標(ex,ey)から、フリックの方向を求めるのですが、

上下左右の4方向だけだったら値の大小だけで良いかもしれませんが、

今回はアナログスティックの様な360°フリックの方向を出したいので、

ここでは三角関数を使用します。

アークタンジェント(tanの逆関数)を使用すれば、y/xから求めることができます。

関数名はatan2()です。

class SampleScene : public cocos2d::Scene
{
private:
    cocos2d::Label* label;
    cocos2d::Vec2 startPos, endPos;
    auto listener1 = EventListenerTouchOneByOne::create();
    listener1->onTouchBegan = [this](Touch* touch, Event* event)->bool
    {
        startPos = touch->getLocation();
        return true;
    };
    listener1->onTouchEnded = [this](Touch* touch, Event* event)->bool
    {
        endPos = touch->getLocation();
        double angle = atan2(endPos.y - startPos.y,endPos.x - startPos.x) * 180.0 / M_PI;
        auto str = String();
        str.appendWithFormat("angle %f", angle);
        label->setString(str.getCString());
        return true;
    };
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

atan2が返す値の単位はラジアンなので、これに×180/PIとすることで角度に変換できます。

よし、これで必要なことは一通りできたかな。

【北海道大戦】じゃんけんアイコンをクラス化

じゃんけんアイコンを使いやすい様にクラス化させます。

    class BattleIcon
    {
        public enum Icon
        {
            Gu,
            Choki,
            Par
        }

        public enum Position
        {
            Attack,
            Deffence
        }

        private asd.TextureObject2D _image = null;
        private Icon _icon;
        private Position _position;
        private int _x;
        private int _y;
        private int width = 80;
        private int height = 80;

        public BattleIcon(Icon icon, Position pos)
        {
            _icon = icon;
            _position = pos;
            _image = new asd.TextureObject2D();
            if (pos == Position.Attack)
            {
                _y = 500;
                switch (icon)
                {
                    case Icon.Gu:
                        _x = 300;
                        _image.Texture = Singleton.ImageGu;
                        break;
                    case Icon.Choki:
                        _x = 450;
                        _image.Texture = Singleton.ImageChoki;
                        break;
                    case Icon.Par:
                        _x = 600;
                        _image.Texture = Singleton.ImagePar;
                        break;
                }
                _image.Position = new asd.Vector2DF(_x, _y);
            }
            else
            {
                _y = 250;
                switch (icon)
                {
                    case Icon.Gu:
                        _x = 300;
                        _image.Texture = Singleton.ImageGu;
                        break;
                    case Icon.Choki:
                        _x = 450;
                        _image.Texture = Singleton.ImageChoki;
                        break;
                    case Icon.Par:
                        _x = 600;
                        _image.Texture = Singleton.ImagePar;
                        break;
                }
                _image.Position = new asd.Vector2DF(_x, _y);
            }
        }

        public void AddLayer(asd.Layer2D layer)
        {
            layer.AddObject(_image);
        }

        public void Show()
        {
            switch (_icon)
            {
                case Icon.Gu:
                    _image.Texture = Singleton.ImageGu;
                    break;
                case Icon.Choki:
                    _image.Texture = Singleton.ImageChoki;
                    break;
                case Icon.Par:
                    _image.Texture = Singleton.ImagePar;
                    break;
            }
        }

        public void Hide()
        {
            _image.Texture = null;
        }

        public void OnMouse(asd.Vector2DF pos)
        {
            if (IsOnMouse(pos))
            {
                switch (_icon)
                {
                    case Icon.Gu:
                        _image.Texture = Singleton.ImageGu2;
                        break;
                    case Icon.Choki:
                        _image.Texture = Singleton.ImageChoki2;
                        break;
                    case Icon.Par:
                        _image.Texture = Singleton.ImagePar2;
                        break;
                }
            }
            else
            {
                switch (_icon)
                {
                    case Icon.Gu:
                        _image.Texture = Singleton.ImageGu;
                        break;
                    case Icon.Choki:
                        _image.Texture = Singleton.ImageChoki;
                        break;
                    case Icon.Par:
                        _image.Texture = Singleton.ImagePar;
                        break;
                }
            }
        }

        public bool IsOnMouse(asd.Vector2DF pos)
        {
            if (pos.X > _x && pos.X < width + _x
                && pos.Y > _y && pos.Y < height + _y)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

とりあえず、使いそうなメソッドをいくつか実装しました。

    class BattleScene : asd.Scene
    {
        protected override void OnUpdated()
        {
            asd.Vector2DF pos = asd.Engine.Mouse.Position;

            switch (_status)
            {
                case GameStatus.SelectDeffenceAction:
                    cycleProcessSelectDeffenceAction(pos);
                    break;
                case GameStatus.SelectAttackAction:
                    break;
                case GameStatus.ShowActionResult:
                    break;
            }
            if (asd.Engine.Mouse.LeftButton.ButtonState == asd.ButtonState.Push)
            {
                switch (_status)
                {
                    case GameStatus.SelectDeffenceAction:
                        break;
                    case GameStatus.SelectAttackAction:
                        break;
                    case GameStatus.ShowActionResult:
                        break;
                }
            }
        }

        private void cycleProcessSelectDeffenceAction(asd.Vector2DF pos)
        {
            _image_gu_deffence.OnMouse(pos);
            _image_choki_deffence.OnMouse(pos);
            _image_par_deffence.OnMouse(pos);
        }

とりあえず、こんなところか。

マウスカーソルを合わせることで色を変えるようにしました。

うん、今日もがんばった。

【ラズパイ】リモートでカメラのプレビュー表示

いや、今回は結構ハマった。

これが半日頑張った成果だ。

まずはラズパイ側。

import base64
import cv2
import json
import os
import sys
import urllib.parse
import time
import threading

from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
from http import HTTPStatus

PORT = 8000

cap = cv2.VideoCapture(0)

def __main__():
    thread = threading.Thread(target=httpServe)
    thread.start()
    
    try:
        while cap.isOpened():
            time.sleep(1)
    except KeyboardInterrupt:
        return

def httpServe():
    handler = StubHttpRequestHandler
    httpd = HTTPServer(('',PORT),handler)
    httpd.serve_forever()

class StubHttpRequestHandler(BaseHTTPRequestHandler):
    server_version = "HTTP Stub/0.1"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_GET(self):
        enc = sys.getfilesystemencoding()

        _, img = cap.read()
        resized_img = cv2.resize(img, (480, 320))
        _, encoded_img = cv2.imencode('.jpg', resized_img, [int(cv2.IMWRITE_JPEG_QUALITY), 30])
        dst_base64 = base64.b64encode(encoded_img).decode('utf-8')

        data = {
            'image': 'data:image/jpg;base64,' + dst_base64
        }

        encoded = json.dumps(data).encode()

        self.send_response(HTTPStatus.OK)
        self.send_header("Content-type", "text/html; charset=%s" % enc)
        self.send_header("Access-Control-Allow-Origin", "null")
        self.send_header("Content-Length", str(len(encoded)))
        self.end_headers()

        self.wfile.write(encoded)     

__main__()

そしてクライアント側。

<!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="" />
  </div>

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

まず、サーバ側の説明。

今回は、すでに使った実績のある、HTTPで通信を行います。

もっと下位層のプロトコルを使うと、もっと効率よくデータの送信ができるのですが、その分、扱いも難しくなります。

HTTPはTCP/UDPよりも、データのやりとりが多くなるので、少しモッサリ感がありますが、扱いが簡単になります。

実際、今回はブラウザで表示させているので!

カメラで撮影し、それを画像に落とすところまでは今まで通りですが、

今回はHTTPで送信するために、Base64に変換し、Jsonに載せて送信します。

Base64の頭にある「data:image/jpg;base64」というのは、このデータはJpegですよ、ということを示す文字列で、これがないと、受け手側は何のデータか判断できません。

「Access-Control-Allow-Origin」はCORS対策です。

例えばクロームなのでは、同じドメインでなければ画像が開けない、という制約がありまして、それを判断しているのが、リクエストヘッダーのOriginと、レスポンスヘッダーのAccess-Control-Allow-Origin。

この二つの値が一致しないと画像はエラーで表示されなくなります。(ただしブラウザによる)

今回はChrome側のOriginがnullだったので、それに合わせました。

FireFoxとかだったらまた話が変わってくるかもしれない。

続いて、クライアント側。

サクッと作成するために、Vue.jsを使用しました。

使用しているライブラリはaxios、jQueryです。

Laravelと同じような構成にしました。

画面には<div id=”app”>と<image id=”camera”>のみです。

やっていることは単純で、画面が作成されたらcreatedメソッドが実行されて、onLoad()を周期的にコールするようにしています。

50という数字を小さくすればヌルヌルになりますし(ただしラズパイ側の負荷が大きくなる)、大きくすればカクカクになります。

今回はラズパイにクーラーつけているから大丈夫だけど、ラズパイZeroでこの負荷はやばいと思う。

そして、レスポンスの中のBase64をimageのsrcに入れれば画像が表示されます。

これを早いサイクルで実行・画像更新することで、動画のように見せることが出来ます。

今回はWebカメラみたいに仕上がりましたが、HTTPが使えるならばクライアントは何だってできます!

【ダイエット支援】もっと具体的な設計をしなくちゃいけなくて

たぶん、前回の記事の内容だけではコーディングはまだできなくって、

もっと具体的に動作の仕組みを考えなくちゃいけないと思いまして。

UIの動き

  • 文字を入力すると、データベースから入力候補を取得し、表示する
  • 入力した内容が入力候補と一致すると、その栄養素値が自動で入力される
  • 入力を空にすると、入力候補も空にする

テキスト窓に入力したかどうかはVue.jsではv-on:changeで処理を起動することができるみたいなので、これを使用します。

データベース

基本的には入力済みの食品・栄養素の情報(テンプレート)からデータを抽出して候補がある物から選択するという形にしたいと思います。

ただ、そう言った食品や栄養素の情報を全部用意するのは大変なので、ユーザーが入力した情報を記憶しておいて(ヒストリ)、管理者が正式データとして登録する、という仕組みにしたいと思います。

なので、管理者画面が必要になりますね。

データベースはテンプレートとヒストリの二つ用意しておきたいと思います。

ここらへん、本当はもっと詰めなくちゃいけない所ですが、今回はあとでどうにでもできるように、リスクが少ない方法を選択したいと思います。

あとでデータベース構成を変えるとなるとコードの修正も大変なので。

なので、今後の作業はこんな感じです。

  • データベースの構築
  • テンプレートからデータを入力する処理
  • ユーザーが入力したデータをヒストリに保存する処理
  • 管理者がヒストリからテンプレートに移す処理

よし、これでいきましょう。

【COCOS2D-X】タッチ処理を使用する。

さて、今回はタッチ処理を試してみたいと思います。

タッチ処理はスマホ専用の処理なので、Windowsでは動かないので、スマホで動作確認します。

で、タッチした座標を画面に表示させたいと思います。

    auto listener1 = EventListenerTouchOneByOne::create();
    listener1->onTouchBegan = [this](Touch* touch, Event* event)->bool
    {
        auto str = String();
        str.appendWithFormat("Touch (%f %f)", touch->getLocation().x, touch->getLocation().y);
        label->setString(str.getCString());
        return true;
    };
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

タッチ処理はイベントリスナーを使用するのですが、

EventListenerTouchOneByOneとEventListenerTouchAllAtOnceがあるのですが、前者はシングルタッチ、後者はマルチタッチのようです。

使用するハンドラ名が若干違うようです。

今回はシングルタッチを使用します。

で、今回初めてC++のラムダ式を使ったのですが、

[]はキャプチャといって、ラムダ式内に持っていく変数を指定するみたいなんですよね。

座標を表示するラベルなんですが、

ローカルにLabelオブジェクトを定義して[&label]って書いてもエラーで動かなかったので、

今回はサンプルコードに従って、labelはprivate変数に定義し、キャプチャには[this]とします。

こうすることでprivateのlabelオブジェクトを使用することができました。

あとは、引数touchからx,y座標をラベルに表示させる。

スマホだからか、画像のクリッピング処理がWindowsとは動きが違ってたんですよね。

ちょっとここは要調査ですわ。

もしかしたら、Windows用とスマホ用に画像を2種類用意しないといけないかもしれん。

【北海道大戦】バトルシーンへパラメータを渡す。

前回までの状況はこちら。

バトルシーンを作成するために、バトルシーンで使用するパラメータを設定します。

シーンはその都度作成しなくちゃいけないので、コンストラクタで渡すのが一番良いでしょう。

        public enum Player
        {
            Attack,
            Deffence
        }

        public BattleScene(City attack, City deffence, Player player)
        {
            _attack = attack;
            _attackPower = attack.Population;
            _deffence = deffence;
            _deffencePower = deffence.Population;
            _player = player;
            _status = GameStatus.SelectDeffenceAction;
        }

playerというパラメータは、プレイヤーが攻撃側か防御側かを示すパラメータです。

あれこれ悩んだ結果、これが一番簡単だろうと。

これを画面に表示させます。

            var attackCityLabel = new asd.TextObject2D();
            attackCityLabel.Font = Singleton.LargeFont;
            attackCityLabel.Text = _attack.Name;
            attackCityLabel.Position = new asd.Vector2DF(450, 150);
            layer.AddObject(attackCityLabel);

            var deffenceCityLabel = new asd.TextObject2D();
            deffenceCityLabel.Font = Singleton.LargeFont;
            deffenceCityLabel.Text = _deffence.Name;
            deffenceCityLabel.Position = new asd.Vector2DF(450, 650);
            layer.AddObject(deffenceCityLabel);

            _attackParam = new asd.TextObject2D();
            _attackParam.Font = Singleton.LargeFont;
            _attackParam.Text = "戦闘力:" + _attack.Population;
            _attackParam.Position = new asd.Vector2DF(700, 650);
            layer.AddObject(_attackParam);

            _deffenceParam = new asd.TextObject2D();
            _deffenceParam.Font = Singleton.LargeFont;
            _deffenceParam.Text = "戦闘力:" + _deffence.Population;
            _deffenceParam.Position = new asd.Vector2DF(700, 150);
            layer.AddObject(_deffenceParam);

さらに、

このフローからバトルシーンの状態を抽出。

        enum GameStatus {
            SelectDeffenceAction,
            SelectAttackAction,
            ShowActionResult
        }

これを実装。

        protected override void OnUpdated()
        {
            switch (_status)
            {
                case GameStatus.SelectDeffenceAction:
                    break;
                case GameStatus.SelectAttackAction:
                    break;
                case GameStatus.ShowActionResult:
                    break;
            }
            if (asd.Engine.Mouse.LeftButton.ButtonState == asd.ButtonState.Push)
            {
                switch (_status)
                {
                    case GameStatus.SelectDeffenceAction:
                        break;
                    case GameStatus.SelectAttackAction:
                        break;
                    case GameStatus.ShowActionResult:
                        break;
                }
            }
        }

あとは、各状態について実装していけば良いのですな。

とりあえず今回はここまでにしておこう。

【ラズパイ】プログラムからカメラで動画撮影する。

今回はさらにxキーで動画撮影開始・停止を行います。

プログラムはこのようになりました。

from datetime import datetime
import cv2, os

cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'XVID')


def main():

  if cap == None:
    return False

  out = None
  capture = False

  while cap.isOpened():
    # カメラから映像を読み込む
    _, img = cap.read()

    cv2.imshow("preview", img)

    if capture:
      if out != None:
        out.write(img)

    key = cv2.waitKey(1)
    if key == ord('z'):
      cv2.imwrite("test.png", img)
    if key == ord('x'):
      if not capture:
        capture = True
        out = cv2.VideoWriter('test.avi',fourcc, 20.0, (640,480))
      else:
        capture = False
        out.release()
        out = None
    elif key < 255:
      break

  # 事後処理
  cap.release()
  if out != None:
    out.release()
  cv2.destroyAllWindows()

if __name__ == '__main__':
  main()
  

今回は新しくVideoWriter_fourcc()というのが出てきました。

引数の文字列は動画コーデックを示しているみたいです。

よく分からんけど。

まぁ、結局はこのコードをコピペして使うんだー

※ちなみに、プログラムをブログに書いたり、gitHubにあげているのは、就職して職場内に行ったとしてもコピペして使えるようにするためです。

VideoWriter()をつかって動画に保存するためのオブジェクトを作成します。

引数は、動画ファイル名、上のfourCC、フレームレート(fps)、解像度です。

オブジェクトを作成したら、フレームデータをこのオブジェクトにwrite()で書き込むだけです。

今回はxキーで録画開始・停止を行うので、xキーオンオフでオブジェクトを作成・解放を操作しています。

次回はいよいよリモートやってみようかな

【ラズパイ】プログラムからカメラの画像を保存する

前回までの状況はこちら。

前回はカメラの画像を表示させていただけですが、

今回はこの画像をファイルに保存します。

例えば、zキーを押すことで画像をファイルに保存することにします。

プログラムはこうなります。

from datetime import datetime
import cv2, os

def main():

  cam = cv2.VideoCapture(0)
  if cam == None:
    return False

  while True:
    # カメラから映像を読み込む
    _, img = cam.read()

    cv2.imshow("preview", img)

    key = cv2.waitKey(1)
    if key == 122:
      cv2.imwrite("test.png", img)
    elif key < 255:
      break

  # 事後処理
  cam.release()
  cv2.destroyAllWindows()

if __name__ == '__main__':
  main()

cv2.waitKey(1)が122というのは、押したキーがzキーだったと言うことを示しています。

画像ファイルに保存する場合はimwrite()を使ってフレームデータとファイル名を渡すとカレントディレクトリに指定ファイル名で保存されます。

【ダイエット支援】今後の機能追加について

一通り食事管理機能は完成したのですが、

正直、いまいち使いづらいです。

数値を毎回入力しなければならないので。

過去に入力したデータを再利用できないかな、と思っているのですが、

調べてみると、HTMLのinputタグにはtype=searchというものがありまして、

                                <input type="search" v-model="contents.item" autocomplete="on" list="keyword"/>
                                <datalist id="keyword">
                                    <option value="札幌" />
                                    <option value="札駅" />
                                    <option value="新さっぽろ" />
                                    <option value="東札幌" />
                                    <option value="札束" />
                                </datalist>

こんな感じでautocomplete=”on” list=”キーワード”と記入すると、

こんな感じでdatalistタグのid=”キーワード”の内容が入力候補として表示されます。

これをうまく使えないかと。

品名の一部を入力→入力履歴を検索→候補を表示→履歴からデータを入力

という感じで、うまく処理できないかと思っています。

データベース

本来なら栄養素情報を別テーブルにしてそれを参照するという、正規化が必要なのだと思いますが、

いまからデータベースに変更を入れるのは、既存機能の大規模改修が発生(めんどくさい)ので、

それはそのままに、履歴検索用のテーブルを用意することにします。

それがあれば、入力履歴検索用のAPIを作成すれば行けるような気がします。

UI

datalistの中のoptionタグがv-forでリストを反映させることができると思います。

ただ、大量のoptionが画面に表示されても鬱陶しいので、例えば、検索結果が10件以上だったらあえて表示させない、というのも、一つの手かもしれません。

とりあえず、こんな方針でやってみますか。

次回から着手します。

【Cocos2d-x】画像を使用する

今回は画像を使ってみます。

画像はスプライトと呼ばれる単位で扱われます。

例えば、ただ画面に画像を表示する場合は、

bool SampleScene::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("pipo-charachip001b.png");
    if (sprite == nullptr)
    {
        problemLoading("'pipo-charachip001b.png'");
    }
    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);
    }
    return true;
}

画像をResources/res配下に設置し、

Sprite::create(“[画像ファイル名]”)スプライトの作成。

setPosition()で表示位置を決めてaddChild()でスプライトを設置すれば表示されます。

でも大抵は一枚の絵の中にいくつものパーツがまとめられているのが普通です。

なので、画像をクリッピングして、一部だけを表示させます。

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("pipo-charachip001b.png", Rect(0,0,32,32));
    if (sprite == nullptr)
    {
        problemLoading("'pipo-charachip001b.png'");
    }
    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 sprite2 = Sprite::create("pipo-charachip001b.png", Rect(0,32,32,32));
    if (sprite2 == nullptr)
    {
        problemLoading("'pipo-charachip001b.png'");
    }
    else
    {
        // position the sprite on the center of the screen
        sprite2->setPosition(Vec2(visibleSize.width/2 + origin.x + sprite->getContentSize().width, visibleSize.height/2 + origin.y));

        // add the sprite as a child to this layer
        this->addChild(sprite2, 0);
    }
    return true;

じゃあ、これを使って、アニメーションさせます。


    auto sprite = Sprite::create("pipo-charachip001b.png");
    if (sprite == nullptr)
    {
        problemLoading("'pipo-charachip001b.png'");
    }
    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);
    }

    Vector<SpriteFrame*> animFrames;
    animFrames.reserve(12);
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(0,0,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(32,0,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(64,0,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(0,32,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(32,32,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(64,32,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(0,64,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(32,64,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(64,64,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(0,96,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(32,96,32,32)));
    animFrames.pushBack(SpriteFrame::create("pipo-charachip001b.png", Rect(64,96,32,32)));

    Animation* animation = Animation::createWithSpriteFrames(animFrames, 0.1f);
    Animate* animate = Animate::create(animation);

    sprite->runAction(RepeatForever::create(animate));

    auto moveBy = MoveBy::create(2, Vec2(50, 0));
    sprite->runAction(moveBy);

    return true;

SpriteFrameを組み合わせてAnimationを作成し、それからAnimateを作成します。

これからアクションを作成して、実行させます。

なかなか良い感じじゃないですか?