A Painterly Path

A PAINTERLY PATH

What is this?

In A Painterly Path you are put into the role of a lost wooden figure trying to find your lost artist. You look through the paintings of your creator and use his brushstrokes to move forward. The basic concept of the game is to create a fast-paced exploration of exciting and mysterious environments

    • Engine: Unity
    • Language: C#
    • Platform: PC
    • Development Time: 4 weeks 
    • Team Size: 5

    My Role

    The Splines

    My most notable tasks:

    Splines

    HUB


    Miscellaneous task:

    Puzzels

    Enemy

    Audio

    Help-Text

    The player is using splines to move from one platform to another. I made these splines as user friendly as possible for our level designer to easily form them and put them to use. A spline was made from several assembled cubes. Depending on how our level designer wanted to shape the splines, he could add cubes from a touch of a button. Points would then be generated on the spline to determine the shape of it. To create a curve, all we had to do is move a point.

      
      
      
      public void AddCurve()
      {
          Vector3 point = points[points.Length - 1];
          Array.Resize(ref points, points.Length + 3);
          point.x += 1f;
          points[points.Length - 3] = point;
          point.x += 1f;
          points[points.Length - 2] = point;
          point.x += 1f;
          points[points.Length - 1] = point;
          Array.Resize(ref modes, modes.Length + 1);
          modes[modes.Length - 1] = modes[modes.Length - 2];
          EnforceMode(points.Length - 4);
       
          if (loop)
          {
              points[points.Length - 1] = points[0];
              modes[modes.Length - 1] = modes[0];
              EnforceMode(0);
          }
      }
      
          

      When the player enters a spline, the transform of the player is being moved towards a direction. The method Start Walking made sure the movement of the player in the spline where smooth and in the right direction. 

       

      To make it easier for a player to attach itself to a spline midair, a lerp is added when the player is near the spline and pressing the activation button.

        
        
        public void StartWalking(float position, GameObject currentSpline)
        {
            if (walkTimer > walkEnterDelay)
            {
                spline = currentSpline.GetComponent();
                progress = position;
         
                input.walking = true;
                lerping = true;
                lerpDistance = 0f;
                PlayerStartPosition = transform.position;
                playerController.jumped = false;
                ps.fadeSplineSound = false;
                input.walkTimer = 0f;
                walking = true;
         
            }
        }
        
            

        
        
        private void OnTriggerEnter(Collider other)
            {
                 
                if (other.CompareTag("Player"))
                {
                    if(input == null)
                    {
                        input = other.GetComponent();
                    }
                    if (!input.walking && Input.GetAxis("SplineAction") > 0f)
                    {
                        input.StartWalking(position, transform.parent.gameObject);
                        if (transform.gameObject.GetComponentInParent () != null) {
                            transform.gameObject.GetComponentInParent ().StartWalking (1);
                        }
                    }
                }
            }
        
            

        The HUB

        The HUB is the Game Manager. All data the player has is saved throw-out all three levels. The player jumps into a painting to enter each level.

         

        The first level is on the righthanded side; the player won’t be able to pass through any other levels if they haven’t unlocked them. After completing level one a spline appears so the player can reach the second level.

        When the player has completed both levels, the last painting will open and take the player to the last level.

          A visual loading screen appears when the player enters a new level. The Loading Screen Manager handles the progress bar depending on how far the generation of the next level is. I used an asynchronous operation, so our game will continue to operate even though it’s loading. 

            
            
            using UnityEngine;
            using UnityEngine.UI;
            using System.Collections;
            using UnityEngine.SceneManagement;
            using TMPro;
            
            public class LoadingScreenManager : MonoBehaviour
            {
            
                [Header("Loading Visuals")]
                public TextMeshProUGUI loadingText;
                public Image progressBar;
                public Image fadeOverlay;
            
                [Header("Timing Settings")]
                public float waitOnLoadEnd = 0.25f;
                public float fadeDuration = 0.25f;
            
                [Header("Loading Settings")]
                public LoadSceneMode loadSceneMode = LoadSceneMode.Single;
                public ThreadPriority loadThreadPriority;
            
                [Header("Sound")]
                AsyncOperation operation;
                Scene currentScene;
            
            
                public static int sceneToLoad = -1;
               
                static int loadingSceneIndex = 2; 
            
                public static void LoadScene(int levelNumber)
                {
                   sceneToLoad = levelNumber;
                   SceneManager.LoadScene(loadingSceneIndex);
                }
            
                void Start()
                {
            
                    if (sceneToLoad < 0)
                        return;
            
                    fadeOverlay.gameObject.SetActive(true);
                    currentScene = SceneManager.GetActiveScene();
                    StartCoroutine(LevelCoroutine(sceneToLoad));
                }
            
            
                IEnumerator LevelCoroutine(int SceneNumber)
                {
              
                    AsyncOperation async = SceneManager.LoadSceneAsync(SceneNumber);
                    async.allowSceneActivation = false;
                    FadeIn();
                    while (!async.isDone)
                    {
                        progressBar.fillAmount = async.progress / 0.9f; 
                        loadingText.text = Mathf.Round(progressBar.fillAmount * 100)  + "%"; 
            
                        if(progressBar.fillAmount >= 0.5f)
                        {
                            async.allowSceneActivation = true;
                            yield return 0;
                        }
                        yield return 0;
            
                        float t = 0;
                        while (progressBar.fillAmount < 1)
                        {
                            t += Time.deltaTime;
                            progressBar.fillAmount = t / 5;
                            loadingText.text = Mathf.Round(progressBar.fillAmount * 100) + "%";
                            Debug.Log(t);
                            yield return null;
                            
                        }
                        progressBar.fillAmount = 1;
                        FadeOut();          
                    }
            
            
                    yield return null;
                }
            
                void FadeIn()
                {
                    fadeOverlay.CrossFadeAlpha(0, fadeDuration, true);
                    print("fade in");
                }
            
                void FadeOut()
                {
                    fadeOverlay.CrossFadeAlpha(1, fadeDuration, true);
                    print("fade out");
                }
            }
            
                

            Miscellaneous

            When something new is being introduced a help-text will appear on the screen.

            To be able to continue the path (though the green door on the image below), the player has to use the splines to destroy the red obstacle for it to open.

            The Process

            This was the first bigger game project I made. This was a school project and our task were first to create a 2D version of the game and then make it into a 3D game – that also would be our finished product.

            Challenges

            This was very challenging, our group decided to create a game where the player could attach itself onto splines to get to a new platform. I was in charge of the splines and at that time I had never herd of a Bezier Curve. After some research I realized that this curve was the best way of doing it. It was very challenging to make it, I read a lot of articles to understand the subject. I also felt pressured, because our teachers told us the splines must be functionally done after a week, if not we have to come up with a new concept.

             

            Another challenge was the editor scripting. This was something I had never done before, instead of learning the basic I had to dig deep to manage to create an editor for the splines.

            Iteration

            When I finally managed to make the splines work, I realized that I had made it very complicated for our level designer to use. This is when I realized I had to make some kind of editor scripting.

             

            As a first project, with only a year of programming experience, I definitely took a task that was way over my head. But this is what the team (including me) wanted and I really didn’t want to let them down. So, I worked a lot to make sure we had a working spline in the end.

             

            After looking at my code, a couple of years later, it feels good to see how I’ve improved. Now I like to make editor scripting and I would never create a Bezier curve the way I did it in this project. During this project it was a mess, but it worked and that was all than mattered!