Cocos2d-xで2点間の距離・角度を取得する(3.x系)

タイトルの通りですが、何故かCocos2d-xの2.x系で説明されている古めの記事しかなったので新しい(と言っても相当前ですが)3.x系での書き方の紹介です。

2.xと3.xではメソッド名や関数の位置が全然違うので少し書き方が違います。ただ、古いメソッドなどが残ってたり(残ってなかったり)するのですが毎回読み替えも結構つらいので新しいやり方で行きたいと思います。

環境

動作確認環境は以下の通りです。

  • Cocos2d-x 3.17.1
  • VisualStudio2017 15.9.14
  • Windows10

2点間の距離を求める

Cocos2d-xである1点の座標を表す方がcocos2d::Vec2クラスとして用意されていて、そこにあるメソッドを使用すれば自力で計算する必要がありません。ちなみにcocos2d::Vec2にはcocos2d::Pointという別名がついているので座標を表すときはこちらを使用していきます。

図にするとこんな感じです。

f:id:Takachan:20190927001951p:plain

ある2点を直線で結んだ距離を求めます。

#include "cocos2d.h"

// (1) 基本形、Cocos2d-xで座標を表すPoint型どうしの距離をとる
static float getDistance(const cocos2d::Point& a, const cocos2d::Point& b)
{
    float distance = a.getDistance(b); // この1行でOK
    return distance;
}

// (2) 2つのNodeオブジェクトどうし(Nodeを継承してるSpriteとかでもOK)の距離をとる
static float getDistance(cocos2d::Node* a, cocos2d::Node* b)
{
    return getDistance(a->getPosition(), b->getPosition());
}

// (3) タッチイベントの時にタッチ開始位置 - 現在位置の距離を取得する
static float getDistanceStartPosToCurrentPos(cocos2d::Touch* pTouch)
{
    return getDistance(pTouch->getStartLocation(), pTouch->getLocation());
}

// (3)の使い方
static void foo()
{
    using namespace cocos2d;
    
    auto li = EventListenerTouchOneByOne::create();
    li->onTouchBegan = [](Touch*, Event*) { return true; };
    li->onTouchMoved = [](Touch* pTouch, Event* pEvent)
    {
        // スワイプ中のタッチ開始位置と現在のタッチ位置の距離を取得する
        float distance = getDistanceStartPosToCurrentPos(pTouch);
    }
}

基本的に、(1)のPointどうしの比較を実装しておけば対象オブジェクトが変わっても幅広く対応可能かと思います。

また、上記距離の取り方は自力で計算もできます(通常、手計算しないですが…

#include <cmath>

// (4) [番外] 自力で計算する
static float getDistance(cocos2d::Point a, cocos2d::Point b)
{
    // (a) 全部自分で計算する方法
    float distance = sqrt(pow(b.x - a.x, 2) + pow(b.y - a.y, 2));
    return distance;
    
    // (b) 座標の引き算をPointで型で行う方法
    Point pd = b - a;
    float distance = sqrt(pow(pd.x, 2) + pow(pd.y, 2));
    return distance;
}

sqrtfと書かないでもcmathに引数がfloatのオーバーロードがあるのでsqrtをそのまま呼び出して大丈夫です。

2点間の角度を求める

次はある2点間の角度を求める方法です。

図にするとこんな感じです。

f:id:Takachan:20190927002344p:plain

Aを基準にBの角度をラジアン単位で求めます。

#include "cocos2d.h"

// (1) 基本形、Cocos2d-xで座標を表すPoint型間の角度を求める
static float getAngle(const cocos2d::Point& a, const cocos2d::Point& b)
{
    Point dist = b - a;
    return dist.getAngle(); // この1行でOK
}

// (2) 2つのNodeオブジェクトどうし(Nodeを継承してるSpriteとかでもOK)の角度を求める
static float getAngle(cocos2d::Node* a, cocos2d::Node* b)
{
    return getAngle(a->getPosition(), b->getPosition());
}

// (3) タッチイベントの時にタッチ開始位置 - 現在位置の角度を求める
static float getAngleStartPosToCurrentPos(cocos2d::Touch* pTouch)
{
    return getAngle(pTouch->getStartLocation(), pTouch->getLocation());
}

// (3)の使い方
static void foo()
{
    using namespace cocos2d;
    
    auto li = EventListenerTouchOneByOne::create();
    li->onTouchBegan = [](Touch*, Event*) { return true; };
    li->onTouchMoved = [](Touch* pTouch, Event* pEvent)
    {
        // スワイプ中のタッチ開始位置と現在のタッチ位置の距離を取得する
        float angle = getAngleStartPosToCurrentPos(pTouch);
        float degree = CC_RADIANS_TO_DEGREES(angle);
        
        CCLOG("radian=%f, degree=%f", angle, degree);
        // > radian=1.57079, degree=90
    }
}

CC_RADIANS_TO_DEGREESで変換すると-180°~180°の範囲で角度が表されるようになります。

こちらも基本的に、(1)のPointどうしの比較を実装しておけば対象オブジェクトが変わっても幅広く対応可能かと思います。

また、上記角度の取り方は自力で計算もできます。

#include <cmath>

// (4) [番外] 自力で計算する
static float getAngle(cocos2d::Point& a, cocos2d::Point& b)
{
    // (a) 全部自分で計算する方法
    float rad = atan2(b.y - a.y, b.x - a.x);
    return rad;
    
    // (b) 座標の引き算をPointで型で行う方法
    Point pd = e - s;
    float rad = atan2(pd.y, pd.x);
    return rad;
}

atan2fと書かないでもcmathに引数がfloat型ののオーバーロードがあるのでatan2をそのまま呼び出して大丈夫です。

もうCocos2d-xなんて誰も使ってないのにこなこと書いて果たして何の意味があるのか疑問に思わなくもないですが…

以上です。