Amadiro - ludum dare 46 Post Mortem




I was waiting for ludum dare #46 since few months now. Ludum Dare #44 was my first ludum dare ever and I remember having a good time making a little game called 'Billy The Man' (you can play it here). 

The lockdown, caused by the corona virus situation,  presented me with a great opportunity: a weekend for making a game (48h really as I partecipated in the compo). Judging by the number of submissions (highest since ever) I wasn't the only one thinking that.

.. and this is how pretty much all went.

FRIDAY 

I started to prepare and setup the project with basic stuffs I knew I would need anyway, like a basic template for menu, few packages from unity (like proGrid and few others) and the great voxel importer tool I always use for my voxel-based games.  I spent some time to prepare a sample scene with basic camera position. And that was it. I went to bed at around 2PM, in 1h the theme would have been announced. 

SATURDAY

Woke up around 9AM and after discovering that the theme was "Keep it alive" the first thought in my head was: "ok for once I like the theme" (as usually ludum dare themes aren't that easy to digest). So I started to think of various alternatives: one of those was to do something with a fire maybe with a low poly style (with art made in Blender). After thinking a bit more I scratched this initial idea in favour of something more challenging for me: make a sort of infinite scroller casual game with voxel art.  The inspiration for the game was "QBert" an arcade game made back in 1982:


and most recent "Down the Mountain", a mobile game I recently played.

With that in mind I started to think about gameplay. First problem to solve was with blocks arrangement. 


I did few trials in the unity editor playing with some placeholder graphics (just boxes) trying to figure out the camera settings and the position for the cubes. I ended up making 1 prefab containing the positions (10 blocks in total) representing the position where to spawn each of the cube. I decided to go with 10 because every two lines of cubes the pattern repeat itself. I just needed to create 1 prefab containing 2 lines of cubes with 10 positions, then I knew that I could use that prefab to spawn new blocks when needed a simple +1 on y-axis should have been enough to spawn the blocks in the right position (for x-z-axis I needed to store the "last" spawned block position).

So up to now I could spawn blocks in the right positions:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlocksGenerator : MonoBehaviour
{
    public GameObject BlocksPrefab;
    public Vector3 InitialBlocksPoisition;
    private Vector3 previousBlockPosition;
    private Vector3 increment;
    // Start is called before the first frame update
    void Start()
    {
        BlocksDestroyer.OnBlocksDestroyed += GenerateNextBlock;
        previousBlockPosition = InitialBlocksPoisition;
        increment = new Vector3(1.0f, -2.0f, 1.0f);
        BulkGeneration(40);
    }
    private void OnDestroy()
    {
        BlocksDestroyer.OnBlocksDestroyed -= GenerateNextBlock;
    }
    private void BulkGeneration(int numberOfBlocks)
    {
        for (int i = 0; i < numberOfBlocks; i++)
        {
            GenerateNextBlock();
        }
    }
    private void GenerateNextBlock()
    {
        Vector3 blockPosition = previousBlockPosition + increment;
        Instantiate(BlocksPrefab, blockPosition, Quaternion.identity, this.transform);
        previousBlockPosition = blockPosition;
    }
}
 

Next problem to solve:  when we should spawn the next set of blocks? To answer this question I needed to makes things start moving so I went on trying to move all the blocks in a time-based fashion. It turns out was a bit of messy solution and bit complicated so I ended up just moving the camera :) . Here is the simple script:

public class CameraMovement : MonoBehaviour
{
    public float Speed = 1.0f;
    // Start is called before the first frame update
    void Start()
    {
        
    }
    // Update is called once per frame
    void Update()
    {
        transform.Translate(-Vector3.up * Speed * Time.deltaTime);
    }
}

So now the camera moves but I had no player :D . Time to start placing another box in the mix.  

I decided to work on the player  just after lunch.


The grey box there represents my player now and I need to move it in 2 directions: right and left. 

For this it seemed very logical and simple to just use the Unity animator in order to animate those 2 basic movements. I recorded two simple animations: one to go down on the left and one to go down on the right:


 

The curve editor was very helpful in obtaining a smooth animation. After that I attached a simple script to the player in order to trigger the event to move it with the animator (with root motion flag activated on the player's animator).

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
    private Animator animator;
    // Start is called before the first frame update
    void Start()
    {
        animator = GetComponent<animator>();
    }
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.LeftArrow))
        {
            animator.SetTrigger("MoveLeft");
        }
        else if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            animator.SetTrigger("MoveRight");
        }
    }
}

As you can see pretty basic stuffs until now but we already have something that resemble a gameplay: 
- environment (check!)
- camera movement (check!)
- player movement (check!)

When I pressed play I was actually going down with my player and that's a bit of rewarding as you feel excited by the progress. It is Saturday's afternoon and I am pretty satisfied with the result so far knowing that the next day I could have time to make art, sounds music and work on the gameplay with the "good graphic" (and other million things that you have in mind when you do ludum dare ). 

I had dinner, watched a little bit of tv and worked on the game trying to finalize the prefabs: adding colliders, triggers, rigidbodies where needed and  flashing out the list of different type of blocks I would have needed for the next day.

It is around 1AM Sunday morning and I go to bed.  

SUNDAY

After a good night sleep I woke up in a good mood quite early around 8:30AM. I decided to start the day with creating music. I opened up Bosca Ceoil and start putting together a "konami-style" kind of melody with a touch of bass sound base. I was pretty happy with the result even if it was a simple track made during breakfast.  I decided to keep it and go back to Bosca later on to create an "intro" music for the menu.

It is around 11AM and it's time to create art. MagicaVoxel is my tool for that, I have been practicing with it since more than a year now. I wanted the player to be a "cute" robot so I googled for some reference images and I end up making this:


Imported in Unity and updated my player prefab. 

With the player in place I needed to start modelling the various blocks 


Modelling the blocks with MagicaVoxel was pretty easy and very fast so now I have few different types of blocks and I just need to make a prefab out of each one. A block manager attached to the main transform will manage block generation:


I decided to have different types of collections (Deadly blocks, normal blocks and collectables) so that i can easily add a new block in the mix just adding it in the array. The spawning process is just regulated by some probability and some checks:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlocksManager : MonoBehaviour
{
    public GameObject[] Blocks;
    public GameObject[] DeadlyBlocks;
    public bool countDeadlyBlocks = false;
    public bool putDeadlyBlocks = false;
    private int numberOfChildPositions;
    private void Awake()
    {
        numberOfChildPositions = countDeadlyBlocks ? transform.childCount - 2 : transform.childCount;
        InitBlocks();
    }
    private void InitBlocks()
    {
        for (int i = 0; i < numberOfChildPositions; i++)
        {
            Vector3 position = transform.GetChild(i).position;
            GenerateBlock(position);
        }
    }
    private void GenerateBlock(Vector3 position)
    {
        if (putDeadlyBlocks && Random.Range(1, 6) == 2)
        {
            Instantiate(DeadlyBlocks[Random.Range(0, DeadlyBlocks.Length)], position, Quaternion.identity, this.transform);
        }
        else
        {
            Instantiate(Blocks[Random.Range(0, Blocks.Length)], position, Quaternion.identity, this.transform);
        }
    }
} 

Pretty basic, filled with bugs but hey! it works more or less... I ended up refining this logic later on.

Lunch break, cooked by my lovely lady.. ... coffee... and at about 4PM in the afternoon I am back on my desktop.

So... were where we? oh yes,  blocks are spawned but when they get removed? Well, the logical and easiest thing to do was to attach a 'BlockDestroyer' to the camera with a simple script and that's exactly what I did.

 

 As you can see the green box is the box collider attached to the camera rig (white box in the picture) that is moving down: anything colliding with it will be destroyed, including the player  so I got the player's dead detection for free using this method.  

With this part done I went on finishing up the gameplay adding the "game manager" responsible to receive events like gamestart gamepause player dead, ecc.. 

With the game manager in place I focused on getting the menu up to speed. Using the template prepared on Friday I was able with a few clicks to arrange buttons, changes colours and fonts to match the game style and even adding music.

The game was getting a shape and now the feature creep was hitting me in the face (Feature creep , also known as featuritis, is tendency to want to add more and more features into the game as development goes on). I had to do a step back and, instead of adding more features that would have result in more bugs to fix I spent 30 minutes or so playtesting the game trying also to adjust the difficulty. I spent 10 minutes making a quick music intro for the menu and it was dinner time.

Nearly 7 hours to the deadline but I am not scared or anxious: I know I have a playable game with decent graphics and more important I am proud of what I achieved so far. I am also ok thinking that I will be spending next 5 hours making builds, uploading everything on itch.io and submitting the game on ludum dare page. In fact Sunday night went like that, I created a webGL build, fixed last minutes bugs and uploading everything on the website. 

At 11:30PM I went to bed tired but satisfied. 


So, now for you that read this post, I hope I managed to pass onto you some of the excitement you get when you do something you really like. Ludum dare is maybe the best way to try out your ideas. If you are into gamedev I really recommend you to join the event. Gamedev is hard, it can be overwhelming but also very rewarding. 

If you want to give it a try you can play the game here: https://danidesa.itch.io/amadiro

Ludum dare game page here: https://ldjam.com/events/ludum-dare/46/amadiro  

Ps: let me know in the comments what you think about the game and about this post. Cheers! And see you on next ludum dare!

Files

Amadiro (ver 1.2.3) Play in browser
Apr 21, 2020

Leave a comment

Log in with itch.io to leave a comment.