映射C#移动量的可能路径
本文关键字:路径 移动 映射 | 更新日期: 2023-09-27 18:20:49
我正在重新创建cluedo游戏,我想绘制玩家在掷骰子后可以移动的可能路径。
我通过绘制图片框并将其命名到映射位置来映射网格。
以下是我迄今为止可能的路径代码:
int Roll;
private void RollDice()
{
ResetTiles();
JimRandom Random = new JimRandom();
//Roll DIce 1
int dice1 = Random.Next(1, 7);
//Roll DIce 2
int dice2 = Random.Next(1, 7);
Roll = dice1 + dice2;
//Set Dice images
pbDice1.BackgroundImage = Roller[dice1 - 1].Picture;
pbDice2.BackgroundImage = Roller[dice2 - 1].Picture;
btnRoll.Enabled = false;
Test(Roll);
//Show available moves
Control[] lCurrent = PnlBoard.Controls.Find("pnl" + CurrentPlauer, true);
Panel Current = null;
System.Drawing.Point CurrentLoc = new System.Drawing.Point(0, 0);
foreach (Control c in lCurrent)
{
Current = c as Panel;
CurrentLoc = new System.Drawing.Point(c.Location.X, c.Location.Y);
}
//Dynamic map
List<string> possiblities = new List<string>();
int currentRow = CurrentLoc.Y / tileWidth;
int currentCol = CurrentLoc.X / tileHeight;
//Find all possible start blocks
string down = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow + 1);
string up = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow - 1);
string left = String.Format("Col={0:00}-Row={1:00}", currentCol - 1, currentRow);
string right = String.Format("Col={0:00}-Row={1:00}", currentCol + 1, currentRow);
List<string> startBlocks = new List<string>();
//See if down is available
Control[] LPossible = PnlBoard.Controls.Find(down, true);
if (LPossible.Length > 0)
{
startBlocks.Add(down);
}
//See if Up is available
LPossible = PnlBoard.Controls.Find(up, true);
if (LPossible.Length > 0)
{
startBlocks.Add(up);
}
//See if left is available
LPossible = PnlBoard.Controls.Find(left, true);
if (LPossible.Length > 0)
{
startBlocks.Add(left);
}
//See if right is available
LPossible = PnlBoard.Controls.Find(right, true);
if (LPossible.Length > 0)
{
startBlocks.Add(right);
}
//possiblilities 1
foreach (string s in startBlocks)
{
Control[] lStarBlock = PnlBoard.Controls.Find(s, true);
PictureBox startBlock = lStarBlock[0] as PictureBox;
int sRow = startBlock.Location.Y / tileWidth;
int sCol = startBlock.Location.X / tileHeight;
//Rows
for (int row = sRow; row < sRow + Roll; row++)
{
//Columns
for (int col = sCol; col < sCol + Roll; col++)
{
possiblities.Add(String.Format("Col={0:00}-Row={1:00}", col, row));
}
}
}
//Show possible moves
foreach (string p in possiblities)
{
LPossible = PnlBoard.Controls.Find(p, true);
if (LPossible.Length > 0)
{
PictureBox active = LPossible[0] as PictureBox;
active.Image = Cluedo.Properties.Resources.TileActive;
System.Threading.Thread.Sleep(1);
Application.DoEvents();
}
//else
//{
// break;
//}
}
}
这里有很多不同的地方。这更像是一篇代码审查文章,但最终还是有一个解决问题的方法,也许剩下的可以帮助你改善代码的整体状态。
随机性
您正在为每个方法调用创建一个新的随机生成器实例:
JimRandom Random = new JimRandom();
如果快速连续调用该方法,这通常会导致生成相同的值。也许这就是你使用加密RNG而不是PRNG的原因?一个PRNG应该足够像这样的游戏,只要你重用它
使用正确的类型
您使用以下代码确定当前玩家位置:
//Show available moves
Control[] lCurrent = PnlBoard.Controls.Find("pnl" + CurrentPlauer, true);
Panel Current = null;
System.Drawing.Point CurrentLoc = new System.Drawing.Point(0, 0);
foreach (Control c in lCurrent)
{
Current = c as Panel;
CurrentLoc = new System.Drawing.Point(c.Location.X, c.Location.Y);
}
看起来CurrentPlauer
是一个字符串。创建一个Player
类来存储玩家的名称和当前位置会让事情变得更容易:
Point currentLocation = currentPlayer.Location;
从UI代码中分离游戏逻辑
您通过对控件进行字符串查找来检查可通过的瓦片:
string down = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow + 1);
// ...
Control[] LPossible = PnlBoard.Controls.Find(down, true);
if (LPossible.Length > 0)
{
startBlocks.Add(down);
}
通常,2D阵列用于像这样的瓦片映射,可能封装在Tilemap
或Map
类中。这使得使用分幅更加自然,因为您可以直接在分幅坐标中工作,而不必在UI和分幅坐标之间进行转换。它还将代码更干净地分解为游戏逻辑和UI部分(没有UI,你的帖子中的代码是不可能测试的):
// TileMap class:
public bool IsPassable(int x, int y)
{
if (x < 0 || x >= Width || y < 0 || y >= Height)
return false;
return tiles[x][y] != Tile.Wall; // enum Tile { Wall, Ballroom, DiningRoom, Hall, ... }
}
// When called from your Board code:
if (map.IsPassable(currentLocation.X, currentLocation.Y + 1))
startBlocks.Add(new Point(currentLocation.X, currentLocation.Y + 1));
减少重复
至于检查所有直接相邻的瓦片,不需要重复相同的代码4次:
// Let's make a utility function:
public static IEnumerable<Point> GetNeighboringPositions(Point position)
{
yield return new Point(position.X - 1, position.Y);
yield return new Point(position.X, position.Y - 1);
yield return new Point(position.X + 1, position.Y);
yield return new Point(position.X, position.Y + 1);
}
// In the Board code:
foreach (Point neighboringPosition in GetNeighboringPositions(currentPosition))
{
if (map.IsPassable(neighboringPosition.X, neighboringPosition.Y))
startBlocks.Add(neighboringPosition);
}
确定有效移动
最后,我们得到了决定当前玩家可以移动到哪些区块的代码:
//possiblilities 1
foreach (string s in startBlocks)
{
Control[] lStarBlock = PnlBoard.Controls.Find(s, true);
PictureBox startBlock = lStarBlock[0] as PictureBox;
int sRow = startBlock.Location.Y / tileWidth;
int sCol = startBlock.Location.X / tileHeight;
//Rows
for (int row = sRow; row < sRow + Roll; row++)
{
//Columns
for (int col = sCol; col < sCol + Roll; col++)
{
possiblities.Add(String.Format("Col={0:00}-Row={1:00}", col, row));
}
}
}
这样做的目的是检查一个矩形区域,使用起始位置作为其左上角。它对多达4个相邻位置执行此操作,因此矩形将部分重叠。这是行不通的。如果地图上没有任何障碍物,像这样的东西,再加上曼哈顿的距离检查,可能会奏效(如果你不忘向左和向上看的话)。或者更好的是,一些奇特的循环检查菱形区域。
然而,你有很多墙需要处理,所以你需要一种不同的方法。玩家的当前位置在距离0处。它的直接邻居在距离1处。他们的邻居在距离2处——除了那些距离较低的瓷砖(已经覆盖的瓷砖)。距离2处的瓦片的任何邻居要么在距离3处,要么已经被覆盖。当然,墙砖必须跳过。
因此,你需要跟踪哪些瓷砖已经被覆盖,以及你还需要检查哪些相邻的瓷砖,直到你用完移动点。让我们把它总结成一种可重复使用的方法:
public List<Point> GetReachableTiles(Point currentPosition, int maxDistance)
{
List<Point> coveredTiles = new List<Point> { currentPosition };
List<Point> boundaryTiles = new List<Point> { currentPosition };
for (int distance = 0; distance < maxDistance; distance++)
{
List<Point> nextBoundaryTiles = new List<Point>();
foreach (Point position in boundaryTiles)
{
foreach (Point pos in GetNeighboringPositions(position))
{
// You may also want to check against other player positions, if players can block each other:
if (!coveredTiles.Contains(pos) && !boundaryTiles.Contains(pos) && map.IsPassable(pos.X, pos.Y))
{
// We found a passable tile:
coveredTiles.Add(pos);
// And we want to check its neighbors in the next 'distance' iteration, too:
nextBoundaryTiles.Add(pos);
}
}
}
// The next 'distance' iteration should check the neighbors of the current boundary tiles:
boundaryTiles = nextBoundaryTiles;
}
return coveredTiles;
}