Space Engineers

Space Engineers

55 ratings
Official Guide: Scripting a Deathmatch Scenario
By Steinmarder
This guide will show you how to use Visual Scripting to create a simple Team Deathmatch Scenario
   
Award
Favorite
Favorited
Unfavorite
Introduction
Scripting a simple Deathmatch Scenario - Step by Step


Hey fellow engineers! In this guide I'll show you a little bit about Visual Scripting and how to use it to create a simple deathmatch scenario for multiplayer.

We will be taking the "Rival Platforms" Map that was part of the stock selection of maps by the time of writing this article, and modify it into a more automated deathmatch scenario.

In case the map is no longer available when you read this guide, simply create one of your own with two bases for the red and blue team, and two factions with the "RED" and "BLU" faction tag.

Add a neutral spawn location with a medbay, owner of which set to "Nobody" and you are set to go and follow this guide.

What we will be doing

Instead of having players manually choose their team over the faction screen we will be assigning them to their teams via visual script when they enter a trigger zone.
They will then also be teleported to their respective bases and can jump into whatever devices you have prepared for them there and start fighting each other.

A Beacon will be placed in each team's base for two purposes:

1. Marking the Location of the Bases

2. Serving as the Victory Condition (if your beacon gets destroyed, the game is lost)

If the Victory Condition is reached, we will have the game display a message and restart the map.
Setting up the Visual Scripting Tool
First you will have to set up your Visual Scripting Tool. You can (at the time of writing this article) find it in your Space Engineers installation folder under Tools\VRageEditor\VisualScripting.bat




If you are firing it up for the first time, you will have to set the path to your Space Engineers\Content folder in the Editor's settings. After doing this, restart the Editor (VisualScripting.bat) and we can get started!

Now you want to create a NEW item (button top left corner) and select "Level Script" in the dropdown box. Assign it a name of your choosing, the file in our little tutorial is called "deathmatch_example.vs".

Next we need to attach the script to our scenario file, so the call to the script is added into the scenarios .sbc file.
For the example scenario I have, as mentioned above, taken the "Rival Platforms" scenario. Thus, I just copied all the files under CustomWorlds\Rival Platforms\ into another folder named \Rival Platforms v2\

When attaching the script to the scenario, in the dialogue box, you can also set the name of the scenario and it will be changed in the .sbc file accordingly, since the name of the scenario is also saved there, not in the name of the folder as it might suggest when you look at the screenshots.
Preparing your map
Now lets start to prepare the map. As briefly mentioned in the introduction, we need a neutral spawn room where every player joining the server can spawn before selecting a team.

So, if you are starting out from nothing, create a room, hook it up with power, place a medical bay there and set the owner of the bay to "Nobody".
Using the "Rival Platforms" map, change the spawnroom to something like this:

We will make it so that players walking to the red room get assigned to team "RED", players who walk into the blue room get assigned to team "BLU".
In the "Rival Platforms" map these factions are already set up. Otherwise, create them as NPC factions that accept everyone.

Next, we will start to get the actual functionality into the map.
Setting up Triggers, Waypoints and Special Entities
Now lets go trigger-happy ;)

First, we will create a few waypoints. With these we get exact positions in the game world, which we can use in the scripting. To some of the waypoints, we will also attach triggers.

Triggers are sphere shaped, can be adjusted in size and fire off an 'Event' when a player enters them. It will also always pass the unique PlayerID of the player who entered the trigger zone over to the script.

Waypoints

So, in your map, Press F11 until you see the "Scripting Tools" on the right hand side of your screen.
This will also put you into spectator mode, so you can increase/decrease the speed with which you travel through the map by rolling your mousewheel!


At the desired position of a waypoint, in case of the screenshot above the teleportation target point in the Blue Team's base, hit the "Spawn Entity" button and it will create your first waypoint.
You will also see little gizmos with which to move your waypoint around a bit, "R" will change between translation and rotation mode.. but lets not worry too much about that right now.

While it would be ok to just leave the default name (Waypoint_0) and refer to it in our script, it is always good practice to follow a foolproof naming convention, especially if you want to change your work again a few months from now. So with the waypoint selected, hit the "Rename" button in the top right.


Repeat the same process with the Red Team's platform / the other team's base in your scenario.

Trigger Zones

Now we will go and set up the trigger zones to assign players to the different teams. So head back over to your spawn room and place a waypoint in the middle of each of the rooms.

Selecting waypoints inside of a grid can sometimes be a little tricky, you have to hit the exact center. To prevent you from moving around your entire base by accident, hit the "Disable Transformation" button in the top when you have problems selecting a waypoint.

Now, with a waypoint selected, hit the "Attach to selected entity" button. A dialogue will appear, requesting you to name your new trigger. If you are following this example to the letter, name them "Trigger_Red" for the Red Team's, and "Trigger_Blue" for the Blue Team's room.


You can see in the above screenshot, the text: "Trigger: Trigger_Red Entity: Waypoint_Red"

The text in this box will only appear if your spectator camera is inside the trigger zone. Use this to finetune the size of your trigger. You can use the "+" and "-" left and ride of the "Size" button, or click on "Size" and type in the desired size value.

In the end it should look something like this:


Special Entities

Just one thing left to do now: We need to prepare for our victory condition. We wanted to use the team's Beacons for this. In case of the Rival Platforms Scenario, they are already there. If you are making your own map, place a beacon for each team (or another block if you have something different in mind).

Now when you select the beacon, it will show you the name of the Grid as the "Selected Entity" and the name of the Block in the "Selected Block" field.

This is not yet a unique name though!!

At this point, what you are seeing there is the name with which the block would be listed in the control panel. But it is not the name of our Entity as handled by the script yet. At this point, it only has a number which, yes, we could refer to (you can find it in the scenarios .sbc file) but it would make your script hard to understand.

So, hit "Rename" on the right of "Selected Block" and give it a unique name. In our example we go with "Beacon_Red" and "Beacon_Blue".

If you are creating your own scenario, do the same with any other block you would like to refer to in your visual script.
Assigning Players to Factions and teleporting them to their Base
So lets get back to the Visual Scripting Tool. Now that we have prepared everything in our scenario, we can start putting the logic together.

As mentioned, we want two things to happen when a player walks into one of the trigger zones:

  1. Assign the player to the relevant team
  2. Teleport the player to the correct platform



We will also be displaying a message, informing everyone on the server that said player has joined a team.

But let us go through it step by step:

Setting the player's faction

So, picking from the lower right corner of the Visual Scripting interface, under "KeyEvents" find "AreaTrigger_Entered" and doubleclick. The empty event should appear in the middle of your interface.
Now simply click and drag from the little node to the right of the white "AreaTrigger_Entered" box and release. It will open a menu with a list of different functions to choose from as the next node.
Under "Sandbox.Game" and then "Factions" you will find "SetPlayersFaction" with the exclamation marks behind it, telling you it accepts "playerID" and "factionTag" as input. Doubleclick, and the new function will appear, connected by a white line from node to node.

As mentioned before, the "AreaTrigger_Entered" event also provides a parameter, the unique Player-ID (int64 / long "playerId") of the player who entered the zone. You can see this at the bottom of the event box. From this Output node, drag a line to the Input node for the playerId on the "SetPlayersFaction" function.

You will see now that one line is white, the other one is black. Parameters or values being handed over are displayed by a black line. Sequence nodes are connected with white lines, highlighting the flow of your script.

Now the faction tag could be supplied from somewhere else also, as you can see from the third node that accepts a "String" as input, but we will just doubleclick the field and type in "RED".

Teleporting the Player

So, from the SetPlayersFaction's sequence node, drag a new line out and find "SetPlayersPosition" under "Sandbox.Game" and then "Players".
Having that linked, we will again need to connect the "playerId" value output' by the "AreaTrigger_Entered" event with the "playerId" input of the "SetPlayersPosition" function.

If one would run the mission right now and walk into the trigger, the character would already get teleported, but to the 0,0,0 coordinate at the center of the map. We need to supply a "Vector3D" as the new position value to the "SetPlayersPosition" function.

So, rightclick an empty location, above the "AreaTriggered_Event" for example, from the box that appears select "Function" and then under "Entity" find "GetEntityPosition".



In "GetEntityPosition" doubleclick the empty field and put in the name of the teleportation target waypoint of the Red Team's base, in our example "Waypoint_Platform_Red". Now from the output node that is returning a "Vector3D" type, drag a line to the input node for "Vector3D" with the "SetPlayersPosition" function. Done :)

Displaying a "PlayerXY has joined the ... Team" message

We also want the rest of the server know if a new player joins a team and thus the fight.. So, rightclick again to create a new function, find "GetPlayersName" in the list of "Players" functions and again provide the "playerId" input from the "AreaTrigger_Entered" event.

The string this function provides as output is now the same as the player's name as displayed in the game.
So with an Arithmetic Node (rightclick), left on the default setting (addition, "+"), we can combine the string providing the player's name with another one we type in as a "constant" (rightclick -> Constant).
In the empty constant field, we input " has joined the Red Team!". Now connecting the "GetPlayersName" output as "Input A" and the new constant as "Input B" in our Arithmetic Node, the full output would be a string, reading "Steinmarder has joined the Red Team!" in my case.

Now all thats left to do is to actually print this to the screen of everyone as a message. For this, find and place "ShowNotificationToAll" from "Notifications" in the functions screen. To have the message be displayed for 5 seconds, set "disappearTimeMs" to 5000 (miliseconds), connect the Arithmetic Node's output to the string input for the message and add it to the sequence by connecting to it from the "SetPlayersPosition" sequence node.

Repeat

Now repeat all of these steps for the blue team. Remember, the faction-TAG is just "BLU".
Checking for the Victory Condition and restarting the Game
This part is pretty straightforward, albeit a little confusing on first glance. First we create a "BlockDestroyed" event from the list at the bottom right of your screen.
Then we setup two Arithmetic and two Branching nodes. "Input A" for the Arithmetic Nodes is the String "entityName" of the "BlockDestroyed" event.
For the "Input B" on both of the Arithmetic Nodes, we create constants, putting the strings "Beacon_Red" into the one, "Beacon_Blue" into the other. These are the unique names we assigned earlier. In the Arithmetic Nodes, doubleclick the field containing the "+" and select the "==" instead. "Equals".
Thus they will check if the entity name of the last block destroyed matches either "Beacon_Red" or "Beacon_Blue".

Now sequence the Branching Nodes as shown in the screenshot below.
What happens here is this:
  1. If the Arithmetic Operation "is entityName == Beacon_Red" = true, the sequence on the "True" node is fired
  2. If it is = false the sequence on the "False" node is fired
  3. "False" node leads to second Branching Node where "Beacon_Blue" is now compared to the entity destroyed
  4. If "entityName == Beacon_Blue" = true, the sequence on this "True" node is fired, if it is = false, nothing happens



You will find the functions sequenced to each node's "True", "SessionReloadLastCheckpoint", under "Gameplay".

As the "fadeTimeMS" let us put in 10000 for 10 seconds, the "String font" we change to "Red" and the messages should read something like "The Red Team's Beacon has been destroyed! Blue Team Wins!" and vice versa for the other one.

Now as you can see, I have also added a "ShowNotificationToAll" function node again, like for the message when a player joins a team, and put the same values and messages in there again.

This is because the script is not run on everyones machine. Thus, only the player who hosts the game can actually see the message belonging to the SessionReload function being printed on his screen.
Also, Auto-Save needs to be disabled in the world-settings or instead of restarting the scenario you end up just travelling back in time for 5 minutes ;)
Preventing Shenanigans
So, having your scenario and scripts set up you eagerly await to see players fight and try to destroy the enemy's team's beacon.

"But wait!" you think "What will prevent them from just shutting off their beacon to hide it?"

Well you do, silly. ;)



Like shown in the screen above, you can hook in the games update method, add a "Sequence Node" to then check with two Branching Nodes for the Boolean value that is returned from "IsBlockEnabled" functions for your two beacons.
If the value returns "false" for one, just enable the block again with an "EnableBlock" function for the respective block.

Because this happens every frame, it will feel like the button is not even responding to the player clicking on it, staying stoically on "ON". Solved ;)
Hints / Notes
By the time of writing this guide, there is a little bug left in the visual scripting that wont assign players fully to a faction until they reconnect or just do it manually the old fashioned way.
This is fixed and should be in the dev-branch of the game on the 16th of February, in stable on the 23rd.

The way described in this tutorial is also not the optimal way to do this, it just makes for an easy introduction.
Using a "BlockDestroyed" event to check for the Team's Beacons destruction actually forces the server to check every single time a block is destroyed anywhere in the game world if this was indeed one of the two blocks. For a quick deathmatch with your friends this doesnt matter.. If you have a large gameworld with many players... ;)
A better way to do it would be to hook a check for the existence of the blocks into the "Update" method of the game and then only let it check every ... 300 frames for example.
< >
10 Comments
ATMLVE Feb 18, 2017 @ 8:05pm 
Ha I don't need answers to random questions, I need tutorials, like this.
Steinmarder  [author] Feb 18, 2017 @ 7:47pm 
There is also both, a Keen Software House - Discord Server and a Space Engineers - Reddit where you can get in touch with other modders and community members directly, to get quick answers to your questions :)
ATMLVE Feb 16, 2017 @ 2:08pm 
Sounds good to me! Thank you so much for communicating. I have been trying for so long do get some communication from Keen on this stuff.
Steinmarder  [author] Feb 16, 2017 @ 1:39pm 
If you follow this guide to the end, to get a feel for the system, and then just have a look again at the different functions available in the list.. Many things should become obvious in regards to what you can do. (Player, Inventory and so on)

Concerning the drones: There will be a more elegent system to access simple AI behaviour in the future, but the person working on that has a few other things on his list with higher priority right now, so.. "stay tuned".

As to more guides: I will be publishing / re-working several more on different topics, to (once and for all?!) have them no longer become obsolete/outdated.
So.. lemme finish those other guides and then I can take requests ;)
ATMLVE Feb 16, 2017 @ 10:44am 
Yeah I've seen you around Ronin, we both want the same thing! Lol.

Something else that would be great to see a guide on is how to affect a player's inventory, such as add/take out items, or detect what the player has in their inventory.
Ronin Planetary Industries (RPI) Feb 16, 2017 @ 10:08am 
Thank you, thank you, thank you. A guide to the visual scripting of drone behaviors (strafing, ambushing, etc.) would be appreciated so much. They were advertised in Xoc's updates but impossible to figure out without full knowledge of the levels and visual scripting attached to the missions. Just a clean world with one drone in it would be so great...
Thanks in advance.
ATMLVE Feb 16, 2017 @ 9:27am 
This guide is awesome. Could you please make more? Particularly on how to use the special drone functions, such as strafing and ramming, which were introduced in an update and are only available in visual scripting? nevertheless, any more guides would be appreciated, this one shows how to enable/disable blocks, something that was unknown before.
Steinmarder  [author] Feb 15, 2017 @ 3:41pm 
This is a "Keen's" guide mate ;)

But thank you, stay tuned for more and others to be reworked and updated in the future in this manner.

About your question.. Yeah.. you could for example create two global variables, then keep tracking 'whenever a player dies - event' and check if he was one or the other faction .. And have another routine keep checking if one or the other number goes over a pre-determined value.

There you go, your "100 Kills - Team Deathmatch - Objective" .. if thats what you had in mind ;)
[AI]666Savior Feb 15, 2017 @ 2:25pm 
is there a way to chang ethis to determine "number of players killed out of x" and give victory then? The game mode you are decribing here is a bit closer to rush or conquest. Also, props on the guide, i can actually understand it, unlike keen's guide...
System1024 Feb 14, 2017 @ 1:10pm 
hey steinmarder, good job scripting!
See you on Medieval Engineers!