使用Canvas在WPF C#中跟踪玩家的移动

huangapple go评论76阅读模式
英文:

Tracking player's movement in WPF C# using Canvas

问题

我正在尝试使用WPF C#和Canvas构建原始的吃豆人游戏项目,但在鬼魂的追踪和移动部分遇到了问题。
我的想法是检查鬼魂是否会在下一步移动时穿过墙壁并阻止它。

但显然我编程错误了,鬼魂会一直移动直到达到墙壁,然后向后移动一步,然后再次移动到墙壁,如此反复。

我应该如何修复算法?当然,如果你有其他想法,我很愿意听取。

以下是与鬼魂移动相关的代码:

//flags are used to check if the ghosts next move is through a wall
double x1 = Canvas.GetTop(pacman);
double y = Canvas.GetLeft(pacman);
if (Math.Abs(Canvas.GetTop(redGuy) - x1) > Math.Abs(Canvas.GetLeft(redGuy) - y) && (flagUp == false || flagDown == false))
{
    if (Canvas.GetTop(redGuy) < x1)
    {
        if (flagDown == false)
        {
            Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) + ghostSpeed);
        }
        else if (flagRight == false || flagLeft == false)
        {
            if (Canvas.GetLeft(redGuy) < y && flagLeft == false)
            {
                Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) + ghostSpeed);
            }
            else if (flagRight == false)
            {
                Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) - ghostSpeed);
            }
        }
    }
    else
    {
        if (flagUp == false)
        {
            Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) - ghostSpeed);
        }
        else if (flagRight == false || flagLeft == false)
        {
            if (Canvas.GetLeft(redGuy) < y && flagLeft == false)
            {
                Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) + ghostSpeed);
            }
            else if (flagRight == false)
            {
                Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) - ghostSpeed);
            }
        }
    }
}
else if (flagRight == false || flagLeft == false)
{
    if (Canvas.GetLeft(redGuy) < y)
    {
        if (flagLeft == false)
        {
            Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) + ghostSpeed);
        }
        else if (flagUp == false || flagDown == false)
        {
            if (Canvas.GetTop(redGuy) > x1 && flagUp == false)
            {
                Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) - ghostSpeed);
            }
            else if (flagDown == false)
            {
                Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) + ghostSpeed);
            }
        }
    }
    else
    {
        if (flagRight == false)
        {
            Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) - ghostSpeed);
        }
        else if (flagUp == false || flagDown == false)
        {
            if (Canvas.GetTop(redGuy) > x1 && flagDown == false)
            {
                Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) - ghostSpeed);
            }
            else if (flagRight == false)
            {
                Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) + ghostSpeed);
            }
        }
    }
}

希望这可以帮助你解决问题。

英文:

I'm trying to build a project of the original pacman game in WPF C# using Canvas and I got stuck on the ghosts tracking and movement.
the Idea is to check if the ghost will move through a wall the next move and prevent it.

But apparently I programed it wrong and the ghost is moving until it reaches the wall, then moving a step backwards, and then again move to the wall and so on.

How am I supposed to fix the algorithm? and of course, If you have any other idea I'd love to hear it.

Thanks!

Here's is the ghosts movement relevant code:

//flags are used to check if the ghosts next move is through a wall
double x1 = Canvas.GetTop(pacman);
double y = Canvas.GetLeft(pacman);
if (Math.Abs(Canvas.GetTop(redGuy) - x1) &gt; Math.Abs(Canvas.GetLeft(redGuy) - y) &amp;&amp; (flagUp == false || flagDown == false))
{
if (Canvas.GetTop(redGuy) &lt; x1)
{
if (flagDown == false)
{
Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) + ghostSpeed);
}
else if (flagRight == false || flagLeft == false)
{
if (Canvas.GetLeft(redGuy) &lt; y &amp;&amp; flagLeft == false)
{
Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) + ghostSpeed);
}
else if (flagRight == false)
{
Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) - ghostSpeed);
}
}
}
else
{
if (flagUp == false)
{
Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) - ghostSpeed);
}
else if (flagRight == false || flagLeft == false)
{
if (Canvas.GetLeft(redGuy) &lt; y &amp;&amp; flagLeft == false)
{
Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) + ghostSpeed);
}
else if (flagRight == false)
{
Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) - ghostSpeed);
}
}
}
}
else if (flagRight == false || flagLeft == false)
{
if (Canvas.GetLeft(redGuy) &lt; y)
{
if (flagLeft == false)
{
Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) + ghostSpeed);
}
else if (flagUp == false || flagDown == false)
{
if (Canvas.GetTop(redGuy) &gt; x1 &amp;&amp; flagUp == false)
{
Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) - ghostSpeed);
}
else if (flagDown == false)
{
Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) + ghostSpeed);
}
}
}
else
{
if (flagRight == false)
{
Canvas.SetLeft(redGuy, Canvas.GetLeft(redGuy) - ghostSpeed);
}
else if (flagUp == false || flagDown == false)
{
if (Canvas.GetTop(redGuy) &gt; x1 &amp;&amp; flagDown == false)
{
Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) - ghostSpeed);
}
else if (flagRight == false)
{
Canvas.SetTop(redGuy, Canvas.GetTop(redGuy) + ghostSpeed);
}
}
}
}

答案1

得分: 1

我认为你在游戏设计方面的主要问题在于以下几点:

  1. 不要将用户界面(UI)作为数据存储。

  2. 相反,将UI用作数据的表示。

你可以将游戏区域视为一个方格网格,大致与你的吃豆人/玩家的大小相同。在内部用一个二维数组来表示,例如Square[100,80]。

每个吃豆人或幽灵都与一个方格相关联,使用整数x、y坐标。

如果一个幽灵的坐标与玩家的坐标相同,那么它们就抓住了玩家。

你仍然可以将精灵定位在画布上,并从它们的坐标计算它们的位置。或者,可以使用ItemsControl来表示游戏板,其中Canvas作为ItemsPanel。这是一种相当常见的方法,如果你搜索一下,应该能找到相关信息。基本上,你将一个ObservableCollection绑定到ItemsControl的ItemsSource属性上。该集合具有与其内容相匹配的视图模型,例如PlayViewModel、GhostViewModel、WallViewModel等。然后,使用DataType将其模板化到用户界面中,以将视图模型与数据模板匹配。

你可以通过公开x和y属性并将它们绑定到项目容器的Canvas.Left和Canvas.Top属性来在该网格上定位。可以使用转换器从方格值计算px值。

如果x和y是px值:

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <Setter Property="Canvas.Left" Value="{Binding Path=XPos}" />
        <Setter Property="Canvas.Top" Value="{Binding Path=YPos}" />
    </Style>
</ItemsControl.ItemContainerStyle>

但是一个方格会比一个px要大,所以你需要通过转换器进行一些乘法转换。

你可能会认为这个模板化业务效率不高,但它的扩展性非常好。对于我们的地图编辑应用程序,你可以"绘制"树林的轮廓,然后通过这种方式将其转换为屏幕上的区域。然后在该区域内填充树木,可能会有成千上万棵树。

对于其中的每一个,都有一个TreeVM和一个使用3个模板中的一个的数据模板。

一旦你有了一个网格,你就可以使用路径规划算法,比如空间A*,如果幽灵要朝向吃豆人移动的话。我记不清他们是这样做的还是随机移动。

如果你对游戏设计感兴趣,那么很多游戏需要路径规划,而空间A可能是最常用的方法。特别是如果所有方格的移动成本都相同的话。我们的游戏使用了具有可变成本的空间A

Amit的网页是不错的:

http://theory.stanford.edu/~amitp/GameProgramming/

英文:

I would say your main problem here is in game design.

Don't use UI as a data store.

Instead use UI as a representation of your data.

I'm not sure this is really an answer suitable for SO but it's not going to fit in comments.

Think of your playing area as being a grid of squares. Roughly the size of your pacman/player.

Represent this internally with a 2d array so roughly say Square[100,80].

A given pacman or ghost has a Square they're associated with using integer x,y co-ordinates.

If a ghost has the same co-ordinates as the player then they got him.

You could still position sprites round on the canvas and calculate where they are from their co-ordinates. Alternatively the board could be represented using an itemscontrol with a canvas as itemspanel. This is a fairly common approach you should be able to find if you search. But basically you bind an observablecollection<object> to itemssource of the itemscontrol. That collection has a viewmodel typed to indicate what it is. A playerviewmodel, ghostviewmodel, wallviewmodel etc. These are then templated into UI using datatype to match viewmodel to datatemplate.
You can position on that grid by exposing x,y properties and binding them to the canvas.left and canvas.top properties of the item container. Use a converter to calculate px values from square values.

If x and y were px values:

  &lt;ItemsControl.ItemContainerStyle&gt;
&lt;Style TargetType=&quot;ContentPresenter&quot;&gt;
&lt;Setter Property=&quot;Canvas.Left&quot; Value=&quot;{Binding Path=XPos}&quot; /&gt;
&lt;Setter Property=&quot;Canvas.Top&quot; Value=&quot;{Binding Path=YPos}&quot; /&gt;
&lt;/Style&gt;
&lt;/ItemsControl.ItemContainerStyle&gt;

But a square will be bigger than a px so you want some multiplication conversion via a converter.

You might think this templating business would be inefficient. It scales very well though. For our map editing app one of the things you can do is "draw" the outline of a woods. That is translated into an area on screen in this way. It is then filled with trees. There can be thousands of trees.

For each of these there's a treevm and a datatemplate that uses one of 3 templates.

https://i.stack.imgur.com/pZJVD.png

Once you have a grid you can use a pathing algorithm such as spatial A* if ghosts move towards the pacman. I can't rememember whether they do that or just move randomly.

If you're interested in designing games then many require pathing and spatial A* is probably the most commonly used. Especially if all squares are the same cost. Our game uses spatial A* with variable cost though.

Amit's pages are good:

http://theory.stanford.edu/~amitp/GameProgramming/

huangapple
  • 本文由 发表于 2023年1月9日 16:09:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75054560.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定

  • 开发者交流平台

    本页二维码