commit 5ab78b4c204cbbc6d5e585e1ca5d4591d960f740 Author: atseirjo Date: Fri May 22 09:16:59 2026 +0200 Initial commit: Star Explorer Solar2D Game diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b57ece2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Solar2D / Corona SDK +*.bak +*.orig +*.tmp +*.log + +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# Build directories +build/ +*.apk +*.ipa +*.app + +# IDE +.vscode/ +.idea/ +*.swp +*.swo diff --git a/AndroidResources/res/mipmap-anydpi-v26/ic_launcher.xml b/AndroidResources/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/AndroidResources/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AndroidResources/res/mipmap-hdpi/ic_launcher.png b/AndroidResources/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..00d8155 Binary files /dev/null and b/AndroidResources/res/mipmap-hdpi/ic_launcher.png differ diff --git a/AndroidResources/res/mipmap-hdpi/ic_launcher_foreground.png b/AndroidResources/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..e59e834 Binary files /dev/null and b/AndroidResources/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/AndroidResources/res/mipmap-mdpi/ic_launcher.png b/AndroidResources/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..b698936 Binary files /dev/null and b/AndroidResources/res/mipmap-mdpi/ic_launcher.png differ diff --git a/AndroidResources/res/mipmap-mdpi/ic_launcher_foreground.png b/AndroidResources/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..0248230 Binary files /dev/null and b/AndroidResources/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/AndroidResources/res/mipmap-xhdpi/ic_launcher.png b/AndroidResources/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..de9b007 Binary files /dev/null and b/AndroidResources/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/AndroidResources/res/mipmap-xhdpi/ic_launcher_foreground.png b/AndroidResources/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..1b524ae Binary files /dev/null and b/AndroidResources/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/AndroidResources/res/mipmap-xxhdpi/ic_launcher.png b/AndroidResources/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d09f0d9 Binary files /dev/null and b/AndroidResources/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/AndroidResources/res/mipmap-xxhdpi/ic_launcher_foreground.png b/AndroidResources/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..79e5a74 Binary files /dev/null and b/AndroidResources/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/AndroidResources/res/mipmap-xxxhdpi/ic_launcher.png b/AndroidResources/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..850ddc2 Binary files /dev/null and b/AndroidResources/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/AndroidResources/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/AndroidResources/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..76b589b Binary files /dev/null and b/AndroidResources/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/AndroidResources/res/values/values.xml b/AndroidResources/res/values/values.xml new file mode 100644 index 0000000..3533153 --- /dev/null +++ b/AndroidResources/res/values/values.xml @@ -0,0 +1,4 @@ + + + #e2e2e2 + diff --git a/Icon-120.png b/Icon-120.png new file mode 100644 index 0000000..3e20484 Binary files /dev/null and b/Icon-120.png differ diff --git a/Icon-152.png b/Icon-152.png new file mode 100644 index 0000000..3f23cc6 Binary files /dev/null and b/Icon-152.png differ diff --git a/Icon-167.png b/Icon-167.png new file mode 100644 index 0000000..b20f9e7 Binary files /dev/null and b/Icon-167.png differ diff --git a/Icon-180.png b/Icon-180.png new file mode 100644 index 0000000..82a43db Binary files /dev/null and b/Icon-180.png differ diff --git a/Icon-40.png b/Icon-40.png new file mode 100644 index 0000000..8d0f6ec Binary files /dev/null and b/Icon-40.png differ diff --git a/Icon-58.png b/Icon-58.png new file mode 100644 index 0000000..10bee7b Binary files /dev/null and b/Icon-58.png differ diff --git a/Icon-76.png b/Icon-76.png new file mode 100644 index 0000000..74e3ae3 Binary files /dev/null and b/Icon-76.png differ diff --git a/Icon-80.png b/Icon-80.png new file mode 100644 index 0000000..94dc723 Binary files /dev/null and b/Icon-80.png differ diff --git a/Icon-87.png b/Icon-87.png new file mode 100644 index 0000000..0673169 Binary files /dev/null and b/Icon-87.png differ diff --git a/Icon-hdpi.png b/Icon-hdpi.png new file mode 100644 index 0000000..d1553e0 Binary files /dev/null and b/Icon-hdpi.png differ diff --git a/Icon-ldpi.png b/Icon-ldpi.png new file mode 100644 index 0000000..dcfafc8 Binary files /dev/null and b/Icon-ldpi.png differ diff --git a/Icon-mdpi.png b/Icon-mdpi.png new file mode 100644 index 0000000..433e147 Binary files /dev/null and b/Icon-mdpi.png differ diff --git a/Icon-xhdpi.png b/Icon-xhdpi.png new file mode 100644 index 0000000..932acdf Binary files /dev/null and b/Icon-xhdpi.png differ diff --git a/Icon-xxhdpi.png b/Icon-xxhdpi.png new file mode 100644 index 0000000..bad0495 Binary files /dev/null and b/Icon-xxhdpi.png differ diff --git a/Icon-xxxhdpi.png b/Icon-xxxhdpi.png new file mode 100644 index 0000000..99e0798 Binary files /dev/null and b/Icon-xxxhdpi.png differ diff --git a/Icon.png b/Icon.png new file mode 100644 index 0000000..517e663 Binary files /dev/null and b/Icon.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Contents.json b/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..477b25b --- /dev/null +++ b/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,108 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-40.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-58.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-87.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-80.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-120.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-180.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-152.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-167.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-1024.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Images.xcassets/AppIcon.appiconset/Icon-1024.png b/Images.xcassets/AppIcon.appiconset/Icon-1024.png new file mode 100644 index 0000000..f62c818 Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-1024.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-120.png b/Images.xcassets/AppIcon.appiconset/Icon-120.png new file mode 100644 index 0000000..9272f16 Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-120.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-152.png b/Images.xcassets/AppIcon.appiconset/Icon-152.png new file mode 100644 index 0000000..df58a7b Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-152.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-167.png b/Images.xcassets/AppIcon.appiconset/Icon-167.png new file mode 100644 index 0000000..137dbac Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-167.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-180.png b/Images.xcassets/AppIcon.appiconset/Icon-180.png new file mode 100644 index 0000000..40fa8e3 Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-180.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-40.png b/Images.xcassets/AppIcon.appiconset/Icon-40.png new file mode 100644 index 0000000..a53cda3 Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-40.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-58.png b/Images.xcassets/AppIcon.appiconset/Icon-58.png new file mode 100644 index 0000000..76ef3f6 Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-58.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-76.png b/Images.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000..7fed12e Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-80.png b/Images.xcassets/AppIcon.appiconset/Icon-80.png new file mode 100644 index 0000000..b06a11f Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-80.png differ diff --git a/Images.xcassets/AppIcon.appiconset/Icon-87.png b/Images.xcassets/AppIcon.appiconset/Icon-87.png new file mode 100644 index 0000000..ea59b45 Binary files /dev/null and b/Images.xcassets/AppIcon.appiconset/Icon-87.png differ diff --git a/Images.xcassets/Contents.json b/Images.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib b/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib new file mode 100644 index 0000000..351e482 Binary files /dev/null and b/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib differ diff --git a/LaunchScreen.storyboardc/Info.plist b/LaunchScreen.storyboardc/Info.plist new file mode 100644 index 0000000..32288e8 Binary files /dev/null and b/LaunchScreen.storyboardc/Info.plist differ diff --git a/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib b/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib new file mode 100644 index 0000000..48a7d93 Binary files /dev/null and b/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib differ diff --git a/LaunchScreen.storyboardc/designable.storyboard b/LaunchScreen.storyboardc/designable.storyboard new file mode 100644 index 0000000..cca8ab4 --- /dev/null +++ b/LaunchScreen.storyboardc/designable.storyboard @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f1cbb70 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Star Explorer + +This is an example game built with [Corona](http://www.coronalabs.com) to go along with the [Getting Started](https://docs.coronalabs.com/guide/programming/index.html) tutorial. The included source code goes along with the respective chapter. + +## Chapters + +* [Introduction to Corona](https://docs.coronalabs.com/guide/programming/intro/index.html) +* [Chapter 1 — Creating an App](https://docs.coronalabs.com/guide/programming/01/index.html) +* [Chapter 2 — Upward & Onward](https://docs.coronalabs.com/guide/programming/02/index.html) +* [Chapter 3 — Bringing it to Life](https://docs.coronalabs.com/guide/programming/03/index.html) +* [Chapter 4 — Creating Scenes](https://docs.coronalabs.com/guide/programming/04/index.html) +* [Chapter 5 — Converting the Game to Composer](https://docs.coronalabs.com/guide/programming/05/index.html) +* [Chapter 6 — Implementing High Scores](https://docs.coronalabs.com/guide/programming/06/index.html) +* [Chapter 7 — Sounds and Music](https://docs.coronalabs.com/guide/programming/07/index.html) +* [Chapter 8 — Deployment](https://docs.coronalabs.com/guide/programming/08/index.html) + +## Credits + +[Corona Labs](http://www.coronalabs.com) would like to extend its gratitude to: + +* Dr. Brian Burton ([burtonsmediagroup.com](http://www.burtonsmediagroup.com)) for his guidance, direction, and core tutorial content in creating this series. He has also created several [books](http://www.burtonsmediagroup.com/) about programming in Corona SDK. + +* Eric Matyas ([soundimage.org](http://www.soundimage.org)) for the game's music and sound effects. Eric offers numerous audio tracks and sound effects on his website, available on a donation basis. + +* ([Kenney](http://kenney.nl/)) for some of the game artwork. Kenney game studio supports other developers by creating free game assets and high quality learning material. diff --git a/audio/80s-Space-Game_Looping.wav b/audio/80s-Space-Game_Looping.wav new file mode 100644 index 0000000..4dc64ef Binary files /dev/null and b/audio/80s-Space-Game_Looping.wav differ diff --git a/audio/Escape_Looping.wav b/audio/Escape_Looping.wav new file mode 100644 index 0000000..adf47b5 Binary files /dev/null and b/audio/Escape_Looping.wav differ diff --git a/audio/Midnight-Crawlers_Looping.wav b/audio/Midnight-Crawlers_Looping.wav new file mode 100644 index 0000000..df76bb5 Binary files /dev/null and b/audio/Midnight-Crawlers_Looping.wav differ diff --git a/audio/explosion.wav b/audio/explosion.wav new file mode 100644 index 0000000..f1c9156 Binary files /dev/null and b/audio/explosion.wav differ diff --git a/audio/fire.wav b/audio/fire.wav new file mode 100644 index 0000000..f04fd90 Binary files /dev/null and b/audio/fire.wav differ diff --git a/background.png b/background.png new file mode 100644 index 0000000..bc9e5b1 Binary files /dev/null and b/background.png differ diff --git a/build.settings b/build.settings new file mode 100644 index 0000000..561f8eb --- /dev/null +++ b/build.settings @@ -0,0 +1,57 @@ +-- +-- For more information on build.settings, see the Project Build Settings guide at: +-- https://docs.coronalabs.com/guide/distribution/buildSettings +-- + +settings = +{ + orientation = + { + -- Supported values for orientation: + -- portrait, portraitUpsideDown, landscapeLeft, landscapeRight + default = "portrait", + supported = { "portrait", }, + }, + + -- + -- Android section + -- + android = + { + usesPermissions = + { + "android.permission.INTERNET", + }, + }, + + -- + -- iOS section + -- + iphone = + { + xcassets = "Images.xcassets", + plist = + { + UIStatusBarHidden = false, + UILaunchStoryboardName = "LaunchScreen", + }, + }, + + -- + -- Plugins section + -- + plugins = + { + + }, + + -- + -- Project section + -- + excludeFiles = + { + -- Exclude unnecessary files for each platform + all = { "Icon.png", "Icon-*dpi.png", "Images.xcassets", }, + android = { "LaunchScreen.storyboardc", }, + }, +} diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..d4081ae --- /dev/null +++ b/config.lua @@ -0,0 +1,23 @@ +-- +-- For more information on config.lua see the Project Configuration Guide at: +-- https://docs.coronalabs.com/guide/basics/configSettings +-- + +application = +{ + content = + { + width = 768, + height = 1024, + scale = "zoomEven", + fps = 60, + + --[[ + imageSuffix = + { + ["@2x"] = 2, + ["@4x"] = 4, + }, + --]] + }, +} diff --git a/game.lua b/game.lua new file mode 100644 index 0000000..256cea0 --- /dev/null +++ b/game.lua @@ -0,0 +1,360 @@ + +local composer = require( "composer" ) + +local scene = composer.newScene() + +-- ----------------------------------------------------------------------------------- +-- Code outside of the scene event functions below will only be executed ONCE unless +-- the scene is removed entirely (not recycled) via "composer.removeScene()" +-- ----------------------------------------------------------------------------------- + +local physics = require( "physics" ) +physics.start() +physics.setGravity( 0, 0 ) + +-- Configure image sheet +local sheetOptions = +{ + frames = + { + { -- 1) asteroid 1 + x = 0, + y = 0, + width = 102, + height = 85 + }, + { -- 2) asteroid 2 + x = 0, + y = 85, + width = 90, + height = 83 + }, + { -- 3) asteroid 3 + x = 0, + y = 168, + width = 100, + height = 97 + }, + { -- 4) ship + x = 0, + y = 265, + width = 98, + height = 79 + }, + { -- 5) laser + x = 98, + y = 265, + width = 14, + height = 40 + }, + }, +} +local objectSheet = graphics.newImageSheet( "gameObjects.png", sheetOptions ) + +-- Initialize variables +local lives = 3 +local score = 0 +local died = false + +local asteroidsTable = {} + +local ship +local gameLoopTimer +local livesText +local scoreText + +local backGroup +local mainGroup +local uiGroup + +local explosionSound +local fireSound +local musicTrack + + +local function updateText() + livesText.text = "Lives: " .. lives + scoreText.text = "Score: " .. score +end + + +local function createAsteroid() + + local newAsteroid = display.newImageRect( mainGroup, objectSheet, 1, 102, 85 ) + table.insert( asteroidsTable, newAsteroid ) + physics.addBody( newAsteroid, "dynamic", { radius=40, bounce=0.8 } ) + newAsteroid.myName = "asteroid" + + local whereFrom = math.random( 3 ) + + if ( whereFrom == 1 ) then + -- From the left + newAsteroid.x = -60 + newAsteroid.y = math.random( 500 ) + newAsteroid:setLinearVelocity( math.random( 40,120 ), math.random( 20,60 ) ) + elseif ( whereFrom == 2 ) then + -- From the top + newAsteroid.x = math.random( display.contentWidth ) + newAsteroid.y = -60 + newAsteroid:setLinearVelocity( math.random( -40,40 ), math.random( 40,120 ) ) + elseif ( whereFrom == 3 ) then + -- From the right + newAsteroid.x = display.contentWidth + 60 + newAsteroid.y = math.random( 500 ) + newAsteroid:setLinearVelocity( math.random( -120,-40 ), math.random( 20,60 ) ) + end + + newAsteroid:applyTorque( math.random( -6,6 ) ) +end + + +local function fireLaser() + + -- Play fire sound! + audio.play( fireSound ) + + local newLaser = display.newImageRect( mainGroup, objectSheet, 5, 14, 40 ) + physics.addBody( newLaser, "dynamic", { isSensor=true } ) + newLaser.isBullet = true + newLaser.myName = "laser" + + newLaser.x = ship.x + newLaser.y = ship.y + newLaser:toBack() + + transition.to( newLaser, { y=-40, time=500, + onComplete = function() display.remove( newLaser ) end + } ) +end + + +local function dragShip( event ) + + local ship = event.target + local phase = event.phase + + if ( "began" == phase ) then + -- Set touch focus on the ship + display.currentStage:setFocus( ship ) + -- Store initial offset position + ship.touchOffsetX = event.x - ship.x + + elseif ( "moved" == phase ) then + -- Move the ship to the new touch position + ship.x = event.x - ship.touchOffsetX + + elseif ( "ended" == phase or "cancelled" == phase ) then + -- Release touch focus on the ship + display.currentStage:setFocus( nil ) + end + + return true -- Prevents touch propagation to underlying objects +end + + +local function gameLoop() + + -- Create new asteroid + createAsteroid() + + -- Remove asteroids which have drifted off screen + for i = #asteroidsTable, 1, -1 do + local thisAsteroid = asteroidsTable[i] + + if ( thisAsteroid.x < -100 or + thisAsteroid.x > display.contentWidth + 100 or + thisAsteroid.y < -100 or + thisAsteroid.y > display.contentHeight + 100 ) + then + display.remove( thisAsteroid ) + table.remove( asteroidsTable, i ) + end + end +end + + +local function restoreShip() + + ship.isBodyActive = false + ship.x = display.contentCenterX + ship.y = display.contentHeight - 100 + + -- Fade in the ship + transition.to( ship, { alpha=1, time=4000, + onComplete = function() + ship.isBodyActive = true + died = false + end + } ) +end + + +local function endGame() + composer.setVariable( "finalScore", score ) + composer.gotoScene( "highscores", { time=800, effect="crossFade" } ) +end + + +local function onCollision( event ) + + if ( event.phase == "began" ) then + + local obj1 = event.object1 + local obj2 = event.object2 + + if ( ( obj1.myName == "laser" and obj2.myName == "asteroid" ) or + ( obj1.myName == "asteroid" and obj2.myName == "laser" ) ) + then + -- Remove both the laser and asteroid + display.remove( obj1 ) + display.remove( obj2 ) + + -- Play explosion sound! + audio.play( explosionSound ) + + for i = #asteroidsTable, 1, -1 do + if ( asteroidsTable[i] == obj1 or asteroidsTable[i] == obj2 ) then + table.remove( asteroidsTable, i ) + break + end + end + + -- Increase score + score = score + 100 + scoreText.text = "Score: " .. score + + elseif ( ( obj1.myName == "ship" and obj2.myName == "asteroid" ) or + ( obj1.myName == "asteroid" and obj2.myName == "ship" ) ) + then + if ( died == false ) then + died = true + + -- Play explosion sound! + audio.play( explosionSound ) + + -- Update lives + lives = lives - 1 + livesText.text = "Lives: " .. lives + + if ( lives == 0 ) then + display.remove( ship ) + timer.performWithDelay( 2000, endGame ) + else + ship.alpha = 0 + timer.performWithDelay( 1000, restoreShip ) + end + end + end + end +end + + +-- ----------------------------------------------------------------------------------- +-- Scene event functions +-- ----------------------------------------------------------------------------------- + +-- create() +function scene:create( event ) + + local sceneGroup = self.view + -- Code here runs when the scene is first created but has not yet appeared on screen + + physics.pause() -- Temporarily pause the physics engine + + -- Set up display groups + backGroup = display.newGroup() -- Display group for the background image + sceneGroup:insert( backGroup ) -- Insert into the scene's view group + + mainGroup = display.newGroup() -- Display group for the ship, asteroids, lasers, etc. + sceneGroup:insert( mainGroup ) -- Insert into the scene's view group + + uiGroup = display.newGroup() -- Display group for UI objects like the score + sceneGroup:insert( uiGroup ) -- Insert into the scene's view group + + -- Load the background + local background = display.newImageRect( backGroup, "background.png", 800, 1400 ) + background.x = display.contentCenterX + background.y = display.contentCenterY + + ship = display.newImageRect( mainGroup, objectSheet, 4, 98, 79 ) + ship.x = display.contentCenterX + ship.y = display.contentHeight - 100 + physics.addBody( ship, { radius=30, isSensor=true } ) + ship.myName = "ship" + + -- Display lives and score + livesText = display.newText( uiGroup, "Lives: " .. lives, 200, 80, native.systemFont, 36 ) + scoreText = display.newText( uiGroup, "Score: " .. score, 400, 80, native.systemFont, 36 ) + + ship:addEventListener( "tap", fireLaser ) + ship:addEventListener( "touch", dragShip ) + + explosionSound = audio.loadSound( "audio/explosion.wav" ) + fireSound = audio.loadSound( "audio/fire.wav" ) + musicTrack = audio.loadStream( "audio/80s-Space-Game_Looping.wav" ) +end + + +-- show() +function scene:show( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is still off screen (but is about to come on screen) + + elseif ( phase == "did" ) then + -- Code here runs when the scene is entirely on screen + physics.start() + Runtime:addEventListener( "collision", onCollision ) + gameLoopTimer = timer.performWithDelay( 500, gameLoop, 0 ) + -- Start the music! + audio.play( musicTrack, { channel=1, loops=-1 } ) + end +end + + +-- hide() +function scene:hide( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is on screen (but is about to go off screen) + timer.cancel( gameLoopTimer ) + + elseif ( phase == "did" ) then + -- Code here runs immediately after the scene goes entirely off screen + Runtime:removeEventListener( "collision", onCollision ) + physics.pause() + -- Stop the music! + audio.stop( 1 ) + composer.removeScene( "game" ) + end +end + + +-- destroy() +function scene:destroy( event ) + + local sceneGroup = self.view + -- Code here runs prior to the removal of scene's view + -- Dispose audio! + audio.dispose( explosionSound ) + audio.dispose( fireSound ) + audio.dispose( musicTrack ) +end + + +-- ----------------------------------------------------------------------------------- +-- Scene event function listeners +-- ----------------------------------------------------------------------------------- +scene:addEventListener( "create", scene ) +scene:addEventListener( "show", scene ) +scene:addEventListener( "hide", scene ) +scene:addEventListener( "destroy", scene ) +-- ----------------------------------------------------------------------------------- + +return scene diff --git a/gameObjects.png b/gameObjects.png new file mode 100644 index 0000000..ea3d8d5 Binary files /dev/null and b/gameObjects.png differ diff --git a/highscores.lua b/highscores.lua new file mode 100644 index 0000000..4f533da --- /dev/null +++ b/highscores.lua @@ -0,0 +1,156 @@ + +local composer = require( "composer" ) + +local scene = composer.newScene() + +-- ----------------------------------------------------------------------------------- +-- Code outside of the scene event functions below will only be executed ONCE unless +-- the scene is removed entirely (not recycled) via "composer.removeScene()" +-- ----------------------------------------------------------------------------------- + +-- Initialize variables +local json = require( "json" ) + +local scoresTable = {} + +local filePath = system.pathForFile( "scores.json", system.DocumentsDirectory ) + + +local function loadScores() + + local file = io.open( filePath, "r" ) + + if file then + local contents = file:read( "*a" ) + io.close( file ) + scoresTable = json.decode( contents ) + end + + if ( scoresTable == nil or #scoresTable == 0 ) then + scoresTable = { 10000, 7500, 5200, 4700, 3500, 3200, 1200, 1100, 800, 500 } + end +end + + +local function saveScores() + + for i = #scoresTable, 11, -1 do + table.remove( scoresTable, i ) + end + + local file = io.open( filePath, "w" ) + + if file then + file:write( json.encode( scoresTable ) ) + io.close( file ) + end +end + + +local function gotoMenu() + composer.gotoScene( "menu", { time=800, effect="crossFade" } ) +end + + +-- ----------------------------------------------------------------------------------- +-- Scene event functions +-- ----------------------------------------------------------------------------------- + +-- create() +function scene:create( event ) + + local sceneGroup = self.view + -- Code here runs when the scene is first created but has not yet appeared on screen + + -- Load the previous scores + loadScores() + + -- Insert the saved score from the last game into the table, then reset it + table.insert( scoresTable, composer.getVariable( "finalScore" ) ) + composer.setVariable( "finalScore", 0 ) + + -- Sort the table entries from highest to lowest + local function compare( a, b ) + return a > b + end + table.sort( scoresTable, compare ) + + -- Save the scores + saveScores() + + local background = display.newImageRect( sceneGroup, "background.png", 800, 1400 ) + background.x = display.contentCenterX + background.y = display.contentCenterY + + local highScoresHeader = display.newText( sceneGroup, "High Scores", display.contentCenterX, 100, native.systemFont, 44 ) + + for i = 1, 10 do + if ( scoresTable[i] ) then + local yPos = 150 + ( i * 56 ) + + local rankNum = display.newText( sceneGroup, i .. ")", display.contentCenterX-50, yPos, native.systemFont, 36 ) + rankNum:setFillColor( 0.8 ) + rankNum.anchorX = 1 + + local thisScore = display.newText( sceneGroup, scoresTable[i], display.contentCenterX-30, yPos, native.systemFont, 36 ) + thisScore.anchorX = 0 + end + end + + local menuButton = display.newText( sceneGroup, "Menu", display.contentCenterX, 810, native.systemFont, 44 ) + menuButton:setFillColor( 0.75, 0.78, 1 ) + menuButton:addEventListener( "tap", gotoMenu ) +end + + +-- show() +function scene:show( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is still off screen (but is about to come on screen) + + elseif ( phase == "did" ) then + -- Code here runs when the scene is entirely on screen + + end +end + + +-- hide() +function scene:hide( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is on screen (but is about to go off screen) + + elseif ( phase == "did" ) then + -- Code here runs immediately after the scene goes entirely off screen + composer.removeScene( "highscores" ) + end +end + + +-- destroy() +function scene:destroy( event ) + + local sceneGroup = self.view + -- Code here runs prior to the removal of scene's view + +end + + +-- ----------------------------------------------------------------------------------- +-- Scene event function listeners +-- ----------------------------------------------------------------------------------- +scene:addEventListener( "create", scene ) +scene:addEventListener( "show", scene ) +scene:addEventListener( "hide", scene ) +scene:addEventListener( "destroy", scene ) +-- ----------------------------------------------------------------------------------- + +return scene diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..60d9b3d --- /dev/null +++ b/main.lua @@ -0,0 +1,25 @@ +-- Copyright (c) 2017 Corona Labs Inc. +-- Code is MIT licensed and can be re-used; see https://www.coronalabs.com/links/code/license +-- Other assets are licensed by their creators: +-- Art assets by Kenney: http://kenney.nl/assets +-- Music and sound effect assets by Eric Matyas: http://www.soundimage.org + +print("main.lua file accessed") + +local composer = require("composer") + +-- Hide status bar +display.setStatusBar(display.HiddenStatusBar) + +-- Seed the random number generator +math.randomseed(os.time()) + +-- Reserve channel 1 for background music +audio.reserveChannels(1) +-- Reduce the overall volume of the channel +audio.setVolume(0.5, { channel = 1 }) + +-- Go to the menu screen +composer.gotoScene("menu") + + diff --git a/main_original.lua b/main_original.lua new file mode 100644 index 0000000..3e4151e --- /dev/null +++ b/main_original.lua @@ -0,0 +1,255 @@ +-- Copyright (c) 2017 Corona Labs Inc. +-- Code is MIT licensed and can be re-used; see https://www.coronalabs.com/links/code/license +-- Other assets are licensed by their creators: +-- Art assets by Kenney: http://kenney.nl/assets +-- Music and sound effect assets by Eric Matyas: http://www.soundimage.org + +local physics = require( "physics" ) +physics.start() +physics.setGravity( 0, 0 ) + +-- Seed the random number generator +math.randomseed( os.time() ) + +-- Configure image sheet +local sheetOptions = +{ + frames = + { + { -- 1) asteroid 1 + x = 0, + y = 0, + width = 102, + height = 85 + }, + { -- 2) asteroid 2 + x = 0, + y = 85, + width = 90, + height = 83 + }, + { -- 3) asteroid 3 + x = 0, + y = 168, + width = 100, + height = 97 + }, + { -- 4) ship + x = 0, + y = 265, + width = 98, + height = 79 + }, + { -- 5) laser + x = 98, + y = 265, + width = 14, + height = 40 + }, + }, +} +local objectSheet = graphics.newImageSheet( "gameObjects.png", sheetOptions ) + +-- Initialize variables +local lives = 3 +local score = 0 +local died = false + +local asteroidsTable = {} + +local ship +local gameLoopTimer +local livesText +local scoreText + +-- Set up display groups +local backGroup = display.newGroup() -- Display group for the background image +local mainGroup = display.newGroup() -- Display group for the ship, asteroids, lasers, etc. +local uiGroup = display.newGroup() -- Display group for UI objects like the score + +-- Load the background +local background = display.newImageRect( backGroup, "background.png", 800, 1400 ) +background.x = display.contentCenterX +background.y = display.contentCenterY + +ship = display.newImageRect( mainGroup, objectSheet, 4, 98, 79 ) +ship.x = display.contentCenterX +ship.y = display.contentHeight - 100 +physics.addBody( ship, { radius=30, isSensor=true } ) +ship.myName = "ship" + +-- Display lives and score +livesText = display.newText( uiGroup, "Lives: " .. lives, 200, 80, native.systemFont, 36 ) +scoreText = display.newText( uiGroup, "Score: " .. score, 400, 80, native.systemFont, 36 ) + +-- Hide the status bar +display.setStatusBar( display.HiddenStatusBar ) + + +local function updateText() + livesText.text = "Lives: " .. lives + scoreText.text = "Score: " .. score +end + + +local function createAsteroid() + + local newAsteroid = display.newImageRect( mainGroup, objectSheet, 1, 102, 85 ) + table.insert( asteroidsTable, newAsteroid ) + physics.addBody( newAsteroid, "dynamic", { radius=40, bounce=0.8 } ) + newAsteroid.myName = "asteroid" + + local whereFrom = math.random( 3 ) + + if ( whereFrom == 1 ) then + -- From the left + newAsteroid.x = -60 + newAsteroid.y = math.random( 500 ) + newAsteroid:setLinearVelocity( math.random( 40,120 ), math.random( 20,60 ) ) + elseif ( whereFrom == 2 ) then + -- From the top + newAsteroid.x = math.random( display.contentWidth ) + newAsteroid.y = -60 + newAsteroid:setLinearVelocity( math.random( -40,40 ), math.random( 40,120 ) ) + elseif ( whereFrom == 3 ) then + -- From the right + newAsteroid.x = display.contentWidth + 60 + newAsteroid.y = math.random( 500 ) + newAsteroid:setLinearVelocity( math.random( -120,-40 ), math.random( 20,60 ) ) + end + + newAsteroid:applyTorque( math.random( -6,6 ) ) +end + + +local function fireLaser() + + local newLaser = display.newImageRect( mainGroup, objectSheet, 5, 14, 40 ) + physics.addBody( newLaser, "dynamic", { isSensor=true } ) + newLaser.isBullet = true + newLaser.myName = "laser" + + newLaser.x = ship.x + newLaser.y = ship.y + newLaser:toBack() + + transition.to( newLaser, { y=-40, time=500, + onComplete = function() display.remove( newLaser ) end + } ) +end + +ship:addEventListener( "tap", fireLaser ) + + +local function dragShip( event ) + + local ship = event.target + local phase = event.phase + + if ( "began" == phase ) then + -- Set touch focus on the ship + display.currentStage:setFocus( ship ) + -- Store initial offset position + ship.touchOffsetX = event.x - ship.x + + elseif ( "moved" == phase ) then + -- Move the ship to the new touch position + ship.x = event.x - ship.touchOffsetX + + elseif ( "ended" == phase or "cancelled" == phase ) then + -- Release touch focus on the ship + display.currentStage:setFocus( nil ) + end + + return true -- Prevents touch propagation to underlying objects +end + +ship:addEventListener( "touch", dragShip ) + + +local function gameLoop() + + -- Create new asteroid + createAsteroid() + + -- Remove asteroids which have drifted off screen + for i = #asteroidsTable, 1, -1 do + local thisAsteroid = asteroidsTable[i] + + if ( thisAsteroid.x < -100 or + thisAsteroid.x > display.contentWidth + 100 or + thisAsteroid.y < -100 or + thisAsteroid.y > display.contentHeight + 100 ) + then + display.remove( thisAsteroid ) + table.remove( asteroidsTable, i ) + end + end +end + +gameLoopTimer = timer.performWithDelay( 500, gameLoop, 0 ) + + +local function restoreShip() + + ship.isBodyActive = false + ship.x = display.contentCenterX + ship.y = display.contentHeight - 100 + + -- Fade in the ship + transition.to( ship, { alpha=1, time=4000, + onComplete = function() + ship.isBodyActive = true + died = false + end + } ) +end + + +local function onCollision( event ) + + if ( event.phase == "began" ) then + + local obj1 = event.object1 + local obj2 = event.object2 + + if ( ( obj1.myName == "laser" and obj2.myName == "asteroid" ) or + ( obj1.myName == "asteroid" and obj2.myName == "laser" ) ) + then + -- Remove both the laser and asteroid + display.remove( obj1 ) + display.remove( obj2 ) + + for i = #asteroidsTable, 1, -1 do + if ( asteroidsTable[i] == obj1 or asteroidsTable[i] == obj2 ) then + table.remove( asteroidsTable, i ) + break + end + end + + -- Increase score + score = score + 100 + scoreText.text = "Score: " .. score + + elseif ( ( obj1.myName == "ship" and obj2.myName == "asteroid" ) or + ( obj1.myName == "asteroid" and obj2.myName == "ship" ) ) + then + if ( died == false ) then + died = true + + -- Update lives + lives = lives - 1 + livesText.text = "Lives: " .. lives + + if ( lives == 0 ) then + display.remove( ship ) + else + ship.alpha = 0 + timer.performWithDelay( 1000, restoreShip ) + end + end + end + end +end + +Runtime:addEventListener( "collision", onCollision ) diff --git a/menu.lua b/menu.lua new file mode 100644 index 0000000..9b0d70b --- /dev/null +++ b/menu.lua @@ -0,0 +1,99 @@ + +local composer = require( "composer" ) + +local scene = composer.newScene() + +-- ----------------------------------------------------------------------------------- +-- Code outside of the scene event functions below will only be executed ONCE unless +-- the scene is removed entirely (not recycled) via "composer.removeScene()" +-- ----------------------------------------------------------------------------------- + +local function gotoGame() + composer.gotoScene( "game", { time=800, effect="crossFade" } ) +end + +local function gotoHighScores() + composer.gotoScene( "highscores", { time=800, effect="crossFade" } ) +end + + +-- ----------------------------------------------------------------------------------- +-- Scene event functions +-- ----------------------------------------------------------------------------------- + +-- create() +function scene:create( event ) + + local sceneGroup = self.view + -- Code here runs when the scene is first created but has not yet appeared on screen + + local background = display.newImageRect( sceneGroup, "background.png", 800, 1400 ) + background.x = display.contentCenterX + background.y = display.contentCenterY + + local title = display.newImageRect( sceneGroup, "title.png", 500, 80 ) + title.x = display.contentCenterX + title.y = 200 + + local playButton = display.newText( sceneGroup, "Play", display.contentCenterX, 700, native.systemFont, 44 ) + playButton:setFillColor( 0.82, 0.86, 1 ) + + local highScoresButton = display.newText( sceneGroup, "High Scores", display.contentCenterX, 810, native.systemFont, 44 ) + highScoresButton:setFillColor( 0.75, 0.78, 1 ) + + playButton:addEventListener( "tap", gotoGame ) + highScoresButton:addEventListener( "tap", gotoHighScores ) +end + + +-- show() +function scene:show( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is still off screen (but is about to come on screen) + + elseif ( phase == "did" ) then + -- Code here runs when the scene is entirely on screen + + end +end + + +-- hide() +function scene:hide( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is on screen (but is about to go off screen) + + elseif ( phase == "did" ) then + -- Code here runs immediately after the scene goes entirely off screen + + end +end + + +-- destroy() +function scene:destroy( event ) + + local sceneGroup = self.view + -- Code here runs prior to the removal of scene's view + +end + + +-- ----------------------------------------------------------------------------------- +-- Scene event function listeners +-- ----------------------------------------------------------------------------------- +scene:addEventListener( "create", scene ) +scene:addEventListener( "show", scene ) +scene:addEventListener( "hide", scene ) +scene:addEventListener( "destroy", scene ) +-- ----------------------------------------------------------------------------------- + +return scene diff --git a/scene-template.lua b/scene-template.lua new file mode 100644 index 0000000..70a0379 --- /dev/null +++ b/scene-template.lua @@ -0,0 +1,77 @@ + +local composer = require( "composer" ) + +local scene = composer.newScene() + +-- ----------------------------------------------------------------------------------- +-- Code outside of the scene event functions below will only be executed ONCE unless +-- the scene is removed entirely (not recycled) via "composer.removeScene()" +-- ----------------------------------------------------------------------------------- + + + + +-- ----------------------------------------------------------------------------------- +-- Scene event functions +-- ----------------------------------------------------------------------------------- + +-- create() +function scene:create( event ) + + local sceneGroup = self.view + -- Code here runs when the scene is first created but has not yet appeared on screen + +end + + +-- show() +function scene:show( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is still off screen (but is about to come on screen) + + elseif ( phase == "did" ) then + -- Code here runs when the scene is entirely on screen + + end +end + + +-- hide() +function scene:hide( event ) + + local sceneGroup = self.view + local phase = event.phase + + if ( phase == "will" ) then + -- Code here runs when the scene is on screen (but is about to go off screen) + + elseif ( phase == "did" ) then + -- Code here runs immediately after the scene goes entirely off screen + + end +end + + +-- destroy() +function scene:destroy( event ) + + local sceneGroup = self.view + -- Code here runs prior to the removal of scene's view + +end + + +-- ----------------------------------------------------------------------------------- +-- Scene event function listeners +-- ----------------------------------------------------------------------------------- +scene:addEventListener( "create", scene ) +scene:addEventListener( "show", scene ) +scene:addEventListener( "hide", scene ) +scene:addEventListener( "destroy", scene ) +-- ----------------------------------------------------------------------------------- + +return scene diff --git a/title.png b/title.png new file mode 100644 index 0000000..9e061a6 Binary files /dev/null and b/title.png differ