Tamsayılı olmayan hız değerleri - bunu yapmanın daha temiz bir yolu var mı?


21

Genellikle karakterimi piksel tabanlı bir oyunda taşımak için 2.5 gibi bir hız değeri kullanmak isteyeceğim. Olsa da, çarpışma tespiti genellikle daha zor olacaktır. Sonuçta böyle bir şey yapıyorum:

moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
    moveX(1);
}

Bunu her yazmak zorunda kaldığımda içini sıkıyorum, tam sayı olmayan hız değerleri olan bir karakteri hareket ettirmek için daha temiz bir yol var mı yoksa sonsuza dek bunu yaparken sıkışıp kalacağım mı?


11
Tamsayı ünitenizi daha küçük hale getirmeyi düşündünüz mü (örneğin, ekran ünitesinin 1 / 10'u) sonra 2.5, 25'e çevirir ve yine de tüm kontroller için tamsayı olarak kabul edebilir ve her kareye tutarlı şekilde davranabilirsiniz.
DMGregory

6
Yalnızca tamsayılar kullanılarak uygulanabilecek Bresenham hat algoritmasını benimsemeyi düşünebilirsiniz .
n0rd

1
Bu yaygın olarak eski 8bit konsollarda yapılır. Alt
Lucas

Yanıtlar:


13

Bresenham

Eski zamanlarda insanlar hala çizgi ve daire çizmek için kendi temel video rutinlerini yazarken, bunun için Bresenham çizgi algoritmasını kullanmaktan hiç hoşlanmadılar.

Bresenham bu sorunu çözüyor: Ekranda dxpikselleri yatay yönde hareket ettiren , aynı zamanda dydikey yönde pikselleri kapsayan bir çizgi çizmek istiyorsunuz . Satırlara doğal bir "floaty" karakteri vardır; Tamsayılı pikselleriniz olsa bile, rasyonel eğilimler ile sonuçlanır.

Algoritma hızlı olmasına rağmen, bu sadece tamsayı aritmetiğini kullanabileceği anlamına gelir; ve ayrıca herhangi bir çarpma veya bölme olmadan, sadece toplama ve çıkarma işleminden kurtulur.

Bunu davanıza uyarlayabilirsiniz:

  • "X yönü" (Bresenham algoritması açısından) saatinizdir.
  • "Y yönünüz", artırmak istediğiniz değerdir (yani, karakterinizin konumu - dikkatli olun, bu aslında sizin sprite "y" ya da ekrandaki herhangi bir şey değil, daha soyut bir değerdir)

Burada "x / y", ekrandaki konum değil, zamanın boyutlarından birinin değeri. Açıkçası, sprite ekranınızda isteğe bağlı bir yönde çalışıyorsa, ayrı ayrı çalışan 2B, 2B ve 3B olmak üzere birden fazla Bresenham'ınız olacaktır.

Örnek

Diyelim ki karakterinizi eksenlerinizden biri boyunca 0 - 25 arasında basit bir hareketle hareket ettirmek istediğinizi varsayalım. Hız 2.5 ile hareket ettiğinden, şasiye 10 da ulaşacaktır.

Bu, (0,0) ile (10,25) arası bir çizgi çizme ile aynıdır. Bresenham'ın çizgi algoritmasını alın ve çalışmasına izin verin. Doğru yaparsanız (ve onu incelerken, nasıl doğru bir şekilde yaptığınız çok çabuk anlaşılacaktır), o zaman sizin için 11 "puan" üretecektir (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).

Adaptasyonla ilgili ipuçları

Bu algoritmayı googleda tutar ve bir kod bulursanız (Wikipedia'da oldukça büyük bir anlaşma vardır), dikkat etmeniz gereken bazı şeyler vardır:

  • Açıkçası her türlü dxve çalışır dy. Ancak belirli bir vakaya ilgi duyuyorsunuz (yani asla sahip olmayacaksınız dx=0).
  • Her zamanki uygulama bağlı olarak ekranda çeyrek daire için birkaç farklı durumlarda sahip olacak dxve dyolumlu, olumsuz, ve ayrıca olsun abs(dx)>abs(dy)veya olmasın. Elbette burada ihtiyacınız olanı da seçebilirsiniz. Özellikle 1her kene tarafından arttırılan yönün daima “saat” yönünüz olduğundan emin olmalısınız .

Bu basitleştirmeleri uygularsanız, sonuç gerçekten çok basit olacak ve tüm gerçeklerden tamamen kurtulacak.


1
Bu kabul edilen cevap olmalı. 80'lerde C64'te oyunlar ve 90'larda PC'lerde fraktallar programladıktan sonra, kaçınabileceğim her yerde kayan nokta kullanmaya devam ediyorum. Veya elbette, FPU'ların günümüz işlemcilerinde her yerde yaygın olarak bulunmasıyla, performans argümanı çoğunlukla tartışmasızdır, ancak kayan nokta aritmetiğinin hala daha fazla transistöre ihtiyacı var, daha fazla güç çekiyor ve çoğu işlemci kullanımda değilken FPU'larını tamamen kapatacak. Bu nedenle kayan noktalardan kaçınmak, mobil kullanıcılarınızın pillerini bu kadar çabuk boş bırakmadıkları için teşekkür etmenizi sağlar.
Guntram Blohm,

@GuntramBlohm Kabul edilen cevap Sabit Nokta kullanıldığında da mükemmel çalışıyor, ki bunun için iyi bir yol. Sabit nokta sayıları hakkında ne düşünüyorsunuz?
leetNightshade

Bunu bulduktan sonra kabul edilen cevaba değiştirdim, bunu 8-bit ve 16-bit günlerde nasıl yaptıkları.
Akümülatör

26

Ne istersen onu yapmanın harika bir yolu var.

Bir floathıza ek olarak, gerçek hız ve yuvarlak hız floatarasında bir fark içerecek ve birikecek ikinci bir değişkene sahip olmanız gerekir . Bu fark daha sonra hızın kendisi ile birleştirilir.

#include <iostream>
#include <cmath>

int main()
{
    int pos = 10; 
    float vel = 0.3, vel_lag = 0;

    for (int i = 0; i < 20; i++)   
    {
        float real_vel = vel + vel_lag;
        int int_vel = std::lround(real_vel);
        vel_lag = real_vel - int_vel;

        std::cout << pos << ' ';
        pos += int_vel;
    }
}

Çıktı:

10 10 11 11 11 12 12 12 12 13 13 13 14 14 14 14 15 15 15 15 16


5
Neden bu çözümü hem hız hem de konum için şamandıra (veya sabit nokta) kullanmayı ve pozisyonu sadece en sondaki tam sayılara yuvarlamayı tercih ediyorsun?
CodesInChaos,

@CodesInChaos Benim çözümümü bunun yerine tercih etmiyorum. Bu cevabı yazarken, bilmiyordum.
HolyBlackCat

16

Hareket için değişken değerleri, çarpışma ve görüntü oluşturma için tam sayı değerlerini kullanın.

İşte bir örnek:

class Character {
    float position;
public:
    void move(float delta) {
        this->position += delta;
    }
    int getPosition() const {
        return lround(this->position);
    }
};

Hareket move()ederken kesirli pozisyonları biriktiren kullanırsınız . Ancak, çarpışma ve görüntü oluşturma getPosition()işlevi kullanarak ayrılmaz konumlarla başa çıkabilir .


Ağa bağlı bir oyun olması durumunda, dünya simülasyonu için kayan nokta türlerinin kullanılması zor olabilir. Bkz. Örneğin gafferongames.com/networking-for-game-programmers/… .
liori

@liori Şamandıra yerine bırakma yerine koyma gibi davranan sabit bir puan sınıfınız varsa, bu çoğunlukla bu sorunları çözmez mi?
leetNightshade

@ leetNightshade: uygulamaya bağlıdır.
liori

1
Meselenin uygulamada olmadığı, modern ağ oyunu çalıştırabilen hangi donanımın IEEE 754 yüzerinde olmadığını söyleyebilirim ???
Sopel 19
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.