如何在 Pygame 中使用 raycast 3D 修复碰撞机制?

问题描述 投票:0回答:1
import pygame
import math
import numpy as np
from pygame.locals import *
import game_map

# Define color
B_RED = (125, 22, 22)
M_RED = (107, 19, 16)
L_RED = (69, 15, 16)
BLACK = (5, 5, 10)
WHITE = (255,255,255)
GRAY = (25,25,25)

# Define the world map
world_map = game_map.world_map # World map is from another python file

def close():
    """ Close the application """
    pygame.display.quit()
    pygame.quit()
    return

clock = pygame.time.Clock()

def main():
    """ Main function to run the game """
    pygame.init()

    # Display information
    WIDTH = 1400
    HEIGHT = 800
    window = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("Autophobia")

    # Initialize player position and direction
    position_x = 5  # Initial X position
    position_y = 8  # Initial Y position
    direction_x = 1  # Initial direction X
    direction_y = 0  # Initial direction Y
    plane_x = 0  # Camera plane X
    plane_y = 0.66  # Camera plane Y (for FOV)

    # Define movement and rotation speeds
    move_speed = 0.1
    rotation_speed = 0.05

    ## Main loop of the program
    while True:
        # Check the key if the player is quitting or not
        for event in pygame.event.get():
            if event.type == QUIT:
                close()

        # Key binding
        keys = pygame.key.get_pressed()
        if keys[K_ESCAPE]:
            close()
        if keys[K_UP]:
            position_x += direction_x * move_speed
            position_y += direction_y * move_speed
        if keys[K_DOWN]:
            position_x -= direction_x * move_speed
            position_y -= direction_y * move_speed
        if keys[K_RIGHT]:
            # Rotate RIGHT
            old_dir_x = direction_x
            direction_x = direction_x * math.cos(rotation_speed) - direction_y * math.sin(rotation_speed)
            direction_y = old_dir_x * math.sin(rotation_speed) + direction_y * math.cos(rotation_speed)
            # Rotate plane
            old_plane_x = plane_x
            plane_x = plane_x * math.cos(rotation_speed) - plane_y * math.sin(rotation_speed)
            plane_y = old_plane_x * math.sin(rotation_speed) + plane_y * math.cos(rotation_speed)
        if keys[K_LEFT]:
            # Rotate LEFT
            old_dir_x = direction_x
            direction_x = direction_x * math.cos(-rotation_speed) - direction_y * math.sin(-rotation_speed)
            direction_y = old_dir_x * math.sin(-rotation_speed) + direction_y * math.cos(-rotation_speed)
            # Rotate plane
            old_plane_x = plane_x
            plane_x = plane_x * math.cos(-rotation_speed) - plane_y * math.sin(-rotation_speed)
            plane_y = old_plane_x * math.sin(-rotation_speed) + plane_y * math.cos(-rotation_speed)

        # Raycasting Logic
        window.fill((25, 25, 25))  # Clear the screen
        pygame.draw.rect(window, (50,50,50), (0, HEIGHT/2, WIDTH, HEIGHT/2))  # Draw floor

        # Calculate ray direction for each column
        for columns in range(WIDTH):
            camera_x = 2.0 * columns / WIDTH - 1.0
            ray_pos_x = position_x
            ray_pos_y = position_y
            ray_dir_x = direction_x + plane_x * (2 * columns / WIDTH - 1) + 0.000001 # To avoid division by 0
            ray_dir_y = direction_y + plane_y * (2 * columns / WIDTH - 1) + 0.000001

            map_x = int(ray_pos_x)
            map_y = int(ray_pos_y)

            # Delta distance
            delta_dis_x = math.sqrt(1 + (ray_dir_y / ray_dir_x) ** 2)
            delta_dis_y = math.sqrt(1 + (ray_dir_x / ray_dir_y) ** 2)

            # Step and initial side distance
            step_x, step_y = 0, 0
            side_dis_x, side_dis_y = 0.0, 0.0

            # Initialize side
            if ray_dir_x < 0:
                step_x = -1
                side_dis_x = (ray_pos_x - map_x) * delta_dis_x
            else:
                step_x = 1
                side_dis_x = (map_x + 1.0 - ray_pos_x) * delta_dis_x
            if ray_dir_y < 0:
                step_y = -1
                side_dis_y = (ray_pos_y - map_y) * delta_dis_y
            else:
                step_y = 1
                side_dis_y = (map_y + 1.0 - ray_pos_y) * delta_dis_y

            # Perform DDA

            hit = 0
            while (hit == 0):
                if side_dis_x < side_dis_y:
                    side_dis_x += delta_dis_x
                    map_x += step_x
                    side = 0  # X-axis hit
                else:
                    side_dis_y += delta_dis_y
                    map_y += step_y
                    side = 1  # Y-axis hit
                if (world_map[map_x][map_y]) > 0:
                    hit = 1

            # Correction of the POV
            if side == 0:
                perp_wall_dist = abs((map_x - ray_pos_x + (1 - step_x) / 2) / ray_dir_x)
            else:
                perp_wall_dist = abs((map_y - ray_pos_y + (1 - step_y) / 2) / ray_dir_y)

            # Calculate the height of the wall
            line_height = int(HEIGHT / (perp_wall_dist + 0.00001))

            # Calculate the draw start and end
            draw_start = -line_height / 2 + HEIGHT / 2
            if draw_start < 0:
                draw_start = 0
            draw_end = line_height / 2 + HEIGHT / 2
            if draw_end >= HEIGHT:
                draw_end = HEIGHT - 1

            # Choose the color based on the map value
            if 0 <= map_x < len(world_map[0]) and 0 <= map_y < len(world_map):
                color = L_RED if world_map[map_y][map_x] == 1 else M_RED
            else:
                color = (25, 25, 25)  # or some other default color

            # Draw the vertical line
            pygame.draw.line(window, color, (columns, draw_start), (columns, draw_end),1)

        pygame.event.pump()
        pygame.display.flip()
        pygame.display.update()
        clock.tick(60)

if __name__ == "__main__":
    main()

代码中的问题是,当我运行它时,游戏正常工作,但碰撞系统似乎不起作用。我认为问题应该出在 DDA 算法代码内部,无论我修改了多少(尝试在

if(world_map[][])
中将 x 更改为 y,将 y 更改为 x,或者可能需要对光线投射本身的算法做一些处理) ?我现在不知道了,如果您能帮我解决这个问题,我将不胜感激。

在这段代码中,我尝试使用 ChatGPT 更改算法,例如多次更改

if (world_map[map_y][may_x])
,并尝试添加更多条件,例如:

hit = 1
... # Three dots mean there are more lines of codes between the two lines above and below
while (hit == 0):
    if side_dis_x < side_dis_y:
        side_dis_x += delta_dis_x
        map_x += step_x
        side = 0  # X-axis hit
    else:
        side_dis_y += delta_dis_y
        map_y += step_y
        side = 1  # Y-axis hit

    # Extra condition still doesn't work
    if map_x < 0 or map_x >= len(world_map[0]) or map_y < 0 or map_y >= len(world_map):
        break
    # Copied from ChatGPT
    if world_map[map_y][map_x] > 0 and (abs(ray_pos_x - map_x) < 0.5 or abs(ray_pos_y - map_y) < 0.5):
        hit = 1

基本上,如果 hit = 1,那么它应该阻止玩家穿过墙壁,然后它将继续检查玩家是否撞到墙壁,但在这段代码中,除了碰撞系统之外,一切都运行良好。我厌倦了解决这个问题很长一段时间,但它仍然不起作用。

python pygame raycasting dda raycast
1个回答
0
投票

在您的移动代码中,当用户按下箭头键时,玩家的位置更新如下:

if keys[K_UP]:
    position_x += direction_x * move_speed
    position_y += direction_y * move_speed
if keys[K_DOWN]:
    position_x -= direction_x * move_speed
    position_y -= direction_y * move_speed

此代码使玩家沿其面对的方向向前或向后移动,但它不会检查新位置是否在墙内。因此,玩家可以毫无阻力地穿过墙壁。

为了防止玩家移动到墙内,需要检查移动后的新位置是否在地图上的墙内。以下是修改移动代码以包含碰撞检测的方法:

if keys[K_UP]:
    new_position_x = position_x + direction_x * move_speed
    new_position_y = position_y + direction_y * move_speed

    if world_map[int(new_position_x)][int(position_y)] == 0:
        position_x = new_position_x
    if world_map[int(position_x)][int(new_position_y)] == 0:
        position_y = new_position_y

if keys[K_DOWN]:
    new_position_x = position_x - direction_x * move_speed
    new_position_y = position_y - direction_y * move_speed

    if world_map[int(new_position_x)][int(position_y)] == 0:
        position_x = new_position_x
    if world_map[int(position_x)][int(new_position_y)] == 0:
        position_y = new_position_y
© www.soinside.com 2019 - 2024. All rights reserved.