Announcement

Collapse
No announcement yet.

[Full Game Source + Guide] Dev_Ninja (Fruit Slicing)

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    [Full Game Source + Guide] Dev_Ninja (Fruit Slicing)

    Hey everyone, this is the basic framework of a fruit slicing game. It makes use of my mobile gesture system, but only for aesthetics really.


    I set out to recreate a simple fruit slicing game with UDK and UScript. It's pretty heavily commented, but if you need any clarification just ask . This source gives fully commented UScript examples of:

    The MobileMenu system (including MobileMenuList)
    An application of my gesutre system
    Getting input from the user via the iDevice's native keyboard (from Xces)
    A massive use of the BasicSave and BasicLoadObject
    A real time color picker via MobileSliders (from WillyG302)

    It also includes all of the raw source, textures, meshes, particles, sounds, etc.



    Dev_Ninja Source

    Install Guide

    There are multiples of classes so you can implement functionality specific to meshes (create a slow time or frenzy on slice). I'll go over the most important classes and their relationships.

    The PreGame: Small class, used to open the main menu on launch. Also good to reset the game to after a session for clean up
    Code:
    /*
    * Dev_Ninja_PreGame is used when game is first opened. It opens the MainMenu on PostLogin.
    */
    class Dev_Ninja_PreGame extends MobileMenuGame;
    
    
    defaultproperties
    {
    	InitialSceneToDisplayClass=class'Dev_Ninja_MainMen  u'
    }
    MobileMenuScenes
    Main Menu: Opens on load, points to PlayMenu, HighScoreMenu, and OptionsMenu.
    Code:
    /*
    * Dev_Ninja_MainMenu is the first menu to open when Dev_Ninja_PreGameController initializes.
    */
    class Dev_Ninja_MainMenu extends MobileMenuScene;
    
    /* When a touch ends, check the Tag of the MobileMenuObject that was pressed 
    * 1. If the play button was pressed, open the PlayMenu and close this one.
    * 2. If the options button was pressed, open the OptionsMenu and close this one.
    * 3. Open the HighScore menu close this one.
    */
    function OnTouch(MobileMenuObject Sender, ETouchType Type, float TouchX, float TouchY)
    {
    	if(Type == Touch_Ended)
    	{
    		Switch(Sender.Tag)
    		{
             /* 1 */
    			case "PLAYBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_PlayMenu'  ); 
                                  InputOwner.CloseMenuScene(self); 
                break;
             /* 2 */
    			case "OPTIONSBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_OptionsMe  nu'); 
                                     InputOwner.CloseMenuScene(self); 
                break;
             /* 3 */
             case "HIGHSCOREBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_HighScore  Menu'); 
                                        InputOwner.CloseMenuScene(self);
                break;
    		}
    	}
    }
    
    /* 
    * For a thorough overview of these properties, go to: http://udn.epicgames.com/Three/MobileMenuTechnicalGuide.html#MobileMenuScene 
    */
    defaultproperties
    {   
    	bRelativeLeft=true
    	bRelativeTop=true
    	bRelativeWidth=true
    	bRelativeHeight=true
    	Left=0
    	Top=0
    	Width=1
    	Height=1
       
       Begin Object Class=MobileMenuImage Name=Background
          Tag="Background"
          Left=0
          Top=0
          Width=1.0
          Height=1.0
          bRelativeWidth=true
          bRelativeHeight=true
          Image=Texture2D'D_NContent.Textures.dev_ninja'
          ImageDrawStyle=IDS_Stretched
       End Object
       MenuObjects.Add(Background)
    
       Begin Object Class=MobileMenuButton Name=PlayButton
          Tag="PLAYBUTTON"
          Left=0.2
          Top=0.5
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Play'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(PlayButton)
    
       Begin Object Class=MobileMenuButton Name=OptionsButton
          Tag="OPTIONSBUTTON"
          Left=0.6
          Top=0.5
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Options'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(OptionsButton)
    
       Begin Object Class=MobileMenuButton Name=HighScoreButton
          Tag="HIGHSCOREBUTTON"
          Left=0.39
          Top=0.7
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.High_Score  s_black'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(HighScoreButton)
    }
    OptionsMenu: This was an interesting menu to put together - beside the poor choice of flattening the visuals into the background image which causes scaling issues on different devices due to explicit positioning. It implements a real time "color picker" via MobileMenuSliders (Thanks WillyG302) that spans the whole RGB range ,0-255. It also has an option to turn sound on and off.

    Code:
    /*
    * Dev_Ninja_OptionsMenu is where the game options are set. Opened from the MainMenu.
    */
    class Dev_Ninja_OptionsMenu extends MobileMenuScene;
    
    /*
    * @ var OptR,OptG,OptB: Values set through the respective sliders. Saved to the Options object's R,G,B values.
    * @ var bShouldPlaySound: Not implemented. Saved to the options Object.
    */
    var float                           OptR,OptG,OptB;
    var bool                            bShouldPlaySound;
    
    /* This function gets called when the Scene is opened, loads the Options object */
    function Opened(string Mode)
    {
       LoadOptionsObject();
    }
    /* Called when the scene is touched. Should look familiar by now.*/
    function OnTouch(MobileMenuObject Sender, ETouchType Type, float TouchX, float TouchY)
    {
    	if(Type == Touch_Ended)
    	{
    		Switch(Sender.Tag)
    		{
             case "ONBUTTON": bShouldPlaySound = true; 
                break;
             case "OFFBUTTON": bShouldPlaySound = false; 
                break;
             case "BACKBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_MainMenu'  ); 
                                     InputOwner.CloseMenuScene(self); 
                break;
             case "SAVEBUTTON": `Log("SAVEBUTTON Ended"); SaveOptionsObject(); 
                break;
    		}
    	}
    }
    /* Dev_Ninja_Slider calls this with its slider value. */
    function SetSliderValues(string SliderTag, float SliderValue)
    {
       switch(SliderTag)
       {
          case "RSLIDER": OptR = SliderValue; 
             break;
          case "GSLIDER": OptG = SliderValue; 
             break;
          case "BSLIDER": OptB = SliderValue; 
             break;
       }
    }
    /* 
    * Because the ParticleFake object has bTellSceneBeforeRendering=true, this function gets called so we 
    * can change the drawcolor before its drawn again.
    */
    function PreRenderMenuObject(MobileMenuObject MenuObject, Canvas Canvas,float RenderDelta)
    {
       /* Make sure it's the ParticleFake, and set its drawcolor to its slider value. */
       if(MenuObject.isA('MobileMenuImage'))
       {
          MobileMenuImage(MenuObject).ImageColor.R = OptR;
          MobileMenuImage(MenuObject).ImageColor.G = OptG;
          MobileMenuImage(MenuObject).ImageColor.B = OptB;
       }
       /* If it's a Slider, draw the slider at its last saved value. */
       else if(MenuObject.isA('Dev_Ninja_Slider'))
       {
          switch(MenuObject.Tag)
          {
             case "RSLIDER": Dev_Ninja_Slider(MenuObject).NubLeft = OptR; 
                break;
             case "GSLIDER": Dev_Ninja_Slider(MenuObject).NubLeft = OptG; 
                break;
             case "BSLIDER": Dev_Ninja_Slider(MenuObject).NubLeft = OptB; 
                break;
          }
          /* This makes sure it only gets called once */
          MenuObject.bTellSceneBeforeRendering=false;
       }
    }
    /* Load the Options object, set the OptR/G/B values to the loaded obvect's R/G/B. */
    function LoadOptionsObject()
    {
       local Dev_Ninja_Options Options;
       Options = new class'Dev_Ninja_Options';
       class'Engine'.static.BasicLoadObject(Options, "Dev_Ninja_Options.bin", true, 1);
    
       OptR = Options.R;
       OptG = Options.G;
       OptB = Options.B;
    
       bShouldPlaySound = Options.bShouldPlaySound;
    }
    
    /* Save the Options object with the new slider values and sound bool. */
    function SaveOptionsObject()
    {
       local Dev_Ninja_Options Options;
       Options = new class'Dev_Ninja_Options';
    
       Options.bShouldPlaySound = bShouldPlaySound;
       Options.R = OptR;
       Options.G = OptG;
       Options.B = OptB;
    
       class'Engine'.static.BasicSaveObject(Options, "Dev_Ninja_Options.bin", true, 1);
       InputOwner.OpenMenuScene(class'Dev_Ninja_MainMenu'  ); 
       InputOwner.CloseMenuScene(self); 
    }
    
    /* 
    * For a thorough overview of these properties, go to: http://udn.epicgames.com/Three/MobileMenuTechnicalGuide.html#MobileMenuScene.
    * For the MobileMenuSlider, head to http://willyg302.wordpress.com/2012/09/15/udk-mobile-slider-tutorial/.
    */
    defaultproperties
    {   
       bRelativeLeft=true
       bRelativeTop=true
       bRelativeWidth=true
       bRelativeHeight=true
       Left=0
       Top=0
       Width=1
       Height=1
       
       Begin Object Class=MobileMenuImage Name=Background
          Tag="Background"
          Left=0
          Top=0
          Width=1.0
          Height=1.0
          bRelativeWidth=true
          bRelativeHeight=true
          Image=Texture2D'D_NContent.Textures.Options_Menu'
          ImageDrawStyle=IDS_Stretched
          ImageUVs=(bCustomCoords=true,U=1024,V=512,UL=1024,  VL=512)
       End Object
       MenuObjects.Add(Background)
    
    
       Begin Object Class=MobileMenuImage Name=ParticleFake
          Tag="ParticleFake"
          Left=0.6
          Top=0.22
          Width=270
          Height=270
          bRelativeLeft=true
          bRelativeTop=true
          bTellSceneBeforeRendering=true;
          Image=Texture2D'D_NContent.Textures.MGT_Gesture_Te  xture'
          ImageUVs=(bCustomCoords=true,U=30,V=32,UL=30,VL=32  )
          ImageDrawStyle=IDS_Stretched
       End Object
       MenuObjects.Add(ParticleFake)
    
       Begin Object Class=Dev_Ninja_Slider Name=RSlider
          Tag="RSLIDER"
          Left=0.22
          Top=0.2
          Width=380
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          SliderMin=0
          SliderMax=255
          NubWidth=64
          NubHeight=64
          bTellSceneBeforeRendering=true;
          Image=Texture2D'D_NContent.Textures.SliderNub'
          ImageUV=(bCustomCoords=true,U=32,V=32,UL=32,VL=32)
       End Object
       MenuObjects.Add(RSlider)
    
       Begin Object Class=Dev_Ninja_Slider Name=GSlider
          Tag="GSLIDER"
          Left=0.22
          Top=0.37
          Width=380
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          SliderMin=0
          SliderMax=255
          NubWidth=64
          NubHeight=64
          bTellSceneBeforeRendering=true;
          Image=Texture2D'D_NContent.Textures.SliderNub'
          ImageUV=(bCustomCoords=true,U=32,V=32,UL=32,VL=32)
       End Object
       MenuObjects.Add(GSlider)
    
       Begin Object Class=Dev_Ninja_Slider Name=BSlider
          Tag="BSLIDER"
          Left=0.22
          Top=0.535
          Width=380
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          SliderMin=0
          SliderMax=255
          NubWidth=64
          NubHeight=64
          bTellSceneBeforeRendering=true;
          Image=Texture2D'D_NContent.Textures.SliderNub'
          ImageUV=(bCustomCoords=true,U=32,V=32,UL=32,VL=32)
       End Object
       MenuObjects.Add(BSlider)
    
       Begin Object Class=MobileMenuButton Name=OnButton
          Tag="ONBUTTON"
          Left=0.35
          Top=0.7
          Width=64
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.On'
          ImagesUVs(0)=(bCustomCoords=true,U=64,V=64,UL=64,V  L=32)
       End Object
       MenuObjects.Add(OnButton)
    
       Begin Object Class=MobileMenuButton Name=OffButton
          Tag="OFFBUTTON"
          Left=0.45
          Top=0.7
          Width=64
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Off'
          ImagesUVs(0)=(bCustomCoords=true,U=64,V=64,UL=64,V  L=32)
       End Object
       MenuObjects.Add(OffButton)
    
       Begin Object Class=MobileMenuButton Name=BackButton
          Tag="BACKBUTTON"
          Left=0.28
          Top=0.85
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Back'
          ImagesUVs(0)=(bCustomCoords=true,U=256,V=64,UL=256  ,VL=64)
       End Object
       MenuObjects.Add(BackButton)
    
       Begin Object Class=MobileMenuButton Name=SaveButton
          Tag="SAVEBUTTON"
          Left=0.55
          Top=0.85
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Save'
          ImagesUVs(0)=(bCustomCoords=true,U=256,V=64,UL=256  ,VL=64)
       End Object
       MenuObjects.Add(SaveButton)
    
    }
    The OptionsMenu's primary function is to edit the Options object which is saved and loaded via the BasicSaveObject and BasicLoadObject.

    Options Object: Very simple
    Code:
    /*
    * Dev_Ninja_Options is the object that is saved to, and loaded from.
    * Make sure to load objects before you save to them, or you'll override 
    * the last save with the variable defaults.
    */
    class Dev_Ninja_Options extends Object;
    
    /*
    * Read/Write variables
    *
    * @ var R,B,G: Saved to in the Options menu via the sliders. Loaded from when Dev_Ninja_PlayerController initializes.
    * @ var Saved to in the OptionsMenu. Loaded in the PlayerController.
    * @ var GameMode: Saved to in the PlayMenu. Loaded in the PlayerController.
    */
    var float 						R,G,B;
    var bool						bShouldPlaySound;
    var string						GameMode;
    	
    defaultproperties
    {
    }
    PlayMenu: Also edits the Options Object to set the game mode.
    Code:
    /*
    * Dev_Ninja_PlayMenu is called from the MainMenu.
    */
    class Dev_Ninja_PlayMenu extends MobileMenuScene;
    
    function OnTouch(MobileMenuObject Sender, ETouchType Type, float TouchX, float TouchY)
    {
    	if(Type == Touch_Ended)
    	{
    		Switch(Sender.Tag)
    		{
             case "QUICKPLAYBUTTON": SetGame("QuickPlay"); 
                break;
             case "ENDLESSPLAYBUTTON": SetGame("Endless"); 
                break;
             case "BACKBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_MainMenu'  ); 
                                  InputOwner.CloseMenuScene(self); 
                break;
    		}
    	}
    }
    /*
    * Load the Options object so its not overridden with variable defaults.
    * Set the Options GameMode based off the button that was pressed.
    * Run a console command from the controller to to open Dev_Ninja map and set its GameType to Dev_Ninja_Game.
    */
    function SetGame(string GameMode)
    {
       /* 1 */
       local Dev_Ninja_Options Options;
       Options = new class'Dev_Ninja_Options';
       class'Engine'.static.BasicLoadObject(Options, "Dev_Ninja_Options.bin", true, 1);
       /* 2 */
       Options.GameMode = GameMode;
       class'Engine'.static.BasicSaveObject(Options, "Dev_Ninja_Options.bin", true, 1);
       /* 3 */
       InputOwner.Outer.ConsoleCommand("open Dev_Ninja_Map?Game=Dev_Ninja.Dev_Ninja_Game");
    }
    
    /* 
    * For a thorough overview of these properties, go to: http://udn.epicgames.com/Three/MobileMenuTechnicalGuide.html#MobileMenuScene 
    */
    defaultproperties
    {   
       bRelativeLeft=true
       bRelativeTop=true
       bRelativeWidth=true
       bRelativeHeight=true
       Left=0
       Top=0
       Width=1
       Height=1;
       
       Begin Object Class=MobileMenuImage Name=Background
          Left=0
          Top=0
          Width=1.0
          Height=1.0
          bRelativeWidth=true
          bRelativeHeight=true
          Image=Texture2D'D_NContent.Textures.Blank_Backroun  d'
          ImageDrawStyle=IDS_Stretched
       End Object
       MenuObjects.Add(Background)
    
       Begin Object Class=MobileMenuImage Name=PlayMenu
          Left=0.3
          Top=0.1
          Width=512
          Height=128
          bRelativeLeft=true
          bRelativeTop=true
          Image=Texture2D'D_NContent.Textures.Play_Menux2'
          ImageDrawStyle=IDS_Stretched
          ImageUVs=(bCustomCoords=true,U=0,V=0,UL=512,VL=128  )
       End Object
       MenuObjects.Add(PlayMenu)
    
       Begin Object Class=MobileMenuButton Name=QuickPlayButton
          Tag="QUICKPLAYBUTTON"
          Left=0.2
          Top=0.5
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Quick_Play  '
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(QuickPlayButton)
    
       Begin Object Class=MobileMenuButton Name=EndlessPlayButton
          Tag="ENDLESSPLAYBUTTON"
          Left=0.6
          Top=0.5
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Endless'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(EndlessPlayButton)
    
       Begin Object Class=MobileMenuButton Name=BackButton
          Tag="BACKBUTTON"
          Left=0.39
          Top=0.85
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Back'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(BackButton)
    }
    HighScoreMenu: Renders a dynamic list that consists of a single HighScores Object.
    Code:
    /*
    * Dev_Ninja_HighScoreMenu can be opened from the MainMenu or after a QuickPlay is over and a new high score is recorded.
    */
    class Dev_Ninja_HighScoreMenu extends MobileMenuScene;
    
    /*
    * @ var HighScores: HighScore object pointer. 
    */
    var Dev_Ninja_HighScores HighScores;
    
    event InitMenuScene(MobilePlayerInput PlayerInput, int ScreenWidth, int ScreenHeight, bool bIsFirstInitialization)
    {
    	super.InitMenuScene(PlayerInput, ScreenWidth, ScreenHeight, bIsFirstInitialization);
       
       /* Load the HighScore object on Init. so all the saved scored are rendered in the list. */
       class'Engine'.static.BasicLoadObject(HighScores, "Dev_Ninja_Highscores.bin", true, 1);
    }
    
    /* When a touch ends, check the Tag of the MobileMenuObject that was pressed 
    * 1. If the play menu button was pressed, open the PlayMenu and close this one.
    * 2. If the main menu button was pressed, open the MainMenu and close this one.
    */
    function OnTouch(MobileMenuObject Sender, ETouchType Type, float TouchX, float TouchY)
    {
       if(Type == Touch_Ended)
       {
          Switch(Sender.Tag)
          {
             /* 1 */
             case "PLAYMENUBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_PlayMenu'  ); 
                                     InputOwner.CloseMenuScene(self);  
                break;
             /* 2 */
             case "MAINMENUBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_MainMenu'  ); 
                                     InputOwner.CloseMenuScene(self); 
                break;
          }
       }
    }
    
    /* 
    * For a thorough overview of these properties, go to: http://udn.epicgames.com/Three/MobileMenuTechnicalGuide.html#MobileMenuScene 
    */
    defaultproperties
    {   
    	bRelativeLeft=true
    	bRelativeTop=true
    	bRelativeWidth=true
    	bRelativeHeight=true
    	Left=0
    	Top=0
    	Width=1
    	Height=1
    
       Begin Object Class=MobileMenuImage Name=HighScore
          Left=0.3
          Top=0.1
          Width=512
          Height=128
          bRelativeLeft=true
          bRelativeTop=true
          Image=Texture2D'D_NContent.Textures.High_Scores'
          ImageUVs=(bCustomCoords=true,U=0,V=0,UL=512,VL=128  )
          ImageDrawStyle=IDS_Stretched
    
       End Object
       MenuObjects.Add(HighScore)
    
       Begin object class=Dev_Ninja_HighScores name=HighScoreObject
          bIsVisible=true
       end object
       HighScores=HighScoreObject
    
       Begin Object Class=MobileMenuList Name=HighScoreList
          Left=0.30
          Top=0.5
          Width=0.4
          Height=0.5
          bIsHidden=false
          bRelativeWidth=true
          bRelativeHeight=true
          bRelativeLeft=true
          bRelativeTop=true
          EndOfListSupression=1.0
          Items.Add(HighScoreObject)
       End Object
       MenuObjects.Add(HighScoreList)
    
       Begin Object Class=MobileMenuButton Name=PlayMenuButton
          Tag="PLAYMENUBUTTON"
          Left=0.1
          Top=0.7
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Play_Menu'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(PlayMenuButton)
    
       Begin Object Class=MobileMenuButton Name=MainMenuButton
          Tag="MAINMENUBUTTON"
          Left=0.7
          Top=0.7
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Main_Menu'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(MainMenuButton)
    }
    QuitMenu: Simple creates a Quit button in the top right of the screen while a session is in progress.
    Code:
    /*
    * Dev_Ninja_QuitMenu is opened when the game starts, just a button.
    */
    class Dev_Ninja_QuitMenu extends MobileMenuScene;
    
    /*
    * On touch end, check the button's tag.
    * 1. IF]f quit button was pressed, open the PreGame and close this menu. 
    */
    function OnTouch(MobileMenuObject Sender, ETouchType Type, float TouchX, float TouchY)
    {
    	if(Type == Touch_Ended)
    	{
    		switch(Sender.Tag)
    		{
             /* 1 */
    			case "QUITBUTTON": InputOwner.Outer.ConsoleCommand("open Empty?Game=Dev_Ninja.Dev_Ninja_PreGame"); 
                                   InputOwner.CloseMenuScene(self); 
                break;
    		}	
    	}
    }
    
    /* 
    * For a thorough overview of these properties, go to: http://udn.epicgames.com/Three/MobileMenuTechnicalGuide.html#MobileMenuScene 
    */
    defaultproperties
    {
       bRelativeLeft=true
       bRelativeTop=true
       Left=0.8
       Top=0.1
       Width=128
       Height=32
    
       Begin Object Class=MobileMenuButton Name=QuitButton
          Tag="QUITBUTTON"
          Left=0.8
          Top=0.0
          Width=128
          Height=32
          bRelativeLeft=true
          bRelativeTop=true
          Images(0)=Texture2D'D_NContent.Textures.Quit'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       	End Object
       	MenuObjects.Add(QuitButton)	
    
    }
    HighScores Object: Extends a MenuListItem for the RenderItem hook, holds two dynamic arrays (Player and Score) that's saved to when a new high score is met.
    Code:
    /*
    * Dev_Ninja_HighScoreMenu can be opened from the MainMenu or after a QuickPlay is over and a new high score is recorded.
    */
    class Dev_Ninja_HighScoreMenu extends MobileMenuScene;
    
    /*
    * @ var HighScores: HighScore object pointer. 
    */
    var Dev_Ninja_HighScores HighScores;
    
    event InitMenuScene(MobilePlayerInput PlayerInput, int ScreenWidth, int ScreenHeight, bool bIsFirstInitialization)
    {
    	super.InitMenuScene(PlayerInput, ScreenWidth, ScreenHeight, bIsFirstInitialization);
       
       /* Load the HighScore object on Init. so all the saved scored are rendered in the list. */
       class'Engine'.static.BasicLoadObject(HighScores, "Dev_Ninja_Highscores.bin", true, 1);
    }
    
    /* When a touch ends, check the Tag of the MobileMenuObject that was pressed 
    * 1. If the play menu button was pressed, open the PlayMenu and close this one.
    * 2. If the main menu button was pressed, open the MainMenu and close this one.
    */
    function OnTouch(MobileMenuObject Sender, ETouchType Type, float TouchX, float TouchY)
    {
       if(Type == Touch_Ended)
       {
          Switch(Sender.Tag)
          {
             /* 1 */
             case "PLAYMENUBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_PlayMenu'  ); 
                                     InputOwner.CloseMenuScene(self);  
                break;
             /* 2 */
             case "MAINMENUBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_MainMenu'  ); 
                                     InputOwner.CloseMenuScene(self); 
                break;
          }
       }
    }
    
    /* 
    * For a thorough overview of these properties, go to: http://udn.epicgames.com/Three/MobileMenuTechnicalGuide.html#MobileMenuScene 
    */
    defaultproperties
    {   
    	bRelativeLeft=true
    	bRelativeTop=true
    	bRelativeWidth=true
    	bRelativeHeight=true
    	Left=0
    	Top=0
    	Width=1
    	Height=1
    
       Begin Object Class=MobileMenuImage Name=HighScore
          Left=0.3
          Top=0.1
          Width=512
          Height=128
          bRelativeLeft=true
          bRelativeTop=true
          Image=Texture2D'D_NContent.Textures.High_Scores'
          ImageUVs=(bCustomCoords=true,U=0,V=0,UL=512,VL=128  )
          ImageDrawStyle=IDS_Stretched
    
       End Object
       MenuObjects.Add(HighScore)
    
       Begin object class=Dev_Ninja_HighScores name=HighScoreObject
          bIsVisible=true
       end object
       HighScores=HighScoreObject
    
       Begin Object Class=MobileMenuList Name=HighScoreList
          Left=0.30
          Top=0.5
          Width=0.4
          Height=0.5
          bIsHidden=false
          bRelativeWidth=true
          bRelativeHeight=true
          bRelativeLeft=true
          bRelativeTop=true
          EndOfListSupression=1.0
          Items.Add(HighScoreObject)
       End Object
       MenuObjects.Add(HighScoreList)
    
       Begin Object Class=MobileMenuButton Name=PlayMenuButton
          Tag="PLAYMENUBUTTON"
          Left=0.1
          Top=0.7
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Play_Menu'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(PlayMenuButton)
    
       Begin Object Class=MobileMenuButton Name=MainMenuButton
          Tag="MAINMENUBUTTON"
          Left=0.7
          Top=0.7
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Main_Menu'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(MainMenuButton)
    }
    GameOverMenu: If there isn't a new high score after a quickplay session, this menu opens. It lets the player access the other menus.
    Code:
    /*
    * Dev_Ninja_GameOverMenu is opened after a QuickPlay didn't yield a new high score.
    */
    class Dev_Ninja_GameOverMenu extends MobileMenuScene;
    
    /* When a touch ends, check the Tag of the MobileMenuObject that was pressed 
    * 1. If the play menu button was pressed, open the PlayMenu and close this one.
    * 2. If the high score menu button was pressed, open the HighScoreMenu and close this one.
    * 3. If the main menu button was pressed, open the MainMenu and close this one.
    */
    function OnTouch(MobileMenuObject Sender, ETouchType Type, float TouchX, float TouchY)
    {
    	if(Type == Touch_Ended)
    	{
    		switch(Sender.Tag)
    		{
             /* 1 */
    			case "PLAYMENUBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_PlayMenu'  );
    										InputOwner.CloseMenuScene(self);
    				break;
             /* 2 */
    			case "HIGHSCOREBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_HighScore  Menu');
    										InputOwner.CloseMenuScene(self);
    				break;
             /* 3 */
    			case "MAINMENUBUTTON": InputOwner.OpenMenuScene(class'Dev_Ninja_MainMenu'  );
    										InputOwner.CloseMenuScene(self);
    				break;
    		}
    		
    	}
    }
    
    
    /* 
    * For a thorough overview of these properties, go to: http://udn.epicgames.com/Three/MobileMenuTechnicalGuide.html#MobileMenuScene 
    */
    defaultproperties
    {
    	bRelativeLeft=true
    	bRelativeTop=true
    	bRelativeWidth=true
    	bRelativeHeight=true
    	Left=0
    	Top=0
    	Width=1
    	Height=1
    
    	Begin Object Class=MobileMenuImage Name=GameOver
          Left=0.3
          Top=0.1
          Width=512
          Height=128
          bRelativeLeft=true
          bRelativeTop=true
          Image=Texture2D'D_NContent.Textures.GameOver'
          ImageDrawStyle=IDS_Stretched
          ImageUVs=(bCustomCoords=true,U=0,V=0,UL=512,VL=128  )
       End Object
       MenuObjects.Add(GameOver)
    
    
       Begin Object Class=MobileMenuButton Name=PlayMenuButton
          Tag="PLAYMENUBUTTON"
          Left=0.1
          Top=0.8
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Play_Menu'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(PlayMenuButton)
    
       Begin Object Class=MobileMenuButton Name=MainMenuButton
          Tag="MAINMENUBUTTON"
          Left=0.7
          Top=0.8
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.Main_Menu'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(MainMenuButton)
    
    
    
       Begin Object Class=MobileMenuButton Name=HighScoreButton
          Tag="HIGHSCOREBUTTON"
          Left=0.39
          Top=0.8
          Width=256
          Height=64
          bRelativeLeft=true
          bRelativeTop=true
          TopLeeway=20
          Images(0)=Texture2D'D_NContent.Textures.High_Score  s_white'
          ImagesUVs(0)=(bCustomCoords=true,U=0,V=0,UL=256,VL  =64)
       End Object
       MenuObjects.Add(HighScoreButton)
    }
    Some how a straight copy/paste messed up some of my formatting?

    #2
    Continued:


    "Color Picker" Slider: Simply sends its clamped value to the OptionsMenu to update particle swipe's color during the Menu's PreRenderMenuObject function.
    Code:
    /*
    * Many thanks to William Gaul for the MobileMenuSlider. 
    * http://willyg302.wordpress.com/
    *
    * Dev_Ninja_Slider sends its values to the OptionsMenu.
    */
    class Dev_Ninja_Slider extends MobileMenuSlider;
    
    
    function SetNubPosition(float X)
    {
    	local float ret;
    	ret = FClamp((X - OwnerScene.Left - Left - 0.5*NubWidth - TouchOffset) / (Width - NubWidth), 0.0, 1.0);
    	NubLeft = bIncremental ? (float(Round(ret * (SliderMax - SliderMin))) / (SliderMax - SliderMin)) : ret;
    	/* Send the OptionsMenu this slider's tag and its clamped value. Magic */
    	Dev_Ninja_OptionsMenu(OwnerScene).SetSliderValues(Tag, ret);
    }
    
    defaultproperties
    {
    }
    WillyG's MobileMenuSlider: Because it's too awesome not to share.
    Code:
    /*
    * MobileMenuSlider.
    * @ author William Gaul.
    * http://willyg302.wordpress.com/2012/09/15/udk-mobile-slider-tutorial/
    */
    class MobileMenuSlider extends MobileMenuObject;
    
    var Texture2D Image;
    var UVCoords ImageUV;
    var LinearColor ImageColor;
    
    /* NubLeft is a 0 - 1 range of the nub's position on the slider. To left is 0, to right is 1.
     * NubHeight is relative to Height, at 1.0 NubHeight = Height
     * NubWidth is also relative to height.
     */
    var float NubLeft, NubWidth, NubHeight;
    var bool bRelativeNubWidth, bRelativeNubHeight;
    
    var Vector LastTouchLocation, CurrentTouchLocation;
    
    // True if current touch is a swipe
    var bool bSwipe;
    // Minimum distance a touch must move to be a swipe. Less than this, it is a tap.
    var float SwipeTolerance;
    
    // NOT TO BE CONFUSED WITH BISACTIVE!!!
    var bool bActive;
    
    var float SliderMin, SliderMax;
    
    // Snaps to nearest integer value
    var bool bIncremental;
    
    // If the person touches the nub off-center, we want to keep this offset as we slide
    var float TouchOffset;
    
    // If true, if player slides outside of slider zone and releases, the slide will not register
    var bool bHasCancel;
    
    function InitMenuObject(MobilePlayerInput PlayerInput, MobileMenuScene Scene, int ScreenWidth, int ScreenHeight, bool bIsFirstInitialization)
    {
    	super.InitMenuObject(PlayerInput, Scene, ScreenWidth, ScreenHeight, bIsFirstInitialization);
    	NubLeft = 0.0;
    	NubHeight = bRelativeNubHeight ? (NubHeight * Height) : NubHeight;
    	NubWidth = bRelativeNubWidth ? (NubWidth * Height) : NubWidth;
    }
    
    function RenderObject(canvas Canvas, float DeltaTime)
    {
    	local LinearColor DrawColor;
    	Canvas.SetPos(OwnerScene.Left + Left + GetNubPixelLeft(), OwnerScene.Top + Top);
    	Drawcolor = ImageColor;
    	Drawcolor.A *= Opacity * OwnerScene.Opacity;
    	Canvas.DrawTile(Image, NubWidth, NubHeight, ImageUV.U, ImageUV.V, ImageUV.UL, ImageUV.VL, DrawColor);
    }
    
    //Pixels from left edge of slider that left edge of nub is
    function float GetNubPixelLeft()
    {
    	return (NubLeft * (Width - NubWidth));
    }
    
    function SetNubPosition(float X)
    {
    	local float ret;
    	ret = FClamp((X - OwnerScene.Left - Left - 0.5*NubWidth - TouchOffset) / (Width - NubWidth), 0.0, 1.0);
    	NubLeft = bIncremental ? (float(Round(ret * (SliderMax - SliderMin))) / (SliderMax - SliderMin)) : ret;
    }
    
    //Transforms slider value into a more usable number based on max/min and clamping
    function float GetSliderValue()
    {
    	local float ret;
    	ret = Lerp(SliderMin, SliderMax, NubLeft);
    	return bIncremental ? float(Round(ret)) : ret;
    }
    
    function SetSliderValue(float Value)
    {
    	NubLeft = FClamp((Value - SliderMin) / (SliderMax - SliderMin), 0.0, 1.0);
    }
    
    event bool OnTouch(ETouchType Type, float TouchX, float TouchY, MobileMenuObject ObjectOver, float DeltaTime)
    {
    	if(Type == Touch_Began)
    	{
    		if(CheckBounds(TouchX, TouchY))
    		{
    			if(CheckHitNub(TouchX, TouchY))
    				TouchOffset = TouchX - OwnerScene.Left - Left - GetNubPixelLeft() - 0.5*NubWidth;
    			bActive = true;
    			bSwipe = false;
    			LastTouchLocation.X = TouchX;
    			LastTouchLocation.Y = TouchY;
    			SetNubPosition(TouchX);
    		}
    	}
    	else if(Type == Touch_Moved)
    	{
    		if(bActive)
    		{
    			CurrentTouchLocation.X = TouchX;
    			CurrentTouchLocation.Y = TouchY;
    			SetNubPosition(TouchX);
    			CheckSwipe();
    		}
    	}
    	else if(Type == Touch_Ended)
    	{
    		if(bHasCancel && !CheckBounds(TouchX, TouchY))
    			SetNubPosition(LastTouchLocation.X);
    		// What if you wanted to save the value of the slider? Just pass it up to the parent class!
    		//ParentClass(OwnerScene).SaveSliderValue(Tag, GetSliderValue());
    		bActive = false;
    		TouchOffset = 0.0;
    	}
    	else if(Type == Touch_Cancelled)
    	{
    		bActive = false;
    	}
    	return true;
    }
    
    function bool CheckHitNub(float X, float Y)
    {
    	X -= (OwnerScene.Left + Left);
    	Y -= OwnerScene.Top;
    	if(X >= GetNubPixelLeft() && X <= GetNubPixelLeft() + NubWidth && Y >= Top && Y <= Top + NubHeight)
    		return true;
    	return false;
    }
    
    function bool CheckBounds(float X, float Y)
    {
    	X -= OwnerScene.Left;
    	Y -= OwnerScene.Top;
    	if(X >= Left && X <= Left + Width && Y >= Top && Y <= Top + Height)
    		return true;
    	return false;
    }
    
    function CheckSwipe()
    {
    	if(VSize(LastTouchLocation - CurrentTouchLocation) > SwipeTolerance)
    		bSwipe = true;
    }
    
    defaultproperties
    {
    	ImageColor=(r=1.0,g=1.0,b=1.0,a=1.0)
    	bIsActive=true;
    	bActive=false;
    	TouchOffset=0.0
    	SwipeTolerance=5.0
    }
    Now we'll get into the actual game.

    The actual GameClass: This class is set as the gametype when the user begins a session and the Dev_Ninja_Map is opened. It sets the game's PlayerController and HUD.
    Code:
    /*
    * Game class declares Player Controller and HUD.
    */
    class Dev_Ninja_Game extends FrameworkGame;
    
    /* RestartPlayer stub.*/
    simulated function RestartPlayer(Controller NewPlayer);
    
    
    defaultproperties
    {
    	PlayerControllerClass = class'Dev_Ninja.Dev_Ninja_PlayerController'
    
    	HUDType=class'Dev_Ninja.Dev_Ninja_HUD'
    
    	bDelayedStart=false
    }
    PlayerController: This class handles all the game logic, save/loading, and sounds that are played. It's the main functionality of the whole game.
    Code:
    /*
    * Dev_Ninja_Player controller is where the game logic takes place.
    */
    class Dev_Ninja_PlayerController extends GamePlayerController;
                          
    /*
    * @ var MPI: Cache MobilePlayerInput.
    * @ var ViewportSize: Local screen coordinates.
    * @ var PickDistance: Distance to trace.
    * @ var D_NPS: The particle swipe.
    * @ var PlayMenu,
    *       QuitMenu,
    *       HighScoreMenu
    *       GameOverMenu: Menus.
    * @ var Notes: Array of Notes placed in the level, used as spawn locations. 
    * @ var TouchX/Y: Arrays for holding TouchLocationX/Y.
    * @ var Gesture: String array to write directional input to.
    * @ var GestureGate: How long to record gestures.
    * @ var Tolderance: Directional tolerance.
    * @ var bShouldRecord: Used to open and close GestureGate.
    * @ var bShouldPlaySound: Whether to play sound, based off OptionsMenu.            
    */
    var Int                         Score, NewScore, Time, PlaySpeed;
    var String                      PlayerName;
    var MobilePlayerInput           MPI;
    var Vector2D                    ViewportSize;
    var Float                       PickDistance;
    var Dev_Ninja_PSA               D_NPS;
    var Dev_Ninja_PlayMenu          PlayMenu;
    var Dev_Ninja_QuitMenu          QuitMenu;
    var Dev_Ninja_HighScoreMenu     HighScoreMenu;
    var Dev_Ninja_GameOverMenu      GameOverMenu;
    var Array<Note>                 Notes;
    var Dev_Ninja_HighScores        HighScores;
    var array<int>                  TouchX, TouchY;
    var array<string>               Gesture;
    var float                       GestureGate;
    var Int                         Tolerance;
    var bool                        bshouldRecord;
    var bool                        bShouldPlaySound;
    
    
    /*
    * Stubbed so the player controller can't rotate.
    */
    function UpdateRotation(float DeltaTime);
    
    /*
    * 1. Cache MobilePlayerInput, ViewportSize, and particle system.
    * 2. Set global input delegate.
    * 3. Declare OnInputTouch delegate.
    * 4. Add all notes in the level to the Notes array.
    * 5. Load the Options object.
    */
    event InitInputSystem()
    {
        local Note N;
    
        Super.InitInputSystem();
        /* 1 */ 
        MPI = MobilePlayerInput(PlayerInput);
        LocalPlayer(Player).ViewportClient.GetViewportSize(ViewportSize);
        D_NPS = Spawn(class'Dev_Ninja_PSA');
        /* 2 */
        MPI.OnInputTouch = HandleInputTouch;
        /* 4 */
        foreach AllActors(class'Note', N)
        {
            Notes.AddItem(N);
        }
        /* 5 */
        LoadOptions();
    }
    
    /*
    * 1. Load the Options object.
    * 2. Set the particle swipe's newColor vector from Option's R/G/B.
    * 3. Turn sound on if sound should be on.
    * 4. Set the game logic based on Options.GameMode.
    * 5. If quickplay, start quickplay and startplay timer. If endless set time=0 and start startplay timer.
    */
    function LoadOptions()
    {
        /* 1 */
        local Dev_Ninja_Options Options;
        local vector newColor;
        Options = new class'Dev_Ninja_Options';
        class'Engine'.static.BasicLoadObject(Options, "Dev_Ninja_Options.bin", true, 1);
        /* 2 */
        newColor.X = Options.R;
        newColor.Y = Options.G;
        newColor.Z = Options.B;
        D_NPS.PSC.SetVectorParameter('Color', newColor);
        /* 3 */
        bShouldPlaySound = Options.bShouldPlaySound;
    
        `Log("bShouldPlaySound: "$bShouldPlaySound);
    
        /* 4 */
        switch(Options.GameMode)
        {
            /* 5 */
            case "QuickPlay": QuitMenu = Dev_Ninja_QuitMenu(MPI.OpenMenuScene(class'Dev_Ninja_QuitMenu'));
                                SetTimer(1, true, 'QuickPlayTimer'); 
                                    SetTimer(PlaySpeed, true, 'StartPlay');
                break;
            case "Endless": Time = 0; QuitMenu = Dev_Ninja_QuitMenu(MPI.OpenMenuScene(class'Dev_Ninja_QuitMenu'));
                                SetTimer(PlaySpeed, true, 'StartPlay');
                break;
        }
    }
    
    /*
    * Called every second if GameMode is QuickPlay.
    * 1. At the end of game, load the HighScore object.
    * 2. Iterate through the HighScore's Score array, if current score is greater
    * than any of the saved scores, get the player's input via native keyboard(ONLY WORKS ON MOBILE). Save that index to NewScore
    * and set NewScore to true. The console command sends the user's input as a string parameter to the GetPlayerName exec function.
    * 3. If it isn't a new score, open the GameOver menu.
    * 4. Clear game timers.
    */
    function QuickPlayTimer()
    {
        local int Item, Index;
        local bool bNewScore;
    
        Time--;
    
        if(Time < 0)
        {
            /* Aesthetics */
            Time = 0;
            /* 1 */
            HighScores = new class'Dev_Ninja_HighScores';
            class'Engine'.static.BasicLoadObject(HighScores, "Dev_Ninja_Highscores.bin", true, 1);
    
            foreach HighScores.Score(Item, Index)
            {/* 2 */
                if(Score > HighScores.Score[Index] && !bNewScore)
                {
                    Consolecommand("mobile getuserinput \"NEW HIGH SCORE!\" \"Your Name\" \"SetHighScore\"");
                    NewScore = Index;
                    bNewScore = true;
                }
            }
            /* 3 */
            if(!bNewScore)
            { 
                MPI.CloseMenuScene(QuitMenu);
                GameOverMenu = Dev_Ninja_GameOverMenu(MPI.OpenMenuScene(class'Dev_Ninja_GameOverMenu'));
            }
            /* 4 */
            ClearTimer('QuickPlayTimer');
            ClearTimer('StartPlay');
        }
    }
    /* 
    * Called when the game ends if there's a new high score. 
    * 1. Set PlayerName to the user's input.
    * 2. Insert the PlayerName and score at the index found through iteration.
    * 3. Set the score list to 20. "Top 20 high scores!"
    * 4. Save the HighScore object and open the HighScore menu.
    */
    exec function SetHighScore(optional string NameInput)
    {
        /* 1 */
        PlayerName = NameInput;
        /* 2 */
        HighScores.Score.InsertItem(NewScore, Score);
        HighScores.Player.InsertItem(NewScore, PlayerName);
        /* 3 */
        HighScores.Score.length = 20;
        HighScores.Player.length = 20;
        /* 4 */
        class'Engine'.static.BasicSaveObject(HighScores, "Dev_Ninja_HighScores.bin", true, 1);
        HighScoreMenu = Dev_Ninja_HighScoreMenu(MPI.OpenMenuScene(class'Dev_Ninja_HighScoreMenu'));
    }
    
    
    /*
    * This is the actual game logic.
    * 1. Get a random amount to spawn between 2 and 6.
    * 2. For how ever many SpawnAmount returns, spawn a mesh accordingly at a random note.
    * 3. Give the spawned KActor a random force and random torque.
    */
    function StartPlay()
    {
        local Dev_Ninja_KActor KA;
        local int SpawnAmount, i;
        /* 1 */
        SpawnAmount = RandRange(2, 6);
    
        for(i=0; i<SpawnAmount; i++)
        {
            switch(Rand(4))
            {/* 2 */
                case 0: KA = Spawn(class'Dev_Ninja_Cylinder',,,GetRandomNote(),); 
                    break;
                case 1: KA = Spawn(class'Dev_Ninja_Sphere',,,GetRandomNote(),); 
                    break;
                case 2: KA = Spawn(class'Dev_Ninja_Cube',,,GetRandomNote(),); 
                    break;
                case 3: KA = Spawn(class'Dev_Ninja_Torus',,,GetRandomNote(),); 
                    break;
            }
            /* 3 */
            KA.CollisionComponent.AddForce(GetRandomForce());
            KA.CollisionComponent.AddTorque(GetRandomTorque());
        }
    }
    
    /* Returns a random note */
    function Vector GetRandomNote()
    {
        local vector N;
        N = Notes[Rand(Notes.length)].Location;
        return N;
    }
    
    /* Returns a random vector within a fixed range */
    function Vector GetRandomForce()
    {
        local vector Force;
        Force.X = RandRange(-100, 50);
        Force.Y = RandRange(-1500, 1500);
        Force.Z = RandRange(6500, 8500);
        return Force;
    }
    
    /* Returns a random vector within a fixed range */
    function Vector GetRandomTorque()
    {
        local vector Torque;
        Torque.X = RandRange(-2500, 2500); 
        Torque.Z = RandRange(-2500, 2500);
        return Torque;
    }
    
    
    /*
    * Delegate to handle global input.
    *
    * @ param Handle: The unique identifier of the touch. 
    * @ param Type: The ETouchType type of the touch event. 
    * @ param TouchLocation: The Vector2D location of the touch event in screen coordinates (pixels).
    * @ param DeviceTimestamp: The time the touch event occurred.
    * @ param TouchpadIndex: Which touch pad was touched
    */
    function HandleInputTouch(int Handle, ETouchType Type, Vector2D TouchLocation, float DeviceTimestamp, int TouchpadIndex)
    {     
        /*
        * @ var CheckX/Y,
        *       LastX/Y,
        *       DeltaX/Y: Used for gesture logic.
        * @ var PickLocation: Local screen coordinates.
        * @ var TouchOrigin, 
        *       TouchDir, 
        *       Hitlocation, 
        *       HitNormal: Used for tracing and placing particle system at TouchLocation.
        * @ var HitActor: KActor to slice.         
        */
        local int                   CheckX, LastX, DeltaX, CheckY, LastY, DeltaY;
        local Vector2D              PickLocation;
        local vector                TouchOrigin, TouchDir, HitLocation, HitNormal;
    
        local KActorSpawnable       HitActor;
    
        /* Particle system is turned on. */
        if(Type == Touch_Began)
        {
            D_NPS.PSC.ActivateSystem();
            SetTimer(GestureGate, true, 'ShouldRecord');
        }
    
        /*
        * 1. TouchX/Y gets populated with all incoming TouchLocations.
        * 2. Gesture logic doesn't start until TouchX/Y has at least 2 entries, should probably be handled as a tap.
        * 3. CheckX/Y and LastX/Y are cached and Deltas are calculated.
        * 4. Directional logic runs.
        * 5. Local screen coordinates are calculated, trace is done, particle system's location is set to current TouchLocation.
        */
        else if(Type == Touch_Moved)
        {  
            /* 1 */
            TouchX.AddItem(TouchLocation.X);
            TouchY.AddItem(TouchLocation.Y);
            /* 2 */
            if(TouchX.length > 2 && TouchY.length > 2)
            {
                /* 3 */
                LastX          = TouchX[TouchX.length - 1];
                CheckX         = TouchX[TouchX.length - 2];
                DeltaX         = Abs(LastX - CheckX);
            
                LastY          = TouchY[TouchY.length - 1];
                CheckY         = TouchY[TouchY.length - 2];
                DeltaY         = Abs(LastY - CheckY);
            }
            //else `log("Was probably a tap");
    
            /* 4 */
            if(DeltaY < Tolerance && DeltaX > Tolerance) if(CheckX < LastX) RecordGesture("R"); 
                                                    else if(CheckX > LastX) RecordGesture("L");
            if(DeltaX < Tolerance && DeltaY > Tolerance) if(CheckY > LastY) RecordGesture("U"); 
                                                    else if(CheckY < LastY) RecordGesture("D");
        
            if(DeltaY > Tolerance && DeltaX > Tolerance) if(CheckX > LastX && CheckY < LastY) RecordGesture("DL");
                                                    else if(CheckX > LastX && CheckY > LastY) RecordGesture("UL");
                                                    else if(CheckX < LastX && CheckY < LastY) RecordGesture("DR");
                                                    else if(CheckX < LastX && CheckY > LastY) RecordGesture("UR");
            /* 1 */
            PickLocation.X = TouchLocation.X / ViewportSize.X;
            PickLocation.Y = TouchLocation.Y / ViewportSize.Y;
            LocalPlayer(Player).Deproject(PickLocation, TouchOrigin, TouchDir);
            Trace(HitLocation, HitNormal, TouchOrigin + (TouchDir * PickDistance), TouchOrigin, true);
            D_NPS.SetLocation(HitLocation);
    
            /* 5 */
            foreach TraceActors(class'KActorSpawnable', HitActor, HitLocation, HitNormal, TouchOrigin + (TouchDir * PickDistance), TouchOrigin,,,)
            {
                Dev_Ninja_KActor(HitActor).Slice();
                WorldInfo.MyEmitterPool.SpawnEmitter(ParticleSystem'D_NContent.Particles.D_N_Slice_Particles',HitLocation);
                Score++;
                if(bShouldPlaySound) PlaySound(SoundCue'D_NContent.Sounds.SlashCut');
            }
        }
    
        /*
        * 1. Timer cleared and gesture gate is closed. 
        * 2. Gestures are reset.
        * 3. Particle system is turned off.
        */
        else if(Type == Touch_Ended)
        {
            /* 1 */
            ClearTimer('ShouldRecord');
            /* 2 */
            TouchX.length = 0;
            TouchY.length = 0;
            Gesture.length = 0; 
            /* 3 */       
            D_NPS.PSC.DeactivateSystem();
        }
    }
    
    function ShouldRecord()
    {
        /*Gate is now open*/
        bshouldRecord = true;
    }
    
    function RecordGesture(String NewDirection)
    {
        /*
        * 1. Start Gesture index with the first directional string.
        * 2. Close gesture gate.
        * 3. If sound is On, play swipe soundcue.
        */
        if(Gesture.length == 0 && bshouldRecord)
        {
            /* 1 */
            Gesture[0] = NewDirection;
            /* 2 */
            bshouldRecord = false;
            /* 3 */
            if(bShouldPlaySound)
            {
                PlaySound(SoundCue'D_NContent.Sounds.swipe');
            }
    
        }
    
        /*
        * Make sure the new direction isn't the last and should record.
        * 1. Add new direction to gesture array.
        * 2. Close gesture gate.
        * 3. If sound is On, play swipe soundcue.
        */
        else if(NewDirection != Gesture[Gesture.length - 1] && bshouldRecord)
        {
            /* 1 */
            Gesture.AddItem(NewDirection);
            /* 2 */
            bshouldRecord = false;
            /* 3 */
            if(bShouldPlaySound)
            {
                PlaySound(SoundCue'D_NContent.Sounds.swipe');
            }
        }
    }
    
    defaultproperties
    {  
        /* Defines how fast to spawn groups of meshes */
        PlaySpeed=1.5
        /* Time for quickplay. Time=1 == 1 second. */
        Time=60
        /* Trace distance for placing particle swipe */
        PickDistance=10000
        /* directional tolerance */
        Tolerance=10
        /* How long gesture gate is open, record new directions 1/10 of a second */
        GestureGate=0.1
    
        InputClass=class'GameFramework.MobilePlayerInput' 
    }
    HUD: Extremely Simple HUD, shows timer and score in top left of screen.
    Code:
    /*
    * Dev_Ninja_HUD is pretty basic and right out of "Beginning iOS 3D Unreal Games" by Robert Chin.
    */
    class Dev_Ninja_HUD eXtends UDKHUD;
    
    /* Struct defines the HUD elements. */
    struct HUDInfo
    {
        var string          Label;
        var Vector2D        Location;
        var Vector2D        Scale;
        var Color           TeXtColor;
    };
    
    var HUDInfo     Score;
    var HUDInfo     Time;
    
    
    simulated function PostBeginPlaY()
    {
        super.PostBeginPlaY();
    
        Score.Label       = "Score: ";
        Score.Location.X  = 20;
        Score.Location.Y  = 20;
        Score.Scale.X     = 2;
        Score.Scale.Y     = 3;
        Score.TeXtColor.R = 255;
        Score.TeXtColor.G = 255;
        Score.TeXtColor.B = 255;
    
        Time.Label       = "Time: ";
        Time.Location.X  = 20;
        Time.Location.Y  = 60;
        Time.Scale.X     = 2;
        Time.Scale.Y     = 3;
        Time.TeXtColor.R = 255;
        Time.TeXtColor.G = 255;
        Time.TeXtColor.B = 255;
    }
    /* Set the Canvas to draw each HUDInfo */
    function DrawHUDItem(HUDInfo Info, coerce string Value)
    {
        local vector2D  TeXtSize;
        
        Canvas.SetDrawColor(Info.TeXtColor.R, Info.TeXtColor.G, Info.TeXtColor.B);
        Canvas.SetPos(Info.Location.X, Info.Location.Y);
        Canvas.DrawTeXt(Info.Label, ,Info.Scale.X, Info.Scale.Y);
        Canvas.TeXtSize(Info.Label, TeXtSize.X, TeXtSize.Y);
        Canvas.SetPos(Info.Location.X + ( Info.Scale.X * TeXtSize.X), Info.Location.Y);
        Canvas.DrawTeXt(Value, ,Info.Scale.X, Info.Scale.Y); 
    }
    
    /* DrawHUD overridden to draw the Score and Time from player controller */
    function DrawHUD()
    {
        super.DrawHUD();  
      
        DrawHUDItem(Score, Dev_Ninja_PlayerController(PlayerOwner).Score);
        DrawHUDItem(Time, Dev_Ninja_PlayerController(PlayerOwner).Time);
    }
    
    
    defaultproperties
    {
    	
    }
    The KActor: This class represents all of the meshes that are spawned via the PlayerController before they're sliced.
    Code:
    /*
    * Dev_Ninja_KActor is the super of all the shape classes.
    */
    class Dev_Ninja_KActor extends KActorSpawnable;
    
    
    var StaticMeshComponent  			StatMesh;
    var Dev_Ninja_FSMAS 				FracMesh;
    var int								SlowTimer;
    
    /* ResetHealth stubbed to get rid of another annoying log.*/
    simulated function ResetHealth();
    
    
    /*
    * Slice!
    * 1. Destroy this KActor, so we don't spawn multiple fractured meshes when traced on.
    * 2. Spawn the fractured mesh, it'll spawn at this KActor's location and rotation.
    * 3. Set the fractured mesh's static mesh to this KActors staticmesh. Static mesh is declared in 
    * Dev_Ninja_PlayerController @ StartPlay() switch.
    * 4. Removed fractured mesh's collision, or other KActors will collide after it's been sliced.
    * 5. Split the mesh. The fracmesh doesn't have any velocity so it just falls from where it was sliced.
    */
    function Slice()
    {
    	/* 1 */
    	Destroy();
    	/* 2 */
    	FracMesh = Spawn(class'Dev_Ninja_FSMAS');
    	/* 3 */
    	FracMesh.FracturedStaticMeshComponent.SetStaticMesh(StatMesh.StaticMesh);
    	/* 4 */
    	FracMesh.SetCollisionType(COLLIDE_NoCollision);
    	/* 5 */
    	FracMesh.Explode();
    }
    
    
    defaultproperties
    {
    	/*Remove the light environment so the dynamic mesh doesn't cast a shadow on the unlit materials.*/
    	Components.Remove(MyLightEnvironment)
    
        Begin Object Name=StaticMeshComponent0
        	/*Remove collision with other KActors.*/
        	BlockRigidBody=false
        End Object
        StatMesh=StaticMeshComponent0;
    }
    FracturedStaticMeshActor: Just a bunch of stuff overridden to get rid of so many log warnings.
    Code:
    /*
    * Dev_Ninja's Fractures Static Mesh Actor Spawnable represents
    * the KActor after it gets sliced
    */
    class Dev_Ninja_FSMAS extends FracturedSMActorSpawnable;
    
    
    /* PostBeginPlay is stubbed to remove a lot of log statements, doesn't seem to have any negative effect. */
    simulated event PostBeginPlay();
    
    defaultproperties
    {
    	/* To quiet another log */
    	bHasShownMissingSoundWarning=true;
    }
    ParticleSystemActor: Has a pointer to it's component that is edited when the Options Object is loaded via the PlayerController. Its Vertex Color Parameter is set from the Options Object's R/B/G values.
    Code:
    class Dev_Ninja_PSA extends Actor;
    
    /* ParticleSystemComponent pointer */
    var ParticleSystemComponent PSC;
    
    
    defaultproperties
    {
    	Begin Object Class=ParticleSystemComponent Name=ParticleSystemComponent0
            Template=ParticleSystem'D_NContent.Particles.D_N_Gesture_Particles'
            bAutoActivate=false
    	End Object
    	PSC=ParticleSystemComponent0
    	Components.Add(ParticleSystemComponent0)	
    }
    _Cube,_Cylinder,_Sphere,_Torus: These classes represent their respective meshes. I kept them separate so it would be easy to add individual functionality to its Super's Slice function.

    Comment


      #3
      Since this is a source guide, I'll help ya install and play it.

      Extract the Dev_Ninja files from the .zip in to their respective places inside your UDK directory.

      Navigate to: C:\UDK\YourInstall\UDKGame\Config > Open DefaultEngine.ini > find [UnrealEd.EditorEngine] and add +EditPackages=Dev_Ninja

      Should now look something like this:
      Code:
      [UnrealEd.EditorEngine]
      +EditPackages=UDKBase
      +EditPackages=Dev_Ninja

      Now make a batch file for testing. Open NotePad and copy this:
      Code:
      @echo off
      cls
      C:\UDK\YOURINSTALL\Binaries\Win32\UDK.exe Empty?Game=Dev_Ninja.Dev_Ninja_PreGame -log -simmobile
      Save the file to your desktop as Dev_Ninja.bat (makes sure the save type is "All Files"). The first time you launch you'll be asked to compile scripts - do that. If all goes well the PreGame class will be opened and you'll be looking at the MainMenu. If it doesn't go well, make sure you followed all the steps and all your files are in the right place. If you're having a really hard time, just ask here I'll be glad to clarify.

      Have fun slicing developer meshes!

      Comment


        #4
        awesome, thanks for sharing!

        Comment


          #5
          I must say I thought more people would be interested in a full game's source - especially all in UnrealScript for iOS.

          ...Bump

          Comment


            #6
            If you added "zombie third person cam sprint stamina" to the title, they'd surely come running

            Thanks for sharing, bwhit.

            Comment


              #7
              `Welcome Spoof, thanks for checking it out.

              Comment


                #8
                Hello,
                which version of udk i must use ?

                Comment


                  #9
                  Hey lost, I've developed this on the latest release so that's what you'll need. Feb '13

                  Comment


                    #10
                    Hey ,
                    Actually it only works on the last version since the package is created in the last one so it can't be opened in a previous version .
                    And i tested your game and it work like a charm thank you very much for sharing ! there is so much to learn from this
                    Cheers.

                    Comment


                      #11
                      Doh! Edited post as to not confuse anyone else.

                      I'm glad you got it working! I look forward to what you make with it.
                      Cheers

                      Comment


                        #12
                        Mmm..added to ze library.
                        Thank you sir

                        Comment


                          #13
                          awesome!
                          ill probably just use the menu bit as a start for my menu
                          thank you

                          Comment


                            #14
                            Great post - thanks for the full-source, looks like a well structured project example.

                            Cheers
                            William

                            Comment


                              #15
                              No problem guys, hope you find it useful.

                              I'm scrolling through the source on my tablet and I found a problem. When the Options Object loads, only the R/G/B values are set and not the bShouldPlaySound bool. When working with the BasicSave/Load functions make sure you load all of the object's variables before you save it again or you'll override the unloaded variables with their default values.

                              I'll update when I get back to a computer.

                              Comment

                              Working...
                              X