LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Creating Games? Developing something for fun?

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Fri Oct 08, 2021 2:49 pm

Part 18. PartTutorial Stack 10 Music players, expanded view handler, big change to warppoints...more?

NOTE: Old SHMUP Tutorial Stacks removed. Will attach latest and greatest stacks to my first and last posts of thread.
  • Changes to Tutorial Stack 10
    1. Moved card Script of Card "Title" to stack script only OpenCard and Play button script remain
    2. Added multi-items functionaity to View handler for tracking multiple events
    3. added lastViewItems variable so view can handle multiple items
    4. Play card script now calls InitMusic which will then signal InitGame when it's done loading songs/filling players
    5. Added a condition in InitMusic to skip loading files if the filenames of players on the card are all filled already
    6. Changed LoadingScreen to handle any message sent and display whatever text it is given
    7. InitMusic now creates player objects if they don't exist so any number of songs can be used
    8. added CycleMusic handler for moving through list of songs sequentially back and forth
    9. Added some conditions in on ArrowKey to try to use CycleMusic but there is MUCH keyboard delay
    10. Added a condition to EscapeKey and ArrowKey to send PlayButtonPressed and enter game mode from Title card, for now
    11. Scripts in WarpPoints are now deprecated, the stack script handles the warps on its own
    12. Added variable scrollSpeedMult during warp area slow down to effect scrollspeed without effecting the player
    13. modified InitWarp to resize the warp based on the stack window height,
    14 Warps now use lineSize instead of blendLevel and use player proximity instead change over time for effects
    15. WarpJump now gives 1000 points and and a bonus life
    16 Moved the playerLives object blendLevel setting out of InitPlayer into PlayerLivesIcons handler
    17. Removed some redudant DeactivateBullet/InactiveBullet calls
    18. Modified CheckCollisions to only search for collisions among activeBullets instead of all 20 of them
    19 Modified CheckCollision not to exit if there are activeBullets but no activeEnemies
    20. Still Fighting a HUGE slowdown from warps--Its the graphic effects, so they are turned off.
    21. Added currentLevel variable that gives the player 1000 points when timelevel increments forward
    22. removed 'pins' from the warp groups and encases the special warp graphic in a big box called WarpFrame
    23. timeLevel now starts at 1
Fixes since first upload, stack has been reploaded to this post.
Tutorial 10 Music Fixed

Code: Select all

1. initMusic encapsulated in conditions to skip processing files & making players if there is no Data/Music folder
2. filteredMusicFiles function added, stack was finding .pdfs etc in Livecode app folder
3. all music related handlers will exit if songCount < 1
4. songCount variable set to 0 by default in local variable definitions
Ok super tired so won't go explaining too much right now.
The Play card now loads music from the music folder and generates player objects to play the music dynamically, so there's nothing on the player card if you have no music..which I should probably test to see if that throws a bug...oops. <--- FIXED
The script currently leaves the player objects at the top of the screen so you can see that they were made, soon they'll be tucked off screen.

I was having crazy major slowdowns with the warps and in trouble shooting that I saw my whole method for using scripts within the warp groups was just ridiculous. So I'll have to go back and fix that part of the tutorial for sure. Turns out the slowdowns were all the graphic effects I had turned on the object, even just one graphic effect causes a noticeable slow down. So now proxiimity to the warp gate modifies the linesize of the warp object and dashes take care of it looking sci-fi to a degree. If graphic effects cause slow down, I wonder what happens if I turn them off on everything? I'll just take out my contacts and make glow effects the old fashioned way.

I had added scrollSpeedMult to double the speed to make up for the slow down, but now without the effects its not really needed. SpeedMult was shared with the player so doubling the value freaked the player out. WarpJump now calls StageGraphics on enemies and bullets to fake our apparent change in location.

My timeLevel equals (the seconds minus TimeOfInit )/60 calculation needs to be reworked. If you pause the game or do a little scripting you are at timeLevel 10 when you go back into the game. I reset timeofInit when returning from pause as game continues? Probably

Oh a word of caution. The more songs you have in your Data/Music/ folder the longer your loading time between Title and Play cards, on first run. I threw a bunch of them in there so I wouldn't get song fatigue while developing and every first run of the game i'm sitting on the edge of my seat worrying if it's crashed. I had been display the song names through the label of the loading graphic, but that is an ugly mess.

A devs work is never done.
Last edited by xAction on Fri Oct 15, 2021 12:01 am, edited 3 times in total.

stam
Posts: 2679
Joined: Sun Jun 04, 2006 9:39 pm
Location: London, UK

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by stam » Fri Oct 08, 2021 6:51 pm

xAction wrote:
Fri Oct 08, 2021 2:49 pm
Part 18. PartTutorial Stack 10 Music players, expanded view handler, big change to warppoints...more?
SHMUP_GAME_TUTORIAL_STACK10.zip
Thank you for continuing to share.

I downloaded this stack - i was certain it would be impressive but i'm getting perhaps 2-3 frames per second and it keeps throwing errors about the time and player's name.

I take it that's not supposed to happen?
What frame rate do you get on your system?

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Fri Oct 08, 2021 10:25 pm

My guess is you don't have any music in Data/Music in the same folder as the stack?
Folder structure should be like so:
SMHUPTUTORIAL_FOLDERSTRUCTUREpng.png
How your folder should look
Music.zip
Place the unzipped folder in a Data folder in same folder as your stack
(130.58 KiB) Downloaded 155 times
Sounds.zip
Place the unzipped folder in a Data folder in same folder as your stack
(181.39 KiB) Downloaded 153 times
Forum has size limits on uploads that vexxed my perfect plan.

I guess I have some work adding conditions for no sound. Up until this very moment, there were no players, no music handlers active so things were running smooth without them. Done, stack reuploaded to step Part 18 post

How to measure frames? It's lightning fast here...up until the warp point, then it's pony fast. Once LC starts throwing tons of errors it should slow down.

Ah...weird thing was happening when stack was running with no Data folder, the InitMusic function gathered all files from the Livecode app folder, made players for them and set the filename of the players to things like PDF. The stack now bypasses InitMusic file gathering/player making loop if it doesn't find Data/Music

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 8:35 am

Part 18b. Scripts Review: Music Additions/Fixes
  1. What changed in stack 10 in regards to music that I need to cover.
  2. Play card script now calls InitMusic which will then signal InitGame when it's done loading songs/filling players
  3. InitMusic now creates player objects if they don't exist so any number of songs can be used
  4. added CycleMusic handler for moving through list of song sequentially back and forth
  5. Added a condition in Initmusic to skip loading files if the filenames of players on the card are all filled already
  6. initMusic encapsulated in conditions to skip processing files & making players if there is no Data/Music folder
  7. filteredMusicFiles function added, stack was finding .pdfs etc in Livecode app folder
  8. all music related handlers will exit if songCount < 1
  9. songCount variable set to 0 by default in local variable definitions
It's a numbered list but maybe not the order I go in.

Lets look at the music handlers in the stack script, jeez that's a lot! Probably need more than one post for this.

FilteredMusicFiles

Code: Select all

--// returns only music files compatible with livecode player by default
function FilteredMusicFiles  tfolder
   set the folder to tFolder
   put files() into tFiles
   put "mp3,mp4,avi,aifc,cdda,aif,aiff,caf,m4a,wav,wave,bwf,amr,ac3,au,snd" into supportedFormats
   set itemDel to comma
   repeat for each item T in supportedFormats
      put tFiles into filteredFiles
      filter filteredFiles with "*." & T
      if number of lines of filteredFiles is not 0 then put cr & filteredFiles after finalFileList
   end repeat
   sort finalFileList
   filter finalFileList without empty
   return finalFileList
end FilteredMusicFiles
InitMusic became very long so I'll break it into parts here

---- BEGIN stack script handler InitMusic

Code: Select all

--// load the music which can take some time so is sent first in the message order before InitGame
on InitMusic
   LoadingScreen "show"
   set the layer of button "viewer" to top
A modest start. Then we check if the players have filenames already.

Code: Select all

   --// if the player objects already have songs, don't load files
   put the number of players of this card into nPlayers
   if nPlayers > 0 then 
      put 0 into songCount
      set itemDel to "/"
      if nPlayers > 0 then 
         repeat with p = 1 to nPlayers
            put "Song" & p into playerObjName
            if the filename of player playerObjName is not empty then
               put item -1 of the fileName of player playerObjName into theMusic
               view "",theMusic  
               add 1 to songCount
            end if
         end repeat

Code: Select all

         if songCount = nPlayers then
            InitGame
            exit InitMusic
         end if
      end if
   end if
   
Long winded way of saying:
If the number of players with fileName property filled are the same as the number of players on the stack then InitGame.
view "",TheMusic - clears the lastViewItems variable, then sets the variable to the song name to be viewed in the viewer button.
I think that happens so fast it doesn't matter, but ya never know when you'll accidentally drop a 2 hour movie into the music folder and try to load it.

Code: Select all

   put the folder into oldFolder
   put  stackDirectory() &"/Data/Music" into tMusicFolder
   --// there is no music directory, skip all this
   if there is a folder tMusicFolder then
   
As the comment says if there is no Data/Music directoy all the stuff below will get skipped.
I probably should have don 'if there is not a folder....yada yaday....initGame ; exit InitMusic. what was I thinking?

Code: Select all

      set the folder to tMusicFolder
      put FilteredMusicFiles(tMusicFolder) into musicFiles
      put the number of lines of musicFiles into songCount
      

Get only music files from the folder. The stack was defaulting to Livecode folder when it couldn't find Data/Music.
Now if it doesn't find Data/Music or any any music inside Data/Music it'll skip the rest of the code.

Code: Select all

      if songCount is  not  0 then
         repeat with i = 1 to songCount
            put "Song" & i into playerObjName
            put line i of musicFiles into theMusic
            view "",theMusic  
            put tMusicFolder  &"/" & theMusic into fPath
            
Simply iterate through the files, view their names in the viewer button, expand their names with the folder path.

Code: Select all

            --//make new player objects if needed
            if exists(player playerObjName) is false then
               --// spawning offscreen to avoid the ugliness
               set the left of the templatePlayer to item 1 of CenterScreen()
               set the bottom of the templatePlayer to -20
               create player
               set the name of the last player to playerObjName
               set the layer of the last player to bottom
            end if
            
Originally I had 8 songs, so I had 8 players, but what if you only have one song, or you 20? Loadings songs during the game causes the game to freeze for a bit, so preloading is the only way to go and here we have dynamic player creation to load the songs and keep them at the ready.
the templaterPlayer command was used to spawn the new players offscreen, they are ugly loading in.

Code: Select all

            set the fileName of player playerObjName to fPath
            set the currentTime of player playerObjName to 0
            set the playLoudness of player playerObjName to currentMusicVolume
            --// setting it here, for now, just so we can see that it was made
            set the loc of player  playerObjName to item 1 of CenterScreen(),10
         end repeat
 
basic music player set up, a file to play, the time of the track to start on, the volume, then I pop them back down on the screen while we are developing so we know they were made.

Code: Select all

        
         put -1 into  lastTime 
         put random(songCount) into currentPlayer
         put "Song" & currentPlayer into pName
         start player pName  
      end if
      view "",""
      viewCurrentSong
   end if
   [/url]
   lastTime is the the last instance of currentTime on a player, we initialize it one step behind  so we don't accidentally get the same time every time we check the two values. Here we start the game with a random song from our tracklist. view "" will clear the [i]lastViewItems [/i] variable, and I send another "" empty space just to be sure. Then [b]viewCurrentSong[/b] shows us the name of the songin the [i]viewer [/i]button.
   [code]
   --// finally initialize the actual game
   InitGame
end InitMusic
Finally after that avalanche of code we can get to making mountains, warps and activating enemies.

---END stack script handler InitMusic


I'll move to another post for the rest.
Last edited by xAction on Sat Oct 09, 2021 11:03 am, edited 2 times in total.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 9:52 am

Part 18c. More Music scripts review

ClearPlayers removes the players we spawned in InitMusic, so we can be sure that part of the init is working each time we open the stack then Play card. There's no automatic running of this script, I just paste it into the Message Box before saving and closing my stack.

Code: Select all

on ClearPlayers
   repeat with i = the number of players of this stack  down to 1 step -1
      delete player i of this stack 
   end repeat
   put 0 into songCount
end ClearPlayers
ViewCurrentSong shows the current song in the viewer button label, just like it was it's 1997 all over again.

Code: Select all

on ViewCurrentSong
   if songCount < 1 then exit viewCurrentSong
   put itemDel into oldDel
   set itemDel to "/"
   put "Song" & currentPlayer into pName
   view ("Current Song:"&& item -1 of the fileName of player pName)
   set itemDel to oldDel
end ViewCurrentSong

UpdateMusic will change the song if the currentSong has stalled , ie, reached the end of it's time. This is called in the PlayGame handler.

Code: Select all

--// if the song hasn't progressed in a while, play next one
on UpdateMusic
   if songCount < 1 then exit UpdateMusic
   if the currentCard of this stack is not "Play" then exit UpdateMusic
   put  songCount  into SC
   put "Song" & currentPlayer into pName
   put the currentTime of player pName into cTime
   put cTime into nextTime
   if lastTime is cTime and lastTime > 0 then 
      stop player pName
      set the currentTime of player pName to 0
      put 0 into cTime
      put random(songCount) into currentPlayer
      put "Song" & currentPlayer into newpName
      set the currentTime of player newPname to 0
      put -1 into lastTime
      start player newpName
      put newPName into pName
   end if
   view "",""
   viewCurrentSong
   put nextTime into lastTime 
end UpdateMusic
The relevant code snippet from PlayGame handler looks like this

Code: Select all

--// if the music has stopped at the end of it's track play a new song
   if songCount > 0 then
      add 1 to delayCheck
      if delayCheck> 100 then 
         put 0 into delayCheck
         UpdateMusic
      end if
   end if
Looking at that code I couldn't figure out right away how this works.
So every 100 runs of the PlayGame handler it will signal UpdateMusic which will then check if the lastTime variable and the currenTime property of the player object are the same, if so, then the song has stalled, and a new one is selected. If PlayGame did not delay in checking the lastTime/currentTime difference, there would be no difference at all since it's every 2 milliseconds.

CycleMusic is used to play the tracks in numerical order, I attacked this to the left & right arrow keys, but keyboard input is sporadicly updated.

Code: Select all

on CycleMusic tval
   put  songCount  into SC
   if SC < 1 then exit CycleMusic
   put "Song" & currentPlayer into pName
   put the currentTime of player pName into cTime
   stop player pName
   set the currentTime of player pName to 0
   if currentPlayer+tVal <  1 then put songCount into currentPlayer
   if currentPlayer+tVal > songCount then put 0 into currentPlayer
   add tVal to currentPlayer
   if currentPlayer < 1 then put 1 into currentPlayer
   put "Song" & currentPlayer into newpName
   set the currentTime of player newPname to 0
   put cTime into nextTime
   put -1 into lastTime
   start player newpName
end CycleMusic
RandomSong is called every time the player dies then respawns. Sometimes it's really dramatic.

Code: Select all

--// pick a random song to play
on RandomSong
   if songCount <1 then exit RandomSong
   put  "Song" & currentPlayer into sName
   stop player sName
   put random(songCount) into currentPlayer
   put "Song" & currentPlayer into newPname
   start player newPname
   put -1 into lastTime 
   put 0 into  delayCheck 
   viewCurrentSong
end RandomSong

Okay! That covers music in our game.
Last edited by xAction on Sat Oct 09, 2021 10:17 am, edited 1 time in total.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 10:01 am

Part 18d. Viewer Button, Loading Screen, PlayerLives

Added multi-items functionaity to 'view' handler for tracking multiple events
Added lastViewItems variable so view can handle multiple items

View started out simple, but I wanted it to have functionality, now multiple messages can be viewed in the button label.

Code: Select all

--// view variables in button "viewer"
on view a,b,c,d,e
   if a is empty then put empty into lastViewItems
   put lastViewItems into itemCheck
   Replace comma with cr in itemCheck
   put "" into newViewItems  
   if word 1 to 2 of a is "current song" then filter itemCheck without "Current song*"
   if b is empty then 
      filter itemCheck without a
      put a into newViewItems
   else
      put a,b into newViewItems
   end if
   if c is not empty then  put a,b,c into newViewItems
   if d is not empty then put  a,b,c,d into newViewItems
   if e is not empty then put a,b,c,d,e into newViewItems
   replace cr with comma in itemCheck
   put itemCheck into lastViewItems
   if newViewItems is not empty or newViewItems is not "newViewItems" then
      if lastViewItems is not empty then
         put newViewItems & comma before lastviewItems      
      else
         put newViewItems into lastviewItems
      end if
   end if
   put item 1 to 5 of lastViewItems  into lastViewItems
   put lastViewItems  into viewableItems
   replace comma with "  ||  " in viewableItems
   set the label of button viewer to viewableItems
end view
In essence you can have ahandler sending a message about the current song for general aesthetic pleasantry, and then a level update, and if you're bug hunting you can send it the variable you want to watch and you'll see them all in the button rather than opening the Message Box and having to hunt "put" in the scripts later.

Changed LoadingScreen to handle any message sent and display whatever text it is given
Hmm this is a bit long but instead of breaking it apart I'll just explain it simply
When LoadingScreen is sent a string that is not it's predefined list of two items and is not empty, it'll simply display the text given to it.
I used this for showing the songs that were loading, but that was UGLY.
Then I used to to say "GAME OVER. SCORE XXX"
Could use it to say "LEVEL X" or whatever else.
It also now triggers PauseMenu when sent the PAUSED message
LoadingScreen

Code: Select all

-- // a simple loading screen show with message "show" or hide by default
on LoadingScreen S
   if exists(graphic "loading") is false then
      send  createLoadingScreen to stack (the mainStack of this stack) in 0 milliseconds
      wait 1 second
   end if
   
   lock screen
   set the opaque of graphic "loading" to true
   if s is "PAUSED" then 
      set the opaque of graphic "Loading" to false
      PauseMenu
      end if
      set the label of graphic "loading" to ""
      set the snowName of graphic "loading" to false
      put "0,0,"   & stackDepth() into stackRect 
      set the rect of graphic "loading" to stackRect
      set the loc of graphic loading to CenterScreen()
      set the layer of graphic "loading" to top
      --// handle unique strings
      put "show,Pause" into lsItems
      if s is not empty and s is not among the items of lsItems then
         put s into theString
         put "displayString" into s
      end if

      switch S
         case ""
            hide graphic "loading" 
            break
         case "show" 
            set the label of graphic "loading" to "Loading"
            show graphic "loading"
            break
         case "displayString"
            set the label of graphic "Loading" to theString
            show graphic "Loading"
            break
      end switch
      unlock screen
   end LoadingScreen
I added a bonus life for reaching the warp points, so I removed the part where the lives icons are displayed as active from InitPlayer and gave them their own handler.
PlayerLivesIcons

Code: Select all

on PlayerLivesIcons
   if playerLives > 3 then put 3 into playerLives
   repeat with i = 1 to playerLives
      put "PlayerLives"& i into shipN
      set the foregroundcolor of graphic shipN to Green
      set the blendLevel of graphic shipN to 0
   end repeat
end PlayerLivesIcons
Last edited by xAction on Sat Oct 09, 2021 11:02 am, edited 1 time in total.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 10:53 am

Part 18e. Warps Re-Do
  • Stack 10 changes to warps
    Scripts in WarpPoints are now deprecated, the stack script handles the warps on its own
    Modified InitWarp to resize the warp based on the stack window height,
    Warps now use linesize instead of blendLevel and use player proximity instead change over time for effects
    WarpJump now gives 1000 points and and a bonus life
I was trouble shooting the massive slow down when the player reached the warp group, turned out to be the graphic effects outerGlow, etc.
But while trouble shooting I realized the send message to group 'warpwhatever' was just messy, especially with editing issues in 3 objects whose origin signal comes from the stack.

The first thing I changed was removing the 4 "pin" objects use to maintain the shape of the "WarpPointSource" group after it's copied into "Mgroup" I just put a big invisible rectangle graphic around it called "WarpFrame", I also adjusted the size of the warp dependent on the stack size.
InitWarpPointshas this relevant change:

Code: Select all

--// fix the warp size for the stack size
   put stackDepth() into HW
   put item 2 of HW into SH
   put item 1 of HW into SW
   set the width of graphic "WarpFrame" to SH +200
   set the height of graphic WarpFrame to SW
   set the height of graphic "WarpGate" to SH-200
   set the loc of graphic "WarpFrame" to CenterScreen()
   set the loc of graphic "WarpGate" to CenterScreen()
   set the rect of group "WarpPointSource" to the rect of graphic "WarpFrame"
   set the locklock of group "WarpPointSource" to true
At the end of InitWarpPoints we no longer use blendLevel fading in the warpoint so we have lineSize

Code: Select all

   show group "WarpPointRight"
   --// put warps into closed state
   set the blendLevel of Group "WarpPointLeft" to 0
   set the blendLevel of Group  "WarpPointRight"  to 0
   set the lineSize of graphic "WarpGate"of group "WarpPointLeft" to 0
   set the lineSize of graphic "WarpGate"of group "WarpPointRight" to 0
We set the blendLevel becaus their origin object "WarpPointSource" might have been blendLeveled to some degree during a play session.

In PlayGame after everything else important has run we check for the warps

Code: Select all

  if exists (group "WarpPointLeft" ) or exists (group "WarpPointLeft") then CheckWarp
In CheckWarp we do all the magic. I'll break it apart here.

Code: Select all

--// test if player is within the rect of the warp groups, and activate/deactive the warp if necessary
on CheckWarp
   put the loc of graphic "Player" into playerXY
   put false into warpActivated
   --// if we have warped, fade the warp object
   if the visible of group "WarpPointSource" then
      put true into warpActivated
      fadeWarpPoint
   end if
   
Only WarpPointSource group gets a fade out effect after we've made our jump.
if we've entered the rect of WarpPointLeft ie, by passing its right side boundry then increase the line size of the WarpGate graphic as we get closer to it.

Code: Select all

  
   --// left warp
   if item 1 of playerXY < the right of group "WarpPointLeft" then 
      set the lineSize of graphic "WarpGate" of group "WarpPointLeft" to (abs(item 1 of playerXY -the right of group "WarpPointLeft"))/10
      if  item 1 of playerXY < item 1 of the loc of group "WarpPointLeft"  then WarpJump 
      put true into warpActivated
   end if
   
Likewise for the WarpPointRight, only we pass the left side boundry there.

Code: Select all

   --// right warp
   if item 1 of playerXY > the left of group "WarpPointRight" then 
      set the lineSize of graphic "WarpGate" of group "WarpPointRight" to (abs(item 1 of playerXY -the left of group "WarpPointRight"))/12
      if  item 1 of playerXY > item 1 of the loc of group "WarpPointRight" then WarpJump 
      put true into warpActivated
   end if
end CheckWarp
95 lines of code down 28. Kind of miss the funky graphic effects, but c'est lavie.

warpActivated was used in ScrollLandscape to trigger a speed boost, but A) double speed was barely noticeable with all the lag and B) the speed boost is no longer needed without the lag. Maybe we'll add some sound effects later.

WarpJump now gives 1000 points to player and an extra life, the relevant change is simply:

Code: Select all

  updateScore 1000
   if playerLives < 3 then add 1 to playerLives
   PlayerLivesIcons[/url]

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 11:01 am

Part19. Tutorial Stack 11: Pause Menu, Options Group, Fullscreen, Screen Size, Volume Control
PauseMenuAndOptions.png
NOTE: Old SHMUP Tutorial Stacks removed. Will attach latest and greatest stacks to my first and last posts of thread.
  • Changes in Tutorial Stack 11
    1. Set loading screen to top layer to hide players creation
    2. Set the player object created to bottom layer
    3. During InitMusic set viewer button to top layer to attemp to watch music loading
    4. Set timeOfInit variable to 0 in ResumeGame handler
    5. Added groups "PauseMenuGroup" and "OptionsGroup", added to both cards
    6. Added OptionsGroupSetUp and OptionspApplied handlers to set up the OptionsGroup and to apply the changes made there
    7. Updated UpdateStackSize to resize and refit the mountains group and other screen things after screen options are applied
    8. Added QuitStack substack to the stack for handling the Really want to quit Yes/No? user decision.
    9. Added QuitCommand make sure that stack is closed before closing the mainStack...just in case
    11 Stack has custom proeprties OptionsNewFullScreen, OptionsNewVolume, and NewResolution for OptionspApplied to take action on them.
    12. EscapeKey on title card will show "Options" & "Exit" buttons, with "OptionsGroup" shown if selected
    13. Play mode will leave the options/pause screen and resume game if escape key is pressed as usual.
    14. playLoudness of players is now set by variable currentMusicVolume
    15. added ClearPlayers handler for deleting all the players on a card to test that the InitMusic create players was working each time.
    16. added button "ScreenSizesMenu" as a popup in the OptionsGroup, it's invisible, so just noteing it here since it seems to work like magic.
    17. stack local variable bFullscreen added to keep track of the fullScreen state.
This upload includes GAME_BASE_STACK_v1.livecode, which the readme says:
Contains:
A Title & A Play Card
EscapeKey handles showing PauseMenuGroup
PauseMenuGroup has buttons for: Exiting the game, showing OptionsGroup, and resuming the game
OptionsGroup contains controls for: fullscreen toggle, screen size and volume control
The Exit game button opens "QuitStack" stack which asks "Yes" or "No" to quit
Play card has the "STOP" button to choose "pointer tool" and stop the PlayGame handler, it has a graphic "PlayIndicator" that scrolls across the screen while PlayGame handler repeats.

All the basic necessary local variables and scripts to start a game are inside the stack script. Lots of their guts are commented out. I was going to remove the internals that are unused here but they should serve as a template for the flow of what you need to do to make things happen in your own game, instead of completely winging it from scratch.

Have a look at card scripts to see how some things are trigger as if by magic when switching to card "Play" for instance.

All about the PauseMenuGroup and OptionsGroup in following posts.
Last edited by xAction on Fri Oct 15, 2021 12:02 am, edited 1 time in total.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 1:39 pm

Part 19b. Pause Menu Group
EscapeKey now handles pausing and/or switching to the PauseMenuGroup like so:
I'll break this apart, its a bit long now
EscapeKey

Code: Select all

on EscapeKey
   if the currentCard of this stack is "Title" then
      add 0 to escapeKeyBuffer
      if the visible of group "Title" is true then
         hide group "Title"
         PauseMenu
         exit escapeKey
      end if
     --// escape from options group
      if the visible of group "OptionsGroup" is true then
         hide group "OptionsGroup"
         TitleCardOpened
      end if
       --// escape from Pause Menu Group
      if the visible of group "PauseMenuGroup" is true then
         hide group "OptionsGroup"
         TitleCardOpened
      end if
      exit escapeKey
   end if
First we handle Title card We hide the main Title card group display the PauseMenuGroup which only contains Options and Exit there. If we hit escape key again we pop back to Title group. If we've used "Options" button then we'll leave the OptionsGroup back to the Title group with EscapeKey without setting any changes.

Code: Select all

   add 1 to escapeKeyBuffer
   switch escapeKeyBuffer
      case 1
      case 2
         if bPaused is true then
            if the mouseLoc is within the rect of this card then
               put false into bPaused
               put 0 into outsideStackTime
               set the tool to "browse tool"
               loadingscreen
               ResumeGame
            end if
         else
            put true into bPaused
         end if
         break
      case 3
         set the tool to "pointer tool"
         put 0 into escapeKeyBuffer
         break
   end switch
end EscapeKey
Hide loadingScreen and ResumeGame if game is paused, else set bPaused to true. PlayGame will handle sending PauseGame in that case. ResumeGame handles cleaning up the group visibility if OptionsGroup or PauseMenuGroup are visible.

LoadingScreen is triggering visibility of PauseMenuGroup right now, I'll probably move that to PauseGame though.
I was going to use LoadingScreen to say "Options" but then the buttons were placed awkwardly so it worked out differently
We'll just assume I move in the next stack for now.

PauseMenu handler looks like so

Code: Select all

on PauseMenu
   set the loc of group "PauseMenuGroup" to CenterScreen()
   set the bottom of group "PauseMenuGroup" to item 2 of CenterScreen()+20
   set the layer of group "PauseMenuGroup" to top
   show group "PauseMenuGroup"
end PauseMenu
easy peasy

On the Play card the PauseMenuGroup has "Options", "Resume", "Exit" buttons
Resume calls ResumeGame that looks like this now.

Code: Select all

--// return to the game
on ResumeGame
   set the tool to "browse tool"
   hide group "OptionsGroup"
   hide group "PauseMenuGroup"
   put false into bPaused
   put 0 into escapeKeyBuffer
   LoadingScreen
   --// reactivate the objects that were active before pause
   put activeEnemies & cr & activeBullets &cr  & activeMissiles into activeObjects 
   put cr & "explosion1" & cr & "explosion2"  & cr & "explosion3"  after activeObjects
   filter activeObjects without empty
   repeat  for each line AO in activeObjects
      if exists(graphic AO) then send "ResumeActivity" to graphic AO
   end repeat
   put the seconds into timeOfInit
   send PlayGame to stack (the mainStack of this stack) in 10 milliseconds
end ResumeGame
The major change is that it hides our two new groups.

The "Exit" button opens stack "QuitStack", which resizes to the size of our mainStack, and asks if we really want to quit, yes or no.
If not, then it just closes and we're back at the PauseMenuGroup.
If yes, it sends QuitCommand to the mainStack, closes it'self and then the mainStack and Livecode quit.
I left the scripts in the buttons

Exit button script MouseUp on either card of the tutorial stack

Code: Select all

on mouseUP
   set the outerGlow["spread"] of me to 50
   set the outerGlow["size"] of me to 15
   set the rect of stack "quitStack" to the rect of this stack
   set the loc of group "quitGroup" of stack "QuitStack" to CenterScreen()
   open stack "quitStack"
end mouseUP
MouseUp of button "Yes" of stack "QuitStack"

Code: Select all

on mouseUP
   set the outerGlow["spread"] of me to 5
   set the outerGlow["size"] of me to 15
   send QuitCommand to stack the mainStack of this stack in 200 milliseconds
end mouseUP
I'll cover the Options button and OptionsGroup in next post.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 2:17 pm

Part 19b. Options Button of PauseMenuGroup, the OptionsGroup and associated handlers, variables and custom properties

Pressing Options button of PauseMenuGroup triggers:

OptionsGroupSetUp -- which ill dissect

Code: Select all

on OptionsGroupSetUp
   --// screen size options
   put  screenRect() into fullscreenSize
   repeat with i = 1 to 3 step .5
      put floor(item 3 of fullScreenSize /i) into wOpt
      put floor(item 4 of fullScreenSize /i) into hOpt
      put wOpt & " x " & hOpt & cr after screenOptions
   end repeat
   set the text of button "ScreenSizesMenu" to line 1 to -1 of  screenOptions
   set the label of button "ScreenSizesMenu" to "Screen Size"
   put stackDepth() into curScreenSize
   replace comma with " x " in curScreenSize
   put curScreenSize into field "ScreenSizeValueLabel"
   
First we get the actual size of our computer screen, then we divide that by 1.5, 2, 2.5, and 3 to get a few additonal screen options in the same ratio as our monitor. This seemed a more rational option than the crazy list of incompatible screen sizes some games give. I wanted to be able to set the playable rectangle smaller than the actual display with a big black border but that's a feature for another day.

Code: Select all

   --// fullscreen
   set the hilite of button "fullScreenToggle" to bFullScreen
   
Doesn't get any easier than that. bFullscreen is set to false when the stack loads, and udpated by the OptionsGroup

Code: Select all

   --// music volume
   put currentMusicVolume into vVal
   put the left of graphic "VolumeSlider"+vVAl into volX
   set the loc of graphic "VolumeSliderRectangle" to  volX,item 2 the loc of graphic  "VolumeSlider"
   put vVal into field "MusicVolumeValue"
   set the loc of group "optionsGroup" to CenterScreen()
   set the layer of group "optionsGroup" to top
   loadingScreen 
   show group "OptionsGroup"
end OptionsGroupSetUp
----- END of OptionsGroupSetup handler
I made a custom slider for the OptionsGroup, so here we set it by getting currentMusicVolume which is defaulted to 75 in InitMusic
The volume is simply displayed by the distance from the left along a 100 point long line, and a little rectangle is placed there and of course a numerical value in a field for people who don't speak slider.

All that happens in millisecond and the options group is open. Theres the label fields for Fullscreen, Screen Size, and Music Volume.
Then fullScreenToggle button, "ScreenSizesMenu" button which is a popup menu that is invisble untl the screen size fields are clicked
Finally my custome volume slider and the Apply button.

Let's look at the script yeah, I spent whole minutes on them!
Script of button "fullScreenToggle"

Code: Select all

on mouseUp
   set the hilite of me to not the hilite of me
   set the OptionsNewFullScreen of stack (the mainStack of this stack) to the hilite of me
end mouseUp
Script of button "ScreenSizeMenu"

Code: Select all

on menuPick mPick
   put mPick into field "ScreenSizeValueLabel"
   replace " x "  with  comma in mPick
   set the label of me to "Screen Size"
   set the newResolution of stack (the mainStack of this stack) to  mPick
end menuPick
Script of graphic "VolumeSliderRectangle" of group "OptionsGroup" gets complicated

Code: Select all

local vsY, vsL, vsR
on mouseDown
   put the loc of graphic "VolumeSlider" into vsLoc
   put item 2 of vsLoc into vsY
   put the left of graphic "VolumeSlider"+1 into vsL
   put the right of graphic "VolumeSlider"+1 into vsR
   put item 1 of the mouseLoc into moX
   set the loc of me to moX,vsY
   dragMove
end mouseDown

on dragMove
   put item 1 of the mouseLoc into moX
   if Mox > VSL + 100 then put VSL + 100 into MoX
   if MoX < VSL then put VSL into MoX
   set the loc of me to moX,vsY
   put  abs(item 1 the loc of me - vsL) into vVal
   if vVal > 100 then put 100 into vVal
   put vVal into field "MusicVolumeValue"
    set the OptionsNewVolume of stack (the mainStack of this stack) to vVal
   if the mouse is down then send dragMove to me in 2 milliseconds
end dragMove
Finally the Apply button.

Code: Select all

on Mouseup
   Hide group "OptionsGroup"
OptionsApplied
end Mouseup
But wait, there's more!


-----BEGIN stack handler OptionsApplied

Code: Select all

on OptionsApplied
   --// fullscreen
   put the OptionsNewFullScreen of stack (the mainStack of this stack) into bFullscreen
   set the fullScreen of this stack to bFullscreen
Full screen just like that

[/code]
--// window size
put the newResolution of stack (the mainStack of this stack) into newRez
if newrez is not empty then
set the width of this stack to item 1 of newRez
set the height of this stack to item 2 of newRez
end if
if bFullScreen is false then set the loc of stack (the mainStack of this stack) to item 3 of screenRect()/2,item 4 of screenRect()/2
[/code]
Resize the window, set it to the center of the screen if we're not in fullscreen mode.

Code: Select all

   --// music volume
   put the OptionsNewVolume of stack (the mainStack of this stack)into vVal
   put the number of players of this stack into nPlayers
   repeat with i = 1 to nPlayers
      set the playLoudness of player i of this stack to vVal
   end repeat
   put vVal into currentMusicVolume
   
Iterate over all the music players on the stack, set their playLoudness to the given value store that value in currenMusicVolume

Code: Select all

   send UpdateStackSIze to stack (The mainStack of this stack ) in 300 milliseconds
Update everything on the stack that has some size of stack dependency, like the main "MGroup" with our mountain in it.
The Developer groups and the Viewer button. I think I have to resize the warps too....later.

Code: Select all

   if the currentCard of this stack is "Title" then
      TitleCardOpened
   end if
   
if we are on the Title card act as if the card just opened. Clean up and play, center things and play music.

Code: Select all

   --// resume or init for play card
   if the currentCard of this stack is "Play" then
      if inProgress is true then 
         ResumeGame
      else
         InitMusic
      end if
   end if
end OptionsApplied
if we are on the Play card and inProgress is set to true, then resume game, if by some chance we are there without Inprogress having been set true, then InitMusic to begin initializing everything from scratch on the Play card.

And that's it! Days ago it seemed like it would be some difficult thing, and it was very cut and dried standard stack development stuff.
What's left, stars, background mountains? Stam says has low frame rate. Maybe more options to turn off all the extra graphic effects, disable antialiasing?
Probably should do that before adding more decorations.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sat Oct 09, 2021 6:02 pm

Part 20. Tutorial Stack 12: Anti-Aliasing & Graphic Effects Toggles in Options Group
Stack12AAandFX.png
Stack12AAandFX.png (11.64 KiB) Viewed 7052 times
NOTE: Old SHMUP Tutorial Stacks removed. Will attach latest and greatest stacks to my first and last posts of thread.
  • Tutorial Stack 12 Changes
    1. Added local variables bAntiAlias, bUseEffects, bEffectStatesStored
    2. Added buttons "AntiAliasToggle" & "UseEffectsToggle" in OptionsGroup
    3. Added commands in OptionsGroupSetUp to match the buttons with the bool variables
    4. Added UseAntialiasBool, UseEffectsBool custom Property to hold the state of the OptionsGroup pick
    5. OptionsApplied handler acts on above to set the boolean variables, then sends:
    6. UpdateAntiAlias to toggle AntiAlias and UpdateEffectStates to toggle effects on graphics
    7. Added StoreEffectStates to store the effect states of all objects as custom property EffectStates in the obj
    8. Added IndexEffectsValues to sift through the objects properties for the effects and their keys
It works! I'll explain it later. I'm beat.

--// local variables introduced in stack 12

Code: Select all

local bAntiAlias=true
local bUseEffects=true
local bEffectStatesStored=false
Additions to OptionsGroupSetUp handler in stack script

Code: Select all

 --// antialias
   set the hilite of button "AntiAliasToggle" to bAntiAlias
   --// use effects
   set the hilite of button "UseEffectsToggle" to bUseEffects
The script of button "AntiAliasToggle"

Code: Select all

on mouseUp
   set the hilite of me to not the hilite of me
   set the UseAntialiasBool of stack (the mainStack of this stack) to the hilite of me
end mouseUp
The script of button "UseEffectsToggle"

Code: Select all

on mouseUp
   set the hilite of me to not the hilite of me
   set the UseEffectsBool of stack (the mainStack of this stack) to the hilite of me
end mouseUp
Update to OptionsApplied handler in stack script

Code: Select all

put the UseAntialiasBool of stack (the mainStack of this stack) into bAntiAlias
   UpdateAntiAlias
   put the UseEffectsBool of stack (the mainStack of this stack) into bUseEffects
   UpdateEffectStates
UpdateAntiAlias

Code: Select all

on UpdateAntiAlias
   put the number of graphics of this stack into NUC
   repeat with i = 1 to NUC
      put the name of graphic i of this stack into tContr
      set the Antialiased of graphic i to bAntiAlias
   end repeat
end UpdateAntiAlias
Well that was super easy, everything should be done in a minute right?
The effects panel has a bunch of toggle boxes, so it should be as easy as:
EffectsPane.png
EffectsPane.png (11.9 KiB) Viewed 6979 times

Code: Select all

repeat with N = 1 to  the number of graphics of this stack 
set the outerGlow of graphic N to bUseEffects
end repeat
Well that works for false, but not for true. and if you use
"put the outerGlow of graphic whatever" you don't get false if its false, you get nothing

One of those "one more thing before bed...well I'll sleep when I'm dead" things.

So I had to find a way to extract all th effects data from the objects and store them for when bUseEffects is set back to true.
Easy enough at first, until I introducd a typo then I had to look through this spaghetti to find it.
I'll break this down into parts, it was a lot worse the first go.

------BEGIN UpdateEffectStates

Code: Select all

on UpdateEffectStates
   put "outerGlow,innerShadow,colorOverlay,innerGlow,dropShadow" into theEffects
   put the number of graphics of this stack into NUC
   
The easy part create a list of the effects, get the current number of graphics, I'll probably have to update this for all controls,

Code: Select all

   if bUseEffects is false then
      repeat with i = 1 to NUC
         if bEffectStatesStored is false then
            StoreEffectStates
         end if
         repeat for each item e in theEffects
            do "set the" && e && "of graphic i to false"
         end repeat
      end repeat         
   end if
   
First lets check if we've disabled the effects, because if we don't store all the current effects before turning them off, we won't have access to their data when we turn them back on. StoreEffectStates will handle the storage part for us. I had that as two big ugly loops in this handler at first, what a mess. Finally after storing the current state of the effects we can just switch them off.

Code: Select all

   if bUseEffects is true then
      repeat with i = 1 to NUC
         put the short name of graphic i  into tObject
         put the EffectStates of graphic i  into stateList 
We need to check that we've store effect states, before bUseEffects toggled to false is applied to the objects, this might be the first time the OptionsGroup Apply button was pressed

Code: Select all

         if stateList is empty or number of lines of statesList is 0 then
            put IndexEffectsValues(tObject) into tEffectstates
            set the EffectStates of graphic 1 to tEffectstates
         end if
   
if the custom property EffectStates of the object is empty we get the values of the objects effects via IndexEffectsValues function and store them in the custom property.

Code: Select all

         if  number of lines of statesList  > 0 then
            set itemDel to "="
            repeat for each line SL in stateList
               do "set the" && item 1 of SL && "of graphic i to"&& item 2 of SL 
            end repeat
         end if
      end repeat   
   end if
end UpdateEffectStates
   
Finally if we have any data in about the effects then we set the effects according to that data. If we had to collect the data for the first time we don't touch the object.. we're turning stuff on at this point anyway so if it wasn't on and stored in the part we turned them off we don't need to meddle with it's current state.
------END UpdateEffectStates

StoreEffectStates looks deceptively easy.

Code: Select all

on StoreEffectStates
   put the number of graphics of this stack into NUC
   repeat with i = 1 to NUC
      put  the short name of graphic i  into tObject
      put IndexEffectsValues(tObject) into tStates
      set the EffectStates of graphic i to tStates
   end repeat
   put true into bEffectStatesStored
end StoreEffectStates
Just iterate through the objects, pass their name to the function, get the returned value set custom property of the object to retrive the data later.

HAHA, but then there's
---------------- BEGIN IndexEffectsValues ---------------
Oh huh..it's so much smaller after I've slept, but ill break down because its really three parts

Code: Select all

function IndexEffectsValues aName
   put "outerGlow,innerShadow,colorOverlay,innerGlow,dropShadow" into theE
   put "graphic" && quote & aName & quote into tObject
I had a ton of trouble passing the function (graphic "whatever"), it kept saying "not a container" but maybe that was the typo throwing the bug.
So I just passed it the short name, settled on only cycling through graphic objects and encapsualted it with quotes here in this function.

Code: Select all

   --//Puts
   repeat for each item E in theE
      do "put the" && E && "of" &&  tObject &&"into tProp"
      put the keys of tProp into tKeys
      if tKeys is not empty then
         repeat for each line K in tKeys
            put "put the" && E &"[" & quote & K & quote &"] of" && tObject && "into AVal" && cr after allKeys
         end repeat
      end if
   end repeat
   --return allKEys
First we get a body of text that looks like this, for each object and all their effects
put the outerGlow["size"] of graphic "test" into AVal
put the outerGlow["color"] of graphic "test" into AVal
put the outerGlow["filter"] of graphic "test" into AVal
etc etc
Then we convert that a into a property setting script because that was my first instinct to see if I could make this process work.

Code: Select all

   --//Sets
   repeat for each line A in allKeys 
      do A
      put A into B
      replace "put" with "Set" in B
      replace "into AVal" with "to" && AVal in B
      put B & cr after Cs
   end repeat
   --return cs
We get a body of text for ever object and for all their effects that looks like this:
Set the outerGlow["size"] of graphic "test" to 3
Set the outerGlow["color"] of graphic "test" to 0,0,255
Set the outerGlow["filter"] of graphic "test" to box3pass
Finally we reduce that down to something we can retrieve and easily reuse later. Hardest part here was picking an item delimeter, I settled on the good equals sign.

Code: Select all

   --//short hand
   repeat for each line D in Cs
      put word 3 of D & "=" & Word -1 of D  & cr After EF
   end repeat
   return EF
end IndexEffectsValues
---------------- END IndexEffectsValues ---------------
That returns text like this:
outerGlow["size"]=3
outerGlow["color"]=0,0,255
outerGlow["filter"]=box3pass
outerGlow["opacity"]=255
outerGlow["spread"]=0
outerGlow["blendmode"]=normal
outerGlow["range"]=255
We slap that in the custom property of the object and we can reset the properties later, easy peasy....back in UpdateEffectStates
essentially with this:

Code: Select all

 set itemDel to "="
            repeat for each line SL in stateList
               do "set the" && item 1 of SL && "of graphic i to"&& item 2 of SL 
            end repeat
Zippity zappity the effects are backity!

That was a lot more fun after some sleep. What next? Background mountains? Stars?...more coffee.
Last edited by xAction on Fri Oct 15, 2021 12:05 am, edited 1 time in total.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sun Oct 10, 2021 10:16 am

Part 21. Tutorial Stack 13: Move Any Mountain, Mostly
Adding those background mountains was a nightmare. I don't comprehend how the groups work at all.
I set everything to center screen, I lock the group rect, I give the mountains an offset from the center. NOPE!
Finally I centered a rectangle graphic "'backframe" which seemed to help somewhat They are still not optimally placed, but I got tired of fighting.
BackGroup.png
NOTE: Old SHMUP Tutorial Stacks removed. Will attach latest and greatest stacks to my first and last posts of thread.
  • Changes to Tutorial Stack 13
    1. Background mountains added to game
    2. InitBackgroundMountains handler produces two mountains "MidGround" & "Background" & a "BackFrame" graphic
    3. GroupMulti handler added to group multiple object takes arguments group name, object type and a string list of items
    4. Function Quoted added to avoid typing quote & thing & quote all the time
    5. ResetBackgroundGroup was added to manage the background mountain group
    6. All background group scrolling actions in ScrollLandscape are enabled
    7. Setup of sizes of warp group in InitWarpPoints has been extracted to WarpsToScreenHeight
    8. WarpsToScreenHeight is called when the stack size is updated to resize the warp
    9. Changed the screen size step in OptionsGroupSetUp to .2 more screen size options while maintaining ratio
    10. Changed the static values of scrollSpeedMult in ScrollLanscape to floor(item 1 of stackDepth()/90)
    11. Added subtle altiude adjustment viewpoint changes to background group
    12 Added bUseBackMountains variable and USeBackMountains stack custom property to go with BackMountainsToggle button Options Group
    13 Added condition to ScrollLandscape to skip parralax scroll of background if bUseBackMountains is false
    14 Added condition in WarpJump & UpDateStackSize to skip background mountain stuff if bool is false too
    14. Added condition to exit CheckCollisions if cheatMode was true
    15. Added statements to hide OptionsGroup and PauseMenuGroup to InitGame
We begin our (background) mountain trek in InitGame where we use these commands:

Code: Select all

--// generate the background mountains
   InitBackgroundMountains
   if exists(group "Backgroup") then ResetBackgroundGroup
   if exists(group "Backgroup") then set the layer of group "Backgroup" to bottom
------ BEGIN initBackgroundMountains stack handler

Code: Select all

--//create background mountains group
on InitBackgroundMountains
   if exists(graphic "BackFrame") then delete graphic "BackFrame"
   if exists(graphic "midGround") then delete graphic "midGround"
   if exists(graphic "Background") then delete graphic "Background"
   if exists(group "Backgroup") then delete group "Backgroup"
First we clear out any old mountain stuff we had made previously.

Code: Select all

   --//Create the back frame to control the group ...for some reason
   create graphic
   set the style of the last graphic to "Rectangle"
   set the lineSize of the last graphic to 0
   set the opaque of the last graphic to false
   set the backgroundCOlor of the last graphic to white
   set the name of the last graphic to "BackFrame"
   
M guess is the backframe object gives us a consistant size for the group? I don't know, I couldn't position my mountains correctly without it, they didnt go where I told them to at all.

Code: Select all

   --//background mountains #1
   InitMountains
   set the name of the last graphic to "MidGround"
   set the bottom of graphic "MidGround" to the top of graphic "mountains"
   --//more blue as we go back in virtual space
   put random(128)+128 into mColor
   set the foregroundColor of the last graphic to 0,255,mColor
   set the outerGlow["color"] of the last graphic to 0,255,mColor
   set the blendLevel of the last graphic  to 30
We run InitMountains the handler that made our front most mountains, it leaves us with a loose graphic on the map with no name so we can name it here, a little fussing with the colors for a sense of distance.

Code: Select all

   --//background mountains #2
   InitMountains
   set the name of the last graphic to "Background"
   set the bottom of graphic "midGround" to the top of graphic "midGround"
   --//more blue and less visible as we go back in virtual space
   put random(128)+128 into mColor
   set the foregroundColor of the last graphic to 128,128,mColor
   set the outerGlow["color"] of the last graphic to 128,128,mColor
   set the blendLevel of the last graphic  to 40
   --// more haze
   set the outerGlow["size"] of the last graphic to 25
   set the outerGlow["spread"] of the last graphic to 83
   
Same as above, less bold colors, fuzzier.

Code: Select all

   GroupMulti "Backgroup","graphic","BackFrame,MidGround,Background""
  end InitBackgroundMountains
   
Groups the item list objects of the type given in a group named "Backgroup"
------ END initBackgroundMountains stack handler[/b]

GroupMulti is very simple

Code: Select all

--// group multiple items
on GroupMulti groupName,objType,tItemString
   repeat for each item i in tItemString
      put objtype && quoted(i) && "and"& space after tGroupString
   end repeat
   do ("Group" && word 1 to -2 of tGroupString)
   set the name of it to groupName
end GroupMulti
it used the function Quoted

Code: Select all

--// encapsulate strings in quotes
function quoted tWords
   return quote & tWords & quote
end quoted
The background mountain group command would look like this without those two handlers:

Code: Select all

put "graphic" && quote & "BackFrame" & quote && "and" && "graphic" && quote & "Midground" & quote && "and" && "graphic" && quote & "Background" & quote  into tGroupobjs
do ("group" && tGroupobjs)
Yuck

Once the mountains are made we have sure they fit our screen in a way we need them for scrolling.
That's wehre ResetBackgroundGroup comes in.
---- BEGIN ResetBackgroundGroup stack handler -----

Code: Select all

on ResetBackgroundGroup
   put "0,0," & StackDepth() into stackRect
   put CenterScreen() into CSxy
   set the rect of group "Backgroup" to stackRect
   set the lockLoc of group "Backgroup" to true
The mountain graphics are 140000+ points long, and the group will be too if you don't lock it down.

Code: Select all

   set the radioBehavior of group "Backgroup" to false
   set the traversalOn of group "Backgroup" to false
   set the threeD of group "Backgroup" to false
   put the height of graphic "MidGround" into MH
   set the hScrollbar of group "Backgroup" to true
   set the scrollBarwidth of group "Backgroup" to 0
   [/group]
   Turn of and/or reduce all the usual group properties, we just want a box whose contents can be shifted around via scripts
   
   [code]
   set the loc of graphic "Background" to CSxy
   add 180 to item 2 of CSXy
   set the loc of graphic "MidGround" to CSxy
   put the width of graphic "Background" into hW
   set the hScroll of group "Backgroup" to hW/2
   subtract 100 from item 2 of CSxy
   
Put the highest mountain "Background" at Center Screen. Drop the middle mountain 180 pixels below it.
Set the hScroll of the group to the middle of the mountain.
Hmm not sure why I only subtract 100 from the Y coordinate before setting the backFrame...might be a left over bit.
I'll be messing with it again soon enough, until my mountains are Bob Ross perfect.

Code: Select all

   --// backframe helps to position everything else, for whatever reason
   set the rect of graphic "BackFrame" to  stackRect
   set the loc of graphic "BackFrame" to  CSxy
   set the lockloc of graphic "BackFrame" to true
end ResetBackgroundGroup
Finally set the backframe to the center of the screen, at the screen size. Placing it at any other point in the script had unwanted results with the mountain positions. Moving mountains is a test of will power, math skills and coding sorcery.
---- END ResetBackgroundGroup stack handler ----
Last edited by xAction on Sun Oct 31, 2021 1:22 am, edited 3 times in total.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sun Oct 10, 2021 11:35 am

Part 21b. More Mountain Magic.

Okay our mountains are all set up. Once we start flying around ScrollLandscape takes care of moving the mountain a slower speed than the foreground for that parallax effect all the kids rave about. The relevant code has been sitting commented in our stacks this whole time.

Code: Select all

--// parallax background moves every other cycle
   put 1 into cycleDelay
   if lastParallax < cycleDelay then 
      add 1 to lastParallax
   else
      put 0 into lastParallax
      put the hscroll of group "Backgroup" into pHS
      if flyDir is "right" then add 1*1.5 to pHS
      if flyDir is "left" then add -1*1.5 to pHS
      set the hscroll of group "BackGroup" to pHS
   end if
  
Every other run of ScrollLandScape the background will move. lastParallaxis just a value of one or zero, anything more causes stuttering. Cycledelay as used to test optional numbers, it'll probably get recycled somewhere else eventually.

I added additional effect where if you the graphic "player" is higher in the scene the background mountains dip down, and if it's in the front the mountains rise up a little.

Code: Select all

   --// psuedo altitude adjustment effect
   put the loc of  graphic "Player" into playerXY
   put item 1 of the the loc of  graphic "background" into scrbaX
   put item 1 of the  the loc of graphic "MidGround" into scrmidX
   put item 2 of playerXY-item 2 of csXy into centerDelta
   put item 2 of the originxy of  graphic "background" into bacY
   put item 2 of  the originxy of graphic "MidGround" into midY
   add floor(centerDelta*.05)*-1 to midY
   set the loc of graphic "MidGround" to scrmidX,midY
   add floor(centerDelta*.085)*-1 to bacY
   set the loc of graphic "background" to scrbaX,bacy
We find the difference betweenn the player and the center of the screen, and then apply a percentage of that to the original Y location of the mountains, while maintaing the ever udpating X location that is being shifted around by the hScroll of the group. I knew originXY custom property would come in handy again.

All this scrolling action is now encapsulated by the conditional

Code: Select all

if bUseBackMountains is true then
--//scrolling stuff
endif
Because we can turn the mountains off in the OptiosnGroup now
Zippity Blippity Mountains Go or Mountains Show.
OptionsGroupStack13.png

I also saw a need to change the speed we scroll the foreground group.
If you go to full screen it feels like you slow down. Our initial scroll speed was...6
Now we get speed via this calcuation floor(item 1 of stackDepth()/90)
So if your screen width is 1200 the scrolling speed is 13.
If your screen width is1 600 the speed is 17

A note about or scrolling mountain length, it's only 200 individual points on a line long that juts forward randomly from 2 to a 150 points each increment.
Which generally results in a mountain that is 140000+ pixels long. I theory on 32bit computers we can go to 65,000 something? It's just then testing things like the warps could take six times longer. Once the game is 99.99% complate bumping the length of the mountains can be tested, which will bring openings for more creative uses of all that space.

When I return we''ll look at some changes to the warp sizing.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sun Oct 10, 2021 8:37 pm

Part 21b. Big Warp, Little Warp
Now that we have dynamic window sizing I we need to adjust the Warp size to fit.
I extracted the warp size setting in InitWarpPoints and gave it a new home since we'll need to use it when we resize the window, which is managed by UpDateStackSize

WarpsToScreenHeight

Code: Select all

--// set heights at init & adjust warps if we changed screen height
on WarpsToScreenHeight
   --// fix the warp size for the stack size
   put stackDepth() into HW
   put item 2 of HW into SH
   put item 1 of HW into SW
   put "Source,Left,Right" into tItems
   repeat for each item W in tItems
      put "WarpPoint"& W  into tWarpgroup
      set the width of graphic "WarpFrame" of group tWarpGroup to SH +200
      set the height of graphic "WarpFrame" of group tWarpGroup  to SW
      set the height of graphic "WarpGate" of group tWarpGroup  to SH-200
      put the loc of graphic "WarpFrame" of group tWarpGroup  into wXY
      put item 2 of CenterScreen() into item 2 of wXy
      
      set the loc of graphic "WarpFrame" of group tWarpGroup  to wXy
      set the loc of graphic "WarpGate" of group tWarpGroup  to wXy
      set the rect of group tWarpgroup to the rect of graphic "WarpFrame" of group tWarpGroup
      set the bottom of group tWarpGroup to the bottom of group "MGroup"
      set the bottom of graphic "WarpGate" of group tWarpGroup  to top of graphic "Mountains"
      set the lockloc of group tWarpgroup to true
   end repeat
end WarpsToScreenHeight
Since its a lot of repititious action on 3 differen objectst I just interate over the unique parts of their names.
Later after we WarpJump this is used again to set the WarpPointSource in the right spot.

xAction
Posts: 86
Joined: Sun Oct 03, 2021 4:14 am

Re: LiveCode Game Tutorial: Side Scrolling Shoot'em'up

Post by xAction » Sun Oct 10, 2021 8:40 pm

Part 22. Tutorial Stack 14. Bug Hunt

NOTE: Old SHMUP Tutorial Stacks removed. Will attach latest and greatest stacks to my first and last posts of thread.

I'll put all the changes up front. The blow by blow is below and second post
  • Tutorial Stack 14
    1. changed PlayerMove handling of the X mouse position, 20 pixel change = change in ship direction
    2. Warps now ping pong the player, flipping their direction on exit
    3. Fixed OptionsApplied to work on Title Card without throwing a bug
    4. Copied "Viewer" button to Title card
    5. Extended the mountain length by 50 units per point to guarantee scroll space in the group
    6. Fixed UpdateStackSize to run all the updates instead of skipping over them in some cases
    7. Removed storing EffectStates in UpdateEffectStates
    8. Added StoreEffectStates to InitGame
    9. Fixed OptionsApplied to hide/show the group "Backgroup" when bUseBackMountains is set false in Title screen before it's made
    10. Removed a bunch of 'unlock screens' PlayGame handles most of them
    11. Hid OptionsGroup and PauseMenuGroup in via Play Card script
    12. Moved PauseMenu out of LoadingScreen and into PauseGame
    13. Added on RawkeyDown to try to capture the EscapeKey better but it's still sporadic
    14. Added ExitTitleCard before it called TitleGlow when using OptionsGroup and/or PauseMenuGroup
    15. Added DistToScreen to create the bullet speeds, replacing the random number generator
    16. Removed Excessive lines of LERP code in enemy bullets, replace with simple vector calculation
    17. Added Midpoint, Endpoint, and RotatePoints functions to stack script


I think that's it for the basic game ya? Lets playtest a few hours and see.
And just two seconds to find a bug.

Code: Select all

 if bUseBackMountains is false is true then
      hide group "Backgroup"
   else
      show group "BackGroup"
   end if
Ain't no group "BackGroup" on the Title card.

If options are set in the "Title" card we need to allow screen size, full screen, music volume, and then exit OptionsApplied if we are not on card "Play"


So right after we set the music volume variable currentMusicVolume we'll exit the handler
Ah have to move the UpdateStackSIze up the message path where it can be triggered as well

Code: Select all

  
   send UpdateStackSIze to stack (The mainStack of this stack ) in 300 milliseconds
   if the currentCard of this stack is "Title" then
      TitleCardOpened
      exit OptionsApplied
   end if
Since we can set options at the "Title" card we'll need to revist them in InitGame to have the game match the choise we made, right before we set the timeOfInit so we effect all the graphics that made have been created and give the player precious milliseconds of game time before the difficulity level increases.

Back to the bug hunt.

Okayaaa, Because the WarpPoints are set initially along the points of the mountain and the points of the mountain are incremented in steps from 2 to 150 , there can be a condition where we can't scroll the group far enough to reach the warp. Let's try making the mountains bigger.

Code: Select all

 put 50+random(50)+random(100)into nextX
Okay that seems to work fine, and the forerground mountains become more lumpy hillside which is pleasant.

Now why is the viewer button floating in the middle of the screen? TYPO!
set the bottom of button "Viewer" to item 2 of of HW ---oops
Nope, not just that.
Because UpDateStackSize is being called on the Title screen then
lastRect is being set to the rect of the window before "viewer" card is drawn and then

Code: Select all

   put  lastRect  into lRect
  if cRect is not lRect then
Fails to run all the various Play card updates

So last track the card with a new variable lastCard and only modify it in UpdateStackSize

Fixed. With some extra adjustments so viewer card is as wide as the window and centered.
UpDateStackSize needed to be called again in InitGame so changes made on the "Title" card were applied on the "Play" card.
I supposed I could have added a bunch of "Of card "Play" to ever statement...bleh.

Code: Select all

--// if the stack size has changed adjust various game elements to center screen
on UpDateStackSize
   put the rect of stack (the mainStack of this stack) into cRect
   put  lastRect  into lRect
   put the currentCard of this stack into curCard
   lock screen
   if cRect is not lRect or lastCard is not curCard then
      put stackDepth() into HW
      put CenterScreen() into CSxy
      
      set the width of button "Viewer" to item 2 of HW
      set the loc of button "Viewer" to CSxy
      set the width of button "Viewer" to item 1 of HW
      set the left of button "Viewer" to 0
      set the bottom of button "Viewer" to item 2 of  HW
      
      if the currentCard of this stack is "Play" then
         ResetForegroundGroup
         if bUseBackMountains is true then ResetBackgroundGroup
         set the loc of graphic "player" to CSxy
         set the loc of graphic "sMultBox" to CSxy
         set the bottom of group "Developer" to item 2 of of HW
         set the right of group "DevControlButtonGroup" to item 1 of HW
         set the layer of group "DevControlButtonGroup" to top
         --// fix the warps
         WarpsToScreenHeight
      end if
      put cRect into  lastRect
      put curCard into lastCard 
   end if
   end UpDateStackSize
Oh I was afraid of this. The effects aren't being stored or are being overwritten empty.... lets see now.
Okay All saving of effect states via a call to StoreEffectStates is handled at InitGame

UpdateEffectStates is only works with prestored values and doesn't alter those.

Code: Select all

on UpdateEffectStates
   put "outerGlow,innerShadow,colorOverlay,innerGlow,dropShadow" into theEffects
   put the number of graphics of this stack into NUC
   if bUseEffects is false then
      repeat with i = 1 to NUC
         repeat for each item e in theEffects
            do "set the" && e && "of graphic i to false"
         end repeat
      end repeat         
   end if
   if bUseEffects is true then
      repeat with i = 1 to NUC
         put the short name of graphic i  into tObject
         put the EffectStates of graphic i  into stateList 
         if  number of lines of statesList  > 0 then
            set itemDel to "="
            repeat for each line SL in stateList
               do "set the" && item 1 of SL && "of graphic i to"&& item 2 of SL 
            end repeat
         end if
      end repeat   
   end if
end UpdateEffectStates
OptionsApplied was checking that we had entered PlayGame and set "inProgress" to true in order to show or hide the background mountains. That was to ensure we'd actually created the mountains before trying to manipulate their visibility.
But it caused the mountains to be shown even if they were turned off on the Title screen options. This should fix things

Code: Select all

      if bUseBackMountains is false and exists (group "Backgroup") then
         hide group "Backgroup"
      else
         show group "BackGroup"
      end if
Hrmmm, removed a bunch of 'unlock screens' that were floating around and let PlayGame manage the unlocking of screens to reduce a bunch of split second flashes of things that should not be seen...and then OptionsGroup was always visible when Play card started, probably because I hit escape key to go back to the Title card.

Added this to the card script OpenCard to fix that

Code: Select all

    if exists (group "OptionsGroup") then hide group "OptionsGroup"
     if exists (group "PauseMenuGroup") then hide group "PauseMenuGroup"
Moved the call to PauseMenu out of loadingScreen and into PauseGame where it belongs.

Added on RawKeyDown to check for the EscapeKey because I had a hard time capturing key input when the stack was full screen sized.
Seems to be working, but then Title card threw a fit because I wast trying to view the keyinput so I copied Viewer button to Title card.

Mashing EscapeKey the Title card created a ...lets call it a Train of calls to TitleCardIdle which caused the throbbing game title effects to become a crazy flashing. Added this before TitleGlow was called and TitleCardIdle was sent a message to send again in TitleCardidle

Code: Select all

if visible of group "OptionsGroup" is true or visible of group "PauseMenuGroup" is true then exit TitleCardIdle 
I was looking at a way of getting consistant bulletspeed. The random number I was throwing at it was giving me some crazy slow bullets.
I think I found a good technique.
once the bullet has the delta value to taget we pass that to
DistToScreen
like so

Code: Select all

put DistToScreen(dx,dy,item 1 of playerXY,item 2 of playerXY) into distXY
DistToScreen finds how how far the the target is from the screen edge...in the direction that its traveling according to the delta

Code: Select all

function DistToScreen deltax,delty,originx,originy
   if deltax < 0 then 
      put originx into distX 
   else
      put originX- item 1 of screenDepth() into distX
   end if
   if delta <0 then 
      put originy into distY
   else
      put originY - item 2 of screenDepth() into distY 
   end if
   return distX,distY
end DistToScreen
In the bullet we divided each result by a 100

Code: Select all

  put (item 1 of distXY)/100 into vx
   put (item 2 of distXY)/100 into vy
And now we got some murder bullets, it adds a bit if inaccuracy shots though.

They might be too fast, I dunno, I racked up 17770 points in game, but I made it.

Turns out we don't need this big pile of script in the bullets

Code: Select all

-// plot the trajectory to target as a series of x,y coordinates
   repeat with i = 0 to 1 step 0.01
      put item 1 of enemyXY +(item 1 of playerXY - item 1 of enemyXY) *i into newX
      put item 2 of enemyXY +(item 2 of playerXY - item 2 of enemyXY) *i into newY
      put newX & comma & newY & cr after pathToTarget
   end repeat
   filter pathToTarget without empty
   --// use the first and last line of the point list to determine the x,y delta
   put line 1 of pathToTarget into axy
   put line -1 of pathToTarget into bxy
   
   put deltaVal(item 1 of axy, item 1 of bxy)/100 into dx
   put deltaVal(item 2 of axy, item 2 of bxy)/100 into dy
Just

Code: Select all

   put deltaVal(item 1 of enemyXY, item 1 of playerXY)/100 into dx
   put deltaVal(item 2 of enemyXY, item 2 of playerXY)/100 into dy
I'm having trouble getting the escape key to respond without dying or exitng the stack first.
It's sporadic and troublesome.
Last edited by xAction on Fri Oct 15, 2021 12:06 am, edited 4 times in total.

Post Reply

Return to “Games”