Simple Path Randomization in CREY
Randomization will freshen up the gameplay experience and promote replayability. There are multiple ways to go about it, but I'm going to concentrate on the simplest of them all.
Also, this article will be an experiment by me on writing builder tutorials for CREY. I'm not one for talking, so don't expect video tutorials from me. I will stick to written tutorials with lots of images. If this ends up being useful, then I will keep making them, covering all kinds of topics.
Base concept & live demo
The basic idea is to make a static, hand-crafted map with multiple pathways, and then block some of those paths. The room placement of the map does not change, and every room is still accessible, however the player must take a different route each time.
I made this little map for the demo, feel free to try it out:
The goal is to collect the key and exit the area. Then it resets, and you can explore the same map, but every time it will be slightly different.
The map layout
In the demo, there are 3 paths the player can take:
- Go through the room on the left. (RED)
- Go straight through the corridor. (GREEN)
- Go through the 2 joined rooms on the right. (YELLOW)
The key is randomly placed in one of the 3 predefined locations. (BLUE)
The (?)-marks along the way show the potential locations for a blockade. As you can see, some paths have more than one. This is to demonstrate how a single path's complexity can grow.
The randomization has two stages here:
- Decide which path or paths to block (red, green, or yellow path). Here we can only block up to 2 paths, so there would always be at least one free path.
- Upon deicing to block a path, for paths that has multiple blockable points along the way (like red and yellow), choose only one of the options.
The second point is really important here. Imagine sealing off two doorways of the same color. That would still block that path as a whole, but it would also seal off a room that could potentially hold the key.
I'm always blocking at least one path, but a maximum of two. This, with the key placements add up to 51 possible variations on just this little map. And you can scale it however you want. You can even add sub-paths to the main paths, or even more parallel pathways.
How to randomize the paths
Let's start with the logic. We want to block minimum 1, maximum 2 of the available paths, but how do we do that? First, let's visualize what we're trying to achieve.
We either block one path:
|Block path A||Block path B||Block path C|
Or we block two paths:
|Block path A||Block path B||Block path C|
This is called a truth table. It tells us all the possible variations we want to have on the output (the 3 paths) and puts them into a nice list. Also, notice how the second table is practically the inverse if the first one.
Putting this to practice calls for the Randomizer prop. It works similarly to the Selector prop, as they both has a set number of outputs, and one of those outputs has a HIGH signal, while the rest is always LOW. This output will be used to select one of the 6 cases from the truth table. Set the Ports input in the Randomizer to 6, this will make it have 6 output ports.
Now, to connect the cases to the actual blocking logic. The second column in the truth table, "Block path A", has a YES at case 1, 5 and 6. You can use an OR Gate with 3 input ports, and wire 1, 5 and 6 from the Randomizer to it. Then repeat this for the other two paths as well.
There is only one thing missing. The Randomizer will always have the first output selected. To randomize it, we must trigger the Randomize input when the game starts. Providing a constant HIGH signal is enough to achieve this. For this, I'm using the output from an IF NOT Gate prop. The input on the IF NOT Gate does not need to be wired anywhere.
This is optional, but to help organize things, double click on the title of the OR Gate where it says "OR Gate". This lets you edit the title. Now replace the old title with "Block path A". Then the same for B and C.
How to visualize a blocked path
A blocked path can take many forms. It can be a door that is either open, or permanently closed. However, this might give the feeling it could open up at some point.
The closed state might as well be the complete absence of a door, replaced with a wall.
Or you can also block off corridors with rubble.
Putting it together
One way to control a blockade with wires is to use a Spawner prop.
To have it spawn your door, use the targeting tool, and target the object you want as the door.
- Right click on the Spawner prop.
- Select the Target option. (The blue crosshair icon)
- With targeting active, click on the object you want to spawn.
- Right click anywhere to exit targeting mode.
- Once targeting is done, whenever you select either the object or the prop, a blue line is drawn between them.
Now place the Spawner in place where you want the door to spawn. As for the door object, it can be placed anywhere on the map. Once something is targeted by a Spawner, it will be used as the template, and will be hidden in gameplay mode.
To set up the Spawner with the randomizer:
- Set Spawn Range to 0.
- This input can be used to have the target object spawned in a random location withing the specified range, but we don't need this now. Setting it to 0 will make it spawn exactly at the location of the Spawner.
- Wire the block signal from the OR Gates to both Max Number and Spawn.
- When a signal is wired to a numeric input, it well be interpreted as LOW = 0, HIGH = 1. Setting the Max Number to 0 causes any previously spawned object to unspawn. In this case, it allows for a re-randomization.
- When the Spawn input gets a HIGH signal, it spawns the targeted object once.
Now set up the Spawner for all 3 doors, as shown in the image above. You can already see the result in playtest mode, give it a try! Keep restarting the game to see a different setup of doors each time.
How to add multiple doors along a single path
As I stated earlier, if you have multiple doors along one pathway, and that pathway is about to be blocked, only one door must be locked, otherwise you'll make some rooms completely inaccessible from either side.
This calls for a second Randomizer prop. If we have 2 doors, that's 2 ports on the Randomizer. And then one AND Gate to each Spawner. The logic is that [(IF the path is to be blocked) AND (IF this specific door is selected)], then spawn that door. And then the same for the other door.
Don't forget to wire the Randomize input to an IF NOT Gate to trigger the randomization upon starting the game.
How to spawn the keys for the exit
We have already ensured that every room will remain accessible, so the key can go anywhere. With 3 keys, we need 3 Spawners in the 3 possible locations, and a Randomizer with 3 outputs to trigger one of them. Based on the previous examples you already know how to do this.
As for the logic inside the key, it requires...
- One prefab representing the key, and another one for the exit door. I'm using just primitive cubes for this demo.
- A Sensor to detect when the player has walked over the key.
- A Destroyer prop targeting the key, so the player can see it getting "picked up".
- A Box it up prop to make the key, Sensor, and Destroyer as one unit for the Spawner to target.
- A Lift prop to animate the exit door.
- Alternatively, you can use a Joint Rotator for a rotational animation. It works fundamentally the same as the Lift prop.
Now, place the Sensor on the prefab that represents the key. The white mesh around the sensor shows the area where the detection occurs. It's a sphere by default, but you can change it to a box or cylinder. For each shape there are several inputs where you can set the size of the area (see image below).
The first parameter in the Sensor prop controls how the "Detected" output will behave.
- If set to "While contact", the output Detected signal will be HIGH while the player inside the sensor area, and LOW when not.
- If set to "Forever", the output will remain HIGH forever after the first detection.
Set it to "Forever", because we're going to need a constant signal to keep the exit door open.
Now, let's wire up the rest of logic. We need the key to disappear when picked up, and the exit door to open.
Open the Destroyer prop and wire the Detected signal from the Sensor to the Destroyer's Destroy input. The first parameter controls what gets destroyed when it gets the signal. By default it is set to "Target", just what we need. Now target the prefab that represents the key from the Destroyer prop. (Right click on the prop, select "target", then click on the prefab to be targeted)
Then to set up the door, open the Lift prop. Wire the Detected signal from the Sensor to the Lift's Move input. When this port gets a HIGH signal, the lift will move to its second position. You can set how far that is with the Second point input. You will also need the door prefab to move with the lift. For that, the door must be glued to the Lift prop. Compared to the targeting, this is the other way around! Right click on the door, press the first icon that says Glue, then click on the Lift prop. A yellow line will show the connection (see image below).
Now back to the key. Whatever prefab you have chosen for the key, they all have a physical shape that the player will collide with. We don't need this. Double click on the key, and set Physics type to none.
Now let's box up the key. The Spawner can only target a single object, so the key and its internal logic has to be contained in a single unit. That's what the Box it up prop is for.
- Select all the objects you want in the box (it's OK to include the box as well, nothing will happen if you try to put it inside itself).
- (the objects you want inside are the key, the Sensor, and the Destroyer)
- Press B, as in boxing. Now you are in boxing mode, similar to targeting and gluing.
- Click on the box to put all the selected items into the box.
- Now double click on the box to close it. Notice how the props that are inside get hidden. You can double click again to open it.
Notice how the entire unit gets outlined when you hover the closed box or any of its visible contents. Or when you hover the box while it's open. It helps spotting if you have left out something.
Now time for the spawning logic. As you're already familiar with Spawners I'm going to skip ahead to the targeting. Target the box one by one from each Spawner.
Time for playtest! If you had set it up correctly, it should work as promised. Now, time for some experimenting and world building on your own!
A few extra notes
Let's talk about the behavior of wires on spawned stuff. When items are spawned, their wiring to non-spawned props will be added as well. But there's a catch!
As you might have noticed, you can pull any number of wires from an output port, but can only have one wire going into an input port. If you think about it, this makes sense, because how should multiple inputs behave? Trigger when at least one has a HIGH signal? Or rather when all of them? Or maybe trigger on every rising edge? (That is the moment when the signal goes from LOW to HIGH).
When a spawned prop has a wire to a non-spawned prop's input port, it will work, but only if you spawn it once. Spawning more will make multiple wires appear in the input port, which is invalid, and only the first wire will actually work, the rest are ignored.
There's a workaround for this with Variable props, but that's for another tutorial.