gpt4 book ai didi

c# - 如何修复网格行和列在 wpf 中不更新?

转载 作者:行者123 更新时间:2023-12-05 05:43:59 25 4
gpt4 key购买 nike

我正在尝试在 WPF 中制作贪吃蛇游戏,因此我决定使用网格来显示棋盘。蛇应该移动它的 x 和 y 位置来改变网格列和网格行属性。为此,我创建了一个 SnakePlayer 类,一个 Food 类。在 MainWindow 中,我每 200 毫秒调用一次游戏循环,然后听键盘设置蛇的方向。问题是,即使蛇的 x, y 位置在代码中正确更改(我对此进行了测试),蛇的位置变化没有可视化,因为它一直保持在初始位置。

贪吃蛇类:

namespace Snake
{
internal class SnakePlayer
{
// keeps track of the current direction and makes the snake keep moving
public (int x, int y) Acceleration = (x: 0, y: 1);
//rappresents the coordinate of each snake part
private readonly List<(int x, int y)> Body = new();
public (int x, int y) Head;
public SnakePlayer(int NUMBER_OF_ROWS, int NUMBER_OF_COLUMNS)
{
int x = Convert.ToInt32((NUMBER_OF_ROWS - 1) / 2);
int y = Convert.ToInt32((NUMBER_OF_COLUMNS - 1) / 2);
Body.Add((x, y));
Head = Body.ElementAt(0);
}
public void UpdatePosition()
{
for (int i = Body.Count - 2; i >= 0; i--)
{
(int x, int y) = Body.ElementAt(i);
Body[i + 1] = (x, y);
}
MoveHead();
}
private void MoveHead()
{
// for example if acceleration is (1,0) the head keeps going to the right each time the method is called
Head.x += Acceleration.x;
Head.y += Acceleration.y;
}
public void Show(Grid gameGrid)
{
/*
* i basically erase all the previous snake parts and
* then draw new elements at the new positions
*/
gameGrid.Children.Clear();
Body.ForEach(tail =>
{
Border element = GenerateBodyPart(tail.x, tail.y);
gameGrid.Children.Add(element);
});
}
private static Border GenerateBodyPart(int x, int y)
{
static void AddStyles(Border elem)
{
elem.HorizontalAlignment = HorizontalAlignment.Stretch;
elem.VerticalAlignment = VerticalAlignment.Stretch;
elem.CornerRadius = new CornerRadius(5);
elem.Background = Brushes.Green;
}
Border elem = new();
AddStyles(elem);
Grid.SetColumn(elem, x);
Grid.SetRow(elem, y);
return elem;
}
public void Grow()
{
var prevHead = (Head.x,Head.y);
AddFromBottomOfList(Body,prevHead);
}
public bool Eats((int x, int y) position)
{
return Head.x == position.x && Head.y == position.y;
}
public void SetAcceleration(int x, int y)
{
Acceleration.x = x;
Acceleration.y = y;
UpdatePosition();
}
public bool Dies(Grid gameGrid)
{
bool IsOutOfBounds(List<(int x, int y)> Body)
{
int mapWidth = gameGrid.ColumnDefinitions.Count;
int mapHeight = gameGrid.RowDefinitions.Count;
return Body.Any(tail => tail.x > mapWidth || tail.y > mapHeight || tail.x < 0 || tail.y < 0);
}
bool HitsItsSelf(List<(int x, int y)> Body)
{
return Body.Any((tail) =>
{
bool isHead = Body.IndexOf(tail) == 0;
if (isHead) return false;
return Head.x == tail.x && Head.y == tail.y;
});
}
return IsOutOfBounds(Body) || HitsItsSelf(Body);
}
public bool HasElementAt(int x, int y)
{
return Body.Any(tail => tail.x == x && tail.y == y);
}
private static void AddFromBottomOfList<T>(List<T> List,T Element)
{
List<T> ListCopy = new();
ListCopy.Add(Element);
ListCopy.AddRange(List);
List.Clear();
List.AddRange(ListCopy);
}
}
}

食品类:

namespace Snake
{
internal class Food
{
public readonly SnakePlayer snake;
public (int x, int y) Position { get; private set; }
public Food(SnakePlayer snake, Grid gameGrid)
{
this.snake = snake;
Position = GetInitialPosition(gameGrid);
Show(gameGrid);
}
private (int x, int y) GetInitialPosition(Grid gameGrid)
{
(int x, int y) getRandomPosition()
{
static int RandomPositionBetween(int min, int max)
{
Random random = new();
return random.Next(min, max);
}
int cols = gameGrid.ColumnDefinitions.Count;
int rows = gameGrid.RowDefinitions.Count;
int x = RandomPositionBetween(0, cols);
int y = RandomPositionBetween(0, rows);
return (x, y);
}
var position = getRandomPosition();
if (snake.HasElementAt(position.x, position.y)) return GetInitialPosition(gameGrid);
return position;
}
public void Show(Grid gameGrid)
{
static void AddStyles(Border elem)
{
elem.HorizontalAlignment = HorizontalAlignment.Stretch;
elem.VerticalAlignment = VerticalAlignment.Stretch;
elem.CornerRadius = new CornerRadius(500);
elem.Background = Brushes.Red;
}
Border elem = new();
AddStyles(elem);
Grid.SetColumn(elem, Position.x);
Grid.SetRow(elem, Position.y);
gameGrid.Children.Add(elem);
}
}
}

主窗口:

namespace Snake
{
public partial class MainWindow : Window
{
const int NUMBER_OF_ROWS = 15, NUMBER_OF_COLUMNS = 15;
private readonly SnakePlayer snake;
private Food food;
private readonly DispatcherTimer Loop;
public MainWindow()
{
InitializeComponent();
CreateBoard();
snake = new SnakePlayer(NUMBER_OF_ROWS, NUMBER_OF_COLUMNS);
food = new Food(snake, GameGrid);
GameGrid.Focus();
GameGrid.KeyDown += (sender, e) => OnKeySelection(e);
Loop = SetInterval(GameLoop, 200);
}
private void GameLoop()
{
snake.UpdatePosition();
snake.Show(GameGrid);
food.Show(GameGrid);
if (snake.Eats(food.Position))
{
food = new Food(snake, GameGrid);
snake.Grow();
}
else if (snake.Dies(GameGrid))
{
Loop.Stop();
snake.UpdatePosition();
ResetMap();
ShowEndGameMessage("You Died");
}
}
private void OnKeySelection(KeyEventArgs e)
{
if(e.Key == Key.Escape)
{
Close();
return;
}
var DIRECTIONS = new
{
UP = (0, 1),
LEFT = (-1, 0),
DOWN = (0, -1),
RIGHT = (1, 0),
};
Dictionary<string, (int x, int y)> acceptableKeys = new()
{
{ "W", DIRECTIONS.UP },
{ "UP", DIRECTIONS.UP },
{ "A", DIRECTIONS.LEFT },
{ "LEFT", DIRECTIONS.LEFT },
{ "S", DIRECTIONS.DOWN },
{ "DOWN", DIRECTIONS.DOWN },
{ "D", DIRECTIONS.RIGHT },
{ "RIGHT", DIRECTIONS.RIGHT }
};
string key = e.Key.ToString().ToUpper().Trim();
if (!acceptableKeys.ContainsKey(key)) return;
(int x, int y) = acceptableKeys[key];
snake.SetAcceleration(x, y);
}
private void CreateBoard()
{
for (int i = 0; i < NUMBER_OF_ROWS; i++)
GameGrid.RowDefinitions.Add(new RowDefinition());
for (int i = 0; i < NUMBER_OF_COLUMNS; i++)
GameGrid.ColumnDefinitions.Add(new ColumnDefinition());
}
private void ResetMap()
{
GameGrid.Children.Clear();
GameGrid.RowDefinitions.Clear();
GameGrid.ColumnDefinitions.Clear();
}
private void ShowEndGameMessage(string message)
{
TextBlock endGameMessage = new();
endGameMessage.Text = message;
endGameMessage.HorizontalAlignment = HorizontalAlignment.Center;
endGameMessage.VerticalAlignment = VerticalAlignment.Center;
endGameMessage.Foreground = Brushes.White;
GameGrid.Children.Clear();
GameGrid.Children.Add(endGameMessage);
}
private static DispatcherTimer SetInterval(Action cb, int ms)
{
DispatcherTimer dispatcherTimer = new();
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(ms);
dispatcherTimer.Tick += (sender, e) => cb();
dispatcherTimer.Start();
return dispatcherTimer;
}
}
}

主窗口.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"
WindowStyle="None"
Background="Transparent"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="600" Width="600" ResizeMode="NoResize" AllowsTransparency="True">
<Border CornerRadius="20" Height="600" Width="600" Background="#FF0D1922">
<Grid x:Name="GameGrid" Focusable="True" ShowGridLines="False"/>
</Border>
</Window>

最佳答案

虽然我不完全理解您希望这款游戏的用户体验,但我进行了一些更改以产生更有意义的结果。

您的 UI 未更新的主要原因是您从未更改 GenerateBodyPart 的位置。它始终等于其初始值。您不应传递永远不变的“尾部”,而应传递具有新位置的“头部”。

改变这个:

private static Border GenerateBodyPart(int x, int y)
{
....
Grid.SetColumn(elem, tail.x);
Grid.SetRow(elem, tail.y);
return elem;
}

要这样(注意我删除了 static 关键字以到达 Head):

private Border GenerateBodyPart(int x, int y)
{
....
Grid.SetColumn(elem, Head.x);
Grid.SetRow(elem, Head.y);
return elem;
}

此外,您的“方向”对于向上和向下都不正确。应该是这样的:

var DIRECTIONS = new
{
UP = (0, -1),
LEFT = (-1, 0),
DOWN = (0, 1),
RIGHT = (1, 0),
};

进行这些更改后,UI 至少更新了蛇的位置。 Watch video .我不确切知道你希望蛇如何显示,但那是你以后要弄清楚的。 :)

祝您编码愉快!

这是我的完整源代码以供引用:Download here

关于c# - 如何修复网格行和列在 wpf 中不更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71690908/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com