Skip to content

Saving and Loading Game States in a Visual Novel (Pygame)

In a visual novel, saving and loading game states is essential for providing users the ability to pause their progress and resume from the same point later. This document will cover how to implement a save and load system using Python's pickle module and Pygame. We will provide a practical, well-explained example that fits within the context of a visual novel.

Overview

The main goal of saving and loading game states is to store relevant game data, such as:

  • Current chapter or scene.
  • Dialogue progression.
  • Player choices and variables.
  • Background images, character sprites, and music status.

This guide will show how to:

  • Store these variables in a file (save).
  • Retrieve the variables (load) and restore the game state.

Setup: Required Libraries

We will use the following Python libraries:

  • pickle: To serialize and deserialize (save/load) game state objects.
  • os: To manage file paths for saved games.
  • Pygame: For the visual novel's structure.

Saving Game Data

We’ll store the game’s current state in a dictionary, including details like the current scene, the dialogue position, player choices, etc.

import pickle
import os

# Example game state
game_state = {
    'current_scene': 'chapter_2',
    'dialogue_position': 5,
    'player_choices': {'choice_1': 'yes', 'choice_2': 'no'},
    'background_image': 'background_day.png',
    'character_sprites': ['char1_happy.png', 'char2_neutral.png'],
}

# Function to save game state
def save_game(state, save_slot='save1'):
    save_path = os.path.join('saves', f'{save_slot}.pkl')
    with open(save_path, 'wb') as save_file:
        pickle.dump(state, save_file)
    print(f"Game saved in {save_slot}")

# Example: Saving the game state
save_game(game_state)

Explanation:

  • We store the game state as a dictionary and use pickle to serialize it.

  • The save_game function takes the game_state and saves it to a file (save1.pkl) in the saves directory.

Loading Game Data

When loading, we read the game state from a file and return the data to resume the game.

def load_game(save_slot='save1'):
    save_path = os.path.join('saves', f'{save_slot}.pkl')
    if os.path.exists(save_path):
        with open(save_path, 'rb') as save_file:
            state = pickle.load(save_file)
        print(f"Game loaded from {save_slot}")
        return state
    else:
        print(f"No saved game found in {save_slot}")
        return None

# Example: Loading the game state
loaded_game_state = load_game()
print(loaded_game_state)

Explanation:

  • The load_game function attempts to read a saved file. If it exists, it deserializes the data using pickle and restores the game state.

  • If the file doesn’t exist, it returns None and notifies the player that no save was found.

Integrating with Pygame

Let’s now see how this system works within a visual novel framework in Pygame. We'll simulate a basic structure where the game's progress is saved and loaded.

import pygame
import pickle
import os

# Initialize Pygame
pygame.init()

# Set up the game window
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Visual Novel')

# Game state variables
game_state = {
    'current_scene': 'intro',
    'dialogue_position': 0,
    'background_image': 'bg_intro.png',
    'character_sprites': []
}

# Load background function
def load_background(image_path):
    bg = pygame.image.load(image_path)
    screen.blit(bg, (0, 0))

# Save and Load functions from previous examples
def save_game(state, save_slot='save1'):
    save_path = os.path.join('saves', f'{save_slot}.pkl')
    with open(save_path, 'wb') as save_file:
        pickle.dump(state, save_file)
    print(f"Game saved in {save_slot}")

def load_game(save_slot='save1'):
    save_path = os.path.join('saves', f'{save_slot}.pkl')
    if os.path.exists(save_path):
        with open(save_path, 'rb') as save_file:
            state = pickle.load(save_file)
        print(f"Game loaded from {save_slot}")
        return state
    else:
        print(f"No saved game found in {save_slot}")
        return None

# Main game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # Press S to save the game
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_s:
                save_game(game_state)
            # Press L to load the game
            elif event.key == pygame.K_l:
                loaded_state = load_game()
                if loaded_state:
                    game_state = loaded_state

    # Load the background image from the game state
    load_background(game_state['background_image'])

    # Update the display
    pygame.display.update()

pygame.quit()

Explanation:

  • This Pygame loop displays the background based on the game_state.

  • Players can press the S key to save the game and the L key to load the game.

  • The load_background function loads and displays the background image according to the current game state.

Handling Multiple Save Slots

To add support for multiple save slots, you can modify the save and load functions to take a slot name dynamically:

def save_game(state, save_slot):
    save_path = os.path.join('saves', f'{save_slot}.pkl')
    with open(save_path, 'wb') as save_file:
        pickle.dump(state, save_file)

def load_game(save_slot):
    save_path = os.path.join('saves', f'{save_slot}.pkl')
    if os.path.exists(save_path):
        with open(save_path, 'rb') as save_file:
            return pickle.load(save_file)
    else:
        return None

The player can be prompted to enter a save slot name, such as save1, save2, etc.