Dead Men Do Tell Tales

A few days ago I told you about my participation in the MermaladaJam 2. My first game jam. Responsible for a good backache and Dead men do tell tales.

A game made in 72 hours by a team of five people and which, one could say, is a marvel of beauty and playability. You can read more in the previous post.

Today I want to talk more technically about what I did exactly in the game on a technical level and what things I have learned along the hard road of 72 hours.

Technical Overview

To begin with, the obvious thing to say is that it is a game made in Unity (C#), the engine that I control the best right now.

Once the idea was down to earth and the first mechanics were clarified, I started working on an empty scene in which I created a plane to start supporting the rest of the objects.

The beggining of Dead men do tell tales
This is the beggining. Player. Npc’s. Boxes.

The main mechanics were the following:

  • A character controlled by the player. Simple. No jumping.
  • Interaction with different types of objects with the mouse.
    • “Empty” objects that had to sound
    • Objects “with coin”
    • NPCs that would give us conversation
    • Unlockable doors
  • Doors blocked and unlockable only with a certain amount of coins.
  • NPCs that wander around the room
  • A dialogue system
  • An oxygen system that runs out over time and kills the character when it reaches zero.
  • An information system: let’s call them tooltips, to indicate if the coin has been collected, if a door needs X coins, etc.

In addition to these main mechanics, we must also consider all the preparation, layout and logic of the user interface (UI).

Of that list, there were only three things that worried me in this order:

  • NPCs wandering (I had never done it in 3D).
  • A dialogue system (I wasn’t sure if there was a quick and efficient way to do it).
  • Clicking objects. Yes, it sounds stupid but I had worked very little (nothing) with methods like OnMouseDown(), OnMouseEnter(), OnMouseExit()…

Player Movement

The movement of the character is no mystery. I have done it in the simplest way with a Rigidbody and a Capsule Collider. Using

float horizontalInput = Input.GetAxis("Horizontal"); 
float verticalInput = Input.GetAxis("Vertical");

And some checks to launch the “swimming” sound and whether the game is paused or not so that the character cannot move. The pause game applied to both Pause and dialogues. As well as the oxygen restrictor.

For those kinds of things I have a GameManager as a singleton so that I can call it from any class. And like this two more, the SoundManager and the DialogueManager.

It should be said that the SoundManager was started in the Main Menu scene so I make it persistent between scenes, by reusing AudioSources and so that the music does not have cuts from one scene to another.

Clickable Objects

There is a common point in this game and that is that a large part of the scenery must be clickable to be able to interact. So a ClickableObject class came in handy. In it I manage two very important things:

  • the distance of that object with the player that I check with an interaction distance variable. In other words, if the character is within range, it allows him to interact. If not, no matter how much you click that object, nothing happens.
  • checking object labels. With them I distinguish the type of material (to launch different sounds), if it has a coin, if it is the chest at the end of the game, if it is a door…

Thanks to these checks, from that file I already derive to the GameManager, the SoundManager or execute the logic I need.

Doors

With the help of the ClickableObject and the DoorManager I manage the distance at which the player can interact with the door and how many coins are needed.

General Dead men do tell tales

It should be said that at first the door, once you had the necessary coins, would require a click to unlock it. In the end I removed the click and only with the interaction distance is enough and the door unlocks without further ado.

The doors are basically 4 elements. The door itself, a MeshRenderer with a space in the middle. Two particle systems, one that imitates a purple smoke and another blue particles, that make the effect of door blocked by evil curse and an invisible collider in the center of the space.

Once you get the necessary coins we deactivate the particles and the collider and voila you can now pass.

NPCs

Finally, having the NPCs move “autonomously” was not remotely the most complicated thing. Unity has the NavMesh components and it is as simple as adding NavMeshSurface to the ground and NavMeshAgent to each NPC.

With that and quite a few random variables such as the range to walk, the delay between stopping and walking again, etc. we had it:

void Update()
{
    _wanderTimer += Time.deltaTime;

    if (_wanderTimer >= wanderDelay && !isTooltipActive)
    {
        Vector3 newPos = RandomNavSphere(transform.position, wanderRadius, -1);
        _agent.SetDestination(newPos);
        _wanderTimer = 0;
        _agent.isStopped = false;
    }

    if (_agent.remainingDistance < 0.5f)
    {
        _agent.isStopped = true;
    }

    DetectPlayer();
}

Here there are also several checks because the movement is not complex but the NPC as such is. On the one hand, we also check the distance with the player so that the NPC stops moving. It would be ugly to try to talk to him and have him walk away ignoring us, wouldn’t it?

On the other hand, I had to hook directly into the dialogue so there are checks for what moment of the dialogue we are in, if it is the initial dialogue, the “I will only give you one clue” dialogue or the final dialogue in the case of the final NPC.

Dialogue System

Here I decided to create a ScriptableObject called “dialogue” and that it would contain an array to manage the lines or phrases of the conversation. It may be a bit repetitive but I think it is useful at least in this case. Let me explain.

For each line, you have to fill in whether the one speaking is a Ghost (npc) or the player. You have to put a name, an image and the phrase. Yes, we could save ourselves the name and image by putting them only once but at first I thought it could be useful for example to be able to choose a different image for each phrase to change the expressions. And hey, in the end it was useful and it looked good.

Dialogue Dead men do tell tales

In addition, I separate from this main conversation a conversation of a single line so that if you talk to the ghost again, it doesn’t give you the whole spiel and is just a phrase as a clue.

And another loose phrase that we call the final phrase. Only one character has it, yes, but I had 72 hours and I didn’t have time to polish the system.

Once the ScriptableObject of each NPC is generated, they are assigned to them and that’s it. With the checks of the NPC class and the DialogueManager we have a pretty cool dialogue system in the purest graphic adventure style.

Oxygen, the pressure on the player

Oxygen was something that we discarded as the main mechanic. But I insisted. It wasn’t exactly something complicated to do and all games need to be given a bit of pressure, difficulty… incentive. And dying is not bad. It allows the game to be replayed several times.

Managed directly from the GameManager with an Invoke to subtract each second that passes and stopping it with the same Pause variable that we also use to control whether the player can move or not. In this case it serves so that in both pause and dialogue the oxygen does not subtract.

void Start()
{
    inGameUI.UpdateCoinsDisplay();
    InvokeRepeating("DecreaseOxygen", 1.0f, 1.0f);
}

public void CollectCoin()
{
    currentCoins++;
    inGameUI.UpdateCoinsDisplay();
    TooltipManager.Instance.ShowTooltip("¡Has encontrado una moneda!", TooltipManager.TooltipType.Informational);
}

void DecreaseOxygen()
{
    if (!gameIsPaused)
    {
        inGameUI.DecreaseOxygen(1);
    }
}

And to reward the player, each time you find a coin it restores some oxygen.

Tooltips and User Interface

For the entire user interface I have used Unity UI Toolkit. The new way to create interfaces in Unity. I had no idea about this but in Flantastic Café Run I had to learn it and here I took the opportunity to get more comfortable with it. In terms of concept it is easy and even more so for me coming from web development. Everything is Flex.

It has its pros and cons. Although I think more pros directly. Although it is still a bit green. It is very powerful.

There is not much mystery here. Separating each screen/panel well with its UIDocument, a script to initialize buttons, events and values and logic from the GameManager, DialogueManager interconnected as needed.

For example, I created a message queue for the tooltips. Because when you pick up a coin, it tells you that the coin has been picked up and that the oxygen has been restored. If you also pick up a coin right away, you have two other messages. So I queued them so that they would come out one after the other.

    public void HideTooltip()
    {
        tooltipQueue.Clear();
        _tooltipContainer.style.display = DisplayStyle.None;
    }

    public void ShowTooltip(string message, TooltipType type = TooltipType.Informational)
    {
        tooltipQueue.Enqueue(message);
        _tooltipContainer.style.display = DisplayStyle.Flex;
        if (!isTooltipActive)
        {
            StartCoroutine(ProcessTooltipQueue());
        }
        currentTooltipType = type;
    }

    IEnumerator ProcessTooltipQueue()
    {
        isTooltipActive = true;

        while (tooltipQueue.Count > 0)
        {
            string message = tooltipQueue.Dequeue();
            _tooltipLabel.text = message;
            _tooltipContainer.style.display = DisplayStyle.Flex;
            PositionTooltip();
            yield return new WaitForSeconds(displayDuration);
            isTooltipActive = false;
            if (currentTooltipType == TooltipType.Informational)
            {
                _tooltipContainer.style.display = DisplayStyle.None;
            }
        }

        isTooltipActive = false;
    }

Conclusion

And I think that’s the most notable thing. Of course, there are many hours dedicated to it. A lot of testing to make sure the level is playable, you don’t get stuck, everything works as expected, etc.

There are a few more small logics implemented, such as:

  • The final chest that stops time, does a countdown of the coins, the ghost captain appears in front of the player, launches the last dialogue and you can skip to the credits.
  • A bubble spawner to give it an underwater touch. The bubbles are a particle system.
  • A spawner of little fish. Same model, different textures and instantiate it in different sizes.
  • For wooden, metal and glass objects that do not contain a coin, we play a random sound between several of the same material.
  • Camera in a specific position and following the character.
Top view Dead men do tell tales

A lot of work on the visual aspect to “imitate” or “pretend” that we are underwater. The bubbles, the fish, models of aquatic plants. But also a Global Volume with several post-processing effects such as vignette, bloom…

Lighting with blue color change, fog. Optimization of assets, statics, etc.

In short, a good deal of video game development in a weekend.

Have I learned things? Yes. A lot. To create a dialogue system. Mouse interactions. UI changes from code.

Have I improved? Yes. I have become more proficient in Unity UI Toolkit. I have created a more complete and autonomous sound system.

Was it worth it? Totally. Not only for working hand in hand with the team and knowing that I can trust them, but also because the result is a 90% game that serves as a portfolio for me.

Will I repeat it? Without a doubt. Not soon because it consumes a lot of time and effort, but in a few months I will repeat it.

From here on out there are 4 days left until the voting ends. We have received a lot of feedback on the game and there are several important aspects to improve in order to say that it is 100%. I am already working on those aspects and I want, even though it is a short experience, it to be “perfect”, so as soon as MermeladaJam 2 allows it, I will upload the changes for the final version.

Once I’m done with this, I’ll move on to what I was working on: Flantastic Cafe Run and Blender.

Remember to play the game here:

Goodbye!


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.