Need suggestions on using the Best Practice/Code Structure for Visual Novel Game

Hi People, hope everyone is doing great.

My team and i are working on a Visual Novel project and we are using Corona SDK to code our entire game. I wanted to seek help and advise from experienced Corona developers on how i could potentially structure my code a bit better that it is well optimized and eat’s up less resources. 

Kindly observe the following code:

local tapCount = 0 

function TransitionFunc()

    tapCount = tapCount + 1

    if(tapCount == 1) then

        --Sarah Dialog Disappear

       transition.to( DialogText,{ alpha = 0, time = 800 } )

        --Dialog Text for Kamali

        DialogText = display.newText( " What day is it again?.. ", 245, 280, “SceneAssets/GeoSansLight”, 14)

        DialogText:setFillColor( 0,0,0 )

        DialogText.alpha = 0

        --Sarah Dialog Disappear

       transition.to( DialogText,{ alpha = 1 ,time = 800 } )

       ButtonAppearDisappear()

    elseif(tapCount == 2) then

       --Kamali Transition Disappear

       transition.to( K1,{ alpha = 0,time = 800 } )

       --Dialog Text Transition Disappear

       transition.to( DialogText,{alpha = 0 ,time = 600} )

       --Sarah Name Image Disappears

        transition.to( Sarah_Name,{alpha = 0 ,time = 800} )

        --Dialog Text for Kamali

        DialogText = display.newText( “Tuesday?..”, 245, 280, “SceneAssets/GeoSansLight”, 14)

        DialogText:setFillColor( 0,0,0 )

        DialogText.alpha = 0

        --Kamali Transition Appear

       transition.to( K2,{ alpha = 1,time = 800 } )

        --Kamali Name Image Appears

        transition.to( Kamali_Name,{alpha = 1 ,time = 800} )

        --Dialog Text Appears

        transition.to( DialogText,{ time = 800, alpha = 1})

        ButtonAppearDisappear()

–Inside scene:create

button:addEventListener( “tap”, TransitionFunc )

     button:addEventListener( “tap”, ClickSound )

     next:addEventListener( “tap”, changeScenes ) 

     next:addEventListener( “tap”, ClickSound )

 So i would like to explain what i am doing currently is that i have assigned this TransitionFunc function to the button. Whenever the button is pressed it adds + 1 to the tap Count variable and then the if/elseif structure compares the value to tapCount, depending on the value the previous text and sprite is hidden by alpha = 0 and new sprite and dialogue is generated/visible by transition.to alpha = 1.

Q1)Now i am having a feeling that this might be a poor way of doing things. And i needed your help on how to incorporate Constructors and parameters to control repetitive tasks.

Q2) Is there anyway that i could declare a method of transition.to without parameters and then specify the object, time, and delay later to save lines of repetitive code.

Q3) If anyone has a better logic for a way of doing dialog and character transitions please kindly share with me so that i can structure my thing better. 

Your help will be very much appreciated guys, help a brother out.

Thank you

It looks like this function makes text objects appear/disappear depending on a value.  If that is the case, I would just declare another function to do the animation.

Some untested code…

function AnimateText(object, alpha, time) transition.to(object, {alpha = alpha, time = time}) -- whatever else end function TransitionFunc() tapCount = tapCount + 1 if tapCount == 1 then AnimateText(Dialogtext, 0, 800) -- other animations, if needed end -- etc, etc end

Certainly the way you’re doing it will get very convoluted when you’re up to tap count 400 and have to remember what needs to be hidden and shown. You’ll need to make use of tables, that contain data about each line, such as the text, who says it, any images that need to be shown etc, sounds to be played. 

So you could split up your game into a series of conversations, each containing a number of ‘lines’. Draw your DialogText, NameText objects once in scene:create() and keep a reference to them at the top of the scene, and then amend the text based on the next line.

A very rough outline:

[lua]

– conversations.lua

local m = {}

m[#m+1] = { backgroundImage = “forest.jpg”, lines = {}} – a new conversation

m[#m].lines[#m.lines+1] = {name = “Kamali”, text = “What day is it, again?”, image = “kamaliConfused.png”}

m[#m].lines[#m.lines+1] = {name = “Kamali”, text = “Tuesday?”} – no image, we’ll continue with previous one

m[#m].lines[#m.lines+1] = {name = “Sarah”, text = “Yeah, I think so…?”, image = “sarahShrugs.png”, sound = “laugh.mp3”  }

m[#m+1] = { backgroundImage = “cinema.jpg”, lines = {}} – a second conversation

m[#m].lines[#m.lines+1] = {name = “Simon”, text = “Hmm, how did we get here?”, image = “simon.png”}

return m

– myGame.lua

local conversations = require(“conversations”)

local currentConversation = 1

local currentLine = 1

local displayNextLine = function ()

  local line = conversations[currentConversation][currentLine]

  

  

  DialogText.text = line.text

  NameText.text = line.name

  if (line.image ~= nil) then

    – set my image fill here

  end

  

  if (line.sound ~= nil) then

    – play sound here

  end

  

  currentLine = currentLine + 1

  

  if (currentLine > #conversations[currentConversation].lines) then

    currentConversation = currentConversation + 1

    currentLine = 1

   – do anything else we need to after a conversation finished

  end

  

end

local setupConversation = function ()

   

   – change background image here, and anything else before a new conversation starts

end

local TransitionFunc = function ()

  

    if (conversations[currentConversation] == nil) then

      print (“No more conversations to show…”)

      return

    end

   if (currentLine == 1) then

    setupConversation()

   end

   

    displayNextLine()

     

  

end

button:addEventListener( “tap”, TransitionFunc )

[/lua]

Firstly @Nick Sherman, @schizoid2k  thank you guys for taking the time to reply and taking the time to write sample code to help me out.

I tried Nick’s code, basically i have 3 lua files in the game folder right now. Conversation.lua, game.lua and main.lua all connected with composer.api.

----Game File-----


– game.lua

local composer = require( “composer” )

local conversations = require(“conversations”)


– Your code here

local currentConversation = 1

local currentLine = 1

local DialogText

local NameText

local K3

local button

local TransitionFunc = function ()

  

    if (conversations[currentConversation] == nil) then

      print (“No more conversations to show…”)

      return

    end

   if (currentLine == 1) then

    setupConversation()

   end

   

    displayNextLine()

end

function scene:create( event )

    local sceneGroup = self.view

  function DisplayNextLine()

    local line = conversations[currentConversation][currentLine]

  

    DialogText.text = line.text

    NameText.text = line.name

    if (line.image ~= nil) then

    – set my image fill here

    --Kamali’s Second pose

    K3 = display.newImageRect( sceneGroup, “SceneAssets/klab6.png”, 260, 380 )

    K3.x = display.contentCenterX + 180

    K3.y = display.contentCenterY

    K3.alpha = 0

    --Button

    button = display.newImageRect( sceneGroup, “SceneAssets/blue.png”, 40, 40 )

    button.x = display.contentCenterX + 250

    button.y = display.contentCenterY + 120

    button.alpha = 0

    end

  

    currentLine = currentLine + 1

    if (currentLine > #conversations[currentConversation].lines) then

        currentConversation = currentConversation + 1

        currentLine = 1

       – do anything else we need to after a conversation finished

    end

    button:addEventListener( “tap”, TransitionFunc )

  end

end

– show()

function scene:show( event )

    local sceneGroup = self.view

    local phase = event.phase

    if ( phase == “will” ) then

        – Code here runs when the scene is still off screen (but is about to come on screen)

    elseif ( phase == “did” ) then

        – Code here runs when the scene is entirely on screen

    end

end

– hide()

function scene:hide( event )

    local sceneGroup = self.view

    local phase = event.phase

    if ( phase == “will” ) then

        – Code here runs when the scene is on screen (but is about to go off screen)

    elseif ( phase == “did” ) then

        – Code here runs immediately after the scene goes entirely off screen

    end

end

– destroy()

function scene:destroy( event )

    local sceneGroup = self.view

    – Code here runs prior to the removal of scene’s view

end

– -----------------------------------------------------------------------------------

– Scene event function listeners

– -----------------------------------------------------------------------------------

scene:addEventListener( “create”, scene )

scene:addEventListener( “show”, scene )

scene:addEventListener( “hide”, scene )

scene:addEventListener( “destroy”, scene )

– -----------------------------------------------------------------------------------

return scene

-----conversation file-------- (Didnt throw in everything yet just for the sake of simplicity and trying the suggested structure)


– conversations.lua


– Your code here

local m = {}

m[#m+1] = { backgroundImage = “SceneAssets/Lab_bg1.png”, lines = {}} – a new conversation

m[#m].lines[#m.lines+1] = {nameText = “Sarah”, text = “What day is it, again?”, image = “SceneAssets/KamaliLab2.png”}

m[#m].lines[#m.lines+1] = {nameText = “Kamali”, text = “Tuesday?”} – no image, we’ll continue with previous one

return m

------main.lua-------


– main.lua


– Your code here

local composer = require( “composer” )

composer.gotoScene( “game” )

So i get a compile time error that i am not specifying the length of lines correctly. Attempt to get length of field lines (a nil value) stack traceback. Perhaps i am doing something wrong, apologies if i am being too nooby, i am new to Lua and Corona all together.

@Schizoid2k

Thanks for your input. I did try making a function the way you suggested. The issue which comes is if i call a function within a specific tapCount, lets say i call it when (tapCount == 1), what happens is before the dialogueText is updated with new Dialogscript, the function call hides previous text and displays next text within the function call, so if i paste it before my dialogueText = display.newText, the function never picks up the updated dialogueText… and if i paste it after, i end up having none of the dialogues disappearing and end up having overlapping dialogues on top of one another.

function dialogFunc()

    --Dialog Disappear

    transition.to( DialogText,{ alpha = 0, time = 800 } )

    --Dialog Appear

    transition.to( DialogText,{ alpha = 1 ,time = 800, delay = 600 } )

 end

function TransitionFunc()

    tapCount = tapCount + 1

    if(tapCount == 1) then    

        --Dialog Text for Kamali

        DialogText = display.newText( dialogScript[1] , 245, 280, “SceneAssets/GeoSansLight”, 14)

        dialogFunc()

        ButtonAppearDisappear()

Also is there a way to check how much memory or resource intensive my code is getting? I googled and found a method on stack overflow which shows garbage collection however i am not sure if i implemented it correctly with my code as it didnt work earlier. 

Can post my full github repository link here if maybe that helps to look at my code better for you guys.

Thanks again for the help, really appreciate it fellas.

Apologies, each line of dialog should be added like this:

[lua]

m[#m].lines[#m[#m].lines+1] = {name = “Kamali”, text = “What day is it, again?”, image = “kamaliConfused.png”}

[/lua]

I did it this way so if you want to insert/remove conversations, or lines in the middle of conversations, you don’t have to go around renumbering everything. 

What we’re doing is:

m[#m] - grab the last element added in the ‘m’ array.

.lines - access the lines table of that element so we can add to it

[#m[#m].lines+1] - grab the last element again so we can count how many lines already added, and add one to get the index to set in the lines table with whatever follows.