Saya mencoba menerapkan semacam fisika ruang palsu dalam game 2D saya. Saya memiliki pandangan top down dari kapal ruang angkasa saya. Anda dapat mengubah arah dan mengatur kecepatan hingga maksimum yang kemudian mempercepat kapal ke arah itu sesuai dengan jumlah akselerasi mesin kapal.
Saya memiliki kode yang berfungsi dengan baik agar kapal perlahan mulai bergerak ke arah itu dan meningkatkan kecepatan hingga kecepatan maks terpenuhi.
Memperbarui
Walaupun jawabannya sedikit membantu, itu tidak membawa saya ke solusi akhir saya. Sepertinya saya tidak bisa mengubah teori menjadi kode kerja. Berikut beberapa parameter lainnya:
- Kami sedang bekerja dengan kisi 2D
- Kapal memiliki mesin tunggal di mana Anda dapat mengatur daya dari 0 hingga 1 untuk menunjukkan daya penuh.
- Mesinnya memiliki kecepatan maksimal
- Ada gesekan ruang palsu di mana jika Anda tidak lagi menerapkan daya ke kapal, pada akhirnya akan berhenti.
Masalah
Masalah yang saya alami adalah ketika saya mengubah arah. Jika saya bepergian dalam satu arah dengan kecepatan 300, kemudian mengubah arah ke arah yang berlawanan, saya sekarang langsung bepergian dengan kecepatan yang telah ditentukan alih-alih melambat, dan kembali ke kecepatan itu ke arah itu.
Keadaan yang diinginkan
Kode saat ini
public void Update(Consoles.Space space)
{
var GameTimeElapsedUpdate = (float)SadConsole.Engine.GameTimeElapsedUpdate;
Graphic.PositionOffset = viewPortMaster.Position;
// Update the engine
ShipDetails.Engine.Update();
// Degrade the current velocity with friction??
if (velocity.Length() < 0f)
{
var accelerationFrame = ShipDetails.Engine.GetAccelerationFrame();
if (velocity.X > 0)
velocity.X -= accelerationFrame;
else if (velocity.X < 0)
velocity.X += accelerationFrame;
if (velocity.Y > 0)
velocity.Y -= accelerationFrame;
else if (velocity.Y < 0)
velocity.Y += accelerationFrame;
}
// Handle any new course adjustments
if (IsTurnRightOn)
SetHeading(heading + (ShipDetails.TurningSpeedRight * GameTimeElapsedUpdate));
if (IsTurnLeftOn)
SetHeading(heading - (ShipDetails.TurningSpeedLeft * GameTimeElapsedUpdate));
// Handle any power changes
if (IsPowerIncreasing)
{
SetPower(ShipDetails.Engine.DesiredPower + (GameTimeElapsedUpdate * ((ShipDetails.Engine.MaxSpeed / Settings.SecondsForFullPowerAdjustment) / ShipDetails.Engine.MaxSpeed)));
if (ShipDetails.Engine.DesiredPower > 1.0d)
ShipDetails.Engine.DesiredPower = 1.0d;
}
if (IsPowerDecreasing)
{
SetPower(ShipDetails.Engine.DesiredPower - (GameTimeElapsedUpdate * ((ShipDetails.Engine.MaxSpeed / Settings.SecondsForFullPowerAdjustment) / ShipDetails.Engine.MaxSpeed)));
if (ShipDetails.Engine.DesiredPower < 0.0d)
ShipDetails.Engine.DesiredPower = 0.0d;
}
// Calculate new velocity based on heading and engine
// Are we changing direction?
if (vectorDirectionDesired != vectorDirection)
{
// I think this is wrong, I don't think this is how I'm supposed to do this. I don't really want to
// animate the heading change, which is what I think this is actually doing..
if (vectorDirectionDesired.X < vectorDirection.X)
vectorDirection.X = Math.Min(vectorDirection.X + (vectorDirectionDesired.X * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.X);
else if (vectorDirectionDesired.X > vectorDirection.X)
vectorDirection.X = Math.Max(vectorDirection.X + (vectorDirectionDesired.X * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.X);
if (vectorDirectionDesired.Y < vectorDirection.Y)
vectorDirection.Y = Math.Min(vectorDirection.Y + (vectorDirectionDesired.Y * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.Y);
else if (vectorDirectionDesired.Y > vectorDirection.Y)
vectorDirection.Y = Math.Max(vectorDirection.Y + (vectorDirectionDesired.Y * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.Y);
}
vectorDirection = vectorDirectionDesired;
if (ShipDetails.Engine.Power != 0)
{
var force = new Vector2(vectorDirection.X * (float)ShipDetails.Engine.Speed, vectorDirection.Y * (float)ShipDetails.Engine.Speed);
var acceleration = new Vector2(force.X / ShipDetails.Engine.Acceleration, force.Y / ShipDetails.Engine.Acceleration) * GameTimeElapsedUpdate;
velocity = new Vector2(velocity.X + acceleration.X, velocity.Y + acceleration.Y);
Point endingLocation;
endingLocation.X = (int)velocity.X;
endingLocation.Y = (int)velocity.Y;
velocity.X -= endingLocation.X;
velocity.Y -= endingLocation.Y;
MapPosition += endingLocation;
}
if (this == Settings.GameWorld.CurrentShip)
{
var debug = space.GetDebugLayer();
debug.Clear();
debug.Print(0 + space.ViewArea.X, 0 + space.ViewArea.Y, $"Ship: {MapPosition}");
debug.Print(0 + space.ViewArea.X, 1 + space.ViewArea.Y, $"Speed: {ShipDetails.Engine.Speed} Desired: {ShipDetails.Engine.DesiredPower}");
debug.Print(0 + space.ViewArea.X, 2 + space.ViewArea.Y, $"Heading: {heading} Adjusted: {adjustedHeading}");
debug.Print(0 + space.ViewArea.X, 3 + space.ViewArea.Y, $"Dir: {vectorDirection.X.ToString("0.00")}, {vectorDirection.Y.ToString("0.00")} DirDes: {vectorDirectionDesired.X.ToString("0.00")}, {vectorDirectionDesired.Y.ToString("0.00")}");
}
}
Kode Pengiriman Mesin
class ShipEngine
{
public int Acceleration;
public int AccelerationBonus;
public int MaxSpeed;
public int MaxAfterburner;
public int Speed { get { return (int)(Power * MaxSpeed); } }
// This is a 0-1 no power to full power rating where MaxSpeed is full power
public double DesiredPower { get { return desiredPower; } set { desiredPower = value; if (value != Power) isDesiredTriggered = true; } }
public double Power;
public bool IsAdjusting { get { return Speed != 0; } }
private double desiredPower;
private bool isDesiredTriggered;
public void Update()
{
if (DesiredPower != Power)
{
var GameTimeElapsedUpdate = (float)SadConsole.Engine.GameTimeElapsedUpdate;
var accelerationFrame = (((float)(Acceleration + AccelerationBonus) / Settings.SpeedSquareSecond) * GameTimeElapsedUpdate);
if (DesiredPower > Power)
{
Power += accelerationFrame;
if (Power > DesiredPower)
Power = DesiredPower;
}
else if (DesiredPower < Power)
{
Power -= accelerationFrame;
if (Power < DesiredPower)
Power = DesiredPower;
}
}
}
public float GetAccelerationFrame()
{
return (((float)Acceleration / Settings.SpeedSquareSecond) * (float)SadConsole.Engine.GameTimeElapsedUpdate);
}
}
Jawaban:
Saya tidak terbiasa dengan
xna
... tapi saya tahu matematika. Dan menerapkan fisika tanpa memahami matematika di baliknya seperti masuk ke politik tanpa tahu bagaimana berbohong. Jadi mari kita mulai!Pertama-tama, cara Anda menggerakkan kapal tidak berdasarkan fisika. Anda tidak ingin pemain mengubah posisi kapal secara langsung. Yang ingin Anda lakukan adalah membiarkan pemain menerapkan akselerasi ke kapal, lalu biarkan fisika menghitung kecepatan kapal , lalu biarkan dunia mengubah posisi kapal dengan kecepatan yang baru dihitung itu. Velocity adalah perbedaan posisi kapal dalam waktu. Jika bergerak 5 unit ke kanan dan 1 unit ke atas, itu bergerak dengan kecepatan
(5,-1)
. Akselerasi adalah perbedaan dalam kecepatan kapal - itu hanya mempengaruhi posisi kapal dengan mengubah kecepatannya. Jika kapal Anda pergi 2 unit ke kiri dan 1 unit ke bawah, berarti kecepatan(2,1)
, dan pemain mempercepatnya ke arah yang berlawanan, artinya(-2,-1)
), itu akan berhenti di tempat dengan unit waktu berikutnya (baik itu bingkai atau centang atau apa pun). Dengan kata lain, Anda perlu menambahkan vektor percepatan ke vektor kecepatan dan kemudian menghitung di mana kapal berikutnya.Vektor
Bayangkan panah yang dimulai di suatu tempat (asal), menunjuk ke suatu tempat (arah) dan memiliki panjang tertentu (besarnya). Sekarang gambarkan dengan dua nilai - seberapa banyak X dan seberapa banyak Y adalah akhir dari awalnya. Untuk penyederhanaan saya hanya akan berbicara tentang sumbu X yang berarti vektor Anda menunjuk pada sesuatu yang "banyak X" ke kanan (positif) atau ke kiri (negatif).
Kecepatan
Sekarang, bagaimana posisi kapal harus berubah di antara frame? Dengan vektor kecepatan. Mari kita asumsikan kapal Anda mulai dari lokasi (0,0) dengan kecepatan (12,0). Ini berarti akan mengubah posisinya sebagai berikut:
Percepatan
Bagaimana kita mengubah arah? Anda tidak ingin hanya mengubah kecepatan menjadi
(-12,0)
. Itu berarti kapal bergerak dari 100 parsec ke kanan hingga 100 parsec yang tersisa dalam satu "bingkai". Saya tidak ingin berada di kapal itu ketika itu terjadi. Sekali lagi, "panjang" vektor disebut "magnitudo" dan dalam hal kecepatan terjadi pada kecepatan. Jadi Anda ingin besarnya kecepatan (kecepatan kapal) perlahan-lahan turun ke 0 dan kemudian berakselerasi ke negatif 12 (yang berarti bergerak dalam arah yang berlawanan). Anda dapat melakukannya dengan menambahkan akselerasi ke kecepatan, mis. Akselerasi dari(-4,0)
, jadi sekarang kapal bergerak sebagai berikut (pemain menekan ke kiri pada "bingkai" ke-3 lalu melepaskannya pada tanggal 9):Jadi, Anda ingin menerapkan akselerasi
(4,0)
untuk membuat kapal secara bertahap mendapatkan kecepatan dalam arah X positif ketika pemain menekan panah kanan dan menerapkan akselerasi(-4,0)
ketika panah kiri ditekan. Jelas ketika tidak ada tombol yang ditekan Anda tidak menerapkan akselerasi yang berarti kapal menjaga kecepatannya (bergerak dengan kecepatan konstan dalam arah tertentu). Jika Anda ingin memperlambat secara bertahap ketika tidak ada tombol yang ditekan, tambahkan vektor lain, panggilDrag
dan berikan arah yang selalu berlawanan dengan kecepatan (yaitu menuju bagian belakang kapal) hingga besarnya kecepatan mencapai 0. Semoga Anda mendapatkan ide .Kode
Apa yang akan saya lakukan (kode semu, Anda harus memperbaikinya, menambahkan enkapsulasi, dll., Juga mengabaikan beberapa aspek, misalnya pergi diagonal sedikit lebih cepat daripada lurus ke kiri, kanan, atas atau bawah):
sumber
Untuk melakukan ini, Anda perlu mensimulasikan inersia. Ini adalah bagaimana saya akan merekomendasikan melakukannya:
sumber
if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)
Anda memiliki MaxSpeed di sana dua kali .. Juga,ThrustForward
menggunakanthis.Accel
tetapi komentar Anda mengatakan ini adalah akselerasi Max apakah itu benar juga?this.MaxSpeed
ada dua kali untuk mengoptimalkan kode.Vector2.Length()
membutuhkan waktu lebih lama untuk dihitung daripadaVector2.LengthSquared()
Pernyataan berikut jika melakukan hal yang sama tetapi tidak dioptimalkan dan lebih mudah dipahami:if (this.Vel.Length() > this.MaxSpeed)
Ok itu sebenarnya sangat sederhana untuk dicapai. Pertama-tama seperti yang Anda sebutkan, arah mesin Anda menggambarkan jalur gerakan. Ini membuatnya nyaman untuk digunakan.
Pertama-tama, selalu simpan vektor dari arah Anda bergerak.
Selanjutnya Anda harus memiliki vektor tampilan mesin Anda.
Jadi untuk saat ini ketika Anda mulai bergerak, katakanlah benar, arah dan tampilan vektor mesin mengarah ke kanan. Ketika Anda sekarang ingin mengubah let.s mengatakan ke atas (90 derajat), maka Anda cukup mengubah vektor mesin pencari.
Sekarang tiba bagian menyenangkan. Tentukan dengan fungsi apa pun seberapa kuat untuk mempengaruhi perubahan arah dan istirahat.
pertama-tama perubahan arah.
Tergantung pada Kecepatan Anda dan perubahan sudut Anda bisa memperlambat dan mengubah arah vektor.
Jika Anda ingin perubahan arah lengkap (180 derajat), maka itu matematika sederhana. Dalam pembaruan Anda cukup ubah kecepatan Anda secara perlahan. Ketika kecepatan berubah menjadi nol, balikkan vektor arah 180 derajat dan mulailah menambah kecepatan lagi.
Pada putaran 90 derajat, itu menjadi sedikit lebih rumit. Anda perlu mendefinisikan fungsi untuk menghitung berapa banyak kapal diizinkan untuk berubah sesuai dengan kecepatan dan jika itu akan memperlambat berapa banyak. Tetapi Anda dapat bermain dengan nilai-nilai sampai sesuai dengan yang Anda inginkan.
sumber