Halo semuanya! Hari ini saya ingin berbicara tentang matematika. Matematika adalah ilmu yang sangat menarik dan bisa sangat berguna ketika mengembangkan game, dan secara umum ketika bekerja dengan grafik komputer. Banyak (terutama pemula) tidak tahu bagaimana ini digunakan dalam pengembangan. Ada banyak masalah yang tidak memerlukan pemahaman yang mendalam tentang konsep-konsep seperti: integral, bilangan kompleks, kelompok, cincin, dll., Tetapi berkat matematika Anda dapat memecahkan banyak masalah menarik. Dalam artikel ini, kami mempertimbangkan vektor dan integral. Jika tertarik, selamat datang di kucing. Proyek Illustrating Unity, seperti biasa, terpasang.
Vektor matematika.Vektor dan matematika vektor adalah alat penting untuk pengembangan game. Banyak operasi dan tindakan terkait sepenuhnya. Sangat lucu bahwa untuk mengimplementasikan kelas yang menampilkan panah vektor di Unity, sudah diambil sebagian besar operasi khas. Jika Anda berpengalaman dalam matematika vektor, blok ini tidak akan menarik bagi Anda.
Aritmatika vektor dan fungsi yang bergunaRumus analitik dan detail lainnya mudah di google, jadi kami tidak akan membuang waktu untuk ini. Operasi itu sendiri akan diilustrasikan oleh animasi gif di bawah ini.
Penting untuk dipahami bahwa setiap titik pada intinya adalah vektor dengan titik awal di titik nol.Gif dibuat menggunakan Unity, jadi akan perlu untuk mengimplementasikan kelas yang bertanggung jawab untuk merender panah. Panah vektor terdiri dari tiga komponen utama - satu baris, satu ujung, dan teks dengan nama satu vektor. Untuk menggambar garis dan tip, saya menggunakan LineRenderer. Mari kita lihat kelas vektor itu sendiri:
Kelas panahusing System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; public class VectorArrow : MonoBehaviour { [SerializeField] private Vector3 _VectorStart; [SerializeField] private Vector3 _VectorEnd; [SerializeField] private float TextOffsetY; [SerializeField] private TMP_Text _Label; [SerializeField] private Color _Color; [SerializeField] private LineRenderer _Line; [SerializeField] private float _CupLength; [SerializeField] private LineRenderer _Cup; private void OnValidate() { UpdateVector(); } private void UpdateVector() { if(_Line == null || _Cup == null) return; SetColor(_Color); _Line.positionCount = _Cup.positionCount = 2; _Line.SetPosition(0, _VectorStart); _Line.SetPosition(1, _VectorEnd - (_VectorEnd - _VectorStart).normalized * _CupLength); _Cup.SetPosition(0, _VectorEnd - (_VectorEnd - _VectorStart).normalized * _CupLength); _Cup.SetPosition(1, _VectorEnd ); if (_Label != null) { var dv = _VectorEnd - _VectorStart; var normal = new Vector3(-dv.y, dv.x).normalized; normal = normal.y > 0 ? normal : -normal; _Label.transform.localPosition = (_VectorEnd + _VectorStart) / 2 + normal * TextOffsetY; _Label.transform.up = normal; } } public void SetPositions(Vector3 start, Vector3 end) { _VectorStart = start; _VectorEnd = end; UpdateVector(); } public void SetLabel(string label) { _Label.text = label; } public void SetColor(Color color) { _Color = color; _Line.startColor = _Line.endColor = _Cup.startColor = _Cup.endColor = _Color; } }
Karena kami ingin vektor memiliki panjang tertentu dan persis sesuai dengan titik yang kami tentukan, panjang garis dihitung dengan rumus:
_VectorEnd - (_VectorEnd - _VectorStart).normalized * _CupLength
Dalam rumus ini
(_VectorEnd - _VectorStart) .normalisasi adalah arah vektor. Ini dapat dipahami dari animasi dengan perbedaan vektor, dengan anggapan bahwa
_VectorEnd dan
_VectorStart adalah vektor dengan start di (0,0,0).
Selanjutnya, kami menganalisis dua operasi dasar yang tersisa:
Menemukan yang normal (tegak lurus) dan tengah vektor adalah tugas yang sangat umum ketika mengembangkan game. Mari kita menganalisisnya dengan contoh menempatkan tanda tangan pada vektor.
var dv = _VectorEnd - _VectorStart; var normal = new Vector3(-dv.y, dv.x).normalized; normal = normal.y > 0 ? normal : -normal; _Label.transform.localPosition = (_VectorEnd + _VectorStart) / 2 + normal * TextOffsetY; _Label.transform.up = normal;
Untuk menempatkan teks tegak lurus ke vektor, kita perlu yang normal. Dalam grafik 2D, normalnya cukup sederhana.
var dv = _VectorEnd - _VectorStart; var normal = new Vector3(-dv.y, dv.x).normalized;
Jadi kami mendapat yang normal untuk segmen.
normal = normal.y> 0? normal: -normal; - operasi ini bertanggung jawab untuk memastikan bahwa teks selalu ditampilkan di atas vektor.
Kemudian tetap menempatkannya di tengah-tengah vektor dan menaikkannya normal ke jarak yang akan terlihat indah.
_Label.transform.localPosition = (_VectorEnd + _VectorStart) / 2 + normal * TextOffsetY;
Kode menggunakan posisi lokal sehingga Anda dapat memindahkan panah yang dihasilkan.
Tapi itu tentang 2D, tapi bagaimana dengan 3D?
Dalam 3D, plus atau minus adalah sama. Hanya rumus normal yang berbeda, karena normal sudah dibawa bukan ke segmen, tetapi ke bidang.
Script untuk kamera using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class SphereCameraController : MonoBehaviour { [SerializeField] private Camera _Camera; [SerializeField] private float _DistanceFromPlanet = 10; [SerializeField] private float _Offset = 5; private bool _IsMoving; public event Action<Vector3, Vector3, Vector3, float, float> OnMove; private void Update() { if (Input.GetMouseButtonDown(0) && !_IsMoving) { RaycastHit hit; Debug.Log("Click"); var ray = _Camera.ScreenPointToRay(Input.mousePosition); if(Physics.Raycast(ray, out hit)) { Debug.Log("hit"); var startPosition = _Camera.transform.position; var right = Vector3.Cross(hit.normal, Vector3.up).normalized; var endPosition = hit.point + hit.normal * _DistanceFromPlanet + right * _Offset; StartCoroutine(MoveCoroutine(startPosition, endPosition, hit.point + right * _Offset)); OnMove?.Invoke(startPosition, hit.point, hit.normal, _DistanceFromPlanet, _Offset); } } } private IEnumerator MoveCoroutine(Vector3 start, Vector3 end, Vector3 lookAt) { _IsMoving = true; var startForward = transform.forward; float timer = 0; while (timer < Scenario.AnimTime) { transform.position = Vector3.Slerp(start, end, timer / Scenario.AnimTime); transform.forward = Vector3.Slerp(startForward, (lookAt - transform.position).normalized, timer / Scenario.AnimTime); yield return null; timer += Time.deltaTime; } transform.position = end; transform.forward = (lookAt - transform.position).normalized; _IsMoving = false; } }
Dalam contoh kontrol ini, normal ke pesawat digunakan untuk menggeser titik akhir lintasan ke kanan sehingga antarmuka tidak menghalangi planet. Normal dalam grafik 3D adalah produk vektor yang dinormalisasi dari dua vektor. Yang nyaman, di Unity ada kedua operasi ini dan kami mendapatkan catatan ringkas yang indah:
var right = Vector3.Cross(hit.normal, Vector3.up).normalized;
Saya pikir bagi banyak orang yang berpikir bahwa matematika tidak diperlukan dan mengapa Anda perlu mengetahuinya sama sekali, itu menjadi sedikit lebih jelas masalah apa yang dapat diselesaikan dengan sederhana dan elegan. Tapi itu adalah opsi sederhana yang tidak boleh diketahui oleh setiap pengembang game sebagai magang. Angkat bilah - bicara tentang integral.
IntegralSecara umum, integral memiliki banyak aplikasi, seperti: simulasi fisik, VFX, analytics, dan banyak lagi. Saya belum siap untuk menggambarkan semuanya secara rinci sekarang. Saya ingin menggambarkan yang sederhana dan dapat dipahami secara visual. Mari kita bicara tentang fisika.
Misalkan ada tugas - untuk memindahkan objek ke titik tertentu. Misalnya, ketika memasuki pemicu tertentu, buku-buku dari rak harus terbang keluar. Jika Anda ingin bergerak secara seragam dan tanpa fisika, maka tugas itu sepele dan tidak memerlukan integral, tetapi ketika hantu mendorong buku dari rak, distribusi kecepatan seperti itu akan terlihat sangat berbeda.
Apa itu integral?
Bahkan, ini adalah area di bawah kurva. Tetapi apa artinya ini dalam konteks fisika? Misalkan Anda memiliki distribusi kecepatan dari waktu ke waktu. Dalam hal ini, area di bawah kurva adalah jalur yang akan dilalui objek, dan inilah yang kita butuhkan.

Pindah dari teori ke praktik, Unity memiliki alat hebat yang disebut AnimationCurve. Dengan menggunakannya, Anda dapat menentukan distribusi kecepatan dari waktu ke waktu. Mari kita buat kelas seperti itu.
kelas MoveObj using System.Collections; using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class MoveObject : MonoBehaviour { [SerializeField] private Transform _Target; [SerializeField] private GraphData _Data; private Rigidbody _Rigidbody; private void Start() { _Rigidbody = GetComponent<Rigidbody>(); Move(2f, _Data.AnimationCurve); } public void Move(float time, AnimationCurve speedLaw) { StartCoroutine(MovingCoroutine(time, speedLaw)); } private IEnumerator MovingCoroutine(float time, AnimationCurve speedLaw) { float timer = 0; var dv = (_Target.position - transform.position); var distance = dv.magnitude; var direction = dv.normalized; var speedK = distance / (Utils.GetApproxSquareAnimCurve(speedLaw) * time); while (timer < time) { _Rigidbody.velocity = speedLaw.Evaluate(timer / time) * direction * speedK; yield return new WaitForFixedUpdate(); timer += Time.fixedDeltaTime; } _Rigidbody.isKinematic = true; } }
Metode GetApproxSquareAnimCurve adalah integrasi kami. Kami menjadikannya metode numerik paling sederhana, cukup periksa nilai-nilai fungsi dan jumlahkannya beberapa kali. Saya menetapkan 1000 untuk kesetiaan, secara umum, Anda dapat memilih yang terbaik.
private const int Iterations = 1000; public static float GetApproxSquareAnimCurve(AnimationCurve curve) { float square = 0; for (int i = 0; i <= Iterations; i++) { square += curve.Evaluate((float) i / Iterations); } return square / Iterations; }
Berkat area ini, kita sudah tahu apa jarak relatifnya. Dan kemudian membandingkan dua jalur yang dilalui, kita mendapatkan koefisien kecepatan speedK, yang bertanggung jawab untuk memastikan bahwa kita berjalan pada jarak yang diberikan.
Anda mungkin memperhatikan bahwa objek tidak persis cocok, ini disebabkan oleh kesalahan float. Secara umum, Anda dapat menghitung ulang yang sama dalam desimal, dan kemudian menyusul di float untuk akurasi yang lebih besar.
Sebenarnya itu saja untuk hari ini. Seperti biasa, pada akhirnya ada
tautan ke proyek GitHub , di mana semua sumber untuk artikel ini. Dan Anda bisa bermain dengan mereka.
Jika artikelnya masuk, saya akan melakukan sekuel di mana saya akan memberi tahu Anda tentang penggunaan konsep yang sedikit lebih rumit, seperti bilangan kompleks, bidang, grup, dan banyak lagi.