Java连连看游戏源代码完整解析

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本压缩包包含连连看游戏的Java源代码,该游戏是一种广受欢迎的休闲益智游戏。源代码利用Java编程语言开发,适合学习和定制游戏。内容涉及游戏循环、用户输入、图形渲染、规则判断等关键功能,包含安装、导入、运行等使用说明,是Java游戏开发的宝贵学习资源。 llk.rar_游戏源代码

1. Java游戏开发概述

1.1 Java游戏开发的历程

Java自1995年问世以来,便因其“编写一次,到处运行”的跨平台特性,在游戏开发领域占据了一席之地。从早期的Applet小程序,到如今的大型网络Java游戏,Java展现了其在游戏开发领域的灵活性和强大的生命力。

1.2 Java游戏开发的优势与挑战

Java作为一门面向对象的编程语言,具有高度的抽象性和封装性,为游戏开发提供了丰富的API和图形库支持,如AWT, Swing, JavaFX等。然而,Java在性能上与C++等语言相比存在劣势,尤其是在图形处理和实时性能方面,这使得Java游戏开发面临了不少挑战。

1.3 Java游戏开发的现状与趋势

随着Java虚拟机(JVM)性能的不断提高,以及游戏引擎如LibGDX, jMonkeyEngine的发展,Java游戏开发正在逐步克服性能瓶颈。目前,Java主要应用于小型游戏和教育领域,未来可能会因为云游戏和跨平台游戏的兴起,迎来新的发展契机。

2. 连连看游戏逻辑的实现

2.1 Java编程语言基础

2.1.1 Java的数据类型与结构

Java编程语言提供了丰富而灵活的数据类型,支持开发者创建多种结构的数据。基本数据类型包括整型、浮点型、字符型和布尔型,它们分别对应了 int , float , char , boolean 。Java的数据类型可以分为两大类:基本类型和引用类型。基本类型直接存储数据值,而引用类型则存储对数据对象的引用。

在连连看游戏中,基本数据类型经常用于存储如玩家得分、游戏计时器的数值。例如,玩家每次完成一个有效的匹配,得分可能会以基本数据类型 int 的形式增加。

int playerScore = 0; // 初始得分为0
playerScore += 10; // 完成一次匹配后,玩家得分增加10分

引用数据类型包括类、接口、数组等。在游戏开发中,我们常常创建类来表示游戏中的各种实体和逻辑,如表示游戏板的 Board 类,或代表游戏状态的 GameState 类。

2.1.2 Java面向对象编程基础

Java是一种面向对象的编程语言,它提供了类和对象等概念,这有助于管理和组织代码。面向对象编程(OOP)的主要概念包括类、对象、继承、多态和封装。

  • 类:定义了对象的状态和行为。
  • 对象:类的实例。
  • 继承:允许一个类继承另一个类的属性和方法。
  • 多态:允许不同类的对象对同一消息做出响应。
  • 封装:隐藏对象的内部状态和行为的实现细节,只向外界提供访问接口。

在连连看游戏中,使用面向对象的方法可以构建清晰的代码结构。例如, Tile 类可以代表游戏中的一个拼图块,它会有自己的状态(例如,位置、是否已被选中)和行为(例如,翻转或匹配)。

public class Tile {
    private boolean selected; // 拼图块是否被选中
    private boolean matched; // 拼图块是否已匹配

    public void select() {
        selected = true;
    }

    public boolean isSelected() {
        return selected;
    }

    // 其他与Tile相关的状态和行为方法
}

通过面向对象编程,我们可以将游戏逻辑封装在不同的类中,使得代码更加模块化、易于理解和维护。

2.2 游戏逻辑核心算法

2.2.1 图的遍历与匹配

在连连看游戏中,游戏板可以看作一个图结构,其中每个拼图块代表图中的一个节点。游戏的主要逻辑之一是检测玩家选择的两个拼图块是否可以连接。这可以转化为图论中的路径问题。

使用深度优先搜索(DFS)或广度优先搜索(BFS)算法可以遍历图以检查节点之间的连接性。以DFS为例,我们可以通过递归遍历从一个节点开始的所有可能路径,直到找到匹配的拼图块或到达死路。

public boolean dfs(Tile[][] board, Tile start, Tile end) {
    // 检查结束条件,即找到匹配的拼图块
    if (start.equals(end)) {
        return true;
    }

    // 将当前拼图块标记为已访问
    start.setVisited(true);

    // 遍历所有相邻的拼图块
    for (Tile neighbor : start.getNeighbors()) {
        // 如果邻近的拼图块未被访问且不是起始拼图块,则递归调用dfs
        if (!neighbor.isVisited() && !neighbor.equals(start)) {
            if (dfs(board, neighbor, end)) {
                return true;
            }
        }
    }

    // 回溯:取消当前拼图块的已访问标记
    start.setVisited(false);

    return false;
}

在上述代码示例中, Tile 类需要实现 getNeighbors() setVisited() equals() 方法来支持图的遍历逻辑。

2.2.2 时间复杂度与算法优化

遍历算法的时间复杂度对于游戏性能至关重要,特别是在需要快速响应的场合。在上面的DFS示例中,如果游戏板很大,遍历可能会非常慢,因为其时间复杂度为O(V+E),其中V是节点数,E是边数。在最坏的情况下,时间复杂度可能接近于O(V^2)。

为了优化算法性能,可以实现一些启发式方法,比如限制遍历的深度,或者使用A*搜索算法等更高效的路径搜索策略。此外,可以缓存已匹配的拼图块对,从而减少需要计算的路径数量。

public class MatchCache {
    private Map<Pair<Tile, Tile>, Boolean> cache = new HashMap<>();

    public boolean isMatched(Tile tile1, Tile tile2) {
        Pair<Tile, Tile> pair = new Pair<>(tile1, tile2);
        return cache.getOrDefault(pair, false);
    }

    public void addMatchedPair(Tile tile1, Tile tile2, boolean matched) {
        Pair<Tile, Tile> pair = new Pair<>(tile1, tile2);
        cache.put(pair, matched);
    }
}

在这个例子中, MatchCache 类用于存储和查询已匹配的拼图块对,从而减少了算法的重复计算,提高了性能。

3. 图形用户界面设计与实现

图形用户界面(GUI)是现代软件应用不可或缺的一部分,尤其对于游戏来说,它直接关系到用户体验的直观感受。在Java中,通过Swing和JavaFX这样的图形包,开发者可以设计出丰富多彩且直观的用户界面。本章节将探讨如何设计与实现图形用户界面,以及如何通过事件机制响应用户的操作。

3.1 用户界面组件的设计

用户界面组件是构成整个图形用户界面的基础元素。组件的设计需要充分考虑用户体验、界面布局和风格统一性。

3.1.1 界面布局与风格统一

为了实现美观且用户友好的界面,开发者需要遵循一些设计原则。布局管理器是Swing组件的核心,它负责确定组件在容器内的位置和大小。最常用的布局管理器包括FlowLayout, BorderLayout, 和GridBagLayout等。一个良好的界面布局应该清晰直观,使用户能够迅速找到需要的功能。

下面是一个使用 BorderLayout 的基本示例代码,展示如何创建一个简单的界面布局:

import javax.swing.*;

public class MainFrame extends JFrame {
    public MainFrame() {
        setTitle("连连看游戏");
        setSize(800, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置面板的布局管理器
        setLayout(new BorderLayout());

        // 添加中间的面板,用于游戏的主要内容
        add(new GamePanel(), BorderLayout.CENTER);
        // 添加底部控制面板
        JPanel controlPanel = new JPanel();
        JButton startButton = new JButton("开始游戏");
        JButton exitButton = new JButton("退出游戏");
        controlPanel.add(startButton);
        controlPanel.add(exitButton);
        add(controlPanel, BorderLayout.SOUTH);
        setVisible(true);
    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

在上述代码中,我们创建了一个 MainFrame 类继承自 JFrame ,并通过 BorderLayout 布局管理器将界面分为中部的游戏面板和底部的控制面板。在实际的项目中,开发者需要根据具体需求设计更多的组件和布局。

3.1.2 组件交互逻辑实现

用户界面组件之间的交互逻辑是实现良好用户体验的关键。组件之间的通信通常通过事件监听器来实现。事件监听器允许用户在与组件交互时(例如点击按钮、输入文本等)触发特定的响应。

为了实现组件间的交互逻辑,开发者需要为组件添加事件监听器。下面是一个简单的示例,演示了如何为按钮添加点击事件监听器:

// 继承ActionListener接口
class GameStartListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        // 在这里添加开始游戏的逻辑
        System.out.println("游戏开始");
    }
}

// 在界面上创建按钮,并为其添加监听器
JButton startButton = new JButton("开始游戏");
startButton.addActionListener(new GameStartListener());

在这个例子中,我们创建了一个 GameStartListener 类实现了 ActionListener 接口,并重写了 actionPerformed 方法,在按钮被点击时输出“游戏开始”。然后我们创建了一个按钮 startButton ,并通过 addActionListener 方法为它添加了我们自定义的监听器实例。

3.2 图形用户界面的事件机制

事件机制是GUI的核心概念之一,它允许用户通过操作界面组件(如点击按钮、输入文本等)来与程序进行交互。

3.2.1 界面响应事件的分类

Java中GUI事件主要分为两大类:低级事件和高级事件。低级事件包括鼠标、键盘事件,它们直接对应于用户的物理输入。高级事件包括动作事件、窗口事件等,它们通常是低级事件的抽象,例如动作事件可能由用户点击按钮触发。

在Java Swing中,每个组件都可以注册一个或多个事件监听器。这些监听器会响应相应的事件,并执行一些特定的操作。例如:

// 添加键盘事件监听器
myTextField.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        // 当按下键盘时执行的操作
    }
});

3.2.2 事件监听器的实现与应用

事件监听器是接口,需要通过实现特定的方法来创建。Swing提供了大量的监听器接口,如 MouseListener KeyListener ActionListener 等。每个接口都定义了一系列的方法,以便在事件发生时被调用。

为了提高代码的可维护性,通常将事件监听器的实现放在单独的类中。以下是一个将事件监听器实现分离出来的示例:

// 创建一个单独的类实现ActionListener接口
public class StartGameAction implements ActionListener {
    private MainFrame mainFrame;

    public StartGameAction(MainFrame mainFrame) {
        this.mainFrame = mainFrame;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        mainFrame.startGame();
    }
}

// 在界面类中,使用构造器将监听器与主窗口关联
public class MainFrame extends JFrame {
    private JButton startButton;
    public MainFrame() {
        startButton = new JButton("开始游戏");
        startButton.addActionListener(new StartGameAction(this));
        // 其他代码...
    }

    public void startGame() {
        // 开始游戏的具体逻辑
    }
}

在这个设计中,我们创建了 StartGameAction 类实现 ActionListener 接口,并在 actionPerformed 方法中定义了点击开始游戏按钮后执行的逻辑。然后,在 MainFrame 类中将按钮的监听器设置为 StartGameAction 的实例。这种设计使得界面类 MainFrame 不需要直接了解事件处理的细节,从而提高了代码的模块化和可重用性。

在本章节中,我们深入了解了图形用户界面的设计和实现方法,包括界面布局、组件交互逻辑、以及事件监听器的构建与应用。通过这些知识的掌握,读者将能为游戏等应用程序打造出既美观又功能强大的用户界面。在后续章节中,我们将探讨游戏的状态管理和事件处理,进一步提升游戏的交互性和用户体验。

4. 游戏状态管理和事件处理

游戏状态管理是游戏设计中的核心组成部分,它负责追踪和切换游戏中的各种状态,如开始菜单、游戏进行中、暂停、结束等。合理的状态管理机制能够使游戏的控制逻辑更加清晰,增强用户体验。而事件处理则是游戏响应用户操作和内部事件的机制,它通过事件驱动编程实现,是游戏开发中不可或缺的技术之一。

4.1 游戏状态的管理与切换

在游戏开发过程中,游戏状态的定义和管理是相当重要的。游戏状态指的是游戏在某个特定时刻的快照,包括玩家的生命值、分数、所处的关卡等信息。状态管理需要确保在任何时刻,游戏都能根据当前的状态采取适当的行动。

4.1.1 游戏状态的定义与切换逻辑

游戏状态可以使用枚举类型、类或单例模式来定义。例如,使用枚举类型定义状态可以这样做:

public enum GameState {
    MAIN_MENU, // 主菜单
    PLAYING,   // 游戏进行中
    PAUSED,    // 游戏暂停
    GAME_OVER, // 游戏结束
    LEVEL Completed // 级别完成
}

切换逻辑则需要在游戏逻辑的关键点调用,如玩家按下暂停按钮、游戏结束条件触发等。对于简单的状态切换,我们可以直接在事件处理函数中修改状态变量。而对于更复杂的状态管理,我们可以考虑使用状态模式(State Pattern),这是一种行为设计模式,允许对象在内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

4.1.2 状态模式在游戏中的应用

状态模式的核心是上下文(Context)和状态(State)。上下文是一个状态机,拥有状态对象的引用,状态对象定义了游戏某个状态的行为。以下是一个简化的状态模式示例:

// 状态接口
public interface GameState {
    void handle(Context context);
}

// 具体状态实现
public class PlayingState implements GameState {
    @Override
    public void handle(Context context) {
        // 处理游戏进行中的逻辑
    }
}

// 上下文类
public class Context {
    private GameState state;

    public void setState(GameState state) {
        this.state = state;
    }

    public void request() {
        state.handle(this);
    }
}

// 客户端代码
Context context = new Context();
context.setState(new PlayingState());
context.request(); // 切换到游戏进行中的状态并调用相应逻辑

在实际游戏开发中,状态管理可能涉及复杂的逻辑和状态之间的转换,例如玩家生命值为0时游戏结束。状态模式通过分离状态行为,提高了代码的可读性和可维护性。

4.2 游戏事件的处理机制

游戏事件处理是游戏编程中的一项关键技术,它允许游戏响应玩家的操作,如点击、按键、定时事件等,还可以响应内部事件,如动画完成、碰撞检测等。

4.2.1 事件处理框架的构建

构建一个事件处理框架需要定义事件类型、事件监听器和事件分发器。事件类型可以是一个枚举,事件监听器定义为接口,事件分发器负责将事件分发给相应的监听器。

// 事件类型枚举
public enum EventType {
    KEY_PRESSED, 
    MOUSE_CLICK,
    TIMER_EXPIRED
}

// 事件监听器接口
public interface GameEventListener {
    void handleEvent(Event event);
}

// 事件分发器
public class EventDispatcher {
    private List<GameEventListener> listeners = new ArrayList<>();

    public void addListener(GameEventListener listener) {
        listeners.add(listener);
    }

    public void dispatchEvent(Event event) {
        for (GameEventListener listener : listeners) {
            listener.handleEvent(event);
        }
    }
}

4.2.2 事件驱动编程的实践

事件驱动编程是一种程序设计范式,核心思想是程序的流程是由外部事件来驱动的。在游戏开发中,事件驱动编程允许游戏以非线性的方式运行,为玩家提供了更大的自由度。

在实践中,游戏引擎通常已经内置了事件处理机制。例如,在Java中,可以使用Swing或JavaFX库构建图形用户界面,这两个库提供了丰富的事件监听器接口。在实现事件监听时,需要重写这些接口的方法,以响应相应的事件。

// 鼠标点击事件监听器的实现
textField.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON1) {
            // 处理鼠标左键点击事件
        }
    }
});

在上面的例子中,通过添加一个 MouseListener 的匿名子类,并重写 mouseClicked 方法,实现了一个鼠标点击事件的监听器。在事件发生时,游戏逻辑可以据此执行相应的代码。

事件处理机制对于游戏开发来说,是至关重要的。它不仅处理了用户的输入,还可以处理游戏内部的逻辑,如定时器事件等。通过构建一个良好的事件处理框架,可以提高游戏的响应性和交互性,优化玩家体验。

5. 动画制作与游戏循环构建

5.1 动画效果的实现方法

动画是游戏中的灵魂,它使静态的画面动起来,为玩家带来视觉上的享受。在连连看游戏中,动画不仅用于提升玩家的视觉体验,还用于指导玩家理解游戏规则,比如匹配成功时的动画效果可以让玩家知道成功消除了一对相同的图案。

5.1.1 帧动画与时间控制

帧动画是最传统的动画形式之一,它通过连续播放一系列静态图片来形成动态效果。在Java中实现帧动画需要使用到 javax.swing.Timer 类来进行时间控制,该类可以定时执行任务。以下是实现帧动画的简化代码示例:

import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class AnimationPanel extends JPanel implements ActionListener {
    private final int DELAY = 100; // 动画帧间隔时间
    private Timer timer;
    private Image[] animationFrames; // 存储动画每一帧的图片

    public AnimationPanel(Image[] frames) {
        this.animationFrames = frames;
        timer = new Timer(DELAY, this);
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(animationFrames[currentFrameIndex], 0, 0, this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        currentFrameIndex = (currentFrameIndex + 1) % animationFrames.length;
        repaint();
    }
}

在上面的代码中, DELAY 变量定义了每一帧播放的时间间隔, Timer 对象用于定时触发 actionPerformed 方法,更新当前帧的图片并请求重绘面板。

5.1.2 动画效果的优化策略

动画优化的核心是提高帧率和减少不必要的计算。在连连看游戏中,可以通过以下几个策略来优化动画效果:

  • 使用双缓冲技术 :通过 createImage 方法创建一个图像缓冲区,先将动画绘制到这个缓冲区上,最后一次性绘制到屏幕上,可以有效减少画面闪烁。
  • 预加载动画资源 :游戏启动时,将所有动画帧预先加载到内存中,避免在播放动画时进行资源的读取操作。
  • 适当调整帧率 :根据游戏运行的硬件条件适当调整帧率,确保动画流畅性的同时,减少CPU的负担。

5.2 游戏循环与资源管理

游戏循环是游戏运行时的核心控制逻辑,负责游戏状态的更新和渲染。资源管理是保证游戏能够高效稳定运行的关键,涉及到游戏资源的加载、管理和释放。

5.2.1 游戏主循环的设计与实现

游戏主循环负责游戏的主逻辑,包括处理用户输入、更新游戏状态和渲染画面。以下是简单的游戏主循环实现:

public void gameLoop() {
    while (isRunning) {
        processInput(); // 处理用户输入
        updateGame();   // 更新游戏状态
        render();       // 渲染画面
        try {
            Thread.sleep(16); // 控制循环时间,约60帧每秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,游戏循环每帧都会先处理输入,然后更新状态,最后进行渲染。 Thread.sleep(16) 控制循环时间,大约可以达到每秒60帧的渲染速度。

5.2.2 游戏资源的加载与释放

游戏资源包括图像、音频、字体等,正确管理这些资源是保证游戏顺利运行的前提。以下是资源加载和释放的基本原则:

  • 资源预加载 :在游戏开始阶段预加载必要的资源,避免在游戏过程中加载资源导致的卡顿。
  • 资源释放时机 :游戏结束后释放所有资源,或者在资源长时间不用时将其从内存中释放,以避免内存泄漏。
public void loadResources() {
    // 加载图像资源
    Image image = Toolkit.getDefaultToolkit().getImage("path/to/image.png");
    // 加载音频资源
    Audio audio = AudioSystem.getAudioInputStream("path/to/audio.wav");
}

public void unloadResources() {
    // 释放图像资源
    // 释放音频资源
    // 释放其他资源
}

在这个示例中,资源的加载和释放方法被设计为单独的函数,方便在需要的时候调用。具体的加载和释放逻辑需要根据实际的资源类型和使用方式来定制。

6. 用户输入处理与连连看游戏规则编码

6.1 用户输入的捕获与解析

6.1.1 鼠标和键盘事件的处理

在Java中处理鼠标和键盘事件通常通过实现相应的接口完成。对于连连看游戏而言,用户输入主要是通过鼠标点击来完成的。我们可以使用AWT库中的 MouseListener MouseMotionListener 接口来捕捉和处理鼠标事件。

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

public class GamePanel extends JPanel implements MouseListener, MouseMotionListener {
    // ...

    @Override
    public void mouseClicked(MouseEvent e) {
        // 处理鼠标点击事件
        int x = e.getX();
        int y = e.getY();
        // 将屏幕坐标转换为游戏坐标
        // ...
        // 根据坐标判断点击的方块并进行处理
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        // 处理鼠标移动事件(例如高亮显示可消除的方块)
    }

    // 其他事件处理方法(mousePressed, mouseReleased, mouseEntered, mouseExited)
    // ...
}

鼠标事件处理是用户交互的关键部分,它需要详细地判断用户的意图并给予相应的游戏反馈。

6.1.2 输入响应与逻辑判断

在捕捉到用户的输入后,我们需要根据连连看的游戏规则来判断用户的输入是否有效,并作出相应的逻辑判断。比如,在用户点击了两个相同的图案后,我们需要判断它们是否满足消除的条件(是否可以在不超过三条线的情况下连接)。

6.2 连连看游戏规则的编码实现

6.2.1 游戏规则逻辑的详细编码

连连看的游戏规则可以分解为几个主要部分:方块的匹配、路径的查找以及消除的验证。在编码实现中,我们需要将这些规则逐一实现。

public class LianliankanGame {
    // ...
    public boolean checkAndRemoveBlocks(int block1X, int block1Y, int block2X, int block2Y) {
        // 验证两个方块是否可以连接消除
        // ...
        // 如果可以消除则移除方块,返回true
        return true;
    }

    // 其他游戏逻辑方法,如初始化游戏板、随机排列方块等
    // ...
}

代码中的 checkAndRemoveBlocks 方法是一个核心方法,它将负责检查两个方块是否可以通过合法路径消除,并在验证成功后将它们从游戏板中移除。

6.2.2 规则测试与代码维护

测试是保证游戏质量的关键步骤。在连连看游戏中,我们需要为各种规则编写单元测试,确保每一个逻辑判断都能得到正确执行。此外,随着游戏的发展,代码维护也是不可忽视的部分,包括代码重构、性能优化和文档更新。

// 示例代码块,假设有单元测试框架和测试类
public class LianliankanGameTest {
    public void testBlockMatching() {
        // 测试方块匹配逻辑是否正确
    }

    // 其他测试方法
    // ...
}

编写测试用例并定期运行可以帮助开发者快速定位问题,同时确保代码的健壮性。对于一个复杂的游戏来说,良好的测试和维护策略可以显著降低后期开发和运营的风险。

在这一章节中,我们详细探讨了连连看游戏中的用户输入处理和游戏规则编码实现。首先讲解了如何通过接口捕获鼠标和键盘事件,然后深入到游戏规则的具体实现,以及如何进行有效的测试和代码维护。这些内容为后续章节的游戏开发文档编写提供了坚实的基础。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本压缩包包含连连看游戏的Java源代码,该游戏是一种广受欢迎的休闲益智游戏。源代码利用Java编程语言开发,适合学习和定制游戏。内容涉及游戏循环、用户输入、图形渲染、规则判断等关键功能,包含安装、导入、运行等使用说明,是Java游戏开发的宝贵学习资源。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值