Aug 13, 2022Kuina-chan


This is the tutorial 5 to learn the basic features of Kuin Programming Language for exe execution environment. This time, let's create a menu screen and a save function, which are modest but important in game creation.

1Sequence Design

1.1Creating Sequence



A game usually consists of a number of scenes, such as the title screen and menu screen, in addition to the main content. How these scenes are switched is called the sequence. Before writing a program, let's first consider the sequence.
I decided to use the sequence shown in Figure 1-1 for the menu screen I will create.
Menu Screen Sequence
Figure 1-1: Menu Screen Sequence
First of all, I will write a program for the sequence "fade-in, cursor selection, fade-out" which is the general flow. As before, select main And A draw Control from the snippet, and then add the code shown in Figure 1-2.
  1. enum Sequence
  2.   fadeIn
  3.   menu
  4.   fadeOut
  5. end enum
  6.  
  7. var sequence: @Sequence
  8.  
  9. var wndMain: wnd@Wnd
  10. var drawMain: wnd@Draw
  11.  
  12. func main()
  13.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  14.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  15.  
  16.   do @sequence :: %fadeIn
  17.  
  18.   while(wnd@act())
  19.     switch(@sequence)
  20.     case %fadeIn
  21.     case %menu
  22.     case %fadeOut
  23.     end switch
  24.  
  25.     do draw@render(60)
  26.   end while
  27. end func
Figure 1-2: menu1.kn
The enumeration type and variables of the sequence are created in lines 1 to 7, initialized in line 16, and in lines 19 to 23 during the main loop, the switch block is used to branch to process the sequence accordingly.
Since there is no content in the sequence, nothing will happen when I run it, but for now I have a rough idea of the flow.

1.2Fade In And Fade Out



I'll implement what's in fade in and fade out.
I could implement the fade effect on my own, but since the wipe library already provides a simple mechanism for fading, I will use it (Figure 1-3).
  1. enum Sequence
  2.   fadeIn
  3.   menu
  4.   fadeOut
  5. end enum
  6.  
  7. var sequence: @Sequence
  8.  
  9. var wndMain: wnd@Wnd
  10. var drawMain: wnd@Draw
  11.  
  12. func main()
  13.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  14.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  15.  
  16.   do @sequence :: %fadeIn
  17.   do wipe@set(%fade, true, 60, draw@black)
  18.   do draw@clearColor(draw@white)
  19.  
  20.   while(wnd@act())
  21.     switch(@sequence)
  22.     case %fadeIn
  23.       if(wipe@draw())
  24.         do @sequence :: %menu
  25.       end if
  26.     case %menu
  27.       do @sequence :: %fadeOut
  28.       do wipe@set(%fade, false, 60, draw@black)
  29.     case %fadeOut
  30.       if(wipe@draw())
  31.         ret
  32.       end if
  33.     end switch
  34.  
  35.     do draw@render(60)
  36.   end while
  37. end func
Figure 1-3: menu2.kn
The wipe@set function on lines 17 and 28 sets the start of the fade. The arguments in parentheses are, in order: type of wipe, fade in if true and fade out if false, number of frames to complete, and color.
Initially, the %fadeIn sequence in lines 23 to 25 will be executed, and the wipe@draw function in line 23 will draw the fade effect. When the fade is complete, the wipe@draw function returns true, so the sequence is switched to the next, %menu, at line 24.
In the %menu sequence in lines 27 and 28, the sequence is immediately changed to the next sequence %fadeOut in line 27, and the fadeout is started in line 28. The %fadeOut sequence in lines 30 to 32 ends with ret in line 31 as soon as the fadeout is finished.
Since the effect of the fade cannot be seen if the background is black, the background color is set to white using the draw@clearColor function in line 18. When you run the program, you can see that it fades in, exits the menu operation momentarily, and then fades out to exit (Figure 1-4).
Fade
Figure 1-4: Fade

2Creating Menu Screen

2.1Arranger



Now I will create the menu screen from here. This time I will use Kuin's visual placement feature. The procedure is shown in Figure 2-1.
Arranger Procedure
Figure 2-1: Arranger Procedure
The procedure is to create a menu screen visually, and the Kuin program will be generated automatically.
To use this feature, click on "Add" in the lower right corner of the Kuin editor and add a new .kn file (Figure 2-2).
How To Use Arranger (1)
Figure 2-2: How To Use Arranger (1)
From the dialog that comes up when you press (1) in the figure, select "2D Draw" in (2), enter the file name "menu" in (3), and press the "Add" button in (4) to add the .kn file.
shows the screen after adding the file.
How To Use Arranger (2)
Figure 2-3: How To Use Arranger (2)
(1) allows you to switch the .kn file. (2) allows you to configure the screen settings.
For this program, the screen size is 1600 x 900, so set the Position in (2) to "0 x 0 - 1600 x 900". If you press the "-" button for Zoom in (2) and set it to 0.5, the display on the Kuin editor will be reduced, which is an easy setting to create for this program.
Now let&apot;s place the background (Figure 2-4).
How To Use Arranger (3)
Figure 2-4: How To Use Arranger (3)
Select "Img" in (1) and drag (2) to place it, then set the Position in (3) to "0 x 0 - 1600 x 900" with the placed object selected. Then the object will be the right size to fit on the black screen. To select the object, click on it directly in (2) or select it from (4).
Next, save the data up to this point in a file of your choice, and then press Ctrl+Shift+R. In the res folder that appears, copy and place samples/free_resources/dot_back_side.png as before.
Let's set the placed image to the Img object. Select img1 from (4), and in the dialog box that comes up by pressing "Edit Properties..." in (3), set the properties as shown in Table 2-1, and press the OK button. If the image appears, you have succeeded.
Table 2-1: Properties Of img1
Property Value To Be Entered
color 0xFFAAAAAA
scrHeight 450
scrWidth 800
texPath res/dot_back_side.png
Let's create a menu screen using the same procedure (Figure 2-5).
How To Use Arranger (4)
Figure 2-5: How To Use Arranger (4)
Place four Text objects, two Rect objects, and one Circle object, and set their positions as shown in Table 2-2.
Table 2-2: Position Of Each Object
Object Name Position
text1 150 × 100 - 200 × 100
text2 150 × 200 - 200 × 100
text3 150 × 300 - 200 × 100
text4 150 × 400 - 200 × 100
rect1 400 × 100 - 1000 × 50
rect2 405 × 105 - 990 × 40
circle1 70 × 100 - 50 × 50
For each object, select it and press "Edit Properties..." at the bottom to set its properties as shown in Table 2-3.
Table 2-3: Properties Of Each Object
Object Name Property Value To Be Entered
text1 text Volume
text2 text Save
text3 text Load
text4 text Exit
rect1 colorFill 0x00000000
colorStroke 0xFFFFFFFF
strokeWidth 2.0
rect2 colorFill 0xFFAAFF80
circle1 colorFill 0x99FF9988
colorStroke 0xFFFFFFFF
strokeWidth 3.0
The arrangement of the menu screen is now complete. Let's check out the automatically generated program. Click the "Show Code" button at the top of the Kuin editor to view the program (Figure 2-6).
How To Use Arranger (5)
Figure 2-6: How To Use Arranger (5)
Press (1) to display the automatically generated program, and press again to return to the placement screen. If you look at the automatically generated program, you will see that the functions init, fin, and draw have been generated.
These can be called from the main program to draw the screen you have placed. Select "\main" from (2) to display the main program and add the program shown in Figure 2-7.
  1. enum Sequence
  2.   fadeIn
  3.   menu
  4.   fadeOut
  5. end enum
  6.  
  7. var sequence: @Sequence
  8.  
  9. var wndMain: wnd@Wnd
  10. var drawMain: wnd@Draw
  11.  
  12. func main()
  13.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  14.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  15.  
  16.   do @sequence :: %fadeIn
  17.   do wipe@set(%fade, true, 60, draw@black)
  18.   do draw@clearColor(draw@white)
  19.  
  20.   do \menu@init()
  21.  
  22.   while(wnd@act())
  23.     do \menu@draw()
  24.     switch(@sequence)
  25.     case %fadeIn
  26.       if(wipe@draw())
  27.         do @sequence :: %menu
  28.       end if
  29.     case %menu
  30.       do @sequence :: %fadeOut
  31.       do wipe@set(%fade, false, 60, draw@black)
  32.     case %fadeOut
  33.       if(wipe@draw())
  34.         ret
  35.       end if
  36.     end switch
  37.  
  38.     do draw@render(60)
  39.   end while
  40.  
  41.   do \menu@fin()
  42. end func
Figure 2-7: menu3.kn
When executed, the menu screen you created will be displayed instead of the white background (Figure 2-8).
Drawing Menu Screen
Figure 2-8: Drawing Menu Screen

2.2Cursor Movement



Since the cursor does not move as it is, let's add a process to move the cursor.
You can implement it on your own, but since the cursor movement process is used in many situations, it is already available in the cursor library. Let's use it this time (Figure 2-9).
  1. enum Sequence
  2.   fadeIn
  3.   menu
  4.   fadeOut
  5. end enum
  6.  
  7. var sequence: @Sequence
  8.  
  9. var wndMain: wnd@Wnd
  10. var drawMain: wnd@Draw
  11.  
  12. func main()
  13.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  14.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  15.  
  16.   do @sequence :: %fadeIn
  17.   do wipe@set(%fade, true, 60, draw@black)
  18.   do draw@clearColor(draw@white)
  19.  
  20.   do \menu@init()
  21.  
  22.   var cursor: cursor@Cursor :: #cursor@Cursor
  23.   do cursor.set(4, 0, false, true)
  24.  
  25.   while(wnd@act())
  26.     do \menu@draw()
  27.     switch(@sequence)
  28.     case %fadeIn
  29.       if(wipe@draw())
  30.         do @sequence :: %menu
  31.       end if
  32.     case %menu
  33.       do cursor.update()
  34.       do \menu@circle1.y :: (cursor.get() + 1) $ float * 100.0
  35.       if(input@pad(0, %a) = 1)
  36.         switch(cursor.get())
  37.         case 3
  38.           do @sequence :: %fadeOut
  39.           do wipe@set(%fade, false, 60, draw@black)
  40.         end switch
  41.       end if
  42.     case %fadeOut
  43.       if(wipe@draw())
  44.         ret
  45.       end if
  46.     end switch
  47.  
  48.     do draw@render(60)
  49.   end while
  50.  
  51.   do \menu@fin()
  52. end func
Figure 2-9: menu4.kn
An instance of the cursor@Cursor class is created in line 22 and initialized in line 23. The arguments in parentheses of the set method are, in order, the number of items to be selected by the cursor, the initial value of the cursor, false to move the cursor with the up and down keys and true to move it with the left and right keys, and whether the first and last items can be moved back and forth.
By calling the update method as shown in line 33, the cursor movement caused by key input will be handled automatically. By calling the get method as shown in line 34, the cursor position is returned as an integer starting from 0. The process here moves the Y coordinate of the circle accordingly.
In lines 35 to 41, when the Z key (A button) is pressed, if the cursor is in the third position, the program is terminated. The third is the Exit position.
When you run it, you will see that you can move the cursor with the up and down keys, select Exit and press the Z key to exit.

2.3Increase/Decrease Of Volume Gauge And Decision Process



The next step is to add the volume gauge increase/decrease and decision process. While we're at it, let's also add the process of playing background music to check the volume. Press Ctrl+Shift+R, then copy and place samples/free_resources/bgm_sample.ogg in the res folder.
Once you have it in place, add the program Figure 2-10.
  1. enum Sequence
  2.   fadeIn
  3.   menu
  4.   fadeOut
  5. end enum
  6.  
  7. var sequence: @Sequence
  8.  
  9. var wndMain: wnd@Wnd
  10. var drawMain: wnd@Draw
  11.  
  12. func main()
  13.   do ogg@init()
  14.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  15.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  16.  
  17.   do @sequence :: %fadeIn
  18.   do wipe@set(%fade, true, 60, draw@black)
  19.   do draw@clearColor(draw@white)
  20.  
  21.   do \menu@init()
  22.  
  23.   var cursor: cursor@Cursor :: #cursor@Cursor
  24.   do cursor.set(4, 0, false, true)
  25.   var volume: cursor@Cursor :: #cursor@Cursor
  26.   do volume.set(11, 10, true, false)
  27.   do bgm@play("res/bgm_sample.ogg", 0.0)
  28.  
  29.   while(wnd@act())
  30.     do \menu@draw()
  31.     switch(@sequence)
  32.     case %fadeIn
  33.       if(wipe@draw())
  34.         do @sequence :: %menu
  35.       end if
  36.     case %menu
  37.       do cursor.update()
  38.       do \menu@circle1.y :: (cursor.get() + 1) $ float * 100.0
  39.       do volume.update()
  40.       do \menu@rect2.width :: volume.get() $ float * 99.0
  41.       do bgm@volume(volume.get() $ float / 10.0)
  42.       if(input@pad(0, %a) = 1)
  43.         switch(cursor.get())
  44.         case 3
  45.           do @sequence :: %fadeOut
  46.           do wipe@set(%fade, false, 60, draw@black)
  47.         end switch
  48.       end if
  49.     case %fadeOut
  50.       if(wipe@draw())
  51.         ret
  52.       end if
  53.     end switch
  54.  
  55.     do draw@render(60)
  56.   end while
  57.  
  58.   do \menu@fin()
  59. end func
Figure 2-10: menu5.kn
Lines 25 to 26 and 40 to 41 are the same as before for the cursor. The value is between 0 and 10, and can be increased or decreased using the left and right keys.
Line 27, bgm@play is used to play background music. The bgm library is a library that wraps the snd library and allows for convenient background music playback. It can only handle one music file at a time, but can play it without creating an instance, and can also do crossfade playback.
The arguments in parentheses for bgm@play are, in order, file name and playback start position (seconds). To play an ogg format audio file, you need to call ogg@init once, as shown in line 13.
In lines 39-40, the length of the volume gauge is adjusted according to the left and right keys, and the actual volume of the background music is changed in line 41 according to these values.
Run it and press the left and right keys with Volume selected, you will see the volume gauge and the background music increase or decrease in loudness (Figure 2-11).
Menu Screen Operation
Figure 2-11: Menu Screen Operation

3Saving And Loading

3.1Saving And Loading



Finally, let's add the feature to save the volume value (Figure 3-1).
  1. enum Sequence
  2.   fadeIn
  3.   menu
  4.   fadeOut
  5. end enum
  6.  
  7. var sequence: @Sequence
  8.  
  9. var wndMain: wnd@Wnd
  10. var drawMain: wnd@Draw
  11.  
  12. class SaveData()
  13.   +var volume: int
  14. end class
  15.  
  16. func main()
  17.   do ogg@init()
  18.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  19.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  20.  
  21.   do @sequence :: %fadeIn
  22.   do wipe@set(%fade, true, 60, draw@black)
  23.   do draw@clearColor(draw@white)
  24.  
  25.   do \menu@init()
  26.  
  27.   var cursor: cursor@Cursor :: #cursor@Cursor
  28.   do cursor.set(4, 0, false, true)
  29.   var volume: cursor@Cursor :: #cursor@Cursor
  30.   do volume.set(11, 10, true, false)
  31.   do bgm@play("res/bgm_sample.ogg", 0.0)
  32.  
  33.   var key: []bit8 :: [
  34.   |0x01b8, 0x23b8, 0x45b8, 0x67b8, 0x89b8, 0xABb8, 0xCDb8, 0xEFb8,
  35.   |0x01b8, 0x23b8, 0x45b8, 0x67b8, 0x89b8, 0xABb8, 0xCDb8, 0xEFb8,
  36.   |0x01b8, 0x23b8, 0x45b8, 0x67b8, 0x89b8, 0xABb8, 0xCDb8, 0xEFb8,
  37.   |0x01b8, 0x23b8, 0x45b8, 0x67b8, 0x89b8, 0xABb8, 0xCDb8, 0xEFb8]
  38.   var path: []char :: wnd@sysDir(%appData) ~ "TestKuin/Test.bin"
  39.  
  40.   while(wnd@act())
  41.     do \menu@draw()
  42.     switch(@sequence)
  43.     case %fadeIn
  44.       if(wipe@draw())
  45.         do @sequence :: %menu
  46.       end if
  47.     case %menu
  48.       do cursor.update()
  49.       do \menu@circle1.y :: (cursor.get() + 1) $ float * 100.0
  50.       do volume.update()
  51.       do \menu@rect2.width :: volume.get() $ float * 99.0
  52.       do bgm@volume(volume.get() $ float / 10.0)
  53.       if(input@pad(0, %a) = 1)
  54.         switch(cursor.get())
  55.         case 1
  56.           var saveData: @SaveData :: #@SaveData
  57.           do saveData.volume :: volume.get()
  58.           var bins: []bit8 :: cipher@encrypt(saveData $> []bit8, key)
  59.           do file@makeDir(file@dir(path))
  60.           var file: file@Writer :: file@makeWriter(path, false)
  61.           if(file <>& null)
  62.             do file.write(bins)
  63.             do file.fin()
  64.           end if
  65.         case 2
  66.           var file: file@Reader :: file@makeReader(path)
  67.           if(file <>& null)
  68.             var bins: []bit8 :: cipher@decrypt(file.read(file.fileSize()), key)
  69.             do file.fin()
  70.             var saveData: @SaveData :: bins $< @SaveData
  71.             do volume.set(11, saveData.volume, true, false)
  72.           end if
  73.         case 3
  74.           do @sequence :: %fadeOut
  75.           do wipe@set(%fade, false, 60, draw@black)
  76.         end switch
  77.       end if
  78.     case %fadeOut
  79.       if(wipe@draw())
  80.         ret
  81.       end if
  82.     end switch
  83.  
  84.     do draw@render(60)
  85.   end while
  86.  
  87.   do \menu@fin()
  88. end func
Figure 3-1: menu6.kn
This time, this program will only save the volume, but the actual game programs will save various values such as lives, money, etc. So I prepared a class that summarizes the values for the saved data as shown in lines 12 to 14.
In lines 33 to 38 I defined the key to be used for encryption and the file path for the saved data. "0xnumberb8" means a hexadecimal value of bit8. bit8 is a type that handles 8-bit values. The key is like a password, so when you actually create the game, you should be able to set it to whatever you want and no one will know about it.
When "Save" is pressed, in lines 55 through 64, the program puts the volume value into the class I created, encrypts it, and saves it. The cipher@encrypt function is used for encryption. The arguments in parentheses are data to be encrypted and key in that order. The data must be specified in []bit8 type, and by writing "$>[]bit8", you can convert the whole class to []bit8 type. Encrypted data is saved using the file library.
The same procedure applies to loading saved data. In lines 65 to 72, the file is loaded in the file library and the cipher@decrypt function is used to decrypt the data. Since the combined data is of type []bit8, I convert it to the type of the original class by writing "$<Type of data" in line 71. For more details on how to use the file library, see Reading And Writing Text And Binary File.
Run it, change the Volume and press the Z key on Save, then exit, start it again and press the Z key on Load. The volume value you just set will be restored.
The menu screen is now complete. Next time, I will make a calculator application.
1660379141enf