Interlude 9.1 Introduction
One of the most useful design patterns in game design is being able to move an object on the screen using buttons (up, down, left and right).
Interlude 9.2 Solution 1 - One Tap per Move
Our first solution will move an object on the screen by a defined amount (shipSpeed) each time a button is tapped. We have defined the shipSpeed variable as a parameter so that you can tweak the distance each tap moves the ship. Once again we have used @Vega's Mesh Button class for the four directional buttons. Note that this code only works properly with a landscape orientation.
Let's have a look at the main class first.
--# Main -- Use this function to perform your initial setup function setup() -- This block of code is optional. We just include it in -- every project so that we have a method of version control. version = 1.0 saveProjectInfo("Description", "Move Object Demonstration") saveProjectInfo("Author", "Reefwing Software") saveProjectInfo("Date", "11th July 2012") saveProjectInfo("Version", version) print("MoveShip v"..version.."\n") -- Initialise the co-ordinates of your "Ship" -- These will be updated by the action methods -- associated with each directional button. shipPosition = vec2(WIDTH/2, HEIGHT/2) -- This slider parameter will control the ship "speed" -- The larger the number the further the ship will -- move with each button tap. -- -- The format of this function is: -- parameter("name", min value, max value, init value) parameter("shipSpeed", 1, 10, 4) -- Define the four Buttons used to move the ship -- They wont be visible until you draw() them. -- Note that 50 pixels is the minimum height for the default -- button font size. local mButtonSize = vec2(100, 50) local mLocX = WIDTH - 250 local mLocY = 100 leftButton = Button("Left", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) leftButton.action = function() leftButtonTapped() end mLocX = mLocX + 150 rightButton = Button("Right", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) rightButton.action = function() rightButtonTapped() end mLocX = mLocX - 75 mLocY = mLocY + 60 upButton = Button("Up", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) upButton.action = function() upButtonTapped() end mLocY = mLocY - 120 downButton = Button("Down", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) downButton.action = function() downButtonTapped() end -- Assign the colours that you want to use in your game. blackColour = color(0,0,0) end -- And now for the draw() function which is quite simple: -- -- This function gets called once every frame -- Codea will attempt to call draw() 60 times per second, it -- can be much less than this if you have a lot going on in your code. function draw() -- This sets a black background color background(blackColour) -- Do your drawing here -- Draw your ship at the current co-ordinates stored in shipPosition sprite("Tyrian Remastered:Boss D", shipPosition.x, shipPosition.y) -- Draw the four directional buttons leftButton:draw() rightButton:draw() upButton:draw() downButton:draw() end -- Most of the action happens in the button call back functions... -- -- Button action methods function leftButtonTapped() -- Update ship position as long as the ship isnt off the screen. -- math.max() returns the maximum value of the arguments, so -- if shipPosition < 0 it will set it to 0. shipPosition.x = math.max(shipPosition.x - shipSpeed, 0) end function rightButtonTapped() -- Update ship position as long as the ship isnt off the screen. -- math.min() returns the minimum value of the arguments, so -- if shipPosition > WIDTH it will set it to WIDTH. shipPosition.x = math.min(shipPosition.x + shipSpeed, WIDTH) end function upButtonTapped() shipPosition.y = math.min(shipPosition.y + shipSpeed, HEIGHT) end function downButtonTapped() shipPosition.y = math.max(shipPosition.y - shipSpeed, 0) end -- Handle screen touches -- Note that you need to pass any touches through -- to your button touch handlers. function touched(touch) leftButton:touched(touch) rightButton:touched(touch) upButton:touched(touch) downButton:touched(touch) end
Interlude 9.3 Solution 2 - Tap and Hold Continuous Move
In some cases you want your object to move for as long as you hold down the directional button. This turns out to be an easy change due to the functionality built into the Button class. We can detect whether a user is holding down a button by querying its state and seeing if it is "pressing". In this instance you don't need the button action methods (leftButtonTapped, rightButtonTapped, etc) so you can delete them if you aren't using them for something else.
The new draw() function is shown below. While a directional button is being held down its position will be updated by shipSpeed once per frame (roughly 60 times per second).
You can download the entire moveShipCode here.
function draw() -- This sets a black background color background(blackColour) -- Do your drawing here -- Draw your ship at the current co-ordinates stored in shipPosition if leftButton.state == "pressing" then shipPosition.x = math.max(shipPosition.x - shipSpeed, 0) elseif rightButton.state == "pressing" then shipPosition.x = math.min(shipPosition.x + shipSpeed, WIDTH) elseif upButton.state == "pressing" then shipPosition.y = math.min(shipPosition.y + shipSpeed, HEIGHT) elseif downButton.state == "pressing" then shipPosition.y = math.max(shipPosition.y - shipSpeed, 0) end sprite("Tyrian Remastered:Boss D", shipPosition.x, shipPosition.y) -- Draw the four directional buttons leftButton:draw() rightButton:draw() upButton:draw() downButton:draw() end
Interlude 9.4 Solution 3 - Adding Flames and Direction
This final version adds sprites to represent the ship engine flames (as shown above) and will point the ship in the right direction using rotation which is based on the directional buttons. You can download the complete program including the Button class.
--# Main -- Use this function to perform your initial setup function setup() -- This block of code is optional. We just include it in -- every project so that we have a method of version control. -- -- Version 3.0 adds rocket flames and points the ship in the -- direction of movement. version = 3.0 saveProjectInfo("Description", "Move Object Demonstration") saveProjectInfo("Author", "Reefwing Software") saveProjectInfo("Date", "11th July 2012") saveProjectInfo("Version", version) print("MoveShip v"..version.."\n") -- Initialise the co-ordinates of your "Ship" -- These will be updated in the draw() function to allow for -- continuous pressing of the directional buttons. shipPosition = vec2(WIDTH/2, HEIGHT/2) -- The ship sprite used faces down. Initially we want it facing up so -- we need to rotate the sprite 180 degrees when we draw() it. For those new to rotation, -- note that there are 360 degrees in a circle, 180 degrees is half a circle. -- We will then use this variable to rotate the ship so that it is -- facing in the right direction when the up, down, left and right -- buttons are tapped. rotationDegrees = 180 -- This slider parameter will control the ship "speed" -- The larger the number the further the ship will -- move with each button tap. -- -- The format of this function is: -- parameter("name", min value, max value, init value) parameter("shipSpeed", 1, 10, 4) -- Define the four Buttons used to move the ship -- They wont be visible until you draw() them. -- Note that 50 pixels is the minimum height for the default -- button font size. local mButtonSize = vec2(100, 50) local mLocX = WIDTH - 250 local mLocY = 100 leftButton = Button("Left", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) mLocX = mLocX + 150 rightButton = Button("Right", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) mLocX = mLocX - 75 mLocY = mLocY + 60 upButton = Button("Up", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) mLocY = mLocY - 120 downButton = Button("Down", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) -- Assign the colours that you want to use in your game. blackColour = color(0,0,0) end -- This function gets called once every frame -- Codea will attempt to call draw() 60 times per second, it -- can be much less than this if you have a lot going on in your code. function draw() -- This sets a black background color background(blackColour) -- Do your drawing here -- Draw your ship at the current co-ordinates stored in shipPosition -- -- pushMatrix() saves any transformations (rotate, translate or scale) that have been made. -- popMatrix() returns to these saved transformations. -- If you are going to perform transformations it is a good idea to encapsulate your code -- with these so that you don't have unexpected effects elsewhere. pushMatrix() local buttonDown = false if leftButton.state == "pressing" then shipPosition.x = math.max(shipPosition.x - shipSpeed, 0) buttonDown = true rotationDegrees = 270 elseif rightButton.state == "pressing" then shipPosition.x = math.min(shipPosition.x + shipSpeed, WIDTH) buttonDown = true rotationDegrees = 90 elseif upButton.state == "pressing" then shipPosition.y = math.min(shipPosition.y + shipSpeed, HEIGHT) buttonDown = true rotationDegrees = 180 elseif downButton.state == "pressing" then shipPosition.y = math.max(shipPosition.y - shipSpeed, 0) buttonDown = true rotationDegrees = 0 end translate(shipPosition.x, shipPosition.y) rotate(rotationDegrees) sprite("Tyrian Remastered:Boss D", 0, 0) if buttonDown then -- draw the engine flames sprite("Tyrian Remastered:Flame 1", 17, 100) sprite("Tyrian Remastered:Flame 1", -19, 100) end popMatrix() -- Draw the four directional buttons leftButton:draw() rightButton:draw() upButton:draw() downButton:draw() end -- Handle screen touches -- Note that you need to pass any touches through -- to your button touch handlers. function touched(touch) leftButton:touched(touch) rightButton:touched(touch) upButton:touched(touch) downButton:touched(touch) end
I am wondering what a "codeaimage" is in one of my programs i get an error like this:16: bad argument #2 to 'saveImage' (codeaimage expected, got string)
ReplyDeletemy second question is how do I use images from spritely in my projects this is actually trying to do when I got the error above.
Hi Anon,
DeleteIn the example above a "codeaimage" is "Tyrian Remastered:Boss D". It is used in the following context:
sprite("Tyrian Remastered:Boss D", 0, 0)
If you tap on the codeaimage string, the sprite pack dialog opens from which you can select an image (i.e a png file).
One of the sprite packs will be Dropbox. So if you open up a dropbox account you can access your custom images using this.
Have a look at the following tutorial if you want to learn how to use Spritely to create images and use them in your App: http://codeatuts.blogspot.com.au/2012/07/tutorial-6-minesweeper-part-1.html
I got the buttons to work for one movement per push, but when I went to the next section of the tutorial which makes it so you can touch and hold for multiple movements, it goes to a blank screen that isnt even in fullscreen. I think that it might have to do with that I'm not using the same button class you were in the tutorial. I am using the button and roundrect class from sounds plus. I'm not using the button class from this tutorial because I only need a right and left button. Also the image that Im am using is from spritely I imported it to my documents uing this app that I made:
ReplyDeletehttps://sites.google.com/site/codeaapps/spritely-saver
I want the ship to look pixelated but the way i have it set the ship starts on the right side of the screen. I put the game on this site so it wouldn't the comment to long :) heres what I have:
https://sites.google.com/site/codeaapps/spaceteroids
anybody?
DeleteDear Anonymous - I have already given you the code and classes which you need. Perhaps you should learn to debug your own code...
Delete