การเคลื่อนที่แบบวงกลมบนฮาร์ดแวร์ที่ใช้พลังงานต่ำ


10

ฉันกำลังคิดเกี่ยวกับแพลตฟอร์มและศัตรูที่เคลื่อนที่เป็นวงกลมในเกม 2D เก่าและฉันก็สงสัยว่ามันทำอย่างไร ฉันเข้าใจสมการแบบพารามิเตอร์และเป็นเรื่องง่ายที่จะใช้บาปและ cos เพื่อทำสิ่งนั้น แต่ NES หรือ SNES สามารถโทรหาแบบเรียลไทม์ได้หรือไม่ ฉันยอมรับความโง่เขลาอย่างหนัก แต่ฉันคิดว่าสิ่งเหล่านั้นเป็นการปฏิบัติการที่มีราคาแพง มีวิธีที่ฉลาดกว่าในการคำนวณการเคลื่อนไหวนั้นถูกกว่าหรือไม่?

ฉันพยายามหาอัลกอริธึมจากอัตลักษณ์รวมของตรีโกณมิติที่จะใช้ตรีโกณมิติแบบ precalculated แต่ดูเหมือนว่าซับซ้อน


1
จริง ๆ แล้วฉันถูกถามคำถามนี้ในระหว่างการสัมภาษณ์งานเมื่อหลายปีก่อน
Crashworks

คำตอบ:


14

บนฮาร์ดแวร์เช่นที่คุณกำลังอธิบายวิธีแก้ปัญหาทั่วไปสำหรับกรณีทั่วไปคือเพียงแค่สร้างตารางการค้นหาสำหรับฟังก์ชันตรีโกณมิติที่เราสนใจบางครั้งร่วมกับการเป็นตัวแทนจุดคงที่สำหรับค่า

ปัญหาที่อาจเกิดขึ้นกับเทคนิคนี้คือมันใช้เนื้อที่หน่วยความจำถึงแม้ว่าคุณสามารถดาวน์ไลท์นี้ได้โดยการตั้งค่าความละเอียดของข้อมูลในตารางของคุณที่ต่ำกว่าหรือโดยการใช้ประโยชน์จากธรรมชาติของฟังก์ชั่นบางอย่างเพื่อเก็บข้อมูลน้อยลง

แต่สำหรับเฉพาะภายในวงกลม - ทั้งเพื่อ rasterize พวกเขาหรือจะย้ายสิ่งที่ตามหนึ่งการเปลี่ยนแปลงของอัลกอริทึมสาย Bresenham สามารถได้รับการว่าจ้าง แน่นอนว่าอัลกอริทึมที่แท้จริงของ Bresenhamนั้นมีประโยชน์สำหรับการสำรวจเส้นทางที่ไม่ได้อยู่ในทิศทางแปด "หลัก" ค่อนข้างถูก


2
เรื่องจริง. LUT และวงกลมถูกกำหนดให้เป็น 256 องศาให้ค่าของตรีโกณมิติราคาถูกการทำมิเรอร์ทำได้ก็ต่อเมื่อหน่วยความจำแน่นและเป็นทางเลือกสุดท้ายที่จะได้รับไม่กี่ไบต์ การอ้างอิงของ Bresenham ก็เป็นอีกจุดหนึ่งสำหรับการเคลื่อนไหวที่แตกต่างเช่นกัน
แพทริคฮิวจ์

4
แม้กระทั่งบนฮาร์ดแวร์ที่ทันสมัยการเรียกใช้ตรีโกณมิติยังคงเป็นตารางการค้นหา มันเป็นเพียงตารางการค้นหาในฮาร์ดแวร์ที่มีการปรับแต่งผ่านการขยายตัวของเทย์เลอร์ (ในความเป็นจริงแล้วผู้ผลิตคอนโซลรายใหญ่รายหนึ่งนำการทำงานของ SIMD sin () ไปใช้งานเป็นเพียงชุดฮาร์ดคอร์เทย์เลอร์)
Crashworks

3
@ Crashworks: มันไม่มีทางเป็นซีรีย์ Taylor เลยมันจะโง่จริงๆ มันอาจเป็นพหุนามน้อยที่สุด อันที่จริงการใช้งานที่ทันสมัยทั้งหมดของบาป () ที่ฉันเคยเห็นมานั้นขึ้นอยู่กับชื่อพหุนามน้อยที่สุด
sam hocevar

@ SamHocevar อาจเป็นได้ ฉันเพิ่งเห็นผลรวมของ ax + bx ^ 3 + cx ^ 5 + ... และสันนิษฐานว่า "Taylor series"
Crashworks

9

มีการเปลี่ยนแปลงของอัลกอริทึมของ BresenhamโดยJames Frithซึ่งน่าจะเร็วกว่านี้เพราะมันจะกำจัดการคูณอย่างสมบูรณ์ ไม่จำเป็นต้องใช้ตารางการค้นหาใด ๆ เพื่อให้บรรลุถึงสิ่งนี้แม้ว่าจะสามารถเก็บผลลัพธ์ไว้ในตารางได้หากรัศมียังคงที่ เนื่องจากทั้งอัลกอรึทึมของ Bresenham และ Frith ใช้สมมาตร 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ฉันทิ้งสิ่งนี้ไว้ไม่เปลี่ยนแปลงเนื่องจากนี่เป็นวิธีที่อัลกอริธึมของ Frith ถูกเขียนขึ้นในตอนแรกด้วยการแก้ไขเพิ่มเติมด้วยตัวเองในภายหลัง (ดูที่นี่ ) แก้ไขแล้ว
ProphetV

2

นอกจากนี้คุณยังสามารถใช้ฟังก์ชันตรีโกณฯ รุ่นใกล้เคียงได้โดยใช้ Taylor Expansions http://en.wikipedia.org/wiki/Taylor_series

ตัวอย่างเช่นคุณสามารถมีการประมาณค่าไซน์ที่สมเหตุสมผลโดยใช้คำสี่ชุดเทย์เลอร์เป็นครั้งแรก

ซายน์


นี่คือความจริงโดยทั่วไป แต่มาพร้อมกับคำเตือนหลายอย่างที่ฉันต้องการไปให้ไกลที่สุดเท่าที่จะบอกว่าคุณควรจะแทบไม่เคยเขียนรหัสของคุณเองบาป () เว้นแต่คุณมากคุ้นเคยกับสิ่งที่คุณทำ โดยเฉพาะอย่างยิ่งมี (ชื่อน้อย) ชื่อพหุนามดีกว่ารายการที่มีการประมาณด้วยเหตุผลที่ดีกว่าและคุณต้องเข้าใจว่าจะใช้สูตรและวิธีใช้ระยะเวลาของบาปและ cos เพื่อ จำกัด การโต้แย้งของคุณให้อยู่ในช่วงที่ ซีรีส์ใช้ นี่เป็นหนึ่งในกรณีเหล่านั้นที่คำพังเพย 'ความรู้เล็กน้อยเป็นสิ่งที่อันตราย' ดังขึ้นจริง
Steven Stadnicki

คุณสามารถให้การอ้างอิงบางส่วนเพื่อให้ฉันสามารถเรียนรู้ชื่อพหุนามหรือการประมาณที่ดีกว่านี้ได้หรือไม่? ฉันต้องการเรียนรู้สิ่งนั้นจริงๆ สิ่งที่ชุดนี้เป็นส่วนที่ใจเป่าใจที่สุดของหลักสูตรแคลคูลัสของฉัน

จุดเริ่มต้นที่คลาสสิกคือหนังสือสูตรตัวเลขซึ่งให้ข้อมูลที่เป็นธรรมเกี่ยวกับการคำนวณฟังก์ชันตัวเลขหลักและคณิตศาสตร์ที่อยู่เบื้องหลังการประมาณของพวกเขา อีกสถานที่ที่คุณอาจมองหาวิธีการที่ล้าสมัย แต่ยังคุ้มค่าที่จะรู้คือค้นหาอัลกอริธึมที่เรียกว่าCORDIC
Steven Stadnicki

@Vandell: ถ้าคุณต้องการที่จะสร้างพหุนาม Minimax ผมยินดีที่จะได้ยินความคิดของคุณเกี่ยวกับLolRemez
sam hocevar

ซีรี่ส์ Taylor ใกล้เคียงกับพฤติกรรมของฟังก์ชั่นรอบ ๆ จุดเดียวไม่ใช่ในช่วงเวลา พหุนามดีมากสำหรับการประเมินความบาป (0) หรืออนุพันธ์อันดับที่เจ็ดรอบ x = 0 แต่ข้อผิดพลาดที่ x = pi / 2 หลังจากนั้นคุณสามารถสะท้อนและทำซ้ำได้ค่อนข้างใหญ่ คุณสามารถทำได้ดีกว่าประมาณห้าสิบเท่าโดยการประเมินซีรี่ส์ Taylor รอบ x = pi / 4 แทน แต่สิ่งที่คุณต้องการจริงๆคือพหุนามที่ช่วยลดข้อผิดพลาดสูงสุดในช่วงเวลาโดยมีค่าความแม่นยำใกล้จุดเดียว
Marcks โทมัส

2

ขั้นตอนวิธีการหนึ่งที่น่ากลัวในการเดินทางเหมือนกันกว่าวงกลมเป็นอัลกอริทึม Goertzel มันต้องการเพียงแค่การคูณ 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 คะแนนแรก:

อัลกอริทึม Goertzel

แน่นอนว่าอัลกอริทึมสามารถทำงานกับฮาร์ดแวร์จุดคงที่ได้ การชนะอย่างชัดเจนต่อ Bresenham คือความเร็วคงที่ตลอดทั้งวงกลม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.