Skip to main content
added 2 characters in body
Source Link
Nicol Bolas
  • 26.1k
  • 3
  • 78
  • 104
replaced http://gamedev.stackexchange.com/ with https://gamedev.stackexchange.com/
Source Link

I agree with Kevin Ried's answerKevin Ried's answer in part: if you intend to be able to save and reload your game arbitrarily, then you'll need to solve this problem in addition to many more. However, if your game simply doesn't need this (for whatever reason), then you can employ less invasive measures.

I agree with Kevin Ried's answer in part: if you intend to be able to save and reload your game arbitrarily, then you'll need to solve this problem in addition to many more. However, if your game simply doesn't need this (for whatever reason), then you can employ less invasive measures.

I agree with Kevin Ried's answer in part: if you intend to be able to save and reload your game arbitrarily, then you'll need to solve this problem in addition to many more. However, if your game simply doesn't need this (for whatever reason), then you can employ less invasive measures.

Source Link
Nicol Bolas
  • 26.1k
  • 3
  • 78
  • 104

I agree with Kevin Ried's answer in part: if you intend to be able to save and reload your game arbitrarily, then you'll need to solve this problem in addition to many more. However, if your game simply doesn't need this (for whatever reason), then you can employ less invasive measures.

The best way to solve it for such a game is to be disciplined in what is considered actual data and what is not.

For example, let us say you're making a top-down 2D space shooter game. And you want to test the AI of an entity. So you have a script that governs the control over that entity.

Your script should contain and manage all of the data needed to control it, but your actual code should only be calling the UpdateControllerAction function, which calls the script. This function will tell the game what the entity is doing: accelerating in direction X, firing in direction Y, etc. That is, there is a very rigid interface between external code and the script. The script can go out and detect things (being targeted by unit X, was hit last frame by unit Y, etc). But everything it does happens in here.

When it comes time to reload this script, you simply take the Controller object (the AI that knows about scripts, the thing that holds the script itself). And you tell it to reload its script, which will have the script do all of its necessary preamble work and return the new main function. From that point on, the UpdateControllerAction will call the new script function.

You let garbage collection take care of the old data. Since the script was responsible for keeping track of targets and such, reloading the script clears those. So the downside is that reloading will mean that the entity will basically be starting from a neutral state.

This mechanism works because of the separation of responsibility and having a minimal interface between the script and the outside world. The C++ world only keeps track of the main script function, not a bunch of script data and such. The script world can call into the C++ world (to find targets, etc), but all of its persistent data stays within the script world. It is therefore bound to the script function; when that dies, everything goes with it. New scripts, even new instances of the same script, can't access that data.

Here's how this would work in a language like Lua. This script would represent a minimal behavior script. It's just an example, so don't take it too seriously in terms of AI logic.

--Arguments to this script are this ship's information and a list of its allies.
local shipData, alliedList = ... 

local targetList = {}

--Updates and prioritizes the targetList.
local function FindTargets()
  --...
end

--The AI function.
return function()
  FindTargets();

  if(#targetList ~= 0) then
    local currTarget = targetList[1]
    AttackTarget(currTarget);
  else
    --No target.
    FlyStraight();
  end
end

Each time you run this script, you pass it a list of allies and data on it's ship. And it returns the function you will call from UpdateControllerAction.

Now, you could use multiple functions instead of one. Maybe an update controller function and a message processing function for when certain events happen to the entity (it gets hit, someone targets it, allies change, etc). But the point is to keep the interface both minimal and hidden from C++.