Круговий рух на малопотужному обладнання


10

Я думав про платформи та ворогів, що рухаються по колах у старих 2D іграх, і мені було цікаво, як це зробити. Я розумію параметричні рівняння, і це тривіально використовувати sin і cos, щоб це зробити, але чи могли би NES або SNES робити триггери в реальному часі? Я визнаю важке незнання, але я вважав, що це дорогі операції. Чи є якийсь розумний спосіб обчислити цей рух дешевше?

Я працював над тим, щоб вивести алгоритм із ідентифікацій сумарних сукупностей, який би використовував лише попередньо обчислений триггер, але це здається переплутаним.


1
Мені насправді було задано це питання під час співбесіди на роботі кілька років тому.
Crashworks

Відповіді:


14

На апаратних засобах, таких як ви описуєте, загальним рішенням загального випадку є просто створення оглядової таблиці для функцій тригонометрії, які були зацікавлені, іноді в поєднанні з фіксованими поданнями значень.

Потенційна проблема цієї техніки полягає в тому, що вона займає простір пам’яті, хоча ви можете применшити це, встановивши меншу роздільну здатність даних у вашій таблиці або скориставшись періодичним характером деяких функцій для зберігання менше даних та дзеркального відображення під час виконання.

Однак для конкретно проїжджаючих кіл - або для їх растерризації, або для переміщення чогось по одному, може бути використаний варіант алгоритму лінійки Брезена . Фактичний алгоритм Брезенхема , звичайно, також корисний для проїзду ліній, які не знаходяться у восьми "первинних" напрямках досить дешево.


2
Правдива історія. LUT, а коло визначається як 256 градусів, приносять дешевий триггер, дзеркальне відображення робилося лише в тому випадку, якщо пам'ять була тісною і в крайньому випадку, щоб отримати кілька байт. Посилання на Брезенхема також місце для різного руху.
Патрік Хьюз

4
Навіть на сучасному обладнанні триггерний виклик все ще є таблицею пошуку. Це просто таблиця пошуку апаратних засобів, з деяким доопрацюванням за допомогою розширення Taylor. (Насправді одна з головних виробників консолі реалізує функцію SIMD sin () - це просто тверда кодова серія Тейлора.)
Crashworks

3
@Crashworks: немає абсолютно ніякого способу, що це серія Тейлора, це було б справді нерозумно з них. Це, швидше за все, поліном мінімакс. Насправді всі сучасні втілення sin (), які я коли-небудь бачив, базуються на многочленах minimax.
sam hocevar

@SamHocevar Можливо. Я щойно побачив підведення підсумків ax + bx ^ 3 + cx ^ 5 + ... і припустив "ряд Тейлора".
Crashworks

9

Існує різновид алгоритму Брезенама від Джеймса Фріта , який повинен бути ще швидшим, оскільки він повністю виключає множення. Для цього йому не потрібна таблиця пошуку, хоча результати можна зберігати в таблиці, якщо радіус залишається постійним. Оскільки алгоритм Брезенама і Фріта використовують 8-кратну симетрію, ця таблиця пошуку була б відносно короткою.

// FCircle.c - Draws a circle using Frith's algorithm.
// Copyright (c) 1996  James E. Frith - All Rights Reserved.
// Email:  jfrith@compumedia.com

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 //

Якщо ви отримуєте дивні результати, це тому, що ви посилаєтесь на невизначене (або принаймні не визначене) поведінку . C ++ не вказує, який виклик оцінюється спочатку при оцінці "a () + b ()", а далі викликує модифікацію інтегралів. Щоб цього уникнути, не змінюйте змінну в тому ж виразі, який ви читали, як у xoff++ + xoffта --yoff + yoff. Ваш список змін виправить це, розглянемо виправлення на місці, а не як позначка на примітці. (Див. Розділ 5 пункту 4 стандарту C ++ для прикладів та стандартів, які це прямо вимагає)
MaulingMonkey

@MaulingMonkey: Ви маєте рацію щодо проблематичного порядку оцінювання balance += xoff++ + xoffта balance -= --yoff + yoff. Я залишив це незмінним, оскільки так було написано алгоритм Фріта спочатку, виправлення пізніше додав сам (див. Тут ). Виправлено зараз.
ПророкV

2

Ви також можете використовувати приблизну версію триггерних функцій, використовуючи розширення Taylor http://en.wikipedia.org/wiki/Taylor_series

Наприклад, ви можете мати розумне наближення синусу, використовуючи перші чотири терміни серії Тейлор

синус


Це, як правило, правда, але це так багато застережень, що я б сказав, що ви практично ніколи не повинні писати свій власний код greh (), якщо ви дуже не знайомі з тим, що робите. Зокрема, є (незначно) кращі поліноми, ніж перелічені, ще кращі раціональні наближення, і вам потрібно зрозуміти, де застосовувати формулу і як використовувати періодичність гріха і cos, щоб звузити аргумент до діапазону, де застосовується серія. Це один із тих випадків, коли старий афоризм "трохи знання - небезпечна річ" звучить справжньою.
Стівен Стадницький

Чи можете ви дати кілька посилань, щоб я міг вивчити цей многочлен чи інші кращі наближення? Я дуже хочу цього навчитися. Ця серіальна річ була найбільш розумною частиною мого курсу обчислення.

Класичне місце для початку - книга Числові рецепти, яка дає досить небагато інформації про обчислення основних чисельних функцій та математики за їх наближенням. Ще одне місце, на яке ви можете звернути увагу, для підходу, який трохи застарів, але про нього все-таки варто знати, - це пошук так званого алгоритму CORDIC .
Стівен Стадницький

@Vandell: якщо ви хочете створити поліноми minimax, я буду радий почути ваші думки про LolRemez .
sam hocevar

Серія Тейлора наближає поведінку функції навколо однієї точки, а не на інтервалі. Поліном чудово підходить для оцінки sin (0) або його сьомої похідної навколо x = 0, але похибка при x = pi / 2, після якої можна просто віддзеркалити та повторити, досить велика. Можна зробити приблизно в п’ятдесят разів краще, замість цього оцінивши серію Тейлора навколо x = pi / 4, але те, що ви дійсно хочете, - це многочлен, який мінімізує максимальну помилку на інтервалі за рахунок точності біля однієї точки.
Маркс Томас

2

Один з дивовижних алгоритмів для рівномірного переміщення по колу - це алгоритм Ґерцеля . Для цього потрібно лише 2 множення і 2 доповнення за крок, відсутність таблиці пошуку і дуже мінімальний стан (4 числа).

Спочатку визначте деякі константи, можливо, жорстко закодовані, виходячи з необхідного розміру кроку (у цьому випадку 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;

Алгоритм використовує 4 числа як свій стан, ініціалізований так:

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

І нарешті головний цикл:

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);
}

Тоді це може піти назавжди. Ось перші 50 пунктів:

Алгоритм Ґерцеля

Алгоритм може, звичайно, працювати на апаратному забезпеченні з фіксованою точкою. Ясна перемога проти Брезенхема - постійна швидкість по колу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.