{"author_link":"\/users\/geckoo1337","author_name":"Geckoo1337","author_uid":"geckoo1337","comments":[],"epoch":1713803147,"event":"LD55","format":"md","ldjam_node_id":394506,"likes":4,"metadata":{"p_key":"206563","p_author":"Geckoo1337","p_authorkey":"1000409","p_urlkey":"443097","p_title":"VCDG dev-log","p_cat":"LDJam ","p_event":"LD55","p_time":"1713803147","p_likes":"4","p_comments":"0","p_status":"WAYBACK","us_key":"1000409","us_name":"Geckoo1337","us_username":"geckoo1337","event_start":"1712966400","event_key":"120","event_name":"Ludum Dare 55"},"node":{"_collation":{"body_sanitizer":"TextUtils::SanitizeHTML via existing importer","event":"LD55","removed_author":false},"_superparent":383062,"_trust":15,"author":409,"body":"**Virtual Church Of The Digital Goddess** is my entry for this 55th LudumDare session. When I discovered the chosen theme (Summoning) I was in an uncomfortable situation. By religious conviction I cannot play with black magical art, evil spirits and all things more or less similar. I have immediately understood that a big amount of entries could play with sorcery - and LD members did. I respect this choice, but I tried to create something different without evil invocation, black sabbath, devil, etc. This is the first time in my indie dev life that I encountered this constraint. So I struggled with a new challenge. Let's go!\nI decided to create (another) puzzle game - my favorite games' genre, but adding a new way. I asked myself - is there a way to create a game only with a mathematical core, no rigidbody, no collider, etc? So VCDG is an experimentation into solid math processes. Maybe for this reason, the cubes and their movements seem so smooth, perfectly aligned on edges. To do that I use this code :\n\n```\n\/\/ smooth rotation\nIEnumerator<float> Tumble(GameObject thisOne, Vector3 direction, Vector3 endPosition, Quaternion endRotation, Vector3 pivot, Vector3 rotAxis, float rotSpeed, bool ret)\n{\n    float t = 0.0f;\n\n    if (loader.GetComponent<Loader>()._move != null && loader != null)\n    {\n        if (Time.timeScale > 1.5f)\n            _loader.playSound(_loader._move, this.transform, true, 0.21f, 0.41f, _loader._move.length, false);\n        else\n            _loader.playSound(_loader._move, this.transform, true, 0.31f, UnityEngine.Random.Range(0.71f, 0.92f), _loader._move.length, false);\n    }\n    \/\/ rotate player cube\n    while (t < tumblingDuration)\n    {\n        isTumbling = true; \/\/ unreachable\n        t += Time.deltaTime;\n        \/\/ tip to avoid tiny glitch\n        if (Mathf.Abs(t - tumblingDuration) < tumblingDuration \/ 30)\n            t += 1.0f; \/\/ out of the loop right now\n\n        thisOne.transform.RotateAround(pivot, rotAxis, rotSpeed * Time.deltaTime);\n        yield return Timing.WaitForOneFrame;\n    }\n    \/\/ clean position and rotation\n    thisOne.transform.position = endPosition;\n    thisOne.transform.rotation = endRotation;\n    \/\/ store new position so as to check forbidden face\n    storedPos = endPosition - shift;\n    \/\/ don't store the return movement in back in time mode!!!\n    if (!ret)\n        addNewMov(direction, thisOne);\n    \/\/ check if the last movement is available\n    checkState(thisOne, direction);\n    \/\/ done\n    isTumbling = false;\n}\n```\n\nAlso there are a lot of constraints in the game, especially about colors and position\/rotation. This part was really complicated - and I struggled hard against some non-understandable bugs. Finally I have this code which seems to me relevant and clever :\n\n```\n\/\/ check new position and rotation\nprivate void checkState(GameObject thisOne, Vector3 direction)\n{   \/\/ 1\u00b0 check if a new step is available\n    if (!loader.GetComponent<Loader>().giveMeNumber(loader.GetComponent<Loader>().pattern, listMov.Count))\n    {\n        computeMove(-direction, thisOne, true, true);\n        \/\/ play a sound\n        if (_loader._noMoreStep != null && loader != null)\n            _loader.playSound(_loader._noMoreStep, this.transform, true, 0.4f, 0.95f, _loader._noMoreStep.length, false);\n        \/\/ get out of there\n        return;\n    }\n    \/\/ 2\u00b0 check cube face\n    if (listPos.Contains(thisOne.transform.position + new Vector3(1, 0, 0)) || \n        listPos.Contains(thisOne.transform.position - new Vector3(1, 0, 0)) ||\n        listPos.Contains(thisOne.transform.position + new Vector3(0, 0, 1)) || \n        listPos.Contains(thisOne.transform.position - new Vector3(0, 0, 1)))\n    {\n        foreach (GameObject g in cc)\n        {   \/\/ excludes cube player which are not in our radius\n            if (g != thisOne && Vector3.Distance(g.transform.position, thisOne.transform.position) < 1.1f)\n            {\n                var d = (thisOne.transform.position - g.transform.position).normalized;\n                \/\/ check if cube touching have the same color on their face\n                var isX_A = Mathf.Abs(Vector3.Dot(d, thisOne.transform.right)) > 0.999f;\n                var isY_A = Mathf.Abs(Vector3.Dot(d, thisOne.transform.up)) > 0.999f;\n                var isZ_A = Mathf.Abs(Vector3.Dot(d, thisOne.transform.forward)) > 0.999f;\n\n                var isX_B = Mathf.Abs(Vector3.Dot(d, g.transform.right)) > 0.999f;\n                var isY_B = Mathf.Abs(Vector3.Dot(d, g.transform.up)) > 0.999f;\n                var isZ_B = Mathf.Abs(Vector3.Dot(d, g.transform.forward)) > 0.999f;\n\n                bool sameColor = isX_A == isX_B && isY_A == isY_B && isZ_A == isZ_B;\n                \/\/ this movement is finally forbidden\n                if (!sameColor)\n                {\n                    computeMove(-direction, thisOne, true, true); \/\/ back step\n                    \/\/ get out of there\n                    return;\n                }\n            }\n        }\n    }\n    \/\/ 3\u00b0 check colored tiles\n    if ((Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 180) ||\n             (Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 0))\n    {   \/\/ red tile on ground\n        if (greenTilesPos.Contains(storedPos) || blueTilesPos.Contains(storedPos) || greenGoal.Contains(storedPos) || blueGoal.Contains(storedPos))\n            computeMove(-direction, thisOne, true, true);\n        else if (listBlank.Contains(storedPos))\n            blankTileProcess(1, storedPos);\n        else if (redGoal.Contains(storedPos))\n            levelSolved(thisOne);\n        \/\/ get out of there\n        return;\n    }\n    else if ((Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 270) ||\n             (Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 90))\n    {   \/\/ green tile on ground\n        if (redTilesPos.Contains(storedPos) || blueTilesPos.Contains(storedPos) || redGoal.Contains(storedPos) || blueGoal.Contains(storedPos))\n            computeMove(-direction, thisOne, true, true);\n        else if (listBlank.Contains(storedPos))         \n            blankTileProcess(2, storedPos);\n        else if (greenGoal.Contains(storedPos))\n            levelSolved(thisOne);\n        \/\/ get out of there\n        return;\n    }\n    else if ((Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 270 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 0) ||\n             (Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 90 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 0))\n    {   \/\/ blue tile on ground\n        if (redTilesPos.Contains(storedPos) || greenTilesPos.Contains(storedPos) || redGoal.Contains(storedPos) || greenGoal.Contains(storedPos))\n            computeMove(-direction, thisOne, true, true);\n        else if (listBlank.Contains(storedPos))\n            blankTileProcess(3, storedPos);\n        else if (blueGoal.Contains(storedPos))\n            levelSolved(thisOne);\n        \/\/ get out of there\n        return;\n    }\n}\n\/\/ transform blank tile\nvoid blankTileProcess(int color, Vector3 vPos) \n{\n    int i = listBlank.IndexOf(vPos);\n    \/\/ UnityEngine.Debug.Log(listBlank.IndexOf(vPos) + \" -> \" + vPos);\n    \/\/ colored blank tile with a new color\n    if (Time.timeScale < 1.5f)\n    {\n        if (stampBlank[i] == 0)\n        {   \/\/ is this tile blank?\n            stampBlank[i] = listMov.Count;\n            obj.GetComponent<LevelConstructor>().blankTiles.transform.GetChild(i).GetComponent<Renderer>().sharedMaterial = obj.GetComponent<LevelConstructor>()._materials[color];\n            \/\/ play a sound\n            if (loader.GetComponent<Loader>()._paint != null && loader != null)\n                _loader.playSound(_loader._paint, this.transform, true, 0.5f, 0.9f, _loader._paint.length, false);\n            \/\/ ... or you can use item selection instead of an integer\n            if (color == 1)\n                redTilesPos.Add(vPos);\n            else if (color == 2)\n                greenTilesPos.Add(vPos);\n            else if (color == 3)\n                blueTilesPos.Add(vPos);\n            else\n                return;\n        }\n    }\n}\n```\n\nTo get an inverted clockwise system, allowing the player to come back in time, I used a movement list according to the object and its direction. So the number of steps which are available is computed according to the **List.Count** integer. We got something like that :\n\n```\n\/\/ all movement have been saved so as to allow back in time\nvoid backInTime()\n{\n    while (listMov.Last().artefact != cc[index])\n        splitCube();\n\n    if (listBlank.Contains(cc[index].transform.position - shift))\n    {\n        int i = listBlank.IndexOf(cc[index].transform.position - shift);\n        \/\/ disable a blank tile which has been colored before\n        if (stampBlank[i] == listMov.Count)\n        {\n            obj.GetComponent<LevelConstructor>().blankTiles.transform.GetChild(i).GetComponent<Renderer>().sharedMaterial = obj.GetComponent<LevelConstructor>()._materials[7];\n            stampBlank[i] = 0;\n            \/\/ new blank status\n            redTilesPos.Remove(cc[index].transform.position - shift);\n            greenTilesPos.Remove(cc[index].transform.position - shift);\n            blueTilesPos.Remove(cc[index].transform.position - shift);\n        }\n    }\n    \/\/ invert movement\n    computeMove(-listMov.Last().movement, listMov.Last().artefact, false, true);\n    deleteLast();\n}\n```\n\nMy favorite feature is the rounded blank tile which the player paints when a cube walks on it. This is not an easy feature because of one point - you can back in time. So its status must change too. So I implemented a tag on them in order to \"clean up\" tiles when retrograded cube passes again on a previous tile which has been colored. This tag is just an integer - the step when the tile has been colored. Take a look at the end of the first script - the **blankTileProcess()** function.  In the previous script, you got the code which compute the state of the tile during the right moment. \n\nSome parts of these codes could be improved. As an a single example, I use **Time.timeScale** like a tag - and I don't like it. There are too many **GetComponent** occurences in these scripts - and it's not the best way to get a hand on an object. So I have many to do :wink:  \nFor the FX I used some shaders and this project is my first one using the Unity URP system. I tested this option, but I guess that a legacy system could be \"enough\". I am developing more levels in order to release a full game with 50 levels. I have only one scene for them, and when you see a black screen, it's just a short time to erase the previous level, creating the next... Also I am proud of my level selection system. before I created a button for each level. Now I have only one script to generate how many buttons are required. This script is really useful. Then I share it with you.\n\n```\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UI;\n\npublic class DisplayButtons : MonoBehaviour\n{\n    public Transform button;\n    public int buttonOnX = 10;\n    public int buttonOnY = 5;\n\n    Vector3 nextPos = Vector3.zero;\n    public int spacingX = 80;\n    public int spacingY = 80;\n\n    Vector3 shift;\n    int id = 0;\n\n    void Start()\n    {\n        displayThem();\n    }\n\n    public void displayThem()\n    {\n        nextPos.y = 0;\n        id = 0;\n        shift = new Vector3(((buttonOnX * spacingX)\/2) - (spacingX\/2), (-(spacingY * buttonOnY)\/2) - (spacingY\/2), 0);\n\n        for (int i = 0; i < buttonOnY; i++)\n        {\n            nextPos.y -= spacingY;\n            nextPos.x = 0;\n\n            for (int j = 0; j < buttonOnX; j++)\n            {\n                id++;\n                RectTransform myButton = Instantiate(button as RectTransform);\n\n                myButton.GetComponentInChildren<Text>().text = (id).ToString();\n                myButton.GetComponentInChildren<Text>().fontSize = 40;\n                myButton.GetComponentInChildren<Text>().color = new Color(0.2f, 0.22f, 0.5f, 0.9f);\n                myButton.GetComponentInChildren<Image>().color = new Color(0.7f, 0.5f, 0.8f, 0.9f);\n                myButton.SetParent(transform, false);\n                myButton.localPosition = nextPos - shift;\n\n                nextPos.x += spacingX;\n                int level = PlayerPrefs.GetInt(\"Level\", 1);\n                \n                if (id > level && !Application.isEditor)\n                {\n                    myButton.GetComponentInChildren<Button>().interactable = false; \/\/ disables button for scene that isn't available\n                    myButton.GetComponentInChildren<Text>().color = new Color(0.2f, 0.22f, 0.5f, 0.45f);\n                }\n            }    \n        }\n    }\n}\n```\n\nI exposed some parts of my project, just sharing with you some keys. I guess that it could be improved - and with more time, I will do it. If you want to test the result, the game is available on Itch.io for free. Play, leave a comment - and this way, I can find your own project which I wanna play. Thank you for your support everyone. I wish you the best ++ \n\nhttps:\/\/ldjam.com\/events\/ludum-dare\/55\/virtual-church-of-the-digital-goddess\n\n![VCDG.jpeg](\/\/\/raw\/991\/z\/64afc.jpg)","comments":0,"created":"2024-04-22T16:19:47Z","files":[],"files-timestamp":0,"id":394506,"love":4,"love-timestamp":"2024-04-23T11:03:10Z","meta":[],"modified":"2024-04-23T11:03:10Z","name":"VCDG dev-log","node-timestamp":"2024-04-22T16:59:13Z","parent":383204,"parents":[1,5,9,383062,383204],"path":"\/events\/ludum-dare\/55\/virtual-church-of-the-digital-goddess\/vcdg-dev-log","published":"2024-04-22T16:25:47Z","scope":"public","slug":"vcdg-dev-log","subsubtype":"","subtype":"","type":"post","version":1235859},"node_metadata":{"n_key":"394506","n_urlkey":"443097","n_parent":"383204","n_path":"\/events\/ludum-dare\/55\/virtual-church-of-the-digital-goddess\/vcdg-dev-log","n_slug":"vcdg-dev-log","n_type":"post","n_subtype":"","n_subsubtype":"","n_author":"409","n_created":"1713802787","n_modified":"1713870190","n_version":"1235859","n_status":"WAYBACK"},"source_url":"https:\/\/ldjam.com\/events\/ludum-dare\/55\/virtual-church-of-the-digital-goddess\/vcdg-dev-log","text":"**Virtual Church Of The Digital Goddess** is my entry for this 55th LudumDare session. When I discovered the chosen theme (Summoning) I was in an uncomfortable situation. By religious conviction I cannot play with black magical art, evil spirits and all things more or less similar. I have immediately understood that a big amount of entries could play with sorcery - and LD members did. I respect this choice, but I tried to create something different without evil invocation, black sabbath, devil, etc. This is the first time in my indie dev life that I encountered this constraint. So I struggled with a new challenge. Let's go!\nI decided to create (another) puzzle game - my favorite games' genre, but adding a new way. I asked myself - is there a way to create a game only with a mathematical core, no rigidbody, no collider, etc? So VCDG is an experimentation into solid math processes. Maybe for this reason, the cubes and their movements seem so smooth, perfectly aligned on edges. To do that I use this code :\n\n```\n\/\/ smooth rotation\nIEnumerator<float> Tumble(GameObject thisOne, Vector3 direction, Vector3 endPosition, Quaternion endRotation, Vector3 pivot, Vector3 rotAxis, float rotSpeed, bool ret)\n{\n    float t = 0.0f;\n\n    if (loader.GetComponent<Loader>()._move != null && loader != null)\n    {\n        if (Time.timeScale > 1.5f)\n            _loader.playSound(_loader._move, this.transform, true, 0.21f, 0.41f, _loader._move.length, false);\n        else\n            _loader.playSound(_loader._move, this.transform, true, 0.31f, UnityEngine.Random.Range(0.71f, 0.92f), _loader._move.length, false);\n    }\n    \/\/ rotate player cube\n    while (t < tumblingDuration)\n    {\n        isTumbling = true; \/\/ unreachable\n        t += Time.deltaTime;\n        \/\/ tip to avoid tiny glitch\n        if (Mathf.Abs(t - tumblingDuration) < tumblingDuration \/ 30)\n            t += 1.0f; \/\/ out of the loop right now\n\n        thisOne.transform.RotateAround(pivot, rotAxis, rotSpeed * Time.deltaTime);\n        yield return Timing.WaitForOneFrame;\n    }\n    \/\/ clean position and rotation\n    thisOne.transform.position = endPosition;\n    thisOne.transform.rotation = endRotation;\n    \/\/ store new position so as to check forbidden face\n    storedPos = endPosition - shift;\n    \/\/ don't store the return movement in back in time mode!!!\n    if (!ret)\n        addNewMov(direction, thisOne);\n    \/\/ check if the last movement is available\n    checkState(thisOne, direction);\n    \/\/ done\n    isTumbling = false;\n}\n```\n\nAlso there are a lot of constraints in the game, especially about colors and position\/rotation. This part was really complicated - and I struggled hard against some non-understandable bugs. Finally I have this code which seems to me relevant and clever :\n\n```\n\/\/ check new position and rotation\nprivate void checkState(GameObject thisOne, Vector3 direction)\n{   \/\/ 1\u00b0 check if a new step is available\n    if (!loader.GetComponent<Loader>().giveMeNumber(loader.GetComponent<Loader>().pattern, listMov.Count))\n    {\n        computeMove(-direction, thisOne, true, true);\n        \/\/ play a sound\n        if (_loader._noMoreStep != null && loader != null)\n            _loader.playSound(_loader._noMoreStep, this.transform, true, 0.4f, 0.95f, _loader._noMoreStep.length, false);\n        \/\/ get out of there\n        return;\n    }\n    \/\/ 2\u00b0 check cube face\n    if (listPos.Contains(thisOne.transform.position + new Vector3(1, 0, 0)) || \n        listPos.Contains(thisOne.transform.position - new Vector3(1, 0, 0)) ||\n        listPos.Contains(thisOne.transform.position + new Vector3(0, 0, 1)) || \n        listPos.Contains(thisOne.transform.position - new Vector3(0, 0, 1)))\n    {\n        foreach (GameObject g in cc)\n        {   \/\/ excludes cube player which are not in our radius\n            if (g != thisOne && Vector3.Distance(g.transform.position, thisOne.transform.position) < 1.1f)\n            {\n                var d = (thisOne.transform.position - g.transform.position).normalized;\n                \/\/ check if cube touching have the same color on their face\n                var isX_A = Mathf.Abs(Vector3.Dot(d, thisOne.transform.right)) > 0.999f;\n                var isY_A = Mathf.Abs(Vector3.Dot(d, thisOne.transform.up)) > 0.999f;\n                var isZ_A = Mathf.Abs(Vector3.Dot(d, thisOne.transform.forward)) > 0.999f;\n\n                var isX_B = Mathf.Abs(Vector3.Dot(d, g.transform.right)) > 0.999f;\n                var isY_B = Mathf.Abs(Vector3.Dot(d, g.transform.up)) > 0.999f;\n                var isZ_B = Mathf.Abs(Vector3.Dot(d, g.transform.forward)) > 0.999f;\n\n                bool sameColor = isX_A == isX_B && isY_A == isY_B && isZ_A == isZ_B;\n                \/\/ this movement is finally forbidden\n                if (!sameColor)\n                {\n                    computeMove(-direction, thisOne, true, true); \/\/ back step\n                    \/\/ get out of there\n                    return;\n                }\n            }\n        }\n    }\n    \/\/ 3\u00b0 check colored tiles\n    if ((Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 180) ||\n             (Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 0))\n    {   \/\/ red tile on ground\n        if (greenTilesPos.Contains(storedPos) || blueTilesPos.Contains(storedPos) || greenGoal.Contains(storedPos) || blueGoal.Contains(storedPos))\n            computeMove(-direction, thisOne, true, true);\n        else if (listBlank.Contains(storedPos))\n            blankTileProcess(1, storedPos);\n        else if (redGoal.Contains(storedPos))\n            levelSolved(thisOne);\n        \/\/ get out of there\n        return;\n    }\n    else if ((Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 270) ||\n             (Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 0 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 90))\n    {   \/\/ green tile on ground\n        if (redTilesPos.Contains(storedPos) || blueTilesPos.Contains(storedPos) || redGoal.Contains(storedPos) || blueGoal.Contains(storedPos))\n            computeMove(-direction, thisOne, true, true);\n        else if (listBlank.Contains(storedPos))         \n            blankTileProcess(2, storedPos);\n        else if (greenGoal.Contains(storedPos))\n            levelSolved(thisOne);\n        \/\/ get out of there\n        return;\n    }\n    else if ((Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 270 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 0) ||\n             (Mathf.RoundToInt(thisOne.transform.eulerAngles.x) == 90 && Mathf.RoundToInt(thisOne.transform.eulerAngles.z) == 0))\n    {   \/\/ blue tile on ground\n        if (redTilesPos.Contains(storedPos) || greenTilesPos.Contains(storedPos) || redGoal.Contains(storedPos) || greenGoal.Contains(storedPos))\n            computeMove(-direction, thisOne, true, true);\n        else if (listBlank.Contains(storedPos))\n            blankTileProcess(3, storedPos);\n        else if (blueGoal.Contains(storedPos))\n            levelSolved(thisOne);\n        \/\/ get out of there\n        return;\n    }\n}\n\/\/ transform blank tile\nvoid blankTileProcess(int color, Vector3 vPos) \n{\n    int i = listBlank.IndexOf(vPos);\n    \/\/ UnityEngine.Debug.Log(listBlank.IndexOf(vPos) + \" -> \" + vPos);\n    \/\/ colored blank tile with a new color\n    if (Time.timeScale < 1.5f)\n    {\n        if (stampBlank[i] == 0)\n        {   \/\/ is this tile blank?\n            stampBlank[i] = listMov.Count;\n            obj.GetComponent<LevelConstructor>().blankTiles.transform.GetChild(i).GetComponent<Renderer>().sharedMaterial = obj.GetComponent<LevelConstructor>()._materials[color];\n            \/\/ play a sound\n            if (loader.GetComponent<Loader>()._paint != null && loader != null)\n                _loader.playSound(_loader._paint, this.transform, true, 0.5f, 0.9f, _loader._paint.length, false);\n            \/\/ ... or you can use item selection instead of an integer\n            if (color == 1)\n                redTilesPos.Add(vPos);\n            else if (color == 2)\n                greenTilesPos.Add(vPos);\n            else if (color == 3)\n                blueTilesPos.Add(vPos);\n            else\n                return;\n        }\n    }\n}\n```\n\nTo get an inverted clockwise system, allowing the player to come back in time, I used a movement list according to the object and its direction. So the number of steps which are available is computed according to the **List.Count** integer. We got something like that :\n\n```\n\/\/ all movement have been saved so as to allow back in time\nvoid backInTime()\n{\n    while (listMov.Last().artefact != cc[index])\n        splitCube();\n\n    if (listBlank.Contains(cc[index].transform.position - shift))\n    {\n        int i = listBlank.IndexOf(cc[index].transform.position - shift);\n        \/\/ disable a blank tile which has been colored before\n        if (stampBlank[i] == listMov.Count)\n        {\n            obj.GetComponent<LevelConstructor>().blankTiles.transform.GetChild(i).GetComponent<Renderer>().sharedMaterial = obj.GetComponent<LevelConstructor>()._materials[7];\n            stampBlank[i] = 0;\n            \/\/ new blank status\n            redTilesPos.Remove(cc[index].transform.position - shift);\n            greenTilesPos.Remove(cc[index].transform.position - shift);\n            blueTilesPos.Remove(cc[index].transform.position - shift);\n        }\n    }\n    \/\/ invert movement\n    computeMove(-listMov.Last().movement, listMov.Last().artefact, false, true);\n    deleteLast();\n}\n```\n\nMy favorite feature is the rounded blank tile which the player paints when a cube walks on it. This is not an easy feature because of one point - you can back in time. So its status must change too. So I implemented a tag on them in order to \"clean up\" tiles when retrograded cube passes again on a previous tile which has been colored. This tag is just an integer - the step when the tile has been colored. Take a look at the end of the first script - the **blankTileProcess()** function.  In the previous script, you got the code which compute the state of the tile during the right moment. \n\nSome parts of these codes could be improved. As an a single example, I use **Time.timeScale** like a tag - and I don't like it. There are too many **GetComponent** occurences in these scripts - and it's not the best way to get a hand on an object. So I have many to do :wink:  \nFor the FX I used some shaders and this project is my first one using the Unity URP system. I tested this option, but I guess that a legacy system could be \"enough\". I am developing more levels in order to release a full game with 50 levels. I have only one scene for them, and when you see a black screen, it's just a short time to erase the previous level, creating the next... Also I am proud of my level selection system. before I created a button for each level. Now I have only one script to generate how many buttons are required. This script is really useful. Then I share it with you.\n\n```\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UI;\n\npublic class DisplayButtons : MonoBehaviour\n{\n    public Transform button;\n    public int buttonOnX = 10;\n    public int buttonOnY = 5;\n\n    Vector3 nextPos = Vector3.zero;\n    public int spacingX = 80;\n    public int spacingY = 80;\n\n    Vector3 shift;\n    int id = 0;\n\n    void Start()\n    {\n        displayThem();\n    }\n\n    public void displayThem()\n    {\n        nextPos.y = 0;\n        id = 0;\n        shift = new Vector3(((buttonOnX * spacingX)\/2) - (spacingX\/2), (-(spacingY * buttonOnY)\/2) - (spacingY\/2), 0);\n\n        for (int i = 0; i < buttonOnY; i++)\n        {\n            nextPos.y -= spacingY;\n            nextPos.x = 0;\n\n            for (int j = 0; j < buttonOnX; j++)\n            {\n                id++;\n                RectTransform myButton = Instantiate(button as RectTransform);\n\n                myButton.GetComponentInChildren<Text>().text = (id).ToString();\n                myButton.GetComponentInChildren<Text>().fontSize = 40;\n                myButton.GetComponentInChildren<Text>().color = new Color(0.2f, 0.22f, 0.5f, 0.9f);\n                myButton.GetComponentInChildren<Image>().color = new Color(0.7f, 0.5f, 0.8f, 0.9f);\n                myButton.SetParent(transform, false);\n                myButton.localPosition = nextPos - shift;\n\n                nextPos.x += spacingX;\n                int level = PlayerPrefs.GetInt(\"Level\", 1);\n                \n                if (id > level && !Application.isEditor)\n                {\n                    myButton.GetComponentInChildren<Button>().interactable = false; \/\/ disables button for scene that isn't available\n                    myButton.GetComponentInChildren<Text>().color = new Color(0.2f, 0.22f, 0.5f, 0.45f);\n                }\n            }    \n        }\n    }\n}\n```\n\nI exposed some parts of my project, just sharing with you some keys. I guess that it could be improved - and with more time, I will do it. If you want to test the result, the game is available on Itch.io for free. Play, leave a comment - and this way, I can find your own project which I wanna play. Thank you for your support everyone. I wish you the best ++ \n\nhttps:\/\/ldjam.com\/events\/ludum-dare\/55\/virtual-church-of-the-digital-goddess\n\n![VCDG.jpeg](\/\/\/raw\/991\/z\/64afc.jpg)","title":"VCDG dev-log","wayback_source":[]}