1
$\begingroup$

This is what I want to accomplish: User starts the game and a scene shows up (a landscape). An audio file plays telling the user a story about the fictional world my game is set on. After that, the user might want to change to another scene showing information about an object (let's say a plant or animal) but when coming back to my previous scene (the landscape) I don't want the audio file with the story about my world to play once again. My user might want to pick several objects in the first scene so I don't want the story to be replayed every time he/she picks an object.

I've tried through messages from one scene to another but found out that is not possible since messages only work with objects within one scene.

I decided to try Python scripting, adding a game property to my first scene and changing the property value through code but even though my variable value changes to "4" from its initial value of "2", it still keeps on playing the file, like shown in here:

enter image description here

As you can see my property value changes but the "Property Sensor" is still being triggered, causing the audio file to be played.

Do you have any suggestion on how to accomplish what I want?

$\endgroup$
1
$\begingroup$

You need to store and restore the game state.

Game State

The game state are all changes to the game that happens while playing the game. This includes the position of the camera as well as the currently played pose of an action as well if a menu is visible or not.

In general the game state is covered by the active scenes. When you remove a scene all states from that scene will get removed too.

-> imagine you moved the default cube from (0,0,0) to (2,0,0). You remove the scene ... the cube will be removed together with the changed position and everything else in the scene. There is no way to recover that information.

Storing

As the scene will get lost you can't use the scene as storage. Alternative storage is:

  • other scenes (exist as long as the other scene exist)
  • messages (exist for a single frame, can store a single string)
  • python modules (exist until the module get unloaded or the game ends)
  • files (exist longer than the game session)

In all cases you will need Python to at least restore the data.

This answer will focus on using python modules as storage as this is the most flexible and generic storage (beside keeping the scene alive).

Python module storage

Advantages: - the storage lives until the game session ends - accessible from any active scene - fast read - fast write

(Do not confuse python modules with python scripts - scripts will not persist!)

Basically you write the data to be stored into a module attribute.

storage = "I'm a string to be stored"

The storage has to be at module level (no indentation) Alternative you can write to a module attribute from within a function:

def demonstrateStoring(dataToBeStored):
   global storage
   storage = dataToBeStored

...
demonstrateStoring("I'm a string to be stored")
...

Do not forget keyword global!

Typically you want to store more than a single object. The easiest way is to create a separate object that acts as "sub-storage". It can be any container.

Example usable at your situation

Here I suggest to use a dictionary. It allows you to access the data by a key such as a string:

[Module] storage.py - do not set it up at the python controller directly

storage = {}

def put( key, data ):
    storage[key] = data

def get(key, fallback=None):
    return storage.get(key, fallback)

[Script] store_audio_already_played

import storage

storage.put("audio already played", True)

[Script] activate_when_audio_never_played

import storage
import bge

if not storage.get("audio already played", False):
    controller = bge.logic.getCurrentController()
    for actuator in controller.actuators:
        controller.activate(actuator)

This example is already a solution to your question. What will happen:

A) when the start-up scene gets loaded you check if the storage contains the key "audio already played". At the first run it should not be present. The above script activate_when_audio_never_played will activate all actuators (including your audio actuator).

enter image description here

B) You need to run the script store_audio_already_played after A). Either you place the controller below the controller of A) or you activate the controller at the second frame of the scene. The last one would be better. That way you can be sure it was played at least a little bit.

enter image description here

This one does not need actuators as it acts as actuator already.

Finally that is all.

Process flow:

frame 1 - startup scene:

  • always sensor triggers controller activate_when_audio_never_played
  • controller checks storage for "audio already played"
  • "audio already played" not present
  • activate actuators
  • audio get played

frame 61 - startup scene (after 1 second):

  • delay sensor triggers controller store_audio_already_played
  • controller stores True with key "audio already played"

frame 61 + t - startup scene:

  • switch to other scene

frame 61 + t + s - any scene:

  • switch to startup scene

frame 61 + t + s +1 - startup scene:

  • always sensor triggers controller activate_when_audio_never_played
  • controller checks storage for "audio already played"
  • "audio already played" is present
  • no actuators get activated
  • no audio get played

I suggest to evaluate the process flow when the scene gets switched before frame 61.

Remarks

The module storage is quite powerful. You can store any object your like. You can store as many objects you like. You can even store other containers (such as lists and dicts) in the storage.

It is important to note that you should not store data that can be destroyed such as game objects. You can store them but as soon as you switch scenes your storage will refer to invalid objects (see: KX_GameObject.invalid). It is better to avoid them.

Such a storage can easily be used to create save points. Save points contain the data at the moment of creating them. On request you can restore this data back to scene.

Typically you can save a storage to a file (saving) and load it within the same or another game session (loading). This way the data survives ending the current game. [Assuming you do not store objects that can't be restored such as KX_GameoObject or mathutils.Vector].

You can never "return" to a scene. When you removed it the scene and all of the changes are gone. You can load a scene from the same source. It looks equal as it is constructed the same way. But it is a completely new scene. Any changes of the other (dead) scene will not be present in the new scene.

$\endgroup$
  • $\begingroup$ Thank you so much for your help! This is the second time I see you around and I find your answers to always be so detailed, complete and educative. Before reading your post I kind of find a solution by playing the audio file from outside of a method in a script set in Module, (so the audio would play just once at the beginning) but this solution you have given seems a better way to implement the functionality I was looking to obtain. Thank you so much, once again. $\endgroup$ – PAT Oct 20 '16 at 14:43
  • $\begingroup$ I haven't thought about using the module initialization. I nice idea and fits your situation perfectly. The above solution is more a general solution and serves later additional features such as save/load. $\endgroup$ – Monster Oct 21 '16 at 4:22
  • $\begingroup$ Ok. Guess I'll reconsider both options then... $\endgroup$ – PAT Oct 21 '16 at 16:17
2
$\begingroup$

Do not come back to the start scene. There is no reason to do so. The start is done, so the player should continue with the next steps. (Unless this is what your game flow is supposed to do.)

$\endgroup$
1
$\begingroup$

Do not leave the level scene. It is a perfect storage. You can hide this scene by adding overlay scenes. When you remove the overlay scenes you are back at the level scene.

It will even continue to run while you watch the overlay scene.

When you do not want that you can suspend the level scene.

$\endgroup$
  • $\begingroup$ Alright! Maybe this approach would help with the camera position in the starting scene so I wont need to store where the camera is located in order to go back to that same position when "returning" (reloading) from the new scene (?) $\endgroup$ – PAT Oct 20 '16 at 14:48
  • $\begingroup$ Exactly. The scene remains as it is. But, only fits when you do not have that many scenes e.g. you show a menu, or inventory scene. If your game world consists of different scenes (various outdoor scenes, various in house scenes) the costs [time and memory] would outplay the benefits. $\endgroup$ – Monster Oct 21 '16 at 4:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.