Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Technical documentation regarding the functional systems of Space Station 3D!
Information and documentation regarding the functional systems of SS3D.
This space is for the technical documentation relating to game systems, for design documentation use the link below.
For a list of current controls and control ideas see link below.
All implemented controls are (and must be) in Content/Systems/Input/Controls.inputactions
If you are not familiar with the new input system, here are two good guides for almost everything you might need.
After saving Controls.InputAction, it regenerates Console class in Scripts/SS3D/System/Inputs. You can use Controls class in your script by getting Input property in InputSystem through Subsystems.
All actions and action maps should be named in PascalCase with spaces between words. (Toggle Run, Snap Right, Snap Left, etc). Avoid long names.
All handlers, that subscribe to actions, have to contain InputAction.CallbackContext argument. Handlers have to be subscribed to actions in OnStart() and unsubscribed in OnDestroyed().
If you want to Disable/Enable some acitons (or action maps), use Toggle methods in InputSystem, otherwise, it will cause issues.
Input Consumption is enabled. It means, in actions with modifiers you have to press modifiers first and binding last. This feature prevent multiple actions from being performed unintentionally (For example, if you have E action and CTRL+E action and you pressed CTRL+E, E action won't be performed).
The code for item sprite generation is not made by one of our contributor as far as I know. You can find it in RuntimePreviewGenerator static class. there's a link toward github not functionning anymore. The code is quite complex and not clearly documented.
The idea is to take an item, set up a camera so that the item is well centered in the camera FOV, make a render using the camera and putting it into a 2D texture. I don't know much more about it sadly.
The asset management system.
Asset data
is the nickname of a collection of scripts that for a system initially introduced by Seteron, it beingb v a way to get assets in a more practical manner than using Resources
.
Load
(
"AssetName"
)
.
With Asset data
, however we use static classes
with the corresponding to the asset name, so finding an asset is a manner of knowing the asset name in order to load it.
Add system outline here.
AssetData
system is divided in a few classes:Assets
This class
caches all the AssetDatabases
in the game, its where we retrieve the asset we want. We can use the getters to specify which asset from a database to retrieve it.
DatabaseAsset
A class
that contains a reference for the Name
and DatabaseName
of an asset, this is used to load them in a convenient way as the class is used when we generate the database code.
AssetDatabase
ScriptableObject
that loads all assets present on an AddressablesGroup
, adding them to a list of assets and generating a static class
with DatabaseAssets
related to those assets.
WorldObjectAssetReference
A class
used for world object assets, assets that can be placed in the world as a GameObject
, this has the purpose of creating a way to reference database assets via the inspector, without having to look for a prefab, it is also generated by the asset data system.
AssetDatabaseSettings
a ScriptableSettings
class that loads all the AssetDatabases
. Everything here is automated, you don't have to mess with. Its useful to see if all the databases are loaded, or to ignore code generation.
Whenever the code/asset generation is triggered, the system will scan for asset databases that are not loaded, load them in the list and then start the generation process.
The generation process simply iterates through each database and generate a static class of an DatabaseAsset
for each item in the database.
The system will also generate a bunch of WorldObjectAssetReferences
, this is done on all assets that can be cast to GameObjects, they can be used in the inspector to reference an database asset.
Create an AddressableGroup for your assets. (
Window/AssetManagement/Addressables/Groups
)
.
Set your assets as addressabble and in the groups menu drag them to your new group.
Go to the Assets/Content/Data/Databases
folder, and create a new AssetDatabase by going to Create/SS3D/AssetDatabase
.
Set your AddressablesGroup in the inspector.
Go to ProjectSettings/SS3D/Assets/AssetDatabaseSettings
Press find and load asset databases.
Check if your new database code was generated in the Assets/Scripts/SS3D/Data/Generated
folder
To use an existing asset database, simply look for its file by "Assets/Scripts/SS3D/Data/Generated"
, find the database you want to use and then call the item you want to grab.
Some of these examples have implicit operators to cast them into the correct type. You have to manually add the implicit operators, for now.
This system is very experimental, we are still seeing if this makes sense to use and if this is the best alternative. Please report issues.
If the database is renamed, the old generated class is not deleted.
The new way of referencing DatabaseAssets via WorldObjectReferences only works for GameObjects (by design). Ideally this should be separated by type or category, and have a way to not depend on guids, I was thinking on testing some stuff with the localization table for ids.
Using traits for ID/role permissions
ID Cards also use the Traits system to check for a player's permissions, for more about that check Roles
The gamemode system decides what objectives are given to player and which of them are antagonists.
Gamemodes are what allow a round to begin and end, they set up the objectives that need to be completed by either antagonists or crew members, when these objectives are met the round ends, or alternatively, the shuttle can be called to end it.
They are separated into 2 features, Modes and Objectives.
Modes are what decide how the round is gonna end, how many traitors the round will have and what objectives there will be.
Objectives are things antagonists or crew members must achieve during the round, they are given either to individual players or groups of players, with the objectives being shared or not between them, meaning either an objective can be completed by one player and it's completed by everyone, or it can be solely completed by a single player, making other players fail the objective.
The traits system is a simple way of checking for basic properties in objects.
Ideas for Traits can be found here.
Traits can be used to check for basic properties by name, like what category an item is part of or what permissions an ID Card holds, they can also be separated into categories, currently there are just two, "None" and ID Permission.
You don't have to set anything up, just name the Trait, first be certain there are no other traits in the project with the same name.
To create a trait go to Assets/Content/Data/Traits, right click and go Create -> Inventory -> Traits -> Trait.
Filters are used by container to filter which items can be stored inside them, they check for traits in the items for that.
To create a filter go to Assets/Content/Data/Filter, right click and go Create -> Inventory -> Traits -> Filter.
Using traits and filters with containers and items.
Accepted Traits: Traits the items will need to be stored inside the container. Denied Traits: Traits that are denied, regardless or if the item has accepted traits. Must Have All: Check if the item must have all traits to be stored instead of just a single one.
To add a trait to an item just go to it's prefab and add it to the Traits list:
To add a filter to a container just go to it's prefab and add it to the Container Descriptor's script in the inspector:
This page describes how to add a new objective that can then be added to a game mode.
To add a new gameobject you have to first create a new script in Assets > Scripts > SS3D > Systems > Gamemodes > Objectives.
You may copy another objective that is already done to make your life easier, but the rules an objective has to follow are:
It has to be a child of the GamemodeObjective class.
It has to call the Succeed method to be completeable.
It has to have a CreateAssetMenu property above the class.
It has to be in the SS3D.Systems.Gamemodes.Objectives namespace.
Let's say you wanted to create an objective to picking up a BikeHorn
First you would create a new script that inherits GamemodeObjective, let's call it PickupBikehornObjective.
You would then have to set up the CreateAssetMenu property since the GamemodeObjective class is an ScriptableObject. you should also put it under the SS3D.Systems.Gamemodes.Objectives namespace.
You would then end up with this:
We now add the pickedUpItemId and pickedUpPlayerCkey private properties so we can hook them to the ItemPickedUpEvent and check if the picked up item was a bikehorn.
We now have to add an event listener to the ItemPickedUpEvent, we do that in the AddEventListeners method, we will then hook it to a HandleItemPickedUpEvent method.
We usually call the FinalizeObjective to conclude an objective, but you could call either Succeed or Fail from any method, but following our practices we will do the check inside FinalizeObjective anyway.
Now you just have to create the ScriptableObject, for that we go to the Assets > Content > Data > Gamemode > Objectives folder, right-click and go Create > Gamemode > Objectives > GetBikeHorn.
Now you just have to set up the properties for the objective as follows:
Title: What is the title of the objective that will appear in the UI.
Collaboration Type: if the objective is assigned to a single individual, multiple or if it's a competitve objective, meaning a single players can complete it.
Alignment Requirement: If Traitors, Crew members, or both can get this objective.
Min Assignees: Minimun number of players that will be assigned this objective.
Max Assignees: Maximum number of players that can be assigned this objective.
Done, now you have succesfully added a new objective to the game!
This page describes how to add a new role into the game
The first thing you need to do to create a new role is to go to Assets/Content/Data/Roles, either duplicate an existing role or right click and go Create -> Roles -> RoleData, name both the file and the "Role name" variable as the name of the role.
Role Name: the name of the role PDA Prefab: the PDA prefab that will be spawned in the player's inventory ID Card Prefab: the ID Card prefab that will be spawned inside the PDA Permissions: the list of permissions the ID Card will have. Loadout: the loadout that will populate the player's inventory at round start.
To create a new loadout go to Assets/Content/Data/Roles/Loadouts, right click and go Create -> Roles -> Loadout, there choose the items that will spawn in the player's inventory.
Check which slots will have items and then set which items will be spawned in the droplist that will show, remember that these items have to be registed in the AssetDatabase for them to appear in the droplist.
Now go to the StartingRoles asset in Assets/Content/Data/Roles and add the role to the list.
Drag the Role data into the slot and set how many players can play that role per round, setting it to zero means there is no limit.
Remember that it is possible to create ID Cards with permissions inside the game, which would effectively allow more people to play the same role that what is initially allowed, a player's role is dictated solely by their ID Card, which can be stolen or dumped.
This page describes how to add new permissions into the game
To add a new permission into the game go to Assets/Content/Data/Traits/Identification/Permissions, right click and go Create -> Inventory -> Traits -> Permission, just name it accordingly.
To add the permission into a role, just add it to the Role Data's permission list
This permission can be checked inside of a script throught the Inventory class, by using the HasPermission function.
For example, the Security Locker currently checks for a Security permission to be both locked and unlocked. the Unlock Locker interaction checks for the permission by getting the player's inventory:
Interactions are not inheriting from monobehaviour. This means that interactions are not components on an game object. Interactions are generated by an interaction target or interaction source, which both can be on game objects.
The fundamental parts of the system are the source, the target, and the interaction.
The source is used to perform the interaction. When holding nothing, it is the hands of the player. When an item is held it is the source. Sources are structured like a tree. A source can have a parent and you can traverse up, depending on what information you need. Right now the source structure is a maximum of 2 deep (hands an tools) but that might change.
The target is on what the interaction is performed, essentially what the player clicks. It can be anything from another player to a floor tile. There can be multiple targets per game object.
Interactions are created whenever the player wants to interact. This can happen in both the source and targets. They are essentially the results of interacting and modify the world. These interactions are always run on the server but there are also client interactions to show client only things.
This page describes how to add a new game mode with it's set of objectives
Creating a new game mode is generally simpler than adding an objective, you just have to create a new class inheriting the Gamemode class with a CreateAssetMenu property and... that's it! you don't even have to add any code to it unless you want to change something about the internal working of gamemodes, which is beyond the scope of this guide.
Let's recreate the Nuke gamemode as an example, first go to Assets > Scripts > SS3D > Systems > Gamemodes > Modes and create the new class there, we'll call it NukeGamemode.
Now we just have to create the ScriptableObjects for the game mode, for that we go to Assets > Content > Data > Gamemode, we'll need multiple scriptable objects for each game mode so it might be a good idea to make a new folder for each game mode, so we'll create a Nuke folder for ours.
First we need to create a GamemodeObjectiveCollectionEntry, which is used to control how the objectives will be distributed to players, to create one we right-click and go Create > Gamemode > GamemodeObjectiveCollectionEntry, we then have to set it's properties, we do it as follows:
Gamemode Objective: the actual objective's ScriptableObject.
Assignment Probability: the probability of the objective being assigned to a player.
Remaining Assignments: how many objectives of this type can be assigned.
After that we need to create a GammodeObjectiveCollection which is a collection of objective entries that a game mode can assign to players, to create one we right-click and go Create > Gamemode > GamemodeObjectiveCollection, we then just have to put all of the objective entries into the collection's list.
We now have everything set up to create our actual gamemode's ScriptableObject, for that we right-click and go Create > Gamemode > Modes > NukeGamemode (the name of your gamemode), we then just have to set up it's name and it's GamemodeObjectiveCollection.
Done, the game mode is completed, to test it we just have to go to the GamemodeSystem game object in the Lobby screen and set the round's game mode to ours.
Roles and loadouts system. How to set them up.
For design documentation on this system, use the link below.
Roles are the jobs players get at the start of a round, they decide what the player will spawn with and what permissions they will have in their ID Cards.
Permission Checking ID Cards have a set of permissions that allow you to have access to restricted areas or open locked containers.
Loadouts Loadouts set what each role spawns with, things like outfits and starting items, like a baton for a security officer.
Role Selection The system chooses players's roles the moment they join the lobby, currently a single player is chosen as Security and every other player gets the Assistant role.
Figure out if you want the interaction to be on a target (ex. machine in the world) or on a source (item in hand, ex. welder, crowbar) which interacts with a different target. Once you have worked out if it is a target or source, create a class which implements IInteraction. The most important methods are going to be:
GetName: generates a display name
CanInteract: if the interaction is possible
Start: called when the interaction is started
You can access data about the interaction using the InteractionEvent parameter. I recommend looking at existing interactions for examples.
Add a script to the object. It should inherit from InteractionTargetBehaviour or InteractionTargetNetworkBehaviour, depending on if you want certain things to be networked. You will have to implement GenerateInteractions in your class. There you can return an array of the interactions on your object.
If you are creating an interaction which is only needed for a single kind of object, consider using a simple interaction. You can create it and assign callbacks which you can directly implement in your attached script.
The item component is the source of the interaction. To add more interactions, you can add a component which implements IInteractionSourceExtension.
Send Alainx277 or John a message on the discord, we'd be glad to help
The container & inventory system. How to set them up and use in-game.
Containers are the virtual representation of a space in which items might fit.
Containers are currently equivalent to Rectangle grid of slots, where one can put items in. (add picture here)
The number of items a container can contain depends on the grid size.
A container with a 5x5 size will be able to contain as much as 25 items.
Items themselves have a size and therefore, can take multiple slots.
Containers are synchronized among clients, meaning that when one client modify a container, all other clients observing this container will see it changed.
This page explain what the fields in the AttachedContainer script are doing, and eventually what you have to do to set them up correctly.
The ContainerDescriptor has a custom editor, when checking this checkbox, the custom editor will handle adding necessary scripts, as well as showing you only relevant fields.
99% of the time, you want to check it. The only time you don't want to check it is when for some reasons you want your container scripts spread around multiple game objects, which should be the exception.
This is the name that will appear in interactions, such as "store in toolbox".
It's also useful to distinguish between multiple containers on the same game object.
Checkbox to define if the container can be interacted with. For most containers, that should be the case.
Define if the container needs an UI or not. If not, the "view interaction" won't be available, only the "store" and "take first" interactions.
Define if the container has a open/close interaction, or not.
If a container is openable, then this defines if the container needs to be open to store anything in it. It should be checked for most openable containers but it's there as they could be some exceptions.
You might want your container to behave in an exotic fashion, with particular interactions. If you check this one, you have to add a script on the gameObject implementing InteractionTargetNetworkBehaviour's interface, otherwise you won't be able to interact with your container.
This set up the maximum distance between the user and the container before the UI closes. Only available if HasUI is checked.
This is the size of the container. It represents the number of slots in a rectangle grid, the first number being the width, the second the height.
Set if items should be invisible (not rendered) when inside the container. There's a few cases where we'd like items to be rendered, such as when they are inside a locker.
Set if items should become childs of the container game object when inside the container. In most case you should leave that checked.
A global filter on the container, allowing you to decide what kind of items can fit in it.
This page describe how one can set up a container on a game object in the Unity's editor.
The first thing you need when setting up a container is to add an AttachedContainer script on your game object.
This script has a custom editor that will allow you to build a container in a coherent manner. The AttachedContainer also contains references towards other relevant container related scripts, such as ContainerUI, Container and others.
Once AttachedContainer is added, you should see a pretty confusing editor, looking something like this :
If you're unsure what all this stuff does, just check the Automatic Container Set Up checkbox. It will tidy up the editor as well as set up a bunch of container related scripts automatically.
After checking the box, it should look like this. With Automatic Container Set Up checked, you can mess around with the editor since it's designed to avoid any incompatibilities. Changing some values may remove or add some other fields.
This will also add to your game object the necessary components to make your container functional.
There's a tool-tip for each field, so just hover your mouse on a field to know more about it.
Once your container is all set up, you can use it in game, here's how you can do it with different container set up.
First we set up a container for the toolbox. This container is called TOOLBOX, it's interactive, it has an UI, it can be opened/closed, we can only store things in it when it's opened, we choose a small size for it 5x5, it hides items and it attach them as children game object to the toolbox.
Right click on the toolbox with a free hand (you can't open something with something already in your hand), you should see the interaction radial menu :
You can then select "open TOOLBOX", which should visually open the toolbox.
Once opened, new interactions are available, such as closing the toolbox or viewing it's content.
If you choose view, the container UI will appear :
You can then grab an item with left click and left click or drag the item sprite in the container.
Container's content should not be easily visible to just anybody. We're using Fishnet's solution to limit visibility of some game objects to some clients.
If your container's content should be only accessed by client "viewing it", meaning they used the view interaction on the container (it'll be this case for most containers), then here's how to proceed.
First, make sure your AttachedContainer script is on its own gameObject. This is because fishnet will make the game object invisible to most clients, but we only want the container to be invisible, not the other component of the game object. You can add a Container gameObject as an example :
Now add a NetworkObserver script on this gameObject, check override type as "Add missing" (unless you have a specific reason not to do so), and add a ContainerViewedCondition on it :
Make sure the containerInteractive script is not on the same gameObject, otherwise interactions won't be available to the client. It should be up in the hierarchy, in our example, we could put it on ToolBoxBlue.
That container should be ready to go, and networked as expected !
It's possible to add custom storage conditions to a container. Those conditions allows you to extend container ability to remove or add items, based on pretty much anything you want.
To do so, you'll need to create a new script that implements the IStorageCondition interface.
Here's an example :
This condition is for the jumpsuit container on the Human. It allows the jumpsuit to be removed only if the belt container is empty. Pretty simple !
Just put the condition script on the same game object as your container, you can add multiple conditions if you want.
First you'll need to set up a public reference to an AttachedContainer in Inventory.cs. (give it a name representing the container, such as "LeftPocket"
Then, add an attachedContainer script on the prefab with the inventory script(such as Human) wherever you want it to be. To set up the container, you can draw inspiration from containers already set up on the Human prefab such as the left hand container on hold.l game object.
Set up a reference in inventory editor to your newly created AttachedContainer.
It's all good !
Now if you want to interact with this container through a similar UI as the one for hands or pockets, you'll have to check up our UI guide coming soon.
Inventories represent in part what player can hold on themselves, including clothes, what they have in hand, in their pocket, in their bag, but also what they can access through a container. When a player open a toolbox and use an interaction to view it, the toolbox's container become a part of the player inventory as long as the player is observing the content of the toolbox.
Inventories in SS3D rely on the container system. Every container on a player and every container viewed by the player is part of its inventory.
The inventory is currently responsible for the following functionalities :
Transferring an item to a container, eventually in a given slot.
Spawning an item from a container back in the world.
Firing the open animation when a container is added first to an inventory.
Switching between activated hands.
Adding and removing container to itself, firing event when doing so.
Checking server side some of the above can be done by a client asking to perform those actions. That would for instance include checking if a player can interact with their hand with a given container.
Containers UI are depending a lot on the inventory system, when adding a container to an inventory, it's UI(if it has one) will be displayed.
The Roles System depends on it as well, as it needs to spawn role items in the inventory at the beginning of the round.
Functions and features of the tilemap system.
For mapping design see the Maps Design document linked below.
The tilemap system forms the basis of the space station and is responsible for the creation and management of all items and 2-dimensional tiles that compose the station. Every static object such as floors, walls and bolted objects (i.e., fixtures) are part of the tilemap. Other systems such as construction or atmospherics have a close integration with the tilemap.
Grid-based design with chunks The new design leverages collections more, which allows us to go faster through objects that are on the same layer. Next to that, the map is divided into chunks of 16 x 16 tiles. This should allow more flexibility in the future in cases where we only need to look at objects nearby, without the need to go through the entire map.
Saving and loading The tilemap is now fully serialized and can be saved and loaded at will. This allows a few options like the ability to save chaotic stations at the end of a round for other game modes, but will also make resolving merge conflicts in the map easier.
Multi-tile objects Objects that are bigger than a single tile can now be successfully added to the map. You can now place a cryo tube again without having to worry about it colliding with other nearby objects.
Items Items are now part of the tilemap and can be placed and removed. This means that they are also part of the save system.
To add a new item or object to the tilemap system, a new scriptableobject needs to be created.
Make sure to save the item/tile ScriptableObjects under the corresponding "Content/Data/TileMap/Resources/" folder
The following information can be filled in for a TileObjectSo:
Name String - describes the name of the object
Prefab - which prefab to spawn
Icon - Icon which is used by the creator and construction interface
Layer - which layer the item should be put in
Generic Type - Generics types are used to categorize objects on the same layer. Used by the adjacency connector to connect walls for example
Specific Type - Specific types are used to specify the material that the object is made of. For example used by carpets to ensure that they don't connect to other floors
Width - width of the object. Used for multi-tile objects
Height - height of the object. Used for multi-tile objects
Items can be added in a similar way:
Make sure the Name String is unique. Not doing so will create issues during loading & saving.
Using the tilemap creator.
To get started with the creator, run the game and press B after spawning. This will bring the creator menu up.
Press left mouse button to place an object
Press R to rotate an object
Hold left shift to replace an existing object on the same layer
Click drag to place/delete/replace in a line form
Alt+Click drag to place/delete/replace in a square form
Each tile can hold multiple objects on different layers. The following layers are implemented:
Plenums - These form the foundations of the station. Always required to be present before another layer can be build
Turfs - Walls and floors go on this layer
Wires - High, medium and low voltage wires
Disposal - Disposal pipes fit in this layer
Pipes - Atmospheric pipes go here
WallMountHigh - Wall mounts that are placed high on the wall
WallMountLow - Wall mounts that are placed low on the wall
FurnitureBase - Regular objects such as chairs, tables and counters go here
FurnitureTop - Objects that go on top of FurnitureBase. For example desk lamps
Overlays - Indicators that fit on top of floors go here
The design of the system can be found in the the link below.
Adjacency connections refers to when a tilemap object is connectable to its neighbors, walls being the typical example of this.
In order to make walls and tables connect to each other, you need to make use of a adjacency connector. This keeps track of which neighbours it is connected to and will replace and rotate meshes accordingly.
There are different types of adjacencies supported right now.
Simple - Used for simple connections without a lot of use cases. Covers the basic shapes such as I, U, O, T and X.
Advanced - Expansion of the Simple type with a lot more edge cases covered.
Offset - Used for meshes which are not placed in the middle (thus offset). Used by pipes for example.
Disposal Furniture - checks if connected from above to a disposal pipe. Very specific one.
Disposal Pipe - Used for disposal pipes only.
Door - Specific to doors, because of the specific way it connects to other doors and to walls.
Pipe - For pipes with an offset (not centered on the tile)
The first thing you need is to implement the IAdjacencyConnector interface.
If your tile object connects only with things from the same layer, adjacent to it, you might want to look at the AbstractHorizontalConnector class and inherit from it.
It will only ask you to implement the IsConnected() method, as it varies a lot from one connector to another. Door, simple, advanced, offset, pipe all inherits from it. Chances your connector could too.
If the connections are weirder, such as disposal pipes able to connect to disposal furniture, a different object on a different layer, with a different connector, and with special behavior when it connects, then directly implement the IAdjacencyConnector interface.
Add the right adjacency connector script to your prefab's root game object. You can recognise a Connector script because it implements the IAdjacencyConnector interface. To know which specific script to use, check the connectables design pages.
Connectable scripts usually ask you to add specific mesh for specific configurations. You need to fill them all up for the connector to work properly.
Check the Connectables design page for more informations about your specific connector and how the meshes should look.
Also know that the connectables scripts assume the mesh you link are rotated in a specific fashion, otherwise they may appear incorrectly in game. If you have this issue of meshes badly rotated, try to rotate the models you use, out of the game.
The character customization menu will be separated in:
Name (And alternate names)
First name
Second name
Age
Backstory
Blood type
Bank PIN
PDA
Ringtone
Background color
Body
Sex and other options
Limbs and PartsOrgans
Hair (includes facial hair)
Skin tone
Eye color
Voice (?)
Fart
Scream
Scarring (?)
Crew
Special Role
Equipment
(Favorite items?)
Job loadout
Backpack
Jumpsuit
Socks
Undershirt
Underwear
SS13 perk system
See your character in full screen + animations, and maybe items. (Should be a good "Photo booth", for marketing reasons)
Export character JSON
This page describes some of the inner workings of the tilesystem
In order to create a new tilesystem, you need to create a new GameObject in a scene and add the following scripts:
TileSystem
TileResourceLoader
Furthermore in order to use the creator, you will need to add the ConstructionMenu prefab to the scene.
When playing in runtime, the TileSystem will automatically try to load a saved tilemap from the StreamingAssets folder and recreate it using ScriptableObjects found by the TileResourceLoader.
When placed, the tilemap uses the following structure to keep track of all tiles. Ranked in order from the lowest element to the highest:
PlacedItemObject - This script is attached to every itemthat is spawned on the map. Ensures spawning/despawning and is always attached to a GameObject.
PlacedTileObject - This script is attached to every object that is spawned on the map. Ensures spawning/despawning and holds references to any adjacency connectors. Always attached to a GameObject.
TileObject - This class represents a single object on a single layer and can contain one PlacedTileObject. So for each tile position in the world, there is are (number of layers) amount of TileObjects. Not attached to a GameObject.
TileChunk - A chunk represents a 16 by 16 grid of TileObjects for each layer (16 x 16 x num of layers). Chunks are not really used at the moment, but have to potential to benefit networking and loading for massive maps in the future. Created as a GameObject as a parent to PlacedTileObjects.
TileMap - Holds a dictionary of chunks filled with TileObjects and a list of PlacedItemObjects. The later are not position bound so are not part of a chunk. Created as a GameObject as a parent to chunks and items.
TileSystem - The system to hold TileMaps, and serves as the main interaction point to place and remove items/tiles. Only a single instance of the TileSystem can exist.
TileResourceLoader - Reads and holds the ScriptableObjects for each item and tile that can be placed. Used by the TileSystem to figure out what object is placed.
Saved-X-Object - Used to hold the state of the corresponding class/struct and is serializable. Used for saving and loading.
BuildChecker - Used to validate combinations of placed objects and can check whether an object can be placed. For example, no objects can be build on the Turf layer if a Plenum is missing.
SaveSystem - Generic save system that is used for saving and loading of the tilemap from and to a file. Uses the StreamingAssets folder so that builds also receive it.
TileMapCreator - Used as an in-game editor for placing and removing tiles/items and saving/loading. Is networked so that clients are also able to work on the map.
GhostManager - Used for construction as part of the TileMapCreator to create a "ghost" object to see where you are building.
MultiAdjacencyConnector - Used for connecting walls and tables together. Keeps track of which neighbours it is connected to and will replace and rotate meshes accordingly.
Alain suggested spatial hashing to network big quantities of objects.
Well, bare in mind that this is the design idea, which we haven't executed yet:
Simply the button to exit the map editor.
This menu contains all the tools to edit the tilemap, and has the following tools:
Select
Move around with the mouse.
Edit
Uses the selected object in the object menu with a place hint of the position.
Move (with mouse)
Move an object precisely (only for non-tile-locked objects)
Undo
Undo
Redo
Redo
Quicksave
Saves your map on a list of quicksaves.
Open map selection
Opens a better saving/loading menu
This menu contains all options relating to menu, HUD and view modes.
Reset position to 0
Resets the camera position, zoom, and rotation to the initial values.
Layer view mode
Hide determined layers.
Hide UI
Screenshot friendly feature.
Camera options
FOV options, camera speed, rotation speed.
Map editor settings
Keymap settings, debug options.
The map editing modes are menus to select the list of objects that can be selected to place:
Upper
Lower
Items
Scripting & Placements
Spawn placements, random item spawners, triggers.
Flooring
Turfs
Doors
TileObjects
Wall Attachments
Piping
Disposals
Base tiles
Items. Bananas, sodas, janicarts.
Spawn placements
Random item spawners
Triggers.
This menu contains the selected list of objects based on the map editing modes and subcategories.
Panel containing a search bar to search usign tags, names or keywords
Channels allow a player to communicate with a specific group of other players only, or to listen to only specific messages.
Channels have many properties associated with them to differentiate them. You can find a description for them in the tooltip on Unity. We only describe some important properties of channel in the following.
When entering messages in some channels such as "shouts" or "whispers", only player in a given radius receive those messages.
Some channels such as engineer, science, medical, all require to wear specific headset to emit and listen to messages coming on them.
Some channels ask for some specific rights, such as admins right, to be used. This is necessary so only admins can use channels sending visible messages to everyone.
SS3D has two types of chats available: the lobby chat and the in-game chat.
The lobby chat is useful between rounds and for players that haven't embarked yet. The only channels that can be used there are the OOC channel and the System channel.
The script in charge of handling the lobby chat is LobbyChatWindow, which can be modified in the Lobby GameObject inside the Game scene. The reason why the component is part of the Lobby and not the Chat itself, is because the chat gets disabled and enabled when using the tabs, and a disabled chat won't receive messages. This way, the chat is always enabled.
The lobby chat includes a delayed Welcome message, only sent to the player when they first join the server (before embarking). It can be useful to add some server-specific messages and information.
The in-game chat is considerably more complex than the lobby chat. Apart of the System and OOC channels (also included in the lobby chat), it includes the rest of channels for both OOC and IC communication. A full list of current channels can be found at the end of this page.
The in-game chat window can have several tabs, where each tab includes a list of channels. When joining a round, the chat starts with a default "All" tab, which includes all channels available on start - like Local, OOC, etc. The player can add more tabs by pressing the plus ("+") button at the top left corner of the chat, selecting the desired channels and setting a custom name for the tab. Not selecting any channel will result in a tab with only non-hidable chat channels (like the System channel). The tabs can be reordered by dragging them around, and dragging them out of the tab list will create another chat window - so several chat windows can be opened at once.
On the bottom left corner, there's a dropdown to choose which channel to speak through. The list of channels depend on the current selected tab - so channels not part of the tab won't be available in the dropdown.
Finally, the in-game chat window can be resized and dragged around the UI, so the player can choose the layout they like the most.
Chat channels are implemented using ScriptableObjects. To add a new channel, just create a new Chat Channel with Right Click (on the Project folder) > Create > SS3D > UI > Chat > Chat Channel, and add it to the list of available channels in the ChatChannels asset settings.
There's a list of options to set up the channel to your needs: how to display the character name, the color and format of the chat, if it requires a trait to be used, if should only work within a distance, etc. Check the tooltips of each var for a more complete and updated information.
The way messages are formatted, or the options of the channels, can be easily expanded by either modifying the ChatChannel.cs (for adding more variables) or the ChatMessage.cs (for modifying how a text is displayed). Chat messages are formatted only once - when the player sends it. This is done client-side.
Station Alerts
Who can use it: anyone for now, but should be limited to the captain (when that permission is created)
Chats where is available: in-game
Purposes: for sending messages to the whole crew, both from the captain and the game itself
Color: dark yellow (we're running out of colors)
Format: [Station Alerts] CharacterName: "Message" // [Station Alerts]: "Message" (if sent by computer)
Local
Who can use it: anyone
Chats where is available: in-game
Purposes: for talking with people who are close
Color: white
Format: CharacterName says, "Message"
Other: current distance is 5 Unity units in all directions
Public
Who can use it: anyone with a headset
Chats where is available: in-game
Purposes: for talking with the whole crew from anywhere in the ship
Color: green
Format: CharacterName says, "Message"
Whisper
Who can use it: anyone
Chats where is available: in-game
Purposes: for talking with people that are next to the character
Color: light pink
Format: CharacterName whispers, "Message"
Other: current distance is 2 Unity units in all directions
Shout
Who can use it: anyone
Chats where is available: in-game
Purposes: for talking with people that are farther and can't be reached with the General channel
Color: light orange
Format: CharacterName shouts, "Message"
Other: current distance is 10 Unity units in all directions
AI/Binary/Command/Engineering/Medical/Science/Security/Service/Supply:
Who can use it: anyone with a headset with the permission of the department (Security permission for Security channel and so on)
Chats where is available: in-game
Purposes: for talking with all the people from a specific department
Color: AI: Pink / Binary: Black / Command: Yellowish Green / Engineering: Orange / Medical: Blue / Science: Purple / Security: Dark Red / Service: Green / Supply: Yellowish Brown
Format: CharacterName says, "Message"
Other: currently only Security is linked to the headsets as is the only permission (and headsets) that exist
OOC:
Who can use it: anyone
Chats where is available: lobby and in-game
Purposes: global OOC communication
Color: pink/red
Format: [OOC] PlayerName: "Message"
LOOC:
Who can use it: anyone
Chats where is available: in-game
Purposes: local OOC communication
Color: beige
Format: [LOOC] PlayerName: "Message"
Other: same distance than General - 5 Unity units in all directions
In Game System
Who can use it: only through code
Chats where is available: in-game
Purposes: sending system messages to the player that shouldn't be in the lobby chat - like "unauthorized access to use channel X". Is a "last resource" channel for communicating with the character
Color: red
Format: Message
Other: available in all in-game tabs, can't be hidden
System
Who can use it: only through code
Chats where is available: lobby and in-game
Purposes: welcome message for the player in the lobby, server restart warnings. Is a "last resource" channel for communicating with the player, welcome message can be removed and keep it just for things like server restart or mod warnings
Color: red
Format: Message
Other: available in all tabs, can't be hidden
Distance based channels are channels used to simulate talking without a headset, shouting, whispering ...
*Describe specific channels here*
Department specific channels all require specific headsets to use. Each department have its own headset to communicate exclusively with fellow members of its department.
*Describe specific channels here*
Examining is a fundamental mechanic of SS3D and relies on a clever way to defines which objects should be examined. Check the How it works page.
In SS3D, audio uses a single class to manage most sounds, it's the audio system.
The audio system handles creating and destroying audio sources, putting those sources on the right game objects and syncing sound between clients.
In the vast majority of cases, you should never add an audio source yourself to a game object, but instead you should use the audio system.
One of its perk is that it's reusing already existing audio sources that are not playing anymore, instead of adding a new source everytime something needs to play a sound.
It's also maintaining an healthy amount of audio sources by limiting the maximum amount allowed on a single client.
It should be noted that in order to sync sound between clients, the audio system put audio sources on game object with a network object component. This is an easy way to find the same game object on the client. However, a restriction of that solution is that you should not put more than one audio source per network object.
After a while, if no sound is played, an audio source gets destroyed. This is better for performances.
See the design doc first for more info on the design of the system.
The crafting system script handle storing all possible recipe.
Recipes are organised in an efficient manner that allow to search very fast through them, given we know the target of the recipe and the name of the interaction to execute it.
It's using the following structure :
The system starts by looking for all recipes in the recipe asset database and fill the structure.
Both client and server have access to the full recipe list.
The other major element of the system is the crafting interaction abstract class. When you want to add a new interaction, inherit from it, as it will provide the basis for what you need, if you want to implement a new interaction that will result in crafting something.
You can use the SliceInteraction.cs
script as an implementation example. Notice how the code override the CanCraft method, so it can add its own conditions to allow this specific crafting.
For a crafting interaction to work in game, the interaction needs to be put on an item that will act as the source of that interaction.
In the case of the slice interaction, we could add it to a knife, or any other slicy item. For that, we create a new script that inherits from IInteractionSource :
Just put that script on your knife game object and you're good to go. See how it creates a new sliceInteraction.
Recipes are simple scriptable objects in Assets/Content/Data/Recipes currently. Easy to set up.
Creating a new recipe is very simple :
You can then fill up the recipe with everything it needs. Should be explicit enough.
If you want your recipe to be accessible in game though, you'll need to add it to the recipe asset database.
The examine system uses an exotic way to pick up items. The selection system uses a shader based method. A special camera renders the screen by defining for each object on scene which are selectable, an unique color. Then, the color under the mouse is picked up, and is used to find the game object with the unique color.
To make an object examinable, you'll need to add at least two scripts to it. Selectable.cs, which will allow the Selection system to pickup the object. SimpleExaminable.cs, which will allow to give a name and a description to the object.
SimpleExaminable ask for a scriptableObject, you'll need to create it in Assets\Content\Data\Examine\String. For that you can right click in Project and choose Examine. You can then add a reference to the newly created asset in the examinable object.