2D isometric: màn hình để tọa độ gạch


9

Tôi đang viết một trò chơi 2D đẳng cự và tôi gặp khó khăn khi tìm hiểu chính xác con trỏ đó là ô nào. Đây là một bản vẽ:

trong đó xs và ys là tọa độ màn hình (pixel), xt và yt là tọa độ ô, W và H lần lượt là chiều rộng của ô và chiều cao của ô theo pixel. Ký hiệu của tôi cho tọa độ là (y, x) có thể gây nhầm lẫn, xin lỗi về điều đó.

Điều tốt nhất tôi có thể tìm ra cho đến nay là:

int xtemp = xs / (W / 2);
int ytemp = ys / (H / 2);
int xt = (xs - ys) / 2;
int yt = ytemp + xt;

Điều này có vẻ gần như đúng nhưng lại mang lại cho tôi một kết quả rất thiếu chính xác, khiến cho việc chọn một số ô nhất định trở nên khó khăn hoặc đôi khi nó chọn một ô bên cạnh ô tôi đang cố gắng nhấp vào. Tôi không hiểu tại sao và tôi muốn nếu ai đó có thể giúp tôi hiểu logic đằng sau điều này.

Cảm ơn!

Câu trả lời:


2

Để đo lường chính xác, chúng tôi có thể xem xét sau:

Trước tiên, hãy xem xét cách chuyển đổi tọa độ từ không gian đẳng cự, được xác định bởi vectơ i và j (như trong isometricMap [i, j]) hoặc như yt và xt trên màn hình, sang không gian màn hình, được xác định bởi x và y của màn hình. Giả sử không gian màn hình của bạn được căn chỉnh theo điểm gốc với không gian đẳng cự vì đơn giản.

Một cách để thực hiện chuyển đổi là thực hiện xoay trước, sau đó chia tỷ lệ trục y hoặc trục x. Để có được các giá trị cần thiết để phù hợp với yt và xt của bạn, tôi không thể nghĩ ra ngay tại đây. Bạn có thể tạo một ma trận để làm điều này hoặc không và sau đó sử dụng ma trận ngược, nhưng hoạt động ngược lại về cơ bản là những gì bạn muốn.

Thu nhỏ giá trị theo chiều ngược lại và sau đó xoay ngược lại để lấy các giá trị và làm tròn xuống.

Có nhiều cách khác để tôi đoán, nhưng điều này có vẻ phù hợp nhất với tôi ngay bây giờ.


argh Tôi đã sửa đổi bài đăng này rất nhiều lần và tôi nghĩ rằng tôi không thể có được quan điểm của mình gọn gàng như tôi muốn. Tôi cần phải ngủ.
Toni

1
Cảm ơn, ma trận chắc chắn là giải pháp tốt nhất ở đây. Tôi có một cái gì đó gần như làm việc bây giờ!
Asik

4

Tôi đã có cùng một vấn đề cho một trò chơi mà tôi đang viết. Tôi tưởng tượng rằng vấn đề này sẽ khác nhau dựa trên cách bạn thực hiện chính xác hệ thống đẳng cự của mình, nhưng tôi sẽ giải thích cách tôi giải quyết vấn đề.

Lần đầu tiên tôi bắt đầu với chức năng brick_to_screen của mình. (Tôi giả sử đó là cách bạn đặt gạch ở vị trí phù hợp ở vị trí đầu tiên.) Hàm này có một phương trình để tính screen_x và screen_y. Của tôi trông như thế này (trăn):

def map_to_screen(self, point):
    x = (SCREEN_WIDTH + (point.y - point.x) * TILE_WIDTH) / 2
    y = (SCREEN_HEIGHT + (point.y + point.x) * TILE_HEIGHT) / 2
    return (x, y)

Tôi lấy hai phương trình đó và biến chúng thành một hệ phương trình tuyến tính. Giải hệ phương trình này trong bất kỳ phương pháp nào bạn chọn. (Tôi đã sử dụng phương pháp rref. Ngoài ra, một số máy tính vẽ đồ thị có thể giải quyết vấn đề này.)

Các phương trình cuối cùng trông như thế này:

# constants for quick calculating (only process once)
DOUBLED_TILE_AREA = 2 * TILE_HEIGHT * TILE_WIDTH
S2M_CONST_X = -SCREEN_HEIGHT * TILE_WIDTH + SCREEN_WIDTH * TILE_HEIGHT
S2M_CONST_Y = -SCREEN_HEIGHT * TILE_WIDTH - SCREEN_WIDTH * TILE_HEIGHT

def screen_to_map(self, point):
    # the "+ TILE_HEIGHT/2" adjusts for the render offset since I
    # anchor my sprites from the center of the tile
    point = (point.x * TILE_HEIGHT, (point.y + TILE_HEIGHT/2) * TILE_WIDTH)
    x = (2 * (point.y - point.x) + self.S2M_CONST_X) / self.DOUBLED_TILE_AREA
    y = (2 * (point.x + point.y) + self.S2M_CONST_Y) / self.DOUBLED_TILE_AREA
    return (x, y)

Như bạn có thể thấy, nó không đơn giản như phương trình ban đầu. Nhưng nó hoạt động độc đáo cho trò chơi tôi tạo ra. Cảm ơn lòng tốt cho đại số tuyến tính!

Cập nhật

Sau khi viết một lớp Point đơn giản với nhiều toán tử khác nhau, tôi đã đơn giản hóa câu trả lời này như sau:

# constants for quickly calculating screen_to_iso
TILE_AREA = TILE_HEIGHT * TILE_WIDTH
S2I_CONST_X = -SCREEN_CENTER.y * TILE_WIDTH + SCREEN_CENTER.x * TILE_HEIGHT
S2I_CONST_Y = -SCREEN_CENTER.y * TILE_WIDTH - SCREEN_CENTER.x * TILE_HEIGHT

def screen_to_iso(p):
    ''' Converts a screen point (px) into a level point (tile) '''
    # the "y + TILE_HEIGHT/2" is because we anchor tiles by center, not bottom
    p = Point(p.x * TILE_HEIGHT, (p.y + TILE_HEIGHT/2) * TILE_WIDTH)
    return Point(int((p.y - p.x + S2I_CONST_X) / TILE_AREA),
                 int((p.y + p.x + S2I_CONST_Y) / TILE_AREA))

def iso_to_screen(p):
    ''' Converts a level point (tile) into a screen point (px) '''
    return SCREEN_CENTER + Point((p.y - p.x) * TILE_WIDTH / 2,
                                 (p.y + p.x) * TILE_HEIGHT / 2)

Vâng, một hệ thống hai phương trình tuyến tính cũng sẽ hoạt động. Xem xét chúng ta có hai vectơ không song song, bạn sẽ có thể có được bất kỳ điểm nào trên mặt phẳng bằng cách sử dụng các vectơ đơn vị của yt và xt. Mặc dù tôi nghĩ rằng việc thực hiện của bạn là một chút hạn chế tìm kiếm và tôi sẽ không bận tâm đến việc xác nhận nó.
Toni

2

Bạn đang sử dụng một hệ thống tọa độ tốt. Mọi thứ trở nên phức tạp hơn nếu bạn sử dụng các cột so le.

Một cách để suy nghĩ về vấn đề này là bạn có một hàm để biến (xt, yt) thành (xs, ys). Tôi sẽ làm theo câu trả lời của Thane và gọi nó map_to_screen.

Bạn muốn nghịch đảo của chức năng này. Chúng tôi có thể gọi nó screen_to_map. Hàm nghịch đảo có các tính chất sau:

map_to_screen(screen_to_map(xs, ys)) == (xs, ys)
screen_to_map(map_to_screen(xt, yt)) == (xt, yt)

Hai điều này là những điều tốt để kiểm tra đơn vị một khi bạn có cả hai chức năng được viết. Làm thế nào để bạn viết ngược lại? Không phải tất cả các hàm đều có nghịch đảo nhưng trong trường hợp này:

  1. Nếu bạn đã viết nó như một phép quay theo sau là một bản dịch, thì nghịch đảo là bản dịch ngược (âm dx, dy) theo sau là xoay ngược (góc âm).
  2. Nếu bạn đã viết nó như là một ma trận nhân, thì nghịch đảo là ma trận nghịch đảo nhân.
  3. Nếu bạn đã viết nó dưới dạng phương trình đại số xác định (xs, ys) theo (xt, yt), thì nghịch đảo được tìm thấy bằng cách giải các phương trình đó cho (xt, yt) đã cho (xs, ys).

Hãy chắc chắn kiểm tra rằng hàm nghịch đảo + hàm gốc trả về câu trả lời bạn đã bắt đầu. Thane sẽ vượt qua cả hai bài kiểm tra, nếu bạn lấy phần + TILE_HEIGHT/2bù kết xuất. Khi tôi giải được đại số, tôi nghĩ ra:

x = (2*xs - SCREEN_WIDTH) / TILE_WIDTH
y = (2*ys - SCREEN_HEIGHT) / TILE_HEIGHT
yt =  (y + x) / 2
xt =  (y - x) / 2

mà tôi tin là giống như của Thane screen_to_map.

Chức năng sẽ biến tọa độ chuột thành phao; sử dụng floorđể chuyển đổi chúng thành tọa độ gạch số nguyên.


1
Cảm ơn! Tôi đã kết thúc bằng cách sử dụng một ma trận biến đổi, để viết ngược lại là chuyện nhỏ, tức là nó chỉ là Matrix.Invert (). Thêm vào đó, nó dẫn đến một kiểu mã hóa khai báo hơn (Matrix.Translate () * Matrix.Scale () * Matrix.Rotate () chứ không phải là một loạt các phương trình). Có thể nó hơi chậm hơn một chút, nhưng đó không phải là một vấn đề.
Asik
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.