読者です 読者をやめる 読者になる 読者になる

SHIGEBITログ

主に自作ゲームについて書きます

cocos2d-x: マップ作成 ゴール到達処理

Tiledで作ったマップ内を猫が跳び回るシーンが出来ました。

コインと破壊できるブロック、タイムカウンター、更にゴールエリアの設置もできたので、この時点で一応ゲームにはなっています。
f:id:shigebit:20161128020335j:plain
マップデザインが適当だからか、ギミックが足りないからなのか、プレイしても全体的に今ひとつ楽しく感じないんですけど、ズラッと並んだコインを鮮やかに全部取りきった時はちょっとテンション上がったような気もします。

カスタムイベントでゴール通知

みなさん普通にやっているかもしれませんが、ゴールを目指す系ゲーム(なんか言い方あるんでしょうか?)でプレイヤーがゴール地点に到達した時の処理は、カスタムイベントを利用すると良いでしょう。

一昨日公開した、TMXから物理ボディーを作るクラスを使った、スタート・ゴールの処理を紹介します。

スタート地点

マップのオブジェクトレイヤーの地面や壁と共に、スタート地点とゴール地点を示すオブジェクトを設置します。
レイヤーを分けた方が判りやすいでしょうね。

f:id:shigebit:20161130012348j:plain

  • オブジェクトの形状はどのようなものでも良いのですが、スタート「地点」ですから、分かりやすく1マス分の円にしています。
  • プロパティーに「start」を追加します。値はここでは入れていませんが、スタート地点を複数設定して使い分ける場合はここに識別用の値か文字列を入れます
  • シェイプ作成コールバックに以下のコードを追加します。
PhysicsShape* TmxShapeCreateCallBack::OnCreateCircleShape(int id, float x, float y, float radius, std::map<std::string, std::string>& properties)
{
        // プロパティーに start があれば
        auto it = properties.find("start");
	if (it != properties.end())
	{
		m_startPosition.set(x, y);//この例ではメンバ変数に座標を保存して、後にプレイヤー座標を設定する時点で読み取ります。

                if (!it->second.empty())
                {
                	CCLOG("Start ID  = %s", it->second.c_str());
                }
		return nullptr;           //マップにこのシェイプ(オブジェクト)は不要なので nullptr を戻す。
	}

	auto shape = PhysicsShapeCircle::create(radius, PropertyCheck(properties), Vec2(x, y));
	return shape;
}
ゴールエリア

f:id:shigebit:20161130012351j:plain

  • 範囲を設定する場合は、オブジェクトにポリラインは使わないことをお薦めします。辺の両端が離れたゴールラインなら構いませんが
  • プロパティーに「goal」を追加します。ゴールエリアを複数設定する場合はここに識別用の値か文字列を入れます。
  • シェイプ作成コールバックに以下のコードを追加します。
  • このオブジェクトはマップに追加しないで、別ボディーとして作成します。
PhysicsShape* TmxShapeCreateCallBack::OnCreatePolygonShape(int id, std::vector<cocos2d::Vec2>& points, std::map<std::string, std::string>& properties)
{
    // プロパティーに goal があれば
        auto it = properties.find("goal");
	if (it != properties.end())
	{
		Vec2 center;
		std::vector<Vec2> offsetPoints;
		GetCenter(points, &center, &offsetPoints);//別ボディーとして作成するたのパラメーター取得

		auto body = PhysicsBody::createPolygon(offsetPoints.data(), offsetPoints.size());
		body->setDynamic(false);
		body->setCategoryBitmask(GROUP_PLAYER_EVENT);//(ここの定数は私のゲーム内のものです)
		body->setContactTestBitmask(GROUP_PLAYER);
                body->setCollisionBitmask(0);//接触判定はあるけど衝突はしない。

		GoalArea* obj= GoalArea::create();//このGoalAreaクラスも私のゲーム内のものです。コンタクトリスナーをメンバ関数に持っています。
		obj->setPhysicsBody(body);
		obj->setPosition(center);

 		if (!it->second.empty())
		{
                        int id = std::atoi(it->second.c_str());
                        CCLOG("Goal ID  = %d", id);
			obj->setUserData((void*)id); // ゴールID保存。
		}

		m_nodeForAddPhysicsActor->addChild(obj);//(どこかにaddChildするやり方は各々あるでしょう)

		return nullptr; //マップにこのシェイプ(オブジェクト)は不要なので nullptr を戻す。
	}

	auto shape = PhysicsShapePolygon::create(points.data(), points.size(), PropertyCheck(properties));
	return shape;
}
コンタクトリスナー

ゴールエリアクラスの接触コールバックです。
このコールバックも私の実装なんですけど、とにかく接触時に呼ばれる関数です。
ゴールしたことを、イベント発行します。他のオブジェクトはこれを受けた処理をするわけです。

void GoalArea::OnContactBegin(PhysicsActor* otherActor)
{
        int goalID = (int)this->getUserData();
	getEventDispatcher()->dispatchCustomEvent("Goal", (void*)goalID);//複数ゴールの場合こんな感じでイベント受取先で処理を分けられます。
	getEventDispatcher()->removeCustomEventListeners("Goal");
}
終わり

なんか、最後急に尻すぼみになったような気が、、、
結局私のやり方を紹介しただけになってしまったようです。。。



しつこいですが

「とニャンぽりん」をよろしくお願いします。

          Google Play で手に入れよう

f:id:shigebit:20161124233053j:plain