你如何让一个游戏对象停留在另一个之上?

How do you make a gameobject stay on top of another?

我正在开发 puzzle platformer I made, 的完整版本,你可以在其中切换玩家,在这种情况下(或问题,idk),携带其他玩家不在控制。

基本上我希望被控制的玩家能够将另一个玩家顶在它的头上,并且仍然能够跳得一样高。在 game jam 版本中,我只是将刚体质量设置为零,并使速度与其下方的立方体相同。但是现在,如果我这样做,每当我移动时,上面的玩家都会 极快 并四处传送。不完全是我想要的。

我试过让他们都用力移动和跳跃,但是上面的跳下了,然后他们在空中相遇,上面的又跳了。我也试过用固定关节,但是动作太快而且坏了,而且它们粘在一起太好了。我尝试施加更大的力,但顶部的立方体跳得很高。我就是赢不了。

最重要的是,每当顶部的立方体在空中落到底部的立方体上时,顶部的立方体几乎将底部的立方体撞到地上 - 它们掉落 非常快

我想要的是顶部的立方体很好地坐在底部的立方体上而不比底部的立方体高,只是坐着在它上面,没有降低跳跃,但也很容易被一个推开它的块移除,底部玩家跳到它下面。

编辑:尝试对多维数据集进行育儿无效,相反我遇到了奇怪的故障。

编辑 2:我尝试了 FixedJoint2D,但设置略有不同,有时它可以工作,但有时它会无故中断并且运动很奇怪。就像...你不应该跳那么高。 代码:

        //amControlled is true if this is the player that is being controlled.
        if (!amControlled)
        {
            //rayHitGroundLeft and rayHitGroundRight are raycasts off the left side of the bottom side and the right side of the bottom respectively.
            if ((rayHitGroundLeft && rayHitGroundLeft.collider.CompareTag("Player")) || (rayHitGroundRight && rayHitGroundRight.collider.CompareTag("Player")))
            {
                if (!gameObject.TryGetComponent(out FixedJoint2D joint))
                {
                    joint = gameObject.AddComponent<FixedJoint2D>();
                }

                joint.connectedBody = rayHitGroundLeft.rigidbody;
                // See https://docs.unity3d.com/ScriptReference/Joint-breakForce.html
                // this allows the other collisions simply breaking this connection
                joint.breakForce = 100f;
                // Shall these two objects still be able to collide with each other while they are attached?
                // And shall other objects attached to the same body collide with each other?
                joint.enableCollision = true;

                rb.mass = 0f;
            }
            else
            {
                if (gameObject.TryGetComponent(out FixedJoint2D joint))
                {
                    Destroy(joint);
                }
                rb.mass = 1f;
            }
        }

首先禁用所有可以移动你的顶部的东西object,比如刚体(让它是运动学的)。 然后你可以 parent 顶部 object 到底部 object (或者可能到它头上的空或骨头)就是这样!当你 parent 一个 object 到另一个时,parent 将移动 child。 对于 parent 在代码中使用“topObject.transform.SetParent(bottomObject.transform)” 对于 un parent 使用 "topObject.transform.SetParent(null)"

我想你想调查一下 FixedJoint

Fixed Joints restricts an object’s movement to be dependent upon another object. This is somewhat similar to Parenting but is implemented through physics rather than Transform hierarchy. The best scenarios for using them are when you have objects that you want to easily break apart from each other, or connect two object’s movement without parenting.

您可以通过

在运行时轻松附加它
if(!yourObject.TryGetComponent<FixedJoint>(out var joint))
{
    joint = yourObject.AddComponent<FixedJoint>();
}

joint.connectedBody = theBottomObject;
// See https://docs.unity3d.com/ScriptReference/Joint-breakForce.html
// this allows the other collisions simply breaking this connection
joint.breakForce = FORCE_REQUIRED_TO_BREAK_APART;
// Shall these two objects still be able to collide with each other while they are attached?
// And shall other objects attached to the same body collide with each other?
joint.enableCollision = false;

而如果你想脱离自己,就像设置一样简单

joint.enabled = false;

Destroy(joint);

首先,您需要使用脚本将 Top cube 转换为 Kinematic,然后您需要引用 Bottom cube 以获取它的 Rigidbody.position,让 TopCube.Rigidbody.position = BottomCube.rigidbody.position + offset ,with offset = new vector 3(0, TopCube's height/2 +0.01,0)(+0.01 to they do not overlap each other) .when you stop carry just return Top Cube to动态.

我想我已经修好了!花了一段时间——实际上是 11 天。基本上我让它在玩家身上时保持它的速度(使用 rb.AddForce(new Vector2(rb.velocity.x * -50f, rb.velocity.y * -50f));。我还根据有多少玩家在玩家上面添加更多的力量(通过将跳跃的力量乘以比玩家多一个在顶部),我让它在着陆后等待一帧再跳跃。当跳上和跳下更高的平台时,这会阻止跳跃较低。基本上我现在要复制相关代码。

isGrounded = false;

        float length = 0.017f;
        float extraDistance = 0.004f;

        RaycastHit2D rayHitGroundLeft = Physics2D.Raycast(new Vector2(boxCollider.bounds.center.x - boxCollider.bounds.extents.x + extraDistance, boxCollider.bounds.center.y - boxCollider.bounds.extents.y - extraDistance), Vector2.down, length, groundMask);
        Debug.DrawRay(new Vector2(boxCollider.bounds.center.x - boxCollider.bounds.extents.x + extraDistance, boxCollider.bounds.center.y - boxCollider.bounds.extents.y - extraDistance), Vector2.down * length, color);

        RaycastHit2D rayHitGroundRight = Physics2D.Raycast(new Vector2(boxCollider.bounds.center.x + boxCollider.bounds.extents.x - extraDistance, boxCollider.bounds.center.y - boxCollider.bounds.extents.y - extraDistance), Vector2.down, length, groundMask);
        Debug.DrawRay(new Vector2(boxCollider.bounds.center.x + boxCollider.bounds.extents.x - extraDistance, boxCollider.bounds.center.y - boxCollider.bounds.extents.y - extraDistance), Vector2.down * length, color);

        RaycastHit2D rayHitCeilingLeft = Physics2D.Raycast(new Vector2(boxCollider.bounds.center.x - boxCollider.bounds.extents.x + extraDistance, boxCollider.bounds.center.y + boxCollider.bounds.extents.y + extraDistance), Vector2.up, length, groundMask);
        Debug.DrawRay(new Vector2(boxCollider.bounds.center.x - boxCollider.bounds.extents.x + extraDistance, boxCollider.bounds.center.y + boxCollider.bounds.extents.y + extraDistance), Vector2.up * length, color);

        RaycastHit2D rayHitCeilingRight = Physics2D.Raycast(new Vector2(boxCollider.bounds.center.x + boxCollider.bounds.extents.x - extraDistance, boxCollider.bounds.center.y + boxCollider.bounds.extents.y + extraDistance), Vector2.up, length, groundMask);
        Debug.DrawRay(new Vector2(boxCollider.bounds.center.x + boxCollider.bounds.extents.x - extraDistance, boxCollider.bounds.center.y + boxCollider.bounds.extents.y + extraDistance), Vector2.up * length, color);


        //amControlled is true if this is the player that is being controlled.
        if (!amControlled)
        {
            //rayHitGroundLeft and rayHitGroundRight are raycasts off the left side of the bottom side and the right side of the bottom respectively.
            if ((rayHitGroundLeft && rayHitGroundLeft.collider.CompareTag("Player")) || (rayHitGroundRight && rayHitGroundRight.collider.CompareTag("Player")))
            {
                if (rayHitGroundLeft && rayHitGroundLeft.collider.CompareTag("Player"))
                {
                    rb.AddForce(new Vector2(rb.velocity.x * -50f, rb.velocity.y * -50f));
                    //rb.velocity = new Vector2(0f, 0f);
                    //rb.AddForce(rayHitGroundLeft.rigidbody.velocity * 50f);
                }
                if (rayHitGroundRight && rayHitGroundRight.collider.CompareTag("Player"))
                {
                    rb.AddForce(new Vector2(rb.velocity.x * -50f, rb.velocity.y * -50f));
                    //rb.velocity = new Vector2(0f, 0f);
                    //rb.AddForce(rayHitGroundRight.rigidbody.velocity * 50f);
                }

                //rb.mass = 0f;

                //rb.velocity = new Vector2(0f, 0f);
            }
            else
            {
                if (!((rayHitDownLeft && rayHitDownLeft.collider.CompareTag("Player")) || (rayHitDownRight && rayHitDownRight.collider.CompareTag("Player")) || (rayHitUpLeft && rayHitUpLeft.collider.CompareTag("Player")) || (rayHitUpRight && rayHitUpRight.collider.CompareTag("Player"))))
                {
                    rb.AddForce(new Vector2(rb.velocity.x * -50f, 0));
                }
                //rb.AddForce(new Vector2(rb.velocity.x * -50f, 0));
            }
        }

        if ((rayHitCeilingLeft && rayHitCeilingLeft.collider.CompareTag("Player")) || (rayHitCeilingRight && rayHitCeilingRight.collider.CompareTag("Player")))
        {
            playersAboveMe = 1;
        }
        else
        {
            playersAboveMe = 0;
        }

        if (amControlled)
        {
            if (Input.GetKey(KeyCode.A))
            {
                horizontal -= 1f;
            }

            if (Input.GetKey(KeyCode.D))
            {
                horizontal += 1f;
            }

            if (Input.GetKey(KeyCode.W) && isGrounded && wasGrounded)
            {
                isJumping = true;
                jumpTimeCounter = jumpTime;
                vertical = 10.4f;
            }
            if (!Input.GetKey(KeyCode.W))
            {
                isJumping = false;
            }
            if (isGrounded)
            {
                jumpTimeCounter = jumpTime;
            }
            if (Input.GetKey(KeyCode.W) && isJumping)
            {
                if (jumpTimeCounter > 0)
                {
                    vertical += 0.7f;
                    jumpTimeCounter -= Time.deltaTime;
                }
                else
                {
                    isJumping = false;
                }
            }

            if (isGrounded && !wasGrounded)
            {

            }

            if (Input.GetKeyUp(KeyCode.W))
            {
                isJumping = false;
                isGrounded = false;
            }

            if (Input.GetKey(KeyCode.S))
            {
                vertical -= 0.3f;
            }
        }

        Vector2 movement = new Vector2(horizontal, vertical);
        if (!(rb.velocity.x > 3f && movement.x > 0) && !(rb.velocity.x < -3f && movement.x < 0))
        {
            rb.AddForce(new Vector2(movement.x * 20f, 0f));
        }
        if (!(rb.velocity.y > 5f && movement.y > 0))
        {
            rb.AddForce(new Vector2(0f, movement.y * 20f * (playersAboveMe + 1)));
        }
        if (rb.velocity.y > 5.5f)
        {
            rb.AddForce(new Vector2(0f, rb.velocity.y * -10f));
        }

        if (movement.x == 0 && amControlled)
        {
            rb.AddForce(new Vector2(rb.velocity.x * -50f, 0));
        }

        if (movement.x > 0)
        {
            GetComponent<SpriteRenderer>().flipX = false;
        }
        else if (movement.x < 0)
        {
            GetComponent<SpriteRenderer>().flipX = true;
        }

        if (rb.velocity.y < 0)
        {
            rb.velocity += Vector2.up * Physics2D.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
        }

        wasGrounded = isGrounded;

        vertical = 0f;
        horizontal = 0f;