Author Topic: Cutscenes, dialogs, etc  (Read 10576 times)

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Cutscenes, dialogs, etc
« on: 2009-09-24, 01:54:42 PM »
Hi guys, I'm fishing for ideas here! :)

I've got a couple of rooms done in the first level of my game now.  I felt like it was a good time to try to add something else, so I decided to implement the first dialogs / cutscenes.

In my game, almost all rules verify if the counter Pause equals 0.  If yes, they execute, otherwise, they don't.  When there is a dialog, I put the game in pause, I set another counter "InDialog" to 1 and I execute a special set of rules that execute only then...  But...  I don't know.  It seems like a lot of work and a lot of specific rules for each dialog...  And I'm a little afraid to mess up the sprites that will be used in the gameplay by mistake.

Would there be a better approach?  I thought that maybe I would make map only for dialogs, with sprites specifically designed for these cutscenes, so there would be no danger to mess up the sprites that will be used for the actual gameplay afterwards.  Hoping that swapping cutscene maps and gameplay maps would be invisible to the player... Would it make the game too big to have maps just for cutscenes?

Has anyone done something like this before and found a good way to do it?   :suspious:

Thanks a lot! :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #1 on: 2009-09-24, 02:28:04 PM »
I'm not quite clear on what you're trying to accomplish with InDialog, or what your question is relative to these counters.  You say that it seems like a lot of work, but what is this work accomplishing? When you ask "Would there be a better approach," I ask, "approach to what?"

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Cutscenes, dialogs, etc
« Reply #2 on: 2009-09-24, 02:43:30 PM »
Okay, I admit it was not too clear.  I got confused! :P

I'm trying to make a conversation between 2 characters.  Basically, it works like this: 
- Stop the gameplay (player cannot control the character)
- Character 1 does something (an animation, moves around, etc). 
- Then everything stops and there is a dialog shown, with the text the character is saying (Not a dialog window like a "yes" "no" window, just a part of a conversation).  This dialog stays there until the player presses the proper message dismissal button. 
- When the message disappear, Character 2 does something (an animation, moves around, etc.)
- Then everything stops and there is a dialog shown, with the text the character is saying (Not a dialog window like a "yes" "no" window, just a part of a conversation).  This dialog stays there until the player presses the proper message dismissal button. 
- (Repeat as needed until the conversation is over.) 
- Finally resume the gameplay, giving back the control to the player.

The two counters "Pause" and "inDialog" are used to indicate which are the rules to execute.   I couldn't rely only on the "Pause" counter, because I also use it when the menu appears, etc.  So I had to put the game on hold with the "Pause" counter and then confirm that it really is a dialog or a cutscene that I want to execute by setting another counter: "InDialog".

I'm saying it seems like a lot of work, because I need to be careful with what I do with the sprites during the conversation (since all their rules are turned off, they could walk trough a wall, etc) and, well, it basically becomes like making a movie rather than a game.  There is no real timeline tools to do, for example: "Character 1 moves toward Character 2 for 1 second (or on a specific distance) then stops, then show the dialog, then wait for input".  So I wondered if it would e a better approach to duplicate my gameplay maps to make cutscene maps.  This way I could isolate the problems and be sure that everyhing is okay when I load the gameplay map.

But then, I'm not sure it is the best approach and I haven't tried it either.  So I wondered if someone actually accomplished something similar previously.

I hope it is easier to understand now! :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #3 on: 2009-09-24, 05:59:43 PM »
I always thought that cutscenes would best be handled by re-routing the inputs for some or all of the sprites.  That's why the inputs for each sprite are separated from the player objects and other potential sources of input.  Then you can route inputs from other sources.  My thought is that all the sprites would continue executing the same rules, but instead of mapping input from player objects, the input would come from a Scripted-Input object of some sort, and maybe the script object is created by you recording inputs from a player object.  This has been done in previous implementations of SGDK, but I don't remember if we have it for SGDK2 yet.

Basically this is how it works.  We set up some class that collects the input from the player object.  Note that each set of inputs can be represented as 1 byte (InputBits enum uses bits 0 through 7).  So it's just collecting 1 byte per frame.  The class tracks 2 values: the current input value and the counter value tracking how many frames that same set of inputs has been active.  If the current inputs byte is the same as the last inputs byte, and the counter is less than 255, then you increment the counter, otherwise you add the counter and the input value bytes to the list of bytes, reset the counter and remember the new input value.  Your list of bytes ends up looking like this:
Byte 0: Input value (which inputs are currently being pressed)
Byte 1: Counter value, how many frames are these inputs repeated
Byte 2: Input value
Byte 3: Counter value
etc...

These bytes could be stored nicely in a custom object's embedded data, and the custom object could load them and replay the same actions on that sprite when commanded to become a source of that sprite's input.

It's a relatively compact way to record inputs for a sprite.  Then the sprite simply reacts to inputs as always.  All you have to do is switch the sprite to receive its input from the script instead of the player when appropriate. Of course, you have to make sure that other conditions are the same too.  If your sprite is not in exactly the same location with exactly the same velocity as it was when you recorded the script, then it could play back very differently.

Then the question is, how do you allow the player to choose when to dismiss the dialogs instead of picking that up from the recorded input, and make sure that the states of the sprites aren't changing while it's waiting for the player to dismiss the dialog.  You could override one bit of the script's input with the actual player input, and that will take care of one problem (allowing the real player to dismiss the dialog instead of the recorded actions).  But then you still need to freeze the input and physics while waiting for the player.  One way to deal with this would be to design your sprites and cut scenes so that they don't depend too much on timing.  Don't allow collisions with other sprites to influence the flow of the cut scene and don't allow scripted sprite motions to interact with other movement actions.  Another thing to consider is saving the current map to a memory slot before the cut scene begins using IncludeMapInSaveUnit, then run the cut scene without caring how screwed up the sprites get, then use LoadGame to load that same slot, and your sprites and tiles for only the current map will be restored to exactly the same state that they were in before the cut scene.  Another way to handle it might be to totally disable all sprite rules when a dialog is active, but there isn't a very nice way to do this except to put a rule at the beginning of each sprite definition to avoid processing rules when inDialog = 1.

I don't know if any of this is helping.  You're still kind of left with the same question you started with -- how to disable rules appropriately when a dialog is showing.  My question now is, why not just disable all sprite rules?  Then sprites can't even move so they can't possibly go through walls if you're not calling MoveByVelocity (or anything else).  But that does mean that the dialog has to be managed by a rule that isn' within the sprite, or the user win't be able to even interact with the dialog.

I guess it would help to know more specifically if you have started implementing this and see problems, or are you just trying to think ahead to the problems you will deal with?  Maybe if you get started, we can deal with smaller problems 1 by 1.

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Cutscenes, dialogs, etc
« Reply #4 on: 2009-09-25, 07:27:54 AM »
Wow, thanks bluemonkmn!  I never would have thought to actually record inputs and play them back for a cutscene.  That's a great idea! :)
Saving before and loading after the cutscene is good idea too, but wouldn't loading after the cutscene result in playing the cutscene again, and again, resulting in an infinite loop?

Yes, as you pointed out, I have not started making cutscenes yet.  I'm just planning ahead and wondering what would be the best approach.  I'll give it a try, just making some conversations, without animations and movement.  I'll see how it goes and then, I'll try to add some animations between conversations to see how this turns out.  I'll keep you informed of my progress.

I gave a quick look into the SGDK2 help file, but I don't see any way to record some inputs.  I guess I will have to do it. :)

Thanks again bluemonkmn!  I really appreciate it. :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Cutscenes, dialogs, etc
« Reply #5 on: 2009-09-25, 08:01:16 AM »
If you save only the map data, and you play the cutscene based on a counter being equal to X then it wouldn't play the cutscene a second time if it sets the counter to something else after it starts playing.
Edward Dassmesser

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Cutscenes, dialogs, etc
« Reply #6 on: 2009-09-25, 08:03:47 AM »
Oh, it's possible to save only the map data?  Great!

Thanks durnurd! :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #7 on: 2009-09-26, 07:05:29 AM »
Right, SGDK2 doesn't have a class to record inputs yet, I don't think.  Maybe I'll work on that this morning of something else doesn't get in the way.  I could create an importable code object that would allow you to record the inputs for a sprite, and another that could play them back.  I've done this 2 or 3 times before, so maybe I can do it in my sleep now :).

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #8 on: 2009-09-26, 10:08:13 AM »
Here's what I have so far.  I haven't tested it (so I'm sure it doesn't work), and I still may need to show you how to create derived custom code objects with the binary resources embedded (like fmod sounds).  But I think the essential bits are there.  Still need to provide a mechanism to write the recorded data to a file so you can load it as an embedded binary resource in a derived code object.  But maybe if I'm lucky you'll figure it all out before I check back ;).

Oh, and I'm not sure if I'm off by one when playing back frameCount.  I'm not sure if I should move to the next set of inputs when frameCount reaches 0 or 1.  I'll have to think about that a moment... later.

In case you're eager to get going and try to figure it out, here it is.

Code: [Select]
using System;
using System.Collections.Generic;
using System.Text;

namespace CustomObjects
{
   [Serializable()]
class SpriteRecorder : IPlayer
{
      [Serializable()]
      class KeyFrame
      {
         public double x;
         public double y;
         public double dx;
         public double dy;
         public double oldX;
         public double oldY;
         public int state;
         public int frame;
         public SpriteBase.InputBits inputs;
         public double LocalDX;
         public double LocalDY;
      }

      static Dictionary<SpriteBase, SpriteRecorder> records = new Dictionary<SpriteBase, SpriteRecorder>();
      List<byte> movements = new List<byte>();
      KeyFrame initialState = new KeyFrame();
      byte frameCount = 0;
      [NonSerialized()]
      SpriteBase.InputBits inputs;
      [NonSerialized()]
      int playbackPosition = 0;

      [System.ComponentModel.Description("Store the current inputs of the sprite into a recording session")]
      public static void RecordFrame(SpriteBase sprite)
      {
         SpriteRecorder sr = null;
         if (!records.TryGetValue(sprite, out sr))
         {
            sr = new SpriteRecorder(sprite);
            records[sprite] = sr;
         }
         byte inputByte = (byte)sprite.inputs;
         bool nextStep = false;
         if (sr.frameCount >= 255)
            nextStep = true;
         else
         {
            if (sr.movements.Count == 0)
            {
               if (sprite.inputs != sr.initialState.inputs)
                  nextStep = true;
            }
            else
            {
               if ((byte)sprite.inputs != sr.movements[sr.movements.Count - 1])
                  nextStep = true;
            }
         }
         if (nextStep)
            sr.frameCount++;
         else
         {
            sr.movements.Add(sr.frameCount);
            sr.movements.Add((byte)sprite.inputs);
            sr.frameCount = 0;
         }
      }

      [System.ComponentModel.Description("Plays back one frame of input for the specified sprite")]
      public static bool Playback(SpriteRecorder playback)
      {
         if (playback.frameCount <= 0)
         {
            if (playback.movements.Count > ++playback.playbackPosition)
            {
               playback.inputs = (SpriteBase.InputBits)(playback.movements[playback.playbackPosition++]);
               playback.frameCount = playback.movements[playback.playbackPosition];
            }
            else
               return false;
         }
         else
            playback.frameCount--;
         return true;
      }

      protected static SpriteRecorder CreatePlayback(string name)
      {
         using (System.IO.Stream spriteStm = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(name + ".bin"))
         {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
               new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            SpriteRecorder sr = (SpriteRecorder)bf.Deserialize(spriteStm);
            sr.inputs = sr.initialState.inputs;
            return sr;
         }
      }

      private static void CopyState(KeyFrame kf, SpriteBase sprite)
      {
         sprite.x = kf.x;
         sprite.y = kf.y;
         sprite.dx = kf.dx;
         sprite.dy = kf.dy;
         sprite.state = kf.state;
         sprite.frame = kf.frame;
         sprite.inputs = kf.inputs;
         sprite.LocalDX = kf.LocalDX;
         sprite.LocalDY = kf.LocalDY;
         sprite.oldX = kf.oldX;
         sprite.oldY = kf.oldY;
      }

      private byte[] Serialize()
      {
         movements.Add(frameCount);
         System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
            new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
         using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
         {
            bf.Serialize(ms,this);
            ms.Flush();
            return ms.ToArray();
         }
      }

      private SpriteRecorder(SpriteBase sprite)
      {
         initialState.x = sprite.x;
         initialState.y = sprite.y;
         initialState.dx = sprite.dx;
         initialState.dy = sprite.dy;
         initialState.state = sprite.state;
         initialState.frame = sprite.frame;
         initialState.inputs = sprite.inputs;
         initialState.LocalDX = sprite.LocalDX;
         initialState.LocalDY = sprite.LocalDY;
         initialState.oldX = sprite.oldX;
         initialState.oldY = sprite.oldY;
      }

      #region IPlayer Members

      bool IPlayer.Up
      {
         get { return 0 != (inputs & SpriteBase.InputBits.Up); }
      }

      bool IPlayer.Left
      {
         get { return 0 != (inputs & SpriteBase.InputBits.Left); }
      }

      bool IPlayer.Right
      {
         get { return 0 != (inputs & SpriteBase.InputBits.Right); }
      }

      bool IPlayer.Down
      {
         get { return 0 != (inputs & SpriteBase.InputBits.Down); }
      }

      bool IPlayer.Button1
      {
         get { return 0 != (inputs & SpriteBase.InputBits.Button1); }
      }

      bool IPlayer.Button2
      {
         get { return 0 != (inputs & SpriteBase.InputBits.Button2); }
      }

      bool IPlayer.Button3
      {
         get { return 0 != (inputs & SpriteBase.InputBits.Button3); }
      }

      bool IPlayer.Button4
      {
         get { return 0 !=( inputs & SpriteBase.InputBits.Button4); }
      }

      #endregion
   }
}

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Cutscenes, dialogs, etc
« Reply #9 on: 2009-09-27, 07:18:02 PM »
Cool!  I didn't try it yet, but it seems great.  :)

I'll keep you informed! ;D
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #10 on: 2009-09-27, 09:09:55 PM »
Wish I had more time to work on it.  Maybe tomorrow.

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #11 on: 2009-09-28, 09:28:38 PM »
I worked on it a bunch and have a much better vision of how it can be used now, (as you might if you look at what's available now).  But I still haven't tested anything.  I know it compiles, but that's it.  (Also, I'm not clear on how/if playback will pause and merge with real player input when a dialog appears, but maybe you already have a solution for that.)  Hopefully I'll get time for testing soon.
Code: [Select]
using System;
using System.Collections.Generic;
using System.Text;

namespace CustomObjects
{
   [Serializable()]
   class RecordingDirectory : System.Collections.Generic.Dictionary<string, SpriteRecorder>
   {
      public static readonly RecordingDirectory value;

      static RecordingDirectory()
      {
         using (System.IO.Stream playbackDataStm = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("SpriteRecorder.bin"))
         {
            if (playbackDataStm != null)
            {
               System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
                  new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
               value = (RecordingDirectory)bf.Deserialize(playbackDataStm);
            }
            else
            {
               value = new RecordingDirectory();
            }
         }
      }

      private RecordingDirectory()
      {
      }

      protected RecordingDirectory(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
      {
      }

      [System.ComponentModel.Description("Store the current inputs of the sprite into a recording session of the specified name.")]
      public static void RecordSpriteFrame(SpriteBase sprite, string name)
      {
         SpriteRecorder sr = null;
         if (!value.TryGetValue(name, out sr))
         {
            sr = new SpriteRecorder(sprite);
            value[name] = sr;
         }
         sr.RecordFrame(sprite);
      }

      [System.ComponentModel.Description("Sends recorded input to the specified sprite; returns false if playback is complete.")]
      public static bool Playback(String name, SpriteBase sprite)
      {
         SpriteRecorder playback = null;
         if (!value.TryGetValue(name, out playback))
            throw new ApplicationException(String.Format("Sprite recording \"{0}\" not found", name));

         return playback.Playback(sprite);
      }

      [System.ComponentModel.Description("Reset the specified sprite movement recording to the beginning.")]
      public static void ResetPlayback(String name)
      {
         SpriteRecorder playback = null;
         if (!value.TryGetValue(name, out playback))
            throw new ApplicationException(String.Format("Sprite recording \"{0}\" not found", name));

         playback.Reset();
      }

      [System.ComponentModel.Description("Save all sprite recordings to a binary file named SpriteRecorder.bin.")]
      private void SaveRecordings(string Name)
      {
         string existingDataFile = System.Reflection.Assembly.GetExecutingAssembly().Location;
         existingDataFile = System.IO.Path.GetDirectoryName(existingDataFile);
         existingDataFile = System.IO.Path.Combine(existingDataFile, "SpriteRecorder.bin");
         foreach (KeyValuePair<String, SpriteRecorder> entry in this)
            entry.Value.Flush();
         using (System.IO.FileStream stm = new System.IO.FileStream(existingDataFile, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
         {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
               new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            bf.Serialize(stm, value);
         }
      }
   }

   [Serializable()]
   class SpriteRecorder
   {
      [Serializable()]
      class KeyFrame
      {
         public double x;
         public double y;
         public double dx;
         public double dy;
         public double oldX;
         public double oldY;
         public int state;
         public int frame;
         public SpriteBase.InputBits inputs;
         public double LocalDX;
         public double LocalDY;

         public void CopyToSprite(SpriteBase sprite)
         {
            sprite.x = x;
            sprite.y = y;
            sprite.dx = dx;
            sprite.dy = dy;
            sprite.state = state;
            sprite.frame = frame;
            sprite.inputs = inputs;
            sprite.LocalDX = LocalDX;
            sprite.LocalDY = LocalDY;
            sprite.oldX = oldX;
            sprite.oldY = oldY;
         }
      }

      List<byte> movements = new List<byte>();
      KeyFrame initialState = new KeyFrame();
      [NonSerialized()]
      byte frameCount = 0;
      [NonSerialized()]
      SpriteBase.InputBits inputs;
      [NonSerialized()]
      int playbackPosition = 0;

      public void RecordFrame(SpriteBase sprite)
      {
         byte inputByte = (byte)sprite.inputs;
         bool nextStep = false;
         if (frameCount >= 255)
            nextStep = true;
         else
         {
            if (movements.Count == 0)
            {
               if (sprite.inputs != initialState.inputs)
                  nextStep = true;
            }
            else
            {
               if ((byte)sprite.inputs != movements[movements.Count - 1])
                  nextStep = true;
            }
         }
         if (nextStep)
         {
            movements.Add(frameCount);
            movements.Add((byte)sprite.inputs);
            frameCount = 1;
         }
         else
            frameCount++;
      }

      public bool Playback(SpriteBase sprite)
      {
         if (frameCount <= 1)
         {
            if ((frameCount == 0) && (playbackPosition == 0))
            {
               initialState.CopyToSprite(sprite);
               frameCount = movements[playbackPosition];
            }
            else if (movements.Count > ++playbackPosition)
            {
               inputs = (SpriteBase.InputBits)(movements[playbackPosition++]);
               sprite.oldinputs = sprite.inputs;
               sprite.inputs = inputs;
               frameCount = movements[playbackPosition];
            }
            else
               return false;
         }
         else
            frameCount--;
         return true;
      }

      public void Reset()
      {
         playbackPosition = 0;
         frameCount = 0;
      }

      public SpriteRecorder(SpriteBase sprite)
      {
         initialState.x = sprite.x;
         initialState.y = sprite.y;
         initialState.dx = sprite.dx;
         initialState.dy = sprite.dy;
         initialState.state = sprite.state;
         initialState.frame = sprite.frame;
         initialState.inputs = sprite.inputs;
         initialState.LocalDX = sprite.LocalDX;
         initialState.LocalDY = sprite.LocalDY;
         initialState.oldX = sprite.oldX;
         initialState.oldY = sprite.oldY;
         inputs = initialState.inputs;
      }

      public void Flush()
      {
         if ((frameCount > 0) && (playbackPosition == 0))
            movements.Add(frameCount);
         frameCount = 0;
      }
   }
}

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #12 on: 2009-09-29, 05:33:03 AM »
Wow, this is impressive.  I only had to change one line of code (although I did have to change it 3 times):
Code: [Select]
      private void SaveRecordings(string Name)

should be:
Code: [Select]
      public static void SaveRecordings()

Then it works!

Here's how I used it.  Let me know if you need any additional info/help:
1. Create a new game from the sample game template.
2. Add/Import the SpriteRecorder code as SpriteRecorder.cs.
3. In Level 1, open the Main layer and edit the plan "Player Inputs" in the plan editor.
4. After the "Read player 1 inputs" rule, add a rule named "Record player 1 input".
5. For the rule function, choose CustomObjects.RecordingDirectory.RecordSpriteFrame.
6. Pass the sprite you want to record as a parameter, and provide a quoted string for the name of the recording you want.  I used "p1" as the name.
7. Edit the rules for sprite "Player".
8. Inside the "If menu requested" rule save the recording by adding a rule called "Save recording" which calls the "CustomObjects.RecordingDirectory.SaveRecordings" function.
9. Run the game and play around with the sprite a bit, then press Esc to go to the menu and quit.
10. Open SpriteRecorder.cs for editing in the SGDK2 IDE.
11. From the Embedded Data menu, choose "Load from file...".
12. Go to the directory where your game's EXE file is and select SpriteRecorder.bin.
13. Go back to edit the "Player Inputs" plan
14. Move "Record player 1 input" rule up 1 and change the function to "CustomObjects.RecordingDirectory.Playback"
15. You also have to swap the parameters (I will change this when I release the code as a standard object.  The parameter order should be cosistent. I will use the order currently used by the playback function for both playback and record, I think.)
16. Run the game again and watch the sprite repeat what you did!

Note that the message is not dismissed.  Still have to work that out.  For a while I considered recording and playing back player inputs instead of sprite inputs, but I think for cut scenes you would rather have this operating closer to the sprite.

Hope this works for you.  You can record multiple recordings with different names (and/or different sprites) and have them all saved in the same binary file.

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Cutscenes, dialogs, etc
« Reply #13 on: 2009-09-29, 07:10:25 AM »
Wow!!!  :o

I'll give it at try as soon as possible

Thanks a lot bluemonkmn!  ;D
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Cutscenes, dialogs, etc
« Reply #14 on: 2009-09-29, 08:06:36 AM »
I forgot to mention, right before step 16, you should also disable the Map Player to Inputs rule (whatever it's called).