The math in Gamedev is simple. Vectors and integrals

3r33420. 3r3-31. Hello! Today I would like to talk about mathematics. Mathematics is a very interesting science and it can be very useful in the development of games, and in general when working with computer graphics. Many (especially beginners) simply do not know how it is used during development. There are many problems that do not require a deep understanding of such concepts as: integrals, complex numbers, groups, rings, etc., but thanks to mathematics you can solve many interesting problems. In this article we will consider vectors and integrals. If interested, welcome under cat. Illustrating the Unity project, as always, is attached. 3r3407.  3r33420. 3r3407.  3r33420. 3r3404. 3r3407.  3r33420. Vector math. 3r3407.  3r33420. 3r3407.  3r33420. Vectors and vector math are essential tools for game development. Many operations and actions are tied to it entirely. It's funny that to implement a class that displays a vector arrow in Unity, most of the typical operations are already required. If you are well versed in vector mathematics, this block will be of no interest to you. 3r3407.  3r33420. 3r3407.  3r33420. Vector arithmetic and useful functions 3r3407.  3r33420. 3r3407.  3r33420. Analytical formulas and other details are easy to google, so let's not waste time on it. The operations themselves will be illustrated with gif animations below. 3r3407.  3r33420. 3r3149. It is important to understand that any point is essentially a vector with a beginning at the zero point. 3r33150. 3r3407.  3r33420. 3r3407.  3r33420. 3r33385. 3r33386.
3r33333. Your browser does not support HTML5 video.
3r33333. 3r3333391. 3r31616. 3r31616. 3r31616. 3r3407.  3r33420. 3r3407.  3r33420. Gifs were made using Unity, so it would be necessary to implement a class responsible for drawing arrows. The vector arrow consists of three main components - the line, the tip and the text with the name of the vector. To draw the line and the tip, I used the LineRenderer. Look at the class of the vector itself:
3r33420. 3r3407.  3r33420.
Arrow class [/b]
3r33333. 3r33333. using System.Collections; 3r33420. using System.Collections.Generic; 3r33420. using TMPro; 3r33420. using UnityEngine; 3r33420. 3r33420. public class VectorArrow: MonoBehaviour
{
[SerializeField]private Vector3 _VectorStart; 3r33420.[SerializeField]private Vector3 _VectorEnd; 3r33420.[SerializeField]private float TextOffsetY; 3r33420.[SerializeField]private TMP_Text _Label; 3r33420.[SerializeField]private Color _Color; 3r33420.[SerializeField]private LineRenderer _Line; 3r33420.[SerializeField]private float _CupLength; 3r33420.[SerializeField]private LineRenderer _Cup; 3r33420. 3r33420. private void OnValidate ()
{
UpdateVector (); 3r33420.}
3r33420. private void UpdateVector ()
{
if (_Line == null || _Cup == null) return; 3r33420. 3r33420. SetColor (_Color); 3r33420. _Line.positionCount = _Cup.positionCount = 2; 3r33420. _Line.SetPosition (? _VectorStart); 3r33420. _Line.SetPosition (? _VectorEnd - (_VectorEnd - _VectorStart) .normalized * _CupLength); 3r33420. 3r33420. _Cup.SetPosition (? _VectorEnd - (_VectorEnd - _VectorStart) .normalized * _CupLength); 3r33420. _Cup.SetPosition (? _VectorEnd); 3r33420. 3r33420. if (_Label! = null)
{
var dv = _VectorEnd - _VectorStart; 3r33420. var normal = new Vector3 (-dv.y, dv.x) .normalized; 3r33420. normal = normal.y> 0? normal: -normal; 3r33420. _Label.transform.localPosition
= (_VectorEnd + _VectorStart) /2
+ normal * TextOffsetY; 3r33420. _Label.transform.up = normal; 3r33420.}
3r33420.}
3r33420. public void SetPositions (Vector3 start, Vector3 end)
{
_VectorStart = start; 3r33420. _VectorEnd = end; 3r33420. UpdateVector (); 3r33420.}
3r33420. public void SetLabel (string label)
{
_Label.text = label; 3r33420.}
3r33420. public void SetColor (Color color)
{
_Color = color; 3r33420. _Line.startColor = _Line.endColor = _Cup.startColor = _Cup.endColor = _Color; 3r33420.}
}
3r33351. 3r33352. 3r3407.  3r33420. 3r31616. 3r31616. 3r3407.  3r33420. 3r3407.  3r33420. Since we want the vector to be a certain length and exactly match the points that we specify, the length of the line is calculated by the formula: 3r3407.  3r33420. 3r33333. 3r33333. _VectorEnd - (_VectorEnd - _VectorStart) .normalized * _CupLength 3r33352. 3r3407.  3r33420. 3r3407.  3r33420. In this formula, (_VectorEnd - _VectorStart) .normalized Is the direction of the vector. This can be understood from the animation with the difference of vectors, assuming that _VectorEnd and 3r3149. _VectorStart [/i] - is a vector with the beginning in (??0). 3r3407.  3r33420. 3r3407.  3r33420. Next we analyze the remaining two basic operations: 3r3407.  3r33420. 3r3407.  3r33420. 3r33385. 3r33386.
3r33333. Your browser does not support HTML5 video. 3r3163. 3r33333. 3r3333391. 3r31616. 3r31616. 3r31616. 3r3407.  3r33420. 3r3407.  3r33420. Finding the normal (perpendicular) and the middle of the vector are very common tasks in game development. Let us analyze them using the example of placing a signature over a vector. 3r3407.  3r33420. 3r3407.  3r33420. 3r33333. 3r33333. var dv = _VectorEnd - _VectorStart; 3r33420. var normal = new Vector3 (-dv.y, dv.x) .normalized; 3r33420. normal = normal.y> 0? normal: -normal; 3r33420. _Label.transform.localPosition = (_VectorEnd + _VectorStart) /2 + normal * TextOffsetY; 3r33420. _Label.transform.up = normal; 3r33351. 3r33352. 3r3407.  3r33420. 3r3407.  3r33420. In order to place the text perpendicular to the vector, we need a normal. In 2D graphics, the normal is quite simple. 3r3407.  3r33420. 3r33333. 3r33333. var dv = _VectorEnd - _VectorStart; 3r33420. var normal = new Vector3 (-dv.y, dv.x) .normalized; 3r33351. 3r33352. 3r3407.  3r33420. 3r3407.  3r33420. So we got the normal to the segment. 3r3407.  3r33420. normal = normal.y> 0? normal: -normal; - this operation is responsible for ensuring that the text is always shown above the vector. 3r3407.  3r33420. 3r3407.  3r33420. Then it remains to place it in the middle of the vector and lift it along the normal by a distance that will look beautiful. 3r3407.  3r33420. 3r33333. 3r33333. _Label.transform.localPosition
= (_VectorEnd + _VectorStart) /2
+ normal * TextOffsetY; 3r33351. 3r33352. 3r3407.  3r33420. 3r3407.  3r33420. The code uses local positions so that you can move the resulting arrow. 3r3407.  3r33420. 3r3407.  3r33420. But it was about 2D, but what about 3D? 3r3407.  3r33420. 3r3407.  3r33420. In 3D, plus or minus all the same. Only the normal formula is different, since the normal is already taken not to the segment, but to the plane. 3r3407.  3r33420. 3r3407.  3r33420. 3r33385. 3r33386.
3r33333. Your browser does not support HTML5 video.
3r33333. 3r3333391. 3r31616. 3r31616. 3r31616. 3r3407.  3r33420. In this example of the controller, the normal to the plane is used to shift the end point of the path to the right so that the planet is not blocked by the interface. The normal in 3D graphics is the normalized vector product of two vectors. Conveniently, Unity has both of these operations and we get a nice compact record:
3r33420. 3r33333. 3r33333. var right = Vector3.Cross (hit.normal, Vector3.up) .normalized; 3r33351. 3r33352. 3r3407.  3r33420. 3r3407.  3r33420. I think that many who think that mathematics is not needed and why to know it at all, it became a little clearer which tasks can be solved simply and elegantly with the help of it. But it was a simple option that every game developer should know is not a trainee. Raise the bar - let's talk about the integrals. 3r3407.  3r33420. 3r3407.  3r33420. Integrals 3r3407.  3r33420. 3r3407.  3r33420. In general, the integrals have many applications, such as: physical simulations, VFX, analytics, and much more. I am not ready to describe everything now in detail. I want to describe simple and visually understandable. Let's talk about physics. 3r3407.  3r33420. Suppose there is a task - to move an object to a certain point. For example, so that when entering a certain trigger, books should fly from the shelves. If you want to move evenly and without physics, then the task is trivial and does not require integrals, but when the book pushes a ghost from the shelf, such a distribution of speed will look completely wrong. 3r3407.  3r33420. 3r3407.  3r33420. What is the integral? 3r3407.  3r33420. 3r3407.  3r33420. In essence, this is the area under the curve. But what does this mean in the context of physics? Suppose you have a speed distribution over time. In this case, the area under the curve is the path that the object will take, and this is exactly what we need. 3r3407.  3r33420. 3r3407.  3r33420. class MoveObj [/b]
3r33333. 3r33333. using System.Collections; 3r33420. using UnityEngine; 3r33420. 3r33420.[RequireComponent(typeof(Rigidbody))]3r33420. public class MoveObject: MonoBehaviour
{
[SerializeField]private Transform _Target; 3r33420. 3r33420.[SerializeField]private GraphData _Data; 3r33420. 3r33420. private Rigidbody _Rigidbody; 3r33420. private void Start ()
{
_Rigidbody = GetComponent
(); 3r33420. Move (2f, _Data.AnimationCurve); 3r33420.}
3r33420. public void Move (float time, AnimationCurve speedLaw)
{
StartCoroutine (MovingCoroutine (time, speedLaw)); 3r33420.}
3r33420. private IEnumerator MovingCoroutine (float time, AnimationCurve speedLaw)
{
float timer = 0; 3r33420. var dv = (_Target.position - transform.position); 3r33420. var distance = dv.magnitude; 3r33420. var direction = dv.normalized; 3r33420. var speedK = distance /(Utils.GetApproxSquareAnimCurve (speedLaw) * time); 3r33420. 3r33420. while (timer < time)
{
_Rigidbody.velocity = speedLaw.Evaluate (timer /time) * direction * speedK;
yield return waitForFixedUpdate ();
timer + = Time.fixedDeltaTime;
rr3r3420. rr3r3420. 3r3204. isKinematic = true;
}
}
3r33420. 3r31616. 3r31616. 3r3407.  3r33420. 3r3407.  3r33420. The GetApproxSquareAnimCurve method is our integration. We make it the simplest numerical method, we simply go over the values ​​of the function and sum them a certain number of times. I set 1000 for loyalty, in general, you can choose the best. 3r3407.  3r33420. 3r33333. 3r33333. private const int Iterations = 1000; 3r33420. public static float GetApproxSquareAnimCurve (AnimationCurve curve)
{
float square = 0; 3r33420. for (int i = 0; i <= Iterations; i++)
{
square + = curve.Evaluate ((float) i /Iterations); 3rrh320.} 3rh32020. return square /Iterations; 3rh3207.} 3rrh5151. 3rr3352. 3r3407.  3r33420. 3r3407.  3r33420. Thanks to this area, we further know what the relative distance is. And further, comparing the two paths we have, we get the speed factor speedK, which is responsible for ensuring that we cover a given distance. 3r3407.  3r33420. 3r3407.  3r33420. 3r33385. 3r33386.
3r33333. Your browser does not support HTML5 video. 3r33333. 3r33333. 3r3333391. 3r31616. 3r31616. 3r31616. 3r3407.  3r33420. 3r33385. 3r33386.
3r33333. Your browser does not support HTML5 video.
3r33333. 3r3333391. 3r31616. 3r31616. 3r31616. 3r3407.  3r33420. 3r33385. 3r33386.
3r33333. Your browser does not support HTML5 video. 3r33333. 3r33333. 3r3333391. 3r31616. 3r31616. 3r31616. 3r3407.  3r33420. 3r3407.  3r33420. You may notice that the objects do not exactly match, this is due to the float error. In general, you can recalculate the same in decimal, and then overtake in float for greater accuracy. 3r3407.  3r33420. 3r3407.  3r33420. Actually on this for today. As always at the end of
link to github project 3r3404. in which all the sources for this article. And you can play with them. 3r3407.  3r33420. 3r3407.  3r33420. If the article goes down - I will do a continuation in which I’ll tell you about the use of slightly more complex concepts, such as complex numbers, fields, groups, and more. 3r31616. 3r33420. 3r33420. 3r33420. 3r33434. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script"); e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r33414. 3r33420. 3r31616. 3r33420. 3r33420. 3r33420. 3r33420.
+ 0 -

• • • 