「カメラ」タグのついた投稿

今回はスマホのジャイロ機能を使ってスマホ本体の向きによってカメラの向きを変える方法を紹介します。

 

まず、自信のTransformを保持し、現在しようしている機種がジャイロに対応しているかを確認します。対応していない場合は、このコンポーネントを破棄します。

    // 自信のTransform, 毎フレーム参照すると無駄なので保持する
    Transform m_transform;

    // 調整値
    readonly Quaternion _BASE_ROTATION = Quaternion.Euler(90, 0, 0);

    void Start()
    {
        // サポートするかの確認
        if (!SystemInfo.supportsGyroscope)
        {
            Destroy( this );
            return;
        }

        m_transform = transform;
    }

そして実際にカメラを動かすのは簡単で、Input.Gyroをから値を取り出し、それを少し調整してgameobjectのrotationを変えるだけです。

    void Update()
    {
        // ジャイロの値を獲得する
        Quaternion gyro = Input.gyro.attitude;

        // 自信の回転をジャイロを元に調整して設定する
        m_transform.localRotation = _BASE_ROTATION * (new Quaternion(-gyro.x, -gyro.y, gyro.z, gyro.w));
    }

えー、はい。これだけです。このスクリプトをカメラのゲームオブジェクトにアタッチすれば動きます。

 

この処理を使っているアニュビスの仮面の動画:

 

流石に短すぎるので一応オマケで、GearVRとかの時にこのコンポーネントを破棄する処理を付け加えて見ます。

    void Start()
    {
        // サポートするかの確認, GearVR等でも破棄する
        if (!SystemInfo.supportsGyroscope || UnityEngine.VR.VRSettings.enabled)
        {
            Destroy( this );
            return;
        }

        m_transform = transform;
    }

 

以上です。

今回はあまりのネタの無さに、昔別サイトの掲示板で書いた物を書き直して見ました。このメモは、前に書いた2Dカメラの作り方と同じ論理でC++とdirectx9で2D用のカメラの作り方を乗せます。ちなみに、今回は描画を全てID3DXSpriteを使用していますので、板ポリゴンを使用する場合は別の方法をとってください。

まず、違う記事で書いた2Dカメラを持ってきます。実装方法は、XNAをdirectxに変換するだけなので、詳細は省いときます。必要なのはカメラの現在地、ズーム(拡大)、回転を合わせたマトリックスを出す関数を用意することです。

次に実際に描画する時、ID3DXSpriteのBegin()を呼ぶ際にカメラの行列を保存し、End()が呼ばれるまでの間に描画される全てのスプライトにこのマトリックスを適用す。

後は、描画処理を実装するだけです。少し自分で作ったクラスが入りますが、描画関数はXNAのSpriteBatchに似せて作られています:

// カメラのマトリックスを保存する
D3DXMATRIX _transform = camera.TransformMatrix;

void Sprite::Draw(Texture *texture, Vector2 &position, Colour &colour, Vector2 &origin, float scale, float angle, float depth)
{
// 位置と原点を保存する
D3DXMATRIX m1, m2;
D3DXVECTOR3 ori( origin.X, origin.Y, 1.0f );
D3DXVECTOR3 pos( position.X, position.Y, 0 );

// スケーリング
D3DXMatrixScaling( &m1, scale, scale, 0.0f );

// 回転
D3DXMatrixRotationZ( &m2, angle );
D3DXMatrixMultiply( &m1, &m1, &m2 );

// 移動
D3DXMatrixTranslation( &m2, position.X, position.Y, depth );
D3DXMatrixMultiply( &m1, &m1, &m2);

// パラメータで送られてきた位置や角度とカメラのマトリックスを合わせる
D3DXMatrixMultiply( &m1, &m1, &_transform);

// 行列をスプライトに設定する
HR( _sprite->SetTransform( &m1 ) );

// 実際に描画する
_sprite->Draw( texture, NULL, &ori, NULL, colour );

// 行列をリセットする
D3DXMatrixIdentity( &m1 );
_sprite->SetTransform( &m1 );
}

以上です。実装後はこんな感じになります:

http://www.youtube.com/watch?v=b_6AWmiFz54

今回は2Dゲーム専用のカメラの作り方を紹介します。

 

まず、カメラに必要なメンバを宣言します。今回使うのは、カメラの位置、画面の中心点、ズーム値、カメラがどの位回転しているかを表す値、そして見える範囲です。

 

public class Camera2D
{
Vector2 m_position = Vector2.Zero;
Vector2 m_zoom = Vector2.One;
Rectangle m_visibleArea;
float m_rotation = 0.0f;
Vector2 m_screenPosition = Vector2.Zero;

 

次は、コンストラクタ内で初期化です。引数のwidthとheightは描画領域の大きさです(画面全体の場合は画面の横幅と、縦幅)。

 

public Camera2D(int width, int height)
{
m_visibleArea = new Rectangle(0, 0, width, height);
m_position = new Vector2(width / 2, height / 2);
Position = m_position;
m_screenPosition = new Vector2(width / 2, height / 2);
        }

 

初期化の後は、カメラの位置の移動処理です。まず、位置情報のアクセサは、set時に位置の変更だけでなく、見える範囲の変更も同時にしています。後は、メソッドを作る必要性は無いですが、移動する量を渡して移動するメソッドを作りました。

 

public Vector2 Position
{
get { return m_position; }
set
{
m_position = value;
m_visibleArea.X = (int)(m_position.X - m_visibleArea.Width / 2);
m_visibleArea.Y = (int)(m_position.Y - m_visibleArea.Height / 2);
}
}

public void Move(float x, float y)
{
Position = new Vector2(m_position.X + x, m_position.Y + y);
}

 

最後に、実際にカメラを使っての描画方法です。描画の時には、カメラの持つ情報から行列を作製し、spritebatch.Begin()メソッドのパラメーターに渡します。これにより、spritebatch.End()メソッドが呼ばれるまでの間に行われたspritebatch.Draw()メソッドにはカメラの情報が適用されます。
行列の作製にはまず、位置と画面の中心点を2次元用のベクトルから、3次元用のベクトルに変更します。次に、カメラの持っている情報を組み合わせて行列を作ります。
組み合わせは、移動用行列(-カメラの位置)x スケーリング用の行列(ズーム) x 回転行列(角度) x移動用行列 (画面の中心地)です。最初に移動するのは、カメラの周りを回転したり、サイズ変更したりする為です。
行列同士の掛け算は、順番で結果が変わるので、この順番を守らないと、違う結果になります。ちなみに、3Dモデルとかの移動時にも計算する順番はスケーリング x 回転 x 移動になります。

 

public virtual Matrix GetMatrix()
{
Vector3 pos = new Vector3(m_position, 0);
Vector3 screenPos = new Vector3(m_screenPosition, 0.0f);

return Matrix.CreateTranslation(-pos) *
Matrix.CreateScale(m_zoom.X, m_zoom.Y, 1.0f) *
Matrix.CreateRotationZ(m_rotation) *
Matrix.CreateTranslation(screenPos);
}

 

後はカメラのインスタンスを作り、spritebatch.Begin()を使うときに行列を作るだけです。

 

protected override void Draw()
{
GraphicsDevice.Clear(Color.CornflowerBlue);

//ここの第4引数で、カメラの行列を渡す</pre>
<pre>spriteBatch.Begin(SpriteSortMode.Deferred,
                  BlendState.AlphaBlend,
                  SamplerState.LinearClamp,
                  DepthStencilState.None,
                  RasterizerState.CullCounterClockwise,
                  null,
                  camera.GetMatrix() );

// ...カメラ内の描画
spriteBatch.End();

spriteBatch.Begin();
// ...カメラ外の描画
spriteBatch.End();
}

 

このカメラクラスの使用例です。レベルエディタのゲーム場面の描画部分でカメラが使われています。

以上です。