WPF 2D 游戏:制作一个跟随 Canvas 上的对象的相机

WPF 2D Game: Making a Camera that follows an object on a Canvas

我正在尝试用 WPF 制作一个简单的 2D 游戏,但遇到了一个我无法解决的问题。

假设我有一个 700x700 Canvas 的播放器,但我的主窗口的宽度和高度设置为 400。 我希望能够有一个类似相机的功能,它跟随 canvas 上的一个矩形对象(这个对象象征着玩家),并在玩家移动时显示 canvas 的相应部分。

理论上我怎样才能实现这样的功能?

WPF 并不是真正适合游戏的技术,最好使用 MonoGame 之类的技术。

不过,要回答您的问题,您可以将 canvas 包装在 ScrollViewer 中:

<ScrollViewer x:Name="theScrollView" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" CanContentScroll="True">
    <Canvas x:Name="theCanvas" Width="5000" Height="5000" />
</ScrollViewer>

然后在代码中将视图滚动到相机所在的任何位置:

theScrollView.ScrollToHorizontalOffset(100);

这里有一个关于如何使用 ScrollViewer 执行此操作的粗略示例。使用箭头键移动播放器,同时将其保持在“相机”的视野中。 canvas' 背景设置为径向画笔,以查看相机移动。

MainWindow.xaml

<Window x:Class="WpfApp6.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow"
        Background="#222"
        Loaded="Window_Loaded"
        SizeToContent="WidthAndHeight"
        PreviewKeyDown="Window_PreviewKeyDown">

    <Grid>
        <ScrollViewer x:Name="CanvasViewer" 
                      HorizontalScrollBarVisibility="Hidden"
                      VerticalScrollBarVisibility="Hidden">
            <Canvas x:Name="Canvas"
                    IsHitTestVisible="False">
                <Canvas.Background>
                    <RadialGradientBrush>
                        <GradientStop Offset="0"
                                      Color="Orange" />
                        <GradientStop Offset="1"
                                      Color="Blue" />
                    </RadialGradientBrush>
                </Canvas.Background>
            </Canvas>
        </ScrollViewer>
    </Grid>

</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    double _playerSize;

    Rectangle _playerRect;
    Vector _playerPosition;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        InitializeSizes();
        InitializePlayerRect();
    }

    #region initialize
    private void InitializeSizes()
    {
        _playerSize = 50;

        Canvas.Width = 700;
        Canvas.Height = 700;

        CanvasViewer.Width = 400;
        CanvasViewer.Height = 400;
    }

    private void InitializePlayerRect()
    {
        _playerRect = new Rectangle
        {
            Fill = Brushes.Lime,
            Width = _playerSize,
            Height = _playerSize,
            HorizontalAlignment = HorizontalAlignment.Left,
            VerticalAlignment = VerticalAlignment.Top
        };

        Canvas.Children.Add(_playerRect);
    }
    #endregion

    #region move player
    private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Left: MovePlayerLeft(); break;
            case Key.Up: MovePlayerUp(); break;
            case Key.Right: MovePlayerRight(); break;
            case Key.Down: MovePlayerDown(); break;
        }
    }

    private void MovePlayerLeft()
    {
        var newX = _playerPosition.X - _playerSize;
        _playerPosition.X = Math.Max(0, newX);
        UpdatePlayerPositionAndCamera();
    }

    private void MovePlayerUp()
    {
        var newY = _playerPosition.Y - _playerSize;
        _playerPosition.Y = Math.Max(0, newY);
        UpdatePlayerPositionAndCamera();
    }

    private void MovePlayerRight()
    {
        var newX = _playerPosition.X + _playerSize;
        _playerPosition.X = Math.Min(Canvas.Width - _playerSize, newX);
        UpdatePlayerPositionAndCamera();
    }

    private void MovePlayerDown()
    {
        var newY = _playerPosition.Y + _playerSize;
        _playerPosition.Y = Math.Min(Canvas.Height - _playerSize, newY);
        UpdatePlayerPositionAndCamera();
    }
    #endregion

    #region update player and camera
    private void UpdatePlayerPositionAndCamera()
    {
        UpdatePlayerPosition();
        UpdateCamera();
    }

    private void UpdatePlayerPosition()
    {
        // move the playerRect to it's new position
        _playerRect.Margin = new Thickness(_playerPosition.X, _playerPosition.Y, 0, 0);
    }

    private void UpdateCamera()
    {
        // calculate offset of scrollViewer, relative to actual position of the player
        var offsetX = _playerPosition.X / 2;
        var offsetY = _playerPosition.Y / 2;

        // move the "camera"
        CanvasViewer.ScrollToHorizontalOffset(offsetX);
        CanvasViewer.ScrollToVerticalOffset(offsetY);
    }
    #endregion
}