【问题标题】:need help in c# game code在c#游戏代码中需要帮助
【发布时间】:2018-04-10 03:03:11
【问题描述】:

我正在统一制作塔防游戏,但我被卡住了。我希望我的枪或炮塔一直面对敌人,直到他们超出范围,但每 0.5 秒后,它会更新并面对其范围内的另一个敌人,尽管第一个敌人也在范围内。

这是我的代码:

void Start () 
{
    InvokeRepeating("UpdateTarget", 0f , 0.5f );
}   

void UpdateTarget()
{
    GameObject[] enemies = GameObject.FindGameObjectsWithTag(enemyTag);
    float shortestDistance = Mathf.Infinity;
    GameObject nearestEnemy = null;

    foreach (GameObject enemy in enemies)
    {
        float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
        if (distanceToEnemy < shortestDistance)
        {
            shortestDistance = distanceToEnemy;
            nearestEnemy = enemy;
        }
    }

    if (nearestEnemy != null && shortestDistance <= range)
    {
        target = nearestEnemy.transform;          
    }
    else 
    { 
        target = null; 
    }        
}

【问题讨论】:

  • 也许一开始你应该检查是否已经有一个活动的目标,如果有,不要改变它。喜欢:if (target != null &amp;&amp; Vector3.Distance(transform.position, target.transform.position) &lt;= range) return;(注意:我不了解 Unity,所以这可能不是正确的代码,但你明白了......)
  • 是的,听起来他们不希望它不断更改目标,因此维护“活动目标”变量将是一个好方法。另外,我强烈建议添加一个包围炮塔范围的盒子对撞机,并使用它来限制您必须搜索的敌人数量。 unity3d.com/learn/tutorials/topics/physics/colliders-triggers 可能会感兴趣。 Unity 内置了强大的空间索引功能,因此不利用它是愚蠢的。如果有内置的“最近邻”算法,我不会感到惊讶。

标签: c# unity3d


【解决方案1】:

您必须在 UpdateTarget 中包含一个测试,它应该首先检查最近的敌人是否仍在范围内,在这种情况下退出函数,否则搜索新目标。

逻辑应该是这样的:

//keep old target?
if(nearestEnemy != null)
{
  //calculate distance of current target here..
  if(distance <= range)
     return; //keep this enemy as target
}
//here comes your code for finding a new target...

【讨论】:

  • 如果你有一个敌人靠近,那么它就会成为最近的敌人,很好。你朝它开枪,它停止移动(出于任何原因)。另一个敌人经过它并靠近你,它永远不会是最近的敌人,因为第一个敌人仍在登记中。
【解决方案2】:

GameObject.FindGameObjectsWithTag 对性能影响更大,我建议你创建一个圆形 collider 2d 并将其设置为触发,当调用 OnTriggerEnter2D 时,你可以获取敌人并升旗,这样其他敌人就不会被触发之后,当对该敌人调用 OnTriggerExit2D 时,您可以开始寻找另一个敌人或选择仍在该对撞机半径内的敌人之一。

private bool _foundEnemy;
private Enemy _currentEnemy;

private void OnTriggerEnter2D(Collider2D collision)
{
    if (_foundEnemy) return;

    _currentEnemy = collision.GetComponent<Enemy>();
    // your code here
    _foundEnemy = true;
}

private void OnTriggerExit2D(Collider2D collision)
{
    if(_currentEnemy!=null && collision.GetComponent<Enemy>() == _currentEnemy)
        _foundEnemy = false;
}

【讨论】:

    【解决方案3】:

    你并不完全清楚你想要实现什么行为,但它似乎是锁定一个目标直到它死了,然后才切换到另一个目标。这是您代码中的一个简单修复;但是我发现了其他几个问题:

    1. 冗余代码,例如块和变量。
    2. 几乎是如何失败关注Single Responsibility Principle的示例。
    3. 您不需要“一种或另一种” 方法,因为其他答案正在显示。您可以通过一个简单的boolean flag 作为开关来获得两种行为

    这里有一些注释代码说明了所有这些事情,希望您可以从参考中学习:

    //Defines how to get and compare distance; used during sorting
    public class DistanceComparer<T> : IComparer<T> where T : GameObject {
       public int Compare(T a, T b) {
           return Vector3.Distance(a.transform.position, b.transform.position);
       }
    }
    
    public var bool lockToTargetUntilDead = true; //Should lock to target till it's dead (true)? Or switch to nearest if the nearest changes (false)?
    
    //Finds nearest enemy
    GameObject FindNearestEnemy() {
        var enemies = GameObject.FindGameObjectsWithTag(enemyTag);
        if (enemies==null || enemies.Length==0) return null; //No enemies anywhere
        Array.Sort(enemies, new DistanceComparer()); //Sort enemies by distance
        return enemies[0]; //Return closest enemy
    }
    
    //Determines if enemy is within range (Note: If enemy is null, it's not in range =P)
    bool IsWithinRange(GameObject enemy) {
        return enemy != null && Vector3.Distance(transform.position, enemy.transform.position) <= range;
    }
    
    //Updates target
    void UpdateTarget() {
        var nearestEnemy = FindNearestEnemy();
        if(!IsWithinRange(nearestEnemy)) { //No enemies within range...
            if(target != null) target = null; //Forget current target.
            return; //Return-early.
        }
        if(lockToTargetUntilDead && target!=null) return; //If locking to current target and it ain't dead yet, return-early
        target = nearestEnemy; //If not locking to target, or doesn't have a target, new target!
    }
    
    void Start () {
        InvokeRepeating("UpdateTarget", 0f , 0.5f );
    }
    

    【讨论】:

    • @Draco18s 同意。但至少到目前为止,#NotMyProblem。我对提供更通用的代码风格改进更感兴趣;我并没有真正关注原始代码中的优化问题,或者可以对这些问题进行的改进。 --- +1 为 OP 和未来的读者提供有用的参考;如果您愿意,可以随意编辑和添加优化内容(我不喜欢,ATM)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    相关资源
    最近更新 更多