# 64M35 4-2: A Practice Passion Project

**URL:** <https://forum.defold.com/t/64m35-4-2-a-practice-passion-project/67718>\
**Category:** Dev diaries\
**Created:** [March 5, 2021, 11:18pm UTC](https://forum.defold.com/t/64m35-4-2-a-practice-passion-project/67718 "2021-03-05T23:18:31Z")\
**Posts on this page:** 1\
**Showing post:** 11

<div class="post-metadata">

**Author:** ![thebone](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/thebone/32/23821_2.png) [@thebone](https://forum.defold.com/u/thebone)\
**Post date:** [May 30, 2022, 1:31am UTC](https://forum.defold.com/t/64m35-4-2-a-practice-passion-project/67718/11 "2022-05-30T01:31:50Z")

</div>

**Captain’s log No.5,_2022-05-29T23:05:00Z_**  
_Gui – the necessary evil_

**T+3days**

> [@thebone](#):
>
> I do not see a reason why the message can’t be delivered, since both collections are definitely loaded.

I left off the last post with an issue…

> [@64M35 4-2: Sister Topic](https://forum.defold.com/t/64m35-4-2-sister-topic/71010/2):
>
> My guess is that you forgot to set the `Name` of `manager.collection` in the outline, as that specific error message seems to only happen when trying to send a message to a collection that doesn’t exist.

…and I firmly believe that the Defold community is one of the most nice and helpful communities out there

_ **NOTE to SELF** \* (and anyone else)_: Make sure to name your collections on the collection screen.  
\*or rather: `msg.post(".", "name your collections!")` 😉

* * *

**T+2days**  
_intro sequence_

I made a very simple intro sequence

> **Intro sequence**
>
> ![30-05-22_made with defold](https://forum-defold.b-cdn.net/uploads/default/original/3X/3/4/34c268b2bd8772a3c036d0d036d3d495e29a7521.png)  
> _‘Made with Defold’_  
> _`gui.animate(self.defold_logo, gui.PROP_COLOR, to, gui.EASING_INOUTSINE, 5, 1, my_anim, gui.PLAYBACK_ONCE_PINGPONG)`_  
> ![30-05-22_made by blackbird](https://forum-defold.b-cdn.net/uploads/default/original/3X/4/8/48c8d7e2a292d24203ec3dfaab744465cb9a65cd.png)  
> _‘Made by tyblackbird’_  
> _`gui.animate(self.my_logo, gui.PROP_COLOR, to, gui.EASING_INOUTSINE, 5, 1, finish, gui.PLAYBACK_ONCE_PINGPONG)`_
> 
> I might tweak the durations at some point as it is a bit long. My “logo” is currently just a blank box node and I will also change the defold logo to fit the style of the game later on.

Also I added the most important feature to any intro sequence: the skip function

```lua
--the "I'M SICK OF THE HARD WORK THE DEV PUT INTO THIS AND WANT TO SKIP" function
function on_input(self, action_id, action)
	gui.cancel_animation(self.defold_logo, gui.PROP_COLOR)
	gui.cancel_animation(self.my_logo, gui.PROP_COLOR)
	finish()
end

```

At first I wanted the `intro.gui` to simply be overlayed onto the `main_menu.gui` in the `main.collection` to try and avoid creating unnecessarily small collection files but then I realised if the player decided to skip with ‘Ctrl,’ ‘Enter,’ or ‘X’ on the controller, the game would automatically load the first level. To avoid painful coding, I decided to just initiate the `intro.collection` before the main one.  
However _– and I don’t know if that’s just Defold’s quirky quick build function, –_ but Skipping with ‘Enter’ would sometimes still load the first level before the main menu even appeared, and weirder still the ‘Ctrl+B’ shortcut to quick-build would sometimes act as input to skip the intro alltogether.

* * *

_pause menu_

 ![30-05-22_pause menu](https://forum-defold.b-cdn.net/uploads/default/original/3X/c/c/cc6960b48d6dda49021db4a13f50ad0b9ca67a0b.png)  
The pause menu should appear when the player presses ‘Esc,’ ‘P’ or ‘Start’ on the controller. The `r_u_sure` node only if you’re about to exit the level to menu or desktop.  
_H o w e v e r –_ I was coding this at 1AM and so I was running on sloppy autopilot. For some reason the pause menu was up when the level was loaded and would only go away after TWO ‘Esc’ presses. Turns out I was trying to `gui.set_enabled()` with a string instead of a node.

```lua
gui.set_enabled("node", false) --> no no, baad
gui.set_enabled(gui.get_node("node"), false) --> yeeess

```

The navigation was fairly simple, and I’d already done a more complicated system for navigating the main menu grid of buttons via keyboard.

```lua
    ...
    elseif action_id == hash("Down") then
		--move the matrix down
		if not ovrly_active then --but only if the 'Are you sure' screen is NOT active
			self.location.x = self.location.x + 1
			if self.location.x > self.MAX.x then
				self.location.x = 1
			end
		end
	elseif action_id == hash("Right") then
		--move the matrix right
		if ovrly_active then --but only if the 'Are you sure' screen IS active
			self.location.y = self.location.y + 1
			if self.location.y > self.MAX.y then --and make sure you're not selecting node number 5'928
				self.location.y = 1
			end
		end
    ...

```

The navigation was functioning, now the only thing left to do was, well, _ **actually pause the game.** _  
Lucky for me Defold’s documentation is fairly exhaustive and, like I said before, the community is very helpful.  
a simple `msg.post("proxy:/controller", "set_time_step", {factor = 0, mode = 0})` is all it takes.

> **Just remember that unloading the level doesn't automatically unpause the game**
>
> ```lua
> function final(self)
> --[[
> Since you can only exit through the pause menu, the game remains paused when you reload a level.
> And since the manager_proxy controls literally everything, everything remains paused until you 
> press the pause button twice.
> This is in place to remove the confusion.
> 
> Fun fact, this is the first time I'd used the final(self) function on this project. :)
> It's ten lines long and only one actually does something. lol 
> ]]--
> msg.post(manager_proxy, "set_time_step", unpause)
> end
> 
> ```

Funny thing about that is, `function on_input` doesn’t care about simulation speed. While the pause screen is up and you navigate the menu with the arrow keys, the game moves with them. And since collision _IS_ part of the simulation, the game objects can just fly out of bounds and break.  
I tried to be cheeky about it and send a nuke message like: `msg.post("level:", "release_input_focus")` except a url without a path is completely useless. And honestly I couldn’t have been bothered to find a way to find any game objects in a collection using Lua. I don’t want to do it manually, because I made a pause.go to just chuck into any ol’ level I needed. \*

> **(Also trying to code my way into finding the \`pause.go\` parent collection was a whole ordeal)**
>
> Guys… I’m not good at this.  
> First I tried using the `go.get_parent(".")` function, except I was within a `.gui_script` file and that’s illegal! So I made a `pause.script` file that would find its parent on init and immediately msg it to the gui\_script, except I realised that it returns aN ID NOT A URL AND THAT DOESN’T HELP ME AT ALL!!
> 
> So I deleted the `.script` again and found what I’d been looking for…
> 
> ```lua
> self.parent = msg.post().socket
> 
> ```
> 
> Who knew an empty msg.post() function returned **your own** url?
> 
> Anyway, like I said, I did all that for nothing, because I couldn’t macro-`release_input_focus` throughout the whole collection and I wasn’t going to take a pedestrian approach either.

_Also, I’m surprised any amount of code from that day even works, because the comments are atrociously poorly spelled._

* * *

**T+1day\_yesterday**  
_loading screen and finishing the main menu_

…  
I have written some poor code in my life, but now I have seen hell.

 ![29-05-22_im scared](https://forum-defold.b-cdn.net/uploads/default/original/3X/7/2/72ebcdec06796eaa2123c6d48ae610b3f5b03218.png)  
\*_Pictured above: **Hell** _

Okay, so I know Pong isn’t a big game to load – takes less than half a second on average, but I have plans you know, aspirations. And since I’m working on all these back-end functionalities, might as well make a loading screen.

 ![30-05-22_loading screen](https://forum-defold.b-cdn.net/uploads/default/original/3X/5/3/53bdfccc21b37d6eab11efa6cfac7ccb93dc36d2.png)

```lua
gui.animate(self.basic_icon, "rotation.z", 360, gui.EASING_LINEAR, 2, 0, nil, gui.PLAYBACK_LOOP_FORWARD)

```

**Done.**

_“But how does it work?”_ you may ask– It doesn’t. It breaks my game.  
Sure, I may have almost entirely copy-pasted the collectionProxy code from the manual, but I thought I understood it enough to alter it to accomodate a loading screen.

**_Idea_:** I load the `loading.collection` proxy _(I actually named it this time)_ but I don’t send the enable message. Instead I only enable it _before_ I send a load request and disable it again _after_ I get the `proxy_loaded` response.  
**_Reality_:** It loaded and enabled itself immediately and underlaid the intro sequence. If I tried disabling it it sent an error. If I so much as looked at the `loading.collection` funny it somehow sent a `load_main_menu` message on EACH and EVERY **INPUT.** _(see ‘Hell’ for reference)_  
The horrible wall of errors in the screenshot is just _“couldn’t load menu, because it’s already loading”_ and _“cannot disable loading\_proxy because it was never loaded”_ about 60 times a second any time I touched my keyboard. And because it kept reloading the main menu, the screen was flashing and the navigation was stuck on level 1

I commented any code that mentioned loading.  
I took the **L** and decided to stop for the day, when I remembered I hadn’t fully finished the keyboard navigation on the main menu. I thought, naïve and young: _“It shouldn’t take long, I have a vague idea of what I need to do.”_

* * *

_ **three hours later** _

This is the very rough main menu I had made to just get the job done.

> [@thebone](#):
>
> So let’s make a main menu.
> 
> ![27-05-22_main menu gui](https://forum-defold.b-cdn.net/uploads/default/original/3X/1/0/1065aa37b5787fa9fc89783b0768bb3cd0ad47fa.png)

And this is the list written to represent it.

```lua
self.matrix = {
{"pong_box", "lvl2_box", "lvl3_box", nil; },
{"lvl4_box", "lvl5_box", "lvl6_box", nil; },
{nil, nil, nil, "exit_box"; }; 
} -- self.active_node = self.matrix[row][collumn]

```

So… **TL;DR** I got it working. A bunch of thinking and a few dozen lines of code and a bit of dumb maths. I am _genuinely_ proud and excited about how I solved this issue, which is why this is your last chance to skip all of that.

> **Don't say I didn't warn you.**
>
> As you may know, Lua absolutely **hates** `nil` values. The funny thing is, you could still navigate to the exit button fine. `gui.get_node(nil)` is not a terminal error. But it’s confusing to press ‘down’ and lose your active node in the ether. I know how the matrix is constructed, but anyone else wouldn’t know how to properly exit the game _(which, I mean, means they’re stuck playing my game forever 😉 )_
> 
> So when you press a button and the `self.active_node = nil` You need to get out of that. And preferably to a usable node in the direcition you pressed.
> 
> **_A few issues with that_:** If I go into the direction pressed indefinitely, I will completely bypass exit. I needed a way to find the most suitable node, perpendicular to the direction pressed. But problem! I can easily tell the function which direction I came from and where I’m going, but massively long if statements didn’t quite feel right. It’s a small menu, but still. But problem! I can’t really run any simple mathematic equations on “Left, Right, Up, Down” or a nested list.
> 
> So I thought I had an idea, and I started coding, and realised it wasn’t a good solution and got to delete it all.
> 
> And I’m staring at the screen _(as you do)_ and realise something. Lua allows you to check if a variable has **ANY** value.
> 
> ```lua
> a = "something" 
> if a then ... --> true
> b = nil
> if b then ... -->false
> 
> ```
> 
> So my matrix `{{"node", "node1", nil}, ...}` is basically `{{true, true, false},...}` AND more importantly: `{{1, 1, 0}, ...}.` And _those_ are numbers. For maths. So if I could devise a system that could assign each number a new value, I could then just find the highest number via for loop and save the position. So. I gingerly take a piece of paper with the zest of a child, trying to find good evaluations for straigh-on and to-the-side and … quickly realise I got excited for nothing. I had no idea how to do what I had envisioned. Sometimes the highest value would land onto a nil node. And that defeats the purpose of this whole operation. If you for example clicked “Left:” I assigned the left directly-across node a +1 value. The Up direction would get +2 and Down +3. That meant a valid node above would share the same high value as a nil node below. And I’d almost given up on this idea…
> 
> The answer, _as to most things in life,_ was multiplication. I can add these values as described to the matrix, and after multiply it with the original matrix. The zeroes representing nill nodes cancel out the numbers. The ones representing valid nodes would be completely inert and I would get my code.
> 
> > **A quick example of the system**
> >
> > ```lua
> > --this is the symbolised version of the matrix.
> > --each one represents a viable node, each zero nil
> > mock_matrix = {
> > {1, 1, 1, 0},
> > {1, 1, 1, 0},
> > {0, 0, 0, 1}
> > }
> > 
> > --[[if I, for example go from]] F to 5,-- my direction is to the right, my location at 5.
> > --the movement loops around and so I add +1 to D
> > --I add +2 to G and +3 to 4
> > --Keep in mind, this function is called only when you land on a nil node.
> > {A, B, C, 4}
> > {D, E, F, 5} -->
> > {1, 2, 3, G}
> > 
> > --addition phase
> > --as you see, the nil node top-right is valued the same as the bottom-right valid node.
> > {1, 1, 1, 3}
> > -> {2, 1, 1, 0} ->
> > {0, 0, 0, 3}
> > 
> > --multiplication phase
> > --this phase takes care of that.
> > {1, 1, 1, 0}
> > {2, 1, 1, 0}
> > {0, 0, 0, 3}
> > 
> > selected_node = "exit_node"
> > 
> > ```
> 
> And this is the implementation:
> 
> ```lua
> --within function on_input()
> if action_id == hash("lefUp") or action_id == hash("riUp") or action_id == hash("ctrUp") then
> --move the matrix up
> self.row = self.row - 1
> if self.row < 1 then
> self.row = self.MAXROW
> end
> if check_viable(self) then
> -- cool that's all
> else
> a = get_viable(self, "U")
> self.row = a.x
> self.collumn = a.y
> end
> ...	
> --local functions
> function check_viable(self)
> local from = self.matrix[self.row][self.collumn]
> --is the selected option not nil?
> if from then
> --good. we're done here.
> return true
> end
> end
> function get_viable(self, drctn)
> --it isn't a viable option? sit down, this'll be a while.
> local vert = 0
> local hor = 0
> --mapping target=/=original direction to number
> if drctn == "U" then
> vert = -1
> elseif drctn == "D" then
> vert = 1
> elseif drctn == "R" then
> hor = 1
> elseif drctn == "L" then
> hor = -1
> end
> 
> -- a numerical representation for which spaces are okay.
> local mock_matrix = {
> [0]={[0]=1, 1, 1, 0},
> [1]={[0]=1, 1, 1, 0},
> [2]={[0]=0, 0, 0, 1},
> }
> -- mathematical manipulation of the matrix to determine the best landing position 
> local origin = {x = self.row -1, y = self.collumn -1} --> i have just realised i got rows and collumns backwards on the x and y, but this is how the whole thing is coded so far, and i aint gonna change it.
> mock_matrix[origin.x][origin.y] = 0
> local sec_opt = mock_matrix[(origin.x+vert)%self.MAXROW][(origin.y+hor)%self.MAXCOLLUMN]
> sec_opt = sec_opt * (sec_opt + 1)
> mock_matrix[(origin.x+vert)%self.MAXROW][(origin.y+hor)%self.MAXCOLLUMN] = sec_opt
> local fir_opt = mock_matrix[(origin.x+hor)%self.MAXROW][(origin.y+vert)%self.MAXCOLLUMN]
> local ffir_opt = mock_matrix[(origin.x-hor)%self.MAXROW][(origin.y-vert)%self.MAXCOLLUMN]
> fir_opt = fir_opt * (fir_opt + 3)
> ffir_opt = ffir_opt * (ffir_opt + 2)
> mock_matrix[(origin.x+hor)%self.MAXROW][(origin.y+vert)%self.MAXCOLLUMN] = fir_opt
> mock_matrix[(origin.x-hor)%self.MAXROW][(origin.y-vert)%self.MAXCOLLUMN] = ffir_opt
> 
> --[[
> VIABLE POSITION CRITERIA:
> directly across is two spaces away from origin, therefore it is valued least at n(n+1)
> one perpendicular direction is valued at n(n+2)
> the third direction is valued at n(n+3)
> 
> if 'directly across' had priority over 'across', then the exit_node would never get chosen, because it borders
> on all sides to a nil value.
> 
> Which perpendicular direction is valued more is completely arbitrary.
> If the evaluated direction is marked 0 then n(n+a) = 0
> If the evaluated direction is marked 1 then n(n+a) > 0
> 
> Because in some cases I could try evaluating a direction marked nil outside of the matrix, I used 
> '(origin.x+hor)%self.MAXROW' -> which returns the remainder of division with the MAXINDEX
> Since a%a = 0 and a%a ~= a, I had to alter the indexing in mock_matrix so that it started with 0
> In the return statement I then add 1 to both x and y.
> ]]
> 
> --find the highest number in the mock_matrix and save its indexes
> local high_index = {x = 0, y = 0}
> local high_num = 0
> for ix, list in pairs(mock_matrix) do
> for iy, num in pairs(list) do
> if num >= high_num then
> high_num = num 
> high_index.x = ix + 1
> high_index.y = iy + 1
> end
> end
> end
> 
> return high_index
> end
> 
> ```
> 
> Now… This solution isn’t without issues. Because the `first_option` and `ffirst_option` directions are completely arbitrary you sometimes get strange behaviour. If you press ‘Down’ on the bottom-right node, you’ll somehow end up at the top-left. I understand how the maths does that, but I think it’ll be fine. It’s perfectly okay for what it is.
> 
> But it’s still so cool!!!  
> Just look at those slick one-liners
> 
> ```lua
> mock_matrix[(origin.x+hor)%self.MAXROW][(origin.y+vert)%self.MAXCOLLUMN] = fir_opt
> 
> ```
> 
> _Oooooooooh mamma!_
> 
> Anyway, I did warn you I was gonna be long.

Oh also, I finally put an actual program termination function to the game.  
`sys.exit(0)` – I don’t understand why I was putting that off for five days.

* * *

**T+0days\_today**  
_And here we are._

So I have a new issue, that I don’t know how to solve. Maybe I could try to implement a loading screen via guis and not collections. I’m not sure I want to bang my head against it anyway.

Future plans: Finish all the unfinished `pong.collection` functionality and start work on other things.

_End log, 2022-05-30T01:27:00Z_

_PS.: Can I just say, I love the little “mini map” of code in the editor 🙂_

 ![30-05-22_pause minimap](https://forum-defold.b-cdn.net/uploads/default/original/3X/3/5/35669bd47fc9638b48eaccde8491f1536023527a.png)

---

_[View the full topic](https://forum.defold.com/t/64m35-4-2-a-practice-passion-project/67718)._
