【问题标题】:Make GameObject “attach” properly?使游戏对象正确“附加”?
【发布时间】:2016-03-02 17:35:45
【问题描述】:

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

为了让这个脚本工作,一个游戏对象必须有bool _sticksToObjects = true;,另一个必须有bool _sticksToObjects = false;

我尝试将RigidbodyCollision Detection 模式转换为ContinuousContinuous Dynamic

我认为我的脚本取决于帧速率。这可能就是问题所在。

普通“附加”:

异常“附加”:

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 编辑器的一些截图:

【问题讨论】:

  • @JoeBlow 这不会改变任何东西。 “在检查器中拖动连接”是什么意思?
  • @JoeBlow 我已经编辑了我的问题以使事情更清楚。
  • 你要做的这件事很难正确地完成,并且不能使用unity3d的内置物理引擎来完成。
  • @JerrySwitalski 现在关于赏金问题。
  • @JoeBlow 现在关于赏金问题。

标签: c# unity3d


【解决方案1】:

这是游戏工程中众所周知的一类问题,您会很高兴知道解决方案相对简单。您会很高兴听到有类似但更复杂的问题实际上以相同的方式解决。我会尽力解释。

现在是这样。经常会出现以下问题...

所以我正在开发 GTA。我有一个人形机器人 H 跑来跑去。她接近车辆 V。她打开车门,上车开走。之后,Mecanim 中的一切都陷入了困境,所有代码都停止工作。怎么办?

令人惊讶的是,游戏中的做法是:

令人惊讶的是:你实际上换成了完全不同的模型!!!!!

你在游戏中有 H 和 V。但是你有一个 H 爬到 V 的动画(比如说)。但是,你真的摧毁了 H 和 V 的游戏对象,并且你实例化(或只是唤醒)一个新的,完全不同,游戏对象,即 D(“一辆汽车被一位女士驾驶”)。

(如果你仔细想想,你会发现,当你这样做时,你会仔细调整 D 中的所有内容,以便它匹配帧中“刚刚发生”的内容,与 H 和 V 相关。例如,从字面上看,您将汽车 V 的变换、扭曲等复制到新的汽车内部-D,如果女士 H 具有 SmearedMakeupEffect,您将相同的 SmearedMakeupEffect 放在女士身上-within-D,所有骨骼的位置相同,依此类推。)

另一个简单的例子是,你经常有人问,“我的角色 C 被杀了,我想让它变成布娃娃,怎么做?”事实上,您只是换到一个全新的游戏对象,您已经为游戏的那一段做好了准备。的确,如果你在游戏中有一个角色 A(“Arnie”),那么你有 4 或 5 个“不同的 A”坐在台外是很正常的,所以,有“布娃娃 A”、“会跳舞的 A”、“A带武器”。事实上,其中很多都是组合,你知道“马上的甲”“车上的甲”等等。

有趣的是,这里的“真正”解决方案是,

一旦它们成为新的连接事物,就将它们都销毁并完全交换到新的游戏对象!

如果你从制作游戏中“直到你脸色发青”,这就是你理所当然地会做的事情。尽管这是一个简单的情况,但从长远来看,它会更容易。毕竟,考虑一下发生这种情况时您必须做的所有事情:

  1. 使击中对象成为另一个对象的子对象

  2. 关闭孩子的物理功能

  3. 改变你的物理对整个事物的工作方式

  4. 关闭或更改碰撞对象上的碰撞器,可能使其成为整个对象的一部分

  5. 你可能会有某种新的“分离”物理,它可以被淘汰 - 你必须打开所有这些

  6. 可能会更改声音效果、颜色等小问题

正如您所见,做所有这些事情是一项艰巨的工作,而且确实是其中之一,它只是“更容易正确完成”并更改为新模型。


话虽如此,我知道您想要一个可以粘贴的快速脚本解决方案:) 在这里...

第 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 布尔值进行单元测试

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 中运行,因此只需在 Play 中查看 MyScript 的 Inspector 并调整正确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
      }
  }

等等,你需要做“很多小事”。

也不要忘记,根据您的具体情况,您可能希望将击中对象成为大对象的子对象。 (当然他们会作为一个整体一起移动。)

请注意,在上面的定位代码中,为了便于教学,我只是将其显示为位置,而不是本地位置。如果你想让它们四处乱扔,旋转等等,你可以让被击中的物体成为另一个的孩子,你会以同样的方式使用localPosition。享受吧。

【讨论】:

  • 非常好的答案,付出了很多努力。但是,我在执行您的脚本时遇到了麻烦:我遇到了很多错误。您能否上传一个示例项目以简化流程?谢谢。如果您的项目正常运行并解决了问题,您将立即获得赏金。
  • @JoeBlow 感谢您的精彩回答。经过 3 个小时的“挠头”,恐怕我也会遇到多个错误(11 个错误)。
  • 我认为@PikPok 的建议是正确的。一个可行的示例项目将非常有帮助,并深受我的赞赏。 :) 再次,出色的答案。对此我表示感谢。
  • @JoeBlow 正如我之前告诉你的,我得到了 11 个不同的错误。我不能发布所有这些:这没有任何意义。将它们一一修复需要花费太多时间和精力。就个人而言,我认为示例项目是解决这个问题的最有效方法。
  • @JoeBlow 恕我直言,不。我是一名高级程序员,碰巧是 Unity 新手。当然,我没有忘记任何变量或类似的东西。我相信错误来自我对您(再次,非常好的)答案的可能误解。在如此复杂的问题解决中,很多事情都可能“在翻译中迷失”。而且英语不是我的母语;)同样,一个简单的示例项目是最有效的方法。
【解决方案2】:

我想到的一种可能的方法是:

在“碰撞输入”中检查这些物体之间的距离,并将应该粘在另一个物体上的那个移开一点。

正如您在图片中看到的,A 和 B 之间的距离应该等于宽度的总和除以 2(当然阈值很小)。 如果距离小于宽度的总和 / 2,那么您的“附着”异常,您必须将其中一个对象移开。做到这一点并不难。

【讨论】:

    猜你喜欢
    • 2016-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多