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
相关产品推荐
相关产品推荐

