Gerakan memutar pada perangkat keras bertenaga rendah

10

Saya sedang memikirkan platform dan musuh yang bergerak berputar-putar di game 2D lama, dan saya bertanya-tanya bagaimana itu dilakukan. Saya mengerti persamaan parametrik, dan itu sepele untuk menggunakan dosa dan cos untuk melakukannya, tetapi bisakah NES atau SNES membuat panggilan waktu nyata? Saya mengakui ketidaktahuan yang berat, tetapi saya pikir itu adalah operasi yang mahal. Apakah ada cara pintar untuk menghitung gerakan itu dengan lebih murah?

Saya telah bekerja untuk mendapatkan algoritme dari identitas jumlah trigonometri yang hanya akan menggunakan trigon prakiraan, tetapi tampaknya berbelit-belit.

akroy
sumber
1
Saya sebenarnya ditanya pertanyaan ini selama wawancara kerja beberapa tahun yang lalu.
Crashworks

Jawaban:

14

Pada perangkat keras seperti yang Anda gambarkan, solusi umum untuk kasus umum adalah dengan hanya menghasilkan tabel pencarian untuk fungsi trigonometri yang diminati, kadang-kadang bersamaan dengan representasi titik tetap untuk nilai.

Masalah potensial dengan teknik ini adalah ia menghabiskan ruang memori, meskipun Anda bisa mengecilkan ini dengan menetapkan resolusi data yang lebih rendah di tabel Anda atau dengan mengambil keuntungan dari sifat periodik dari beberapa fungsi untuk menyimpan lebih sedikit data dan mencerminkannya saat runtime.

Namun, untuk secara khusus melintasi lingkaran - baik untuk meraster mereka atau untuk memindahkan sesuatu, variasi algoritma garis Bresenham dapat digunakan . Algoritma Bresenham yang sebenarnya , tentu saja, juga berguna untuk melintasi garis yang tidak berada di delapan arah "primer" dengan cukup murah.


sumber
2
Kisah nyata. LUT dan lingkaran didefinisikan sebagai 256 derajat menghasilkan trigonometri murah, mirroring hanya dilakukan jika ingatannya ketat dan sebagai upaya terakhir untuk mendapatkan beberapa byte. Referensi Bresenham juga tepat untuk gerakan yang berbeda.
Patrick Hughes
4
Bahkan pada perangkat keras modern, panggilan trigonometri masih berupa tabel pencarian. Ini hanya tabel pencarian dalam perangkat keras, dengan beberapa perbaikan melalui ekspansi Taylor. (Sebenarnya implementasi satu produsen konsol utama dari fungsi SIMD sin () hanyalah serangkaian hardcoded Taylor.)
Crashworks
3
@ Crashworks: sama sekali tidak mungkin itu adalah seri Taylor, itu akan sangat bodoh dari mereka. Kemungkinan besar polinomial minimum. Sebenarnya, semua implementasi modern dari dosa () yang pernah saya lihat didasarkan pada polinomial minimum.
sam hocevar
@ SamHocevar Bisa jadi. Saya baru saja melihat penjumlahan dari kapak + bx ^ 3 + cx ^ 5 + ... dan menganggap "seri Taylor".
Crashworks
9

Ada variasi dari algoritma Bresenham oleh James Frith , yang seharusnya lebih cepat karena sepenuhnya menghilangkan multiplikasi. Tidak perlu tabel pencarian untuk mencapai ini, meskipun orang bisa menyimpan hasilnya dalam tabel jika radius tetap konstan. Karena algoritma Bresenham dan Frith menggunakan simetri 8 kali lipat, tabel pencarian ini akan relatif pendek.

// FCircle.c - Draws a circle using Frith's algorithm.
// Copyright (c) 1996  James E. Frith - All Rights Reserved.
// Email:  [email protected]

typedef unsigned char   uchar;
typedef unsigned int    uint;

extern void SetPixel(uint x, uint y, uchar color);

// FCircle --------------------------------------------
// Draws a circle using Frith's Algorithm.

void FCircle(int x, int y, int radius, uchar color)
{
  int balance, xoff, yoff;

  xoff = 0;
  yoff = radius;
  balance = -radius;

  do {
    SetPixel(x+xoff, y+yoff, color);
    SetPixel(x-xoff, y+yoff, color);
    SetPixel(x-xoff, y-yoff, color);
    SetPixel(x+xoff, y-yoff, color);
    SetPixel(x+yoff, y+xoff, color);
    SetPixel(x-yoff, y+xoff, color);
    SetPixel(x-yoff, y-xoff, color);
    SetPixel(x+yoff, y-xoff, color);

    balance += xoff++;
    if ((balance += xoff) >= 0)
        balance -= --yoff * 2;

  } while (xoff <= yoff);
} // FCircle //
NabiV
sumber
Jika Anda mendapatkan hasil yang aneh, itu karena Anda menerapkan perilaku yang tidak ditentukan (atau setidaknya tidak ditentukan) . C ++ tidak menentukan panggilan mana yang dievaluasi terlebih dahulu ketika mengevaluasi "a () + b ()", dan selanjutnya memanggil memodifikasi integral. Untuk menghindari ini, jangan modifikasi variabel dalam ekspresi yang sama dengan yang Anda baca seperti pada xoff++ + xoffdan --yoff + yoff. Daftar perubahan Anda akan memperbaikinya, pertimbangkan untuk memperbaikinya dan bukan sebagai catatan. (Lihat bagian 5 paragraf 4 standar C ++ untuk contoh-contoh dan orang-orang standard yang secara eksplisit menyebut ini)
MaulingMonkey
@MaulingMonkey: Anda benar tentang urutan evaluasi balance += xoff++ + xoffdan balance -= --yoff + yoff. Saya meninggalkan ini tidak berubah, karena ini adalah cara algoritma Frith awalnya ditulis, dengan perbaikan yang kemudian ditambahkan sendiri (lihat di sini ). Diperbaiki sekarang
ProphetV
2

Anda juga dapat menggunakan versi perkiraan fungsi trigonometri menggunakan Taylor Expansions http://en.wikipedia.org/wiki/Taylor_series

Sebagai contoh, Anda dapat memiliki perkiraan yang wajar tentang sinus menggunakan empat istilah seri taylor pertama

sinus

Komunitas
sumber
Ini umumnya benar, tetapi datang dengan begitu banyak peringatan sehingga saya akan mengatakan bahwa Anda seharusnya tidak pernah menulis kode dosa Anda sendiri () kecuali Anda sangat memahami apa yang Anda lakukan. Secara khusus, ada polinomial (sedikit) lebih baik daripada yang terdaftar, bahkan perkiraan rasional yang lebih baik, dan Anda perlu memahami di mana menerapkan formula dan bagaimana menggunakan periodisitas dosa dan cos untuk mempersempit argumen Anda ke kisaran di mana seri berlaku. Ini adalah salah satu kasus di mana pepatah lama 'sedikit pengetahuan adalah hal yang berbahaya' berdering benar.
Steven Stadnicki
Bisakah Anda memberikan beberapa referensi agar saya dapat mempelajari polinomial ini atau perkiraan lain yang lebih baik? Saya benar-benar ingin mempelajarinya. Hal seri ini adalah bagian yang paling mengejutkan dari kursus kalkulus saya.
Tempat klasik untuk memulai adalah buku Numerical Recipes, yang memberikan sedikit informasi tentang menghitung fungsi numerik inti dan matematika di belakang perkiraan mereka. Tempat lain yang mungkin Anda cari, untuk pendekatan yang agak ketinggalan jaman tetapi masih perlu diketahui, adalah untuk mencari apa yang disebut algoritma CORDIC .
Steven Stadnicki
@Vandell: jika Anda ingin membuat polinomial minimum, saya akan senang mendengar pendapat Anda tentang LolRemez .
sam hocevar
Seri Taylor mendekati perilaku fungsi di sekitar satu titik, bukan pada interval. Polinomial sangat bagus untuk mengevaluasi dosa (0) atau turunan ketujuh di sekitar x = 0, tetapi kesalahan di x = pi / 2, setelah itu Anda dapat hanya cermin dan ulangi, cukup besar. Anda dapat melakukan sekitar lima puluh kali lebih baik dengan mengevaluasi seri Taylor sekitar x = pi / 4 sebagai gantinya, tetapi apa yang Anda inginkan adalah polinomial yang meminimalkan kesalahan maksimum pada interval, dengan biaya presisi di dekat satu titik.
Marcks Thomas
2

Salah satu algoritma yang luar biasa untuk melakukan perjalanan secara seragam di atas lingkaran adalah algoritma Goertzel . Ini hanya membutuhkan 2 perkalian dan 2 penambahan per langkah, tidak ada tabel pencarian, dan status yang sangat minimal (4 angka).

Pertama, tentukan beberapa konstanta, mungkin hardcoded, berdasarkan ukuran langkah yang diperlukan (dalam hal ini, 2π / 64):

float const step = 2.f * M_PI / 64;
float const s = sin(step);
float const c = cos(step);
float const m = 2.f * c;

Algoritma menggunakan 4 angka sebagai statusnya, diinisialisasi seperti ini:

float t[4] = { s, c, 2.f * s * c, 1.f - 2.f * s * s };

Dan akhirnya loop utama:

for (int i = 0; ; i++)
{
    float x = m * t[2] - t[0];
    float y = m * t[3] - t[1];
    t[0] = t[2]; t[1] = t[3]; t[2] = x; t[3] = y;
    printf("%f %f\n", x, y);
}

Kemudian bisa pergi selamanya. Inilah 50 poin pertama:

Algoritma Goertzel

Algoritme tentu saja dapat bekerja pada perangkat keras titik tetap. Kemenangan yang jelas melawan Bresenham adalah kecepatan konstan di atas lingkaran.

sam hocevar
sumber