【问题标题】:Problems determining similarity of angles确定角度相似性的问题
【发布时间】:2021-02-09 16:33:35
【问题描述】:

我正致力于在我的统一项目中为敌方 AI 实施 Context Steering 运动,但在确定每个角度的兴趣时遇到了问题。

我有两个类:ContextMap 和 DirectionContext。 DirectionContext 是一个数据存储类。它包含一个角度,一个将角度与移动方向相关联的 Vector3,以及一个介于 0 到 1 之间的浮点数,具体取决于该角度的移动有趣程度。

ContextMap 包含一个 DirectionContext 列表,具体取决于 contextmap 的“详细信息”。所以如果我将detail设置为4,它会生成4个DirectionContexts,如果我将detail设置为8,它会生成8个DirectionContexts。角度从 -180 到 +180。

我想做的是编写一个函数来确定敌方游戏对象和玩家游戏对象之间的角度,然后将其与上下文映射中的每个 DirectionContext 进行比较。我希望它根据对象之间的角度和 DirectionContexts 角度的相似性为每个 DirectionContext 设置兴趣浮动。如果方向相同,我希望它将兴趣设置为 1,如果角度之间的差异尽可能高(360),我希望将兴趣设置为 0。

我遇到的问题是我的脚本没有正确设置角度的兴趣。如果玩家在敌人的正上方,它会按预期工作,但如果玩家在对角线或侧面,它会开始相反的工作。

这是正在发生的事情的图片:黑色物体是敌人,红色是人类。行长取决于兴趣的高低。

Link to the image

正如您在图片中看到的,如果方向是向上/向下,那么它可以正常工作,但是当它转向对角线或两侧时,情况会变得很奇怪。

这是我在代码中提出的:

    public class DirectionContext
    {
        [SerializeField] private Vector3 direction;
        [SerializeField] private float interest;
        [SerializeField] private float angle;
        [SerializeField] private bool isExcluded = false;
        public Vector3 GetDirection() { return direction; }
        public float GetInterest() { return interest; }
        public void SetInterest(float value) { interest = value; }
        public void CheckInterestAndSet(float newInterest)
        {
            if (newInterest > interest)
                interest = newInterest;
        }
 
        public float GetAngle() { return angle; }
 
        public DirectionContext(float angle)
        {
            this.angle = angle;
            this.direction = new Vector3(Mathf.Sin(angle * Mathf.Deg2Rad), Mathf.Cos(angle * Mathf.Deg2Rad), 0).normalized;
        }
}

上下文图:

    public class ContextMap
    {
        [SerializeField] private List<DirectionContext> contextMap = new List<DirectionContext>();
        public List<DirectionContext> GetContext()
        {
            return contextMap;
        }
 
        public void GenerateMap(int detail)
        {
            contextMap.Clear();
 
            for (int i = 0; i < detail; i++)
            {
                float angle = (360 / detail * i) - 180;
                contextMap.Add(new DirectionContext(angle));
            }
        }
 
        public void UpdateContext(int index, float interest)
        {
            contextMap[index].CheckInterestAndSet(interest);
        }
 
        public void DebugDrawMap(Vector3 position, Color color)
        {
            foreach (DirectionContext direction in contextMap)
            {
                Debug.DrawLine(position, (position + direction.GetDirection()).normalized * direction.GetInterest());
            }
        }
 
        public void Clear()
        {
            foreach (DirectionContext dir in contextMap)
                dir.Clear();
        }
    }

Seek:(我省略了SteeringContext,因为它是一个超级简单的类。它只包含可能有趣的变量。seek 中使用的两个是 parent 和 targetDestination。TargetDestination 是玩家位置,而 parent 是对敌人的引用.

    public class Seek : SteeringBehavior
    {
        public override ContextMap AddContext(ContextMap contextMap, SteeringContext steeringContext)
        {
            float angleToTarget = AngleBetween(Vector3.up, steeringContext.targetDestination - steeringContext.parent.transform.position);
            //Debug.Log(angleToTarget);
 
            for (int i = 0; i < contextMap.GetContext().Count; i++)
            {
 
                float delta = Mathf.Abs(Mathf.Max(angleToTarget, contextMap.GetContext()[i].GetAngle()) - Mathf.Min(angleToTarget, contextMap.GetContext()[i].GetAngle()));
                if (delta > 180)
                {
                    delta = 360f - delta;
                }
                float interest = 1f - delta / 180f;
 
                contextMap.UpdateContext(i, interest);
            }
 
            return contextMap;
        }
 
        private float AngleBetween(Vector3 vector1, Vector3 vector2)
        {
            float sin = vector1.x * vector2.y - vector2.x * vector1.y;
            float cos = vector1.x * vector2.x + vector1.y * vector2.y;
            return Mathf.Atan2(sin, cos) * (180) / Mathf.PI;
        }
    }

我使用这个函数调用 Seek:

        private void PopulateInterestMap()
        {
            interestMap.Clear();
 
            foreach (SteeringBehavior behavior in interestBehaviors)
            {
                interestMap = behavior.AddContext(interestMap, steeringContext);
            }
        }

感谢您的帮助!

【问题讨论】:

  • “如果方向相同,我希望它设置兴趣为 1,如果角度之间的差异尽可能高(360),我希望它设置兴趣为 0。”应该 180 不是尽可能远的角度分离? 180后他们开始向另一边靠近......
  • 角度从 -180 到 +180,所以最大可能的差异是 360,至少我认为它应该是这样工作的。
  • 欢迎来到 Stack Overflow。请不要发布图片链接;如果需要图像,请发布图像本身。你的代码中有一些非常奇怪的逻辑,你还没有给我们enough information to reproduce the error。我建议您使用调试器或一些诊断输出语句来查看代码认为涉及哪些角度。
  • 但是 +179° 和 - 179° 的 2 个角度仅相隔 2°。如果你的 AI 正在寻找 -179°,她应该对 +179° 的位置非常感兴趣......
  • 对不起,莫腾我解释错了。角度从 -180 到 +135,没有 +180。 Beta - 我是这个网站的新手,它不允许我嵌入图像并自动将其转换为链接。

标签: c# unity3d math


【解决方案1】:

我解决了。我将回答我自己的问题,因为网上没有真正涉及上下文引导行为的源代码,所以也许这里发布的代码可以为其他人省去我遇到的麻烦。

现在我已经启动并开始工作了,任何搞乱转向行为的人,我强烈建议你研究一下上下文转向(如果你用谷歌搜索,有一些关于它的好处的好的博客文章和论文。)它完全解决了我转向的抖动问题行为,并修复了混合行为在古怪情况下自行抵消的所有问题。

问题不在于确定权重,而在于我将角度转换为方向向量的 DirectionContext 部分。

        this.direction = new Vector3(Mathf.Sin(angle * Mathf.Deg2Rad), Mathf.Cos(angle * Mathf.Deg2Rad), 0).normalized;

应该是

        this.direction = new Vector3(Mathf.Cos(angle * Mathf.Deg2Rad), Mathf.Sin(angle * Mathf.Deg2Rad), 0);

归一化并不是毁掉它的原因,而是 Cos vs Sin。标准化是不必要的。

【讨论】:

    猜你喜欢
    • 2011-02-20
    • 1970-01-01
    • 2014-11-19
    • 1970-01-01
    • 2012-05-13
    • 2016-01-01
    • 2015-01-29
    • 2012-03-16
    • 1970-01-01
    相关资源
    最近更新 更多