⬆️ ⬇️

Language Lua and Corona SDK (1/3 part)

image



If you decide to master the development of games using the Corona SDK , this article will give you the necessary fundamentals of the engine itself and the Lua language on which you will have to develop. In its own way, this language is beautiful and in many respects unusual. I tried to collect in one article all the most necessary information, but it turned out to be more than can be placed in one publication and I had to divide the article into 3 parts, this first part and in it we will consider the following questions:





Source Commenting Procedure



There is a notion of comments in almost any programming language, Lua is no exception. What the comment is generally is the explanations to the source code of the program, which are directly inside the commented code. The syntax of comments is determined by the programming language. From the point of view of the compiler or interpreter, comments are part of the program text that does not affect its semantics. So comments either do not affect the order of the code execution and you write them for yourself and those who after some time can begin to study your source code. Comments can be used as a debugging tool, i.e. You can place in a comment a piece of code temporarily excluding it from execution and thereby simplifying the moment of searching for an error in another section. There are 2 kinds of comments in Lua.

')

Single line comment



Everything written from the sequence of characters "-" to the end of the line is considered a comment.



Multiline comment



A multi-line comment begins with the sequence "- [[" and ends with any number of lines with the sequence "]]". Further examples of the application of comments:



--[[  2  a  b   ]] function summ(a,b) return a + b end print(summ(5,7))-- 12 


I will not pay undue attention to the issue of the quality of commenting code, it decides everyone for himself will cite just a few simple tips:





Variables, constants, scope



Most programming languages ​​have variables. This concept in most cases is understood as the named, or otherwise addressed memory area, the address or name of which can be used to access data and change the value during program execution. In Lua, variables can be created right where they need to be applied, i.e. There is no specific block ( as in languages ​​like Pascal ) where it is necessary to declare variables before using them. I will give an example of declaring variables:



 a = 1 b = 3 local c = 'example' d,pi = 2, 3.14 a = 'stroka' 


Experts in other languages ​​I think noticed several features:





Let us dwell on constants. Under the constant is understood - the variable value of which does not change during use. Frequently, it is convenient to store several values ​​in a project ( for example, an application version ) repeatedly in constants, in some languages ​​there is a tool for creating constants in the direct sense of the word, variables that were declared once and in the future their value cannot be changed; in Lua there is no such tool You can blame everything, ( even the implementation of a function ) and for this reason, a convenient option for protecting against changing constants can be considered - the order of their naming. It is best to call constants in some special way so that they are more noticeable in the code, for example, BIG_BUKVAMI_SO_WORDS_ THROUGH_SPECKING, but this is not a rule but rather a tip.



I will briefly describe the format of acceptable names for variables, constants and functions, some things are declarative and cannot be broken, others are just tips:





Under the scope is understood - the code space within which you can directly access data. An incorrect understanding of the principles of organizing a scope can lead to a lot of unpleasant errors that are usually difficult to eliminate:





Further, we will not once again encounter examples of incorrect organization of the scope, I will focus on these places for this reason, this section can be completed.



Modules and project organization



A software module is a functionally complete program fragment, designed as a separate source code file. As you already know, the execution of any project in the Corona SDK starts with the main.lua file. If you write the entire application in one file, you may have a lot of difficulties, give an example:





There may be other problems if you incorrectly organize the project and write everything into one main.lua file, but in our lessons we will not do that, and then I will show you the method that I chose for my projects and which has proved itself well more than once to accept it or coming up with your own is up to you, but I will draw up further examples of projects in this way. Thus, we came to the conclusion that somehow we need to separate the source of the project into several files, moreover, these files will have different purposes and based on this, each file type will have its own naming order determined by the prefix in the name. This method is personally mine and I do not claim that it is the best, but since the further examples of games that you will see in my articles will have such a design mechanism for the project, I decided to take it as a basis. Briefly run through the types of files that may be useful.



Scene



Scene - sc prefix. The file name format is scName.lua. Where the name begins with a capital letter and determines the purpose of the file. For other types, this rule will also act therefore I will only specify their prefixes. The scene file has a fairly strict format and it is determined by the requirements of the Corona SDK library - composer. A separate lesson will be devoted to the consideration of this library. For now, I am attaching a source file template:



 local composer = require( "composer" ) local scene = composer.newScene() -- ----------------------------------------------------------------------------------- --        --     "composer.removeScene()" -- ----------------------------------------------------------------------------------- -- ----------------------------------------------------------------------------------- --   -- ----------------------------------------------------------------------------------- --   function scene:create( event ) local sceneGroup = self.view --    ,    end --   function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then --       elseif ( phase == "did" ) then --      end end --   function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then --     elseif ( phase == "did" ) then --     end end --  () function scene:destroy( event ) local sceneGroup = self.view --      end -- ----------------------------------------------------------------------------------- --    -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene 


It is worth noting that scene files do not need to be connected in any way to the project, since they will be connected as part of using the composer library.



Universal Library



Universal library - lib prefix. These files should be placed ready-made universal functions that can later be used in different parts of the application, for example, the trigonometric functions of your projects can be placed in the file libTriginom.lua and then these functions will be replenished and in the following projects they can be easily used simply by transferring the file and by connecting. Try to avoid binding in the universal libraries to the graphic design elements of the application, and pass the initial values ​​to the call parameters. I will give an example of a simple library:



 --------------------- --  -- -- libTrigonom.lua -- --------------------- local M = {} --    a,b M.hypotenuse = function(a,b) --c = √(a²+b²) local c = math.sqrt(a*a + b*b) return c end --  M.line_len = function(x1,y1,x2,y2) --length = √((X2-X1)²+(Y2-Y1)²) local length = math.sqrt((x2 - x1)^2 + (y2 - y1)^2) return length end --    M. get_angle = function(x1,y1,x2,y2) return ((math.atan2(y1 - y2, x1 - x2) / (math.pi / 180)) + 270) % 360 end return M 


This library can be connected by placing the following code in main.lua:



 Trigonom = require("libTrigonom") 


I will give an example of using functions in any part of the project:



 local a,b = 3,4-- local c = Trigonom.hypotenuse(a,b) --   print(c)-->> 5 local x1,y1 = 34,-56 -- A local x2,y2 = -134,56 -- B local length = Trigonom.line_len(x1,y1,x2,y2) --   print(length)-->> 201.91087142598 local angle = Trigonom.get_angle(x1,y1,x2,y2) print(angle)-->> 236.30993247402 


Widget / service



Widget / service - prefix svc. Widget is an analogue of the library, but the main functionality is based on the creation of graphic components. Using widgets it is convenient to create a composite interface, i.e. there are basic static components of the scene that are created as part of the main code in sc files and on top of static components can create dynamic widgets in the form of pop-up windows, relocatable forms and other amenities. Most conveniently, all project widgets should be designed with a single interface ( standard set of functions ), for example:





It is worth noting that there is no risk in that you have the same method ( function ) names in different widgets - show / refresh / hide to access a function, you must add the name of the widget ( as is the case with libraries ) and for this reason globally namespace will not be clogged.



I will give an example of a widget ( clock ):



 ------------------------- --    -- ------------------------- M = { GR = {}, --  GUI = {},--   } --    M.get_timezone = function() return os.difftime(os.time(), os.time(os.date("!*t", os.time()))) / 3600 end --     id M.refresh = function(id) local cur = M.GUI[id].capt local tm = os.date("!*t", os.time() + 3600*cur.GTM) --   0    10 local norn_dig = function(n) return n < 10 and '0'..n or n end local sec = cur.is_sec and ":"..norn_dig(tm.sec) or '' cur.text = norn_dig(tm.hour)..':' .. norn_dig(tm.min) .. sec end -- ,  id   --( ): -- x -    x (     x) -- y -    y (     y) -- is_sec -   (   - true) -- GTM -  ,    M.show = function(p) --    p = p and p or {} px = px and px or _W * .5 py = py and py or _H * .5 if p.is_sec == nil then p.is_sec = true end if p.GTM == nil then p.GTM = M.get_timezone() end --  local id = #M.GR + 1 M.GR[id] = display.newGroup()--   M.GUI[id] = {}--   -- -   M.GUI[id].bg = display.newRoundedRect(M.GR[id], px, py, _W * .33, _H * .08, _H * .02) M.GUI[id].bg.strokeWidth = _H * .005--  M.GUI[id].bg:setStrokeColor(0,1,.5)--  M.GUI[id].bg:setFillColor(1,.5,0)--  --      M.GUI[id].bg:addEventListener('touch',function(event) if event.phase == 'began' then M.hide(id) end end) --  M.GUI[id].capt = display.newText(M.GR[id], '', px, py, nil, _H * .05) M.GUI[id].capt:setFillColor(0) M.GUI[id].capt.GTM = p.GTM M.GUI[id].capt.is_sec = p.is_sec M.refresh(id) --    timer.performWithDelay( 500, function(event) if not M.GUI[id].capt.text then timer.cancel( event.source ) else M.refresh(id) end end, 0 ) end --    id    id   M.hide = function(id) if id then display.remove(M.GR[id]) else for i = 1,#M.GR do display.remove(M.GR[i]) end end end return M 


The order of connection and application of the service is as follows:



 _W = display.contentWidth _H = display.contentHeight Clock = require "svcClock" Clock.show() 


Services like libraries should be made fairly versatile, since they are the same candidates for reuse, most of the parameters ( ideally all ) should be made optional; have default values.



Database



Databases - prefix db. These files do not contain functions, but they store information about the static settings of the system objects, for example, the characteristics of units. I will give an example of a database with unit settings:



 return { peasant = { title = {ru = '', en = 'Peasant'}, force = 1, defence = 2, speed = 1, }, spearman = { title = {ru = '', en = 'Spearman'}, force = 3, defence = 2, speed = 2, }, archer = { title = {ru = '', en = 'Archer'}, force = 5, defence = 1, speed = 3, }, knight = { title = {ru = '', en = 'Knight'}, force = 7, defence = 3, speed = 4, }, } 


It should be noted that this type of implementation for larger databases is easier to replace with such an implementation



 local M = { name = {'peasant','spearman','archer','knight'}, title = { {ru = '', en = 'Peasant'}, {ru = '', en = 'Spearman'}, {ru = '', en = 'Archer'}, {ru = '', en = 'Knight'}, }, stats = { {force = 1, defence = 2, speed = 1,}, {force = 3, defence = 2, speed = 2,}, {force = 5, defence = 1, speed = 3,}, {force = 7, defence = 3, speed = 4,}, }, M.res = {}, } for i = 1,#M.name do M.res[M.name[i]] = { title = M.title[i] force = M.stats[i].force, defence = M.stats[i].defence, speed = M.stats[i].speed, } end return M.res 


In fact, this is the same thing in the first case, everything is initialized immediately, and in the second case, when the library is connected, the loop fills exactly the same table, the second method is more versatile because it allows you to edit this data more conveniently in the future, and secondly if a lot of data can significantly reduce the record Connect to use such databases as follows:



 units = require('dbUnits') print(units.archer.title.ru)--      


Insert code



Insert code - the prefix ins. No matter how we try to reduce the size of the files, it is not always possible, in this case some sections united by a common goal can be put into a separate file. I will give an example, there is a code:



 --main.lua local P = '123' k = 23.2 local function sum(a,b) return a+b end local function razn(a,b) return ab end local a,b = 2,3 print(sum(a,b)) print(razn(b,a)) 


now we move the functions sum and razn to a separate file InsExample.lua, we have 2 files:



 -- InsExample.lua function sum(a,b) return a+b end function razn(a,b) return ab end --main.lua local P = '123' k = 23.2 require 'insExample' local a,b = 2,3 print(sum(a,b)) print(razn(b,a)) 


It should be noted that when I moved to a separate file, I made the functions global ( removed local ), otherwise they will go out of scope from main.lua. You also need to understand that inside the file insExample.lua the local variable P will not be visible, but the global variable k will be visible, and if you declare the variable k after require, then it will not be visible.



IMPORTANT : In any use case of require for any file manipulations, this call is executed only once when you first run the thread through this section of the code, you should always take this into account if you do not want to suffer for a long time with complex bugs. Also, if you, like me, in modules of db / lib / svc type use a certain M variable to assemble data before return ( or any other ), do not forget to make this variable local, otherwise every next require will rewrite the result of the previous and this will lead to an even more complex error.



Conditional statements



As in most other languages, Lua has the ability to create conditional code branching operations, for this purpose, several types of if statement are used and a fairly convenient ternary branching method is used.



Comparison operators.



For data comparison, comparison operations are used. Each such operation consists of two variables, between which there is a comparison operator, there are 6 of them in Lua:





In essence, the result of any comparison operation is false or true ( false or true ), these values ​​are characteristic of the type, which, along with other types, will be considered below. Now we will consider using what constructions of the language the comparison operations are applied.



IF operator



At once for example - if a is greater than b, we assign c true



 if a > b then c = true end 


. — if .. , .. c end, c If if:



 local c = false fa > b then c = true end 






 local c = a > b 


:) if. and( ) or( ). , .



AND



and , , . , - , . obj1.obj2.obj3 , :



 if obj1.obj2.obj3 then c = true end 


, , obj1 obj2 .. :



 if obj1 and obj1.obj2 and obj1.obj2.obj3 then  = true end 


.



 local c = obj1 and obj1.obj2 and obj1.obj2.obj3 and true 


OR



or . Those. and, , and — , or — . or.



 if a>b or c>b or k<0 then c = true end 


:



 local c = a>b or c>b or k<0 


NOT



. not , .. a > b a b, not a > b a b.



IF ELSE



: , . if else :



 if a>b then c= true else c =false end 


. AND OR:



 local c = a>b and true or false 


IF ELSEIF ELSE



: 1 — 1, 2 — 2… N — N, X. switch, Lua :



 if a>b or d<b then k = 1 elseif b>c and a<b then k = 2 elseif c>d then k = 3 else k = 0 end 


— . IF ELSEIF ELSE: - else — , - , , end. , , :



 k = 1 if k > 0 then k = 2 print(k) elseif k > 1 then k = 3 print(k) end 


print -> 2. IF ELSEIF ELSE:



 local k = (a>b or d<b) and 1 or (b>c and a<b) and 2 or c>d and 3 or 0 


or and , or , .



: , : - — , - .



( 2/3)

Source: https://habr.com/ru/post/344304/



All Articles