Related PhysicsThe force that acts on the projectile at the time it is launched (and consequently the speed with which it moves in the parable) is a vector given by two components (in terms of a two-dimensional game, of course): a component in the X axis (the strength or speed in the horizontal) and a component in the Y axis (the force or velocity in the vertical): Assuming that there is no resistance of the wind and that the projectile has no propulsion (i.e., it is not a missile that accelerates or slows on its own), the horizontal speed (resul an initially applied force) is constant. That is, she doesn't change until the projectile stops because she was shocked by something. Thus, the horizontal displacement is governed by the Uniform Movement (MU), and given by: (equation 1: that is, final space = initial space + speed x time)Assuming the same for the projectile in terms of vertical drive (no friction, propulsion, etc), vertical speed does not remain constant because it acts all the time the acceleration of gravity ( ). Thus, the vertical displacement is governed by the Uniformly Varied Movement (MUV), and given by: (equation 2: that is, final space = initial space + speed x time + acceleration x time to square)In fact, as the movement includes an acceleration, the speed changes constantly. And so it is given by: (equation 3: final speed = initial speed + acceleration x time)Moreover, when a force is applied on a projectile, the speed with which it will move depends on the mass of the projectile. The heavier the projectile is, the greater it needs to be the initial force in it applied so that it moves at the same speed. This relationship is given by the formula: (equation 4: that is, force = mass x acceleration)Therefore, the physics simulation can be performed using these formulas to calculate, at every instant of time, the position (both in the X axis and in the Y axis) in which the projectile should be.What you wantWhat 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 leaves, that is, ) and final (the target of the projectile, that is, ). That could be estimated 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 the X axis from a force any (which vc can kick or ask pro player to inform). 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 so that the target is hit.It should be easy to realize that the smaller the horizontal speed (i.e., the slower the object moves forward), the longer it will take to reach the target. Thus, the projectile also needs to climb more so that it does not fall in the middle of the way. Thus, the second line estimates the time it will take to go through the horizontal axis, using the equation 1.Finally, vertical speed can be estimated with equation 3 in a simple way. In the parable movement, the projectile will first go up to then descend, all this in the same time that the projectile takes to move horizontally. At the ascent, it slows down, so when reaching the highest point its speed is 0. Therefore, in the second half of the movement, its initial speed is 0 (i.e. ) and he begins to accelerate in the fall. Therefore, in the formula, its initial speed is 0 and the time spent is half the total time previously calculated. The acceleration is that of gravity (the only one acting on the projectile), and is without the negative sign because in algebraic manipulation of equation 3 she "went to another side of equality".Well, these are precisely the X and Y components of the initial speed vector that the projectile needs to have. You can calculate http://alunosonline.uol.com.br/matematica/angulo-entre-dois-vetores.html in relation to the X axis, if desired, using the code:angulo = Arco-Tangente(velocidadeY, velocidadeX)
Note how I said earlier (and emphasized in bold) that this code estimate the initial speed vector for the projectile. Why does he estimate? Well, consider a scenario where speed X is beeemmm low (next to 0). In this case, the projectile will take a lot to go through the horizontal space, and so it needs to be thrown almost upward (almost fully vertical), so it will fall on the target also almost entirely vertically. In this scenario, the calculation hits very well.The problem is in a scenario where speed X is beeemm high. In this case, the projectile will travel the horizontal space very quickly, so it needs to be launched almost parallel to the soil to hit the target. This simple calculation then fails by disregarding the angle of inclination with which the projectile reaches the target (this time it will no longer be almost entirely vertical).It can be thought 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 deducts from the position of the target the displacement given by the angle (the opposite cactus, that is, the sine) of input difference (90 would be if it fell totally vertically, then decontaminates the angle of entry to have the angle of the difference).Note 1: With this solution, this process would need to be
ideally repeated more times iteratively, until the error was fully minimized (converging). But in the code I
I only did once because it was enough for illustration
(it almost always reaches the target when locked in it). But you're still going
observe, by the dashed, that there is some small variation of where
real target is on the X axis.Observation 2: I'm not physical, 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 someone has a better solution, welcome to
share (you can even change in the same code without problems!).
Example in Unity3DThe source code I share below was built based on some sources (mentioned at the end of this post). Projectile drive is in charge of Unity3D physics. I just added one Rigidybody2D as you would normally. The calculation of the stroke uses the same formulas mentioned, but in its variation with trigonometric functions (see references also for details). There are only bumpers in the shells (cannonballs) and the target. The explosion at the "bater" against the ground uses a mere height check (by mere convenience, and to avoid more easily that very high bullets can eventually "rise" a collisor and produce leaks and memory). The code can be executed http://www.luiz.vieira.nom.br/projects/balistica/ (have patience; it takes a little bit to load) or downloaded in a http://www.luiz.vieira.nom.br/projects/balistica/balistica.zip .CodeCannon control class: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;
}
}
Projectile control class: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);
}
}
Reference SitesView of the trajectory: http://www.theappguruz.com/blog/display-projectile-trajectory-path-in-unity Ballistic related calculations: http://fisicamoderna.blog.uol.com.br/arch2007-09-02_2007-09-08.html Calculation of range of a projectile: http://www.dummies.com/how-to/content/calculate-the-range-of-a-projectile-fired-at-an-an.html CreditsBackground image: http://rcupcake.deviantart.com/art/MLP-Background-RD-s-House-Scene-V1-1-292372515 Picture of cannon and bullets: http://www.dreamstime.com/royalty-free-stock-photo-medieval-cannon-image37858865 (extracted from thumbnail without watermark http://thumbs.dreamstime.com/t/medieval-cannon-37858865.jpg , and used only for non-commercial end of illustration of this answer)Target image: http://www.clipartpanda.com/clipart_images/summer-reading-challenge-60288244 Shot Sound: https://www.freesound.org/people/LeMudCrab/sounds/163458/ Explosion sound: https://www.freesound.org/people/Superex1110/sounds/77535/ Image for fire texture in the particles: flame image of the Unity3D standard package.