-1

I am trying to make a simple 2D Game in WPF, and I've come across a problem I can't solve.

Let's say I have a player on a 700x700 Canvas, but my MainWindow's Width and Height are set to 400. I want to be able to have a camera like feature, that follows a rectangle object on the canvas (this object symbolises the player) and shows the corresponding portion of the canvas whenever the player moves.

In theory how could I implement a feature like this?

k0ntrol
  • 17
  • 4

2 Answers2

1

WPF isn't really the right technology for games, you're much better off using something like MonoGame.

To answer your question, though, you can wrap your canvas in a ScrollViewer:

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

Then in code you scroll the view to whatever position your camera is at:

theScrollView.ScrollToHorizontalOffset(100);
Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
0

Here's a rough sample of how to do that with a ScrollViewer. Use the arrow keys to move the player around while keeping it in the view of the "camera". The canvas' background is set to a radial-brush, to see the camera moving.

player at left/top corner

player at right/top corner

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
}
m e
  • 71
  • 4