< Android > アプリ内課金について

Sandboxテスト…

予約されている所定のアイテムIDを使ってアプリ内課金処理の実装をテストできます。
このテストでは、実装が正しくGoogle Playからのレスポンスを処理し、署名を検証できることを確認できます。

つい最近までアプリ内課金テストを行うと
"購入が完了しました。"
とダイアログが表示されていた。
もちろんレシートと一緒にorderIDも取得できていました。

しかし、先週の月曜日(6/20)からダイアログには
"「課金アイテム名(アプリ名)」をライブラリに追加しました。"
と表示され、orderIDの取得も出来なくなりました。

おかげで課金のテストがうまくいかなくなり大変困りました。

Googleに問い合わせてみたところ
2016年6月20日(PDT)にアップデートが入ったとのことでした。
内容は以下の通りです。

Sandbox環境を利用したテスト課金にてorderIDは返却されず、

またGoogle Merchant Centerに表示されなくなります。
この変更に伴い、Sandbox環境を利用したテスト課金におきまして
orderIDではなくpurchaseTokenを代用として今後ご利用ください。

また、テスト課金時に表示される文章が
「ライブラリに追加しました」に変更され、
「続行」ボタンが表示される仕様になりましたが文章の変更のみで
挙動としてはこれまで通りです。


詳しく知りたい方は以下のURLより
developer.android.com

< Cocos2d-x > シーン移動について

Cocos2d-x バージョン : v.3.x


Cocos2d-xのシーン移動はとても簡単で以下の通りです。

// Ver3.x.
Director::getInstance()->replaceScene(HelloWorld::createScene());


このシーンの移動時に特殊な演出をいれる時に使用するのが
Transitionクラスというものです。

以下のようなクラスが用意されています。

  • TransitionFade
  • TransitionFlipAngular
  • TransitionShrinkGrow
  • TransitionMoveInB
  • TransitionMoveInT
  • TransitionMoveInL
  • TransitionMoveInR
  • TransitionFadeTR
  • TransitionFadeUp
  • TransitionFlipX
  • TransitionFlipY
  • TransitionPageTurn
  • TransitionCrossFade


書き方としては以下のような形になります

// TransitionFadeの場合

// 引数1 --- フェードの時間
// 引数2 --- 移動シーン
// 引数3 --- フェードの色
Director::getInstance()->replaceScene(TransitionFade::create(1.0f, HelloWorld::createScene(), Color3B::WHITE));

ほかの使い方については調べるといっぱい出てきますので省略。


今回お話ししたいのはシーン移動時の処理の呼ばれるタイミングについてです。
シーン移動をした場合は以下のような処理が呼ばれます。

Scene1::Scene1
Scene1::init
Scene1::onEnter
Scene1::onEnterTransitionDidFinish

Scene2::Scene2
Scene2::init
Scene2::onEnter

Scene1::onExit
Scene2::onEnterTransitionDidFinish
Scene1::~Scene1

onEnter / onExit
--- シーンの切り替え時に必ず呼ばれる関数。

onEnterTransitionDidFinish
--- Transitionアニメーションが終了したタイミングで呼ばれる関数。
   (Transitionしていなくても呼ばれる)


このonEnterTransitionDidFinish関数で
replaceSceneなどを行うとフリーズするという問題が発生します。

対策としてはonEnterTransitionDidFinish関数ではディレイを入れて
最低1フレーム待機することでこの問題を解決することが出来ます。

試しにシーンに名前を付け、
下記のパターンのときのonEnterTransitionDidFinish関数内で
現在動作しているシーンを取得してみた結果以下のようになった。
シーン移動前のシーン名 : Scene1
シーン移動後のシーン名 : Scene2

1.通常のシーン移動(repreceScene)
 取得できたシーン名 : Scene2

2.Transitionを使用
 取得できない

3.Transitionを使用しディレイを入れる
 取得できたシーン名 : Scene2


デストラクタが関係してるのではないかと…思う。

< Cocos2d-x > カードをめくるようなアニメーション

Cocos2d-x バージョン:3.8.1


まずはどのようなものかご覧ください。


https://youtu.be/TgCKhkd9Oy0youtu.be


このような動きを行うために使用したのがActionと呼ばれるものです。

Actionを組み合わせることによりいかにもカードをめくっているかのように見せています。


組み合わせるときに使うのが以下のものになります。


Sequence
 …順番にActionを実行していく


Spawn
 …同時に複数Actionを実行していく


ソースコードは以下の通りです。

--- Trump.h ---

#ifndef __TRUMP_SCENE_H__
#define __TRUMP_SCENE_H__

#include "cocos2d.h"
#include <random>

// トランプクラス
class Trump : public cocos2d::Layer
{
public:
	enum
	{
		BACK,
		SELECT,
		RESULT,
	};


	CREATE_FUNC(Trump);
	static cocos2d::Scene* createScene();
	virtual bool init();

	bool onTouchBegan(cocos2d::Touch* pTouch, cocos2d::Event* pEvent);
	void onTouchEnded(cocos2d::Touch* pTouch, cocos2d::Event* pEvent);

	void openTrump(int tag);
};

#endif // __TRUMP_SCENE_H__

--- Trump.cpp ---


#include "Trump.h"

// cocos2d を省略
USING_NS_CC;

static const int MAX_TRUMP = 3;
static const int TRUMP_SIZE = 250;

// クリエイトメソッド
Scene* Trump::createScene()
{
	auto scene = Scene::create();
	auto layer = Trump::create();
	scene->addChild(layer);

	return scene;
}

// 初期化メソッド
bool Trump::init()
{
	if (!Layer::init())
	{
		return false;
	}

	// サイズの取得
	auto size = Director::getInstance()->getVisibleSize();

	// タッチリスナー(シングルタッチ)
	auto touchListener = EventListenerTouchOneByOne::create();
	touchListener->onTouchBegan = CC_CALLBACK_2(Trump::onTouchBegan, this);
	touchListener->onTouchEnded = CC_CALLBACK_2(Trump::onTouchEnded, this);
	this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);

	// トランプを配置
	// 裏面のトランプを3枚配置します
	for (int i = 0; i < MAX_TRUMP; i++)
	{
		auto sprite = Sprite::create("Trump_Back.png");
		sprite->setPosition(Point(size.width / 2 - TRUMP_SIZE * (i - 1), size.height / 2));
		sprite->setTag(i);
		this->addChild(sprite,Trump::BACK);
	}

	// ボタン
	auto button = MenuItemImage::create("CloseNormal.png",
					    "CloseSelected.png",
					    [this](Ref* ref){				
					    Director::getInstance()->replaceScene(Trump::createScene());
	});

	button->setPosition(Point(size.width - button->getContentSize().width, button->getContentSize().height));

	auto menu = Menu::create(button, NULL);
	menu->setPosition(Point::ZERO);
	this->addChild(menu);

	return true;
}

void Trump::openTrump(int tag)
{
	// 選択された画像の取得
	auto selectTrump = this->getChildByTag(tag);
	this->reorderChild(selectTrump, Trump::SELECT);

	// 移動先の位置
	auto destPos = Director::getInstance()->getVisibleSize() / 2;

	// 表面に表示する画像を乱数で決める
	std::random_device device;
	std::mt19937 mt(device());
	std::uniform_int_distribution<int> dice(1, 3);

	std::stringstream fileName;
	fileName << "Trump_" << dice(mt) << ".png";

	log("%s", fileName.str().c_str());

        // 表面に表示される画像を作成
	auto resultTrump = Sprite::create(fileName.str().c_str());
	resultTrump->setPosition(destPos);
	resultTrump->setScale(1.5f);
        // 180度回転しているように見せるために角度を設定
	resultTrump->setRotationSkewY(-90.0f);
	this->addChild(resultTrump,Trump::RESULT);

	//
	// アニメーション
	//

	// 1 - 選択したトランプを中央に移動・拡大
	auto anim_Move = CallFunc::create([=]{
		auto move = MoveTo::create(0.6f, Vec3(destPos.width, destPos.height, 0.0f));
		auto scale = ScaleTo::create(0.6f, 1.5f);
		auto action = Spawn::create(move, scale ,NULL);
		selectTrump->runAction(action);
	});

	// 2 - 1と同時に選択外のトランプをフェードアウト・非表示
	auto anim_FadeOut = CallFunc::create([=]{
		for (int i = 0; i < MAX_TRUMP; i++)
		{
			// 選択トランプは除外
			if (i == tag)	continue;
			auto trump = this->getChildByTag(i);
			auto fadeOut = FadeOut::create(0.6f);
			auto visible = CallFunc::create([=]{
				trump->setVisible(false);
			});
			auto action = Sequence::create(fadeOut, visible, NULL);
			trump->runAction(action);
		}
	});

	// 3 - ディレイ
	auto delay = DelayTime::create(0.5f);

	// 4 - 選択したトランプを回転(90度)
	auto anim_Rota1 = CallFunc::create([=]{
		auto rota = RotateTo::create(0.3f, 0.0f,90.0f);
		selectTrump->runAction(rota);
	});

	// 5 - 表示するトランプを回転
	auto anim_Rota2 = CallFunc::create([=]{
		auto delay = DelayTime::create(0.3f);
		auto rota = RotateBy::create(0.3f, 0.0f, 90.0f);
		auto action = Sequence::create(delay, rota, NULL);
		resultTrump->runAction(action);
	});

	this->runAction(Sequence::create(anim_Move, anim_FadeOut, delay, anim_Rota1, anim_Rota2, NULL));
}


bool Trump::onTouchBegan(Touch* pTouch, Event* pEvent)
{
	return true;
}

void Trump::onTouchEnded(Touch* pTouch, Event* pEvent)
{
	// タッチ座標の取得
	auto touchPos = pTouch->getLocation();

	// シーンの取得
	// 0にはcocos2d::Cameraが入っているためシーンの取得時は1
	auto scene = Director::getInstance()->getRunningScene()->getChildren().at(1);
	for (int i = 0; i < MAX_TRUMP; i++)
	{
		auto sprite = scene->getChildByTag(i);

                // 表示していない場合はcontinue
		if (sprite->isVisible() != true) continue;

		if (sprite->getBoundingBox().containsPoint(touchPos))
		{
			// タッチした画像があったとき
			openTrump(i);
			break;
		}
	}
}


使用した画像名
Trump_Back.png
Trump_1.png
Trump_2.png
Trump_3.png

< Unity >LineRendererについて

Unity バージョン : 5.3.2


まず、Unityには線を描く機能が2つほどあります。

一つがオブジェクトの移動軌跡を描くTrailRenderer。

もう一つが今回のお話のLineRenderer。

後者のLineRendererは軌跡ではなく任意の座標を指定し線を描くものになります。

▼始点が(0.0f,0.0f,0.0f) 終点が(3.0f,3.0f,0.0f)
f:id:MTIMSNO:20160608222152j:plain

上の画像は、始点・終点の2点でしたが
これを3点にすると…

f:id:MTIMSNO:20160608222249j:plain

このような感じで始点から2点目にかけてが細くなってしまいます。
ゲームを作っている際にこの仕様?に出会ってしまいめちゃくちゃ悩みました。

色々調べてみた結果、折れ曲がった線を引きたい場合は
LineRendererを2つ使うというのが解決策らしいです。

実際LineRendererを2つ使うと下のようになります。
f:id:MTIMSNO:20160608222319j:plain

ソースコードは以下の通りです。

void Start() {

 // Line1
 CreateLine(Vector3.zero, new Vector3(3.0f,3.0f,0.0f));

 // Line2
 CreateLine(new Vector3(3.0f,3.0f,0.0f), new Vector3(3.0f,-3.0f,0.0f));

}

/// <summary>
/// ライン作成関数
/// </summary>
/// <param name="start">始点</param>
/// <param name="end">終点</param>
private void CreateLine(Vector3 start,Vector3 end)
{

 // オブジェクトの作成
 GameObject newObj = new GameObject();
 // 親子関係設定
 newObj.transform.parent = transform;
 // ラインの作成
 LineRenderer newLine = new LineRenderer();

 // --- 重要 --- //
 newLine = newObj.AddComponent<LineRenderer>();
 // --- 重要 --- //

 // ラインの色
 newLine.SetColors(Color.red,Color.red);
 // ラインの幅
 newLine.SetWidth(0.25f,0.25f);
 // ラインの頂点数?
 newLine.SetVertexCount(2);
 // 始点の設定
 newLine.SetPosition(0, start);
 // 終点の設定
 newLine.SetPosition(1, end);

}

このような方法で曲がっても一定の太さのラインを描くことが出来ます。

ラインとラインのつなぎ目が気になりますが
細くなるよりはマシかと思います。

上記の方法だと、ラインが手前側に表示されない場合がありますので
以下の二つの方法をお試しください。

1・レイヤーの指定

newLine.sortingLayerName = "Line";

のように新しくレイヤーを追加してあげる。

2・z座標の指定
ラインの始点・終点位置のz座標をカメラ側に近づけてあげる。

< Unity >透過処理した画像について

Unity バージョン : 5.3.2

 

Unityに透過処理をした画像を追加すると、下のような画像になります。

 

f:id:MTIMSNO:20160608221058j:plain

 

この画像をオブジェクトに追加すると黒い部分まで表示されてしまいます。

せっかく透過処理を施したのに黒くなってしまっては意味がありません。

そこで、オブジェクトに追加した際に作られるMaterialのShaderを変えることで

透過処理が施された画像にすることが出来ます。

 

Shader → Unlit → Transparent

この手順でShaderを変更することによりオブジェクトが透過されます。

▼Shaderが初期状態のとき

f:id:MTIMSNO:20160608221155j:plain

 

▼ShaderがUnlit/Transparentのとき

f:id:MTIMSNO:20160608221310j:plain

 

普通に透過処理をした画像が使いたいだけならこれでもいいかもしれません。

しかし、この状態でスクリプト上からオブジェクト自身の透過処理を行っても

”A”という文字はこのままで一切透過されません。

そこでShaderを変えることによって

「 オブジェクトの透過 + "A"という文字の透過 」

もできるようになります。

 

Shader → Legacy Shaders → Transparent → Diffuse

この手順を行ったうえでスクリプト上からオブジェクトの透過度を変えると

”A”という文字まで透過してくれます。

▼上記の方法でShader変更後にスクリプトでオブジェクトを透過。

f:id:MTIMSNO:20160608221506j:plain

 

このような感じでShaderを変えることにより透過処理が可能になります。