c# - 正确制作 GameObject “attach”?

标签 c# unity3d

该脚本使立方体“粘”到它与之碰撞的任何物体上。问题在于,当它以相对较高或中等的速度运行时(或者当设备本身速度较慢时),立方体往往会“进入”它与之碰撞的物体,然后粘在上面。我必须进行哪些更改才能解决此问题?

为了让这个脚本工作,一个游戏对象必须有 bool _sticksToObjects = true;和另一个 bool _sticksToObjects = false;
我试过转动 RigidbodyCollision Detection模式为 ContinuousContinuous Dynamic
我认为我的脚本取决于帧速率。这可能就是问题所在。

正常的“附加”:

enter image description here

异常“附加”:

enter image description here

Rigidbody _rigidBody;
Transform _meshTransform;
bool _sticksToObjects = true;

public Transform _stuckTo = null;
protected Vector3 _offset = Vector3.zero;

void Awake()
{
    GameObject CubeMesh = GameObject.FindWithTag ("CubeMesh");
    GameObject Cube = GameObject.FindWithTag ("Cube");

    _rigidBody = Cube.GetComponent<Rigidbody> ();
    _meshTransform = CubeMesh.GetComponent<Transform> ();
}

void Update() 
{
    if (_stuckTo != null)
    {
        transform.position = _stuckTo.position - _offset;
    }
}

void OnCollisionEnter(Collision collision)
{
        if (!_sticksToObjects) {
            return;
        }

        _rigidBody.isKinematic = true;

        // Get the approximate collision point and normal, as there
        // may be multipled collision points
        Vector3 contactPoint = Vector3.zero;
        Vector3 contactNormal = Vector3.zero;
        for (int i = 0; i < collision.contacts.Length; i++) {
            contactPoint += collision.contacts [i].point;
            contactNormal += collision.contacts [i].normal;
        }

        // Get the final, approximate, point and normal of collision
        contactPoint /= collision.contacts.Length;
        contactNormal /= collision.contacts.Length;

        // Move object to the collision point
        // This acts as setting the pivot point of the cube mesh to the collision point
        transform.position = contactPoint;

        // Adjust the local position of the cube so it is flush with the pivot point
        Vector3 meshLocalPosition = Vector3.zero;

        // Move the child so the side is at the collision point.
        // A x local position of 0 means the child is centered on the parent,
        // a value of 0.5 means it's to the right, and a value of -0.5 means it to the left
        meshLocalPosition.x = (0.5f * contactNormal.x);
        _meshTransform.localPosition = meshLocalPosition;

        if (_stuckTo == null || _stuckTo != collision.gameObject.transform) {
            _offset = collision.gameObject.transform.position - transform.position;
        }

        _stuckTo = collision.gameObject.transform;
    }

以下是 Unity 编辑器的一些屏幕截图:

enter image description here

enter image description here

enter image description here

最佳答案

这是游戏工程中众所周知的一类问题,您会很高兴知道解决方案相对简单。您会很高兴听到类似但更复杂的问题实际上以相同的方式解决。我会试着解释一下。
现在事情来了。经常会出现以下问题...

So I'm working on GTA. I have a humanoid, H, running around. She approaches vehicle V. She opens the door and gets in and drives off. After that everything goes to hell in Mecanim and all the code stops working. What to do?


令人惊讶的是,在游戏中完成的方式是:
令人惊讶的是:那时你实际上换了完全不同的模型!!!!!!
你在游戏中有 H 和 V。但是然后你有一个动画(比如说)让 H 爬到 V。但是,你真的摧毁了 H 和 V 的游戏对象,而你实例化(或只是唤醒)一个全新的、完全不同的游戏对象 ,即 D(“一位女士驾驶的汽车”)。
(如果你仔细想想,你会发现,当你这样做时,你仔细调整了 D 中的所有东西,以便它 匹配 帧中“刚刚发生的事情”,与两者有关H 和 V。例如,从字面上看,您将汽车 V 的变换、扭曲等复制到新的 car-inside-D,如果女士 H 具有 SmearedMakeupEffect,则将相同的 SmearedMakeupEffect 放在 Lady-in-D ,您将所有骨骼放置相同,依此类推。)
另一个简单的例子是,你经常有人问,“我的角色 C 被杀了,我想让它变成布娃娃,怎么做?”事实上,您只需切换到一个全新的游戏对象,您已经为游戏的那段时间设置好了。事实上,如果你在游戏中有一个角色 A(“Arnie”),那么你有 4 或 5 个“不同的 As”坐在舞台外是很正常的,所以,有“布娃娃 A”、“会跳舞的 A”、“A”带武器”。事实上,其中许多都是组合,你知道“A on the horse”“A in the car”等等。
有趣的是,这里的“真正”解决方案是,
一旦它们成为一个新的连接物,将它们都销毁并完全交换到一个新的游戏对象!
如果您通过制作游戏制作游戏“直到您脸色发青”,这就是您理所当然地会做的事情。尽管这是一个简单的情况,但从长远来看会更容易。毕竟,请考虑发生这种情况时您必须做的所有事情:
  • 使击中对象成为另一个的 child
  • 关闭 child 的物理
  • 改变你的物理学对整个事物的运作方式
  • 关闭或更改击中对象上的碰撞器,可能使其成为整个对象的一部分
  • 你可能会有某种新的“分离”物理学,它可以被淘汰——你必须打开所有这些
  • 可能会改变音效、颜色等小问题

  • 正如您所看到的,完成所有这些工作是一项艰巨的任务,而事实上,这只是“更容易正确执行”并更改为新模型的其中一项。

    话虽如此,我知道您想要一个可以粘贴的快速脚本解决方案:) 这里是...
    第 0 步,您将创建“主”立方体上的“YourScript”。它会“捕捉”另一个移动的立方体。
    YourScript 基本上看起来像这样......
       [System.NonSerialized] public bool isConnectedNow;
       void OnCollisionEnter(Collision collision)
          GameObject theThingWeCaught = collision.gameObject
          Debug.Log("We caught this thing .. " + theThingWeCaught.name)
          // make it a child of us......
          theThingWeCaught.transform.parent = transform
          theThingWeCaught ... set kinematic
          theThingWeCaught ... probably disable the rigidbody
          theThingWeCaught ... probably disable the collider
          isConnectedNow = true;
    
    这就是你真正需要做的。
    第 1 步,您的脚本必须有 public bool像这样
     [System.NonSerialized] public bool isConnectedNow;
    
    第 2 步,这里是 MyScript继续击球立方体,首先我们将单元测试您的 isConnectedNow bool 正在工作
    public Class MyScript:MonoBehaviour // attach to the "child" cube
     {
     public float correctXDistance;
     public float correctYDistance;
     public Transform bigCube;
     public YourScript yourScript;
    
     void Update()
      {
      string message = yourScript.isConnectedNow ? "free" : "stuck";
      Debug.Log("I am " + message);
      }
     }
    
    附加、调试和运行。使小立方体粘住并从大立方体上取下.. 观察控制台。有用?所以将此添加到 MyScript
    private void DistanceCorrectionX()
     {
     float xDistance = bigCube.position.x - transform.position.x;
     float xSign = Mathf.Sign(xDistance);
     float xDelta = Mathf.Abs(xDistance);
     float closenessPercentage = (xDelta/correctXDistance)*100f;
     if ( closenessPercentage<90f || closenessPercentage>110f)
        {
        // they are not close enough to quantize on this axis
        // this comes in to play when you have multiple axes
        return;  // do nothing.
        }
     float xShouldBe = bigCube.position.x + xSign * correctXDistance;
     Vector3 p = transform;
     p.x = xShouldBe; // be careful it's .y, .z etc for other axes
     transform.position = p;
     }
    
    现在像这样在 MyScript 中的 Update() 中调用它
     void Update()
      {
      Debug.Log("I am " yourScript.isConnectedNow ? "free" : "stuck");
      if (yourScript.isConnectedNow) DistanceCorrectionX();
      }
    
    现在实际播放并坚持下去。现在,由于它在 Update 中运行只是在播放时查看检查器 MyScript调整正确XDistance的值以获得您想要的确切外观。当您决定一个值时,取消播放并将其作为您希望的最终值。
    接下来,在 DistanceCorrectionX只需复制所有代码并为 Y 轴再次执行 DistanceCorrectionX .如果你也做 Z,那就做吧。
    最后。注意你会有很多乱七八糟的代码,像这样......
     void Update()
      {
      // handle all the DistanceCorrectionX etc as seen above.
      
      if (yourScript.isConnectedNow)
          {
          .. turn off the collider on me
          }
      else
          {
          .. turn on the collider on me
          }
      }
    
    依此类推,您需要做“许多小事”。
    也不要忘记,绝大多数情况下您可能想要制作击中对象 的 child 大对象,看你的情况。 (当然,他们会作为一个整体一起走动。)
    请注意,在上面的定位代码中,为了教学清晰,我只是将其显示为位置,而不是本地位置。如果你想让它们四处乱飞,旋转等等,你可以让击中对象成为另一个的 child ,你可以使用 localPosition以同样的方式。享受。

    关于c# - 正确制作 GameObject “attach”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35754290/

    相关文章:

    c# - 为 webClient.DownloadFile() 设置超时

    audio - 多个敌人拥有自己的音频源和剪辑,使我的游戏的音频滞后

    c# - 如何通过 Unity 使用 WebGL/C# 在新选项卡上打开链接?

    c# - Azure Web 角色访问外部 Web 服务时遇到问题

    c# - SignInManager.PasswordSignIn 第一次尝试很慢

    c# - 为什么我不能在实现 IEnumerable 的类上使用 LINQ 方法?

    c# - 使用 Homography 链接两张图片像素

    ios - 无法将统一调试器附加到 iOS 设备

    c# - 谷歌 Firebase 和 Unity (C#) : Unable to download png from bucket

    java - Unity Daydream APK构建失败