Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to achieve surface contact and keep in touch with Unity

2025-02-21 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

Most people do not understand the knowledge points of this article "Unity how to achieve superficial contact and keep in touch", so the editor summarizes the following content, detailed content, clear steps, and has a certain reference value. I hope you can get something after reading this article. Let's take a look at this "Unity how to achieve superficial contact and keep in touch" article.

This tutorial is created using Unity 2019.2.14f1. It also uses the ProBuilder package.

One of the effects

Parkour ball.

Stick to the ground

When our sphere reached the top of the ramp, it began to fly because of its upward momentum. This is realistic, but it may not be ideal.

The sphere flies at the top of the ramp.

A similar situation occurs when the sphere suddenly shows a small height difference. I created a test scenario that demonstrated this step in 0.1 incremental steps.

Step to test the scenario.

If the step is not too high, the ball will bounce when it approaches at a sufficient speed. In the test scenario, this rarely happens even for flat lanes, because I do this by lowering the step height to zero without merging vertices. This creates a so-called phantom collision. The scene geometry should be designed to avoid this, but I insist on pointing it out.

Bounce up the steps.

In real life, there are a variety of technologies that can fix something on the ground. For example, Formula one cars are designed to convert airflow into downpressure. Therefore, there is a realistic basis for doing similar things for our field.

Collision time

Let's consider the moment when the sphere will be launched from the ramp. In order to stick it to the surface, we must adjust its speed so that it is realigned with the surface. Let's check when we can receive the required information. Make the sphere white when it is not on the ground by adjusting its color in the OnGround-based Update, similar to the color shown at the end of the previous tutorial

Void Update () {… GetComponent () .material.SetColor ("_ Color", OnGround? Color.black: Color.white);}

To observe the exact time, temporarily reduce the physical time step and time range.

Three physical steps; time step 0.2; time scale 0.5.

Collisions still exist in the physical steps of sphere emission. We will act on these data in the next step, so we think we have taken root and are no longer needed. This is the step after we no longer get the collision data. Therefore, it is always too late, but as long as we realize this, there will be no problem.

Steps since the last grounding

Let's track how many physical steps have been taken since we grounded. Add an integer field to it and increment it at the beginning of the UpdateState. Then, if it turns out we are on the ground, set it back to zero. We will use it to determine when to hold on to the ground. It is also useful for debugging.

Int stepsSinceLastGrounded;... Void UpdateState () {stepsSinceLastGrounded + = 1; velocity = body.velocity; if (OnGround) {stepsSinceLastGrounded = 0; jumpPhase = 0; if (groundContactCount > 1) {contactNormal.Normalize ();}} else {contactNormal = Vector3.up;}}

Don't we have to guard against integer overflows?

We don't have to worry about it. Integer non-overflow will take several months in real time.

Take a picture

Add the SnapToGround method, which anchors us to the ground as needed. If we succeed, then we will be rooted. Indicates whether this has happened by returning a Boolean value (initially just false).

Bool SnapToGround () {return false;}

In this way, we can easily combine it with OnGround in UpdateState that uses Boolean OR. It works because SnapToGround is called only if OnGround is false.

Void UpdateState () {stepsSinceLastGrounded + = 1; velocity = body.velocity; if (OnGround | | SnapToGround ()) {… }... }

SnapToGround is called only when we are not grounded, so the number of steps since the last grounding is greater than zero. However, we should only try to capture it once we lose contact. Therefore, when the number of steps is greater than 1, we should stop.

Bool SnapToGround () {if (stepsSinceLastGrounded > 1) {return false;} return false;}

X-ray detection

We just want to catch it on the ground that we want to stick to under the sphere. We can check by calling Physics.Raycast with body.position and the down vector as parameters to project rays vertically down from the position of the sphere. The physics engine will perform this ray broadcast and return whether or not to hit something. If not, there is no basis, and we will stop.

If (stepsSinceLastGrounded > 1) {return false;} if (! Physics.Raycast (body.position, Vector3.down)) {return false;} return false

If the ray does hit something, then we must check whether it counts as the ground. Information about the hit information can be retrieved through the third RaycastHit structure output parameter.

If (! Physics.Raycast (body.position, Vector3.down,out RaycastHit hit)) {return false;}

How does this code work?

RaycastHit is a structure, so it is a value type. We can define a variable through RaycastHit hit and pass it to Physics.Raycast as the third parameter. But this is an output parameter, which means that it is passed by reference like an object reference. You must make this clear by adding an out modifier to it. This method is responsible for assigning values to it.

In addition, you can declare variables for output parameters in the parameter list without having to declare them on a separate line. That's what we're doing here.

The hit data includes the normal vector, which we can use to check whether the surface we hit counts as the ground. If not, please abort. Note that in this case, we are dealing with real surface normals, not collision normals.

If (! Physics.Raycast (body.position, Vector3.down, out RaycastHit hit)) {return false;} if (hit.normal.y

< minGroundDotProduct) { return false; } 重新与地面对齐 如果我们此时还没有中止,那么我们只是失去了与地面的接触,但仍然在地面之上,因此我们要抓住它。将地面接触计数设置为1,使用找到的法线作为接触法线,然后返回 true 。 if (hit.normal.y < minGroundDotProduct) { return false; } groundContactCount = 1; contactNormal = hit.normal; return true; 现在我们认为自己已经扎根,尽管我们仍处于空中。下一步是调整速度,使其与地面对齐。就像对齐所需速度一样,它的工作原理是必须保持当前速度,并且我们将显式计算该速度,而不是依靠 ProjectOnContactPlane 。 groundContactCount = 1; contactNormal = hit.normal; float speed = velocity.magnitude; float dot = Vector3.Dot(velocity, hit.normal); velocity = (velocity - hit.normal * dot).normalized * speed; return true; 在这一点上,我们仍然漂浮在地面上,但是重力将有助于将我们拉到地面。实际上,速度可能已经有些下降,在这种情况下,重新调整速度会减慢向地面的收敛速度。因此,仅当其点乘积与表面法线为正时,才应调整速度。 if (dot >

0f) {velocity = (velocity-hit.normal * dot) .normalized * speed;}

This is enough to keep our sphere on the ramp as it passes over the top. They float a little, but are rarely seen in practice. Even though the ball turns white in one frame, in FixedUpdate, we always treat the sphere as grounded. Just call Update when we are in the middle state.

Keep tilting.

It also prevents the ball from starting up when it jumps down the steps.

Stick to it.

Note that we only consider a single point below us to determine if we are above the ground. This method works as long as the geometry of the level is not too noisy or detailed. For example, if the ray happens to be projected into it, then a small deep crack may cause the rupture to fail.

Maximum capture speed

In any case, high speed is the reason why our sphere is launched, so let's add a configurable maximum capture speed. By default, it is set to the maximum speed so that it can always snap when possible.

[SerializeField, Range (0f, 100f)] float maxSnapSpeed = 100f

Maximum capture speed.

Then, when the current speed exceeds the maximum capture speed, the SnapToGround is also terminated. We can do this by calculating the speed earlier before the X-ray test.

Bool SnapToGround () {if (stepsSinceLastGrounded > 1) {return false;} float speed = velocity.magnitude; if (speed > maxSnapSpeed) {return false;} if (! Physics.Raycast (body.position, Vector3.down, out RaycastHit hit)) {return false;} if (hit.normal.y)

< minGroundDotProduct) { return false; } groundContactCount = 1; contactNormal = hit.normal; //float speed = velocity.magnitude; float dot = Vector3.Dot(velocity, hit.normal); if (dot >

0f) {velocity = (velocity-hit.normal * dot) .normalized * speed;} return true;}

Note that due to precision limitations, setting the two maximum speeds to the same value may produce inconsistent results. It is best to make the maximum capture speed above or below the maximum speed.

The same maximum speed will produce inconsistent results.

Probe distance

When there is ground below the sphere, we are catching it no matter how far away it is. It's best to check only the nearby ground. We do this by limiting the scope of the probe. There is no optimal maximum distance, but if the snap is too low, it may fail at a steep angle or at a higher speed, while a too high snap can lead to an unreasonable snap well below the ground. Make it configurable with a minimum value of zero and a default value of 1. Since the radius of our sphere is 0.5, this means that we have to check at most half a unit below the bottom of the sphere.

[SerializeField, Min (0f)] float probeDistance = 1f

Probe distance.

Add the distance as the fourth parameter to the Physics.Raycast.

If (! Physics.Raycast (body.position, Vector3.down, out RaycastHit hit, probeDistance)) {return false;}

Ignore proxy (Agents)

When checking whether the ground fits, it makes sense to consider only the geometry that can represent the ground. By default, raycast checks everything except objects placed on the ignore Raycast layer. The content that should not be counted can change, but the area we are moving is probably not. We will not accidentally encounter the sphere to be projected, because we are projecting outward from its position, but we may encounter another moving sphere. To avoid this, we can set its Layer to ignore Raycast, but let's create a new layer for all active content that should be ignored for this purpose.

Add layers from the Layer drop-down menu of the game object. Option to enter the layer settings. Label and layer (Tags and Layers) section of the project settings. Then define a new custom user layer. For a generic active entity that is not part of the level geometry, we name it Agent.

Labels and layers, as well as custom reseller layer 8.

Move all spheres to this layer. Just change the prefabricated layer.

The layer is set as a proxy.

Next, add a configurable LayerMask probe mask to MovingSphere, which is initially set to-1, which matches all layers.

[SerializeField] LayerMask probeMask =-1

We can then configure the sphere to probe all layers except Raycast and Agent.

The probe hood.

To apply the mask, add it as the fifth parameter to the Physics.Raycast.

If (! Physics.Raycast (body.position, Vector3.down, out RaycastHit hit, probeDistance, probeMask)) {return false;}

Jumping and snapping

The snap now works and can be configured, but it also activates when we jump, offsetting the upward momentum. In order for the jump to work again, we must avoid jumping immediately after the jump. We can track this by calculating the number of physical steps since the last jump, just as we have calculated the number of steps since the last grounded flight. Increments it at the beginning of the UpdateState and sets it back to zero when the jump is activated.

Int stepsSinceLastGrounded, stepsSinceLastJump;... Void UpdateState () {stepsSinceLastGrounded + = 1; stepsSinceLastJump + = 1; … }... Void Jump () {if (OnGround | | jumpPhase

< maxAirJumps) { stepsSinceLastJump = 0; jumpPhase += 1; … } } 现在,即使跳转后过早,我们也可以中止 SnapToGround 。由于碰撞数据的延迟,我们仍然认为启动跳跃后的步骤已接地。因此,如果我们在跳跃后走了两个或更少的步骤,就必须中止。 if (stepsSinceLastGrounded >

1 | | stepsSinceLastJump 0 |

You also need to reset the new data in ClearState.

Void ClearState () {groundContactCount = steepContactCount = 0; contactNormal = steepNormal = Vector3.zero;}

In EvaluateCollision, if we do not have ground contact, please check that it is steep contact. The dot product of a perfectly vertical wall should be zero, but let's be a little wider and accept all values higher than-0.01.

If (normal.y > = minDot) {groundContactCount + = 1; contactNormal + = normal;} else if (normal.y >-0.01f) {steepContactCount + = 1; steepNormal + = normal;}

Crack

The gap is problematic, because once it is stuck in the gap and does not jump, it is impossible to escape unless the air acceleration is very large. I created a test scenario with small cracks to demonstrate this.

To escape the rift.

Jump over the wall

Let's also review the wall jump. Previously, we limited jumps only to ground or air jumps. However, if we set the jump direction to the steep normal instead of touching the normal, then we can also support jumping from the wall.

Start making jump direction variables and delete the current validity check in Jump.

Void Jump () {Vector3 jumpDirection; / / if (OnGround | | jumpPhase

< maxAirJumps) { stepsSinceLastJump = 0; jumpPhase += 1; float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight); float alignedSpeed = Vector3.Dot(velocity,jumpDirection); if (alignedSpeed >

0f) {jumpSpeed = Mathf.Max (jumpSpeed-alignedSpeed, 0f);} velocity + = jumpDirection* jumpSpeed; / /}}

Instead, please check to see if we are on the ground. If so, use the contact normal as the jump direction. If not, then the next check is whether we are in a steep state. If so, use steep normals instead. After that, check the air jump, and for this we use the contact normal again, which has been set to the up vector. If all of this does not apply, it is impossible to jump, and we abort.

Vector3 jumpDirection; if (OnGround) {jumpDirection = contactNormal;} else if (OnSteep) {jumpDirection = steepNormal;} else if (jumpPhase

< maxAirJumps) { jumpDirection = contactNormal; } else { return; } 墙跳。 空中跳跃 此时,我们应该重新考虑空中跳跃。检查跳跃阶段是否小于最大空中跳跃的唯一作用是,在跳跃之后,该阶段会立即重置为零,因为在下一步中,我们仍将其视为接地。因此,我们仅应在启动跳转后多一步才能在 UpdateState 中重置跳转阶段,以免错误着陆。 stepsSinceLastGrounded = 0; if (stepsSinceLastJump >

1) {jumpPhase = 0;}

In order to keep the air jump running normally, we must now check whether the jump phase is less than or equal to the maximum value in Jump.

Else if (jumpPhase

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report