如何使物体在指定位置寻找并减速停止?

Posted

技术标签:

【中文标题】如何使物体在指定位置寻找并减速停止?【英文标题】:How to make an object seek and decelerate to a stop exactly on a specified position? 【发布时间】:2012-06-08 18:30:18 【问题描述】:

简介

我目前正在尝试创建一个脚本(Unity 中的 C# 脚本)来控制 GameObject 的指向和单击行为。

本质上,玩家最终将通过简单地点击屏幕来控制一个热气球,由此对象将开始加速并朝着点击的位置移动。当在点击位置的特定距离处,热气球应该开始向相反方向加速(因此减速)并完全停止在点击位置。

我已经创建了一个功能齐全的注释脚本(在这篇文章的底部找到),它只是将一个游戏对象以恒定的速度移向点击的位置,然后在特定距离内开始减速直到停在该点。

我还创建了一个单独且功能齐全的脚本,该脚本在加速时向点击位置移动(获得那种小行星撞击行为)。这也可以在帖子底部找到。

问题

现在我面临的问题是,我已经工作了很长时间来找到一种解决方案,您可以将这两种行为作为一个工作脚本来实现,也就是说,热气球有加速行为,同时减速并停在目标位置,就像在此处找到的这个视觉示例:

Visual Arrival Behaviour Demonstration

问题

然后我的问题变成了:

你怎样才能使到达行为不仅使用恒定速度,而且还包含在方程中的加速度?我尝试研究这个问题并尝试使用自己的解决方案,但我所做的一切似乎都没有达到应有的效果。

脚本附件

指向和点击移动以恒定的速度与到达行为

using UnityEngine;
using System.Collections;

public class PlayerControl : MonoBehaviour 

    // Fields
    Transform cachedTransform;
    Vector3 currentMovementVector;
    Vector3 lastMovementVector;
    Vector3 currentPointToMoveTo;
    Vector3 velocity;
    int movementSpeed;

    Vector3 clickScreenPosition;
    Vector3 clickWorldPosition;

    float slowingDistance = 8.0f;

    Vector3 desiredVelocity;

    float maxSpeed = 1000.0f;

    // Use this for initialization
    void Start () 
    
        cachedTransform = transform;
        currentMovementVector = new Vector3(0, 0);
        movementSpeed = 50;
        currentPointToMoveTo = new Vector3(0, 0);
        velocity = new Vector3(0, 0, 0);
    

    // Update is called once per frame
    void Update () 
    
        // Retrive left click input
        if (Input.GetMouseButtonDown(0))
        
            // Retrive the click of the mouse in the game world
            clickScreenPosition = Input.mousePosition;

            clickWorldPosition = Camera.main.ScreenToWorldPoint(new      Vector3(clickScreenPosition.x, clickScreenPosition.y, 0));
            currentPointToMoveTo = clickWorldPosition;

            currentPointToMoveTo.z = 0;

            // Calculate the current vector between the player position and the click
            Vector3 currentPlayerPosition = cachedTransform.position;

            // Find the angle (in radians) between the two positions (player position and click position)
            float angle = Mathf.Atan2(clickWorldPosition.y - currentPlayerPosition.y, clickWorldPosition.x - currentPlayerPosition.x);

            // Find the distance between the two points
            float distance = Vector3.Distance(currentPlayerPosition, clickWorldPosition);

            // Calculate the components of the new movemevent vector
            float xComponent = Mathf.Cos(angle) * distance;
            float yComponent = Mathf.Sin(angle) * distance;

            // Create the new movement vector
            Vector3 newMovementVector = new Vector3(xComponent, yComponent, 0);
            newMovementVector.Normalize();

            currentMovementVector = newMovementVector;
        

        float distanceToEndPoint = Vector3.Distance(cachedTransform.position, currentPointToMoveTo);

        Vector3 desiredVelocity = currentPointToMoveTo - cachedTransform.position;

        desiredVelocity.Normalize();

        if (distanceToEndPoint < slowingDistance)
        
            desiredVelocity *= movementSpeed * distanceToEndPoint/slowingDistance;
        
        else
        
            desiredVelocity *= movementSpeed;
        

        Vector3 force = (desiredVelocity - currentMovementVector);
        currentMovementVector += force;

        cachedTransform.position += currentMovementVector * Time.deltaTime;
    

使用加速但没有到达行为的指向和点击移动

using UnityEngine;
using System.Collections;

public class SimpleAcceleration : MonoBehaviour 

    Vector3 velocity;
    Vector3 currentMovementVector;

    Vector3 clickScreenPosition;
    Vector3 clickWorldPosition;

    Vector3 currentPointToMoveTo;
    Transform cachedTransform;

    float maxSpeed;

    // Use this for initialization
    void Start () 
    
        velocity = Vector3.zero;
        currentMovementVector = Vector3.zero;
        cachedTransform = transform;
        maxSpeed = 100.0f;
    

    // Update is called once per frame
    void Update () 
    
        // Retrive left click input
        if (Input.GetMouseButtonDown(0))
        
            // Retrive the click of the mouse in the game world
            clickScreenPosition = Input.mousePosition;

            clickWorldPosition = Camera.main.ScreenToWorldPoint(new Vector3(clickScreenPosition.x, clickScreenPosition.y, 0));
            currentPointToMoveTo = clickWorldPosition;

            // Reset the z position of the clicking point to 0
            currentPointToMoveTo.z = 0;

            // Calculate the current vector between the player position and the click
            Vector3 currentPlayerPosition = cachedTransform.position;

            // Find the angle (in radians) between the two positions (player position and click position)
            float angle = Mathf.Atan2(clickWorldPosition.y - currentPlayerPosition.y, clickWorldPosition.x - currentPlayerPosition.x);

            // Find the distance between the two points
            float distance = Vector3.Distance(currentPlayerPosition, clickWorldPosition);

            // Calculate the components of the new movemevent vector
            float xComponent = Mathf.Cos(angle) * distance;
            float yComponent = Mathf.Sin(angle) * distance;

            // Create the new movement vector
            Vector3 newMovementVector = new Vector3(xComponent, yComponent, 0);
            newMovementVector.Normalize();

            currentMovementVector = newMovementVector;
        

        // Calculate velocity
        velocity += currentMovementVector * 2.0f * Time.deltaTime;

        // If the velocity is above the allowed limit, normalize it and keep it at a constant max speed when moving (instead of uniformly accelerating)
        if (velocity.magnitude >= (maxSpeed * Time.deltaTime))
        
            velocity.Normalize();
            velocity *= maxSpeed * Time.deltaTime;
        

        // Apply velocity to gameobject position
        cachedTransform.position += velocity;
    

【问题讨论】:

这不是人工智能,而是控制理论。我已经删除了标签。 【参考方案1】:

改编第一个脚本:

引入一个变量velocity,就像在第二个脚本中一样。将其设置为等于Start() 中的movementSpeed,之后不要使用movementSpeed在一切正常之前不要继续。

现在介绍加速:

if (distanceToEndPoint < slowingDistance)

   velocity *= distanceToEndPoint/slowingDistance;

else

   velocity += direction * 2.0f * Time.deltaTime;

【讨论】:

【参考方案2】:

根据您希望运动的显示方式,您可能需要constant velocity equations 或these equations。恒速会更容易。

例如:您可以将起点和终点之间的距离除以 2。然后使用数学计算中途加速,然后减速。

【讨论】:

啊,是的,我之前正在研究其中的一些方程 :) 但我认为让我感到困惑的部分是,当你开始添加来自小行星的运动时(其中,当你改变速度时,你不仅停止了当前的方向,而是在一个新的方向上平稳地加速)。我似乎无法将它实现到第一个基本上可以工作的脚本中,它只是不考虑恒定加速度(我会使用它),而只是没有加速度的恒定速度。也许我现在只是盯着自己瞎了眼

以上是关于如何使物体在指定位置寻找并减速停止?的主要内容,如果未能解决你的问题,请参考以下文章

unity 如何让物体匀加速和匀减速运动,求代码

如何停止移动的物体?

Unity 相机围绕物体指定观察角度和位置

使用旋转编码器控制直流减速电机

如何在与另一个物理体碰撞时阻止物体失去位置

为什么轴在执行主动回原点命令时,初始方向没有找到原点,当需要碰到限位开关掉头继续寻找原点开关时并没有掉头,而是直接报错停止轴,报错原因是由于轴碰到了限位开关?