Andengine小ネタ > Andengineの回転の中心について

目次

概要

Entity.setPosition(x,y)の場合、座標の基準は親のEntityです。
このおかげで、親エンティティを移動、回転させても、子エンティティはそれに追従してくれます。

けど、setRotationCenter()やsetScaleCenter()で回転、拡大の中心座標を指定する際には、この話が変わってくるので注意しましょうという話。

そういうわけで、色々調べてここに残しておこうと思います。

基本の考え方

デフォルトでは左上がx=0、y=0の座標です。そしてx軸は水平に左から右に、y軸は鉛直に上から下に伸びています。

以下のようにエンティティを変形させる命令を行うとx軸、y軸はそれに追従して変形します。
  • 大体のエンティティの命令
    • setRotation
    • setScale
      • setScaleX
      • setScaleY
    • setSkew
      • setSkewX
      • setSkewY
  • RectangularShape(Sprite,Rectangle,Textおよびサブクラス)の命令
    • setSize
      • setWidth
      • setHeight
  • Spriteおよびサブクラスの命令
    • setFlipped
      • setFlippedHorizontal
      • setFlippedVertical

例えば、幅32px、高さ32pxのSpriteがあったとします。そのSpriteを上の命令で変形させた後、x=30,y=30の位置に別のSpriteをattachChildでくっつけると、ちゃんと元々の画像の右下の位置にSpriteがくっつきます。

気をつけなければならない話

このページのメインの話題です。

以下の命令は同じく座標を指定する命令ですが、setScaleで拡大縮小すると座標の基準が上の命令と変わってしまいます。
  • setRotationCenter
    • setRotationCenterX
    • setRotationCenterY
  • setScaleCenter
    • setScaleCenterX
    • setScaleCenterY
  • setSkewCenter
    • setSkewCenterX
    • setSkewCenterY
以下ではsetRotationCenter命令に関して書きます。他に関しても同じだと思います。

拡大した後の回転の中心

以下のようなコードを考えます。
//フィールド
	TextureRegion faceRegion;
	VertexBufferObjectManager vbom;
	Scene scene;
 
//(略)
 
	Sprite face1 = new Sprite(200, 200, faceRegion, vbom);
	scene.attachChild(face1);
	face1.setScale(4);
 
	//比較対象
	Sprite face2 = new Sprite(200, 200, faceRegion, vbom);
	scene.attachChild(face2);
 
2つのスプライトを同じ座標に配置して、片方だけ4倍に拡大しています。
この結果は以下のようになります。


次に、拡大した方の顔を回転させてみます。以下のようにしてみます。
//(略)
	Sprite face1 = new Sprite(200, 200, faceRegion, vbom);
	scene.attachChild(face1);
	face1.setScale(4);
	//追加
	face1.setRotation(45)//時計回りに45度回転させる。
 
	//比較対象
	Sprite face2 = new Sprite(200, 200, faceRegion, vbom);
	scene.attachChild(face2);
 
この結果は以下のようになります。


ここまでは特に普通です。デフォルトでは回転、拡大、あとスキューの基準の座標は矩形の中心になります。
例えば幅32px、高さ32pxのSpriteならx=16,y=16です。

それでは、大きい方の顔の回転の基準を左上の角に指定して回転させるとどうなるでしょう。

コードは以下のようになります。
//(略)
	Sprite face1 = new Sprite(200, 200, faceRegion, vbom);
	scene.attachChild(face1);
	face1.setScale(4);
	//追加
	face1.setRotationCenter(0,0)//回転の中心を左上にする。
	face1.setRotation(45)//時計回りに45度回転させる。
 
	//比較対象
	Sprite face2 = new Sprite(200, 200, faceRegion, vbom);
	scene.attachChild(face2);
 
拡大した後に左上の座標x=0,y=0を回転の中心に指定しています。
この実行結果は以下のようになります。


この結果を、回転前の画像と見比べるとわかりますが、回転の中心が左上になっていません。

拡大後の左上でなく拡大前に左上の角があった座標が中心になっています。


この結果から、「回転の中心を座標で指定した場合、その座標の基準は拡大前の縮尺で考えなければならない」ということが考えられます。


どうやって拡大後の回転の中心を決めるか?

2通りあると思います。
  • 頑張って回転の中心を計算する
    • setScaleのたびに計算が必要
  • 親Entityを回転させる
    • 考え方は楽
    • 回転の中心、拡大の中心が常に同じになる

頑張って回転の中心を計算する

この画像の一番右のように、拡大前の基準での座標を計算して回転の中心を決めます。

回転の中心を拡大前の画像の拡大前の基準で決めます(例えば、左上なら(0,0)、右下なら(32,32)です)。これを(pX,pY)とします。

以下の例では回転の中心を左上に指定したいとします。pX=0,pY=0ですね。

拡大率が変わるたびに以下のような計算をします。
//回転の中心
float pX = 0;
float pY = 0;
 
//拡大の中心
float scaleCenterX = face1.getScaleCenterX();
float scaleCenterY = face1.getScaleCenterY();
 
//拡大率
float scaleX = face1.getScaleX();
float scaleY = face1.getScaleY();
 
//求める回転の中心
float rotationCenterX = scaleCenterX + (pX - scaleCenterX) * scaleX;
float rotationCenterY = scaleCenterY + (pY - scaleCenterY) * scaleY;
 
//回転の中心をセット
face1.setRotationCenter(rotationCenterX,rotationCenterY);
 
拡大のたびにこれを実行するのが面倒なら、Spriteの生成時にsetScale,setScaleX,setScaleYをオーバーライドしても良いかもしれません。
Sprite face1 = new Sprite(200, 200, faceRegion, vbom){
		@Override
		public void setScale(float pScale) {
			super.setScale(pScale);
			//回転の中心
			float pX = 0;
			float pY = 0;
 
			//拡大の中心
			float scaleCenterX = getScaleCenterX();
			float scaleCenterY = getScaleCenterY();
 
			//拡大率
			float scaleX = getScaleX();
			float scaleY = getScaleY();
 
			//求める回転の中心
			float rotationCenterX = scaleCenterX + (pX - scaleCenterX) * scaleX;
			float rotationCenterY = scaleCenterY + (pY - scaleCenterY) * scaleY;
 
			//回転の中心をセット
			setRotationCenter(rotationCenterX,rotationCenterY);
		}
	};
	attachChild(face1);
	face1.setScale(4);
	face1.setRotation(45);
	Sprite face2 = new Sprite(200, 200, faceRegion, vbom);
	attachChild(face2);
 

実行すると以下のような感じになります。


左上が回転の中心になっています。

親Entityを回転させる。

回転や拡大が親に追従するので、それを利用します。何も表示しないEntityを作って、Spriteを子としてattachします。その時に回転、拡大の中心に親Entityの座標が来るようにすれば、
あとは、親EntityのsetScaleやsetRotationを実行すれば、親基準で回転、拡大できます。

ですが、この方法だと色々気をつけなければならないことも多いので、あまりお勧めしません。setPositionとかの座標の指定が逆にめんどうになります。

合計: -
今日: -
昨日: -
最終更新:2014年09月17日 06:22