Giá trị tốc độ không nguyên - có cách nào sạch hơn để làm điều này không?


21

Thường thì tôi sẽ muốn sử dụng giá trị tốc độ như 2,5 để di chuyển nhân vật của mình trong trò chơi dựa trên pixel. Phát hiện va chạm nói chung sẽ khó khăn hơn nếu tôi làm điều đó, mặc dù. Vì vậy, tôi cuối cùng đã làm một cái gì đó như thế này:

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

Tôi co rúm bên trong mỗi khi tôi phải viết điều đó, có cách nào sạch hơn để di chuyển một nhân vật với các giá trị tốc độ không nguyên hay tôi sẽ bị kẹt khi làm điều này mãi mãi?


11
Bạn đã xem xét làm cho đơn vị số nguyên của mình nhỏ hơn (ví dụ: 1/10 của đơn vị hiển thị) rồi 2,5 chuyển thành 25 và bạn vẫn có thể coi nó là số nguyên cho tất cả các kiểm tra và xử lý mọi khung hình một cách nhất quán.
DMGregory

6
Bạn có thể xem xét áp dụng thuật toán dòng Bresenham có thể được thực hiện chỉ bằng các số nguyên.
n0rd

1
Đây là một thường được thực hiện trên bảng điều khiển 8 bit cũ. Xem các bài viết như thế này để biết ví dụ về cách thực hiện chuyển động của pixel
Lucas

Câu trả lời:


13

Bresenham

Vào thời xưa, khi mọi người vẫn đang viết các thói quen video cơ bản của riêng họ để vẽ các đường và vòng tròn, không có gì lạ khi sử dụng thuật toán dòng Bresenham cho điều đó.

Bresenham giải quyết vấn đề này: bạn muốn vẽ một đường trên màn hình để di chuyển các dxpixel theo hướng ngang trong khi đồng thời kéo dài các dypixel theo hướng dọc. Có một ký tự "floaty" vốn có cho các dòng; ngay cả khi bạn có pixel nguyên, bạn kết thúc với độ nghiêng hợp lý.

Thuật toán phải nhanh, có nghĩa là nó chỉ có thể sử dụng số học số nguyên; và nó cũng biến mất mà không có bất kỳ phép nhân hay phép chia nào, chỉ có phép cộng và phép trừ.

Bạn có thể điều chỉnh trường hợp đó:

  • "Hướng x" của bạn (theo thuật toán Bresenham) là đồng hồ của bạn.
  • "Hướng y" của bạn là giá trị bạn muốn tăng (nghĩa là vị trí của nhân vật của bạn - cẩn thận, đây không thực sự là "y" của sprite của bạn hoặc bất cứ điều gì trên màn hình, hơn nữa là một giá trị trừu tượng)

"X / y" ở đây không phải là vị trí trên màn hình, mà là giá trị của một trong các kích thước của bạn theo thời gian. Rõ ràng, nếu sprite của bạn đang chạy theo hướng tùy ý trên màn hình, bạn sẽ có nhiều Bresenham chạy riêng, 2 cho 2D, 3 cho 3D.

Thí dụ

Giả sử bạn muốn di chuyển nhân vật của mình theo chuyển động đơn giản từ 0 đến 25 dọc theo một trong các trục của bạn. Khi nó di chuyển với tốc độ 2.5, nó sẽ phát triển ở khung 10.

Điều này giống như "vẽ một đường" từ (0,0) đến (10,25). Lấy algoritm dòng của Bresenham và để cho nó chạy. Nếu bạn làm đúng (và khi bạn nghiên cứu nó, nó sẽ nhanh chóng trở nên rõ ràng về cách bạn làm đúng), sau đó nó sẽ tạo ra 11 "điểm" cho bạn (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).

Gợi ý thích ứng

Nếu bạn google thuật toán đó và tìm một số mã (Wikipedia có một hiệp ước khá lớn về nó), có một số điều bạn cần để ý:

  • Nó rõ ràng hoạt động cho tất cả các loại dxdy. Bạn quan tâm đến một trường hợp cụ thể mặc dù (nghĩa là bạn sẽ không bao giờ có dx=0).
  • Việc thực hiện thông thường sẽ có một số trường hợp khác nhau cho các góc phần tư trên màn hình, tùy thuộc vào việc có dxdytích cực, tiêu cực abs(dx)>abs(dy)hay không. Bạn tất nhiên cũng chọn những gì bạn cần ở đây. Bạn phải đặc biệt chắc chắn rằng hướng tăng theo 1mỗi tích tắc luôn là hướng "đồng hồ" của bạn.

Nếu bạn áp dụng những đơn giản hóa này, kết quả thực sự sẽ rất đơn giản và loại bỏ hoàn toàn mọi thực tế.


1
Đây phải là câu trả lời được chấp nhận. Đã lập trình các trò chơi trên C64 vào những năm 80 và fractals trên PC vào những năm 90, tôi vẫn không ngừng sử dụng điểm nổi bất cứ nơi nào tôi có thể tránh được. Hoặc tất nhiên, với các FPU có mặt khắp nơi trong các bộ xử lý ngày nay, đối số hiệu năng chủ yếu là tranh luận, nhưng số học dấu phẩy động vẫn cần nhiều bóng bán dẫn hơn, tiêu thụ nhiều năng lượng hơn và nhiều bộ xử lý sẽ tắt hoàn toàn các FPU của chúng khi không sử dụng. Vì vậy, tránh điểm nổi sẽ khiến người dùng di động của bạn cảm ơn bạn vì đã không hút hết pin nhanh như vậy.
Guntram Blohm hỗ trợ Monica

@GuntramBlohm Câu trả lời được chấp nhận cũng hoạt động hoàn toàn tốt khi sử dụng Điểm cố định, điều mà tôi nghĩ là một cách tốt để làm điều đó. Bạn cảm thấy thế nào về số điểm cố định?
leetNightshade

Thay đổi câu trả lời này thành câu trả lời được chấp nhận sau khi tìm hiểu đây là cách họ đã thực hiện trong 8 ngày và 16 bit.
Tích lũy

26

Có một cách tuyệt vời để làm chính xác những gì bạn muốn.

Ngoài một floatvận tốc, bạn sẽ cần phải có một floatbiến thứ hai sẽ chứa và tích lũy một sự khác biệt giữa vận tốc thực và vận tốc tròn . Sự khác biệt này sau đó được kết hợp với chính vận tốc.

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

Đầu ra:

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


5
Tại sao bạn thích giải pháp này hơn là sử dụng float (hoặc điểm cố định) cho cả vận tốc và vị trí và chỉ làm tròn vị trí cho toàn bộ số nguyên vào cuối?
CodeInChaos

@CodesInChaos Tôi không thích giải pháp của mình hơn giải pháp đó. Khi tôi viết câu trả lời này, tôi không biết về nó.
HolyBlackCat

16

Sử dụng các giá trị nổi cho chuyển động và giá trị số nguyên cho va chạm và kết xuất.

Đây là một ví dụ:

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

Khi bạn di chuyển, bạn sử dụng move()tích lũy các vị trí phân số. Nhưng va chạm và kết xuất có thể xử lý các vị trí tách rời bằng cách sử dụng getPosition()hàm.


Lưu ý rằng trong trường hợp trò chơi được nối mạng, sử dụng các loại dấu phẩy động để mô phỏng thế giới có thể khó khăn. Xem ví dụ: gafferongames.com/networking-for-game-programmers/ ,.
liori

@liori Nếu bạn có một lớp điểm cố định hoạt động như một sự thay thế thả nổi cho float, thì điều đó không giải quyết được những vấn đề đó sao?
leetNightshade

@leetNightshade: phụ thuộc vào việc thực hiện.
liori

1
Tôi muốn nói rằng vấn đề là không tồn tại trong thực tế, phần cứng nào có khả năng chạy trò chơi nối mạng hiện đại không có phao nổi IEEE 754 ???
Sốt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.