Créme Engine: Modern Platformer Engine (On Hold)

SNM’s original release is now deprecated, please check out CE’s pre-release instead:

Old Post


Greetings, today I wanted to once again share a little project I’ve been working on over the span of a previous month, an open source 3D platformer. And in case you had a deja-vu, yeah, I’m the same guy who released this kit last year.

It’s been a full year, and over the span of it my scripting changed quite a bit (maybe for the better, maybe for the worse, up to you to judge), which meant that maintaining the kit from last year was not a viable option for me anymore. That’s why I just opted to start from scratch while doing something more fun this time around, and what better than just doing a full game that stands alone on it’s own, and works as a little demo. So yeah, that’s pretty much what this is.

This time around I decided to focus on making an extensible, general purpose, and relatively easy to expand upon platformer, that’s designed to be used to make full collect-a-thon/platformer games, while offering a bunch of general purpose utilities that can be useful for any games in general.

I don’t wanna bore you any longer with this essay, so here’s a little preview trailer I made for the game:


Resume

Before we fully dive in on the contents of the post as this is going to be pretty lengthy, here’s a brief resume regarding what I’ll cover so you can skip the parts that don’t interest you:


Game’s Features

The game’s features are split on 3:

  • Utilities: The general purpose modules the rest of the game’s code relies on.
  • External Resources: The modules/code I used in the game that aren’t made by me, which I’ll cover in the attribution section.
  • General Code: The mechanics, systems, and stuff that composes the game, which is what i’m going to be covering on this section.

On top of that, server/client code was split in 2 under the same folder, but I won’t get into that as I think it’s pretty self explanatory. This time around I don’t have a decent computer to record on, so i’m not going to be providing footage (the trailer pretty much showcases everything though).


Moveset

Much like last time, the game comes with a moveset, which consists of the following moves:

  • Double Jump: Pressing the jump button again in air will trigger a second jump, boosting you further into the air.
  • Dive: Pressing the action button in air, while moving, will trigger an airdive, pushing you forward.
  • Body Slam: Pressing the action button in air, while not moving, will trigger a body slam that will rapidly push you into the ground.
  • Roll: Pressing the action button while walking, will trigger a roll pushing you forward, this move can gain momentum and can be extended by spamming it.
  • Knock: Triggered when a move is cancelled by running into an obstacle, mostly called internally by other moves.
  • Tilt: This is technically and effect, but I thought it’d be worth mentioning. It simply titls the character’s orientation when walking for a cartoony effect.

Said moveset is divided on 3 scripts:

  • Moveset: The state machine.
  • Effects: Module containing the effects the moveset uses, such as the tilt and the trail.
  • Moves: Module containing the moves the moveset uses, such as the roll and the dive.

Dependencies:

I’ll go further into these in the Documentation section, but for now, all you need to know is that the moveset relies on these (it prob relies on other stuff too, but these are the most notable ones).

  • PublicTables: Move states, useful variables such as the JumpCount, and the ToggleMovement function extension are accessed from the state machine’s public table for ease of use (the move functions themselves still are accessed from the Moves module).
  • BindingUtil: “Action” binding used by the moves is setup using BindingUtil.
  • CharUtils: Moveset’s ToggleMovement function uses CharUtils ToggleMovement to disable both, it uses other stuff such as the player’s Animations/Sounds as well.

Examples:

To change the state of a move, or to disable the moveset itself, you’d do the following:

--Get PublicTable
local PublicTables = require(Modules:WaitForChild("PublicTables"))
local MovesetTable = PublicTables:GetIdentifier(script.Parent:WaitForChild("Moveset"))

--Change the JumpCount
MovesetTable.JumpCount = MovesetTable.MaxJumps

--Disable Moveset
MovesetTable.MovesetEnabled = false --Moves use Metamethods to ensure your changes apply on the go, meaning ObjectValues are no longer necessary

--Enable Moveset and Movement
MovesetTable.ToggleMovement(nil, true) --This function is originally from the Moves module, it was extended to the table for ease of use
MovesetTable:ToggleMovement(true) --Maybe you can call it without passing self, but I haven't tested it

--There's an issue with the previous implementation though, if we call it too soon it might not exist yet, so in order to prevent that, we can either use the function from Moves, or call it the following way:
local ToggleMovement = PublicTables:WaitForKey(MovesetTable, "ToggleMovement") --Yields until ToggleMovement Exists
ToggleMovement(nil, false)

To trigger a move manually, you’d have to require Moves:

--Get Module
local MovesetMoves = require(Moveset:WaitForChild("Moves"))

--Trigger Jump
MovesetMoves:Jumped(true)

Playlist Zones

Playlist Zones are an area based music system that triggers music when you enter an area. It supports multiple instances to represent a zone, and it supports multiple audios.

How to use:
Place all the audios you wanna play in the folder, and they’ll play when you enter it, like a playlist.

API
There’s no API, only the PlaytestVisible (true by default) variable that shows non-transparent parts while testing in studio. Rest of the system is automatic, you drag in/out the audio and it just works.

Zone Titles
This is a standalone extension of PlaylistZones (you can delete it if you don’t like it) that simply displays the Playlist’s name when you enter it, which I used to display the name of the areas in the game.


Collectibles

Collectibles consist of 2 scripts, the Client-Sided one and the Server-Sided one, the client handles the appearence/trigger of the collectibles, while the server awards/manages them.

There’s 3 collectible types in SNM:

  • Coffees: These are the game’s main collectibles, they display a fancy animation with a name/description when obtained, and they are the ones usually used to progress in platformers.
  • Toasts: These are the secondary collectibles, they act as an extra for completionists, and are usually hidden at places the player doesn’t frequent too much.
  • Money: This is the game’s currency (aka: tertiaty collectible), it acts as a guide for the player indicating where to go, and it acts as an incentive for players to come back in games where the currency can be spent. They respawn every 12 hours by default.

All types can be customized in their appropiate server/client side variables to your liking, and of course forked to fit your game properly.

Notes

Colletibles changed quite a bit since the previous kit, and since there’s no API for it, i’m just going to give you some notes regarding what’s new and i’m going to end the section here.

  • Placeholder Boxes: Once you open the project you might realize the collectibles look like boxes, this was done to make placing them easier (you can fork this out pretty easly by removing the section of the script that applies appearence), models/audio for the collectibles are in ReplicatedStorage/Assets and can be customized.
  • Placeholder Boxes Delay: Only downside of applying appearences automatically, is that if you add a lot you might notice a small delay when they are being applied (SNM has around 400, and it takes a couple seconds on my current device for them to load), you can either leave it there, or hide it with a loading screen.
  • New Identifier Structure: The structure collectibles follow changed since the previous kit to be less annoying, now the name of the collectible acts as the Collectible Identifier for the current level, and the identifier value inside it acts as the Place Identifier.
  • Bonus Collectibes Removal: Bonus collectibles are not a thing anymore unlike the previous kit as they didn’t feel necessary.
  • Concerns Regarding Exploits: No safety checks are done by default on SNM, in general, it’s up to you to implement server-side checks in order to prevent exploiters abusing collectibles.
  • New Datastore: Collectibles now take advantage of an easy to use datastore module in order to improve legibility and ease of use. Refer to said module’s documentation for more details.

Checkpoints

Pretty self explanatory, checkpoints with a cool effect when reached.

How to use:

  • Name your initial spawn “Spawn” and set the Enabled property to true.
  • Name all other checkpoints “Checkpoint” and set the enabled property to false.
  • Done.

Mechanics

SNM comes with 4 base game mechanics by default:

  • Bouncy Surfaces: Parts named “BouncySurface” will make the player bounce, bounces can be accumulated to gain extra height.
  • Swings: Sticks you can spin on, pressing the jump button will launch you forward, pressing the Interact button will invert their direction.
  • Tight Ropes: They basically let you walk on them and that’s about it, I planned to add the same effect bouncy surfaces have but I ran out of time.
    Moving Platforms: Moving platforms you can ride on. They were supposed to be client sided because they ruin the game for folks with high ping otherwise, but they didn’t move the player in the client, so yeah, rip.

Damage

Game’s damage handler with rough support for custom damage/deaths (rough as in not a module or anything, just a regular script with a table for custom stuff).

  • When the player touches with an object named Damage, they’ll take 25 damage.
  • If an IntValue under the name of DamageValue is placed inside a damage object, it’ll deal the amount specified in there.
  • If a custom damage object, such as Water is touched, it’s custom function will be ran.

Quests

These are a last minute addition, studio kept booting up for some reason (context on that down below), so I used that extra time to add an example quest.

There’s only 1 quest in the game, and it was made as an example regarding how to use the Dialogue System’s events to make a quest.

So, how do I make a quest?

There technically isn’t a quest system as it felt unnecessary, the way it’s done in the Walter Quest is as follows:

  • You make a LocalScript under Workspace/Quests/YourQuest.
  • You store all your Quest Assets in there.
  • You hook it to the events the Dialogue System provides to detect when a dialogue starts/ends.
  • You use the functions provided by the Dialogue System to Store/Update NPC dialogues.
  • Once the quest is complete you use PivotTo to position the earned collectible, and BadgesHandler to award badges.

Of course there are better and cleaner ways to implement quests, but this one felt good enough for me as quests aren’t common enough on my end for this implementation to be an issue. It’s cheap but it works.


Dialogues

The dialogues script is the one in charge of handling the NPC Dialogues, the NPC Pathfinding and the Dialogue Typewritting (the typewritting is done in a separate module to allow using it on cutscenes and such). On top of it, it offers some events to allow for things such as quests.

Events

  • DialoguePlayed: Fires when a new dialogue starts.
  • DialoguePassed: Fires when a new dialogue line starts.
  • DialogueCancelled: Fires when the dialogue ends early.
  • DialogueCompleted: Fires when the dialogue completes.
  • DialogueEnded: Fires both when the dialogue completes or gets cancelled.

Functions

  • StoreDialogue(NPC): Takes the npc’s folder as argument, stores it’s current dialogue.
  • RestoreDialogue(NPC): Takes the npc’s folder as argument, restores the previously stored dialogue.
  • UpdateDialogue(NPC, Dialogue, StorePrevious): Takes the npc’s folder as argument, the new dialogue (StringValue) you want to replace the current one with, and the optional storeprevious argument calls storedialogue if true is passed.

Modules Documentation


PublicTables

I learned OOP recently, and even though I find it pretty useful for modules, I often find myself on a bottleneck when using it for in-game systems such as the game’s moveset where I need all scripts to access each other’s information. PublicTables are a frankenstein I made mashing together shared, OOP, and instances that solved that issue.

The result is a system with it’s own benefits, it’s own drawbacks, but overall, it gave me what I wanted, and I used it on a big chunk of the project. It’s not intended to be used for modules, just for small in-game systems where you need to store isolated information (an NPC’s state using it’s own model as a table for example).

Benefits:

  • Easy to use.
  • It allows using any instance in your game, not just modules, as a table that holds shared information. It also accepts string “passwords”.
  • Provides a built-in changed event as well as a yielding function to avoid race conditions.
  • If you are one of the few folks that still use shared, you’ll probably feel pretty comfortable using it (only difference is that PublicTables force you to isolate your code, which can be done easly either by passing script.Parent, or a string “password”).

Drawbacks:

  • No autocompletion.
  • Unlike with classes, functions aren’t reused here, which means they take more memory. You can avoid that by defining your functions outside the table.
  • Very likely a lot slower than OOP, haven’t really tested, but this is mostly meant for small tasks.

Events

  • _Changed(Key, Value): Fires each time one of your PublicTable’s keys changes, returns said key and it’s new value.

API:

  • PublicTables:GetIdentifier(Obj): Takes an “Obj”, returns a public table. Obj can be either an: Instance in which case a code will be “generated” for it, assigned to the instance as an attribute, and used in the future to access the PublicTable. A String, in which case said string will be used as the code. Or nil, in which case a code will be “generated” and passed back as a second parameter.
  • PublicTables:FindIdentifier(Identifier): Takes your “PublicTable’s” Identifier (aka: it’s code), returns the PublicTable or nil if not found.
  • PublicTables:WaitForIdentifier(Identifier): Takes your “PublicTable’s” Identifier (aka: it’s code), yields until it’s found and returns it.
  • PublicTables:FindKey(Table, Key): Takes your “PublicTable” and the “Key” you wish to find, returns the key or nil if not found.
  • PublicTables:WaitForKey(Table, Key) : Takes your “PublicTable” and the “Key” you wish to yield for, yields until said key is found and returns it.
  • PublicTables:Destroy(Table): This one’s untested as idrk how to properly clean tables, but in theory it destroys the “PublicTable” and it’s connections.

Example of usage:
For example the game’s NPCs use PublicTables to remember their current goal and state, here’s a dumbed down chunk of that:

local function Pathfind(NPC, Bool)
	local self = PublicTables:GetIdentifier(NPC) --Our PublicTable
	self.CurrentGoal = self.CurrentGoal or nil --Our NPC's current goal
	self.Cancelled = self.Cancelled or false --Our NPC's state

	--[[
	Down below the actual pathfinding is done using the NPC's
	isolated variables, without any need for a constructor
	--]]
end

Miscellaneous

Quite literally what the name implies, a module with general purpose useful functions. They always vary, and some of them were just found on the forum, but I always like having one of these in all my games.

The ones not listed here are useless and I simply forgot to clean them, I’ll prob do it on a patch when I can.

API:

  • MiscModule.GetPlatform(): Returns an approximation of what device the player is on, which I commonly use to display controls on tutorials. Returns either “Console”, “Tablet”, “Phone” or “Desktop”.
  • MiscModule.SoundToSpace(Sound, Object, Range): Takes a sound, it’s desired parent, and an optional range, plays the sound and destroys it when done.
  • MiscModule.ShuffleTable(Table): Takes a table, returns it with its order rearranged.
  • MiscModule.NewRandom(Min, Max, LastRandom, Range): Math random, but recursive to prevent repeated values. Min is the smallest desired number, Max the greatest, LastRandom is optional, it lets you pass the last random the function returned, and range is optional too, it let’s you pass a distance between the numbers.
  • MiscModule.Debris(Obj, Time): It spawns a new task, waits and deletes the passed instance once the time passes.
  • MiscModule.TweenOnce(Tween, Yield): Plays the passed tween and cleans it once it ends, yield lets you yield your script until said tween ends.
  • MiscModule.MuteSound(Sound, Bool): Fades the passed sound out, making it muted until false is passed. Used to mute the game’s music when you grab a Main Collectible.

DialogueHandler

This is the dialogue system’s typewritter, isolated into it’s own module to allow using it for cutscenes and such.

API:

  • DialogueHandler.Enum: Table that exists to help you figure out what the DialogueTable contains, can’t be actually used to autocomplete keys so don’t even try.
  • DialogueHandler.FindWords(String, SpecialWords, Count): Function used by type internally to find special words and replace them with their RichText version.
  • DialogueHandler.Type(DialogueTable): Takes a DialogueTable who contains the UI elements and content/assets used for the dialogue, check the Enum table inside the script and the dialogue system itself for reference.

Datastores.rar

I have always struggled using datastores, and I never managed to understand the popular datastore modules everyone seems to use due to their API, in my opinion, being filled with boilerplate. This module aims to solve that, simplifying datastores as much as I could to a level that’s far easier to grasp, reason why it’s called Datastores.rar.

How do I use it?

If all you care about is saving, all you need are 2 functions, Save and Load, with an optional Reset one to wipe data.

Of course, saving each time something in your game changes can oversaturate datastores, which is why the module offers a SharedData variable you can opt to place your data table on in order to cache it and save said data later, a SaveShared function to share its contents, and 2 additional functions that save the data automatically for you AutoSaveSharedData (not recommended) and SaveSharedDataOnShutdownOrLeave.

Why should I use this over X?

You shouldn’t, this system uses raw datastores and has no fancy data loss preventions like session locking and such because I honestly don’t know that much about datastores, in fact, I had to read GEILER123456’s datastore tutorial while making it. It also isn’t heavily tested either, so it’s prone to bugs too.

It’s a simpler solution to it’s overcomplicated alternatives, aimed at folks like myself with no clue how a datastore works. If you got more experience with these than me I welcome you to use a different module or fork this one to your liking.

API:

Constructor:

  • DataKey: The name of the datastore.
  • PlrKey: The name of the key under which the player data is saved (TDLR: Personal player data name).
  • Player: Optional, only used for a safety check when loading the player data.

Functions:

  • Save(Data, Retries): Attempts to save the data table you provided the number of retries you provided or 20 by default.
  • Load(Retries): Attempts to load the data using the constructor’s key the number of retries you provided or 20 by default.
  • Reset(Retries, Default): Only useful for RTE requests, otherwise you can just pass nil to the Save function. Retries the amount you passed or 20 by default.
  • SaveShared(Retries): Calls the save function passing SharedData, retries the number of times you passed or 20 by default.
  • AutoSaveSharedData(Time, Retries): Calls the SaveShared function each time the interval you provided passes, can be paused setting PauseAutoSave to true or passing nil in the Time param. You figured out by now it retries.
  • SaveSharedDataOnShutdownOrLeave(Retries): Calls the SaveShared function both when the player leaves and when the server shuts down, retries blah blah blah…

Parameters/Variables:

  • Data: Data to be saved (dictionary expected).
  • Retries: How many times you wanna retry before giving up on loading/saving the data (20 by default).
  • Default: If your game uses a table by default when the data is initially created, you can pass it again when resetting the data.
  • Time: Interval of time between which the game will save automatically if AutoSave and SharedData are on.
  • SharedData: Variable holding the shared data table that related functions/your scripts can use.
  • PauseAutoSave: Determines weather the autosave shared data function will save next time the interval passes or not, only works if it’s function was called in the first place.

Events:

  • Saved: Self Explanatory.
  • Loaded: Self Explanatory.
  • Erased: Self Explanatory.
  • SharedSaved: Self Explanatory.
  • SharedLoaded: Self Explanatory.
  • AutoSaved: Self Explanatory.

Example of usage:

--Setup
local DataModule = require(Modules:WaitForChild("Datastores.rar"))
local Data = DataModule.new("GameData", Player.UserId, Player)

--Load
local LoadedData = Data:Load(20) --Retry 20 times, returns data
if LoadedData ~= "Error" then --If there weren't errors loading the data
	if LoadedData then
		--Data already exists
	else
		--New player
	end
else
	--Error
end

--Save
Data:Save({Apples = 1, Oranges = 2, Basket = {}}, 20) --Try to save the passed table a max of 20 times

--"Merge"
Data.SharedData = {Apples = 1, Oranges = 2, Basket = {}} --Cache your data
Data:SaveShared(20) --Try to save the shared data table a max of 20 times

--AutoMerge (Not recommended)
Data:AutoSaveSharedData(30, 20) --Autosave shared every 30 secs

--Merge on leave
Data:SaveSharedDataOnShutdownOrLeave(20) --Save on leave/shutdown

ContextBindingUtil

This is pretty much just PseudoPerson’s ContextActionUtility, but with some minor additions on top of it to make rebinding the controls on my games easier. I barely added anything so yeah, full credit to them for their awesome module, been using it for years.

New Features:

  • Support for multiple functions.
  • Shared name-based binding.
  • Updating your previously binded action, which is useful to let your players set their own controls.
  • I also wanted to add a function to force a button to have a certain position, but I ran out of time, so maybe I’ll add that on a patch.

API:

  • BindingModule:BindAction(ActionName, Functions, MobileButton, Binding1, Binding2): Much like CAS, it takes an action, a function, a boolean to determine if the mobile button shows, and the 2 keys the action will be binded to. Additionally, a table with multiple functions can be passed in the 2nd parameter.
  • BindingModule:UpdateAction(ActionName, Binding1, Binding2): Takes a previously binded action and updates it’s keybinds with the passed ones.
  • BindingModule:AddFunctionsToAction(ActionName, Functions): Adds the passed function to your previously binded action, can optionally take a table with multiple functions.
  • BindingModule:UnbindAction(ActionName): Unbinds the action.
  • BindingModule:FindAction(ActionName): Looks for the passed action, returns it’s PublicTable, which to be honest is pretty useless, and if it doesn’t exist it returns nil.
  • BindingModule:WaitForAction(ActionName): Yields until the requested action exists.
  • CAU’s Functions: The following CAU functions can be called directly from CBU: SetTitle, SetImage, GetButton, DisableAction. They only work if the action’s PublicTable exists and they do the exact same they do in CAU.

CharacterUtils

Like miscellaneous, but for the character.

API:

  • CharacterUtils.BundleAnimationIds: Table containing all animations, from all animation packs, including blundle exclusive ones. Useful for NPCs.
  • CharacterUtils.Sounds: Table pointing to the LocalPlayer’s sounds (keep in mind it’ll change on death).
  • CharacterUtils.Animations: Table pointing to the LocalPlayer’s animations, might rarely return nil (keep in mind it’ll change on death).
  • CharacterUtils:ToggleReset(Bool): Toggles the player’s reset button (retries forever, I don’t like limited retries as they often fail on some devices/connections).*
  • CharacterUtils:ToggleMovement(Bool): Toggles the player’s movement.
  • CharacterUtils:GetFloor(Offset): Casts a raycast down and returns the floor or nil.
  • CharacterUtils:GetInputDirection(): Gets the direction the player moves torwards based on their input.
  • CharacterUtils.RayParams: Rayparams the GetFloor function uses.
  • CharacterUtils.PlayerModule: Player’s PlayerModule (because the module itself uses it).
  • CharacterUtils.Controls: Player’s Controls Module (because the module itself uses it).

BadgesHandler

Small wrapper that simplifies BadgeService and allows using it from the client. Due to me not adding badges it’s untested so lmk if you have issues, but the exact same implementation worked fine on my other projects.

How does it work on the client?
Once you require the module at least once in the server, a connection will be made that allows the module to call itself from the client, allowing functions to work both sides normally.

Doesn’t calling it from the client allow exploiters to cheat badges?
Yeah, so do remotes in most cases if not checked on the server, which I assure you most devs don’t do. If you are worried about it I suggest you do the following:

  • Add a dictionary with a check function tied to each BadgeID you wanna “protect”.
  • If said BadgeID is found in the dictionary when the client calls the server, call said dictionary function.
  • Perform a date/distance/validity check to ensure the player can get the badge.

This is assuming each badge needs it’s own individual check, otherwise a general one should work.

API:

  • BadgesHandler.HasBadge(Player, BadgeId, MaxRetries): Takes the player, the BadgeID and the max amount of retries before nil is returned, indicating the operation failed. Returns the result (true means you have it, false means you don’t) if successful.
  • BadgesHandler.AwardBadge(Player, BadgeId, MaxRetries): Takes the player, the BadgeID and the max amount of retries before nil is returned, indicating the operation failed. Returns the result (true means the badge was awarded, false means the badge is disabled) if successful.

Attribution

This game wouldn’t have been possible without the following resources, so huge shoutout to their respective creators:


License

SNM relies on external resources, so, to avoid breaching any licenses I decided to split the license into chunks.

  • Server/Client side code and Utility Modules written by me are licensed under MIT
  • Forked modules, modules that rely heavily on external resources, and the game’s map (which contains some free models), are all Unlicensed.
  • External resources, of course, retain their Own licenses if they have any.

What’s the license for:
This time around I don’t care that much about attribution, license is simply there to ensure you, the developer, that you can use the code without worrying about me changing my mind with the code and doing something malicious later.

TDLR for the MIT license:

  • You have to keep the license and the copyright notice in your file if you redistribute my code.
  • You can’t hold me liable for anything.
  • Other than that, do as you please, attribution is optional but appreciated.

Cut Content

If you played the demo already you might have noticed some areas feel unfinished (or you might have noticed this doesn’t have that much content after reading the post). This is because sadly, I had to cut around half of the content planned for the game.

Turns out around 4 days ago while I was getting ready for an early access release, I got greeted with the unpleasant surprise that after months of vague warnings without date, 32 bit support was dropping on the 17th of this month, giving me pretty much no time to polish the release.

I find it a bit depressing that after giving the platform over 4 years of my time, they couldn’t bother giving me said notification a bit earlier, but who knows, maybe i’m to blame for not opening the roblox player that often.

For the previous reason, some of the following content had to be cut from the release (at least until I can afford myself a new computer, which I doubt will be soon):

  • Level design is pretty much non-existent and around half of the map had to be cut.
  • Some moves from the moveset, some utility modules, additional mechanics, and additional features for already existing ones had to be cut.
  • Localization for the game had to be cut.
  • Badges for the game were cut.
  • There’s certainly room for optimization.
  • Long etc, some of it in the proyect’s Trello.

More stuff that I either don’t remember or didn’t get past the drawing board also got cut (for example, another project of mine required a boss battle system, so I was thinking about implementing that here both for personal use and to showcase how it’d be made), but for the most part, that’s about it.

If you want a more lengthy version detailing what was left unfinished, I went ahead and left the project’s trello public for anyone interested in what got cut and what bugs were left unfixed, so feel free to take a look at it:


Extensions

Extensions are additions to the resource that didn’t fit SNM as a game, but were added post-launch as general resources for it.


EgoMov Patch

Egomoose’s moveset helped me a lot when back when I couldn’t script a single line for my life, so I thought it’d be cool to give it a little update with some stuff I wish the moveset had back when I started using it, that’s pretty much what it is.

It’s a patch made by me in like 3-4 days that makes it compartible with SNM, updates deprecated code, adds some QoL features such as mobile support, a simpler installation and some utility functions (there’s a more detailed comment in the installer specifying what I added, so check it out for more info).

If you find any issues with it other than the ones specified in the installer lmk, can’t guarantee I’ll fix it but I’ll try.

SNM Port:
SNM Egomoose Port.rbxl (627.5 KB)

Standalone File:
EgoMov Patch.rbxm (47.3 KB)


Closing

And finally, the parts y’all came here for, the project itself, uncopylocked for your use:

Ignoring the final 2 days of development, this project was a lot of fun to work on, and even though i’m not exactly happy this is the final product i’m releasing, I still hope you can find it of use. I’m unsure weather I’ll come back to it later on or if I’ll just switch to a different platform, but at least i’m glad I managed to release something.

Thanks a bunch to Andrixter for helping me record the trailer for the game and for providing feedback, thanks to the folks who upvoted and commented on the post I did on reddit I couple days ago (first time something I make gets liked that much), thanks to the folks who made the resources mentioned in the post, and thank you for sparing me some of your time.

I kinda forgot the links at the end bc I made the post early by mistake, but if you like my work and wanna see future stuff I make you can find me on Twitter and Reddit, i’m not a very social person so I rarely post/reply, but I post creations there from time to time. Other than that if you wanna support me just playing SNM and upvoting it if you liked it is more than enough.

103 Likes

It’s beautiful! I’m speechless…

3 Likes

Amazing! I’ll definitely check the code, thanks for this!

3 Likes

I’m sure the project is well made, but I can barely see it in the trailer. The whole screen can’t stay still for more than 2 seconds.

2 Likes

Yeah apologies, might have edited it a bit too much

I’ll try to update the reply with some mobile footage once i’m back home

Edit:
Alr, here’s a bit of footage of the example quest (sorry about the quality, my phone isn’t very good)

5 Likes

Thanks a lot for this
I was exactly looking for some decent and recent game that was open source so I can look into the code to analyze and improve my skills

1 Like

Very good, enjoyable game, I found something about 293 dollars, 8 cups of coffee and some pieces of bread, also completed a task for the baker (I do not remember his profession).
Thank you very much for such a gift and for the detailed documentation!
P.S: I found the bug when chatting with NPC and clicking on the dialog box, it can trigger the event “Delete achievements”.

1 Like

Small update because studio somehow still boots

  • Added an extensions section to the post.
  • Made a patch for egomoose’s moveset that simplifies its workflow, updates deprecated code, and makes it compartible with SNM (you can find it in the extensions section).
  • Forgot to include the animations for you to reupload in the original release, added them a couple days ago.
  • Silently fixed some very minor issues with the game (they are in the trello).
  • Trello was kinda illegible, cleaned it up a bit.
  • Added some spam at the end of the post because why not.
  • Forgot to credit the icon library I used, added it to the attribution section now.
  • For the majority that probably missed it because I made the post early by mistake, the documentation is done.
8 Likes

Is it possible to add the bounce effect to the tight ropes? i’ve been trying for a few hours but i cant get it to work.

(i got the bounce animation to somewhat work, but the player is only able to walk after the tween has ended)

1 Like

It’s definitely possible but yeah, I think I had the exact same issue myself while making the tight ropes so I had to cut the effect out for this release. Try playing around with a lerp that accounts for the player’s position instead of a tween, maybe that works.

Otherwise, I’ll probably update this with a bunch of the stuff I didn’t have time to add in a couple months (busy with uni atm), so if I manage to get the effect working by then I’ll let you know.

2 Likes

bro is “fixing good” from the video a reference to breaking bad

1 Like

I tried using lerp, but it doesn’t have easing styles (probably a skill issue of mine)

Right, I have never used one of these before so I might be wrong on this one, but I think people use Spring Modules to achieve that bouncy feel in their interpolations, try looking one of those modules up here on the forum.

Yeah

2 Likes

Hey, thanks for the help. I managed to get it to work with a spring module after another whole day of trying

2 Likes

so uh… i have a issue here
so basically i cannot test in-studio. it just kicks you and talks about datastore related?
whatever it is i can’t test in studio because of this


idk im not a programmer or anything

Make sure you have datastores enabled in your game’s settings, the game only kicks you if you failed to load the data around 30 times if I remember correctly (if you are actually failing to load the data over 30 times, which is unlikely, try raising the amount of retries).

1 Like

New laptop, new (smol) patch

  • Speed of the movement, some moveset moves, and some mechanics has been boosted. Why now? Because back when I released the game I was only able to playtest it on mobile and didn’t realize how slow it felt.
  • Diving against a wall now resets your jump count a maximum of one time for additional verticality.
  • The depth of field, as well as some particles were too big on max graphics due to the same reason as above, that’s fixed.
  • Even though I’m confident I added a check to prevent the dialogue system breaking on death, that check no longer works, so the option to reset is disabled during them for the time being.
  • Tight rope speed was too fast, decreased it.
  • The roll move didn’t account for your framerate, making it feel horrible on pc, this should hoperfully be fixed.

Let me know if you find any bugs/issues after these changes.

What’s left for the project?

No promises I’ll have the time to, but ideally if I don’t fail every single uni subject by the end of the year I’ll have this magical thing called vacations, in which i’m hoping to come back and finish the game.

What’s expected for this “Update”?

  • Rewrite of the game’s core systems: PublicTables don’t really work on a big scale, so I’ll be switching the game to a more modular self-contained structure. This should hoperfully be easier to to use and maintain, as well as more widely used.
  • Level design: Currently the game has no level design, with all collectible locations being sort of improvised last minute. I’d like to come back and give that a try, it won’t be great as i’m no level designer, but it’ll be better than nothing.
  • Additional Moves/Mechanics/Extensions: Lots of mechanics and moves had to be cut/simplified, I prob won’t add them all, but you can expect some new ones. One I had almost finished when roblox stopped booting was a remake of some classic adventure game power ups for an extension, so hoperfully I can get that one out soon.
  • Moveset Improvements: Even though I just readjusted the moveset speed, it still doesn’t feel fun to play with to me, so with the upcoming new physics based character controller i’m hoping I get to make something more decent. I’d also like to have a tutorial section explaining how to make your own moveset if mine still happens to suck, so let me know if anyone would be interested in that.
  • Misc: Badges, Localization and such might too be added, but they wouldn’t be a high priority, I don’t wanna overscope the project.
  • Versions: If I ever get to releasing this version I’ll keep the current one and it’s documentation separate for anyone using it.

Let me know if there’s anything you’d like me to add to the resource, I’ll try to tackle everything I can by the end/start of the year if I have the time to. And thanks a bunch for the support the resource has gotten and the kind words some folks dropped at the group wall, it means a bunch.

4 Likes

EgoMov Patch (Extension) Hotfix

Two days ago someone reached out with a couple questions regarding the port of the game that used the Egomoose Moveset, so I opened the file and umm… It turns out that due to an oversight with mobile buttons it didn’t work at all in desktop/console since release :grimacing:, what a way to release a community patch huh?

So today I went ahead and fixed that as well as pretty much all the bugs I came across, here’s a list:

  • Mobile buttons no longer cause the script to error while setting up.
  • The crouch animation no longer cuts itself, (happened due to me forgetting to round the MoveDirection).
  • The Double Jump animation didn’t show during the fall state due to what I assume is a roblox bug, it is now loaded on the spot so it plays properly.
  • When using the simplified controls, you were able to cancel moves you weren’t supposed to be able to cancel in the original resource (longjump and highjump), and you were able to stack some moves (dive and longjump), that was unintended and it should be fixed now.
  • Downward slopes broke the longjump for some reason, spent most of the day trying to fix that, couldn’t find a reliable fix, added a task.wait(0.1) and that did the trick :cold_face:.
  • You used to be able to spam the long jump by holding shift which felt too overpowered, now every time you perform a longjump the crouch state is ended, forcing you to enter it again before triggering the move.
  • Much like the previous, the highjump and jump end the crouch state too, as it felt awkward to be forced into a previous state when you were using the NonHoldable setting for the moveset (Did I ever mention in the post this patch has a lot of accessibility settings as well as mobile support? You should check it out, I think it’s pretty epic).
  • If you dropped the moveset in a baseplate you might have noticed that the HighJump didn’t work, that’s because the Moveset uses JumpPower instead of JumpHeight. Now if you forget to enable that it’ll be done for you with a warn in the console reminding you to do it.
  • The chasis didn’t follow the player, causing the size of their model to get ridiculously big, now it gets welded to them (the original release also fixed the displacement present in the original resource, but that’s not really new).

Additional Troubleshooting for future reference:

Q: I dropped the EgoMov patch in a baseplate and it doesn’t work, errors saying the folder with the chasis inside is nil.
A: That’s very likely due to you having FilteringEnabled on, either turn it off, or move the folder somewhere it won’t be destroyed and update the Patcher to look for it there.

Q: I’m getting a warn in the console telling me to turn UseJumpPower on.
A: Yeah, turn UseJumpPower on or fork the moveset to use JumpHeight instead.

Q: Will this be updated once roblox drops their new character controller to benefit from it?
A: Probably not, but if it breaks with the update I’ll try to ensure it still works.

Q: Does this rely on any of SNM’s dependancies such as PublicTables? I don’t like those. Is it exclusive to this game?
A: Nope, it’s fully self contained and can be used for whatever as it is as far as I remember. The port adapts the SNM mechanics to work with the moveset, not vice versa, but if you don’t want those feel free to just grab the standalone file.

Q: Are the mechanics in the port up to date with the actual game? Will they ever be?
A: No, that’d be too much work. They are just there to showcase how to interact with the Moveset, if you want all up to date grab the newest version of SNM, delete the default moveset, and edit the scripts to work with the EgoMov Patch using the old port as a reference.

Q: Unrelated to the moveset, but the collectibles don’t work.
A: You have to publish the game under your profile and turn on datastores in order for them to work.

Q: I found a new bug.
A: If it isn’t exclusive to this fork I suggest reporting it in the original thread as it’s pretty hard for me to fix something I didn’t really make myself. Feel free to report it here too though and I’ll see what I can do to get it fixed.


Here’s some bad quality footage:

And here’s the updated files:

Hoperfully this is the last hotfix I have to make in a while, sorry for bumping the post again.

4 Likes

Any updates on Sip N’ Munch? All animations broke.


Sorry for the 70-day old bump. But it needs to be addressed.

(Also the water freezes me, even if I reset my character.)

Edit: realized I had to publish first, whoops. Animations are still broken though.

Roblox doesn’t allow sharing animations, so you need to reupload them to your profile in order for them to work (if i’m not mistaken I left them in Assets > Animations > Animation > KeyframeSequence (the one you reupload, the blue square). Other than that, like you already figured, you need to enable datastores for collectibles to work.

Work on the new version started last week, but it won’t be out until next year as the scripting needs to be redone from scratch.

2 Likes