preface

Hi, I’m Chen Pipi.

An overview of the

Main task

The main task (core theme) of this article is “Projectile Motion”.

Projectile movement often appears in our daily life, such as basketball players shooting basketball in the air after the movement.

Projectile motion can also be seen in various video games, such as the movement of shells in the air after a tank shot in World of Tanks.

As you can see, the title of this article is “Projectile Motion in Game Development practice”, and we will work together to derive various projectile motion formulas and apply them in the game to achieve various interesting features.

Side quests

In addition to the main quest, this article also includes some side quests:

  • Object interaction and ray casting in 3D games
  • Dot and cross products of vectors in three dimensions (linear algebra)
  • Calculation of the projection of a vector onto a plane in three dimensions
  • Calculation of directed angles of vectors in three – dimensional space
  • Predraw the trajectory of the shell in real time

results

Results show

Just to show you what we’re trying to achieve.

Preview online: app.chenpipi.cn/cocos-case-…

Dynamic graph:

The sample project

The example project that accompanies this article, Projectile, is open source.

In order to avoid a smelly and long article, some functions and features in the project are not introduced in the article. Meanwhile, I will integrate the project code and insert it into the article, so the actual project structure will be slightly different from that in the code and the article.

Download source code:

  • Gitee Warehouse: gitee.com/ifaswind/co…
  • GitHub Repository: github.com/ifaswind/co…
  • Cocos store: store.cocos.com/app/detail/…

The game engine used in this project is Cocos Creator 3.4.2.

The body of the

Scenario building

Start with a lighthearted building block experience.

The environment

Quickly build a simple scene with colorful obstacles (or platforms) of varying heights.

Remember to add the right Collider to each of these things so that we can interact with them via rays.

πŸ€ͺ actually spent most of the day adjusting the sizes and colors of various shapes…

The cannon

A Cannon is assembled using a few basic shapes built in. Although it looks like a flashlight (πŸ”¦), I prefer to call it a Cannon!

πŸ€ͺ I really tried my best, spent a long time, but look for a long time actually quite pleasing to the eye…

Here are a few key parts of the cannon:

  • Yaw Axis that allows the gun to rotate left and right

  • Pitch Axis that allows the barrel to be adjusted up and down

  • A Fire Point that determines the position and direction of a projectile.

The shells

Use the built-in sphere to make a simple projectile (Bullet) and save it as a separate Prefab.

Add a Sphere Collider and a RigidBody so that the shell has physical properties.

Another thing to note is that the LinearDamping parameter for the rigid body component defaults to 0.1. We need to set this to 0 otherwise the shell will automatically slow down as it flies.

Basis function

Implement some basic functions with code.

Aim and fire

Cannon

Create a component script called Cannon to implement the various features of the Cannon.

Let’s start with simple “Aim” and “Shoot” functions:

At present, only the pitching Angle is updated in the cannon aiming function.

GameController

Create a component script named GameController to implement the game’s control logic.

Implement the logic of “click the mouse to launch shells” :

🎯 Operation effect:

Around toward

Right now the cannon can only shoot in one fixed direction, stupid, how about we control the left and right orientation of the cannon according to the position of the click.

Click interaction with ray casting

Let’s take a quick look at how to specify the target position of the cannon by clicking on the screen.

To interact with objects in a two-dimensional world using a mouse or touch screen in a 2D game, you need only look at a two-dimensional coordinate on the screen to determine whether or not to “hit” the object.

In 3D games, interacting with objects in a three-dimensional world requires a “Raycast” method.

To put it simply, a Ray is emitted from the Near Plane of the Camera. The length of the Ray is equal to the distance from the Near Plane to the Far Plane. The Ray intersects with the object in the scene, and if it intersects, it is counted as hitting the object.

Most game engines provide the ability to “create rays from two-dimensional coordinates in screen space” on the camera. For example, the Camera component of Cocos Creator provides screenPointToRay.

The specific click function is very simple to implement:

It is important to note that objects in the scene must have any type of Collider to be able to participate in ray detection.

Look straight at the target

Now we can get a target position, but in most cases our target position is not on the same level as the gun, and if the gun node looks directly at the target position, it will get an abnormal performance.

In addition, I secretly made a simple cursor (white arrow and crosshair) to highlight the current target position.

Note: Since in Cocos Creator 3.4.2, the front of the node is pointing in the -Z direction, it is now the rear end of the cannon looking at the target node.

Yaw Angle

In fact, we only wanted to change the left and right orientation of the gun. The technical term for this is Yaw.

πŸ›© Yaw

Yaw describes the motion of an object based on its yaw axis (usually the Y axis in the object’s own coordinate system in game development), changing the direction in which it points, to the left or right of its direction of motion.

Note: In rotation, in addition to Yaw, there are usually “Pitch” and “Roll”, which describe motion based on the other two main axes, respectively.

What we need to change is the yaw Angle of the gun in order to point the gun at the specified position. The correct way is:

  1. Creates a direction vector from the yaw axis node position to the target position
  2. The direction vector is projected onto the horizontal plane where the yaw axis node is located
  3. The Angle between the direction vector and the forward vector of the cannon is calculated by Dot Product

Note: “yaw axis node” is the child node of the cannon. It also bears all the nodes of the gun body. Changing the yaw Angle of the yaw axis node can change the orientation of the gun body.

“Disoriented”

Wait, the dot product is always between 0 and 180 degrees, which means that the dot product tells you the minimum Angle between two vectors, but it doesn’t tell you whether a vector is to the left or right of the other vector, because 45 degrees on the left and 45 degrees on the right are both 45 degrees for the dot product.

For example, in the picture below, the two purple vectors are 45 degrees from the blue vector, but they are on opposite sides of the blue vector:

Directed Angle

But in the game engine, the rotation of an object is in the range of 0 360 degrees, or -180 180 degrees (we all know that “90 degrees left” means the same thing as “-90 degrees right” or “270 degrees right”).

At the end of the day, all we need is an Directed Angle with a plus or minus sign.

When we say “direction,” those of you who are good at linear algebra will quickly realize that we can determine whether a vector is to the left or right of another vector by “Cross Product.”

In addition, vector directed Angle calculation in three dimensional space needs a reference plane to be meaningful.

Specific calculation steps:

  1. Project the two vectors onto the same reference plane
  2. Find the normal vectors of the two vectors by cross product
  3. The direction is obtained by the length of the projection of the method vector onto the normal vector of the reference plane
  4. Use dot product to calculate the Angle between two vectors
  5. Direction applied to the Angle (symbol)

A function of projecting a vector onto a plane:

Note: In a three-dimensional world, it is possible to represent a class of planes facing the same direction by using a direction vector as a plane normal, because “plane normal” means that the direction vector must be perpendicular to the plane it represents.

Code practice

With the yaw Angle fixed, the cannon will now be coded “toward target position” :

Note: The rotation of nodes in Cocos Creator 3.4.2 conforms to the right hand rule, that is, the counterclockwise direction is the positive direction.

Then implement the logic of “moving the mouse to control the cannon aiming” in GameController:

🎯 Stage Results:

Shooting mode

Now our cannon can follow the mouse to rotate 360 degrees and fire shells, but the pitch Angle and speed of the shells are fixed, it doesn’t look advanced at all.

The expectation is that, given a target position, the cannon can calculate its own firing Angle and speed.

To this end, we can design three different patterns:

  • Fixed firing Angle, dynamic calculation of the initial velocity of the shell
  • Fixed initial velocity of projectile, dynamic calculation of firing Angle
  • Dynamically calculate the firing Angle and the initial velocity of the projectile

Add to that the current “fixed firing Angle and initial velocity of the shell” mode, and we have four modes in total.

We’ll implement them one by one.

Fixed Angle and velocity

With the addition of the schema concept, we changed the current code structure slightly to make the subsequent coding more convenient and elegant.

The Aim function of the Cannon component calculates the Angle and velocity of the Cannon based on the mode:

Projectile motion

Although the previous abba abba so much, but seems to have nothing to do with our theme projectile motion.

A simple introduction

So what is projectile motion?

Projectile motion is the motion of the object thrown at any initial velocity under the action of earth gravity.

Projectile motion can be divided into flat throw motion and oblique throw motion:

  • Project Motion: An object has a zero Angle between its ejection direction and the Horizontal plane, so it only has a Horizontal initial velocity.

  • When an object exits from a horizontal plane at an Angle, it has an initial velocity in both the horizontal direction and the vertical direction.

In general, when we refer to projectile motion, we refer to oblique motion, because oblique motion is compatible with horizontal motion

In addition, projectile motion is often called “parabolic motion,” which I think is a little more convenient.

The basic formula

πŸ‘Ύ Welcome to the main prequest!

Before we dive in, let’s learn (review) some simple pre-knowledge.

First, let’s meet the members who will appear in various formulas:

  • SSS – Displacement
  • XXX – Horizontal Displacement
  • Yyy – Vertical Displacement
  • HHH – Maximum Height (Max Height)
  • ΞΈΞΈΞΈ – Initial Angle
  • VVV – Initial Velocity
  • TTT – Time (Time)
  • GGG – Gravitational Acceleration

Note: We assume initial speed by default
v v
It has a direction, and it corresponds to the initial Angle
Theta. Theta.
.

Note: Standard acceleration of gravity
g g
The value is 9.80665, but the general default in game engines is 9.8 or 10. The default value of gravity in Cocos Creator 3.4.2 is 10.

Note: All projectile motions discussed in this paper are “ideal projectile motions”.

Free fall displacement formula

A formula for calculating the displacement of a body when it is affected only by gravity.


s = 1 2 βˆ— g βˆ— t 2 (Free fall displacement formula) S = \frac{1}{2} * g * t^2 \tag{freefall displacement formula}

Horizontal displacement formula

In projectile motion, the body is affected in the horizontal direction only by its initial velocity.

That is, the object’s horizontal velocity is identical to the horizontal component of its initial velocity, i.e. V βˆ—cos⁑θv * \cos{ΞΈ}vβˆ—cosΞΈ.


x = v βˆ— cos ⁑ Theta. βˆ— t (Horizontal displacement formula) X = v * \cos{ΞΈ} * t \tag{horizontal displacement formula}

Note: in the calculation of horizontal displacement, we take “right” as the positive direction.

Vertical displacement formula

In projectile motion, the object is affected by initial velocity and gravitational acceleration in vertical direction.

In other words, the object actually exists at the same time in two vertical directions:

  • Displacement affected by the vertical component of the initial velocity
  • Displacement of a free-falling body affected by gravity

y = v βˆ— sin ⁑ Theta. βˆ— t 1 2 βˆ— g βˆ— t 2 (Vertical displacement formula) Y = v * \ sin {theta} * t – \ frac {1} {2} * g * t ^ 2 \ tag} {vertical displacement formula

Note: In the calculation of vertical displacement, “up” is the positive direction, so the free fall displacement is negative.

Advanced mode

😈 Welcome to the main quest!

Fixed Angle

Now for the second mode: fix a firing Angle and dynamically calculate the initial velocity of the projectile based on the target position.

In this mode, the cannon firing Angle is fixed, and a target point in three-dimensional space is given. We need to calculate the initial velocity of the cannon firing.

Calculate the horizontal and vertical displacements

Given the location of the target point, and we already know the location of the launch point, we can calculate the horizontal and vertical displacement from the launch point to the target point.

When both points are projected onto the same horizontal plane, the distance between them is the horizontal distance. The difference in y value between the launch point and the target point is their vertical distance.

The actual calculation process is as follows:

  1. Vector subtraction is used to obtain the direction vector from the transmitting point to the target point
  2. The y value of the direction vector is the vertical distance
  3. When a direction vector is projected onto a horizontal plane, its length is the horizontal distance

Note: In game development, the easiest way to represent a horizontal plane is to use a vertical vector as a plane normal, such as a value of{ x: 0, y: 1, z: 0 }The direction vector of.

Note: in Cocos Creator 3.4.2Vec3.UP = new Vec3(0, 1, 0).

Initial velocity formula

Ahem, back to business.

Let’s look at the information we have right now:

  • Horizontal displacement (XXX)
  • Vertical displacement (YYY)
  • Initial Angle (ΞΈΞΈΞΈ)

And what we want to ask is:

  • Initial speed (VVV)

Looking at the “horizontal displacement formula” and the “vertical displacement formula” above, there seems to be one key piece of information missing:

  • Time (TTT)

Not a problem! We can try to get rid of this time term.

First, we can easily obtain the “horizontal displacement time formula” from the “horizontal displacement formula” :


x = v βˆ— cos ⁑ Theta. βˆ— t (Horizontal displacement formula) X = v * \cos{ΞΈ} * t \tag{horizontal displacement formula}

left \downarrow

t = x v βˆ— cos ⁑ Theta. (Horizontal displacement time formula) T = frac{x}{v * cos{ΞΈ}} \tag{horizontal displacement time formula}

Then substitute the “horizontal displacement time formula” into the “vertical displacement formula” :


y = v βˆ— sin ⁑ Theta. βˆ— x v βˆ— cos ⁑ Theta. 1 2 βˆ— g βˆ— ( x v βˆ— cos ⁑ Theta. ) 2 * * y = v \ sin {theta} \ frac {x} {\ v * cos {theta}} – \ frac {1} {2} * g * \ left ({\ frac {x} {\ v * cos {theta}}} \ right) ^ 2

⇓ \Downarrow

y = x βˆ— v βˆ— sin ⁑ Theta. v βˆ— cos ⁑ Theta. 1 2 βˆ— g βˆ— ( x v βˆ— cos ⁑ Theta. ) 2 Y = \ frac {x * v * \ sin {theta}} {\ v * cos {theta}} – \ frac {1} {2} * g * \ left ({\ frac {x} {\ v * cos {theta}}} \ right) ^ 2

⇓ \Downarrow

y = x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 v 2 βˆ— cos ⁑ 2 Theta. (Formula 2 of vertical displacement) Y = x * \ tan {theta} – \ frac {1} {2} * g * \ frac {x ^ 2} {\ cos ^ v ^ 2 * 2 {theta}} \ tag {2} vertical displacement formula

Well, we’ve eliminated the time term (TTT), and we’ve got a new vertical displacement formula, which we’ll call “vertical displacement formula 2” just to distinguish it.

Finally, we try to “isolate” the initial velocity term (VVV) in the formula:


y = x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 v 2 βˆ— cos ⁑ 2 Theta. (Formula 2 of vertical displacement) Y = x * \ tan {theta} – \ frac {1} {2} * g * \ frac {x ^ 2} {\ cos ^ v ^ 2 * 2 {theta}} \ tag {2} vertical displacement formula

left \downarrow

0 = x βˆ— tan ⁑ Theta. g βˆ— x 2 2 βˆ— v 2 βˆ— cos ⁑ 2 Theta. y 0 = x * \ tan – \ frac {theta} {g * x ^ 2} {\ cos ^ v ^ 2 * 2 * 2 {theta}} – y

left \downarrow

g βˆ— x 2 2 βˆ— v 2 βˆ— cos ⁑ 2 Theta. = x βˆ— tan ⁑ Theta. y * x ^ 2 \ frac {g} {\ cos ^ v ^ 2 * 2 * 2 {theta}} \ tan = x * {theta} – y

left \downarrow

1 2 βˆ— v 2 βˆ— cos ⁑ 2 Theta. = x βˆ— tan ⁑ Theta. y g βˆ— x 2 \ frac {1} {\ cos ^ v ^ 2 * 2 * 2 {theta}} = \ frac {x * \ tan – y} {theta} {g * x ^ 2}

left \downarrow

1 v 2 = ( x βˆ— tan ⁑ Theta. y ) βˆ— 2 βˆ— cos ⁑ 2 Theta. g βˆ— x 2 V ^ \ frac {1} {2} = \ frac {\ tan (x * {theta} – y) \ cos ^ 2 * 2 * {theta}} {g * x ^ 2}

left \downarrow

v = g βˆ— x 2 2 βˆ— x βˆ— sin ⁑ Theta. βˆ— cos ⁑ Theta. 2 βˆ— y βˆ— cos ⁑ 2 Theta. (Initial velocity formula) V = \ SQRT {\ frac {g * x ^ 2} {2 * x * \ sin \ {theta} * cos \ {theta} – 2 * y * cos ^ 2 {theta}}} \ tag} {velocity formula

πŸ’₯ Boom!

That’s right! That’s the formula we want for our initial velocity!

Code practice

Now, let’s put the formula into code:

Note: For some cases that cannot be calculated, validity judgments are added to the code.

The cannon calculates the initial velocity according to the target position when aiming:

🎯 Stage Results:

Fixed speed

The third mode: the initial velocity of the shell is fixed, and the firing Angle is dynamically calculated according to the target position.

In this mode, the initial velocity of the cannon is fixed, and a target point in three-dimensional space is given. We need to calculate the firing Angle.

Initial velocity formula

In the derivation of “velocity formula”, we get a “vertical displacement formula 2” without time term (TTT), and here we can try to use this formula directly to derive the Angle formula.


y = x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 v 2 βˆ— cos ⁑ 2 Theta. (Formula 2 of vertical displacement) Y = x * \ tan {theta} – \ frac {1} {2} * g * \ frac {x ^ 2} {\ cos ^ v ^ 2 * 2 {theta}} \ tag {2} vertical displacement formula

Wait, now we’re looking for the initial Angle (ΞΈΞΈΞΈ), and it’s not a good idea to have both tan⁑θ\tan{ΞΈ}tanΞΈ and cos⁑2ΞΈ\cos^2{ΞΈ}cos2ΞΈ in “vertical displacement formula 2”.

But the good news is that we can simply replace cos⁑2θ\cos^2{θ}cos2θ with 1tan⁑2θ+1\frac{1}{\tan^2{θ} +1}tan2θ+11 (since they are equal), which should help us reduce some of the difficulties.


y = x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 v 2 βˆ— 1 tan ⁑ 2 Theta. + 1 Y = x * \ tan {theta} – \ frac {1} {2} * g * \ frac {x ^ 2} {v ^ 2 * \ frac {1} {\ tan ^ 2 + 1} {theta.}}

The new formula seems to be getting more complicated, so let’s try it out:


y = x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 v 2 βˆ— 1 tan ⁑ 2 Theta. + 1 Y = x * \ tan {theta} – \ frac {1} {2} * g * \ frac {x ^ 2} {v ^ 2 * \ frac {1} {\ tan ^ 2 + 1} {theta.}}

⇓ \Downarrow

y = x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 βˆ— ( tan ⁑ 2 Theta. + 1 ) v 2 Y = x * \ tan {theta} – \ frac {1} {2} * g * \ frac {x ^ 2 * (\ tan ^ 2 + 1) {theta}} {2} v ^

left \downarrow

y βˆ— v 2 = v 2 βˆ— x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 βˆ— ( tan ⁑ 2 Theta. + 1 ) Y * v ^ 2 * * x ^ 2 = v \ tan {theta} – \ frac {1} {2} * g * x ^ 2 * (\ tan ^ 2 + 1) {theta.}

⇓ \Downarrow

y βˆ— v 2 = v 2 βˆ— x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 βˆ— tan ⁑ 2 Theta. 1 2 βˆ— g βˆ— x 2 Y * v ^ 2 * * x ^ 2 = v \ tan {theta} – \ frac {1} {2} * g * x ^ 2 * \ tan ^ 2 {theta} – \ frac {1} {2} * g * x ^ 2

Here we can move the term on the left to the right:


0 = v 2 βˆ— x βˆ— tan ⁑ Theta. 1 2 βˆ— g βˆ— x 2 βˆ— tan ⁑ 2 Theta. 1 2 βˆ— g βˆ— x 2 y βˆ— v 2 0 = v ^ 2 * * x \ tan {theta} – \ frac {1} {2} * g * x ^ 2 * \ tan ^ 2 {theta} – \ frac {1} {2} * g * x ^ 2 – y * v ^ 2

Well, the formula seems to conform to the characteristics of “Quadratic equations with one variable”.


0 = v 2 βˆ— x βˆ— tan ⁑ Theta. g βˆ— x 2 2 βˆ— tan ⁑ 2 Theta. g βˆ— x 2 + 2 βˆ— y βˆ— v 2 2 0 = v ^ 2 * * x \ tan – \ frac {theta} {g * x ^ 2} {2} * \ tan ^ 2 – \ frac {theta} {g * x ^ 2 + 2 * * y v ^ 2} {2}

⇓ \Downarrow

0 = v 2 βˆ— tan ⁑ Theta. g βˆ— x 2 βˆ— tan ⁑ 2 Theta. g βˆ— x 2 + 2 βˆ— y βˆ— v 2 2 βˆ— x 0 = v ^ 2 * \ tan – \ frac {theta} * * x {g} {2} \ tan ^ 2 – \ frac {theta} {x ^ 2 + 2 g * * * y v ^ 2} {2} * x

⇓ \Downarrow

0 = ( g βˆ— x 2 βˆ— tan ⁑ 2 Theta. ) + ( v 2 βˆ— tan ⁑ Theta. ) + ( g βˆ— x 2 + 2 βˆ— y βˆ— v 2 2 βˆ— x ) * x = 0 (- \ frac {g} {2} * \ tan ^ 2 {theta}) + (v ^ 2 * \ tan {theta}) + (- \ frac {x ^ 2 + 2 g * * * y v ^ 2} {2 * x})

Hey, hey, hey, it’s a quadratic equation with one variable!


0 = a βˆ— x 2 + b βˆ— x + c (General form of quadratic equation of one variable) 0 = a * x^2 + b * x + c \tag

We can match all the parts of the formula:


x = tan ⁑ Theta. (Unknown terms) X = \tan{θ} \tag{unknown term}

a = g βˆ— x 2 (Quadratic coefficient) A = -\frac{g * x}{2} \tag{quadratic coefficient}

b = v 2 (Coefficient of primary term) B = v^2 \tag{first term coefficient}

c = g βˆ— x 2 + 2 βˆ— y βˆ— v 2 2 βˆ— x (Constant term) C = – \ frac {x ^ 2 + 2 g * * * y v ^ 2} {2 * x} \ tag {of}

This is easy to do, for quadratic equations of one variable, we can directly use the “formula method” to solve.

Here is the “root formula” for a quadratic equation of one variable:


x = b Plus or minus b 2 4 βˆ— a βˆ— c 2 βˆ— a (Finding the root formula of quadratic equation of one variable) X = \ frac {- b + \ SQRT {b ^ 2-4 * a * c}} {2 * a} \ tag {} a yuan quadratic equation for root formula

Substitute the unknown term, quadratic term coefficient, primary term coefficient and constant term into the root formula:


tan ⁑ Theta. = v 2 Plus or minus v 4 g βˆ— ( g βˆ— x 2 + 2 βˆ— y βˆ— v 2 ) g βˆ— x \ tan = {theta} \ frac {- v ^ 2 + \ SQRT {v ^ 4 – g * (x ^ 2 + g * * * y v ^ 2)}} {x} – g *

left \downarrow

Theta. = arctan ⁑ ( v 2 Plus or minus v 4 g βˆ— ( g βˆ— x 2 + 2 βˆ— y βˆ— v 2 ) g βˆ— x ) (Initial Angle formula) Theta = \ arctan {\ left (\ frac {- v ^ 2 + \ SQRT {v ^ 4 – g * (x ^ 2 + g * * * y v ^ 2)}} {- g * x} \ right)} \ tag} {the initial Angle formula

πŸŽ‰ Bingo!

Finally, the initial Angle formula!

One other thing to note, though, is that this formula has at most two solutions.

πŸ”’ Quadratic Equation with one variable

An integral equation containing only one unknown quantity (unary) and the terms of the unknown quantity having the highest degree of 2 (quadratic) is called a unary quadratic.

Ax2 +bx+c=0(a≠0) Ax ^2 +bx+ C =0(a≠0) ax2+bx+c=0(a=0). Ax2ax ^2ax2 is called the quadratic term, and AAA is the quadratic coefficient; BXBXBX is called the first order term, BBB is the first order term coefficient; CCC is called the constant term.

— Introduction from Baidu Encyclopedia

Code practice

Put the formula into code:

Note: For some cases that cannot be calculated, validity judgments are added to the code.

The cannon calculates the initial Angle from the target position when aiming.

In the case of two answers, let’s just go with the larger Angle. You can also choose different angles depending on the situation.

If the result is not valid, it means that no matter what Angle the projectile is fired at, the projectile will never reach its destination. In this case, we can choose to maintain the current Angle.

🎯 Stage Results:

Not fixed Angle and velocity

Previously, we respectively implemented fixed Angle and fixed speed schemes, which are quite good, but feel not too flexible.

What is flexible? I don’t want the cannon fixed at a certain Angle or speed, I want freedom, I don’t want anything!

So let’s implement the fourth mode: specify a target position, dynamically calculate the firing Angle and the initial velocity of the projectile.

Situation analysis

The information we have:

  • Horizontal displacement (XXX)
  • Vertical displacement (YYY)

What we require is:

  • Initial Angle (ΞΈΞΈΞΈ)
  • Initial speed (VVV)

Remember the vertical displacement formula from the beginning, yeah, the one with the time term.


y = v βˆ— sin ⁑ Theta. βˆ— t 1 2 βˆ— g βˆ— t 2 (Vertical displacement formula) Y = v * \ sin {theta} * t – \ frac {1} {2} * g * t ^ 2 \ tag} {vertical displacement formula

Boy, there are 5 terms in a formula, 3 of which are unknown, what the hell…

Looks like we have to find another way.

Maximum height formula

We all know that oblique throw can be divided into two main stages, the first stage of ascent, after reaching a maximum height, followed by the descent stage.

If we can determine the maximum height, is there some room for manoeuvre?

πŸ€” When does the object reach its maximum height?

πŸ€“ the answer is when the object stops rising (before the upward phase ends and the downward phase begins), i.e., at this point the object’s vertical velocity is zero.

As we mentioned earlier, in oblique throw motion, vertical displacement is affected by two velocities:

  • Vertical component of initial velocity (vertical up)
  • Speed due to gravity (vertical downward)

When the vertical velocity of the object is 0, it means that “the vertical component of the initial velocity” is equal to “the velocity given by gravity” :


v βˆ— sin ⁑ Theta. = g βˆ— t V * \sin{ΞΈ} = g * t

From the above formula, we can obtain the formula for the time when the object reaches the maximum height, which we call “maximum height time formula” :


t = v βˆ— sin ⁑ Theta. g (Maximum height time formula) T = \frac{v * \sin{ΞΈ}}{g} \tag{maximum height time formula}

We substitute the “maximum height time formula” into the “vertical displacement formula” and eliminate the time term (TTT) again:


y = v βˆ— sin ⁑ Theta. βˆ— t 1 2 βˆ— g βˆ— t 2 (Vertical displacement formula) Y = v * \ sin {theta} * t – \ frac {1} {2} * g * t ^ 2 \ tag} {vertical displacement formula

left \downarrow

h = v βˆ— sin ⁑ Theta. βˆ— v βˆ— sin ⁑ Theta. g 1 2 βˆ— g βˆ— ( v βˆ— sin ⁑ Theta. g ) 2 H \ sin = v * * \ frac {theta} {v * \ sin {theta}} {g} – \ frac {1} {2} * g * \ left (\ frac {v * \ sin {theta}} {g} \ right) ^ 2

⇓ \Downarrow

h = v 2 βˆ— sin ⁑ 2 Theta. 2 βˆ— g (Maximum height formula) H = \frac{v^2 * \sin^2{ΞΈ}}{2 * g} \tag{Max height formula}

So we get the formula for maximum height.

Initial Angle formula 2

According to the “maximum height formula”, we can get the “initial Angle formula 2” containing the maximum height term (HHH) and the initial speed term (VVV) :


Theta. = arcsin ⁑ ( 2 βˆ— g βˆ— h v ) (Formula 2 of initial Angle) Theta = \ arcsin {\ left (\ frac {\ SQRT {2 * g * h}} {n} \ right)} \ tag initial Angle formula {2}

Formula 2 for initial velocity

And the “initial velocity formula 2” containing the maximum height term (HHH) and the initial Angle term (ΞΈΞΈΞΈ) :


v = 2 βˆ— g βˆ— h sin ⁑ Theta. (Formula 2 of initial velocity) V = \ frac {\ SQRT {2} * g * h} {\ sin {theta}} \ tag {2} velocity formula

Wuhu, buy one get two free, very good deal!

Don’t be happy, our problems are still unresolved…

According to the existing formula, the initial Angle and the initial velocity must be solved before the other can be solved.

Time formula

We were at a loss when our old friend, the vertical displacement formula, dropped in:


y = v βˆ— sin ⁑ Theta. βˆ— t 1 2 βˆ— g βˆ— t 2 (Vertical displacement formula) Y = v * \ sin {theta} * t – \ frac {1} {2} * g * t ^ 2 \ tag} {vertical displacement formula

πŸ”’ it says: in this case we can try to eliminate the terms and minimize the number of unknowns in the formula, just like in the game of elimination.

😼 I say: good! Who doesn’t like to play match-and-match?

Then substitute the “initial velocity formula 2” just obtained into the “vertical displacement formula” :


y = 2 βˆ— g βˆ— h sin ⁑ Theta. βˆ— sin ⁑ Theta. βˆ— t 1 2 βˆ— g βˆ— t 2 (Vertical displacement formula) Y = \ frac {\ SQRT {2} * g * h} {\ sin {theta}} * \ sin {theta} * t – \ frac {1} {2} * g * t ^ 2 \ tag} {vertical displacement formula

⇓ \Downarrow

y = 2 βˆ— g βˆ— h βˆ— t 1 2 βˆ— g βˆ— t 2 y = \sqrt{2 * g * h} * t – \frac{1}{2} * g * t^2

At this time, we find that this formula also seems to satisfy the characteristics of “quadratic equation with one variable” :


y = 2 βˆ— g βˆ— h βˆ— t 1 2 βˆ— g βˆ— t 2 y = \sqrt{2 * g * h} * t – \frac{1}{2} * g * t^2

left \downarrow

0 = 2 βˆ— g βˆ— h βˆ— t 1 2 βˆ— g βˆ— t 2 y 0 = \sqrt{2 * g * h} * t – \frac{1}{2} * g * t^2 – y

⇓ \Downarrow

0 = ( 1 2 βˆ— g βˆ— t 2 ) + ( 2 βˆ— g βˆ— h βˆ— t ) + ( y ) 0 = (-\frac{1}{2} * g * t^2) + (\sqrt{2 * g * h} * t) + (-y)

Remember, the general form of a quadratic equation with one variable:


0 = a βˆ— x 2 + b βˆ— x + c (General form of quadratic equation of one variable) 0 = a * x^2 + b * x + c \tag

Match each part of the formula:


x = t (Unknown terms) X = t \tag{unknown item}

a = 1 2 βˆ— g (Quadratic coefficient) A = -\frac{1}{2} * g \tag{quadratic coefficient}

b = 2 βˆ— g βˆ— h (Coefficient of primary term) B = SQRT {2 * g * h} \tag{first term coefficient}

c = y (Constant term) C = -y \tag{constant term}

Or the formula for finding the root of a quadratic equation with one variable:


x = b Plus or minus b 2 4 βˆ— a βˆ— c 2 βˆ— a (Finding the root formula of quadratic equation of one variable) X = \ frac {- b + \ SQRT {b ^ 2-4 * a * c}} {2 * a} \ tag {} a yuan quadratic equation for root formula

Substitute the unknown term, quadratic term coefficient, primary term coefficient and constant term into the root formula:


t = 2 βˆ— g βˆ— h Plus or minus 2 βˆ— g βˆ— h 4 βˆ— ( 1 2 ) βˆ— g βˆ— y 2 βˆ— ( 1 2 ) βˆ— g T = \ frac {- \ SQRT {2} * g * h + \ SQRT {2 * g * * h – 4 (- \ frac {1} {2}) * g * – y}} {2 * (- \ frac {1} {2})} * g

⇓ \Downarrow

t = 2 βˆ— g βˆ— h Plus or minus 2 βˆ— g βˆ— ( h y ) g (Time formula) T = \ frac {- \ SQRT {2} * g * h + \ SQRT {2 * g * (h – y)}}} {- g \ tag} {time formula

πŸ₯° Great!

Now we can figure out the total time (TTT) of the object from the starting point to the target point.

Initial Angle formula 3

Things have changed, so let’s sort out what we have now:

  • Horizontal displacement (XXX)
  • Vertical displacement (YYY)
  • Maximum height (HHH)
  • Time (TTT)

We still require:

  • Initial Angle (ΞΈΞΈΞΈ)
  • Initial speed (VVV)

Do the same thing again, go away!

Let’s use the “horizontal displacement formula” this time:


x = v βˆ— cos ⁑ Theta. βˆ— t (Horizontal displacement formula) X = v * \cos{ΞΈ} * t \tag{horizontal displacement formula}

Try substituting “initial velocity formula 2” :


x = 2 βˆ— g βˆ— h sin ⁑ Theta. βˆ— cos ⁑ Theta. βˆ— t X = \ frac {\ SQRT {2} * g * h} {\ sin {theta}} * \ cos {theta} * t

left \downarrow

x βˆ— sin ⁑ Theta. cos ⁑ Theta. βˆ— t = 2 βˆ— g βˆ— h \ \ frac {x * sin {theta}} {\ cos t} {theta} * = \ SQRT {2} * g * h

left \downarrow

sin ⁑ Theta. cos ⁑ Theta. = 2 βˆ— g βˆ— h βˆ— t x \ frac {\ sin {theta}} {\ cos {theta}} = \ frac {\ SQRT h} {2 * g * * t} {x}

left \downarrow

tan ⁑ Theta. = 2 βˆ— g βˆ— h βˆ— t x \tan{ΞΈ} = \frac{\ SQRT {2 * g * h} * t}{x}

left \downarrow

Theta. = arctan ⁑ ( 2 βˆ— g βˆ— h βˆ— t x ) (Formula 3 of initial Angle) Theta = \ arctan {\ left (\ frac {\ SQRT h} {2 * g * * t} {x} \ right)} \ tag initial Angle formula 3} {

😘 Excellent!

Now we can figure out the initial Angle from the maximum height (HHH) and the time (TTT)!

Key formula

Whoo, so many formulas are confusing, so let’s separate out the ones that work.

Use “time formula” to calculate time (TTT) :


t = 2 βˆ— g βˆ— h Plus or minus 2 βˆ— g βˆ— ( h y ) g (Time formula) T = \ frac {- \ SQRT {2} * g * h + \ SQRT {2 * g * (h – y)}}} {- g \ tag} {time formula

Use “initial Angle Formula 3” to calculate the initial Angle (ΞΈΞΈΞΈ) :


Theta. = arctan ⁑ ( 2 βˆ— g βˆ— h βˆ— t x ) (Formula 3 of initial Angle) Theta = \ arctan {\ left (\ frac {\ SQRT h} {2 * g * * t} {x} \ right)} \ tag initial Angle formula 3} {

Calculate the initial velocity (VVV) using “Initial velocity Formula 2” :


v = 2 βˆ— g βˆ— h sin ⁑ Theta. (Formula 2 of initial velocity) V = \ frac {\ SQRT {2} * g * h} {\ sin {theta}} \ tag {2} velocity formula

Code practice

πŸ₯΄ Math is a mystery.

Finally, I can write code.

The cannon calculates the initial Angle and velocity according to the target position when aiming.

We will be the biggest height in calculateAngleAndVelocity functions as 1.

🎯 To take a look at the actual effect:

No needle, that’s about what we want!

☹️ there seems to be a problem though, when we set the target position above the orange pole, i.e. the vertical distance between the target and the launch point is greater than the maximum altitude we give, the cannon goes straight to watt.

Well, we defined the maximum altitude based on the launch point, and when the maximum altitude is less than the vertical distance between the target point and the launch point, the shell will definitely not reach the target point.

In addition, the maximum height can never be less than zero, because the height of the launch site is zero, how can the maximum height be less than the height of the launch site, it doesn’t make sense.

Therefore, the maximum height had better not be fixed. Since we can directly calculate the vertical distance and horizontal distance of the target point of the launching point, why not calculate an appropriate maximum height according to these two data?

🎯 Try again:

😎 Perfect!

So far we have successfully implemented the various shooting functions of the cannon!

Artillery trajectory

πŸ›Έ is not over yet!

When we know the projectile’s launch point, target point, initial Angle, and initial speed, we can also plot the trajectory of the projectile.

Something like this:

Isn’t it great!

Trajectory point generation

Compared to the previous content, this is very simple to implement, directly to the formula:


x = v βˆ— cos ⁑ Theta. βˆ— t (Horizontal displacement formula) X = v * \cos{ΞΈ} * t \tag{horizontal displacement formula}

y = v βˆ— sin ⁑ Theta. βˆ— t 1 2 βˆ— g βˆ— t 2 (Vertical displacement formula) Y = v * \ sin {theta} * t – \ frac {1} {2} * g * t ^ 2 \ tag} {vertical displacement formula

t = x v βˆ— cos ⁑ Theta. (Horizontal displacement time formula) T = frac{x}{v * cos{ΞΈ}} \tag{horizontal displacement time formula}

Yes, they are.

According to “horizontal displacement formula” and “vertical displacement formula”, we can calculate the horizontal and vertical displacement of shells at any time in the process of movement.

According to the “horizontal displacement time formula” we can calculate the total time from the firing point to the target point.

We generate several track points, calculate the time interval between track points according to the total time of movement, traverse all track points, calculate the displacement of each track point at the corresponding time, and finally set the position of track points.

Here, I use small white cuboids as track points. After batch processing is enabled, only 1 Drawcall is required to draw the whole track, and there is basically no need to worry about performance problems.

🎯 to see the effect:

You can see that the generated trace is basically correct, but there are still two problems to solve.

Number of track points

The first problem is that the number of track points is fixed. The farther the target point is from the launching point, the farther the interval between the track points is, which is very sparse and not very beautiful.

For this problem, we can actually determine the number of track points according to the horizontal distance between the target point and the launching point.

🎯 effect:

When the number of track points is not fixed and changes frequently, Node pools are recommended to optimize runtime performance and avoid frequent creation and destruction of a large number of nodes.

Track point rotation

Another problem is that although each locus point is in the correct position, their rotation does not change, which looks strange.

In fact, at every point in the projectile’s motion, the direction of the object’s motion changes. So what we want is that the orientation of each locus point should also correspond to the direction of motion at the corresponding time.

As we mentioned at the beginning, the horizontal velocity of the object in the projectile motion is identical to the horizontal component of the initial velocity, and the formula can be obtained:


v x = v βˆ— cos ⁑ Theta. (Instantaneous horizontal velocity formula) V_ {x} = v * \cos{ΞΈ} \tag{instantaneous horizontal velocity formula}

The vertical velocity of the object is equal to the vertical component of the initial velocity (vertical upward) minus the velocity given by gravity (vertical downward), and the formula can be obtained:


v y = v βˆ— sin ⁑ Theta. g βˆ— t (Instantaneous vertical velocity formula) V_ {y} = v * \sin{ΞΈ} -g * t \tag{instantaneous vertical velocity formula}

With the horizontal and vertical velocity components, we can work out the direction of the actual velocity:


Theta. t = arctan ⁑ ( v y v x ) (Instantaneous Angle formula) Theta _ {t} = \ arctan {\ left ({\ frac {v_ {y}} {v_ {x}}} \ right)} \ tag} {instantaneous Angle formula

Put the formula into code:

Good, now that we can calculate the Angle of motion at any time, how do we apply that Angle to an object in three dimensions?

In three dimensions, for rotation between different axes, order is very important, different order will get completely different results.

In our case, we need to “Orient” the trajectory point to the target (yaw Angle) and then apply the instantaneous velocity direction (pitch Angle).

In game development we can use Quaternion to achieve multiple rotations:

  1. Generate quaternions based on forward and up vectors
  2. Apply pitch Angle based on X-axis rotation

Actual code:

🎯 final effect:

πŸ₯³ Wonderful!

Aimed at the cursor

Finally, we append a small Feature to the cursor.

Add a demand

In the section “Click Interaction and raycasting”, this article briefly introduces how to obtain the 3d world coordinates of screen clicks through raycasting.

In addition to the coordinates of the hit point, the results of ray projection also include the normal line of the hit surface.

We can use the normal line as the upward direction of the cursor, so that the cursor is always perpendicular to the surface of the hit point.

Adaptive target surface

But you can’t determine the rotation of an object with just one normal line!

A three-dimensional cartesian coordinate system has three axis vectors that are perpendicular to each other, and at least two perpendicular vectors are needed to construct a three-dimensional cartesian coordinate system (the third axis can be found by cross product of the vectors).

πŸ’¬ so we also need to find a vector perpendicular to the normal line of the target plane, and theoretically a vector has an infinite number of vectors perpendicular to it, what can we do?

While I was busy with my meal, I came up with a very clever plan:

  1. Generate a direction vector using the target position and the gun position
  2. Project the direction vector onto the plane represented by the normal line of the target plane
  3. After normalization, a unit vector perpendicular to the normal line of the target surface is obtained
  4. The rotation of the object can be determined by the unit vector and the target plane normals

Just code it:

🎯 to see the effect:

Our cursor will always be perpendicular to the surface of the object and will be directly in front of the cannon as much as possible.

🎯 after changing the crosshair into a circle, there is no sense of violation:

😏 TSK TSK, I am really a little clever ~

conclusion

Do not know how to sum up, did not see clear words, just watch again ~

πŸ€ͺ Abba abba!

The resources

Projectile motion:En.wikipedia.org/wiki/Projec…

Quadratic equation:En.wikipedia.org/wiki/Quadra…


The last

portal

Personal blog (rookie small stack) : chenpipi.cn

Open source homepage (Chen PI PI) : gitee.com/ifaswind

Novice small stack

😺 MY name is Pipi Chen, a game developer who is still learning and loves to share.

🎨 this is my personal public account, focusing on but not limited to game development and front-end technology sharing.

πŸ’– each original is very carefully, your attention is my original power!

Input and output.