Unity is great for fast prototyping games. If you have an idea of some new game mechanics, with Unity you can implement and test it as soon as possible. Under the cut, I'll tell you how you can make a simple puzzle using only standard components and not getting into graphic editors. In the course of the article, I will demonstrate a couple of game design tricks, and in the end I will talk a little about aesthetics, dynamics and mechanics.
For the most impatient of the links below is a ready-made prototype.
Online versionCompiled version for Windows [
Mirror ] ~ 7.5 MB
')
What are we going to do? A two-dimensional puzzle with a kolobok in the role of the main character, who can move the boxes, which can press buttons, which can open the doors, behind which there is an exit from the level that I built. Or you, we have a tutorial here after all.
It is understood that you have already managed to download
Unity and played a little in the editor. Not? Now is the time, I will wait.
Rough sketch

I lied, I will not wait. Create an empty project without unnecessary packages and choose the layout of the windows to your liking, I will use Tall. Add a sphere to the hierarchy, drag the main camera onto it. Now the camera will follow our sphere if it suddenly wants to walk. Rename the sphere to “Player”, drag it to the Project, now we have a
prefab , which we can use in any new scenes, if any. Do not forget to check the prefab coordinates when creating and using, if we want to make a toy in two dimensions, then the third axis should be set to zero for all interacting objects.
Now add a light source, go to the menu GameObject -> Create Other ->
Directional light . Its coordinates do not matter, it will illuminate our objects equally from any location. However, it makes sense to raise it slightly above the stage, so as not to interfere with the selection of objects, so we will set coordinates for it (0; 0; -10). Speaking of the scene, the X axis will grow from left to right, Y from bottom to top, and Z from the viewer deep into the screen. Click on the arrows around the cube in the upper right corner of the scene and rotate it as necessary.
Add a cube to the stage, call it “Wall” and drag it into Assets. The lonely cubic wall next to the spherical bun is not very impressive, is it? Three Scale fields in the inspector will allow us to pull the wall, and the key combination Ctrl + D will create a copy of it. In Unity there are
many other useful hot keys , for example, holding Ctrl restricts the movement of objects to single intervals, and the V key will allow you to attach objects to the tops, and they will stick to the tops of other objects. Great, isn't it? And you still write your engine? Oh well.

Figure out something like a room, save the scene, click Play and admire your creation for a couple of minutes. Good game designers call it testing. Something is missing, huh? Hmm Perhaps, if I admire a little more, then ...
Scripts and Physics
We need more movement and color! Although, if your severe childhood was filled with concrete toys, then you can leave everything as it is. For everyone else, it's time for scripts. I will give examples in C #, but you can write in JS or Boo. In fact, it makes no sense to choose the last two, they were added to Unity rather as an appendage, are less supported, expand worse and it is more difficult for them to find examples. Boo is especially awful, which is essentially unpythonic Python. An abomination. Viva, pythonists!
Create a C # Script, call it “PlayerController”, drag and drop onto the Player prefab and open your favorite editor using
Visual Studio . First you need to rub the extra rubbish, leave only the necessary.
using UnityEngine; public class PlayerController: MonoBehaviour { void Update() { } }
The Update function is called in each frame, which is very convenient for the implementation of the movement, inside it we will place the code. Button press player can be obtained using the class
Input . Unity comes with great input settings, just write
Input.GetAxis ("Horizontal") and we already know if a player on the keyboard has pressed an arrow to the right or left. If a player has a gamepad connected, then he can control it from him, we don’t even have to write extra code.
var direction = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);
With such a simple line we get information about the user's actions and create a motion vector. In order to make a vector somewhere we need
Rigidbody . Select the Player prefab and use the Component -> Physics -> Rigidbody menu to add the required component. Now we can refer to it in our script.
rigidbody.AddForce(direction);
The
AddForce function has several interesting
options for the application of force , but for a start, the default values will suffice.
Done! Save, click Play, test.

Um, you know, it reminded me of that moment from the Inception movie, where a man flipped around wildly and rolled head over hem, then down the wall, now down the ceiling. That was probably how he felt.
We need to prevent rotation and movement along the Z axis. Select the prefab, look at the Rigidbody component and see the Constraints section. We leave unchecked only the first two checkboxes X and Y, the other four are included. Just above, remove the tick Use Gravity and prescribe Drag equal to four (in the section on aesthetics, I will tell why it was done). Testing again.
It moves and does not spin! Hooray! But it does it too slowly. Add one variable to our script and use it in the formula of our movement. All the code will look like this:
using UnityEngine; public class PlayerController : MonoBehaviour { public int acceleration; void Update() { var direction = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0); rigidbody.AddForce(direction * acceleration); } }
Have you noticed how the new Acceleration field of our script appeared in the inspector? Effectively, yes? We drive in the field thirty or something to your taste and check in action.
Materials and Colliders
It's time to make some kind of button to have something to push. Duplicate the Wall prefab and rename it to “Button”. In the inspector at the collider we tick Is Trigger. This will dissolve our button and cause other objects to pass through it. Create the “Button” script and hang it on the button.
Trigger colliders have
OnTriggerEnter and
OnTriggerExit events , which are triggered whenever something crosses a trigger area. In fact, this is not quite true, because there are many different objects and the physics engine does not handle all collisions, read more
here .
To get started, just check how the triggers work. Write something to the Unity console. The
Debug.Log function
is very useful, besides the text, it also knows how to print various game objects.
using UnityEngine; public class Button : MonoBehaviour { void OnTriggerEnter(Collider other) { Debug.Log("Hello"); } void OnTriggerExit(Collider other) { Debug.Log("Habr!"); } }
They threw a button on the stage. Have you tested? Go ahead. It would be clearer if our button changes color when pressed. For color, we need to attach
material to the button. Create -> Material, call it “Button Mat” and drop it on the button. In the properties of the material we choose for Main Color green. Now in the script we can refer to the color of the material using
renderer.material.color and change it as it pleases. Let's make the button blush from the entry of our bun into it. Somehow it happened.
using UnityEngine; public class Button : MonoBehaviour { void OnTriggerEnter(Collider other) { renderer.material.color = new Color(1, 0, 0); } void OnTriggerExit(Collider other) { renderer.material.color = new Color(0, 1, 0); } }
The
Color class can accept, in addition to the three RGB, the alpha, but we have the usual diffuse shader, so it is not important for us. We are testing!

If you have not done it yet, then it is time to clean up in our project, otherwise we will get lost in the confusion of prefabs and scripts. For example, create a folder “Levels” for storing scenes, “Prefabs” for storing blanks, “Materials” for materials and “Scripts” for scripts, and then sort the accumulated wealth into daddies.
You know, our button still does not look like a button! Let's flatten her and make her push under the kolobok. Select the button in the hierarchy, make it 0.3 units thick and lay it on the floor, i.e. set the Z coordinate to 0.35. See the inspector at the top of the three convenient buttons "Select", "Revert" and "Apply"? With them, you can interact with the prefab on the spot. Click Apply and all buttons will now be flat and lying.
To implement software animation, we will use the
Transform class. It has a
localPosition property that will allow us to move the button:
transform.localPosition += new Vector3(0, 0, 0.3f);
This code will click the button. In general, it looks like this:
using UnityEngine; public class Button : MonoBehaviour { void OnTriggerEnter(Collider other) { transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); } void OnTriggerExit(Collider other) { transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); } }
Tested. Hitting a button causes it to stumble neatly because of the spherical collider bun, which will not always touch the recessed button. How to solve it? We recall that the games are half made of lies, which means that the dimensions of the collider do not necessarily have to coincide with the model one. We look at the properties of the collider in the inspector, quadruple its size along the Z axis and shift it by -1.5 in the same direction. We are testing! So much better.
Doors, drawers and magnets
Now that we have a fully functional button, we can make it do something. Let's clone the prefab of the wall, call it “Door”, create the red material “Door Mat”, hang it where necessary and throw a freshly baked door on the stage. In order to somehow affect the door, we need to have a link to its object, so we will create a new variable for the button.
public GameObject door;
GameObject is a class in which all-all-all objects on the stage are
wrapped , which means they all have the
SetActive function, which is represented in the inspector by a tick in the upper left corner. If you still use Unity of the third version, then you should use
alternatives . Using the properties of the activity, you can hide objects without deleting them. They seem to disappear from the scene and their colliders no longer participate in the calculations. Straight what is needed for the door. We give the code to the following form:
using UnityEngine; public class Button : MonoBehaviour { public GameObject door; void OnTriggerEnter(Collider other) { door.SetActive(false); transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); } void OnTriggerExit(Collider other) { door.SetActive(true); transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); } }
Select a button on the stage, drag the door from the hierarchy to the appeared field in the properties of the button's script. We check the code in action.

The arrival of a button on the button automati- cally dissolves the door and returns it to the place after the siding. But what is the use of a button that constantly turns off and locks the door for us? The hour of boxes has arrived! Copy the wall prefab, call it “Box”, add Rigidbody to it, don't forget to do the same operations as with Player, and then throw it onto the stage.

As you probably noticed, pushing the box is not very convenient. In addition, if it gets stuck in a corner of the room, then it will be impossible to get it. As an option, we can make teleporter zones around the corners of the room, which will move all the crates that fall into them, but this is a bit tricky. Add a magnet to PlayerController that will attract all nearby boxes. The
Input.GetButton function, unlike
Input.GetButtonDown, will return true as long as the requested button is pressed. What we need.
if (Input.GetButton("Jump"))
How are we going to find the boxes? There are many options, for example, we can attach another collider to the Player and register OnTriggerEnter or
OnTriggerStay , but then it will be necessary to solve the problem of early response of the button trigger. Remember
that matrix
link with different colliders? Exactly. In addition, the magnet should work only at the touch of a button, the rest of the time it is not needed. Therefore, we will manually check for collisions using
Physics.OverlapSphere .
Transform.position will give us the coordinates of the center of the bun. Look for objects nearby:
var big = Physics.OverlapSphere(transform.position, 2.1f);
Let's look for objects that practically relate to the bun.
var small = Physics.OverlapSphere(transform.position, 0.6f);
The two spheres obtained capture all the objects, including the walls and buttons. To weed out the excess, use the
labels , they will be useful to us more than once. Go to Edit -> Project Settings -> Tags and create labels for all occasions: “Box”, “Wall”, “Button”, “Door”. "Player" is already there. Select the prefabs and mark them with the drop-down list at the top of the inspector. Now we can weed out the boxes we need:
foreach (var body in big) if (System.Array.IndexOf(small, body) == -1 && body.tag == "Box") body.rigidbody.AddForce((transform.position - body.transform.position) * 20);
Found an object in a large area, checked its presence in a small area, checked the label, moved to itself. A bit of mathematics with vectors, nothing complicated for those who did not skip school.

The box still pushes us a little, let's not bother with it now, it still does not interfere with movement.
Attentive readers have long noticed one button bug. The attentive and diligent him already corrected. What is this bug?

If two objects fall into the field of the collider, and then one object goes about its business, then an extra button shutdown is triggered. What to do? We need to count the number of koloboks and boxes in the area of the collider and turn off the button when there is no one near and turn it on for any number of both. Unfortunately, there is no list of current collisions in Unity. Very sorry. Perhaps the developers have not yet reached this point. In any case, this is solved by a couple of lines of code. We can make our list and put all incoming objects into it, remove all outgoing ones, and change the state of the button in Update.
Bad option using UnityEngine; using System.Collections.Generic; public class Button : MonoBehaviour { public GameObject door; public bool pressed = false; private List<Collider> colliders = new List<Collider>(); void Update() { if (colliders.Count > 0 && !pressed) { door.SetActive(false); transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); pressed = true; } else if (colliders.Count == 0 && pressed) { door.SetActive(true); transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); pressed = false; } } void OnTriggerEnter(Collider other) { colliders.Add(other); } void OnTriggerExit(Collider other) { colliders.Remove(other); } }
Lertmind suggested using a counter instead of a list. This method is definitely more elegant, but the old code is left above for an example of how not to do it.
using UnityEngine; public class Button : MonoBehaviour { public GameObject door; private int colliderCount = 0; void OnTriggerEnter(Collider other) { if (colliderCount == 0) { door.SetActive(false); transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); } colliderCount++; } void OnTriggerExit(Collider other) { colliderCount--; if (colliderCount == 0) { door.SetActive(true); transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); } } }
Scenes, Particles and Shaders
We have a game character, walls, a button and a door, but nothing is hidden behind it. It's time to make a move between levels.
Duplicate the wall prefab, rename it to “Finish”, change the label to the eponymous one, turn the collider into a trigger. Create the material “Finish Mat” with inviting blue color and hang it on the finish.

Whole family to gather. But somehow it is not very tantalizing and too much like a wall. And at the door. And on the cube.
Shaders come to the rescue! Now we use an ordinary matte
diffuse shader for all materials. In the properties of the material we choose to finish
Transparent / Specular . This shader will take into account the alpha color and highlight the second color that we indicate. We put in a blue alpha in half, and make the reflection white. We are testing.
While the finish does not look very transparent, you need to somehow hint that it is ethereal. To do this, add to the finish a
system of particles that will float inside and entice the player. Component -> Effects -> Particle System. If you choose the finish on the stage, you can look at the simulation to make it easier to create the desired effect. First of all, we tick Prewarm, then particles in the game will appear in advance and will continue their uncomplicated life, and will not appear in front of the player. Start Lifetime by one. Start Speed will do less, for example 0.1. Start Size 0.1. Color expose the blue. On the Emission tab, we change the Rate to two hundred. On the Shape tab, set the Shape to Box, which will make the particles appear in the entire finish. Then we set the Random Direction checkbox so that the particles fly in different directions. Activate the Size over Lifetime tab, select an ascending line there. On the Randerer tab, we change the standard Renderer Mode to Mesh. Change Mesh to sphere. Done! Many, many small bubbles appear and disappear, and the finish now looks much more fun.

It remains to make the finish move the player to the next level. There are several useful functions and variables for managing scenes in Unity.
Application.loadedLevel will show us the current level,
Application.levelCount will show their number, and
Application.LoadLevel will load the desired one. In addition, we need to specify in Build Settings all the scenes in which we want to go. Create a new script “Finish”, hang it on the prefab and write the following inside:
using UnityEngine; public class Finish : MonoBehaviour { void OnTriggerEnter(Collider other) { if (other.tag == "Player") if (Application.loadedLevel + 1 != Application.levelCount) Application.LoadLevel(Application.loadedLevel + 1); else Application.LoadLevel(0); } }
We check that the player hit the finish line, and then move to the next or first level. We are tasting our new full-featured finish.
Aesthetics, dynamics and mechanics
Here is our prototype and ready. We can now press buttons, open doors and move from level to level. If we only want to test a new mechanic, then this is enough. But if we want to make a prototype of the game, then we need to think more about aesthetics and dynamics. Perhaps you have not often heard these terms as applied to games. In short, the mechanic is some kind of user interaction with the game world. The button that the user can press to open the door is one mechanic. Boxes that can also press buttons are different. Mechanics interact with each other and create the dynamics of the game. The player found the box, dragged it to the button, opened the door, moved to another level. Aesthetics - a feeling of the game. Have you ever felt in a shooter that you really pull the trigger? Pleasant feedback, animation, sound - all this affects the aesthetics of shooting. The aesthetics of the game as a whole is influenced by many factors, from download speed to plot. The painstaking work on aesthetics distinguishes one-day games from games that are remembered.
Let's look at our creation. The most commonly used mechanics - movement.
Let's take a close look at whether everything is in order. Open the code. var direction = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);
If you look closely, you can see that our vector is longer along the diagonals, which means that the applied force is also larger. You can fix this by normalizing the vector: var direction = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0).normalized;
And you can not fix it. Features of the game engine can also be part of aesthetics. Quake 3 without the jumps would be completely different. It is the knowledge of the subtleties of mechanics that distinguishes beginners from flying demons-killers of professional players. But subtleties should not harm convenience, which is why we previously changed Drag from Rigidbody player to four. Such friction causes the bun to stop quickly, but not immediately. A great acceleration gives a sense of control. Ideally, the start should also not take place immediately, it would be useful for accurate maneuvers. These small details of mechanics affect the overall aesthetics.We peer harder and notice that ... See? Not?
Compile the project and set the settings to a minimum. Great kolobok flies, right? Yes, so quickly that the colliders fly through. This is all due to the Update function in the control script that is executed in each frame. If the game is accelerated twice, then all forces are applied twice as often. To solve this problem, you can simply change the Update to FixedUpdate , which does not depend on the frame rate, but is updated by timer. If the Transform were not used for the movement, but Rigidbody, then it would be easier to unlink the movement from FPS using Time.deltaTime .The camera gives the player the opportunity to look into the game world, so it also depends a lot on its perspective and behavior. Now our camera just dangles a balloon over the main character, but if we tilt it a little, it will be easier for us to perceive the level, a sense of perspective will appear. Or we can change the camera on the contrary orthogonal, then everything will be completely flat.
The appearance of the game is also of great importance. By appearance, I mean not the number of polygons in the frame. Remember, games are art. What things are important in related arts? Stylistics, color, light, shadow. Try to find an interesting mix of colors here or collect your set here . Look for example at Proun. This game is made in the spirit of Suprematism, so close to Habra. You can try to copy some color solutions, but without textures and witchcraft with shaders, it will not be so beautiful. The impatient have already looked at the result of my attempt at the links at the top, and attentive readers can see them again at the end of the article.The essence of this section is as follows: even if you only make a prototype of the game, you should immediately think about what you want to say to the future player, what feelings to evoke in him. This is true for all types of art. Make a minimal sketch of the style is not difficult, and it will pay off first of all for you, it will be easier for you to choose a further direction.Conclusion
With the help of primitive cubes and a couple of drops of color, you can make quite a tolerable prototype, which (in my humble opinion) will be nice to play. Using Unity is not difficult at all, but we must not forget why you need all this.Links
Prototype from articleOnline versionCompiled version for Windows [ Mirror ] ~ 7.5 MBSources [ Mirror ] [ GitHub ] ~ 165 KBAdvanced prototypeOnline versionCompiled version for Windows [ Mirror ] ~ 7.5 MBSources [ Mirror ] [ GitHub ] ~ 325 KB