XNA 2D相机引擎跟随精灵

37

如何在XNA游戏中创建最佳视差效果?我希望相机可以跟随我的精灵在世界范围内移动,这样我就可以添加缩放、平移、震动和其他特效。有没有人能提供一个可靠的示例,最好是在GameComponent中实现?

4个回答

45

我使用上述教程的组合来解决问题,并创建了下面的类。它会动态地朝着目标点运动并跟随它移动。试试看。

public interface IFocusable
{
    Vector2 Position { get; }
}

public interface ICamera2D
{
    /// <summary>
    /// Gets or sets the position of the camera
    /// </summary>
    /// <value>The position.</value>
    Vector2 Position { get; set; }

    /// <summary>
    /// Gets or sets the move speed of the camera.
    /// The camera will tween to its destination.
    /// </summary>
    /// <value>The move speed.</value>
    float MoveSpeed { get; set; }

    /// <summary>
    /// Gets or sets the rotation of the camera.
    /// </summary>
    /// <value>The rotation.</value>
    float Rotation { get; set; }

    /// <summary>
    /// Gets the origin of the viewport (accounts for Scale)
    /// </summary>        
    /// <value>The origin.</value>
    Vector2 Origin { get; }

    /// <summary>
    /// Gets or sets the scale of the Camera
    /// </summary>
    /// <value>The scale.</value>
    float Scale { get; set; }

    /// <summary>
    /// Gets the screen center (does not account for Scale)
    /// </summary>
    /// <value>The screen center.</value>
    Vector2 ScreenCenter { get; }

    /// <summary>
    /// Gets the transform that can be applied to 
    /// the SpriteBatch Class.
    /// </summary>
    /// <see cref="SpriteBatch"/>
    /// <value>The transform.</value>
    Matrix Transform { get; }

    /// <summary>
    /// Gets or sets the focus of the Camera.
    /// </summary>
    /// <seealso cref="IFocusable"/>
    /// <value>The focus.</value>
    IFocusable Focus { get; set; }

    /// <summary>
    /// Determines whether the target is in view given the specified position.
    /// This can be used to increase performance by not drawing objects
    /// directly in the viewport
    /// </summary>
    /// <param name="position">The position.</param>
    /// <param name="texture">The texture.</param>
    /// <returns>
    ///     <c>true</c> if the target is in view at the specified position; otherwise, <c>false</c>.
    /// </returns>
    bool IsInView(Vector2 position, Texture2D texture);
}

public class Camera2D : GameComponent, ICamera2D
{
    private Vector2 _position;
    protected float _viewportHeight;
    protected float _viewportWidth;

    public Camera2D(Game game)
        : base(game)
    {}

    #region Properties

    public Vector2 Position
    {
        get { return _position; }
        set { _position = value; }
    }
    public float Rotation { get; set; }
    public Vector2 Origin { get; set; }
    public float Scale { get; set; }
    public Vector2 ScreenCenter { get; protected set; }
    public Matrix Transform { get; set; }
    public IFocusable Focus { get; set; }
    public float MoveSpeed { get; set; }

    #endregion

    /// <summary>
    /// Called when the GameComponent needs to be initialized. 
    /// </summary>
    public override void Initialize()
    {
        _viewportWidth = Game.GraphicsDevice.Viewport.Width;
        _viewportHeight = Game.GraphicsDevice.Viewport.Height;

        ScreenCenter = new Vector2(_viewportWidth/2, _viewportHeight/2);
        Scale = 1;
        MoveSpeed = 1.25f;

        base.Initialize();
    }

    public override void Update(GameTime gameTime)
    {
        // Create the Transform used by any
        // spritebatch process
        Transform = Matrix.Identity*
                    Matrix.CreateTranslation(-Position.X, -Position.Y, 0)*
                    Matrix.CreateRotationZ(Rotation)*
                    Matrix.CreateTranslation(Origin.X, Origin.Y, 0)*
                    Matrix.CreateScale(new Vector3(Scale, Scale, Scale));

        Origin = ScreenCenter / Scale;

        // Move the Camera to the position that it needs to go
        var delta = (float) gameTime.ElapsedGameTime.TotalSeconds;

        _position.X += (Focus.Position.X - Position.X) * MoveSpeed * delta;
        _position.Y += (Focus.Position.Y - Position.Y) * MoveSpeed * delta;

        base.Update(gameTime);
    }

    /// <summary>
    /// Determines whether the target is in view given the specified position.
    /// This can be used to increase performance by not drawing objects
    /// directly in the viewport
    /// </summary>
    /// <param name="position">The position.</param>
    /// <param name="texture">The texture.</param>
    /// <returns>
    ///     <c>true</c> if [is in view] [the specified position]; otherwise, <c>false</c>.
    /// </returns>
    public bool IsInView(Vector2 position, Texture2D texture)
    {
        // If the object is not within the horizontal bounds of the screen

        if ( (position.X + texture.Width) < (Position.X - Origin.X) || (position.X) > (Position.X + Origin.X) )
            return false;

        // If the object is not within the vertical bounds of the screen
        if ((position.Y + texture.Height) < (Position.Y - Origin.Y) || (position.Y) > (Position.Y + Origin.Y))
            return false;

        // In View
        return true;
    }
}

这里是如何与SpriteBatch一起使用它的:

spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                  SpriteSortMode.FrontToBack,
                  SaveStateMode.SaveState,
                  Camera.Transform);
spriteBatch.Draw(_heliTexture,
                 _heliPosition,
                 heliSourceRectangle,
                 Color.White,
                 0.0f,
                 new Vector2(0,0),
                 0.5f,
                 SpriteEffects.FlipHorizontally,
                 0.0f);
spriteBatch.End();

如果这对你有帮助,请告诉我,感谢StackOverflow和社区。W00t!


我知道这是一个比较老的帖子,但这个解决方案非常棒,我很想实现它以更好地理解它。唯一的问题是我似乎无法让它正常工作。我已经把类的所有部分连接好了,已经初始化了,正在更新,代码中没有抛出任何错误。然而,在运行时,该类Update重写的这一点会抛出一个NullReferenceException:_position.X += (Focus.Position .X - Position.X)* MoveSpeed * delta; _position.Y + = (Focus.Position.Y - Position.Y)* MoveSpeed * delta; 调试器说相机.焦点为空...我已经尝试了所有的方法。有什么想法吗?? - user239066
哇,好久不见了。但是如果我正确地阅读我的代码,那么你需要一个精灵来实现IFocusable接口。然后你需要将该精灵传递给相机。希望这可以帮到你。 - Khalid Abuhakmeh
5
如果 IFocusable 的 Focus 属性永远不应该为空,那么最好的构造函数是:Camera2D(Game game, IFocusable focused) - Andreas Grech
4
在Update方法中,您可能需要在计算转换矩阵之前计算Origin属性。否则,在基于比例计算最新原点和进行变换时会出现临时差异。此外,您可以直接使用Matrix.CreateScale(Scale),而无需显式创建新的Vector3。 - Factor Mystic
一开始我在实现这个过程中遇到了一些问题。对于那些遇到空摄像机问题的人,可以在Initialize()函数中加入Components.Add(camera);。顺便说一句,这个摄像机真的很棒 :) - Bogdan Rybak
显示剩余2条评论

19

1
很酷,谢谢。我必须先看一下它们是否可行。 - Khalid Abuhakmeh

4

2
我知道这是一个老问题,但我也有同样的问题,我找到了这个很棒的Monogame相机库,所以我想分享一下...
你只需要添加这个相机库(https://github.com/aloisdeniel/Comora),就可以轻松安装并且跟踪精灵。只需将相机位置设置为您精灵的位置即可:this.camera.Position = 您的精灵位置;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接