You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arcade框架本地多人赛车游戏分屏视图实现求助

问题:实现俯视角多人赛车游戏的等分分屏视图

开发一款俯视角本地多人赛车游戏,目标为每位玩家提供独立的屏幕视图。当前遇到的问题是无法调整Camera在窗口内的位置,理想状态是实现4个玩家各占一个独立无重叠的等分屏幕视图。现有双Camera示例代码中,两个Camera大小不一,需要修改为等分屏幕且互不重叠的效果。

import arcade as arc
from arcade.pymunk_physics_engine import PymunkPhysicsEngine

SCREEN_WIDTH = 700
SCREEN_HEIGHT = 500

LEFT_VIEWPORT_MARGIN = 100
RIGHT_VIEWPORT_MARGIN = 100
TOP_VIEWPORT_MARGIN = 100
BOTTOM_VIEWPORT_MARGIN = 100


PHY_ENGINE = PymunkPhysicsEngine()


class Player(arc.Sprite):
    def __init__(self, player_num=0, control="wasd"):
        super().__init__()
        if player_num == 0:
            self.texture = arc.load_texture(":resources:images/space_shooter/playerShip1_orange.png")
        elif player_num == 1:
            self.texture = arc.load_texture(":resources:images/space_shooter/playerShip1_blue.png")

        self.control = control

        self.move_up = False
        self.move_down = False
        self.move_left = False
        self.move_right = False

    def update(self):
        if self.move_up:
            PHY_ENGINE.apply_force(self, (0, 1000))
        if self.move_down:
            PHY_ENGINE.apply_force(self, (0, -1000))
        if self.move_right:
            PHY_ENGINE.apply_force(self, (1000, 0))
        if self.move_left:
            PHY_ENGINE.apply_force(self, (-1000, 0))


class GameView(arc.View):
    def __init__(self):
        super().__init__()
        self.width = SCREEN_WIDTH
        self.height = SCREEN_HEIGHT

        self.scene = None

        self.p_cameras = []
        self.gui_camera = None
        self.camera_info = [[0, 0], [0, 0]]
        self.map_width = 10000
        self.map_height = 10000

        self.players = []

        # input stuff
        self.w_pressed = False
        self.s_pressed = False
        self.a_pressed = False
        self.d_pressed = False

        self.up_pressed = False
        self.down_pressed = False
        self.left_pressed = False
        self.right_pressed = False

        self.move_up = False
        self.move_down = False
        self.move_left = False
        self.move_right = False

        # game stuff
        self.physics_engine = PHY_ENGINE
        self.background = arc.load_texture(":resources:images/backgrounds/abstract_1.jpg")

    def process_keychange(self):
        for player in self.players:
            # WASD movement
            if player.control == "wasd":
                if self.w_pressed:
                    player.move_up = True
                else:
                    player.move_up = False

                if self.s_pressed:
                    player.move_down = True
                else:
                    player.move_down = False

                if self.d_pressed:
                    player.move_right = True
                else:
                    player.move_right = False

                if self.a_pressed:
                    player.move_left = True
                else:
                    player.move_left = False

            # arrow key movement
            elif player.control == "arrows":
                if self.up_pressed:
                    player.move_up = True
                else:
                    player.move_up = False

                if self.down_pressed:
                    player.move_down = True
                else:
                    player.move_down = False

                if self.right_pressed:
                    player.move_right = True
                else:
                    player.move_right = False

                if self.left_pressed:
                    player.move_left = True
                else:
                    player.move_left = False

    def on_key_press(self, key, modifiers):
        if key == arc.key.W:
            self.w_pressed = True
        if key == arc.key.S:
            self.s_pressed = True
        if key == arc.key.A:
            self.a_pressed = True
        if key == arc.key.D:
            self.d_pressed = True

        if key == arc.key.UP:
            self.up_pressed = True
        if key == arc.key.DOWN:
            self.down_pressed = True
        if key == arc.key.LEFT:
            self.left_pressed = True
        if key == arc.key.RIGHT:
            self.right_pressed = True

        if key == arc.key.ESCAPE:
            arc.exit()

    def on_key_release(self, key, modifiers):
        if key == arc.key.W:
            self.w_pressed = False
        if key == arc.key.S:
            self.s_pressed = False
        if key == arc.key.A:
            self.a_pressed = False
        if key == arc.key.D:
            self.d_pressed = False

        if key == arc.key.UP:
            self.up_pressed = False
        if key == arc.key.DOWN:
            self.down_pressed = False
        if key == arc.key.LEFT:
            self.left_pressed = False
        if key == arc.key.RIGHT:
            self.right_pressed = False

        self.process_keychange()

    def on_show_view(self):
        arc.set_viewport(0, self.window.width, 0, self.window.height)

        self.load_level()

    def load_level(self):
        self.p_cameras = [arc.Camera(), arc.Camera(viewport_width=500, viewport_height=400)]
        self.scene = arc.Scene()
        self.scene.add_sprite_list("player")

        player1 = Player()
        player1.center_y = 200
        player1.center_x = 200
        self.scene.add_sprite("player", player1)
        self.physics_engine.add_sprite(player1, moment_of_inertia=PymunkPhysicsEngine.MOMENT_INF,
                                       collision_type="player", damping=.01)

        player2 = Player(player_num=1, control="arrows")
        player2.center_y = 300
        player2.center_x = 200
        self.scene.add_sprite("player", player2)
        self.physics_engine.add_sprite(player2, moment_of_inertia=PymunkPhysicsEngine.MOMENT_INF,
                                       collision_type="player", damping=.01)

        self.players.append(player1)
        self.players.append(player2)

    def on_draw(self):
        if self.p_cameras is None:
            return

        for camera in self.p_cameras:
            camera.use()
            arc.draw_lrwh_rectangle_textured(camera.position.x,
                                             camera.position.y,
                                             camera.viewport_width, camera.viewport_height, self.background)
            self.scene.draw()

    def center_camera_to_player(self, index=0):
        target_player = self.players[index]
        camera = self.p_cameras[index]
        view_left = self.camera_info[index][0]
        view_bottom = self.camera_info[index][1]
        # Scroll left
        left_boundary = view_left + LEFT_VIEWPORT_MARGIN
        if target_player.left < left_boundary:
            view_left -= left_boundary - target_player.left

        # Scroll right
        right_boundary = view_left + self.width - RIGHT_VIEWPORT_MARGIN - (SCREEN_WIDTH - camera.viewport_width)
        if target_player.right > right_boundary:
            view_left += target_player.right - right_boundary

        # Scroll up
        top_boundary = view_bottom + self.height - TOP_VIEWPORT_MARGIN - (SCREEN_HEIGHT - camera.viewport_height)
        if target_player.top > top_boundary:
            view_bottom += target_player.top - top_boundary

        # Scroll down
        bottom_boundary = view_bottom + BOTTOM_VIEWPORT_MARGIN
        if target_player.bottom < bottom_boundary:
            view_bottom -= bottom_boundary - target_player.bottom

        # keeps camera in left bound of map
        if view_left < 0:
            view_left = 0

        # keeps camera in right bound of map
        if (view_left + self.width) > self.map_width:
            view_left = self.map_width - self.width

        # keeps camera in bottom bound of map
        if view_bottom < 0:
            view_bottom = 0

        # keeps camera in top bound of map
        if view_bottom + self.height > self.map_height:
            view_bottom = self.map_height - self.height

        # Scroll to the proper location
        position = view_left, view_bottom
        camera.move_to(position, .3)

    def on_update(self, delta_time: float):
        self.process_keychange()
        self.center_camera_to_player(0)
        self.center_camera_to_player(1)
        self.scene.update()
        self.physics_engine.step()


def main():
    """Main function"""
    window = arc.Window(SCREEN_WIDTH, SCREEN_HEIGHT)
    start_view = GameView()
    window.show_view(start_view)
    arc.run()


if __name__ == "__main__":
    main()
解决方案

核心思路

通过为每个Camera设置窗口内的视口位置和匹配的视口尺寸,实现分屏效果。对于2玩家,将屏幕横向分割为两个等宽区域;对于4玩家,将屏幕分割为2x2的四个等大区域。同时修正相机跟随逻辑,确保每个相机独立跟踪对应玩家。

修改步骤及代码

1. 调整Camera初始化配置

在load_level方法中,根据玩家数量创建对应数量的Camera,明确设置每个Camera的窗口视口位置和尺寸:

  • 2玩家分屏:每个Camera宽度为SCREEN_WIDTH // 2,高度为SCREEN_HEIGHT,分别位于窗口左侧和右侧。
  • 4玩家分屏:每个Camera宽高为SCREEN_WIDTH//2和SCREEN_HEIGHT//2,分别位于左上、右上、左下、右下四个象限。

2. 修正on_draw中的背景绘制

背景需要绘制在Camera对应的窗口视口区域,而非世界坐标位置,使用camera.viewport_left和camera.viewport_bottom定位窗口内的绘制位置。

3. 修正相机跟随逻辑

移除原代码中混淆世界视口和窗口视口的计算,让每个Camera基于自身视口尺寸跟踪玩家。

以下是修改后的完整代码:

import arcade as arc
from arcade.pymunk_physics_engine import PymunkPhysicsEngine

SCREEN_WIDTH = 700
SCREEN_HEIGHT = 500
PLAYER_COUNT = 2  # 可修改为4实现4玩家分屏

LEFT_VIEWPORT_MARGIN = 100
RIGHT_VIEWPORT_MARGIN = 100
TOP_VIEWPORT_MARGIN = 100
BOTTOM_VIEWPORT_MARGIN = 100


PHY_ENGINE = PymunkPhysicsEngine()


class Player(arc.Sprite):
    def __init__(self, player_num=0, control="wasd"):
        super().__init__()
        textures = [
            ":resources:images/space_shooter/playerShip1_orange.png",
            ":resources:images/space_shooter/playerShip1_blue.png",
            ":resources:images/space_shooter/playerShip1_green.png",
            ":resources:images/space_shooter/playerShip1_red.png"
        ]
        self.texture = arc.load_texture(textures[player_num])

        self.control = control

        self.move_up = False
        self.move_down = False
        self.move_left = False
        self.move_right = False

    def update(self):
        if self.move_up:
            PHY_ENGINE.apply_force(self, (0, 1000))
        if self.move_down:
            PHY_ENGINE.apply_force(self, (0, -1000))
        if self.move_right:
            PHY_ENGINE.apply_force(self, (1000, 0))
        if self.move_left:
            PHY_ENGINE.apply_force(self, (-1000, 0))


class GameView(arc.View):
    def __init__(self):
        super().__init__()
        self.width = SCREEN_WIDTH
        self.height = SCREEN_HEIGHT

        self.scene = None

        self.p_cameras = []
        self.gui_camera = None
        self.camera_info = []  # 存储每个相机的世界视口位置
        self.map_width = 10000
        self.map_height = 10000

        self.players = []

        # 输入控制映射
        self.control_states = {
            "wasd": {"w": False, "s": False, "a": False, "d": False},
            "arrows": {"up": False, "down": False, "left": False, "right": False},
            "ijkl": {"i": False, "k": False, "j": False, "l": False},
            "numpad": {"8": False, "2": False, "4": False, "6": False}
        }

        # 游戏资源
        self.physics_engine = PHY_ENGINE
        self.background = arc.load_texture(":resources:images/backgrounds/abstract_1.jpg")

    def process_keychange(self):
        for idx, player in enumerate(self.players):
            controls = self.control_states[player.control]
            player.move_up = controls["w"] if player.control == "wasd" else controls["up"] if player.control == "arrows" else controls["i"] if player.control == "ijkl" else controls["8"]
            player.move_down = controls["s"] if player.control == "wasd" else controls["down"] if player.control == "arrows" else controls["k"] if player.control == "ijkl" else controls["2"]
            player.move_left = controls["a"] if player.control == "wasd" else controls["left"] if player.control == "arrows" else controls["j"] if player.control == "ijkl" else controls["4"]
            player.move_right = controls["d"] if player.control == "wasd" else controls["right"] if player.control == "arrows" else controls["l"] if player.control == "ijkl" else controls["6"]

    def on_key_press(self, key, modifiers):
        if key == arc.key.W:
            self.control_states["wasd"]["w"] = True
        if key == arc.key.S:
            self.control_states["wasd"]["s"] = True
        if key == arc.key.A:
            self.control_states["wasd"]["a"] = True
        if key == arc.key.D:
            self.control_states["wasd"]["d"] = True

        if key == arc.key.UP:
            self.control_states["arrows"]["up"] = True
        if key == arc.key.DOWN:
            self.control_states["arrows"]["down"] = True
        if key == arc.key.LEFT:
            self.control_states["arrows"]["left"] = True
        if key == arc.key.RIGHT:
            self.control_states["arrows"]["right"] = True

        # 4玩家额外控制
        if key == arc.key.I:
            self.control_states["ijkl"]["i"] = True
        if key == arc.key.K:
            self.control_states["ijkl"]["k"] = True
        if key == arc.key.J:
            self.control_states["ijkl"]["j"] = True
        if key == arc.key.L:
            self.control_states["ijkl"]["l"] = True

        if key == arc.key.NUM_8:
            self.control_states["numpad"]["8"] = True
        if key == arc.key.NUM_2:
            self.control_states["numpad"]["2"] = True
        if key == arc.key.NUM_4:
            self.control_states["numpad"]["4"] = True
        if key == arc.key.NUM_6:
            self.control_states["numpad"]["6"] = True

        if key == arc.key.ESCAPE:
            arc.exit()

    def on_key_release(self, key, modifiers):
        if key == arc.key.W:
            self.control_states["wasd"]["w"] = False
        if key == arc.key.S:
            self.control_states["wasd"]["s"] = False
        if key == arc.key.A:
            self.control_states["wasd"]["a"] = False
        if key == arc.key.D:
            self.control_states["wasd"]["d"] = False

        if key == arc.key.UP:
            self.control_states["arrows"]["up"] = False
        if key == arc.key.DOWN:
            self.control_states["arrows"]["down"] = False
        if key == arc.key.LEFT:
            self.control_states["arrows"]["left"] = False
        if key == arc.key.RIGHT:
            self.control_states["arrows"]["right"] = False

        if key == arc.key.I:
            self.control_states["ijkl"]["i"] = False
        if key == arc.key.K:
            self.control_states["ijkl"]["k"] = False
        if key == arc.key.J:
            self.control_states["ijkl"]["j"] = False
        if key == arc.key.L:
            self.control_states["ijkl"]["l
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:10:15