0.8.1 progress

after that quick bugfix, I’ve been working on 0.8.1 (to be released at the end of the month).

there’s not a lot to SHOW right now:

* made fuel shards a little bigger and brighter (they’re hard to see in those folded space segments)
* added an achievement for catching a fuel before it hits the ground
* added a new accessory, “emergency backup systems” that makes you revive faster, and doubles your shield regen when you DO revive
* removed the 1-tile-tall corridors that are sometimes generated on the bottom of levels

other things I have planned this update:

* prevent trees/bushes/etc from hiding forcefield buttons!
* fix bug where water does not flood-fill passages leading to fuel rooms
* look into a possible bug with the whale miniboss – it seems his protective fish have gone missing, maybe??
* more low-level enemies, to make the first few levels less repetitive

when I have more cool things to show, I’ll definitely make a YouTube video to show them off!

until then!

donation made to Planned Parenthood

Just wanted to announce that Mysterious Space has earned enough in sales to do another donation. Following the schedule I set out on the Donations page, I’ve donated $500 to Planned Parenthood.

Next on the list is Community Idea Stations (you may know them better as PBS or NPR), and after that, The Foundation to Decrease World Suck.

Thanks to everyone who’s bought the game… I know you probably don’t really care what happens to you money after you’ve bought the game, but I’m excited about donating the money to awesome causes.

And thanks, as always, to everyone playing the game, leaving feedback, and recommending the game to others. The reason I make games is so that others will have fun playing them 🙂

0.7.8 has been released!

to copy/paste my Steam announcement:

“on the same day as Terraria? you must be mad! no one will be playing anything else for the next week!”

I DIDN’T KNOW. well: there’s probably always SOMETHING with an awesome new update on any given day, so… whateve’s! 😛

anyway! what does 0.7.8 hold?! a full changelog can be found here https://mysteriousspace.com/changelog/0-7-x/ but here’s the highlights:

* FULL SCREEN OPTION NOW AVAILABLE! whoa! I know! press Alt+Enter, like you would any game, or use the in-game options.
* new “folded space” mini-levels within the other levels! go fast! avoid debris! get fuel!
* innocent creatures now roam the surfaces of many planets. they are probably harmless herbivores, so please don’t be cruel and startle them 😛
* a couple little bug fixes. you probably didn’t even notice these bugs. except maybe the whale miniboss one. that one was hilariously obvious 😛

what might the next release have in store?!

here’s some VAGUE PLANS:
* I need to finally look into Linux port. but NO PROMISES! just PEEKING at this might open a terrible can of worms >_> but: I will look, and even if it’s not something I feel like I can get done for the next release without losing my mind, I will make some progress. it IS coming!
* we’ve got minibosses, crates & buttons, and folded space, but none of these really feel COMPLETE and UNITED to me. I’m going to re-examine these, and see if/how they can be better integrated.
* more french translation text. Ambre (the French translator) has gotten way ahead of me; there’s a ton of text just waiting to be added!
* a revamped tutorial. the current one is lame and boring and I hate it 😛
* peaceful fish in the sea, to mirror the peaceful creatures that have been added to the land.
* populated outposts where you can trade stuff and/or do side-quests??! I’ve been wanting to do this for forever; now that I’ve got a solid month before the next release, I feel much better about starting this! (this may tie in to tying together mini-bosses and crates and things.)

thanks for reading; thanks for playing! look forward to the next release at the end of July, and I’ll keep you guys posted on my progress over at my YouTube channel, as always 🙂 https://www.youtube.com/channel/UCAaN0HHfuFIWHIHDfiIEitg

“Hey, Listen!”

today my friend Katie and I got together, and recorded some samples for Mysterious Space!

WITH OUR VOICES.

no, we’re not going to voice act the whole game! but I thought it’d be fun to have a little clip for when Blær (the ship’s computer) talks to you.

so we made one! it’s a clip that says, “Hello, Captain” in a stereotypically sci-fi robot voice.

similarly, the broken AI that can sometimes inhabit your weapon ALSO has a clip now, saying “Hai– comm–der” over some static.

and that’s it!

I also, today, made some tweaks to the minibosses, making their strength scale depending on the difficulty of the planet they’re on.

there’s also some more critters running around on planets, now, and most of the flocking math for schools of fish is in, though no fish yet “flock” in the seas of Mysterious Space’s ocean worlds.

but there’s still a week left in the month! and there’s one other, new game mechanic I’d like to get in!

I’ll keep you posted, as always 🙂

0.7.7 is released!

you can get it at these two places:

the biggest addition this update is that of tractor beams! (and contrary to last night’s post, I WAS able to get jiggling effects in for decorations when using the tractor beam near them, which is fun!)

check out the 0.7.7 changelog for a full list of changes.

this is also the last mid-month release! from this point forward, I will only release new versions of Mysterious Space at the end of each month! I want to get on a kind of schedule, to force me to spend a lot of time on each update, and also to allow you guys to know when to expect new content!

thanks for playing, everyone 🙂 have fun! 🙂

the trouble with rocks. and tractor beams.

I’ve added some new mechanics to Mysterious Space for the 0.7.7 release, which I will get out Wednesday (June 17th). I was about to just list the mechanics out – bullet point style – but they’re kind of all tied together, so I better just properly explain 😛

SO:

some planets will now put Fuel in small rooms whose only entrance is blocked by a forcefield. unlike the flickering forcefields you’re used to, these new forcefields remain consistently on. troublesome! HOWEVER: there will always be a button nearby which, when held down, will deactivate the forcefield.

in order to teach the player what to do, I made sure that the button responds to the player ship… it does look like a button, after all (… I hope??), and any sane player would want to press it. so: not wanting to disappoint, and to educate the player “yes, this IS a button; it CAN be pressed”, player ships can press it. the button is also always placed with the forcefield it controls in view, so the player will always know what the button does!

you may be thinking “what do you mean ‘in order to teach’ – it sounds like you just made a button do what buttons do.”

well, kind of. there’s a problem: when the player moves off the button, it pops back up, and the forcefield comes back on.

so using your ship is not really a solution; that being the case, I COULD have simply made the button not respond to the player. but again, I really want to make sure that when you stumble upon one of these buttons for the first time, you will see it, and see what it does. it’s an important piece of information! BECAUSE: your ship is now equipped with a tractor beam, and SOMEWHERE on the planet there will be a large crate (perhaps even two). and you guessed it: crate + button = problem solved.

to teach the player how to use their tractor beam, when you see a crate for the first time, the game has some text blinking over it which reads “Closer”; when you draw close enough, the text changes to the button you must press to activate your tractor beam (depending on whether you’re using a controller, or your keyboard). doing so lifts the crate, and once you’ve carried the crate for a couple seconds, this little tutorial text goes away, never to be seen again.

and so, one way or another, the player will encounter the button, and the crate, and hurrah: puzzle solved.

fantastic.

in developing this, though, I did realize a few side-effects: (ooh, I get to use a proper list this time!)

  • in split-screen co-op mode, one player can hold the button while the other gets the fuel, circumventing the need for a crate! not my intention!
  • on desert worlds, crates can get pushed by wind! (the player could get locked inside the room!)
  • and “hold on a second,” says the player. “why can’t I just tractor beam one of these many, nearby boulders? why do I have to go find a stupid crate?” uh oh…

one at a time:

the split-screen co-op workaround does – at first – seem concerning, but I realized it’s actually a GREAT side-effect. I feel that Mysterious Space co-op doesn’t often encourage players to actually stick together, and work together; often, the players may as well each be playing their own game! that feeling is what lead me to create the “disabled” mechanic – where you don’t quiiiite die, and can revive if you avoid further damage – which encourages players to defend one another. but I never felt like that mechanic was quite enough; it ends up feeling more that you’re punished for not working together rather than rewarded FOR working together. dying, after all, is only a thing you can get frustrated about. this button/forcefield combo, on the other hand, provides a DEFINITE reward for working together: it’s a thing you specifically coordinate in order to accomplish, and accomplishing it furthers your shared goal (get the fuel), and doing it cooperatively saves you time (which in turn nets you more points).

so, yeah: happy accident! keep it!

the desert winds, however, are a little less straight-forward…

on one hand, I could simply make crates NOT pushable by winds, and I thought about doing that, but… no: allowing them to be pushed adds an interesting element of timing to the game; it requires something a little different from the player – asks them to think about things slightly differently – AND it gives further use to speed-boosting effects! (such as those from alien artifacts.)

that all seems too good to pass up!

still, on the OTHER hand, if a player happened to start up their first game, get a desert world, and encounter the button and the crate there while having little or no experience with Mysterious Space’s winds and vents… that could lead to a very frustrating experience!

fortunately, there was an easy solution: I simply disallow the button/crate/forcefield combo on early desert worlds (later desert worlds may still present this combination). since new players will almost certainly die in the early levels, anyway, they’ll have plenty of time to experience both winds, and the button/crate puzzle, before the two are allowed to be combined in later planets. and if a player IS somehow good enough to get so far in their first try, I don’t feel so bad throwing them a curve ball 😛

finally, there’s the issue of the boulders!

and I just about screwed this one all up!

early on in developing these new mechanics, it did occur to me “hey, wait: why can’t I use a boulder instead of getting a crate? that could be kind of fun, too. chuck ’em at enemies, maybe?” but I quickly dismissed it, telling myself “but we have boulders everywhere, and that would destroy the whole point of the puzzle. and it’s a game; players will understand that button + crate = problem solved. that’s how games go.”

a terrible mistake on my part!

thinking back on it now, it seems obvious that not being able to lift the boulders would provide frustration to a player! once you learn to use the tractor beam, you’ll WANT to use it, and “dude! check out all these boulders! oh man, I’m gonna try to stack them all up, that’d be hilario–aww. the tractor beam doesn’t even turn on without a crate around. lame.” and then later “wait, why did I have to go get a crate for this button? there’s a huge boulder RIGHT NEXT TO IT. JUST TRACTOR BEAM THAT. WHY WON’T YOU WORK, TRACTOR BEAM?”

and then every time you have to find a crate, you’ll think “FINE, game, I’ll get a crate, because boulders aren’t good enough for you, apparently.”

UNACCEPTABLE!

so I want to thank ACWraith for leaving this comment on my video showing off the tractor beam: “The planet is covered with rocks, but you need a crate to weigh something down? ;)”

RIGHT?!

I would have almost certainly released 0.7.7 with this error in place if not for this comment reminding me of my error.

but: how to fix it?

I’m a little less confident in this solution than the other solutions I’ve talked about in this post, but… well… I just made the boulders WAY smaller 😛 about 1/3 or 1/4 size.

rocks

it’s not necessarily the best solution, but I do think this is a good FIRST STEP. the boulders will not stand out quite so much as “OBVIOUSLY a suitable crate substitute.” you might still wonder, though: “well, but THAT one looks like it could be big enough,” or “look at all these rocks strewn about; why can’t I just pile them all together?”

so I think a good NEXT step would be to add some kind of animation when using your tractor beam in the presence of rocks; make them jiggle, signaling to the player “clever! but unfortunately, these are not-liftable by your tractor beam.”

giving the seaweed, trees, and other decorations a similar jiggle would probably be a good idea, too, for consistency’s sake.

it may seem silly (“what, you’re just gonna add an animation?”), but that explicit acknowledgement from the game IS satisfying, precisely BECAUSE it acknowledges your attempt. the game has not ignored you! and now you can wonder, in an in-character/in-game way “why doesn’t my tractor beam work on small rocks and trees and things?” instead of complaining, as a video game player: “BUT LOOK AT ALL THESE ROCKS! THIS IS OBVIOUSLY AN OVERSIGHT ON THE PART OF THE GAME DESIGNER!” you may still feel a LITTLE cheated, it’s true, but this is a huge improvement.

the jiggling reaction would have another nice benefit, too, though: it implies that there might be other things out there with more favorable reactions! it gives players a fun, new sub-goal: “TRACTOR BEAM ALL THE THINGS!”

so sure, maybe you wanted rocks to be carryable, and they weren’t, and that was kind of sad, but HEY: what else is out there that you CAN lift? ONE WAY TO FIND OUT.

there are two problems with implementing this RIGHT NOW, however: first, there ISN’T anything else tractor beam-able in the game, so I’d be sending the player on a wild goose chase, and that’s not very nice; second, I don’t think I have the time to implement a jiggle animation right now anyway 😛 GOTTA’ RELEASE TOMORROW. but I think a definite goal for the NEXT next version WILL be to add this jiggle effect, and to make some new things that respond favorably to jiggling.

thanks for reading 🙂

and I hope you’ll have fun with the new tractor beam, even if it won’t work on small rocks 😛

FMOD in C#… it’s a pain to set up. here’s how I did it:

I’ve been having a lot of trouble playing music in Mysterious Space, due to bugs with MonoGame. these bugs range from failing to properly loop songs to crashing the game! music-related bugs have plagued MonoGame for a while (like, years), so waiting for them to get fixed… yeah… dunno about that.

in light of these issues, DDRKirby(ISQ) recommended FMOD to me. it’s a music-playing library that’s been around for a long time. it’s a mature, stable library.

that being said, getting it working in C# has always been something of a problem. there are a lot of wrappers that you can find online, but most have not been updated in a couple years; FMOD comes with a wrapper of its own, but documentation is poor.

I was finally about to get it working in Mysterious Space, though, so for anyone out there googling “FMOD C# example”, as I was trying to do, this is for you.

a few things to mention about my setup:

  • I will be using FMOD’s built-in wrapper
  • I’m using Visual Studio 2012
  • I’m compiling for mixed platforms (32 AND 64-bit; this makes using FMOD a little more “exciting”)

Adding the .cs Files

you can’t add FMOD as a “Reference” for your project. instead, you need to add the DLL files to your project’s root, and include a few .cs files that come with FMOD.

let’s start with the .cs files. they can be found in FMOD’s install directory, under api\lowlevel\inc, for example, C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\lowlevel\inc. add all the .cs files there to your project.

open up fmod.cs and check out that #if WIN64 block. this is telling the fmod wrapper which dll to include. if you’re happy compiling strictly for 32-bit, or strictly for 64-bit, leave this code alone, and #define WIN64 if needed (you can do this either right in the file, or through the project’s settings, or edit out the #if block, or whatever).

if you want your program to run on both 32 AND 64-bit platforms, you’re in for a touch of extra coding which we’ll get to in a moment. for now, though, remove the #if block entirely, leaving only the line public const string dll = "fmod"; (define-related code is determined at compile-time, but we need to include the right DLL during run-time!) technically you don’t have to do this (assuming you DON’T #define WIN64), but I didn’t want to leave in code that suggested something platform-specific was going on. it’s up to you.

Adding the .dll File(s)

if you’re compiling only for 32-bit, or only for 64-bit, find the appropriate dll and add it to the root directory of your project. for 32-bit, the file is fmod.dll; for 64-bit, it’s fmod64.dll. both files are in FMOD’s install directory, under api\lowlevel\lib.

fmod-dllsif you’re compiling for BOTH, you’ll need to do things a little differently: you’ll need to add both files, but they both need to be called fmod.dll (fmod64.dll will be renamed; remember how we modified fmod.cs so that it always looks for “fmod.dll”?). this means putting them in different directories. for myself, I created an “FMOD” directory, and then created directories named “32” and “64” inside that, placing fmod.dll into “32”, and fmod64.dll into “64” before renaming it to fmod.dll.

whichever files you included, make sure that their “Copy to Output Directory” property is set to “Copy if newer” (or “Copy always”). if you leave this property to “Do not copy”, the file(s) will not be copied over to the directory where your program’s executable is placed when compiling.

Actually Using FMOD

I wrote a class responsible for handling music, from loading it, to playing it. I feel like examples are more helpful than long explanations, so here’s the class I wrote. You’ll need to modify it; most of those modifications should be obvious. Further explanation, and example usage, are below the code.

using System;
using System.Runtime.InteropServices;

namespace MysteriousSpace
{
    public class MusicPlayer
    {
        public const int NUM_SONGS = 14;

        public const int SONG_TITLE_MENU = 0;

        public const int SONG_SECTOR_MAP = 1;

        public const int SONG_DESERT = 2;
        public const int SONG_FOREST = 3;
        public const int SONG_OCEAN = 4;
        public const int SONG_BARREN = 5;
        public const int SONG_ICE = 6;
        public const int SONG_LAVA = 7;
        public const int SONG_MINING = 8;
        public const int SONG_ASTEROID_BELT = 9;
        public const int SONG_ENCOUNTER_HUMAN = 10;
        public const int SONG_ENCOUNTER_CYBORG = 11;
        public const int SONG_ENCOUNTER_AI = 12;
        public const int SONG_ENCOUNTER_ALIEN = 13;

        private FMOD.System FMODSystem;
        private FMOD.Channel Channel;
        private FMOD.Sound[] Songs;

        private static MusicPlayer _instance;

        public static MusicPlayer Instance { get { return _instance; } }

        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        public static void Init()
        {
            if (Environment.Is64BitProcess)
                LoadLibrary(System.IO.Path.GetFullPath("FMOD\\64\\fmod.dll"));
            else
                LoadLibrary(System.IO.Path.GetFullPath("FMOD\\32\\fmod.dll"));

            _instance = new MusicPlayer();
        }

        public void Unload()
        {
            FMODSystem.release();
        }

        private MusicPlayer()
        {
            FMOD.Factory.System_Create(out FMODSystem);

            FMODSystem.setDSPBufferSize(1024, 10);
            FMODSystem.init(32, FMOD.INITFLAGS.NORMAL, (IntPtr)0);

            Songs = new FMOD.Sound[NUM_SONGS];

            //LoadSong(SONG_TITLE_MENU, "");
            LoadSong(SONG_SECTOR_MAP, "Set a Course [Seamless]");
            LoadSong(SONG_DESERT, "Uncharted Expanse [Seamless]");
            LoadSong(SONG_FOREST, "Mystic Depths [Seamless]");
            LoadSong(SONG_OCEAN, "Dreamscape (Mysterious Space Edit)");
            //LoadSong(SONG_BARREN, "");
            LoadSong(SONG_ICE, "Always (Mysterious Space Edit)");
            LoadSong(SONG_LAVA, "Afterglow [Seamless]");
            LoadSong(SONG_MINING, "Eternal Excavation");
            LoadSong(SONG_ASTEROID_BELT, "Not the Same Level (Mysterious Space Edit)");
            //LoadSong(SONG_ENCOUNTER_HUMAN, "");
            //LoadSong(SONG_ENCOUNTER_CYBORG, "");
            //LoadSong(SONG_ENCOUNTER_AI, "");
            //LoadSong(SONG_ENCOUNTER_ALIEN, "");
        }

        private void LoadSong(int songId, string name)
        {
            FMOD.RESULT r = FMODSystem.createStream("Content/Music/" + name + ".flac", FMOD.MODE.DEFAULT, out Songs[songId]);
            //Console.WriteLine("loading " + songId + ", got result " + r);
        }

        private int _current_song_id;

        public bool IsPlaying()
        {
            bool isPlaying = false;
            
            if (Channel != null)
                Channel.isPlaying(out isPlaying);

            return isPlaying;
        }

        public void Play(int songId)
        {
            Console.WriteLine("Play(" + songId + ")");

            if (_current_song_id != songId)
            {
                Stop();

                if (songId >= 0 && songId < NUM_SONGS && Songs[songId] != null)
                {
                    FMODSystem.playSound(Songs[songId], null, false, out Channel);
                    UpdateVolume();
                    Channel.setMode(FMOD.MODE.LOOP_NORMAL);
                    Channel.setLoopCount(-1);

                    _current_song_id = songId;
                }
            }
        }

        public void UpdateVolume()
        {
            if(Channel != null)
                Channel.setVolume(Settings.GetInstance().MusicVolume / 100f);
        }

        public void Stop()
        {
            if (IsPlaying())
                Channel.stop();

            _current_song_id = -1;
        }
    }
}

let’s talk about CREATING the instance first.

I implemented MusicPlayer as a “singleton”: it’s constructor is private, but an instance is available via MusicPlayer.Instance.

I’ve got some FUNNY BUSINESS surrounding the instantiation of MusicPlayer.Instance, however:

the following code is required when compiling for both 32 and 64-bit systems. before using MusicPlayer.Instance, you will need to call MusicPlayer.Init().

        private static MusicPlayer _instance;

        public static MusicPlayer Instance { get { return _instance; } }

        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        public static void Init()
        {
            if (Environment.Is64BitProcess)
                LoadLibrary(System.IO.Path.GetFullPath("FMOD\\64\\fmod.dll"));
            else
                LoadLibrary(System.IO.Path.GetFullPath("FMOD\\32\\fmod.dll"));

            _instance = new MusicPlayer();
        }

the conditional LoadLibrary calls are the magic that make running on 32 and 64-bit platforms possible. you may notice the [DllImport("kernel32.dll")] call, and wonder why that’s always 32-bit… it isn’t! confusingly, even in 64-bit Windows, this file is still called “kernel32.dll”!

if you are ONLY targeting 32 OR 64-bit, however, you can replace the above mess with this:

        private static MusicPlayer _instance = new MusicPlayer();

        public static MusicPlayer Instance { get { return _instance; } }

all that funny business surrounding LoadLibrary is gone, and you no longer need to call MusicPlayer.Init() (giving us a much cleaner singleton!)

with that stuff out of the way, what other modifications will you need to make before using MusicPlayer in your code?

the most obvious changes would be to the CONSTs defined at the top. so obvious, I won’t even explain 😛

the MusicPlayer() constructor needs similarly-obvious modifications to load up the proper songs. also, make sure to update the LoadSong(...) method to point to whatever directory your music files are stored in.

finally, check out the UpdateVolume() method. for my game, I keep all the game’s settings in a singleton, which UpdateVolume() is looking at to determine how to set the music’s volume. you’ll need to change this method to grab the volume from wherever you keep it. (if you don’t have such a setting yet, just do Channel.setVolume(1f); note that the volume goes from 0.0f to 1.0f!)

oh, and you don’t HAVE to, but you’ll probably want to change the namespace from “MysteriousSpace” to whatever you’re using for your application.

alright: so how do we actually play music in your application?

  • first, if you’re doing the whole 32+64-bit messiness, call MusicPlayer.Init() before calling any other MusicPlayer method!
  • call PlaySong(...) to play a song, passing in the song id you want to play, for example: MusicPlayer.Instance.PlaySong(MusicPlayer.SONG_TITLE_MENU); the music will loop automatically (check out the PlaySong method to see how this is accomplished; hint: it’s accomplished easily!)
  • if you call PlaySong(...) while another song is playing, the previous song will be stopped, and the new one started! handy!
  • you can call MusicPlayer.Instance.Stop() to stop the music.
  • you can call MusicPlayer.Instance.IsPlaying() to see if a song is currently playing; you may not ever need to use this method, but it’s used internally by other MusicPlayer methods.
  • remember to call MusicPlayer.Instance.UpdateVolume() whenever you need to update the music volume. (for example, in my game, the player can access volume settings during gameplay; when the player changes the volume here, MusicPlayer.Instance.UpdateVolume() is called so that the volume change is applied immediately.)
  • call MusicPlayer.Instance.Unload() to unload FMOD as part of your application’s shutdown/exit code.

All Done!

I had a lot of trouble getting FMOD to work in my project; hopefully this article will save some other people the same trouble I had!