Calculate force to reach point B from point A

Asked

Viewed 2,964 times

13

I have the value of the starting point, the end point (target) and the initial angle of the object.

I would like to know the strength to reach the end point, as in the image below.

Is there a mathematical formula or in Unity itself that I can use to calculate?

inserir a descrição da imagem aqui

  • 9

    Basically this is ballistics (physics of parables). Take a look here: http://fisicamoderna.blog.uol.com.br/arch2007-09-02_2007-09-08.html

  • If you know gravity, you can determine the speed necessary initial, yes, but to speak of "force" it is also necessary to consider the mass of the projectile and the time when the force will be applied to it. I think it’s likely there’s something impl

  • Already implemented in Unity itself in the case of speed, but in the case of force it should be necessary to establish additional parameters. I’m not sure yet. P.S. It was bad, I posted the comment unintentionally, and I can’t find option to edit (I’m on Android)

  • 1

    Unity already has the physics implemented. But the trajectory display, for example, would need to be created by you. This example of trajectory is interesting because in practice it uses the same calculation of physics that I mentioned earlier. You can use it to try to calculate the force: http://www.theappguruz.com/blog/display-projectile-trajectory-path-in-unity

  • good ballistics applied in example 1

  • @bigown This answer is worth about 1000 points, but my strangeness derives from the fact, that in simple questions, if the user does not put a code code he is targeted for not complying with certain policies, however, this is the kind of question "ask me"... In a flagged or something like that... Really the parameters for questions and answers, are not clear for a novice like me...

Show 1 more comment

1 answer

17


Physics Related

The force acting on the projectile at the moment it is launched (and, consequently, the speed at which it moves in the parabola) is a vector given by two components (in terms of a two-dimensional game, of course): a component on the x-axis (the force or velocity in the horizontal) and a component on the y-axis (the force or speed vertically):

inserir a descrição da imagem aqui

Assuming that there is no wind resistance and that the projectile has no propulsion (i.e., it is not a missile that accelerates or decelerates on its own), the horizontal velocity (resulting from an initially applied force) is constant. I mean, it doesn’t change until the projectile stops because it’s hit something. Thus, the horizontal displacement is governed by the Uniform Movement (MU), and given by:

inserir a descrição da imagem aqui

(equation 1: i.e., final space = initial space + speed x time)

Assuming the same for projectile in terms of vertical motion (no friction, propulsion, etc.), vertical velocity does not remain constant because on it acts at all times the acceleration of gravity (inserir a descrição da imagem aqui). Thus, vertical displacement is governed by the Uniformly Varied Movement (MUV), and given by:

inserir a descrição da imagem aqui

(equation 2: i.e., final space = initial space + velocity x time + acceleration x time squared)

In fact, as the movement includes an acceleration, the speed changes constantly. And therefore, it is given by:

inserir a descrição da imagem aqui

(equation 3: final speed = initial speed + acceleration x time)

Also, when a force is applied to a projectile, the speed at which it will move depends on the mass of the projectile. The heavier the projectile is, the greater the initial force applied to it in order for it to move at the same speed. This relation is given by the formula:

inserir a descrição da imagem aqui

(equation 4: i.e., force = mass x acceleration)

Therefore, the physics simulation can be performed using these formulas to calculate, at each instant of time, the position (both on the X-axis and on the Y-axis) at which the projectile must be.

What do you want

What you want to do, then, is to calculate the force vector necessary for the first impulse in the projectile, given the initial positions (from where the projectile comes out, ie, S0) and final (the target of the projectile, ie, S). That might be esteemed with the following pseudo-code:

velocidadeX = forca / massa
tempo = (alvo.x - x) / velocidadeX
velocidadeY = gravidade * (tempo / 2);

The first line uses equation 4 to calculate a speed on axis X from a force whichever (that you can kick or ask the player to report). As the horizontal movement is constant, no matter the chosen value! The balcony is precisely to calculate from it the speed on the Y axis correct for the target to be hit.

It should be easy to see that the lower the horizontal speed (that is, the slower the object moves "forward"), the longer it will take to reach the target. Thus, the projectile also needs to rise higher so that it does not fall in the middle of the way. Thus, the second line estimates the time it will take to traverse the horizontal axis, using equation 1.

Finally, the vertical velocity can be estimated with equation 3 in a simple way. In the parable movement, the projectile will first rise and then descend, all at the same time as the projectile takes to move horizontally. On the ascent, it decelerates, so that when it reaches the highest point its speed is 0. Therefore, in the second half of the movement, its initial speed is 0 (i.e., inserir a descrição da imagem aqui) and it starts to accelerate in the fall. Therefore, in the formula, its initial speed is 0 and the time traveled is half of the previously calculated total time. The acceleration is that of gravity (the only one acting on the projectile), and it is without the negative sign because in the algebraic manipulation of equation 3 it "passed to the other side of equality".

Well, these are just the X and Y components of the initial velocity vector that the projectile needs to have. You can calculate the vector angle in relation to the X axis, if desired, using the code:

angulo = Arco-Tangente(velocidadeY, velocidadeX)

Note as I said earlier (and emphasized in bold) that this code esteem the initial velocity vector for the projectile. Why does it estimate? Well, consider a scenario where the speed X is low (close to 0). In this case, the projectile will take muuiiitoo long to travel across the horizontal space, and so it needs to be thrown almost upwards (almost totally vertically), so that it will fall on the target also almost totally vertically. In this scenario, the calculation is right.

The problem is in a scenario where the speed X is high. In this case, the projectile will travel the horizontal space very quickly, so it needs to be launched almost parallel to the ground to hit the target. This simple calculation then fails to disregard the angle of inclination at which the projectile reaches the target (this time it will no longer be almost entirely vertical).

One can think of a reasonably effective solution that "adjusts" time and speed Y by reconsidering the target from the difference in horizontal space (X) given by the previously calculated impact angle. For example, you can simply run the following lines again:

tempo = (alvo.x - Seno((90 - angulo)) - x) / velocidadeX
velocidadeY = gravidade * (tempo / 2);

In line 2, an excerpt is observed - Seno((90 - angulo)). This part simply subtracts from the position of the target the displacement given by the angle (the opposite cathode, that is, the sine) of the input difference (90 would be if it was totally vertical, then subtracts the entrance angle to have the difference angle).

Observation 1: With this solution, this process would need to be ideally repeated more iteratively, until the error was fully minimized (converged). But in the code I produced I only did once because it was enough for illustration (almost always hits the target when caught in it). But you still go observe, by the dashed, that there is some small variation from where the Real target is on X axis.

Observation 2: I’m not a physicist, so I admit I don’t know if there’s a better way to do this calculation without having to adjust the angle iteratively. If anyone has a better solution, you are welcome to share (can even change in the same code smoothly!). :)

Example in Unity3d

The source code I share below was built based on some sources (mentioned at the end of this post). The motion of the projectile is in charge of the physics of Unity3d. I only added one Rigidybody2D as you would normally. Dashed calculus uses the same formulas mentioned, but in its variation with trigonometric functions (see references also for details). There are only colliders in the projectiles (cannonballs) and on the target. The explosion when "hitting" the ground uses a simple height check (for mere convenience, and to prevent more easily that bullets thrown too high can eventually "miss" a Colisor and produce leaks and memory).

inserir a descrição da imagem aqui

The code can be executed online in Webgl (have patience; it takes a little to load) or downloaded in a zip archive from here.

Code

Class of cannon control:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.EventSystems;

// Classe para controle do canhao
public class CannonControl : MonoBehaviour
{
    // Prefab do ponto de trajetoria (a ser definido via editor)
    [SerializeField]
    private GameObject m_dotPrefab;

    // Prefab do projetil (a ser definido via editor)
    [SerializeField]
    private GameObject m_bulletPrefab;

    // Objeto do alvo (a ser definido via editor)
    [SerializeField]
    private GameObject m_targetObject;

    // Indicativo se a trajetoria deve ser sempre exibida (se true) ou somente
    // quando o botao do mouse estiver pressionado (se false)
    private bool m_alwaysShowTrajectory = false;

    // Propriedade para permitir alteraçao via checkbox na interface grafica
    public bool alwaysShowTrajectory
    {
        get
        {
            return m_alwaysShowTrajectory;
        }

        set
        {
            m_alwaysShowTrajectory = value;
            if(!m_alwaysShowTrajectory)
                hideTrajectoryPoints();
        }
    }

    // Indicativo se a mira deve ser fixada no alvo
    private bool m_fixedInTarget = false;

    // Propriedade para permitir alteraçao via checkbox na interface grafica
    public bool fixedInTarget
    {
        get
        {
            return m_fixedInTarget;
        }

        set
        {
            m_fixedInTarget = value;
        }
    }

    // Indicaçao de que o mouse esta pressionado
    private bool m_mousePressed = false;

    // Transform com a posiçao da boca do canhao
    private Transform m_cannonMouth;

    // Lista com os pontos de trajetoria utilizados
    private List<GameObject> m_trajectoryPoints;

    // Angulo atual do canhao
    private float m_angle;

    // Transform com o limite vertical para os tracos da trajetoria
    // (obtained by its name)
    private Transform m_verticalLimit;

    // Vetor de força com a qual o tiro sera executado.
    private Vector2 m_force;

    // Inicilizaçao do script
    void Start()
    {
        GameObject obj = GameObject.Find("limit");
        if(!obj)
            throw new UnityException("A instancia do objeto empty chamado 'limit' nao foi encontrada!");
        m_verticalLimit = obj.transform;

        // Procura pelo objeto filho que marca a boca do canhao
        m_cannonMouth = transform.Find("mouth");
        if(!m_cannonMouth)
            throw new UnityException("A instancia do objeto empty chamado 'mouth' nao foi encontrada!");

        // Cria inicialmente 30 pontos de trajetoria (a lista e ajustada dinamicamente
        // conforme a necessidade)
        m_trajectoryPoints = new List<GameObject>();
        for(int i = 0; i < 30; i++)
        {
            GameObject dot = (GameObject) Instantiate(m_dotPrefab);
            dot.GetComponent<Renderer>().enabled = false;
            m_trajectoryPoints.Add(dot);
        }
    }

    // Atualizaçao quadro-a-quadro
    void Update()
    {
        // A força de lançamento do projetil e igual ao vetor de distancia do
        // canhao ao ponteiro do mouse.
        Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        Vector2 dirMouse = mousePos - transform.position;
        m_force = dirMouse;

        // Desenha uma linha amarela no scene viewer do editor para indicar
        // visualmente o vetor de força
        Debug.DrawLine(transform.position, mousePos, Color.yellow);

        // Se a mira nao esta fixada no alvo, o jogador que controla o tiro
        // (definindo a direçao e a força do tiro com o mouse)
        if(!m_fixedInTarget)
        {
            // Posiciona o canhao apontando para o mouse
            m_angle = (Mathf.Atan2(m_force.y, m_force.x) * Mathf.Rad2Deg) - 15;
        }
        // Caso contrario, o jogador so define a força no eixo X com base no mouse
        // (o resto e calculado de forma a garantir que acerte o alvo)
        else 
        {
            // Equipara a "altura" do alvo a do canhao
            Vector2 target = m_targetObject.transform.position;
            //target.y = transform.position.y;

            // Calcula os componentes de velocidade x e y
            float mass = m_bulletPrefab.GetComponent<Rigidbody2D>().mass;
            float gravity = Physics.gravity.magnitude;

            float velX = m_force.magnitude / mass;
            float time = (target.x - transform.position.x) / velX;
            float velY = gravity * (time / 2);

            // Calcula o angulo de lançamento
            m_angle = (Mathf.Atan2(velY, velX) * Mathf.Rad2Deg) - 15;

            // Ajusta conforme o angulo em que o tiro "deve" atingir o alvo
            time = (target.x - Mathf.Sin((90 - m_angle) * Mathf.Deg2Rad) - transform.position.x) / velX;
            velY = gravity * (time / 2);

            // Calcula a velocidade e forma finais
            Vector2 velocity = new Vector2(velX, velY);
            m_force = velocity * mass;
        }

        // Rotaciona o canhao para o angulo calculado
        transform.rotation = Quaternion.Euler(0f, 0f, m_angle);

        if(m_alwaysShowTrajectory || m_mousePressed)
            showTrajectoryPoints();

        // Captura o pressionamento/liberaçao do botao do mouse
        // (somente se nao estiver sobre o checkbox da UI)
        if(!EventSystem.current.IsPointerOverGameObject())
        {
            if(Input.GetMouseButtonDown(0))
                m_mousePressed = true;

            if(Input.GetMouseButtonUp(0))
            {
                m_mousePressed = false;
                hideTrajectoryPoints();
                shootBullet();
            }
        }
    }

    // Simula o tiro do canhao
    void shootBullet()
    {
        // Cria o projetil e o posiciona na boca do canhao
        GameObject bullet = (GameObject) Instantiate(m_bulletPrefab);
        bullet.transform.position = m_cannonMouth.position;

        // Aplica a força a bala
        Rigidbody2D body = bullet.GetComponent<Rigidbody2D>();
        body.AddForce(m_force, ForceMode2D.Impulse);

        // Toca o som do tiro
        AudioSource sound = GetComponent<AudioSource>();
        sound.Play();
    }

    // Exibe os pontos de trajetoria para a força atual.
    // O angulo do tiro esta embutido no vetor de força.
    void showTrajectoryPoints()
    {
        float mass = m_bulletPrefab.GetComponent<Rigidbody2D>().mass;
        Vector2 velocity = m_force / mass;
        float angle = Mathf.Rad2Deg * (Mathf.Atan2(velocity.y , velocity.x));

        // Esse valor e uma estimativa de quanto de tempo passa a cada "quadro"
        // Na pratica, ele serve pra ajustar a distancia entre os pontos no traçado
        // Quanto mais proximo de zero (so nao pode ser zero mesmo!), mais parecido
        // com uma linha o traçado se torna
        float step = 0.03f;

        float time = 0;
        Vector2 currentPos = m_cannonMouth.position;
        for(int i = 0; i < m_trajectoryPoints.Count; i++)
        {
            GameObject dot = m_trajectoryPoints[i];
            dot.transform.position = currentPos;
            if(currentPos.y >= m_verticalLimit.position.y)
                dot.GetComponent<Renderer>().enabled = true;
            else
                dot.GetComponent<Renderer>().enabled = false;

            float dx = velocity.magnitude * time * Mathf.Cos(angle * Mathf.Deg2Rad);
            float dy = velocity.magnitude * time * Mathf.Sin(angle * Mathf.Deg2Rad) - (Physics2D.gravity.magnitude * time * time / 2.0f);
            currentPos = new Vector3(m_cannonMouth.position.x + dx , m_cannonMouth.position.y + dy ,2);
            time += step;

            // Ajuste dinamico dos pontos necessarios para traçar a trajetoria.
            // Basicamente: se faltam pontos para alcançar o limite vertical,
            // adiciona mais 10 deles na lista.
            if(i == m_trajectoryPoints.Count - 1 && currentPos.y > m_verticalLimit.position.y)
            {
                for(int j = 0; j < 10; j++)
                {
                    dot = (GameObject) Instantiate(m_dotPrefab);
                    dot.GetComponent<Renderer>().enabled = true;
                    m_trajectoryPoints.Add(dot);
                }
            }
        }
    }

    // Esconde os pontos de trajetoria
    void hideTrajectoryPoints()
    {
        foreach(GameObject dot in m_trajectoryPoints)
            dot.GetComponent<Renderer>().enabled = false;
    }
}

Class of control of projectiles:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

// Classe para controle do projetil (bala de canhao)
// A fisica do projetil fica a cargo da Unity, pelo uso
// do componente Rigidbody 2D.
public class BulletControl : MonoBehaviour
{
    // Transform com o limite vertical para os projeteis
    // (obtained by its name)
    private Transform m_verticalLimit;

    // Inicializaçao do script
    void Start()
    {
        GameObject obj = GameObject.Find("limit");
        if(!obj)
            throw new UnityException("A instancia do objeto empty chamado 'limit' nao foi encontrada!");
        m_verticalLimit = obj.transform;
    }

    // Atualizaçao quadro-a-quadro
    void Update()
    {
        // Checa se o projetil atingiu o "chao" (dado pelo posiçao vertical do
        // objeto vazio chamado 'limit'). Se atingiu, destroi o projetil.
        if(transform.position.y <= m_verticalLimit.position.y)
        {
            explode();
        }
    }

    // Captura a colisao com o alvo (via um trigger)
    void OnTriggerEnter2D(Collider2D other)
    {
        Text score = GameObject.Find ("/Canvas/score").GetComponent<Text>();
        score.text = (int.Parse(score.text) + 1).ToString();

        explode();
    }

    // Captura a colisao com outra bala (via um collider)
    void OnCollisionEnter2D(Collision2D other)
    {
        explode();
    }

    // Anima a explosao
    void explode()
    {
        // Toca a animaçao da exploçao (particulas de fogo)
        ParticleSystem explosion = GetComponent<ParticleSystem>();
        explosion.Play();

        // Toca o som da explosao
        AudioSource sound = GetComponent<AudioSource>();
        sound.Play();

        // Esconde a bala
        GetComponent<Renderer>().enabled = false;

        // Destroy o objeto apos o fim da animaçao
        Destroy(gameObject, explosion.duration);

        // Destroy (e para) esse script imediatamente
        // (para que o som e as particulas nao encavalem)
        Destroy(this);
    }

}

Websites of Reference

Credits

  • 3

    Congratulations @Luiz Vieira from the taste of learning...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.