Вот очень быстрая реализация решения на основе Canvas
, о котором я упоминал в комментариях. Конечно, для превращения этой игры в реально работоспособную игру потребуется больше работы, но, похоже, она прекрасно справляется с дисплеем.
GameVm
содержит модель представления для текущей игры. Он отслеживает ширину и высоту доски, а также любые «сущности», которые в данный момент находятся в игре. Это включает в себя различные сегменты змеиной части, а также пищевые гранулы.
XAML - это, по сути, ItemsControl
, привязанный к свойству Entities
игры. Он выбирает визуальный элемент на основе шаблона, определяемого типом объекта. Я просто использовал текстовые символы, но вы можете легко заменить их на изображения. ItemsControl
размещается внутри Canvas
, а ContentPresenter
каждого элемента оформляется так, чтобы позиционировать его на Canvas
на основе свойств X
и Y
сущности.
Я бы, вероятно, подумал о том, чтобы изменить это, чтобы использовать индексы столбцов / строк, а не "пиксельные" координаты, с ValueConverter
для преобразования их в пиксели для простоты.
Просмотр моделей:
class MainWindowVm
{
public MainWindowVm()
{
Game = new GameVm(20, 20);
}
public GameVm Game { get; }
}
class GameVm : ViewModel
{
public GameVm(int width, int height)
{
Width = width;
Height = height;
Entities = new ObservableCollection<GameEntity>();
Entities.Add(new SnakeHead() { X = 20, Y = 20 });
Entities.Add(new SnakeBody() { X = 30, Y = 20 });
Entities.Add(new SnakeBody() { X = 40, Y = 20 });
Entities.Add(new SnakeTail() { X = 40, Y = 30 });
Entities.Add(new Food() { X = 0, Y = 0 });
Entities.Add(new Food() { X = 60, Y = 20 });
Entities.Add(new Food() { X = 50, Y = 50 });
Entities.Add(new Food() { X = 10, Y = 80 });
}
public ObservableCollection<GameEntity> Entities { get; }
private int _width;
public int Width
{
get => _width;
set => SetValue(ref _width, value);
}
private int _height;
public int Height
{
get => _height;
set => SetValue(ref _height, value);
}
}
abstract class GameEntity : ViewModel
{
private int _x;
public int X
{
get => _x;
set => SetValue(ref _x, value);
}
private int _y;
public int Y
{
get => _y;
set => SetValue(ref _y, value);
}
}
abstract class SnakeSegment : GameEntity { }
class SnakeBody : SnakeSegment { }
class SnakeHead : SnakeSegment { }
class SnakeTail : SnakeSegment { }
class Food : GameEntity { }
XAML:
<Window x:Class="Snake.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"
xmlns:local="clr-namespace:Snake"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowVm />
</Window.DataContext>
<ItemsControl ItemsSource="{Binding Path=Game.Entities}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:SnakeHead}">
<TextBlock Text="%" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SnakeBody}">
<TextBlock Text="#" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SnakeTail}">
<TextBlock Text="." />
</DataTemplate>
<DataTemplate DataType="{x:Type local:Food}">
<TextBlock Text="O" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding Path=X}" />
<Setter Property="Canvas.Left" Value="{Binding Path=Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Window>
Он производит следующий вывод:
![enter image description here](https://i.stack.imgur.com/7faBI.png)