Unity 2d Bow And Arrow Tutorial
_{ Estimated Read Time: ~20 minutes }
Quick Navigation
 Introduction
 Scene Setup
 Setting Up Target Locations
 Motion Physics Review
 Launching the Projectile
 Fixing the Projectile Orientation
 References & Additional Material
1 – Introduction
In this tutorial, we’ll take a closer look at the motion physics of arrows and missiles. The tutorial will walk you through creating a demo scene for launching a projectile (capsule) to a target location (mimbar). If you want to play around with it yourself, the project files can be found on GitHub. You can also see it in action as a WebGL build is also available.
We’ll be using kinematics to achieve our goal, meaning that we’ll only be using
velocity
in our calculations, disregarding the mass and other acting physical forces on the object that would affect the motion such as drag. To launch the projectile to its target, we will be considering the following variables:
Variable Name  Type  Symbol 

Launch Velocity 
Vector3

V_{0} 
Launch Angle in Degrees 
float

α 
Distance to Target 
float

R 
By keeping the other two variables known beforehand, we can calculate any of the variables above. Specifically, we can calculate

the
velocity
, if we know where the target is and what the launch angle will be 
the
launch angle
, if we know how fast we’ll be launching the projectile and where the mangsa is 
the
distance
the projectile will travel, if we know the launch velocity and the launch angle
We’ll berangkat the tutorial with setting up the scene. I’ve used Unity v2017.3.0f3 at the time of preparing this.
2 – Scene Setup
We start by creating a small scene with a few objects: a ground plane, a projectile capsule and a target object which is a tribune with a red mark.
After launching Unity3D and creating a new project:
 Set camera position to
(0, 5, 15)
and rotation to
(30, 0, 0)
. Also, set Clear Flags from
Skybox
to
Solid Color
for a less distracting background in play mode.
 Also, set Clear Flags from
 To add a ground, add a
Plane
, reset its transform and adjust its scale to
(2, 2, 2)
. Add a material and attach it to the plane to color it dark gray (or as you will).
 For the projectile, add a
Capsule
. Place it on position
(0, 0.25, 0)
, rotate it
(90, 0, 0)
and scale it to
(0.5, 0.5 ,0.5)
. Add a
Rigidbody
component to the capsule. We’ll need this for setting the velocity of the object.  Create a C# script:
Projectile.cs
and attach to the capsule object.
 Add a
 For the target, let’s create a platform with a red mark on it.
 Create an Empty Game Object at the root of the scene hierarchy, name it “TargetObject” and place it at position
(5, 0, 5)
.  Create a cube object, name it to “Platform”. Set its scale to
(0, 0.2, 0)
and position to
(0, 0.2, 0)
to elevate it from the ground a little bit. Add a material and attach it to the cube object to color the target platform light gray (or as you will).
 For a red bahan mark that would show the current target location, create an empty GameObject and name it to “Mark”.

Set its position to
(0, 0.31, 0)
^{*}, rotation
(90, 0, 0)
and scale to
(2, 2, 0)
.  Add a Sprite Renderer and set Sprite to
InputFieldBackground
and Color to red.

Set its position to
 Create an Empty Game Object at the root of the scene hierarchy, name it “TargetObject” and place it at position
We want to implement the following behavior for the projectile in the
Projectile.cs
file:
 We want to cycle through setting a new korban location and launching the projectile to the target when
Spacebar
is pressed. Since our projectile will be in one of the two states of setting a new mangsa and launching, we’ll use the
bool bTargetReady
variable to keep track of the state. The projectile will also reset its position to initial position after settings up the new alamat.  For a random point around the projectile as the target location, we’ll pick a point in a circle surrounding the world origin denoted by the radius
float TargetRadius
. We can then add some random height to this random target to end up with a 3D location around the projectile. We’ll look into this in the next section.  To calculate the launch speed of the projectile, we’ll use two variables:
float LaunchAngle
and
Transform TargetObject
.  The
R
key will be used to reset the projectile’s position and rotation to their initial values. For this, we’ll cache the initial position and orientation in
Vector3 initialPosition
and
Quaternion initialRotation
at the beginning. When resetting the object, velocity will also be set to 0.
Let’s start with the
Update()
function:
Remember we have to assign the target object and set the initial values for the launch parameters from the unity pengedit. Drag and drop the target object onto the Projectile.cs script’s
TargetObject
field after selecting the Capsule object. 8.5 units for
TargetRadius
and 70 degrees for
LaunchAngle
are good initial values for the parameters. If you forget to set the
TargetObject
, you’ll get
UnassignedReferenceException: TargetObject has titinada been assigned
errors.
3 – Setting Up Korban Locations
We want to randomly pick a point for the target location. Let’s start with 2D and use a circle, which is centered in the world origin
(0, 0, 0)
.
Start with the Right vector
(1, 0, 0)
in the XZPlane, which also happens to be our ground plane in the scene. Rotate the Right vector a random amount of degrees along Yaxis. Finally, scale the rotated vector by the
TargetRadius
indikator to make the circle larger or smaller. Feel free to play with the this parameter to see what it does.
Note that since Unity uses a
lefthanded coordinate system
(
Xright
,
Yup
and
Zforward
) for the world space, a positive rotation (along YAxis) is a clockwise rotation of the Right vector
(1, 0, 0)
.
The C# function to implement the algorithm would be as follows:
The ground plane is scaled up so that it’s easier to see in the screenshot above that the random position of the bahan podium indeed lies on a circle around the projectile.
Let’s add some randomness to the height of the projectile. We can use two variables to control the randomness:
 A
bool
to toggle randomization of the height offset  A
float
to denote the range of the height offset we’re going to add to the newly acquired target position
Let’s turn on the randomness toggle and use
8.5f
as the offset amount for the korban object height. We will get a random offset value somewhere between 20% and 100% of the specified offset value. We will also make it randomly above or below the ground by multiplying the height with either
1.0f
or
1.0f
.
Now, let’s use the randomness control parameters to add a positive or a negative height offset to the target location.
The plane is still scaled up a little bit from its original scale value of
(0.25, 0.25, 0.25)
in this screenshot. Note that the alamat platform briefly gets the shadow from the ground plane when it is below the launch platform.
4 – Motion Physics Review
Before we get into the code for the
Launch()
function, let’s review some physics to make sure we fully understand how the function works. We’ll begin with a kinematic equation of the motion that describes the position based on the intial velocity and acceleration:
Now let’s look at projectile motion:
A breakdown of the variables are as follows:
α 
Launch angle 
V_{0} 
Initial velocity 
V_{0x } , V_{0y } 
X and Y components of the intial velocity 
H 
The positional difference in Yaxis (final – initial) 
R 
The positional difference in Xaxis (final – initial) 
horizon 
Let’s also consider the time t during which the projectile motion occurs. 
Equations (1), (2) and (3) use trigonometric definitions on the initial velocity.
We look at the motion in two components: x and y axes. We plug in the mengufuk and vertical velocity components to the kinematic equation. The x and y component yield us a relation between
 the distance traveled along the x axis
R, the x component of the initial velocity
V_{0x }
and the time of the trajectory motion
kaki langit.  the distance traveled along the y axis
H, the y component of the initial velocity
V_{0y }
, gravity
G
and the time of the trajectory motion
t.
Then we use plug
t
in the relation that we obtained for the y component of the position earlier. This will yield us a relation between
 the distance traveled along the y axis
H, x axis
R, gravity
G, launch angle
α
and the x component of the initial velocity
V_{0x }
.
Using trigonometric functions in (1), (2), and (3); together with what we’ve obtained with kinematic equations in (4) and (5), we end up with the final equations (6)
V_{0x
}
= √
GR
^{2}
/ 2(H
–
R
tan(α))
V_{0y
}
=
V_{0x
}
tan(α)
5 – Launching the Projectile
We have solved the kinematics keburukan in the previous section and if you recall, we have used X and Y directions in the trajectory figures to denote forward and up. Let’s reimagine the axes on the trajectory figure.
If we assume that
 where we are looking is the
forward
(0, 0, 1)
direction for us  the opposite of gravity vector is
up
(0, 1, 0)
 if we strecth out our right arm it would point in the
right
(1, 0, 0)
direction
we would define the local space vectors for us. Now, we can use the local space vectors with the kinematic equation we’ve acquired earlier.
If we can somehow align the forward vector of the projectile point towards the objek location, we can apply the formula directly to solve the launching komplikasi! Unity provides a
Transform.LookAt()
function to achieve exactly this.
Notice that this seems to work for the targets on the same plane, but
there’s an error in the logic.
We don’t
actually
want the projectile to look directly at the korban (local forward vector points to the mangsa). We want the projectile to turn towards the korban object while keeping its local up vector still pointing in the global up vector.
Let’s consider the case where the incaran object has different height than the projectile. While
we want the behavior on the left
in the gif below,
we would get the behavior on the right
if we use the
LookAt()
function shown above.
We can achieve the intended ‘turn around’ behavior if we discard the Y component of the target and projectile positions like this
Now that the vectors are aligned, we can now calculate the initial velocity for launch since we know all the variables for the solution:
V_{0x
}
= √
GR
^{2}
/ 2(H
–
R
tan(α))
,V_{0y
}
=
V_{0x
}
tan(α)
G 
Physics.gravity.y;

tan(α) 
Mathf.Tan(LaunchAngle * Mathf.Deg2Rad); // in radians

R 
Vector3.Distance(projectileXZPos, targetXZPos);

H 
TargetObjectTF.position.y  transform.position.y;

We can easily translate the localspace velocity vector into globalspace using the
Transform.TransformDirection()
function, which the physics system expects its vectors to be in. Once we transform the vector, we just set the velocity of the rigidbody of the projectile and flip the launch state.
Here’s the
Launch()
function in one piece:
Aaand here we GO!
Wait… That’s not right… But at least we jump in the right direction and land on the target location so that’s a clear win for this section!
6 – Fixing the Projectile Orientation
Obviously, if we’re firing arrows or missiles, we would like the projectile to have a proper orientation during the motion, shown below.
Transform.LookAt()
changes the orientation of the missile to align its local forward vector to point in the target’s direction. Once we set the velocity, we are titinada updating the orientation of the projectile. We can solve this issue by using the collider component to detect if the missile is touching the ground and update its rotation based on its velocity vector.
We’ll add the following to
Projectile.cs
script:
 Add a new boolean
bTouchingGround
for keeping track of whether the object is touching the ground or titinada  Define
OnCollisionEnter()
and
OnCollisionExit()
functions to update
bTouchingGround
.  Update the orientation of the object if it is titinada touching the ground
Quaternion.LookRotation()
us the functionality we need: it returns an orientation quaternion that would rotate an object so that its local forward vector would point in the given direction vector.
Aaaand…
We’re getting closer, but we’re not quite there yet.
Remember we had to apply the initial rotation of 90 degrees on the Xaxis at the beginning? We did that because the cylinder object Unity3D creates is in “standing up” position when its rotation has the default value. In other words, its forward vector is pointing out of the cylinder’s body’s surface in its default orientation. We would like our forward vector to point out of the tip of the cylinder in the default orientation, as shown with mendunia space vectors below:
The neat thing about quaternions is that you can combine them by using the multiplication operator
Quaternion.operator*()
. We can combine the rotation we get from the
Quaternion.LookRotation()
function with the
initialRotation
quaternion to achieve the right trajectory orientation for our capsule object. We combine rotations from right to left, as follows:
Also note that combining rotations is titinada a commutative operation, so the order of multiplication matters (A*B != B*A). You can find
Trajectory.cs
in one piece here
A quick Quaternion reminder here:
The way we achieve ‘combining rotations’ and the details of its math is explained in this Math Stack Exchange Answer and Wikipedia.
This means that
Quaternion.LookRotation(rigid.velocity) * initialRotation
will first apply
initialRotation
to the object to achieve the “default orientation we want”, and then apply the
LookRotation(rigid.velocity)
to rotate the projectile along its trajectory path, projectile’s sharp edge (capsule top) pointing in the velocity direction as shown in the gif right below.I would highly recommend watching Fantastic Quaternions from Numberphile and Quaternion Rotation from Sutrabla on youtube to make sense of quaternions if you feel like you don’falak have a firm grasp on them. Then, read further on the Quaternion interface Unity3D provides and the Quaternion script reference fully unlock the potential of quaternions in Unity3D. If you cannot contain your fascination with quaternions like myself, you can top it off with the amazing Visualizing quaternions (4d numbers) with stereographic projection from Grant of 3Blue1Brown.
Aaand Boom!
You can also see it in action on WebGL here or clone the unity project from GitHub.
Thank you for reading this tutorial! If you have anything to add, questions to ask or feedback to give, please leave them as comments below.
7 – References & Additional Materials
 YouTube: ilectureonline Physics – Mechanics: Projectile Motion
 Crash Course Physics: Motion in a Straight Line
 GameDev StackExchange: How do I set angular velocity/torque so that it’s pointing to velocity/direction?
 Wikipedia: Projectile motion
 Wikipedia: Trajectory
 Wikipedia: Kinematics
 Wikipedia: Trigonometry
 Wikipedia: Quaternion
 Wikipedia: Quaternions and spatial rotation
 Unity3D Script Reference: Quaternion
 Unity3D Manual: Quaternion And Euler Rotations In Unity
 YouTube: Fantastic Quaternions by Numberphile
 YouTube: Quaternion Rotation from Sutrabla
 YouTube: Visualizing quaternions (4d numbers) with stereographic projection
Bonus
Credits and thanks to u/zakerytclarke, who created an illustration of how launch angles affect the trajectory path and posted it on /r/dataisbeautiful.
You can watch the animated version of this here.
 * : We gave it a little offset (
0.31
) in the Y direction. The
0.31
value was not random: The mimbar has an elevation (0.2
) in Ydirection as well, and it is scaled by
0.2
. We consider the half of the scale and add a
0.01
distorsi to end up with
0.2 + 0.1 + 0.01 = 0.31
. We added this offset to eliminate something called ZFighting where objects with similar depth values would appear on top of each other and cause flickering, like this.
_{Click here to go back to tutorial text.}
Source: https://vilbeyli.github.io/ProjectileMotionTutorialforArrowsandMissilesinUnity3D/