Как связать логический массив со свойствами видимости сетки прямоугольников в WPF? - PullRequest
0 голосов
/ 04 сентября 2018

Я пытаюсь сделать простую игру со змеями в WPF, используя сетку прямоугольников. Я закончил логику змеи более или менее, но я озадачен тем, как на самом деле визуализировать мой двумерный массив логических элементов в виде пикселей (прямоугольников), видимых или нет. Этот вопрос адресован , как рассматривать видимость как логическое значение, но как бы я увеличил это?

Приведенный ниже код описывает движение змеи, я собираюсь запустить его метод обновления в отдельном потоке. Он работает путем добавления объектов SnakeBod в список, которые имеют свойство age. Когда возраст превышает определенное число, метод Update прекращает обрабатывать его как существующий. Я стремлюсь контролировать змею с помощью событий KeyDown.

Вопрос: как связать двумерный массив VisibleBods с сеткой 64x64 в моем MainWindow?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace SnakeGame
{
    static class Snake
    {
        static public int SnakeLife;
        static public List<SnakeBod> SnakeBods;
        static SnakePos headPos;
        static public bool[,] VisibleBods = new bool[64, 64];
        internal static SnakePos HeadPos
        {
            get => headPos;
            set
            {
                if (headPos.X != value.X || headPos.Y != value.Y)
                {
                    if (headPos.X > 64 || headPos.X < 0 || headPos.Y > 64 || headPos.X < 0) SnakeLife = -1;
                    bool exists = false;
                    headPos = value;
                    foreach (SnakeBod curBod in SnakeBods)
                    {
                        if (curBod.BodPos.X == value.X && curBod.BodPos.Y == value.Y)
                        {
                            exists = true;
                            if (curBod.age > SnakeLife) curBod.age = 0;
                            else SnakeLife = -1;
                        }
                    }
                    if (!exists) SnakeBods.Add(new SnakeBod(value.X, value.Y));
                }
            }
        }


        static int snakeWait;
        static Direction SnakeDir;
        enum Direction : int
        {
            LEFT = 0,
            UP = 1,
            RIGHT = 2,
            DOWN = 3,
        }

        static Snake()
        {
            headPos = new SnakePos(32, 32);
            for (int i = 0; i < 64; i++)
            {
                for (int j = 0; j < 64; j++)
                {
                    VisibleBods[i, j] = false;
                }
            }
            SnakeLife = 10;
        }

        static void UpdateBod()
        {
            for (int i = 0; i < 64; i++)
            {
                for (int j = 0; j < 64; j++)
                {
                    VisibleBods[i, j] = false;
                }
            }
            foreach (SnakeBod curBod in SnakeBods)
            {
                if (curBod.age < SnakeLife) VisibleBods[curBod.BodPos.X, curBod.BodPos.Y] = true;
            }

        }

        static Thread UpdateThread;
        static void UpdateSnake()
        {
            Thread.CurrentThread.IsBackground = true;
            while (SnakeLife > 0)
            {
                switch (SnakeDir)
                {
                    case Direction.LEFT:
                        HeadPos = new SnakePos(HeadPos.X - 1, HeadPos.Y);
                        break;
                    case Direction.UP:
                        HeadPos = new SnakePos(HeadPos.X, HeadPos.Y - 1);
                        break;
                    case Direction.RIGHT:
                        HeadPos = new SnakePos(HeadPos.X + 1, HeadPos.Y);
                        break;
                    case Direction.DOWN:
                        HeadPos = new SnakePos(HeadPos.X, HeadPos.Y + 1);
                        break;
                }
                foreach (SnakeBod curBod in SnakeBods)
                {
                    curBod.age++;
                }
                Thread.Sleep(snakeWait);
            }
        }
    }

    class Crumb
    {

    }

    class SnakeBod
    {
        public SnakePos BodPos;
        public int age;
        public SnakeBod(int xIn, int yIn)
        {
            age = 0;
            BodPos.X = xIn;
            BodPos.Y = yIn;
        }
    }

    internal struct SnakePos
    {
        public int X;
        public int Y;

        public SnakePos(int xIn, int yIn)
        {
            X = xIn;
            Y = yIn;
        }
    }
}

РЕДАКТИРОВАТЬ: я также знаю этот код проекта , но я хотел попробовать это по-своему. Обычно я делаю привязки в основном в XAML, и, поскольку это крупный масштаб, я решил, что это должно быть сделано в Codebehind. Одним из самых больших препятствий является то, как связать элемент массива внутри ViewModel, так как для обычных полей я просто вызываю OnPropertyChanged (), когда установлено.

Ответы [ 2 ]

0 голосов
/ 04 сентября 2018

Чтобы обновить пользовательский интерфейс при изменении видимости отдельной ячейки в сетке ячеек, вы должны реализовать стандартный подход MVVM, то есть связать ItemsControl с коллекцией элементов ячейки с помощью класса элементов, который реализует INotifyPropertyChanged:

public class Cell : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool isVisible;
    public bool IsVisible
    {
        get { return isVisible; }
        set
        {
            if (isVisible != value)
            {
                isVisible = value;
                PropertyChanged?.Invoke(this,
                    new PropertyChangedEventArgs(nameof(IsVisible)));
            }
        }
    }
}

public class ViewModel
{
    public List<Cell> Cells { get; } =
        Enumerable.Range(0, 4096).Select(i => new Cell()).ToList();
}

ItemsControl будет использовать UniformGrid для отображения сетки ячеек:

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Grid>
    <ItemsControl ItemsSource="{Binding Cells}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Columns="64"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle Width="10" Height="10" Fill="Green"
                           Visibility="{Binding IsVisible,
                               Converter={StaticResource BooleanToVisibilityConverter}}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Чтобы изменить видимость ячейки:

((ViewModel)DataContext).Cells[index].IsVisible = true;
0 голосов
/ 04 сентября 2018

Самое простое решение, которое я вижу, состоит в том, чтобы вообще исключить любой из массивов [,]:

public MyType[] RegularArray { get { return ReduceArrayDimensionality(TwoDimensionalArray); } }

Вы можете приступить к неприятным расширениям .Linq, чтобы добиться этого более привлекательным способом, я думаю, .SelectMany(array => array).ToArray() подойдет.

Тогда твоя привязка станет обычной. Единственное, о чем вам нужно заботиться, уместно BooleanToWahteverYouLikeConverter : IValueConverter - этот парень входит в состав системы привязки WPF. Вы можете найти множество учебных пособий и примеров. Ваш конвертер должен быть достаточно умным, чтобы понять, какой именно boolean следует учитывать на этот раз. Используйте ConverterParameter для передачи необходимого индекса.

Вот и все.

ПОЦЕЛУЙ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...