如何在2D文字游戏上叠加图形效果(类似终端模拟器)?

1 投票
1 回答
752 浏览
提问于 2025-04-18 18:50

我正在开发一个以故事为主的2D游戏,界面像一个终端模拟器,我想加入一些图形效果,比如老电视屏幕的扭曲效果。现在所有的组件都是用Python 3.4写的,游戏需要能够在不同平台上运行(我在Mac上工作)。性能不是特别关键。游戏的主要窗口包含:

  • 终端界面(游戏在这里进行)
  • 菜单覆盖层(处理加载/保存功能以及选项等)
  • 图形覆盖层(在其他输出上渲染图形效果)。

到目前为止,我尝试了两种解决方案,一种是用Qt5,另一种是用SDL2,但我更想讨论Qt5的做法。使用Qt5,我已经成功实现了终端界面(QTextEdit)和菜单覆盖层(QGraphicsView),但我在整合图形效果覆盖层时遇到了问题。我考虑过使用OpenGL着色器或QGraphicsEffect,但后者在Mac OS X上无法使用。

我一直在使用vispy.gloo来进行OpenGL调用,而使用SDL2的方法,我能够实现一个基于着色器的图形效果。

根据这个链接,我尝试将这些效果整合到QMainWindow中,作为现有内容的覆盖层(下面的代码示例没有包含任何图形效果),但首先,这篇帖子并没有真正解决我的问题,其次,我遇到了Qt5的一个已知bug(这个链接)。

我非常感谢任何关于图形效果可能的实现想法,也欢迎推荐新的框架。非常感谢!

Qt5实现的代码示例:

class MainWindow(QtWidgets.QMainWindow):  # {{{1
    """Extension of the QMainWindow class."""

    def __init__(self, central=None, overlay=None, parent=None):
        """Constructor."""

        super(MainWindow, self).__init__(parent)

        self.parent = parent

        # For readability of the output, set a minimum size
        self.setMinimumSize(680, 480)

        # Create the central widget containing the terminal and focus
        self.widget = central  # Instance of QTextEdit
        self.setCentralWidget(self.widget)
        self.setFocusProxy(self.widget)

        # Create the menu overlay
        self.menu_overlay = overlay  # Instance of QGraphicsView
        self.menu_overlay.setParent(self)

        # Here, I would like to add the graphics effect (old TV screen) overlaid
        # on top of the central widget and the menu overlay. The g-e have to be
        # transparent to keyboard and mouse events.

        # Resize and show the main window.
        self.resize(1024, 768)
        self.show()

    def resizeEvent(self, event):
        """Resizes all child widgets that are not automatically resized (e.g. overlays)."""
        pass

1 个回答

1

我找到了解决我问题的方法。不过,不幸的是,我不得不从Python转向QML来实现这个图形效果。

根据Qt的QtQuick教程(http://qt-project.org/doc/qt-5/qtquick-shadereffects-example.html),下面这段代码可以创建一个OpenGL的着色器效果,它的输入来自另一个QML元素:

import QtQuick 2.2
import QtQuick.Window 2.1

Window {
    id: win
    visible: Window.AutomaticVisibility
    width: 640
    height: 480

    Rectangle {
        id: theItem
        anchors.horizontalCenter: parent.horizontalCenter

        // [...] your content here
    }

    ShaderEffectSource {
        id: theSource
        sourceItem: theItem
    }

    ShaderEffect {
        id: shader
        anchors.centerIn: theItem
        width: theItem.width
        height: theItem.height
        property variant u_sampler: theSource
        fragmentShader:
            // [...] any shader program using the following parameters:
            "uniform float qt_Opacity;" +
            "uniform sampler2D u_sampler;" +
            "varying vec2 qt_TexCoord0;" +
            "void main(void) {" +
            "    vec4 color = texture2D(u_sampler, qt_TexCoord0);" +
            "    color.a = qt_Opacity;" +
            "    gl_FragColor = color;" +
            "}"
    }
}

撰写回答