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 realize influential spherical Motion by Unity

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

Share

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

Today, I would like to share with you the relevant knowledge points about how to achieve influential sphere sports in Unity. The content is detailed and the logic is clear. I believe most people still know too much about this knowledge, so share this article for your reference. I hope you can get something after reading this article. Let's learn about it.

This tutorial is made using Unity 2019.4.4f1. It also uses the ProBuilder package.

One of the effects

Correction

I improved the 1.4 section of the orbital camera called "centering the focus" in order to better realize the interaction between the focus center and the focus radius limit. Adjust the OrbitCamera.UpdateFocusPoint as follows:

Void UpdateFocusPoint () {previousFocusPoint = focusPoint; Vector3 targetPoint = focus.position; if (focusRadius > 0f) {float distance = Vector3.Distance (targetPoint, focusPoint); float t = 1f; if (distance > 0.01f & & focusCentering > 0f) {t = Mathf.Pow (1f-focusCentering, Time.unscaledDeltaTime);} if (distance > focusRadius) {t = Mathf.Min neighbors (t, focusRadius / distance) } focusPoint = Vector3.Lerp (targetPoint, focusPoint, t);} else {focusPoint = targetPoint;}}

I also changed the "moving ground" section 2.3 to determine the motion, thus ignoring the lighter connected objects. This prevents the sphere from automatically following the light object it pushes away. Adjust the end of MovingSphere.UpdateState as follows:

If (connectedBody) {if (connectedBody.isKinematic | | connectedBody.mass > = body.mass) {UpdateConnectionState ();}}

Finally, the "optional climbing" in Section 2.5 of "climbing" has been changed to prevent it from automatically sticking to the climbable surface of the animation. This is done by adjusting the desiresClimbing in EvaluateCollision rather than in the Climbing property:

Bool Climbing = > climbContactCount > 0 & & stepsSinceLastJump > 2

... Void EvaluateCollision (Collision collision) {… If (desiresClimbing & & upDot > = minClimbDotProduct & & (climbMask & (1 = speed) {return;}

Velocity.y = speed; body.velocity = velocity;}

Push something into the area.

To prevent a sudden fall to the ground

This simple method works well when launching regular objects, but our sphere does not fire correctly. When it enters the area, it seems to have achieved a great forward speed. This happened because we stuck it on the ground. In this case, it can be solved by reducing the maximum capture speed, but it does not apply to acceleration areas that are set to low speed. In order to prevent grounding, generally speaking, we must instruct MovingSphere not to perform grounding for the time being. We can do this by adding a public method to PreventSnapToGround that sets the stepsSinceLastJump to-1.

Public void PreventSnapToGround () {stepsSinceLastJump =-1;}

Now that AccelerationZone.Accelerate can call this method if the body has a MovingSphere component, we can check and retrieve it by calling the TryGetComponent sphere as the output parameter.

Void Accelerate (Rigidbody body) {… If (body.TryGetComponent (out MovingSphere sphere)) {sphere.PreventSnapToGround ();}}

Activate.

Note that this method does not reset the jump phase, so the springboard does not refresh the air jump without landing.

Continuous acceleration

Instantaneous velocity changes are appropriate for springboards, but we can also use this area to create other continuous acceleration phenomena, such as levitation areas. We can support this by simply adding the same method OnTriggerEnter as OnTriggerStay.

Void OnTriggerStay (Collider other) {Rigidbody body = other.attachedRigidbody; if (body) {Accelerate (body);}}

If the effect lasts longer, it would be better to achieve the velocity change with the appropriate acceleration, so let's add a configurable acceleration to the area with a minimum of zero. If we set it to zero, we will change it immediately, otherwise acceleration will be applied.

[SerializeField, Min (0f)] floatacceleration = 10f speed = 10f

...

Void Accelerate (Rigidbody body) {…

If (acceleration > 0f) {velocity.y = Mathf.MoveTowards (velocity.y, speed, acceleration * Time.deltaTime);} else {velocity.y = speed;}... }

Air acceleration in suspension zone 1.

You can also apply a force so that the acceleration of a mass object will eventually slow down, but a fixed acceleration makes horizontal design easier, so I used this.

In any direction

Finally, in order for it to accelerate in any direction, convert the human body speed to the local space of the region at the beginning of the Accelerate, and convert it back to world space when applied. This is done through InverseTransformDirection and TransformDirection, so the proportion of the region does not affect it. You can now control the direction of acceleration by rotating the area.

Void Accelerate (Rigidbody body) {Vector3 velocity = transform.InverseTransformDirection (body.velocity); …

Body.velocity = transform.TransformDirection (velocity); … }

Jump between the jump zones.

React to existence

The acceleration zone is just an example of how to create a trigger zone with specific behavior. If you need an area to perform other operations, you must write new code for it. But the simple act of detecting and responding to the presence of something somewhere is so common that we ideally write it only once. And many behaviors are so simple (such as activating objects) that dedicated component types cannot be created for them. More complex behaviors are usually just a combination of simple actions. It would be convenient if the level designer could simply configure the game object and add some components to create it without having to create specialized code all the time.

Detection area

Let's start by creating a DetectionZone component that detects whether something exists in its area and notifies interested people when something enters or exits. We configure it with fields of type UnityEvent, onEnter and onExit, from the UnityEngine.Events namespace.

Using UnityEngine;using UnityEngine.Events

Public class DetectionZone: MonoBehaviour {

[SerializeField] UnityEvent onEnter = default, onExit = default;}

Just have it call the method on the appropriate event in OnTriggerEnter and OnTriggerExit. This triggers a method call to everything that is registered for the event. Void OnTriggerEnter (Collider other) {onEnter.Invoke ();}

Void OnTriggerExit (Collider other) {onExit.Invoke ();}

The inspector exposes the events of the component as lists named On Enter () and On Exit (), which are initially empty. There is nothing in parentheses after the name, indicating that these events have no parameters.

There are no events in the detection area.

Material selector

To demonstrate how this works, we will create a simple MaterialSelector component type with configurable materials and MeshRenderer reference arrays. It has a Select public method with an index parameter, which assigns a valid material to the renderer, if valid.

Using UnityEngine

Public class MaterialSelector: MonoBehaviour {

[SerializeField] Material [] materials = default

[SerializeField] MeshRenderer meshRenderer = default

Public void Select (int index) {if (meshRenderer & & materials! = null & & index > = 0 & & index

< materials.Length ) { meshRenderer.material = materials[index]; } }} 创建一个带有红色非活动区域和绿色活动区域的材质选择器组件,这些组件将用于更改检测区域的可视化。尽管不需要将其添加到受影响的游戏对象中,但这是最有意义的。 材料选择器。 现在,通过按项目的+按钮将其添加到检测区域组件的输入事件列表中。通过材质选择器的左下角字段将游戏对象链接到该项目。之后,可以选择MaterialSelector.Select方法。由于此方法具有整数参数,因此其值将显示在方法名称下方。默认情况下,它设置为零,表示无效状态,因此将其设置为1。然后对退出事件执行相同的操作,这次将参数保留为零。 检测区域设置为选择材料。 确保默认情况下,区域对象使用不活动的红色材料。然后以这种方式开始,但是一旦有物体进入区域,它将切换为活动的绿色材料。当有东西离开该区域时,它将再次变为红色。 与检测区域进行交互。 首次进入和最后退出 该检测区域可以工作,但确实可以完成其编程的工作,即每次进入时调用一次进入,每次离开时调用一次退出。因此,我们可以混合使用enter和exit事件(例如enter,enter,exit,enter,exit,exit),并且当其中仍然有东西时,最终会出现视觉上无效的区域。在区域中保持活动状态时,使区域保持活动状态更加直观。使用保证进入和退出事件将严格交替的区域进行设计也更加容易。因此,它仅应在第一件东西进入时和最后一件东西离开时发出信号。将事件重命名为onFirstEnter,并将onLastExit其重命名以使其变得清晰,这将需要再次挂接事件。 重命名的事件。 为了使这种行为成为可能,我们必须跟踪区域中当前的对撞机。我们将通过将DetectionZone命名空间中的List字段初始化为System.Collections.Generic新列表来完成此操作。 using UnityEngine;using UnityEngine.Events;using System.Collections.Generic; public class DetectionZone : MonoBehaviour { [SerializeField] UnityEvent onFirstEnter = default, onLastExit = default; List colliders = new List(); …} 该列表如何工作? 请参阅" 对象管理"系列的" 持久对象"教程。 在OnTriggerEnter中仅调用输入事件如果列表为空,则始终对撞机添加到列表中,以保持它的轨道。 void OnTriggerEnter (Collider other) { if (colliders.Count == 0) { onFirstEnter.Invoke(); } colliders.Add(other); } 在这种情况下,我们将在OnTriggerExit中从列表删除对撞机,仅当列表为空时才调用exit事件。列表的Remove方法返回删除是否成功。应当总是这样,因为否则我们将无法跟踪对撞机,但是我们仍然可以对其进行检查。 void OnTriggerExit (Collider other) { if (colliders.Remove(other) && colliders.Count == 0) { onLastExit.Invoke(); } } 只要区域中有东西,就活跃。 检测出现和消失的对象 不幸的是,OnTriggerExit它是不可靠的,因为在停用,禁用或销毁游戏对象或其对撞机时,不会调用它。不应该单独禁用碰撞器,因为那样会导致物体掉落到几何体中,因此我们将不支持此功能。但是我们应该能够处理整个游戏对象在区域内时被禁用或破坏的情况。 每个物理步骤,我们都必须检查区域中的对撞机是否仍然有效。添加一个在对撞机列表中循环的FixedUpdate方法。如果对撞机进行评估,false则意味着它或其游戏对象已被破坏。如果不是这种情况,我们必须检查其游戏对象是否已停用,我们可以通过activeInHierarchy其游戏对象的属性来查找。如果对撞机不再有效,请从列表中将其删除,并减少循环迭代器。如果列表为空,则调用exit事件。 void FixedUpdate () { for (int i = 0; i < colliders.Count; i++) { Collider collider = colliders[i]; if (!collider || !collider.gameObject.activeInHierarchy) { colliders.RemoveAt(i--); if (colliders.Count == 0) { onLastExit.Invoke(); } } } } 大多数情况下,检测区域中可能没有物体。为了避免不必要的FixedUpdate连续调用,我们可以在唤醒组件时以及最后一个对撞机退出后禁用该组件。然后我们只有在有东西进入后才启用它。之所以有效,是因为无论是否启用行为,总是会触发触发器方法。 void Awake () { enabled = false; } void FixedUpdate () { for (int i = 0; i < colliders.Count; i++) { Collider collider = colliders[i]; if (!collider || !collider.gameObject.activeInHierarchy) { colliders.RemoveAt(i--); if (colliders.Count == 0) { onLastExit.Invoke(); enabled = false; } } } } void OnTriggerEnter (Collider other) { if (colliders.Count == 0) { onFirstEnter.Invoke(); enabled = true; } colliders.Add(other); } void OnTriggerExit (Collider other) { if (colliders.Remove(other) && colliders.Count == 0) { onLastExit.Invoke(); enabled = false; } } 接下来,我们还应该处理区域游戏对象本身被停用或销毁的情况,因为当事件仍在区域中时发生时,调用退出事件是有意义的。我们都可以通过添加OnDisable清除列表并在列表不为空时调用exit事件的方法来做到。 void OnDisable () { if (colliders.Count >

0) {colliders.Clear (); onLastExit.Invoke ();}}

Note that the component of the detection area should not be disabled by other code because it can manage its own state. The general rule is not to disable the detection area component, nor to disable any colliders that may affect the area. All these game objects should be deactivated or destroyed.

Hot charging

Because the hot reload (recompiled in editor playback mode) OnDisable will be called, it violates the rule we just declared. This will cause the exit event to be called in response to a hot overload, and objects that already exist in the area will be ignored. Fortunately, we can detect hot reloading in OnDisable. If the component is enabled at the same time and the game object is active, we will hot reload and do nothing. The same is true when the game object is not destroyed and the component is destroyed, but we rule that this should not be done.

We just need to check when playing in the editor to wrap the code in # if UNITY_EDITOR and # endif.

Void OnDisable () {# if UNITY_EDITOR if (enabled & & gameObject.activeInHierarchy) {return;} # endif if (colliders.Count > 0) {colliders.Clear (); onLastExit.Invoke ();}}

What is the relevant combination of states in OnDisable?

If the component is disabled, disable or disable the game object, and then we move on. Otherwise, if the game object is not active, the game object will be deactivated or destroyed, and then we will continue. Otherwise, it is either hot-loaded or only the components are destroyed, which we ignore.

More complex behavior

This is just a simple demonstration that can be done through events. You can create more complex behaviors by adding more entries to the event list. You don't have to create a new method for this, you can use an existing method. The limitation is that it must be a void method or property setter that matches the parameter list of the event, or has at most one serializable parameter. For example, I made some settings to turn off the floating area while detecting something in the area, in addition to changing the visualization of the area itself.

Toggle the suspension zone.

You don't always have to respond to all events. You may trigger certain events only when you enter or exit. For example, activate something when you enter an area. Then quitting will not deactivate it, while re-entering will activate it again, which will not help.

Can this event-based approach be applied to the entire game?

In theory, yes, this is very useful for rapid prototyping, but it is troublesome. Once you find yourself repeating a complex pattern, you can create a dedicated method or behavior for it, which should be easier to use and optimize later if necessary.

Simple exercise

The last situation we will introduce in this tutorial is moving environment objects. Complex motion can be accomplished by animation and can be triggered by detecting areas. But a simple linear interpolation between two points is usually sufficient, for example, for doors, elevators or floating platforms. So let's add support for this.

Automatic slider

Whatever is interpolated, it is conceptually controlled by a slider from 0 to 1. How to change the value is a different problem than interpolation itself. Keeping the slider separate can also be used for multiple interpolation. Therefore, we will create a component that AutomaticSlider is dedicated to this value. Its configurable duration must be positive. When we use it to animate a physical object, we will increase its value in the FixedUpdate method and make sure it does not overshoot. Once the value reaches 1, we can finish and disable the slider.

Using UnityEngine

Public class AutomaticSlider: MonoBehaviour {

[SerializeField, Min (0.01f)] float duration = 1f

Float value

Void FixedUpdate () {value + = Time.deltaTime / duration; if (value > = 1f) {value = 1f; enabled = false;}

Once again, we will use the Unity event to attach the behavior to the slider. In this case, we need an on-value-changed event that will be used to pass the current value of the slider. Therefore, our event requires a float parameter for which we can use the UnityEvent type. The event is called at the end of the FixedUpdate.

Using UnityEngine;using UnityEngine.Events

Public class AutomaticSlider: MonoBehaviour {… [SerializeField] UnityEvent onValueChanged = default

Float value

Void FixedUpdate () {… OnValueChanged.Invoke (value);}}

However, Unity cannot serialize the common event type, so the event does not appear in the inspector. We must create our own concrete serializable event type, which can simply extend UnityEvent. This type is specific to our slider, so it is set to a nested type by declaring it inside the class as well as the event field itself.

[System.Serializable] public class OnValueChangedEvent: UnityEvent {}

[SerializeField] OnValueChangedEventonValueChanged = default

When you enter playback mode, the slider starts to increase immediately. If you do not want to do this, disable it by default. You can then connect it to the detection area to enable it later.

A disabled slider with a value change event.

Note that in this case, the name of the event is followed by (Single), indicating that it has a parameter. Single refers to the float type, which is a single-precision floating point number.

Position interpolator

Next, create a PositionInterpolator component type Rigidbody that interpolates the configurable position between the two configurable locations through the public method Interpolate with the float parameter. Use Vector3.LerpUnclamped so that the value provided is not limited and is left to the caller. We must change the position of the body through its MovePosition method in order to interpret it as motion, otherwise it will become invisible transmission.

Using UnityEngine

Public class PositionInterpolator: MonoBehaviour {

[SerializeField] Rigidbody body = default; [SerializeField] Vector3 from = default, to = default; public void Interpolate (float t) {body.MovePosition (Vector3.LerpUnclamped (from, to, t);}}

The position interpolator is connected to the slider.

By adding both sider and interpolator to the same platform object, I created a simple mobile platform. The dynamic version of the interpolator method Interpolate is bound to the event of the slider, which is why its value has no fields. I then connect the slider to the detection area to activate the platform when an object enters the area. Notice that the interpolation point is in world space.

Enable the mobile platform.

Automatic reversing

We can move the interpolation AutomaticSlider back and forth by adding a configurable automatic reverse switch to. This requires us to track whether it is reversed and double the code in FixedUpdate, which must support both directions. Similarly, when automatic reversal is activated, we must jump rather than clamp the value. In the case of a very short duration, this may lead to an overshoot, so we will still clamp after the rebound.

[SerializeField] bool autoReverse = false

...

Bool reversed

Void FixedUpdate () {float delta = Time.deltaTime / duration; if (reversed) {value-= delta; if (value = 1f) {if (autoReverse) {value = Mathf.Max (0f, 2f-value); reversed = true;} else {value = 1f; enabled = false;}} onValueChanged.Invoke (value) }

A platform with automatic reversal enabled.

At a steady pace

The motion of linear interpolation is rigid, and the speed will change suddenly when it is reversed. By passing a smooth variant of the value to the event, we can speed it up and slow it down. We give it by applying the smoothstep function, which is 3V 2-2V 3. Make it a configurable option.

Linear and smooth. [SerializeField] bool autoReverse = false, smoothstep = false

... Float SmoothedValue = > 3f * value * value-2f * value * value * value; void FixedUpdate () {… OnValueChanged.Invoke (smoothstep? SmoothedValue: value);}

The platform with the smoothing step enabled.

More control

You can pause the animation by detecting the area event to disable the slider component, but we can also control its direction. The easiest way is to provide its inverted state through public properties. Replace the reversed field with the automatic Reversed property and adjust the case of the other code to match.

/ / bool reversed; public bool Reversed {get; set;}

Let's do the same for the automatic inversion option. In this case, we must keep the serialized field, so add an explicit property.

Public bool AutoReverse {get = > autoReverse; set = > autoReverse = value;}

Complex platform control.

Note that the direction reversal is sudden because it is still a simple interpolation. If you want to stop and reverse smoothly at any time, you need to create more complex logic that uses acceleration and velocity.

Collision

The danger of moving the landscape is that the body may eventually get caught between two close colliders. When the gap between the colliders closes, the body is either ejected or eventually pushed into or through the collider. If the collision surface is at an angle, there is a clear escape path and the body will be pushed in that direction. If not, or if there is not enough time to escape, the body will eventually be crushed to penetrate the Collider. If an object is stuck between two simple colliders thick enough, it can stay inside them and pop up once there is a clear path. Or you'll fall.

Pushed to the ground.

If the collision surface is at an angle, the body will be pushed aside and is likely to escape. Therefore, it is a good idea to design such a configuration by leaving enough space between the surfaces or by introducing a tilted Collider (visible or not). In addition, hiding the Box Collider on the floor can make it stronger to prevent objects from being pushed over. Or, add an area that triggers damage to the area at the appropriate time, indicating that it has been crushed.

The platform has angled colliders and boxes hidden on the floor.

Local interpolation

Configuration in world space can be inconvenient because it cannot be used for the same animation in multiple locations. So let's wrap it by adding a local space option to PositionInterpolator. To do this, we add an optional and configurable Transform relative to where the interpolation occurs. Objects are usually referenced with an interpolator, but this is not required.

[SerializeField] Transform relativeTo = default

Public void Interpolate (float t) {Vector3 p; if (relativeTo) {p = Vector3.LerpUnclamped (relativeTo.TransformPoint (from), relativeTo.TransformPoint (to), t);} else {p = Vector3.LerpUnclamped (from, to, t);} body.MovePosition (p);}

These are all the contents of the article "how to achieve influential Ball Motion in Unity". Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.

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