我正在尝试在C#XNA 4.0中为我的游戏制作一个A *探路者。
如果探路者执行一次,那么怪物会正确移动。但是当怪物移动时,探路者再次执行,并使用敌人的当前位置。 路径探测器需要大约半秒到一秒才能完成。因此,当探路者完成并返回新路径时,怪物已经离开了同时使用的位置。所以现在怪物会全程移回到所使用的位置,只是为了开始新的路径。
我如何解决这个问题?
我不知道你需要什么。如果它是你需要看到的探路者代码,那么我会发布它,但这里是僵尸类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
namespace *Private name*
{
class Zombie : Obj
{
ContentManager Content;
private Vector2 dest;
private bool[,] map;
private List<Note> path = new List<Note>();
private int PathIndex = 0;
private bool queued = false;
private int queTimer = 0;
private int queTime = 10;
private bool finding = false;
private bool walkable = false;
private Thread t;
public Zombie(Vector2 pos, string sprName, int MaxHP, int HP, int damage) : base(pos, sprName, MaxHP, HP)
{
position = pos;
spriteName = sprName;
maxHealth = MaxHP;
if (HP > maxHealth)
Health = maxHealth;
else if (HP < 0)
Health = 1;
else
Health = HP;
maxHealth = 10;
Health = 10;
solid = true;
speed = 1.0f;
dest = position;
this.damage = damage;
}
//Loads the content and sets some values
public override void LoadContent(ContentManager content)
{
spriteTexture = content.Load<Texture2D>(spriteName);
spriteRectangle = new Rectangle((int)position.X, (int)position.Y, spriteTexture.Width, spriteTexture.Height);
centerPosRec.X = (int)position.X + (spriteRectangle.Height / 2);
centerPosRec.Y = (int)position.Y + (spriteRectangle.Width / 2);
Content = content;
}
public override void Update()
{
if (!alive) return;
if (Health <= 0)
alive = false;
if (attackCoolDown > 0)
attackCoolDown--;
if (queTimer > queTime)
{
queTimer = 0;
NewSetPath();
}
else
{ queTimer++; }
MoveToDestination();
spriteRectangle.X = (int)position.X;
spriteRectangle.Y = (int)position.Y;
centerPosRec.X = (int)position.X + (spriteRectangle.Height / 2);
centerPosRec.Y = (int)position.Y + (spriteRectangle.Width / 2);
}
//Here the program finds the path
private void NewSetPath()
{
if (t != null)
if (t.IsAlive == true)
return;
dest = Player.player1.centerPosRec;
if (!finding)
{
finding = true;
t = new Thread(NewFindPath);
t.Start();
}
if (!t.IsAlive && finding)
{
t.Abort();
finding = false;
queued = false;
PathIndex = 0;
}
}
//Here it also finds the path
private void NewFindPath()
{
map = MyPathFinder.writeMap();
MyPathFinder finder;
finder = new MyPathFinder(map);
path = finder.findPath(this.centerPosRec, this.dest);
}
//Moves to the next point/note in the path list
private void MoveToDestination()
{
if (path == null)
{
return;
}
if (PathIndex < path.Count)
{
if (stepToPoint(path[PathIndex]))
{
PathIndex++;
}
else
{
PushTo(speed, rotation);
}
}
else if (path.Count >= 0)
{
path = null;
PathIndex = 0;
queued = false;
dest = Player.player1.position;
NewSetPath();
}
}
//Checkes for collition and distance to the point
private bool stepToPoint(Note note)
{
if (PointDist(centerPosRec.X, centerPosRec.Y, note.posRectangle.Y + (pathFinder.gridSize / 2), note.posRectangle.X + (pathFinder.gridSize / 2)) < pathFinder.gridSize / 2)
{
speed = 0;
return true;
}
rotation = Point_Direction(centerPosRec.X, centerPosRec.Y, note.posRectangle.Y + (pathFinder.gridSize / 2), note.posRectangle.X + (pathFinder.gridSize / 2));
speed = 2f;
return false;
}
public override void Draw(SpriteBatch spriteBatch)
{
try
{
Vector2 center = new Vector2(spriteTexture.Width / 2, spriteTexture.Height / 2);
foreach (Note n in path)
{
spriteBatch.Draw(Content.Load<Texture2D>("OfficeWall"), new Vector2(n.position.Y * 32, n.position.X * 32), null, Color.White, 0, center, scale, SpriteEffects.None, 0);
}
}
catch
{ }
base.Draw(spriteBatch);
try
{
spriteBatch.DrawString(Content.Load<SpriteFont>("HUDFont"), PathIndex + "/" + path.Count, new Vector2(500, 500), Color.White);
}
catch
{ }
}
//Gets distance between points
public static float PointDist(float x1, float y1, float x2, float y2)
{
float xRect = (x1 -x2) * (x1 - x2);
float yRect = (y1 - y2) * (y1 - y2);
double hRect = xRect + yRect;
float dist = (float)Math.Sqrt(hRect);
return dist;
}
//Movement
public override void PushTo(float pix, float dir)
{
float newX = (float)Math.Cos(MathHelper.ToRadians(dir));
float newY = (float)Math.Sin(MathHelper.ToRadians(dir));
newX *= pix;
newY *= pix;
if (!Collision(new Vector2(newX, newY)))
{
base.PushTo(pix, dir);
}
else if (!Collision(new Vector2(0, newY)))
{
this.position += new Vector2(0, newY);
}
else if (!Collision(new Vector2(newX, 0)))
{
this.position += new Vector2(newX, 0);
}
}
public override bool Collision(Vector2 pos)
{
Rectangle area = new Rectangle(spriteRectangle.X, spriteRectangle.Y, spriteRectangle.Width, spriteRectangle.Height);
area.X += (int)pos.X;
area.Y += (int)pos.Y;
foreach (Obj o in Items.objList)
{
if (o.solid && o.alive && o != this && o.GetType() != typeof(Zombie))
{
if (area.Intersects(Player.player1.spriteRectangle))
{
if (Player.player1.alive == true && attackCoolDown == 0)
{
this.attackCoolDown = 55;
Player.player1.Health -= damage;
Player.player1.regenAfterDamageTimer = 0;
Player.player1.PushTo(5f, rotation);
return true;
}
}
if (o.spriteRectangle.Intersects(area))
{
return true;
}
}
}
return false;
}
}
}
好的,这是探路者,它没有完全完成,我知道我必须移动if语句来检查是否正在检查结束位置。我通过更新每个刻度来加快速度,现在它工作得更好。现在需要一个毫秒但是这里是:D
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
namespace *Private name*
{
class MyPathFinder
{
List<Note> path = new List<Note>();
List<Note> openList = new List<Note>();
List<Note> closedList = new List<Note>();
Note start;
Note endPos;
private Note[,] map;
public static int GridSize = 32;
bool NorthRight = false, NorthLeft = false, SouthRight = false, SouthLeft = false, EastRight = false, EastLeft = false, WestRight = false, WestLeft = false;
//string values = "";
public MyPathFinder(bool[,] cMap)
{
map = new Note[cMap.GetLength(0), cMap.GetLength(1)];
for (int x = 0; x < cMap.GetLength(0); x++)
{
for (int y = 0; y < cMap.GetLength(1); y++)
{
Rectangle rec = new Rectangle();
rec.Width = rec.Height = pathFinder.gridSize;
rec.X = x * pathFinder.gridSize;
rec.Y = y * pathFinder.gridSize;
map[x, y] = new Note();
map[x, y].walkable = cMap[x, y];
map[x, y].posRectangle = rec;
map[x, y].position = new Point(x, y);
}
}
}
public static bool[,] writeMap(params Obj[] objEx)
{
bool[,] cMap = new bool[Convert.ToInt16(Game1.room.Width / pathFinder.gridSize), Convert.ToInt16(Game1.room.Height / pathFinder.gridSize)];
//string output2 = "";
//loop through rows
for (int x = 0; x < cMap.GetLength(0); x++)
{
for (int y = 0; y < cMap.GetLength(1); y++)
{
Rectangle rec = new Rectangle();
rec.Width = rec.Height = pathFinder.gridSize;
rec.X = y * pathFinder.gridSize;
rec.Y = x * pathFinder.gridSize;
//output2 += Convert.ToString("{" + y + "," + x);
//If collision with grid then grid is unwalkable
foreach (Obj o in Items.objList)
{
if (o.spriteRectangle.Intersects(rec) && o.alive && o.solid && !objEx.Contains<Obj>(o) && o.GetType() != typeof(Zombie) && o.GetType() != typeof(Player))
{
cMap[x, y] = false;
break;
}
else
{
cMap[x, y] = true;
}
}
//output2 += " = " + cMap[x, y] + "} -- ";
}
//output2 += "\r\n";
}
//File.WriteAllText("DEBUG-Walkable.txt", output2);
return cMap;
}
public List<Note> findPath(Vector2 pos, Vector2 dest)
{
string wtf = "";
int num = 0;
path.Clear();
openList.Clear();
closedList.Clear();
Point startPos = new Point(Convert.ToInt16(Math.Floor((double)(pos.Y / GridSize))), Convert.ToInt16(Math.Floor((double)(pos.X / GridSize))));
Point end = new Point(Convert.ToInt16(Math.Floor((double)(dest.Y / GridSize))), Convert.ToInt16(Math.Floor((double)(dest.X / GridSize))));
start = map[startPos.X, startPos.Y];
endPos = (map[end.X, end.Y]);
openList.Add(map[startPos.X, startPos.Y]);
while (!endPos.closed)
{
num++;
openList = openList.OrderBy(p => p.F).ToList();
Note temp = null;
foreach (Note n in openList)
{
if (!closedList.Contains(n))
{
wtf += "number " + num + " = " + n.position.X + ", " + n.position.Y + "\r\n";
parenting(n.position, end);
n.closed = true;
temp = n;
break;
}
}
if (temp != null)
{
closedList.Add(temp);
openList.Remove(temp);
}
//File.WriteAllText("checking.txt", wtf);
//File.WriteAllText("listContent.txt", outp);
//File.WriteAllText("listCount.txt", openList.Count.ToString());
}
return path;
}
private void parenting(Point pos, Point dest)
{
checkNorth(pos, dest);
checkSouth(pos, dest);
checkEast(pos, dest);
checkWest(pos, dest);
//File.WriteAllText("DEBUGpos[" + pos.Y + ", " + pos.X + "].txt", values);
//if (NorthLeft && NorthRight && SouthLeft && SouthRight && EastLeft && EastRight && WestLeft && WestRight)
// Settings.exit = true;
}
private int GetH(Point v, Point endPos)
{
Point diff = new Point(v.X - endPos.X, v.Y - endPos.Y);
if (diff.X < 0) { diff.X *= 1; }
if (diff.Y < 0) { diff.Y *= 1; }
return Convert.ToInt16(diff.X + diff.Y);
}
private void checkNorth(Point pos, Point dest)
{
Point p = new Point(pos.X - 1, pos.Y);
if (map.XInRange(p.X))
{
if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
{
//values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";
if (map[p.X, p.Y] == endPos)
{
//File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
endPos.parent = pos;
endPos.closed = true;
closedList.Add(endPos);
path.Add(map[p.X, p.Y]);
while (!path.Contains(start))
{
path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
}
path.Reverse();
}
else
{
if (map[p.X, p.Y].open == true)
{
if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
{
Settings.exit = true;
}
}
else
{
map[p.X, p.Y].open = true;
map[p.X, p.Y].parent = pos;
map[p.X, p.Y].H = GetH(p, dest);
map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;
NorthLeft = (map[p.X, p.Y - 1].walkable == true) ? true : false;
NorthRight = (map[p.X, p.Y + 1].walkable == true) ? true : false;
openList.Add(map[p.X, p.Y]);
}
}
}
else
{
NorthLeft = false;
NorthRight = false;
}
}
else
{
SouthLeft = false;
SouthRight = false;
}
//values += NorthLeft.ToString() + " " + NorthRight.ToString() + "\r\n";
}
private void checkSouth(Point pos, Point dest)
{
Point p = new Point(pos.X + 1, pos.Y);
if (p.X < 0)
{
SouthLeft = false;
SouthRight = false;
return;
}
//values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";
if (Enumerable.Range(0, map.GetLength(0)).Contains(p.X))
{
if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
{
if (map[p.X, p.Y] == endPos)
{
//File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
endPos.parent = pos;
endPos.closed = true;
closedList.Add(endPos);
path.Add(map[p.X, p.Y]);
while (!path.Contains(start))
{
path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
}
path.Reverse();
}
else
{
if (map[p.X, p.Y].open == true)
{
if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
{
}
}
else
{
map[p.X, p.Y].open = true;
map[p.X, p.Y].parent = pos;
map[p.X, p.Y].H = GetH(p, dest);
map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;
if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y + 1))
SouthLeft = (map[p.X, p.Y + 1].walkable == true) ? true : false;
if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y - 1))
SouthRight = (map[p.X, p.Y - 1].walkable == true) ? true : false;
openList.Add(map[p.X, p.Y]);
}
}
}
else
{
SouthLeft = false;
SouthRight = false;
}
}
else
{
SouthLeft = false;
SouthRight = false;
}
//values += SouthLeft.ToString() + " " + SouthRight.ToString() + "\r\n";
}
private void checkEast(Point pos, Point dest)
{
Point p = new Point(pos.X, pos.Y + 1);
//File.WriteAllText("testEastpos.txt", p.X + " - " + p.Y + " -- " + map[p.X, p.Y].walkable);
//values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";
if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y))
{
if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
{
if (map[p.X, p.Y] == endPos)
{
//File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
endPos.parent = pos;
endPos.closed = true;
closedList.Add(endPos);
path.Add(map[p.X, p.Y]);
while (!path.Contains(start))
{
path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
}
path.Reverse();
}
else
{
if (map[p.X, p.Y].open == true)
{
if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
{
}
}
else
{
map[p.X, p.Y].open = true;
map[p.X, p.Y].parent = pos;
map[p.X, p.Y].H = GetH(p, dest);
map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;
if (p.X - 1 >= 0)
EastLeft = (map[p.X - 1, p.Y].walkable == true) ? true : false;
else
EastLeft = false;
if (Enumerable.Range(0, map.GetLength(0)).Contains(p.Y))
{
EastRight = (map[p.X + 1, p.Y].walkable == true) ? true : false;
}
else
{ EastRight = false; }
openList.Add(map[p.X, p.Y]);
}
}
}
else
{
SouthLeft = false;
SouthRight = false;
}
}
else
{
EastLeft = false;
EastRight = false;
}
try
{
//File.WriteAllText("testEastSidepos.txt", (p.X - 1) + " - " + p.Y + " -- " + map[p.X - 1, p.Y].walkable + "\r\n" +
// (p.X + 1) + " - " + p.Y + " -- " + map[p.X + 1, p.Y].walkable);
}
catch
{ }
//values += EastLeft.ToString() + " " + EastRight.ToString() + "\r\n";
}
private void checkWest(Point pos, Point dest)
{
Point p = new Point(pos.X, pos.Y - 1);
if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y))
{
//values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";
if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
{
if (map[p.X, p.Y] == endPos)
{
//File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
endPos.parent = pos;
endPos.closed = true;
closedList.Add(endPos);
path.Add(map[p.X, p.Y]);
while (!path.Contains(start))
{
path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
}
path.Reverse();
}
else
{
if (map[p.X, p.Y].open == true)
{
if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
{
}
}
else
{
map[p.X, p.Y].open = true;
map[p.X, p.Y].parent = pos;
map[p.X, p.Y].H = GetH(p, dest);
map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;
WestLeft = (map[p.X + 1, p.Y].walkable == true) ? true : false;
if (p.X - 1 >= 0)
WestRight = (map[p.X - 1, p.Y].walkable == true) ? true : false;
else
WestRight = false;
openList.Add(map[p.X, p.Y]);
}
}
}
else
{
WestLeft = false;
WestRight = false;
}
//values += WestLeft.ToString() + " " + WestRight.ToString() + "\r\n";
}
}
}
}
答案 0 :(得分:0)
当怪物移动时,没有必要使用探路者。它找到了一条路径,使用那条路径。
如果计算出的路径被无法通过的障碍物阻挡,则必须重新计算路径 。
然后你的zobie必须停下来。无论如何,路径无效,它无法移动。
如果环境发生变化,表明会有更便宜的路径,则可以重新计算路径 。
如果全新路径比当前路径的剩余部分便宜,则仅使用新路径。
编辑:
优化探路者:
openList = openList.OrderBy(p => p.F).ToList();
这一行每次都会对列表进行排序。您需要一个排序列表,在插入后对其进行排序。作为快速修复,在插入内容后调用openList.Sort()。这可能不是最佳选择,因为它会对整个列表进行排序,而您已经知道只有一个元素是未排序的。
在.NET中查找优先级队列类的同样问题是here
您的四个北/东/南/西功能是代码重复。检查它们的共同点,并将其提取到一个共同的功能中。您的代码将只是现在的25%。这并不快,但代码越少,就越容易看到内容。