Unity - Physics.OverlapSphere没有检测实例化的GameObjects

时间:2018-05-16 03:59:00

标签: c# unity3d

我的RTS游戏存在问题,我的敌人单位不会攻击我在关卡开始后创建的任何基础建筑物。他们去攻击当等级开始时那里的所有其他建筑物,但没有建造的那些。

有一个列表被设置为最接近单位的目标,他们将攻击他们最近的目标,但任何新实例化的建筑物或单位都不会因某种原因受到攻击。

当WorldObject脚本没有做任何事情时,会在WorldObject脚本中的所有单元上调用DecideWhatToDo()函数。然后它从WorkManager脚本调用FindNearbyObjects()。

在新的单位和建筑物被创建之前,一切都在进行,以前是否有人遇到过这种问题?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using RTS;

public class WorldObject : MonoBehaviour {

    BoxCollider boxCollider;
    bool isDead;
    bool isSinking;

    public string objectName;
    public Texture2D buildImage;
    public int cost, sellValue, maxHitPoints;
    public float hitPoints;
    public virtual bool IsActive { get { return true; } }
    public float weaponRange = 10.0f;
    public float weaponRechargeTime = 1.0f;
    public float weaponAimSpeed = 1.0f;
    public AudioClip attackSound, selectSound, useWeaponSound;
    public float attackVolume = 1.0f, selectVolume = 1.0f, useWeaponVolume = 1.0f;
    public int ObjectId { get; set; }
    public float detectionRange = 20.0f;
    public GameObject explosionPrefab, splat;

    protected NavMeshAgent agent;
    protected AudioElement audioElement;
    protected Animator anim;
    protected List<WorldObject> nearbyObjects;
    protected Rect playingArea = new Rect(0.0f, 0.0f, 0.0f, 0.0f);
    protected Player player;
    protected string[] actions = { };
    protected bool currentlySelected = false;
    protected Bounds selectionBounds;
    protected GUIStyle healthStyle = new GUIStyle();
    protected float healthPercentage = 1.0f;
    protected WorldObject target = null;
    protected bool attacking = false;
    protected bool movingIntoPosition = false;
    protected bool aiming = false;

    private List<Material> oldMaterials = new List<Material>();
    private float currentWeaponChargeTime;
    //we want to restrict how many decisions are made to help with game performance
    //the default time at the moment is a tenth of a second
    private float timeSinceLastDecision = 0.0f, timeBetweenDecisions = 0.1f;

    protected virtual void Awake()
    {
        anim = GetComponent<Animator>();
        boxCollider = GetComponent<BoxCollider>();
        selectionBounds = ResourceManager.InvalidBounds;
        CalculateBounds();
    }

    protected virtual void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        SetPlayer();
        if (player) SetTeamColor();
        InitialiseAudio();
    }

    protected virtual void Update()
    {
        if (isSinking)
        {
            this.transform.Translate(-Vector3.up * 2.5f * Time.deltaTime);
        }
        if (ShouldMakeDecision()) DecideWhatToDo();
        currentWeaponChargeTime += Time.deltaTime;
        if (attacking && !movingIntoPosition && !aiming)
        {

            PerformAttack();
        }
    }

    /**
 * A child class should only determine other conditions under which a decision should
 * not be made. This could be 'harvesting' for a harvester, for example. Alternatively,
 * an object that never has to make decisions could just return false.
 */
    protected virtual bool ShouldMakeDecision()
    {
        if (!attacking && !movingIntoPosition && !aiming)
        {
            //we are not doing anything at the moment
            if (timeSinceLastDecision > timeBetweenDecisions)
            {
                timeSinceLastDecision = 0.0f;
                Debug.Log("");
                return true;
            }
            timeSinceLastDecision += Time.deltaTime;
        }


        return false;
    }

    protected virtual void DecideWhatToDo()
    {
        //determine what should be done by the world object at the current point in time
        Vector3 currentPosition = transform.position;
        nearbyObjects = WorkManager.FindNearbyObjects(currentPosition, detectionRange);

        if (CanAttack())
        {
            List<WorldObject> enemyObjects = new List<WorldObject>();
            foreach (WorldObject nearbyObject in nearbyObjects)
            {
                Resource resource = nearbyObject.GetComponent<Resource>();
                if (resource) continue;
                if (nearbyObject.GetPlayer() != player) enemyObjects.Add(nearbyObject);
            }
            WorldObject closestObject = WorkManager.FindNearestWorldObjectInListToPosition(enemyObjects, currentPosition);
            if (closestObject)
            {
                attacking = true;
                //agent.isStopped = true;
                BeginAttack(closestObject);
            }
        }
    }

    public Player GetPlayer()
    {
        return player;
    }

    protected virtual void OnGUI()
    {
        if (currentlySelected && !ResourceManager.MenuOpen) DrawSelection();
    }

    protected virtual void InitialiseAudio()
    {
        List<AudioClip> sounds = new List<AudioClip>();
        List<float> volumes = new List<float>();
        if (attackVolume < 0.0f) attackVolume = 0.0f;
        if (attackVolume > 1.0f) attackVolume = 1.0f;
        sounds.Add(attackSound);
        volumes.Add(attackVolume);
        if (selectVolume < 0.0f) selectVolume = 0.0f;
        if (selectVolume > 1.0f) selectVolume = 1.0f;
        sounds.Add(selectSound);
        volumes.Add(selectVolume);
        if (useWeaponVolume < 0.0f) useWeaponVolume = 0.0f;
        if (useWeaponVolume > 1.0f) useWeaponVolume = 1.0f;
        sounds.Add(useWeaponSound);
        volumes.Add(useWeaponVolume);
        audioElement = new AudioElement(sounds, volumes, objectName + ObjectId, this.transform);
    }

    public void SetPlayer()
    {
        player = transform.root.GetComponentInChildren<Player>();
    }

    public bool IsOwnedBy(Player owner)
    {
        if (player && player.Equals(owner))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public void CalculateBounds()
    {
        selectionBounds = new Bounds(transform.position, Vector3.zero);
        foreach (Renderer r in GetComponentsInChildren<Renderer>())
        {
            selectionBounds.Encapsulate(r.bounds);
        }
    }

    //!!!!!MULTI SELECTION!!!!!

    public virtual void SetSelection(bool selected, Rect playingArea)
    {
        currentlySelected = selected;
        if (selected)
        {
            if (audioElement != null) audioElement.Play(selectSound);
            this.playingArea = playingArea;
        }
        CalculateBounds();
    }

    //!!!!!MULTI SELECTION!!!!!

    public Bounds GetSelectionBounds()
    {
        return selectionBounds;
    }

    public string[] GetActions()
    {
        return actions;
    }

    public void SetColliders(bool enabled)
    {
        Collider[] colliders = GetComponentsInChildren<Collider>();
        foreach (Collider collider in colliders) collider.enabled = enabled;
    }

    public void SetTransparentMaterial(Material material, bool storeExistingMaterial)
    {
        if (storeExistingMaterial) oldMaterials.Clear();
        Renderer[] renderers = GetComponentsInChildren<Renderer>();
        foreach (Renderer renderer in renderers)
        {
            if (storeExistingMaterial) oldMaterials.Add(renderer.material);
            renderer.material = material;
        }
    }

    public void RestoreMaterials()
    {
        Renderer[] renderers = GetComponentsInChildren<Renderer>();
        if (oldMaterials.Count == renderers.Length)
        {
            for (int i = 0; i < renderers.Length; i++)
            {
                renderers[i].material = oldMaterials[i];
            }
        }
    }

    public void SetPlayingArea(Rect playingArea)
    {
        this.playingArea = playingArea;
    }

    public virtual void SetHoverState(GameObject hoverObject)
    {
        //only handle input if owned by a human player and currently selected
        if (player && player.human && currentlySelected)
        {
            //something other than the ground is being hovered over
            if (hoverObject.name != "Ground")
            {
                Player owner = hoverObject.transform.root.GetComponent<Player>();
                Unit unit = hoverObject.transform.parent.GetComponent<Unit>();
                Building building = hoverObject.transform.parent.GetComponent<Building>();
                if (owner)
                { //the object is owned by a player
                    if (owner.username == player.username) player.hud.SetCursorState(CursorState.Select);
                    else if (CanAttack()) player.hud.SetCursorState(CursorState.Attack);
                    else player.hud.SetCursorState(CursorState.Select);
                }
                else if (unit || building && CanAttack()) player.hud.SetCursorState(CursorState.Attack);
                else player.hud.SetCursorState(CursorState.Select);
            }
        }
    }

    public virtual bool CanAttack()
    {
        //default behaviour needs to be overidden by children
        return false;
    }

    public virtual void PerformAction(string actionToPerform)
    {
        //it is up to children with specific actions to determine what to do with each of those actions
    }

    public virtual void MouseClick(GameObject hitObject, Vector3 hitPoint, Player controller)
    {

        //only handle input if currently selected
        if (currentlySelected && hitObject && hitObject.name != "Ground")
        {
            WorldObject worldObject = hitObject.transform.parent.GetComponent<WorldObject>();
            //clicked on another selectable object
            if (worldObject)
            {
                Resource resource = hitObject.transform.parent.GetComponent<Resource>();
                if (resource && resource.isEmpty()) return;
                Player owner = hitObject.transform.root.GetComponent<Player>();
                if (owner)
                { //the object is controlled by a player
                    if (player && player.human)
                    { //this object is controlled by a human player
                      //start attack if object is not owned by the same player and this object can attack, else select
                        if (player.username != owner.username && CanAttack())
                        {
                            BeginAttack(worldObject);
                        }
                        else ChangeSelection(worldObject, controller);
                    }
                    else ChangeSelection(worldObject, controller);
                }
                else ChangeSelection(worldObject, controller);
            }
        }
    }

    protected virtual void BeginAttack(WorldObject target)
    {
        //if (audioElement != null) audioElement.Play(attackSound);
        this.target = target;
        if (TargetInRange())
        {
            anim.SetBool("Attacking", true);
            attacking = true;
            PerformAttack();
        }
        else AdjustPosition();
    }

    protected void SetTeamColor()
    {
        TeamColor[] teamColors = GetComponentsInChildren<TeamColor>();
        foreach (TeamColor teamColor in teamColors) teamColor.GetComponent<Renderer>().material.color = player.teamColor;
    }

    protected virtual void DrawSelectionBox(Rect selectBox)
    {
        GUI.Box(selectBox, "");
        CalculateCurrentHealth(0.35f, 0.65f);
        DrawHealthBar(selectBox, "");
    }

    protected virtual void CalculateCurrentHealth(float lowSplit, float highSplit)
    {
        healthPercentage = (float)hitPoints / (float)maxHitPoints;
        if (healthPercentage > highSplit) healthStyle.normal.background = ResourceManager.HealthyTexture;
        else if (healthPercentage > lowSplit) healthStyle.normal.background = ResourceManager.DamagedTexture;
        else healthStyle.normal.background = ResourceManager.CriticalTexture;
    }

    protected void DrawHealthBar(Rect selectBox, string label)
    {
        healthStyle.padding.top = -20;
        healthStyle.fontStyle = FontStyle.Bold;
        GUI.Label(new Rect(selectBox.x, selectBox.y - 7, selectBox.width * healthPercentage, 5), label, healthStyle);
    }

    protected virtual void AimAtTarget()
    {
        aiming = true;

        //this behaviour needs to be specified by a specific object
    }

    private void ChangeSelection(WorldObject worldObject, Player controller)
    {
        //this should be called by the following line, but there is an outside chance it will not
        SetSelection(false, playingArea);
        if (controller.SelectedObject) controller.SelectedObject.SetSelection(false, playingArea);
        controller.SelectedObject = worldObject;
        worldObject.SetSelection(true, controller.hud.GetPlayingArea());
    }

    private void DrawSelection()
    {
        GUI.skin = ResourceManager.SelectBoxSkin;
        Rect selectBox = WorkManager.CalculateSelectionBox(selectionBounds, playingArea);
        //Draw the selection box around the currently selected object, within the bounds of the playing area
        GUI.BeginGroup(playingArea);
        DrawSelectionBox(selectBox);
        GUI.EndGroup();
    }

    private bool TargetInRange()
    {
        Vector3 targetLocation = target.transform.position;
        Vector3 direction = targetLocation - transform.position;
        if (direction.sqrMagnitude < weaponRange * weaponRange)
        {
            return true;
        }
        return false;
    }

    private void AdjustPosition()
    {

        Unit self = this as Unit;
        if (self)
        {
            movingIntoPosition = true;
            Vector3 attackPosition = FindNearestAttackPosition();
            self.StartMove(attackPosition);
            attacking = true;
        }
        else
        {

            attacking = false;
        }
    }

    private Vector3 FindNearestAttackPosition()
    {
        Vector3 targetLocation = target.transform.position;
        Vector3 direction = targetLocation - transform.position;
        float targetDistance = direction.magnitude;
        float distanceToTravel = targetDistance - (0.9f * weaponRange);
        return Vector3.Lerp(transform.position, targetLocation, distanceToTravel / targetDistance);
    }

    private void PerformAttack()
    {
        if (!target)
        {
            attacking = false;
            anim.SetBool("Attacking", false);
            anim.SetBool("IsRunning", false);
            return;
        }
        if (!TargetInRange())
        {
            AdjustPosition();
        }
        else if (!TargetInFrontOfWeapon())
        {
            AimAtTarget();
        }
        else if (ReadyToFire())
        {
            //attacking = true;
            UseWeapon();
        }
        //if (TargetInRange() && (attacking = true))
        //{
        //    AdjustPosition();
        //}
    }

    private bool TargetInFrontOfWeapon()
    {
        Vector3 targetLocation = target.transform.position;
        Vector3 direction = targetLocation - transform.position;
        if (direction.normalized == transform.forward.normalized) return true;
        else return false;
    }

    private bool ReadyToFire()
    {
        if (currentWeaponChargeTime >= weaponRechargeTime)
        {

            return true;
        }
        return false;
    }

    protected virtual void UseWeapon()
    {

        if (audioElement != null && Time.timeScale > 0) audioElement.Play(useWeaponSound);
        currentWeaponChargeTime = 0.0f;
        //this behaviour needs to be specified by a specific object
    }

    public void TakeDamage(float damage)
    {
        //GameObject.Instantiate(impactVisual, target.transform.position, Quaternion.identity);
        hitPoints -= damage;
        if (hitPoints <= 0)
        {
            Instantiate(explosionPrefab, transform.position + new Vector3(0, 5, 0), Quaternion.identity);

            Instantiate(splat, transform.position, Quaternion.identity);

            Destroy(gameObject);
        }
    }
}
using UnityEngine;
using System.Collections.Generic;

namespace RTS
{
    public static class WorkManager
    {


        public static Rect CalculateSelectionBox(Bounds selectionBounds, Rect playingArea)
        {
            //shorthand for the coordinates of the centre of the selection bounds
            float cx = selectionBounds.center.x;
            float cy = selectionBounds.center.y;
            float cz = selectionBounds.center.z;
            //shorthand for the coordinates of the extents of the selection bounds
            float ex = selectionBounds.extents.x;
            float ey = selectionBounds.extents.y;
            float ez = selectionBounds.extents.z;

            //Determine the screen coordinates for the corners of the selection bounds
            List<Vector3> corners = new List<Vector3>();
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy + ey, cz + ez)));
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy + ey, cz - ez)));
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy - ey, cz + ez)));
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy + ey, cz + ez)));
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy - ey, cz - ez)));
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy - ey, cz + ez)));
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy + ey, cz - ez)));
            corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy - ey, cz - ez)));

            //Determine the bounds on screen for the selection bounds
            Bounds screenBounds = new Bounds(corners[0], Vector3.zero);
            for (int i = 1; i < corners.Count; i++)
            {
                screenBounds.Encapsulate(corners[i]);
            }

            //Screen coordinates start in the bottom left corner, rather than the top left corner
            //this correction is needed to make sure the selection box is drawn in the correct place
            float selectBoxTop = playingArea.height - (screenBounds.center.y + screenBounds.extents.y);
            float selectBoxLeft = screenBounds.center.x - screenBounds.extents.x;
            float selectBoxWidth = 2 * screenBounds.extents.x;
            float selectBoxHeight = 2 * screenBounds.extents.y;

            return new Rect(selectBoxLeft, selectBoxTop, selectBoxWidth, selectBoxHeight);
        }

        public static GameObject FindHitObject(Vector3 origin)
        {
            Ray ray = Camera.main.ScreenPointToRay(origin);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, ResourceManager.RayCastLimit)) return hit.collider.gameObject;
            return null;
        }

        public static Vector3 FindHitPoint(Vector3 origin)
        {
            Ray ray = Camera.main.ScreenPointToRay(origin);
            RaycastHit hit;
            Debug.DrawRay(ray.origin, ray.direction * ResourceManager.RayCastLimit, Color.yellow);
            if (Physics.Raycast(ray, out hit, ResourceManager.RayCastLimit)) return hit.point;
            return ResourceManager.InvalidPosition;
        }

        public static List<WorldObject> FindNearbyObjects(Vector3 position, float range)
        {
            Collider[] hitColliders = Physics.OverlapSphere(position, range);
            HashSet<int> nearbyObjectIds = new HashSet<int>();
            List<WorldObject> nearbyObjects = new List<WorldObject>();
            for (int i = 0; i < hitColliders.Length; i++)
            {
                Transform parent = hitColliders[i].transform.parent;
                if (parent)
                {
                    WorldObject parentObject = parent.GetComponent<WorldObject>();
                    if (parentObject && !nearbyObjectIds.Contains(parentObject.ObjectId))
                    {

                            nearbyObjectIds.Add (parentObject.ObjectId);
                            nearbyObjects.Add (parentObject);

                    }
                }
            }
            return nearbyObjects;
        }

        public static WorldObject FindNearestWorldObjectInListToPosition(List<WorldObject> objects, Vector3 position)
        {
            if (objects == null || objects.Count == 0) return null;
            WorldObject nearestObject = objects[0];
            float sqrDistanceToNearestObject = Vector3.SqrMagnitude(position - nearestObject.transform.position);
            for (int i = 1; i < objects.Count; i++)
            {
                float sqrDistanceToObject = Vector3.SqrMagnitude(position - objects[i].transform.position);
                if (sqrDistanceToObject < sqrDistanceToNearestObject)
                {
                    sqrDistanceToNearestObject = sqrDistanceToObject;
                    nearestObject = objects[i];
                }
            }
            return nearestObject;
        }
    }
}

1 个答案:

答案 0 :(得分:0)

您需要检查的第一件事是内部

FindNearbyObjects()

变量

hitColliders = Physics.OverlapSphere(position, range);

实例化新建筑物时更改其大小。如果不是这种情况,可能是因为您在实例化时没有向建筑物添加碰撞器。

但是,我认为有更好的方法可以在不使用此物理的情况下检测建筑物.OverlapSphere(),其性能可能很昂贵。如果我是你,我将给建筑物(原始的和实例化的)提供一个特殊的标签,我将在我的逻辑中使用它来检测它们。因此,它们将成为您方法的潜在目标:

DecideWhatToDo()

在这里,您可以阅读有关如何标记GameObjects的信息:

https://docs.unity3d.com/Manual/Tags.html