1

グラフィックを「デバッグ」するためにこのカメラクラスを実行しました。正常に動作しますが、ズームアウトしてからズームインする理由がわかりません。

マウスの右ボタン(RMB)で画面をドラッグすることで機能するズーム機能を実装しました(左で実際に画面をドラッグします)。これはUnityエディターのズームのように機能します(Altキーを押しながらRMBを使用すると、ズームになります)機能)

人民元を持ってバカのように振ると、ズームインするよりもズームアウトし、最終的にはすべてが小さくなります。正確にそこに..ユニティエディタでは、モロンのように振っても、最終的には超ズームアウトされません...

誰かがこの動作を引き起こしている原因を教えてもらえますか?ちょっとトリッキーです..ここに私のクラスがあります、単純さを気にしないでください、私はデバッグのためにそれを投げました:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Shooter

{{

/// <summary>
/// Provides a camera controllable by mouse.
/// Controls include dragging the "screen"...
/// </summary>
class MouseCamera2D
{

    private Matrix m_mView;       // stores the camera state (position, scale/zoom, rotation)
    public Matrix ViewMatrix { get { return m_mView; }  }
    public Vector3 GetPosition() { return m_mView.Translation;  }

    public float m_scaleProportion = 0.01f; // the "speed" to scale, multiplies the offset from mouse drag

    // input data

    private MouseState m_mouseStateRef;
    private MouseState m_mouseStatePreviows = new MouseState();
    private KeyboardState m_kbStateRef;
    private KeyboardState m_kbStatePreviows = new KeyboardState();

    public Keys m_resetZoomKey = Keys.Z;


    //private GraphicsDevice m_graphicsDeviceRef;

    // ctor
    public MouseCamera2D(MouseState mouseState_p, KeyboardState kbState_p/*, GraphicsDevice graphicsDevice_p*/){

        if (mouseState_p == null ||kbState_p == null /*|| graphicsDevice_p == null*/) throw new Exception("cant be null"); // needs the reference

        //m_graphicsDeviceRef = graphicsDevice_p;

        m_mouseStateRef = mouseState_p; // init the reference
        m_kbStateRef = kbState_p;

        m_mView = Matrix.Identity; // set a valid matrix

        //zoomPivot = new Vector2(m_graphicsDeviceRef.Viewport.Width * 0.5f, m_graphicsDeviceRef.Viewport.Height * 0.5f);
    }



    public void InputUpdate()
    {
        m_mouseStatePreviows = m_mouseStateRef;
        m_mouseStateRef = Mouse.GetState();

        m_kbStatePreviows = m_kbStateRef;
        m_kbStateRef = Keyboard.GetState();

        InputDragControl();
        InputScaleControl();
        InputZoomOriginal();
    }

    private void InputDragControl()
    {
        //mouseStatePreviows = mouseStateRef;
       // mouseStateRef = Mouse.GetState();

        if (m_mouseStateRef.LeftButton == ButtonState.Pressed)
        {
            // check if its a drag or a new pivot point

            if (m_mouseStatePreviows.LeftButton == ButtonState.Pressed)
            {
                m_mView.M41 += (m_mouseStateRef.X - m_mouseStatePreviows.X);
                m_mView.M42 += (m_mouseStateRef.Y - m_mouseStatePreviows.Y);
            }
        }

    }

    Vector3 zoomPivot;
    private void InputScaleControl()
    {

        if (m_mouseStateRef.RightButton == ButtonState.Pressed)
        {
            // check if its a drag or a new pivot point

            if (m_mouseStatePreviows.RightButton == ButtonState.Pressed)
            {

                float scale = ((m_mouseStateRef.X - m_mouseStatePreviows.X) + (m_mouseStateRef.Y - m_mouseStatePreviows.Y)); //compute distance with "1d direction"(not abs)

                if (scale != 0.0f)
                {

                    scale *= m_scaleProportion;                       

                    //center zoom on mouse cursor:
                    m_mView *=

                                Matrix.CreateTranslation(-zoomPivot)
                                *
                                Matrix.CreateScale(1.0f + scale)
                                *
                                Matrix.CreateTranslation(zoomPivot)
                                ;

                    Console.WriteLine(scale.ToString());
                    Console.WriteLine(m_mView.M11.ToString());
                    Console.WriteLine("");
                }
            }
            else
            {
                // new press, get pivot point:

                zoomPivot.X = m_mouseStateRef.X;
                zoomPivot.Y = m_mouseStateRef.Y;
            }
        }
    }


    private void InputZoomOriginal()
    {
        if( m_kbStateRef.IsKeyDown(m_resetZoomKey) )
        {

            m_mView *=

                       Matrix.CreateTranslation(-zoomPivot)
                       *
                       Matrix.CreateScale(1.0f/m_mView.M11)
                       *
                       Matrix.CreateTranslation(zoomPivot)
                       ;

            Console.WriteLine(m_mView.M11.ToString());
                    Console.WriteLine("");
        }
    }
}

}

4

1 に答える 1

2

あなたの問題はこのコードのビットから来ています:

1.0f + scale

1ピクセルの動きを見てみましょう。を使用すると、ズームアウトすると0.99m_scaleProportion = 0.01が乗算されます。ズームインすると、 1.01が乗算されます。

1ピクセルずつズームインしてから、1ピクセルずつズームアウトするとどうなりますか?基本的に、2つの値を乗算します。そのように:0.99×1.01=0.9999。これは1未満です。これを繰り返すと、最終的にズーム量はどんどん小さくなります。


ズームを適切に実装する方法:

まず第一に、ほとんどの場合、乗算によって値を累積しないことをお勧めします(つまり、入力に基づいて多くのフレームにわたって値を変更します)。コードでは、(で行っているように)行列を*=絶えず乗算しています。これにより、浮動小数点の問題がゆっくりと発生する可能性があります。(つまり、行列では実行しないでください。また、通常の場合でも実行しないでくださいfloat

あなたがすべきことは、加算によってマウスの動きを蓄積することです。そして、これまでの全体的な動きに基づいてマトリックスを更新します。このようなもの(擬似コード):

if(mouseWentDown)
{
    // store the starting matrix:
    this.originalMatrix = this.currentMatrix;
    int zoomAmount = 0;
}
else if(mouseIsDown)
{
    // Accumulate the mouse movment:
    zoomAmount += (how far the mouse moved this frame);

    // Recalculate the matrix based on the starting matrix:
    this.currentMatrix = this.originalMatrix * 
            Matrix.CreateScale((float)Math.Pow(2, zoomAmount * zoomRate));
}

Math.Pow実際のスケーリング係数を提供する関数の使用に注意してください。この関数は、負の入力の場合は1未満の値を、正の入力の場合は1より大きい値を与えるので便利です。ゼロの入力の場合、1(スケーリングなし)になります。

そしてもちろん、上記にピボットコードを追加することをお勧めします。同様になります。ズーム率も調整する必要があるかもしれません。

注:を使用した2D作業にこのマトリックスを使用している場合はSpriteBatch、Z軸をスケーリングしないでください。X軸とY軸のみをスケーリングし、Z=1のままにします。


余談ですが、技術的には、上記のコードは乗算によって累積されていることに気付くかもしれませんが、ズームが完了したときだけです(したがって、それほど悪くはありません)。私自身のコードでは、通常、ズーム量を常に保存し(ゼロにリセットすることはありません)、完全なマトリックスを再生成します。これは、もう少し堅牢です。

于 2012-08-10T14:29:56.657 に答える