基于对象的MVC游戏设计
我用Python写了一个简单的像推箱子那样的2D游戏,现在想把它整理得更好一些。这个游戏是用一种面向对象的方式写的,每个游戏中的物体都有一个可以调用的绘制函数,用来重新绘制屏幕。这样做挺不错的,因为我可以遍历屏幕上的每个位置,调用对应物体的绘制函数。
我正在学习设计模式,特别喜欢MVC这种模式带来的好处,但我不太明白怎么把每个物体的绘制函数和模型分开,以便放到视图中。有没有好的方法来做到这一点?这是不是说明我不应该使用MVC设计?
3 个回答
我想简单说明一下,当你的模型发生变化时,它不能直接告诉视图“渲染这个变化”——因为模型不应该知道视图的存在。
相反,模型会发出一个事件,告诉大家它已经改变了,视图在收到这个事件后会根据需要重新渲染。视图知道模型的情况,并且会订阅它的事件,但模型对接收这些事件的东西一无所知。
举个例子,我正在写一个OpenGL应用程序,模型里包含一些简单的标准化多边形和多面体,这些在几何上很好处理,但不方便直接传给OpenGL。当视图收到一个事件,说明模型发生了变化(比如添加或修改了一个形状)时,视图会把这些多边形转换成可以传给OpenGL的非标准化顶点数组。这是一个比较耗费资源的操作,所以视图会把这些数组缓存到自己的一份“需要渲染的内容”中。当它进行渲染时,只需遍历这份'视图集合',把每个数组传给OpenGL。因此,视图是唯一知道OpenGL的代码,也是唯一知道如何将模型状态转换成OpenGL结构的代码。
把你的代码改成MVC的结构,意味着你需要创建一个“视图”,这个视图就像是你屏幕上看到的内容。这个视图会有一个叫做draw
的方法,这个方法会接收一个“模型”,这个模型是一个包含所有方块状态的Python对象,然后把这个模型显示到屏幕上。
也许这个draw
方法的工作就是对每个方块对象调用一个renderToScreen(screenView)
的方法,把它们显示出来。或者,如果你想让代码更“纯粹”,你也可以把这个draw
方法放到视图里面去。
MVC有时候可能让人感到困惑,尤其是当你刚开始学习面向对象编程时,你会发现有一个“绘制”方法可以让事情变得井井有条!
下面是实现游戏MVC的一种方式的概述:
模型:
- “Box”(箱子):箱子的类型(box_type)、位置的x坐标(pos_x)、位置的y坐标(pos_y)
- “BoxManager”(箱子管理器):一个箱子列表(list(Box))
- “GameState”(游戏状态):箱子管理器(box_manager)、玩家管理器(player_manager)、目标管理器(goal_manager)等
- “Move”(移动):x方向的变化(dx)、y方向的变化(dy)
控制器:
- “GameController”(游戏控制器):检查有效移动(check_valid_move(move))、移动玩家(move_player(move))、判断是否达到目标状态(is_goal_state())等
视图:
- “Screen”(屏幕):渲染屏幕(render())
- “PlayerRenderer”(玩家渲染器):渲染玩家(render())
- “BoxRenderer”(箱子渲染器):渲染单个箱子(render_box(box))、渲染所有箱子(render_all_boxes())
注意,模型是完全独立的,不会引用控制器或视图模块。这是MVC的一个关键特点。控制器负责处理玩家的输入,并改变全局游戏状态。而视图则查看全局游戏状态并渲染屏幕。
你不一定要采用这种方式,但了解和考虑一下这个选项是很有价值的。