【问题标题】:WPF 2D Game: Making a Camera that follows an object on a CanvasWPF 2D 游戏:制作跟随画布上对象的相机
【发布时间】:2021-02-13 21:43:30
【问题描述】:

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

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

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

【问题讨论】:

    标签: wpf canvas 2d rectangles


    【解决方案1】:

    以下是如何使用 ScrollViewer 执行此操作的粗略示例。使用箭头键移动玩家,同时将其保持在“相机”的视野中。画布的背景设置为径向画笔,以查看相机的移动。

    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
    }
    

    【讨论】:

    • 谢谢这是一个非常详细的解决方案!
    【解决方案2】:

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

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

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

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

    theScrollView.ScrollToHorizontalOffset(100);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-21
      • 2023-03-15
      • 1970-01-01
      • 2021-09-23
      相关资源
      最近更新 更多