首页 > 编程问答 >我怎样才能让这堵墙有正确的颜色?

我怎样才能让这堵墙有正确的颜色?

时间:2024-07-30 04:15:45浏览次数:7  
标签:python tkinter

我正在尝试使用 tkinter 创建一个类似末日的应用程序,但我的墙壁没有正确的颜色。这是我的代码:

执行该工作的代码的一部分:

            color = "#D3D3D3"  # Default wall color is light gray
            if hit:
                for wx, wy, width, height, wall_color in self.map_data:
                    if wx <= target_x <= wx + width and wy <= target_y <= wy + height:
                        color = wall_color
                        break

代码的功能:

    def draw_3d_view(self):
        """Draw the 3D view on the main canvas."""
        self.main_canvas.delete("all")

        fov = math.pi / 3  # Field of view (60 degrees)
        num_rays = int(self.main_canvas['width'])  # Number of rays should match canvas width
        max_depth = 800
        half_fov = fov / 2
        angle_step = fov / num_rays
        screen_width = int(self.main_canvas['width'])
        screen_height = int(self.main_canvas['height'])
        ray_angle = self.player.angle - half_fov

        for ray in range(num_rays):
            depth = 0
            hit = False

            # Cast ray
            for depth in range(1, max_depth):
                target_x = self.player.x + depth * math.cos(ray_angle)
                target_y = self.player.y + depth * math.sin(ray_angle)

                if self.check_collision(target_x, target_y):
                    hit = True
                    break

            if not hit:
                depth = max_depth  # If no hit, set depth to max depth for drawing faraway wall

            # Calculate wall height based on depth, with a scaling factor to make walls appear taller
            scaling_factor = 10  # Adjust this factor to make walls taller or shorter
            wall_height = (screen_height / (depth * math.cos(ray_angle - self.player.angle) + 0.0001)) * scaling_factor
            start_y = screen_height / 2 - wall_height / 2
            end_y = screen_height / 2 + wall_height / 2

            # Determine wall color
            color = "#D3D3D3"  # Default wall color is light gray
            if hit:
                for wx, wy, width, height, wall_color in self.map_data:
                    if wx <= target_x <= wx + width and wy <= target_y <= wy + height:
                        color = wall_color
                        break

            # Draw the vertical line for this ray
            self.main_canvas.create_line(ray, start_y, ray, end_y, fill=color)

            # Move to the next ray angle
            ray_angle += angle_step

        # Ensure the view is updated
        self.update_idletasks()

完整代码:

import tkinter as tk
import gui
import math


class Player:
    def __init__(self, x=0, y=0, size=10, angle=0):
        self.x = x
        self.y = y
        self.speed = 10
        self.size = size
        self.color = "#000000"
        self.angle = angle  # Direction the player is facing

    def update_angle(self):
        if self.angle > 360:
            self.angle = 0
        if self.angle < 0:
            self.angle = 360


class Doom(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Doom Main Window")
        self.geometry("1280x720")
        self.attributes("-fullscreen", True)
        self.bind("<F11>", self.toggle_fullscreen)  # Press F11 to toggle fullscreen

        # Create the main window canvas
        self.main_canvas = gui.create_canvas(self)
        self.main_drawer = gui.TkDrawUtils(self.main_canvas)

        # Create the map view window
        self.map_window = tk.Toplevel(self)
        self.map_window.title("Doom Map View")
        self.map_window.geometry("640x365")
        self.map_canvas = gui.create_canvas(self.map_window)
        self.map_drawer = gui.TkDrawUtils(self.map_canvas)

        # Initialize player position
        self.player = Player(x=50, y=50)  # Set initial player position
        self.player_speed = self.player.speed

        # Example map data
        self.map_data = [
            # (x, y, width, height, color)
            # Rooms
            (10, 10, 200, 120, "#FF0000"),  # Red Room
            (240, 10, 200, 120, "#00FF00"),  # Green Room
            (460, 10, 140, 120, "#FFC0CB"),  # Pink Room
            (10, 160, 120, 200, "#0000FF"),  # Blue Room
            (160, 160, 120, 200, "#FFFF00"),  # Yellow Room
            (300, 160, 140, 200, "#FF00FF"),  # Magenta Room
            (460, 160, 140, 200, "#00FFFF"),  # Cyan Room

            # Corridors
            (120, 60, 120, 20, "#FF0000"),  # Red Corridor
            (360, 60, 20, 100, "#00FF00"),  # Green Corridor
            (440, 80, 20, 20, "#FFC0CB"),  # Pink Corridor
            (120, 180, 40, 20, "#0000FF"),  # Blue Corridor
            (280, 280, 20, 20, "#FFFF00"),  # Yellow Corridor
            (440, 200, 20, 20, "#FF00FF"),  # Magenta Corridor
            (540, 130, 20, 40, "#00FFFF")  # Cyan Corridor
        ]

        # Draw the map and player
        self.draw_map()
        self.refresh()
        self.update_view()

        # Bind keys to movement functions
        self.bind_keys()

    def bind_keys(self):
        self.bind("<w>", self.move_up)
        self.bind("<s>", self.move_down)
        self.bind("<a>", self.move_left)
        self.bind("<d>", self.move_right)
        self.map_window.bind("<w>", self.move_up)
        self.map_window.bind("<s>", self.move_down)
        self.map_window.bind("<a>", self.move_left)
        self.map_window.bind("<d>", self.move_right)
        self.bind("<Left>", self.angle_left)
        self.bind("<Right>", self.angle_right)
        self.map_window.bind("<Left>", self.angle_left)
        self.map_window.bind("<Right>", self.angle_right)

    def angle_left(self, event=None):
        self.player.angle -= 1
        self.player.update_angle()  # Ensure angle is within valid range
        self.refresh()

    def angle_right(self, event=None):
        self.player.angle += 1
        self.player.update_angle()  # Ensure angle is within valid range
        self.refresh()

    def move_up(self, event=None):
        """Move the player forward in the direction they are facing."""
        # Calculate new position based on the player's angle
        new_x = self.player.x + self.player_speed * math.cos(math.radians(self.player.angle))
        new_y = self.player.y + self.player_speed * math.sin(math.radians(self.player.angle))

        if not self.check_collision(new_x, new_y):
            self.player.x = new_x
            self.player.y = new_y

        self.update_view()

    def move_down(self, event=None):
        """Move the player backward from the direction they are facing."""
        # Calculate new position based on the player's angle
        new_x = self.player.x - self.player_speed * math.cos(math.radians(self.player.angle))
        new_y = self.player.y - self.player_speed * math.sin(math.radians(self.player.angle))

        if not self.check_collision(new_x, new_y):
            self.player.x = new_x
            self.player.y = new_y

        self.update_view()

    def move_left(self, event=None):
        """Move the player left relative to their facing direction."""
        # Calculate new position perpendicular to the player's angle (left direction)
        angle_rad = math.radians(self.player.angle)
        new_x = self.player.x + self.player_speed * math.sin(angle_rad)
        new_y = self.player.y - self.player_speed * math.cos(angle_rad)

        if not self.check_collision(new_x, new_y):
            self.player.x = new_x
            self.player.y = new_y

        self.update_view()

    def move_right(self, event=None):
        """Move the player right relative to their facing direction."""
        # Calculate new position perpendicular to the player's angle (right direction)
        angle_rad = math.radians(self.player.angle)
        new_x = self.player.x - self.player_speed * math.sin(angle_rad)
        new_y = self.player.y + self.player_speed * math.cos(angle_rad)

        if not self.check_collision(new_x, new_y):
            self.player.x = new_x
            self.player.y = new_y

        self.update_view()

    def update_view(self):
        """Update both the 2D map and the 3D view."""
        self.draw_map()
        self.draw_3d_view()
        self.update()
        self.update_idletasks()

    def check_collision(self, x, y):
        """Check if the player collides with any walls or corridors."""
        player_size = self.player.size
        player_rect = (x, y, x + player_size, y + player_size)  # Define player rectangle

        for (wx, wy, width, height, color) in self.map_data:
            wall_rect = (wx, wy, wx + width, wy + height)  # Define wall rectangle

            # Check if rectangles overlap
            if (player_rect[0] < wall_rect[2] and player_rect[2] > wall_rect[0] and
                    player_rect[1] < wall_rect[3] and player_rect[3] > wall_rect[1]):
                return False
        return True

    def draw_map(self):
        """Draw walls and player on the map canvas based on the map data."""
        # Clear previous drawings
        self.map_canvas.delete("all")

        # Draw walls
        for (x, y, width, height, color) in self.map_data:
            self.map_drawer.draw_filled_rectangle(x, y, width, height, color)

        # Draw player
        player_size = self.player.size
        player_color = self.player.color
        self.map_drawer.draw_filled_rectangle(self.player.x, self.player.y, player_size, player_size, player_color)

    def draw_3d_view(self):
        """Draw the 3D view on the main canvas."""
        self.main_canvas.delete("all")

        fov = math.pi / 3  # Field of view (60 degrees)
        num_rays = int(self.main_canvas['width'])  # Number of rays should match canvas width
        max_depth = 800
        half_fov = fov / 2
        angle_step = fov / num_rays
        screen_width = int(self.main_canvas['width'])
        screen_height = int(self.main_canvas['height'])
        ray_angle = self.player.angle - half_fov

        for ray in range(num_rays):
            depth = 0
            hit = False

            # Cast ray
            for depth in range(1, max_depth):
                target_x = self.player.x + depth * math.cos(ray_angle)
                target_y = self.player.y + depth * math.sin(ray_angle)

                if self.check_collision(target_x, target_y):
                    hit = True
                    break

            if not hit:
                depth = max_depth  # If no hit, set depth to max depth for drawing faraway wall

            # Calculate wall height based on depth, with a scaling factor to make walls appear taller
            scaling_factor = 10  # Adjust this factor to make walls taller or shorter
            wall_height = (screen_height / (depth * math.cos(ray_angle - self.player.angle) + 0.0001)) * scaling_factor
            start_y = screen_height / 2 - wall_height / 2
            end_y = screen_height / 2 + wall_height / 2

            # Determine wall color
            color = "#D3D3D3"  # Default wall color is light gray
            if hit:
                for wx, wy, width, height, wall_color in self.map_data:
                    if wx <= target_x <= wx + width and wy <= target_y <= wy + height:
                        color = wall_color
                        break

            # Draw the vertical line for this ray
            self.main_canvas.create_line(ray, start_y, ray, end_y, fill=color)

            # Move to the next ray angle
            ray_angle += angle_step

        # Ensure the view is updated
        self.update_idletasks()

    def toggle_fullscreen(self, event=None):
        """Toggle fullscreen mode."""
        current_state = self.attributes("-fullscreen")
        self.attributes("-fullscreen", not current_state)
        if not current_state:
            self.update_idletasks()

    def refresh(self):
        self.player.update_angle()
        self.update_idletasks()
        self.update()
        self.update_view()



if __name__ == '__main__':
    app = Doom()
    app.mainloop()

gui.py:

import tkinter as tk
from PIL import Image, ImageTk


class TkDrawUtils:
    def __init__(self, canvas):
        self.canvas = canvas

    def draw_line(self, x1, y1, x2, y2, color='black'):
        """Draw a line on the canvas from (x1, y1) to (x2, y2) with the specified color."""
        self.canvas.create_line(x1, y1, x2, y2, fill=color)

    def draw_rectangle(self, x, y, width, height, color='black'):
        """Draw a rectangle on the canvas with the specified color."""
        self.canvas.create_rectangle(x, y, x + width, y + height, outline=color, fill=color)

    def draw_filled_rectangle(self, x, y, width, height, color='black'):
        """Draw a filled rectangle on the canvas with the specified color."""
        self.canvas.create_rectangle(x, y, x + width, y + height, fill=color, outline='')

    def draw_image(self, x, y, image_path):
        """Draw an image at the specified location on the canvas."""
        image = Image.open(image_path).convert("RGBA")
        self.image_tk = ImageTk.PhotoImage(image)
        self.canvas.create_image(x, y, image=self.image_tk, anchor='nw')

    def draw_polygon(self, points, fill_color=None, outline_color='black'):
        """
        Draw a polygon connecting the given points.
        :param points: List of (x, y) tuples representing the vertices of the polygon.
        :param fill_color: Color to fill the polygon. If None, the polygon is not filled.
        :param outline_color: Color of the polygon's outline.
        """
        self.canvas.create_polygon(points, fill=fill_color, outline=outline_color)

    def draw_shapes_from_map(self, map_obj, cell_size):
        """
        Draw shapes based on the provided map object.
        The map object should be a list of lists where each cell describes a shape:
        - 'R': Rectangle
        - 'F': Filled Rectangle
        - 'L': Line (followed by coordinates and color)
        - 'I': Image (followed by image path)
        - 'P': Polygon (followed by points and optional fill color)
        """
        for row_index, row in enumerate(map_obj):
            for col_index, cell in enumerate(row):
                x = col_index * cell_size
                y = row_index * cell_size
                if cell.startswith('R'):
                    self.draw_rectangle(x, y, cell_size, cell_size, 'blue')
                elif cell.startswith('F'):
                    self.draw_filled_rectangle(x, y, cell_size, cell_size, 'green')
                elif cell.startswith('L'):
                    _, x2, y2, color = cell.split()
                    x2 = float(x2)
                    y2 = float(y2)
                    self.draw_line(x, y, x2, y2, color)
                elif cell.startswith('I'):
                    _, image_path = cell.split()
                    self.draw_image(x, y, image_path)
                elif cell.startswith('P'):
                    parts = cell.split()
                    points = [(float(parts[i]), float(parts[i + 1])) for i in range(1, len(parts) - 1, 2)]
                    fill_color = parts[-1] if len(parts) % 2 == 0 else None
                    self.draw_polygon(points, fill_color)


def create_canvas(parent):
    """Create a fullscreen Tkinter canvas for the parent window."""
    # Get the screen width and height
    screen_width = parent.winfo_screenwidth()
    screen_height = parent.winfo_screenheight()

    canvas = tk.Canvas(parent, width=screen_width, height=screen_height, bg='white')
    canvas.pack(fill=tk.BOTH, expand=True)

    # Make the canvas resize with the window
    canvas.bind("<Configure>", lambda e: canvas.config(width=parent.winfo_width(), height=parent.winfo_height()))

    return canvas

我尝试过 chatGPT 但那个机器人只是愚蠢的。我尝试了你所在房间的颜色,但看起来不正确。


问题在于正在检查碰撞 更新光线颜色。这意味着如果光线与墙壁碰撞,实际上是在检查光线 下一帧 位置的颜色,而不是它当前击中的墙壁。

以下是解决此问题的步骤:

  1. draw_3d_view 函数中,将碰撞检查和颜色确定代码移到循环的绘制部分 。这样,将在同一位置检查碰撞并确定颜色。

  2. 删除不必要的 hit 变量 ,因为我们现在可以在循环中直接使用碰撞信息。

这是更新后的 draw_3d_view 函数:

def draw_3d_view(self):
    """在主画布上绘制 3D 视图。"""
    self.main_canvas.delete("all")

    fov = math.pi / 3  # 视野(60 度)
    num_rays = int(self.main_canvas['width'])  # 光线数量应与画布宽度匹配
    max_depth = 800
    half_fov = fov / 2
    angle_step = fov / num_rays
    screen_width = int(self.main_canvas['width'])
    screen_height = int(self.main_canvas['height'])
    ray_angle = self.player.angle - half_fov

    for ray in range(num_rays):
        depth = 0

        # 投射光线
        for depth in range(1, max_depth):
            target_x = self.player.x + depth * math.cos(ray_angle)
            target_y = self.player.y + depth * math.sin(ray_angle)

            if self.check_collision(target_x, target_y):
                # 计算基于深度的墙壁高度,使用缩放因子使墙壁看起来更高
                scaling_factor = 10  # 调整此因子以使墙壁更高或更矮
                wall_height = (screen_height / (depth * math.cos(ray_angle - self.player.angle) + 0.0001)) * scaling_factor
                start_y = screen_height / 2 - wall_height / 2
                end_y = screen_height / 2 + wall_height / 2

                # 确定墙壁颜色
                color = "#D3D3D3"  # 默认墙壁颜色为浅灰色
                for wx, wy, width, height, wall_color in self.map_data:
                    if wx <= target_x <= wx + width and wy <= target_y <= wy + height:
                        color = wall_color
                        break

                # 为此光线绘制垂直线
                self.main_canvas.create_line(ray, start_y, ray, end_y, fill=color)
                break

        # 如果没有命中,则将深度设置为最大深度以绘制远处的墙壁
        if depth == max_depth - 1:
            scaling_factor = 10 
            wall_height = (screen_height / (depth * math.cos(ray_angle - self.player.angle) + 0.0001)) * scaling_factor
            start_y = screen_height / 2 - wall_height / 2
            end_y = screen_height / 2 + wall_height / 2
            self.main_canvas.create_line(ray, start_y, ray, end_y, fill=color)

        # 移动到下一个光线角度
        ray_angle += angle_step

    # 确保视图已更新
    self.update_idletasks()

通过此更改,的光线投射引擎现在应该可以正确地为墙壁着色。

标签:python,tkinter
From: 78808895

相关文章

  • 即使安装了软件包,也找不到 python 模块句子转换器
    对于我的python脚本(如下所示),我使用句子转换器包,其中包含SBERT模型。即使在执行“piplist”时明确列出该软件包,并且还安装了torch,所有内容都更新到最新版本,但仍然找不到该模块。脚本:importnumpyasnpimportpandasaspdfromsentence_transformersimportSenten......
  • 有没有办法在 python 中返回类实例的布尔属性?
    我想组织我玩的游戏中的汽车数据。为此,我创建了一个“Car”类。我希望将此类实例的一些属性作为布尔值,这样我就可以将此类的所有实例放入列表中并过滤它们。我当前的解决方案不起作用,因为,我收到以下错误消息:TypeError:__repr__returnednon-string(typeCar)我使用......
  • python 正则表达式匹配一行中的多个单词而不转到下一行
    我正在编写一个解析器来解析以下输出:admin@str-s6000-on-5:~$showinterfacestatusEthernet4InterfaceLanesSpeedMTUAliasVlanOperAdminTypeAsymPFC------------------------------------------......
  • 使用 Python 平滑和对称不规则形状和曲线
    我需要完成三项任务:正则化曲线曲线的对称性完成不完整的曲线例如,这里是输入和预期的输出图像:输入输出|||在一般设置中,形状可以由任何SVG曲线基元(贝塞尔曲线、直线、弧线)表示。为了统一表示,示例包含曲线的折线近似。这些折线保存为......
  • 在Python中通过绝对路径引用数据文件夹
    我有一个大型python项目,其中数据太大,无法每次都以相同的方式共享。不同的人可能会使用网络位置或将某些内容复制到本地驱动器。该路径由不同子文件夹中的脚本和笔记本使用。例如,我将创建一个config.py来定义数据文件夹的路径。importsyssys.path.append('../'......
  • python三天速成记(看完你就会)day3 满满干货~
    续上文啦~EXCEL表的操作上一篇文章讲了怎么读取和操作txt和csv文档,但其实我们生活中还有一个常用的文本格式那就是excel文件,特别是在对大量数据进行处理的时候。excel文件的用处和广泛。其实在python中有很多库可以处理excel文件,但是本文主要介绍使用最实用最广泛的库pan......
  • Python输入验证改进的其他方式
    题目[题目来源:Python编程快速上手——让繁琐工作自动化(第二版)第三章实践项目,下面的解答程序为我自己完成的,仅供参考。]编写一个名为collatz()的函数,他有一个名为number的参数。如果参数是偶数,那么collatz()就输出number//2,并返回该值。如果参数是奇数,那么collatz()就......
  • Python的PyInputPlus模块
    PyInputPlus模块简介PyInputPlus模块的功能:PyInputPlus模块是一个Python第三方模块,需要自己对它进行安装。包含与input()函数类似的、用于多种数据(如日期、数字、电子邮箱地址等)的函数。如果,用户输入了无效的内容,例如格式错误的日期或超过预期范围的数字,那么PyInputPlus模......
  • 用Python实现二进制搜索(二分查找)
    二进制搜索(binarysearch,又称二分搜索)是一种快速有效的搜索方法,用于搜索有序列表中的元素。importmathdefbinary_search(sorted_list,target):"""在有序列表sorted_list中查找目标值target的位置使用二分查找算法"""lower_bound=0#初始......
  • 超详细Python教程——使用Hexo搭建自己的博客
    使用Hexo搭建自己的博客对于一个程序员来说,搭建一个属于自己的博客平台是非常有意义的事情。首先,博客可以记录自己的成长历程,也是对自己一段时间学习和工作的总结和沉淀;其他,通过博客可以营销自己,增强自己在互联网或行业内的影响力,为将来更好的职业生涯打一个坚实的基础。前......