I've not actaully tried this but if I were to this is what I would try 1st.
import an arrow model to the game scene. make sure it has the right collider on. Then add two empty game objects to your scene. Move them into position over your arrow, one at the front one at the back then parent them to the arrow. Add a rigidbody component to each of the empty game objects. To the front one turn up its mass to suit and make sure uses gravity is checked. For the back one you will have to estimate how much mass it needs to have relative to the front one, not enough mass and the arrow will face straight down too early, too much mass and it wont tilt enough. i would then add a script to the arrow getting the rigidbody components of the parented empty game objects then applying a consistent gravity multiplied by its mass or turn the masses of the rigidbody to the same (both 10 or something) then apply different strengths of gravity to each one individually.
or on failing that I would add a rigidbody component to the arrow itself and add torque to it depending on how far it was going, so if it was going 360 feet in 1 second (impossible I know but just for simple maths sakes) it would turn (if it was being fired straight/ 90 degrees to the floor) between 10 and 20 degrees/second
You could either script the parabolic trajectory, which would be giving by a parabolic equation and constantly set the transform to this, or give the arrow more mass and set it's CenterOfMass to something like Vector3(0,1,0), and add in some drag.
I use this to force projectiles to aim correctly, I'm pretty sure I'm not the first. The two lines in Update do the aiming (it's partial C# code):
bool dead; // uses so we can hang around for a while, after hitting
public Transform hitFlash; // the PE when we hit something
void Start () {
dead=false;
}
void Update () {
if(!dead) {
Vector3 moveDir = transform.rigidbody.velocity;
transform.LookAt(transform.position + moveDir);
}
}
void OnCollisionEnter(Collision cc) {
if(dead) return; // just in case we try to double-die
dead=true;
if(hitFlash!=null)
Instantiate(hitFlash, transform.position, transform.rotation);
StartCoroutine(killMe());
}
IEnumerator killMe() {
// stink in for a while, then drop through the ground:
transform.collider.isTrigger = true; // no more collisions
transform.collider.isKinematic = true; // freeze in place
transform.rigidbody.freezeRotation = true; // not sure I need this
transform.rigidbody.velocity = Vector3.zero;
yield return new WaitForSeconds(3);
transform.collider.isKinematic = false; // drop through ground
yield return new WaitForSeconds(3);
Destroy(gameObject);
}
"heavy mass on the tip of the arrow so that when it falls it faces the tip of the arrow"
But all masses fall at the same speed (not accounting for air resistance, which is key). What makes an arrow fly straight (and fall tip first) is the fletching at the back. You can give the fletching some air resistance by turning up Drag.
I know this is old but I was looking for an answer for a long time so here’s what I’ve finally used that worked pretty well: GameObject arrow = Instantiate(crossbowArrow, crossBowShotFrom.position, crossBowShotFrom.rotation); arrow.GetComponent<Rigidbody>().AddForce(arrow.transform.forward * crossbowArrowSpeed, ForceMode.VelocityChange); arrow.GetComponent<Rigidbody>().AddTorque( arrow.transform.forward * 10 );