Quantcast
Channel: Envato Tuts+ Code - Mobile Development
Viewing all 1836 articles
Browse latest View live

iOS Succinctly – Audio

$
0
0

iOS provides several options for working with audio. In this chapter, we’ll introduce two frameworks for dealing with different types of sounds. The Audio Toolbox Framework includes a C library for playing simple system sounds, and the AVFoundation Framework provides an object-oriented interface for intermediate-level playback and recording.

For both frameworks, interacting with audio files is conceptually the same as interacting with images. You still use NSBundle to locate the file on disk, and then load its contents with another class dedicated to interpreting the different audio formats.

The system sound API is defined in the Audio Toolbox Framework. This entire framework is written in C instead of Objective-C, so we’ll be working with the C interface in an iOS application for the first half of this chapter. This changes how we’ll be interacting with core iOS objects, but don’t let that scare you. We’re still dealing with the same objects and concepts as we have been throughout this entire book. For example, instead of using the mainBundle method of NSBundle, we’re going to use a C function called CFBundleGetMainBundle() to access the application bundle.


Creating the Example Application

As in the previous chapter, all we’re going to need is a simple Single View Application. Create a new project and call it Audio. As usual, use edu.self for the Company Identifier and iPhone for Devices, and make sure both the Use Storyboards and Use Automatic Reference Counting check boxes are selected.

Unlike the previous chapter, we need to access two audio frameworks, which aren’t included in the template. To add a new framework to the project, click the project icon in the Project Navigator and select the Audio target. In the Summary tab, scroll down to the Linked Frameworks and Libraries section.

tutorial_image
Figure 113: The frameworks currently included in our project

These are the code libraries you have access to from the application code. You need to add the Audio Toolbox Framework in order to work with system sounds, so click the plus sign in the bottom-left corner. The resulting dialog contains a list of all of the available frameworks and libraries. Start typing “audiotoolbox” in the search bar to find our desired framework.

tutorial_image
Figure 114: Searching for the Audio Toolbox Framework

Click AudioToolbox.framework and then click Add to include it in the project. We’ll also need access to the AVFoundation Framework for the AVAudioPlayer portion of this chapter, so repeat the process for this library, too. You should see both AVFoundation.framework and AudioToolbox.framework in the Summary tab before moving on.

tutorial_image
Figure 115: All of the required libraries for our example project

We’re now ready to start working with system sounds and AVAudioPlayer.


System Sounds

System sounds are designed to be used for simple things like alerts and UI sound effects (for example, button clicks). They are very easy to use, but the trade off is that they don’t provide many options for controlling playback. The major limitations of the system sound API are as follows.

  • Sounds cannot be longer than thirty seconds.
  • You cannot control the volume of system sounds. They use the current device volume. Only one sound can play at a time.
  • Sounds cannot be looped or played after a certain delay.
  • They only support .caf, .aif, and .wav files.
  • It’s not possible to configure how sounds deal with interruptions like incoming calls.

If you find these are too restricting, you should use the AVAudioPlayer discussed later in this chapter. Again, system sounds are meant for specific use cases. If your application is using sounds for more than UI feedback and alerts, you should probably use AVAudioPlayer.

Accessing the Sound File

The resource package for this book contains a short sound file called blip.wav. Add this to the application bundle by dragging it into the Project Navigator. Remember that system sounds use a C interface, so we’ll be accessing this file using a slightly different method than previous chapters. In ViewController.m, change viewDidLoad to the following.

- (void)viewDidLoad {
    [super viewDidLoad];
    CFURLRef blipURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
                                               CFSTR ("blip"),
                                               CFSTR ("wav"),
                                               NULL);
    NSLog(@"%@", blipURL);
}

CFBundleGetMainBundle() is essentially the same as [[NSBundle] mainBundle], except it returns the CoreFoundation object that represents the application bundle instead of the Foundation Framework’s version. The CoreFoundation Framework is a lower-level alternative to the Foundation Framework. We need to use CoreFoundation objects here because that’s what the Audio Toolbox accepts as arguments.

The CFBundleCopyResourceURL() is the CoreFoundation version of NSBundle’s pathForResource:ofType: method. It simply returns the path to the requested resource, and you should see a familiar file path in the NSLog() output.

Once you have a path to the sound file, you need to create a sound object using the AudioServicesCreateSystemSoundID() function. Add the following to viewDidLoad.

AudioServicesCreateSystemSoundID(blipURL, &_blipOne);

This function reads in the content of the sound file, turns it into a sound object that the system sound API knows how to play, and returns an ID you can use to reference it later. AudioServicesCreateSystemSoundID() is defined by the Audio Toolbox Framework, so you need to import it into ViewController.m before you can use it. Add the following to the top of the file.

#import <AudioToolbox/AudioToolbox.h>

You’re storing the sound object’s ID in a private instance variable called _blip, so add that to the implementation declaration, too.

@implementation ViewController {
    SystemSoundID _blip;
}
Playing the Sounds
Next, programmatically add a button to the scene; that button will play the sound when clicked.
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [aButton setTitle:@"Blip"
             forState:UIControlStateNormal];
    [aButton addTarget:self
                action:@selector(playBlip:)
      forControlEvents:UIControlEventTouchUpInside];
aButton.frame = CGRectMake(100.0, 200.0, 120.0, 40.0);
[[self view] addSubview:aButton];
For the playBlip: action, we’ll call the AudioServicesPlaySystemSound() function to play the sound. It accepts a single argument, which should be the SystemSoundID that refers to the desired sound object (e.g., _blip).
- (void)playBlip:(id)sender {
    AudioServicesPlaySystemSound(_blip);
}

When you compile the project, you should be able to click the button and hear a short sound. Make sure your speakers are not muted.

tutorial_image
Figure 116: A blipping button

If you click the button while the sound is still playing, you’ll notice that iOS cancels the current sound and begins a new one. Again, system sounds are intended for UI sound effects and other straightforward applications. To play multiple sounds simultaneously, you’ll need to upgrade to the AVAudioPlayer discussed in the following section.

AudioServicesCreateSystemSoundID() and AudioServicesPlaySystemSound() are pretty much all there is to system sounds. You may also find the AudioServicesPlayAlertSound() function useful. It works exactly like AudioServicesPlaySystemSound(), but it makes the phone vibrate if the user has it enabled.


AVAudioPlayer

The AVFoundation Framework provides a higher-level interface for managing audio playback. For games, custom music players, and other apps that require sophisticated audio controls, this is the framework you should use. Some of its capabilities include:

  • No limitation on sound duration.
  • You can play multiple sounds at the same time.
  • Volume can be controlled for individual sounds.
  • It’s possible to loop a sound or perform other actions when it finishes playing.
  • You can jump to arbitrary points in the sound file.
  • It’s easy to configure behavior for handling interruptions through a delegate object.

AVAudioPlayer is the main class you’ll use from the AVFoundation Framework. It provides an object-oriented way to set the volume, play the sound, stop the sound, specify what part of the sound to play, and so on. You can think of it as the audio equivalent of UIImage/UIImageView.

Supported file formats for AVAudioPlayer include .m4a, .aac, .wav, .mp3, .aif, .pcm, .caf, and a few others. See Apple’s Multimedia Programming Guide for a detailed discussion of audio file formats.

Accessing the Song

AVAudioPlayer is designed to be used with longer sound files, like songs, so we’ve distributed a public domain Billie Holiday tune with this book. Drag the good-morning-heartache.mp3 file from the resource package into the Project Navigator to add it to the bundle.

Since AVFoundation is an Objective-C library, we can go back to using NSBundle to access media assets. Go ahead and remove everything in viewDidLoad, and replace it with the following.

NSURL *soundFileURL = [[NSBundle mainBundle]
                        URLForResource:@"good-morning-heartache"
                         withExtension:@"mp3"];
    _player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL
                                                     error:nil];

The URLForResource:withExtension: method is the NSURL equivalent to pathForResource:ofType:. This was a better option for finding the audio file since you needed an NSURL object to initialize the AVAudioPlayer. The initWithContentsOfURL:error: method loads the content of the sound file into the AVAudioPlayer instance, much like NSString’s stringWithContentsOfURL:encoding:error: method.

AVAudioPlayer resides in the AVFoundation library, so be sure to add the following import statement to ViewController.h. We’ll need to access it from the header file later on.

#import <AVFoundation/AVFoundation.h>

In addition, you’ll need a few private instance variables. Change the implementation declaration to the following.

@implementation ViewController {
    AVAudioPlayer *_player;
    UIButton *_playButton;
    UIButton *_stopButton;
}
Playing the Song
Next, add a Play button to the scene:
_playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_playButton setTitle:@"Play"
             forState:UIControlStateNormal];
[_playButton addTarget:self
                action:@selector(playOrPause:)
      forControlEvents:UIControlEventTouchUpInside];
_playButton.frame = CGRectMake(100.0, 170.0, 120.0, 40.0);
[[self view] addSubview:_playButton];
The playOrPause: action should be implemented like this:
- (void)playOrPause:(id)sender {
    if (_player.playing) {
        [_player pause];
        [_playButton setTitle:@"Play" forState:UIControlStateNormal];
    } else {
        [_player play];
        [_playButton setTitle:@"Pause" forState:UIControlStateNormal];
    }
}

This checks to see if the song is already playing via AVAudioPlayer’s playing property. If it is, it pauses the song using the pause method and changes the button’s title accordingly. If it’s not, it starts the song by calling the play method. You should now be able to compile the app and play or pause a song by tapping the button.

Let’s take this one step further by creating a Stop button. Add the following to the viewDidLoad method.

_stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_stopButton setTitle:@"Stop"
             forState:UIControlStateNormal];
[_stopButton addTarget:self
                action:@selector(stop:)
      forControlEvents:UIControlEventTouchUpInside];
_stopButton.frame = CGRectMake(100.0, 230.0, 120.0, 40.0);
[[self view] addSubview:_stopButton];[/sourcecode]
The corresponding action calls the AVAudioPlayer’s stop method. Note that this method does not change current playback position—it differs from the pause method only in that it undoes the preloading performed by the play method. To jump back to the beginning of the song like you would expect from a typical Stop button, you need to manually set the currentTime property of AVAudioPlayer, like so:</p>
[sourcecode]- (void)stop:(id)sender {
    [_player stop];
    _player.currentTime = 0;
    [_playButton setTitle:@"Play" forState:UIControlStateNormal];
}

AVAudioPlayer Delegates

Another advantage of using AVAudioPlayer over the system sounds API is that it allows you to handle interruptions using the familiar delegate pattern. The AVAudioPlayerDelegate protocol defines a number of methods that let you know when certain events occur outside of your application.

When an interruption like an incoming phone call begins, the audioPlayerBeginInterruption: method is called on the delegate object. The sound will pause automatically, but this gives you a chance to write any other custom handling code you may need. Likewise, the audioPlayerEndInterruption:withOptions: method is called when the interruption is over and you can use the audio player again. However, the system does not automatically resume playback. You need to manually call the play method if you want this to happen.

In addition to interruption handling, the delegate object also lets you know when the sound has finished playing. By defining an audioPlayerDidFinishPlaying:successfully: method on the delegate object, you can do custom cleanup work. For example, you can use this to reset the play button and jump back to the beginning of the song, like so.

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    [self stop:nil];
    NSLog(@"Song finished playing!");
}

For this code to work, you need to make the view controller a formal delegate for the AVAudioPlayer. In ViewController.h, change the interface declaration to the following.

@interface ViewController : UIViewController <AVAudioPlayerDelegate>

Then, the view controller needs to assign itself as the delegate object. In the viewDidLoad method of ViewController.m, add the following line.

_player.delegate = self;

Now if you let the song play through to the end, the Pause button will automatically turn back into a Play button, and clicking it will start the song from the beginning.

tutorial_image
Figure 117: Our simple music player

Summary

In this chapter, we discussed two ways to play audio from an iOS device, system sounds and AVAudioPlayer. The former should be used only for short, simple sounds that don’t need much custom configuration, and the latter gives you more control over the audio playback.

For the average iOS app, the AVFoundation Framework provides a good balance between a user-friendly API and fine-grained control over your sounds. But, keep in mind that iOS also provides more advanced, low-level audio capabilities like Audio Queue Services, as well as a higher-level API for interacting with the user’s existing iTunes library via the Media Player Framework. For specialized applications, you can even drop down into OpenAL, which is a cross-platform library for simulating a 3-D audio environment.

A lot of work has gone into making iOS a multimedia-friendly platform, so there’s no shortage of options for managing images, sounds, and videos from within your application.


Conclusion

This chapter covered the basics of iOS app development. We started by building a simple user interface, which introduced us to the fundamental design patterns of iOS: model-view-controller, delegate objects, and target-action. Then we dove into multi-scene applications and learned how iOS lets scenes communicate with each other and automatically handles transitions from one scene to another. After that, we discussed the iOS file system, application bundles, and required resources like app icons, launch images, and the information property list. Bundles also happened to be the method for localizing applications to multiple regions in iOS, so we were able to serve translated images and text effortlessly to different language speakers. Finally, we took a brief look at the audio capabilities by creating a simple UI sound effect and music player.

While there are still dozens of frameworks left for you to explore on your own, I hope you feel comfortable navigating an Xcode project. Along with some hands-on experience, what you should take away from this book is a high-level understanding of the iOS architecture—how interfaces are created and laid out using UIKit and storyboards, and what classes and libraries to use for managing media assets. Armed with this knowledge, you should be more than ready to venture into the real world of iOS app development.

This lesson represents a chapter from iOS Succinctly, a free eBook from the team at Syncfusion.


Android SDK: Create an Arithmetic Game – Gameplay Logic

$
0
0

In this series we are creating a simple arithmetic game for Android. The game is going to display a calculator-style interface to users, repeatedly presenting questions and keeping track of how many correct answers they score in a row. In the first part of the series we built the user interface, got the main Activity class setup, and laid the foundations for another three Activity class. In this part, we will work on the gameplay logic, with the user selecting from three possible levels of difficulty. We will implement the question and answer gameplay with four operators: addition, subtraction, multiplication, and division. The game will choose operator and operands randomly, with the process tweaked for each operator.


Series Format

This series on Creating an Arithmetic Game will be released in three parts:


Game Overview

The following is a screenshot of the game I’ll teach you how to build:

Arithmetic Game

The main Activity presents three options: Play, How to Play, and High Scores. After pressing the Play option, the user must choose a difficulty level, and then gameplay will begin. Each answer entered by the user will be given a tick or cross as response, while the score will continue incrementing until the user enters a wrong answer or quits the game. We will complete the How to Play and High Scores functionality in the final part of the series, as well as saving the app instance’s state.


1. Launch the Game

Step 1

Last time we prepared the main Activity class to listen for clicks on the three buttons – let’s now implement what happens when the user clicks the Play button. First add the following imports to the app’s main class:

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;

We want the user to select one of three levels to start a game – add an instance variable to the class to store the level names in an array:

private String[] levelNames = {"Easy", "Medium", "Hard"};

In your onClick method create an Alert Dialog in the if statement we created for the Play button.

AlertDialog.Builder builder = new AlertDialog.Builder(this);

Now set the details of the dialog, passing the level names array and setting up the click listener for the options:

builder.setTitle("Choose a level")
	.setSingleChoiceItems(levelNames, 0, new DialogInterface.OnClickListener() {
		public void onClick(DialogInterface dialog, int which) {
			dialog.dismiss();
			//start gameplay
			startPlay(which);
		}
	});

When a level is selected, the onClick method for the Alert Dialog calls a helper method we will add next, passing the index of the selected level from the array. Before we implement the helper method, still inside the if statement for the Play button, create and show the dialog:

AlertDialog ad = builder.create();
ad.show();
Choosing a Level

Now add the helper method to the class, after the Activity onClick method:

private void startPlay(int chosenLevel)
{
    //start gameplay
    Intent playIntent = new Intent(this, PlayGame.class);
    playIntent.putExtra("level", chosenLevel);
    this.startActivity(playIntent);
}

We start the gameplay Activity, passing the level number to the Intent. We will implement clicks on the other two buttons in the final part of the series.


2. Prepare for Gameplay

Step 1

Last time we created an Activity class for gameplay. Eclipse should have inserted the following outline structure, with whatever names you chose:

package com.example.youdothemath;
import android.app.Activity;
public class PlayGame extends Activity {
//class content
}

Add the following import statements to your class:

import java.util.Random;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

Insert the onCreate method and set the content view to the layout we created last time:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_playgame);
}

Extend the class declaration opening line to implement click listening:

public class ArithmeticPlay extends Activity implements OnClickListener

Step 2

Let’s add some instance variables to the class to facilitate gameplay. First, integers to represent the level, operands, operator and answer:

private int level = 0, answer = 0, operator = 0, operand1 = 0, operand2 = 0;

Next define some constants for the four operators, which will streamline the gameplay processing:

private final int ADD_OPERATOR = 0, SUBTRACT_OPERATOR = 1, MULTIPLY_OPERATOR = 2, DIVIDE_OPERATOR = 3;

Now add an array for the text to display for each of these operators, with the array index corresponding to the constant in each case:

private String[] operators = {"+", "-", "x", "/"};

The range of operators is going to depend on the level the user chose and on the operator being used (i.e. the addition operands for the easy level will have a different possible minimum and maximum number than the operands used with other operators and on other levels). We will use a random number generator to choose the operands, with a minimum and maximum in each case. Define the minimum and maximum numbers for each operator and each level using the following 2D arrays:

private int[][] levelMin = {
	{1, 11, 21},
	{1, 5, 10},
	{2, 5, 10},
	{2, 3, 5}};
private int[][] levelMax = {
	{10, 25, 50},
	{10, 20, 30},
	{5, 10, 15},
	{10, 50, 100}};

Each array represents the levels and operators, with the minimum or maximum listed for the three levels for addition, then subtraction, then multiplication, then division – using the same order as the constants we defined. For example, the minimum operand for addition at medium difficulty is 11, while the maximum operand for subtraction at hard difficulty is 30. The purpose of this will become clearer when we implement the part of gameplay where we generate the questions. You can change the minimum and maximum numbers later if you like.

Next add an instance variable for a random number generator we will use throughout the class:

private Random random;

Finally, add instance variables for the user interface elements we defined in the layout file last time, including the Text Views for question, answer and score, the Image View for the tick or cross response image and the buttons for digits 0-9 plus enter and clear:

private TextView question, answerTxt, scoreTxt;
private ImageView response;
private Button btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, enterBtn, clearBtn;

Step 3

Now let’s turn to the onCreate method. After the existing code, retrieve references to the Text and Image Views:

question =  (TextView)findViewById(R.id.question);
answerTxt = (TextView)findViewById(R.id.answer);
response =  (ImageView)findViewById(R.id.response);
scoreTxt =  (TextView)findViewById(R.id.score);

Set the tick/cross response image to be invisible initially:

response.setVisibility(View.INVISIBLE);

Next retrieve references to the number, clear, and enter buttons:

btn1 = (Button)findViewById(R.id.btn1);
btn2 = (Button)findViewById(R.id.btn2);
btn3 = (Button)findViewById(R.id.btn3);
btn4 = (Button)findViewById(R.id.btn4);
btn5 = (Button)findViewById(R.id.btn5);
btn6 = (Button)findViewById(R.id.btn6);
btn7 = (Button)findViewById(R.id.btn7);
btn8 = (Button)findViewById(R.id.btn8);
btn9 = (Button)findViewById(R.id.btn9);
btn0 = (Button)findViewById(R.id.btn0);
enterBtn = (Button)findViewById(R.id.enter);
clearBtn = (Button)findViewById(R.id.clear);

Listen for clicks on all of these buttons:

btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn4.setOnClickListener(this);
btn5.setOnClickListener(this);
btn6.setOnClickListener(this);
btn7.setOnClickListener(this);
btn8.setOnClickListener(this);
btn9.setOnClickListener(this);
btn0.setOnClickListener(this);
enterBtn.setOnClickListener(this);
clearBtn.setOnClickListener(this);

Now retrieve the level number we passed from the main Activity when launching this one:

Bundle extras = getIntent().getExtras();
if(extras != null)
{
    int passedLevel = extras.getInt("level", -1);
    if(passedLevel>=0) level = passedLevel;
}

The level variable was set initially to zero, so if no number is received the game defaults to easy level. After this if statement, still inside onCreate, initialize the random number generator:

random = new Random();

Finally call a helper method to start the game – we will implement the method next:

chooseQuestion();

3. Start Gameplay

Step 1

Add the method you called in onCreate to your class:

private void chooseQuestion(){
//get a question
}

This method is going to execute every time we need a new question. It will choose the operator and operands at random, within the range specified for the operator and level. The method will output the question to the user interface, ready for the user’s answer.

Step 2

Inside the new method, first reset the answer Text View:

answerTxt.setText("= ?");

This is necessary because the answer Text View is going to display the user-entered answer, so it must be reset each time a new question is generated. Select an operator at random, making sure it is within the range of the operators array (it has four in it but you could potentially change this):

operator = random.nextInt(operators.length);

Select two operands at random, using another helper method:

operand1 = getOperand();
operand2 = getOperand();

Step 3

After your chooseQuestion method, add the new helper method to the class, for choosing operands:

private int getOperand(){
//return operand number
}

We use a helper method for this as it may be called repeatedly. The method will return an integer within the relevant range. Add the method content:

return random.nextInt(levelMax[operator][level] - levelMin[operator][level] + 1)
	+ levelMin[operator][level];

This code ensures that the integer is within the correct range for the current operator and level.

Step 4

Back in your chooseQuestion method, you now need to add some qualifications depending on the operator. For example, we don’t want to allow negative answers, so the subtract operators must not produce a negative result. Add a conditional test:

if(operator == SUBTRACT_OPERATOR){
    while(operand2>operand1){
        operand1 = getOperand();
        operand2 = getOperand();
    }
}

If the second operand is greater than the first, we simply carry on generating operands until we get a pair in which the first operand is greater. With the divide operator, we only want answers that are whole numbers, so add a further conditional:

else if(operator==DIVIDE_OPERATOR){
    while((((double)operand1/(double)operand2)%1 > 0) || (operand1==operand2))
    {
        operand1 = getOperand();
        operand2 = getOperand();
    }
}

This time we continue until we have a division resulting in a whole number, also avoiding cases where the two operands are equal. You could of course tweak this code further to improve on the effectiveness of the game.

Step 5

Now we have a valid set of operands for the chosen operator, so let’s calculate the answer using a switch statement:

switch(operator)
{
	case ADD_OPERATOR:
		answer = operand1+operand2;
		break;
	case SUBTRACT_OPERATOR:
		answer = operand1-operand2;
		break;
	case MULTIPLY_OPERATOR:
		answer = operand1*operand2;
		break;
	case DIVIDE_OPERATOR:
		answer = operand1/operand2;
		break;
	default:
		break;
}

We will use this to check the user’s answer later. Now we can display the question to the user:

question.setText(operand1+" "+operators[operator]+" "+operand2);

4. Handle User Input

Step 1

Now we have a question being displayed to the user, we just have to respond to their clicks on the buttons. Add an onClick method to your class:

@Override
public void onClick(View view)
{
//button clicked
}

Find out which button was clicked:

if(view.getId()==R.id.enter){
	//enter button
}
else if(view.getId()==R.id.clear){
	//clear button
}
else {
	//number button
}

Remember that we setup click listeners for the number buttons, the enter button and the clear button. We check first for the enter then clear buttons. If the click event has fired and it wasn’t either of those, it must have been a number button, which we will handle in the else block.

Step 2

Let’s handle the clear button first. In the else if for the clear button, simply reset the answer Text View:

answerTxt.setText("= ?");

Step 3

Now let’s handle the number buttons. In the else block, start by setting the tick/cross image to be hidden, as we don’t want feedback displayed while the user is entering an answer:

response.setVisibility(View.INVISIBLE);

Remember that we set tags representing the relevant numbers in each number button when we added them to the layout. This means that we can handle all number buttons in the same way, retrieving the number pressed from the tag:

int enteredNum = Integer.parseInt(view.getTag().toString());

Now we have two possible scenarios, the user is entering the first digit of the answer, or a subsequent digit. Check the currently displayed answer and respond accordingly, either setting or appending the digit just pressed:

if(answerTxt.getText().toString().endsWith("?"))
	answerTxt.setText("= "+enteredNum);
else
	answerTxt.append(""+enteredNum);

Step 4

Finally, we can handle clicks on the enter button. In the if block for the enter button, first retrieve the answer entered by the user from the answer Text View:

String answerContent = answerTxt.getText().toString();

Check if we have an answer, if not, we do nothing:

if(!answerContent.endsWith("?"))
{
    //we have an answer
}

The answer text is always preceded with the text “= “, with “= ?” displayed until the user presses a number key, so we know we have an answer. Inside the if block, now we can retrieve the number entered:

int enteredAnswer = Integer.parseInt(answerContent.substring(2));

When we check the answer, we will update the score, so call another helper method to retrieve it:

int exScore = getScore();

After the onClick method, add this new method to return the current score by reading the score Text View:

private int getScore(){
    String scoreStr = scoreTxt.getText().toString();
    return Integer.parseInt(scoreStr.substring(scoreStr.lastIndexOf(" ")+1));
}

Back in the onClick block for the enter button, check whether the user’s answer is correct:

if(enteredAnswer==answer){
    //correct
}

Inside this block, respond to the correct answer:

scoreTxt.setText("Score: "+(exScore+1));
response.setImageResource(R.drawable.tick);
response.setVisibility(View.VISIBLE);

We update the score text, display the tick image and set the Image View to display. After the if block, add an else for incorrect answers:

else{
    //incorrect
}

Inside this block, respond to the incorrect answer:

scoreTxt.setText("Score: 0");
response.setImageResource(R.drawable.cross);
response.setVisibility(View.VISIBLE);

We reset the score to zero and display the cross image. Finally, after the else, choose another question:

chooseQuestion();
Gameplay Activity Screen

And so the game continues…


Conclusion

We’ve now completed the second part of the series! At this stage, you should be able to run your app and play the game, although the How to Play and High Scores screens will not function yet – we will complete them in the final tutorial. In this tutorial we have implemented the gameplay logic, handling and responding to user interaction. In the final part of the series we will save and retrieve high scores using the application Shared Preferences. We will also save instance state data to ensure the app continues to function when gameplay is interrupted.

Create a Minigolf Game – Interface Creation

$
0
0

In this tutorial series, I’m going to show you how to create a physics based minigolf game in Corona SDK. You’ll learn about physics manipulation, touch controls, and collision detection. Read on!


1. Application Overview

App Overview

Using pre-made graphics, we’ll code an entertaining game using Lua and the Corona SDK APIs.

The player will be able to use the touch screen on the device to shoot the ball across the stage. You can modify the parameters in the code to customize the game.


2. Target Device

Target Device

The first thing we have to do is select the platform we want to run our app within. This allows us to choose the size for the images we’ll use.

The iOS platform has the following characteristics.

  • iPad 1/2/Mini: 1024x768px, 132 ppi
  • iPad Retina: 2048×1536, 264 ppi
  • iPhone/iPod Touch: 320x480px, 163 ppi
  • iPhone/iPod Retina: 960x640px, 326 ppi
  • iPhone 5/iPod Touch: 1136×640, 326 ppi

Android is an open platform, so there are many different devices and resolutions. A few of the more common screen characteristics include the following.

  • Asus Nexus 7 Tablet: 800x1280px, 216 ppi
  • Motorola Droid X: 854x480px, 228 ppi
  • Samsung Galaxy SIII: 720x1280px, 306 ppi

In this tutorial we’ll be focusing on the iOS platform with the graphic design, specifically developing for distribution to an iPhone/iPod touch. However, the code presented here should apply to Android development with the Corona SDK as well.


3. Interface

Interface

A simple and friendly interface will be used. It involves multiple shapes, buttons, and bitmaps, among other things.

The interface graphic resources necessary for this tutorial can be found in the attached download.


4. Export Graphics

Export Graphics

Depending on the device you have selected, you may need to export the graphics in the recommended PPI. You can do this in your favorite image editor.

I used the Adjust Size… function in the Preview app on Mac OS X.

Remember to give the images a descriptive name and save them in your project folder.


5. App Configuration

An external file will be used to make the application go fullscreen across devices. This is the config.lua file. This file shows the original screen size and the method used to scale that content in case the app is run in a different screen resolution.

application =
{
    content =
    {
        width = 320,
        height = 480,
        scale = "letterbox"
    },
}

6. Main.lua

Let’s write the application!

Open your prefered Lua editor (any Text Editor will work, but you won’t have syntax highlighting) and prepare to write your awesome app. Remember to save the file as main.lua in your project folder.


7. Code Structure

We’ll structure our code as if it were a Class. If you know ActionScript or Java, you should find the structure familiar.

Necesary Classes
Variables and Constants
Declare Functions
    contructor (Main function)
    class methods (other functions)
call Main function

8. Hide Status Bar

display.setStatusBar(display.HiddenStatusBar)

This code hides the status bar. The status bar is the bar on top of the device screen that shows the time, signal, and other indicators.


9. Import Physics

We’ll use the Physics library to handle collisions. Use the following code to import it.

-- Physics
local physics = require('physics')
physics.start()

10. Background

Background

A simple graphic is used as the background for the application interface. The following line of code stores it.

-- Graphics
-- [Background]
local bg = display.newImage('bg.png')

11. Title View

Title View

This is the Title View, it will be the first interactive screen to appear in our game. These variables store its components.

-- [Title View]
local title
local playBtn
local creditsBtn
local titleView

12. Credits View

Credits View

This view will show the credits and copyright of the game. The following variable will be used to store it.

-- [CreditsView]
local creditsView

13. Game Background

Game Background

This is the level background. It also adds the information textfields.

-- Game Background
local gameBg

14. Instructions Message

Instructions

An instructions message will appear at the start of the game. It will be tweened out after two seconds.

-- Instructions
local ins

15. Ball

Ball

The ball graphic. The objective of the game is to put this item in the level hole.

-- Ball
local ball

16. Hole

Hole

Stores the hole physics for collision detection.

-- Hole
local hole

17. Alert

Alert

This is the alert that will be displayed when you win the game. It will complete the level and end the game.

-- Alert
local alertView

18. Sounds

Sounds

We’ll use sound effects to enhance the feeling of the game. You can find the sound used in this example at Soungle.com using the keyword golf.

-- Sounds
local ballHit = audio.loadSound('ball_hit.mp3')
local ballHole = audio.loadSound('ball_hole.mp3')

19. Variables

This are the variables we’ll use. Read the comments in the code to know more about them.

-- Variables
local w1 -- Walls
local w2
local w3
local w4
local w5
local w6
local guide -- Transparent white line that indicates shooting direction and force
local scoring -- Stores the scoring alert image

20. Declare Functions

Declare all functions as local at the start.

-- Functions
local Main = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local showGameView = {}
local gameListeners = {}
local shoot = {}
local onCollision = {}
local alert = {}

21. Constructor

Next we’ll create the function that will initialize all the game logic.

function Main()
	-- code...
end

22. Add Title View

Now we’re going to place the TitleView in the stage and call a function that will add the tap listeners to the buttons.

function Main()
	titleBg = display.newImage('titleBg.png', 114, 50)
	playBtn = display.newImage('playBtn.png', 218, 177)
	creditsBtn = display.newImage('creditsBtn.png', 203, 232)
	titleView = display.newGroup(titleBg, playBtn, creditsBtn)
	startButtonListeners('add')
end

Next Time…

In this part of the series you’ve learned the interface and the basic setup of the game. In the next and final part of the series, we’ll handle the level creation, collision detection, and the final steps to take prior to release like app testing, creating a start screen, adding an icon and, finally, building the app. Stay tuned for the final part!

Create a Minigolf Game – Adding Interaction

$
0
0

This is the second installment in our Corona SDK minigolf tutorial. In today’s tutorial, we’ll add to our interface and the game interaction. Read on!


Where We Left Off. . .

Please be sure to check part one of the series to fully understand and prepare for this tutorial.


1. Start Button Listeners

This function adds the necessary listeners to the TitleView buttons.

function startButtonListeners(action)
	if(action == 'add') then
		playBtn:addEventListener('tap', showGameView)
		creditsBtn:addEventListener('tap', showCredits)
	else
		playBtn:removeEventListener('tap', showGameView)
		creditsBtn:removeEventListener('tap', showCredits)
	end
end

2. Show Credits

The credits screen is shown when the user taps the about button, and a tap listener is added to the credits view to remove it.

function showCredits:tap(e)
	playBtn.isVisible = false
	creditsBtn.isVisible = false
	creditsView = display.newImage('credits.png', -130, display.contentHeight-140)
	transition.to(creditsView, {time = 300, x = 65, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end

3. Hide Credits

When the credits screen is tapped, it’ll be tweened out of the stage and removed.

function hideCredits:tap(e)
	playBtn.isVisible = true
	creditsBtn.isVisible = true
	transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
end

4. Show Game View

When the Play button is tapped the title view is tweened and removed, revealing the game view. There are many parts involved in this view, so we’ll split them up in the next steps.

function showGameView:tap(e)
	transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})

5. Game Background

First, we add the game background.

-- Game Bg
	gameBg = display.newImage('gameBg.png', 54, 14)

6. Instructions Message

The following lines add the instruction message. It will appear for two seconds and then fade it out.

-- Instructions Message
	local ins = display.newImage('ins.png', 260, 230)
	transition.from(ins, {time = 200, alpha = 0.1, onComplete = function() timer.performWithDelay(2000, function() transition.to(ins, {time = 200, alpha = 0.1, onComplete = function() display.remove(ins) ins = nil end}) end) end})

7. TextFields

This part creates the TextFields on the stage. These show information about the maximum shots allowable for a par score and the current amount already made.

-- TextFields
parTF = display.newText("3", 342, 296, native.systemFontBold, 16)
shotsTF = display.newText("0", 449, 296, native.systemFontBold, 16)

8. Ball

Add the ball to the level.

-- Ball
ball = display.newImage('ball.png', 351, 93)
ball.name = 'ball'

9. Hole

The next graphic will use physics to detect when the ball falls in the hole.

-- Hole
hole = display.newCircle(142, 251, 10)
hole.isVisible = false
end

10. Walls

The level walls are created using the next few lines of code. They are invisible by default since we only need them to detect collisions with the ball.

-- Walls
w1 = display.newLine(233, 43, 548, 43) w1.isVisible = false
w2 = display.newLine(398, 93, 398, 198) w2.isVisible = false
w3 = display.newLine(299, 158, 498, 158) w3.isVisible = false
w4 = display.newLine(199, 218, 199, 338) w4.isVisible = false
w5 = display.newLine(133, 278, 249, 278) w5.isVisible = false
w6 = display.newLine(83, 153, 83, 378) w6.isVisible = false

11. Physics

Next we add physics to the game objects.

-- Add Physics
	-- Walls
	physics.addBody(w1, 'static')
	physics.addBody(w2, 'static')
	physics.addBody(w3, 'static')
	physics.addBody(w4, 'static')
	physics.addBody(w5, 'static')
	physics.addBody(w6, 'static')
	-- Ball
	physics.addBody(ball, 'dynamic', {radius = 8})
	-- Hole
	physics.addBody(hole, 'static', {radius = 8})
	hole.isSensor = true
	gameListeners('add')
end

12. Game Listeners

This function adds the necessary listeners to start the game logic.

function gameListeners(action)
	if(action == 'add') then
		gameBg:addEventListener('touch', shoot)
		hole:addEventListener('collision', onCollision)
	else
		gameBg:removeEventListener('touch', shoot)
		hole:removeEventListener('collision', onCollision)
	end
end

13. Shoot Function

This next lines use the touch event and the physics applyForce method to shoot the ball.

function shoot(e)
	if(e.phase == 'moved') then
		display.remove(guide) -- Clears the previous line
		guide = display.newLine(ball.x, ball.y, e.x, e.y) -- Draw a line to serve as feedback
		guide:setColor(255, 255, 255, 80)
		guide.width = 6
	elseif(e.phase == 'ended') then
		display.remove(guide)
		ball:applyForce((ball.x - e.x) * 0.01, (ball.y - e.y) * 0.01, ball.x, ball.y)
		audio.play(ballHit)
		shotsTF.text = tostring(tonumber(shotsTF.text) + 1) -- Increases the shots counter
		-- Prevent Ball from being hit when moving
		gameBg:removeEventListener('touch', shoot)
		-- Stop Ball after a few seconds
		local stopBall = timer.performWithDelay(5000, function() ball:setLinearVelocity(0, 0, ball.x, ball.y) gameBg:addEventListener('touch', shoot) end, 1)
	end
end

14. Collisions

Now we’ll check to see if the ball collides with the hole using the following code, and call an alert if true. This also checks the number of shots made and uses them to set the correct alert to display.

function onCollision(e)
	if(e.other.name == 'ball') then
		audio.play(ballHole)
		display.remove(ball)
		-- Check Scoring
		if(tonumber(parTF.text) > tonumber(shotsTF.text)) then
			scoring = 'birdie'
		elseif(tonumber(parTF.text) == tonumber(shotsTF.text)) then
			scoring = 'par'
		elseif(tonumber(parTF.text) < tonumber(shotsTF.text)) then
			scoring = 'bogey'
		end
		alert(scoring)
	end
end

15. Alert

The alert function creates an alert view, animates it, and ends the game.

function alert(img)
	gameListeners('rmv')
	alertView = display.newImage(scoring .. '.png', (display.contentWidth * 0.5) - 70, (display.contentHeight * 0.5) - 35)
	transition.from(alertView, {time = 300, xScale = 0.5, yScale = 0.5})
	-- Wait 1 second to stop physics
	timer.performWithDelay(1000, function() physics.stop() end, 1)
end

16. Call Main Function

In order to start the game, the Main function needs to be called. With the above code in place, we’ll do that here.

Main()

17. Loading Screen

Loading Screen

The Default.png file is an image displayed while the application loads. Add this image to your project source folder. It will automatically be added by the Corona compiler.


18. Icon

Icon

Using the graphics you created before, you can now create a nice and good looking icon. The icon size for the non-retina iPhone icon is 57x57px, but the retina version is 114x114px and the iTunes store requires a 512x512px version. I suggest creating the 512×512 version first and then scaling down for the other sizes.

It doesn’t need to have the rounded corners or the transparent glare, iTunes and the iPhone will do that for you.


19. Testing in Simulator

Testing

It’s time to do the final test. Open the Corona Simulator, browse to your project folder, and then click open. If everything works as expected, you are ready for the final step!


20. Build

Build

In the Corona Simulator go to File > Build and select your target device. Fill the required data and click build. Wait a few seconds and your app will be ready for device testing and/or submission for distribution!


Conclusion

In this series, we’ve learned about physics behaviour, tap listeners, and collisions, skills that can be really useful in a wide number of games.

Experiment with the final result and try to make your custom version of the game!

I hope you liked this tutorial series and found it helpful. Thank you for reading!

Customize NSLog for Easier Debugging

$
0
0

In this quick tip we are going to learn how to customize the output generated by NSLog in order to debug programs more efficiently. Read on!


Problem

By default, NSLog displays output in the following format:

Date Time OurApp[<process­id>] NSLog output

A real-world example might look like this:

2013­08­03 00:35:53.038 TestApp[460:c07] Value of result = 20

The default output is good, but it leaves something to be desired. Most of the time, we want to see the following in a log statement:

  • Name of the source file where NSLog() was called
  • Source code line number where NSLog() was called
  • Name of the class and method where NSLog() was called
  • Hide date time stamp, application name, and process id info
  • Enable/disable log information by changing mode (e.g. debug, release, staging)

In short, we would like NSLog to be more like this:

(ClassName MethodName) (SourceFileName:LineNumber) NSLog output

Solution

Let’s first look at how NSLog works unaltered. NSLog is just a C function built into the foundation framework of Cocoa, and behaves just like any other variadic C function. Specifically, NSLog sends error messages to the Apple System Log facility. It does this simply by passing its arguments along to the NSLogv function.

Because NSLog is just a wrapper for NSLogv, we can redefine NSLog with our own custom call to NSLogv. That is exactly what I will show you how to do in this tutorial.


1. Start a New Project

Create a new iOS project in Xcode, with the Empty Application template. Call it ExtendNSLog. Check the option for Automatic Reference Counting, but uncheck the options for Core Data and Unit Tests.

Create An iOS project with the Empty Application template
Product name should be “ExtendNSLog”

2. Create an Objective-C Class

Now create a header file along with the project. Select New File > Objective-­C Class. Set the name of the class to ExtendNSLogFunctionality. which will be a subclass of NSObject.

Create an Objective-­C class template
Set the class name to ExtendNSLogFunctionality

3. Add Custom NSLog Logic

Step 1

Open ExtendNSLogFunctionality.h and place the following code within the header:

#import <Foundation/Foundation.h>
#ifdef DEBUG
#define NSLog(args...) ExtendNSLog(__FILE__,__LINE__,__PRETTY_FUNCTION__,args);
#else
#define NSLog(x...)
#endif
void ExtendNSLog(const char *file, int lineNumber, const char *functionName, NSString *format, ...);

The above conditional will define an NSLog statement only when DEBUG is defined. When DEBUG is not defined, the NSLog statement will do nothing. The question arises: how do you control when DEBUG is defined? This can be done by assigning DEBUG=1 in the preprocessor settings for your project.

To do so, click on your application target, and select the Build Settings tab. Next make sure that the “All” and “Combined” options are selected. Search for “preprocessing” and locate the section titled “Preprocessor Macros”. Next, simply add “DEBUG=1″ to the Debug section.

Add the DEBUG=1 flag to the preprocessor settings

Note that in more recent Xcode project templates, there will already be a DEBUG=1 macro defined for the Debug build configuration in the Preprocessor Macros section. For more information, refer to this post on StackOverflow.

Step 2

With the debug macro defined, our next task is to write the custom version of NSLog. Open ExtendNSLogFunctionality.m and add the following code:

#import "ExtendNSLogFunctionality.h"
void ExtendNSLog(const char *file, int lineNumber, const char *functionName, NSString *format, ...)
{
    // Type to hold information about variable arguments.
    va_list ap;
    // Initialize a variable argument list.
    va_start (ap, format);
    // NSLog only adds a newline to the end of the NSLog format if
    // one is not already there.
    // Here we are utilizing this feature of NSLog()
    if (![format hasSuffix: @"\n"])
    {
        format = [format stringByAppendingString: @"\n"];
    }
    NSString *body = [[NSString alloc] initWithFormat:format arguments:ap];
    // End using variable argument list.
    va_end (ap);
    NSString *fileName = [[NSString stringWithUTF8String:file] lastPathComponent];
    fprintf(stderr, "(%s) (%s:%d) %s",
            functionName, [fileName UTF8String],
            lineNumber, [body UTF8String]);
}

Step 3

Now add the ExtendNSLogFunctionality.h include to the prefix header file Prefix.pch within the #ifdef __OBJC__ section.

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import "ExtendNSLogFunctionality.h"
#endif

For a better understanding on prefix headers, have a look at this entry on Wikipedia. Regarding prefix header best practices, check out this StackOverflow post.


4. A Custom Log Example

Now add an NSLog anywhere in your project code. In my case, I decide to add one within AppDelegate.m’s ­method -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.

int result = 20;
NSLog(@"Value of result : %d", result);

If you build and run the project with the Debug configuration now, you should see something like this:

(­[AppDelegate application:didFinishLaunchingWithOptions:]) (AppDelegate.m:21) Value of result : 20

Cheers! This output is much more useful than the default implementation. Hopefully you find that this technique will save you a lot of time while debugging your own programs!

Android SDK: Create an Arithmetic Game – High Scores and State Data

$
0
0

In this series we are creating a simple arithmetic game for Android. The game is going to display a calculator-style interface to users, repeatedly presenting questions and keeping track of how many correct answers they score in a row. In the first two parts of the series we built a calculator-style user interface and implemented the bulk of the gameplay logic, including prompting the user to choose a difficulty level, presenting questions, responding to user input, and updating the score. In this last part of the series we will tie up some loose ends, implement the high scores functionality, and save the app instance data to provide continuity when the state changes.


Series Format

This series on Creating an Arithmetic Game will be released in three parts:


Game Overview

The following is a screenshot of the game I’ll teach you how to build:

Arithmetic Game

The app chooses arithmetic questions using randomly selected operators and operands, with the details determined by the difficulty level. The user interface updates whenever the user enters an answer. To implement the remaining functionality, we will add processing to all of the Activity classes, as well as creating a helper class to model score information – the score information will be used to display high scores in the relevant screen and to save them during gameplay.


1. Save High Scores

Step 1

Last time we implemented most of the gameplay processing in the PlayGame class. Let’s enhance that class now by saving high scores as the user plays. High scores are going to be the number of questions answered correctly in a row, with the High Scores screen displaying the top ten. In your gameplay class, add the following import statements:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;
import android.content.SharedPreferences;

Next add instance variables for the application Shared Preferences and file name:

private SharedPreferences gamePrefs;
public static final String GAME_PREFS = "ArithmeticFile";

In the onCreate method, after the line in which you set the content view, instantiate the Shared Preferences object:

gamePrefs = getSharedPreferences(GAME_PREFS, 0);

Step 2

We will write the score to the Shared Preferences when the user answers a question incorrectly after answering at least one question correctly. We will also set the high score if the Activity is destroyed during gameplay. Since we will be setting the high score from more than one location in the class, let’s add a helper method:

private void setHighScore(){
//set high score
}

In this method we will deal with the details of checking whether or not the current score makes the top ten. Inside the method, first retrieve the score using the helper method we added last time:

int exScore = getScore();

We only want to proceed if the score is greater than zero:

if(exScore>0){
//we have a valid score
}

Place the remainder of the method code inside this conditional block. First, get the Shared Preferences Editor:

SharedPreferences.Editor scoreEdit = gamePrefs.edit();

For each high score, we will include the score and the date, so create a Date Format object:

DateFormat dateForm = new SimpleDateFormat("dd MMMM yyyy");

Get the date and format it:

String dateOutput = dateForm.format(new Date());

Retrieve any existing scores from the Shared Preferences, using a variable name we will also use when setting the new high scores:

String scores = gamePrefs.getString("highScores", "");

Check whether there are any existing scores:

if(scores.length()>0){
	//we have existing scores
}
else{
	//no existing scores
}

The else block is simpler, so complete it first:

scoreEdit.putString("highScores", ""+dateOutput+" - "+exScore);
scoreEdit.commit();

We simply add the new score along with the date, with a dash character between them – each score in the list will use this format. Back in the if block for cases where there are existing high scores, we have something a little more complex to do. We only want to store the user’s top ten scores, so we are going to create a helper class to model a single score object, which will implement the Comparable interface, allowing us to sort the stored scores and save only the top ten.

Step 3

Add a new class to your app’s source package, naming it “Score”. Extend the opening declaration line to implement the Comparable interface:

public class Score implements Comparable<Score>

We will be comparing Score objects against one another. Give the Score class two instance variables, for the score number and date:

private String scoreDate;
public int scoreNum;

For simplicity we are using a public variable for the score number to make it easier when we compare Score objects – you could alternatively use a private variable and add a get method for the score number. Add a constructor method instantiating these:

public Score(String date, int num){
	scoreDate=date;
	scoreNum=num;
}

Now add the compareTo method which will allow us to sort scores to determine the top ten:

public int compareTo(Score sc){
	//return 0 if equal
	//1 if passed greater than this
	//-1 if this greater than passed
	return sc.scoreNum>scoreNum? 1 : sc.scoreNum<scoreNum? -1 : 0;
}

This is actually contrary to what you may normally expect from a compareTo method. Typically, such methods return a negative integer if the passed object is greater and a positive integer if the passed object is lesser – we are doing the opposite. This is because the highest score is the best, so we want these objects to be sorted in descending rather than ascending order so that we can store only the ten best.

Finally add a method to return the date display text:

public String getScoreText()
{
    return scoreDate+" - "+scoreNum;
}

Step 4

Back in your PlayGame class, in the setHighScore method, we can now create a list of Score objects:

List<Score> scoreStrings = new ArrayList<Score>();

We will be storing the high scores in the Shared Preferences as one pipe-delimited string, so split that now:

String[] exScores = scores.split("\\|");

Now loop through, creating a Score object for each high score by splitting each one into its date and number, then adding it to the list:

for(String eSc : exScores){
	String[] parts = eSc.split(" - ");
	scoreStrings.add(new Score(parts[0], Integer.parseInt(parts[1])));
}

After this for loop, create a Score object for the current score and add it to the list:

Score newScore = new Score(dateOutput, exScore);
scoreStrings.add(newScore);

Sort the Scores:

Collections.sort(scoreStrings);

This will sort the Score objects according to the compareTo method we defined in the class declaration, so the first ten Score objects in the sorted list will be the top ten scores. Now we can simply add the first ten to a pipe-delimited string and write it to the Shared Preferences:

StringBuilder scoreBuild = new StringBuilder("");
for(int s=0; s<scoreStrings.size(); s++){
	if(s>=10) break;//only want ten
	if(s>0) scoreBuild.append("|");//pipe separate the score strings
	scoreBuild.append(scoreStrings.get(s).getScoreText());
}
//write to prefs
scoreEdit.putString("highScores", scoreBuild.toString());
scoreEdit.commit();

Notice that we use the getScoreText method we added the Score class. We save the new scores using the variable name we used to retrieve them from the Shared Preferences earlier in the method, as we did when there were no existing scores.

Step 5

Now you can use the setHighScore method – first in the onClick method in the enter button else block, where you set the score display to zero because the user has entered an incorrect answer. Before the line in which you update the score Text View:

setHighScore();

We also want to set the high score if the Activity is about to be destroyed, so add the following method to your class:

protected void onDestroy(){
	setHighScore();
	super.onDestroy();
}

This way the user will not lose their score. To prevent cases where the app saves the high score on certain state changes such as orientation change, which will be unnecessary since we will handle saving state manually, open your Manifest file and extend the Activity element for the gameplay class as follows:

<activity
	android:name=".PlayGame"
	android:configChanges="keyboardHidden|orientation|screenSize" ></activity>

2. Save Instance State

Step 1

Before we finish with the gameplay Activity, let’s save the instance state so that the game data persists through state changes. Add the following method to the class:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
//save state
}

Inside the method, save the score and level:

int exScore = getScore();
savedInstanceState.putInt("score", exScore);
savedInstanceState.putInt("level", level);

Complete the method by calling the superclass method:

super.onSaveInstanceState(savedInstanceState);

Step 2

We’ve saved the score and level data, now let’s restore them. In onCreate, you should already have a conditional block in which you test for a passed extras bundle. Amend this section to accommodate another conditional that will execute first. Replace the following code:

Bundle extras = getIntent().getExtras();
if(extras !=null)
{
	int passedLevel = extras.getInt("level", -1);
	if(passedLevel>=0) level = passedLevel;
}

With this amended version:

if(savedInstanceState!=null){
	//restore state
}
else{
	Bundle extras = getIntent().getExtras();
	if(extras !=null)
	{
		int passedLevel = extras.getInt("level", -1);
		if(passedLevel>=0) level = passedLevel;
	}
}

We have moved the existing check for extras to an else block, first testing whether we have saved instance state data. In the if block, retrieve the level and score, updating the UI:

level=savedInstanceState.getInt("level");
int exScore = savedInstanceState.getInt("score");
scoreTxt.setText("Score: "+exScore);

Your app can now cope with state changes. You could also save the currently displayed question, but we will simply allow the app to choose a new question if a state change prevents it being preserved. On changes such as orientation, which we included in the Activity Manifest element, this code will not actually execute, as the app will not recreate the Activity. The saved state data provides a backup in cases where the app is about to be killed, for example if another Activity is in the foreground.


3. Display High Scores

Step 1

We’ve dealt with saving high scores, now let’s display them. In your app’s main Activity class, complete the content of the else if for the High Scores button in onClick by launching the following Intent:

Intent highIntent = new Intent(this, HighScores.class);
this.startActivity(highIntent);

Step 2

Now open your High Scores Activity class. Add the following imports:

import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.TextView;

Add the onCreate method inside the class, setting the content view to the layout we created earlier:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_high);
}

After setting the content view but still inside onCreate, retrieve the Text View we added to the layout for displaying the high scores:

TextView scoreView = (TextView)findViewById(R.id.high_scores_list);

Now retrieve the Shared Preferences using the constant we defined in the gameplay class:

SharedPreferences scorePrefs = getSharedPreferences(PlayGame.GAME_PREFS, 0);

Split the string into an array of high scores:

String[] savedScores = scorePrefs.getString("highScores", "").split("\\|");

Iterate through the scores, appending them into a single string with new lines between them:

StringBuilder scoreBuild = new StringBuilder("");
for(String score : savedScores){
	scoreBuild.append(score+"\n");
}

Display the result in the Text View:

scoreView.setText(scoreBuild.toString());

That’s the High Scores Activity complete!

Arithmetic High Scores

4. Complete How to Play Activity

Step 1

We have one remaining task – to complete the How to Play Activity. Open the class you created for it. Add the following import:

import android.os.Bundle;

Inside the class, add onCreate and set the content view to the layout we created:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_how);
}

Step 2

Now open your main Activity class and launch the How to Play Activity from the else if for the button in onClick:

Intent helpIntent = new Intent(this, HowToPlay.class);
this.startActivity(helpIntent);

Conclusion

That’s the app complete! Run it to see it in action. In this series we have used Java and Android structures to create an interactive arithmetic game in which the user tries to answer as many questions correctly in a row as possible. We implemented multiple levels and arithmetic question types with operators and operands tailored to the game logic. The app saves high scores and state data automatically, with common app elements including a High Scores display and helpful information about the game. See if you can think of any ways you could enhance the app and use it as a foundation for future development.

Android SDK: Create a Drawing App – Interface Creation

$
0
0

In this series, we will create finger-painting app for Android using touch interaction. The user will be able to select from a color palette, choose a brush size, erase, create a new drawing, or save their existing drawing to the device gallery.

In this first part of the series we will get the app user interface setup, then in the second part we will implement the drawing functionality. In the final part we will add the ability for the user to erase, to start a new drawing, to save existing drawings and to choose brush and eraser sizes.


Series Format

This series on Creating a Drawing App will be in three parts:

  • Interface Creation
  • Touch Interaction (Pending Publication)
  • Essential Functionality (Pending Publication)

Final Preview

Drawing App

Above is a screenshot from the app this series will teach you how to build. Hopefully your drawings will be better than mine!


1. Create an Android Project

Step 1

Start a new Android project in Eclipse, choosing application and package names. We are using a minimum API level of 14 and a target of 17 for the code in this tutorial.

Drawing Project

Let Eclipse create a blank main Activity and layout – you can use the default names.

Drawing Class and Layout

Step 2

Open your project Manifest file and switch to the XML editing tab. Your Activity and SDK levels should already be set. Add the following to your Activity element opening tag, forcing the app only to use portrait:

android:screenOrientation="portrait"

Step 3

Before we start building the interface, let’s define some numbers we will use throughout the series. In your app’s “res/values” folder, if Eclipse has not already created it, add the “dimens.xml” file – if it is already there you can simply add new values to it. The outline should be as follows:

<resources></resources>

If Eclipse created the file, there may be some values in it already. We are going to use three possible brush/ eraser sizes: small, medium, and large. We need to define the size for each as both a dimension and an integer value so that we can use these measurements in both the XML layout and drawable resources and the Java code:

<!-- Brush sizes --><dimen name="small_brush">10dp</dimen><integer name="small_size">10</integer><dimen name="medium_brush">20dp</dimen><integer name="medium_size">20</integer><dimen name="large_brush">30dp</dimen><integer name="large_size">30</integer>

The values for dimension and integer at each size are the same, so that the UI indicates the brush size as it will function when the user draws with it.


2. Create a Custom View Class

Step 1

We are going to define a custom View class in which the drawing will take place. In your app’s source package, create a new class. Name it “DrawingView” and select “android.view.View” as the Superclass. The new class should have the following outline:

public class DrawingView extends View
{
}

Step 2

In your new View class you will need the following import statements in addition to the View import which Eclipse should have added for you:

import android.content.Context;
import android.util.AttributeSet;

Add a constructor method to the class:

public DrawingView(Context context, AttributeSet attrs){
	super(context, attrs);
	setupDrawing();
}

We will add an instance of the custom View to the XML layout file. Add the specified helper method to the class:

private void setupDrawing(){
//get drawing area setup for interaction
}

We will implement this method in the next part of the series, as well as adding other methods to the class.


3. Design the Activity Layout

Step 1

Open your app’s “res/values” strings XML file and add some text strings we will use in the layout:

<string name="start_new">New</string><string name="brush">Brush</string><string name="erase">Erase</string><string name="save">Save</string><string name="paint">Paint</string>

Open the app’s main layout file and switch to the XML tab to edit the code. The Activity screen content will be easiest to implement using Linear Layouts. Replace the content of the layout file with the following outline:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:background="#FFCCCCCC"
	android:orientation="vertical"
	tools:context=".MainActivity" ></LinearLayout>

We set vertical orientation and a gray background color.

Step 2

Inside the main Linear Layout, add another to hold the UI buttons along the top of the screen:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:layout_gravity="center"
    android:orientation="horizontal" ></LinearLayout>

This time the layout is horizontal with a set height and centering applied. Inside this layout we will add the buttons for starting a new drawing, selecting a brush, selecting an eraser, and saving a drawing. We are using the following images for these buttons, although you can create your own if you prefer:

New Button
Brush Button
Eraser Button
Save Button

Copy the images to your app’s drawable folder(s) – if you are creating your own you can target particular densities. To complete the tutorial series without targeting particular screen densities you can simply create a folder named “drawables” and add all of your drawable resources to it.

Let’s now add an ImageButton for each option in the Activity. Start with the button to create a new drawing inside the second Linear Layout:

<ImageButton
    android:id="@+id/new_btn"
    android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:contentDescription="@string/start_new"
    android:src="@drawable/new_pic" />

We will use the ID to respond to button clicks in the Activity class. We specify the new button icon image as source for the ImageButton and add a content description string.

Next add the brush button:

<ImageButton
	android:id="@+id/draw_btn"
	android:layout_width="wrap_content"
	android:layout_height="fill_parent"
	android:contentDescription="@string/brush"
	android:src="@drawable/brush" />

Now add the eraser button:

<ImageButton
	android:id="@+id/erase_btn"
	android:layout_width="wrap_content"
	android:layout_height="fill_parent"
	android:contentDescription="@string/erase"
	android:src="@drawable/eraser" />

On clicking either the brush or eraser button, the user will be prompted to select a size – we will implement this later. Next add the save button:

<ImageButton
	android:id="@+id/save_btn"
	android:layout_width="wrap_content"
	android:layout_height="fill_parent"
	android:contentDescription="@string/save"
	android:src="@drawable/save" />

That’s it for the top Linear Layout control buttons.

Step 3

After the top button Linear Layout, but still inside the outer Linear Layout in the file, let’s now add an instance of the custom view class we created:

<com.example.drawingfun.DrawingView
	android:id="@+id/drawing"
	android:layout_width="fill_parent"
	android:layout_height="0dp"
	android:layout_marginBottom="3dp"
	android:layout_marginLeft="5dp"
	android:layout_marginRight="5dp"
	android:layout_marginTop="3dp"
	android:layout_weight="1"
	android:background="#FFFFFFFF" />

Alter the opening tag chose a different package name. As you can see, you can add a custom View to your layouts in much the same way as the standard Android UI elements. We set general layout properties, a white background for drawing, and provide an ID for referencing the View in Java. By retrieving a reference to this instance of the custom View class, our Activity will be able to access the methods we define in the View class declaration we created.

Step 4

Now let’s add the color palette. After the custom View element, add another Linear Layout for the palette buttons:

<LinearLayout
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_gravity="center"
	android:orientation="vertical" ></LinearLayout>

This element will contain two rows of buttons so enter two more Linear Layouts for these. Place the following inside the one you just added:

<!-- Top Row --><LinearLayout
	android:id="@+id/paint_colors"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:orientation="horizontal" ></LinearLayout><!-- Bottom Row --><LinearLayout
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:orientation="horizontal" ></LinearLayout>

The first row has an ID because we are going to use it in Java when the app starts to set the first default color as selected so that the user can start drawing straight away. For each color, we are going to use the following ImageButton structure:

<ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF660000"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF660000" />

Don’t add this to your layout file yet – we will do that shortly, for now look over the code. We use one of the dimension values we defined for the color button. Notice that the background and tag attributes are the same – the background is for the appearance of the button in the UI, while the tag is so that we can set the paint color according to what the user has clicked in the Activity Java code. The element includes a method to execute on clicks of the button – we will implement this in the Activity class next time. We also specify a drawable named “paint”. Add this to the project now, creating a new file in the project drawable folder(s) and naming it “paint.xml”. Enter the following code in the new drawable file:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" ><item><shape android:shape="rectangle" ><stroke
			android:width="4dp"
			android:color="#FF999999" /><solid android:color="#00000000" /><padding
			android:bottom="0dp"
			android:left="0dp"
			android:right="0dp"
			android:top="0dp" /></shape></item><item><shape xmlns:android="http://schemas.android.com/apk/res/android" ><stroke
			android:width="4dp"
			android:color="#FF999999" /><solid android:color="#00000000" /><corners android:radius="10dp" /></shape></item></layer-list>

This is less complex than it looks at first glance. To create a rounded button appearance, we use two layered Shape Drawables, one a rectangle outline and the other a rounded stroke. The strokes have a gray color, with transparency in the middle through which the background color for each button will be seen (the background color being the color represented by the button).

Back in the layout file, inside the top row layout in the color palette section, add the following ImageButton elements to represent the first six colors, using the structure we outlined above:

<ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF660000"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF660000" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FFFF0000"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FFFF0000" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FFFF6600"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FFFF6600" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FFFFCC00"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FFFFCC00" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF009900"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF009900" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF009999"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF009999" />

Each button is identical apart from the colors defined in the background and tag attributes. Add the next six in the bottom row layout:

<ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF0000FF"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF0000FF" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF990099"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF990099" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FFFF6666"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FFFF6666" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FFFFFFFF"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FFFFFFFF" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF787878"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF787878" /><ImageButton
	android:layout_width="@dimen/large_brush"
	android:layout_height="@dimen/large_brush"
	android:layout_margin="2dp"
	android:background="#FF000000"
	android:contentDescription="@string/paint"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="#FF000000" />

Change the colors if you like, but make sure you use the same color value in the background and tag attributes for each button. You should now be able to see the layout in the Graphical Layout tab in Eclipse:

Drawing App Design

Conclusion

We’ve now completed the first part of the series! Your app won’t do much at the moment, but in the next part we will implement the drawing functions, detect and respond to touch interaction, and allow the user to choose colors. In the final part, we will enhance this functionality to allow the user erase, choose brush and eraser sizes, start a new drawing, or save the current drawing.

In future follow-up tutorials to this series, we will look at using pattern fills in drawing functions, using opacity, and supporting non-touch interaction (e.g. trackball, stylus and mouse). You will be able to use these later tutorials to build on the skills you learn in this series. Stay tuned!

Announcing Envato’s 7th Birthday Bundle!

$
0
0

It’s Envato’s seventh birthday and, to celebrate, we’ve created another fantastic Birthday Bundle! With $500 worth of items available for just $20, it’s worth getting excited about. Consider it our way of saying a huge thank you to all the authors and buyers who have been part of our journey over the past year!


The Birthday Bundle is Now on Sale

In the bundle, you’ll find over $500 worth of items compiled from across our marketplaces — all for $20! All the items in this bundle have been carefully chosen to represent the newest and fastest-moving categories of the past year, as acknowledgement of all our authors and buyers who have helped us grow over the last seven years.

Take a look at what’s included, and grab your copy. Be quick — the bundle ends at noon on the 3rd of September AEST!

notes-post-660x220

Due to the exclusive nature of the Birthday Bundle, the Bundle items are purchased ‘as-is’, meaning no Bundle files are eligible for item support.

This Birthday Bundle will run from the 20th of August at 12pm AEST and ends at 12pm AEST on the 3rd of September. Buy now!


We’re Celebrating With a Free Course!

At Tuts+, we’re celebrating our 7th birthday with another free Tuts+ Course: jQuery UI 101: The Essentials.

Did you know that the jQuery team is responsible for far more just the library that we all know and love? One such example is their highly tested UI framework for rapidly generating everything from calendars, to sliders: jQuery UI.

In this course, Dan Wellman, a well-known author and front-end engineer at Skype, will review every single widget in jQuery UI. You’ll learn the APIs, how they function, and dive into common implementations. Watch it for free now!

freecourse

Thanks for being part of the Envato community, and we hope to celebrate many more birthdays in the future with you.


Android SDK: Create a Drawing App – Touch Interaction

$
0
0

In this series, we will create finger-painting app for Android using touch interaction. The user will be able to select from a color palette, choose a brush size, erase, create a new drawing, or save their existing drawing to the device gallery.


Series Format

This series on Creating a Drawing App will be in three parts:


Final Preview

Drawing App

In the first part of the series we created the user interface. In this second part we will implement drawing on the canvas and choosing colors. In the final part of the series we will introduce the ability to erase, to create new drawings and to save a drawing to the gallery on the user device. We will look at options you can use to enhance this app in future tutorials, including pattern fills, opacity and interaction other than touchscreen.


1. Prepare for Drawing

Step 1

Last time we created a class named “DrawingView” which is a custom View for the drawing functions to take place in. We created the outline of the class declaration and a method named “setupDrawing” – we will implement this now. In your DrawingView class, add the following import statements:

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;

Next add some instance variables at the top of the class:

//drawing path
private Path drawPath;
//drawing and canvas paint
private Paint drawPaint, canvasPaint;
//initial color
private int paintColor = 0xFF660000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;

When the user touches the screen and moves their finger to draw, we will use a Path to trace their drawing action on the canvas. Both the canvas and the drawing on top of it are represented by Paint objects. The initial paint color corresponds to the first color in the palette we created last time, which will be initially selected when the app launches. Finally we declare variables for the canvas and bitmap – the user paths drawn with drawPaint will be drawn onto the canvas, which is drawn with canvasPaint.

Step 2

In the setupDrawing method, let’s instantiate some of these variables now to set the class up for drawing. First instantiate the drawing Path and Paint objects:

drawPath = new Path();
drawPaint = new Paint();

Next set the initial color:

drawPaint.setColor(paintColor);

Now set the initial path properties:

drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(20);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);

We will alter part of this code in the next tutorial when we implement the ability to choose brush sizes, for now we set an arbitrary brush size. Setting the anti-alias, stroke join and cap styles will make the user’s drawings appear smoother.

Complete the setupDrawing method by instantiating the canvas Paint object:

canvasPaint = new Paint(Paint.DITHER_FLAG);

This time we set dithering by passing a parameter to the constructor.

Step 3

We need to override a couple of methods to make the custom View function as a drawing View. First, still inside the DrawingView class, override the onSizeChanged method, which will be called when the custom View is assigned a size:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//view given size
}

Inside this method, first call the superclass method:

super.onSizeChanged(w, h, oldw, oldh);

Now instantiate the drawing canvas and bitmap using the width and height values:

canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);

Step 4

To allow the class to function as a custom drawing View, we also need to override the onDraw method, so add it to the class now:

@Override
protected void onDraw(Canvas canvas) {
//draw view
}

Inside the method, draw the canvas and the drawing path:

canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);

We have not yet implemented the ability for the user to draw the Path using the drawing Paint, but once we do this will present it in the View. Each time the user draws using touch interaction, we will invalidate the View, causing the onDraw method to execute.


2. Facilitate Drawing

Step 1

When the drawing View is on the app screen, we want user touches on it to register as drawing operations. To do this we need to listen for touch events. In your drawingView class, add the following method:

@Override
public boolean onTouchEvent(MotionEvent event) {
//detect user touch
}

Inside the method, retrieve the X and Y positions of the user touch:

float touchX = event.getX();
float touchY = event.getY();

Step 2

The MotionEvent parameter to the onTouchEvent method will let us respond to particular touch events. The actions we are interested in to implement drawing are down, move and up. Add a switch statement in the method to respond to each of these:

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
	drawPath.moveTo(touchX, touchY);
	break;
case MotionEvent.ACTION_MOVE:
	drawPath.lineTo(touchX, touchY);
	break;
case MotionEvent.ACTION_UP:
	drawCanvas.drawPath(drawPath, drawPaint);
	drawPath.reset();
	break;
default:
	return false;
}

Take a moment to look over this code. When the user touches the View, we move to that position to start drawing. When they move their finger on the View, we draw the path along with their touch. When they lift their finger up off the View, we draw the Path and reset it for the next drawing operation.

Step 3

After the switch statement, complete the method by invalidating the View and returning a true value:

invalidate();
return true;

Calling invalidate will cause the onDraw method to execute.


3. Choosing Colors

Step 1

Let’s now implement the ability for the user to choose colors from the palette. In the app’s main Activity, add the following imports:

import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;

Add the following instance variable to the class:

private DrawingView drawView;

This represents the instance of the custom View that we added to the layout. Inside onCreate, after the existing code, instantiate this variable by retrieving a reference to it from the layout:

drawView = (DrawingView)findViewById(R.id.drawing);

We now have the View that is displayed in the Activity on which we can call the methods in the DrawingView class.

Step 2

We set the initial paint color in the drawing View class, let’s now set the user interface up to reflect and manage that. In the main Activity class, add another instance variable to represent the paint color button in the palette:

private ImageButton currPaint;

Inside onCreate, we now want to retrieve the first paint color button in the palette area, which is initially going to be selected. First retrieve the Linear Layout it is contained within:

LinearLayout paintLayout = (LinearLayout)findViewById(R.id.paint_colors);

Get the first button and store it as the instance variable:

currPaint = (ImageButton)paintLayout.getChildAt(0);

We will use a different drawable image on the button to show that it is currently selected:

currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));

Add this file to your app’s drawables now, giving it the name “paint_pressed.xml” and entering the following shape:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" ><item><shape android:shape="rectangle" ><stroke
				android:width="4dp"
				android:color="#FF333333" /><solid android:color="#00000000" /><padding
				android:bottom="0dp"
				android:left="0dp"
				android:right="0dp"
				android:top="0dp" /></shape></item><item><shape xmlns:android="http://schemas.android.com/apk/res/android" ><stroke
				android:width="4dp"
				android:color="#FF333333" /><solid android:color="#00000000" /><corners android:radius="10dp" /></shape></item></layer-list>

This is very similar to the “paint.xml” drawable we created last time, but with a darker color around the paint.

Step 3

Now we can let the user choose colors. When we created the layout last time, we listed an onClick attribute for the color palette buttons – add the method to your main Activity class now:

public void paintClicked(View view){
	//use chosen color
}

Inside this method, first check that the user has clicked a paint color that is not the currently selected one:

if(view!=currPaint){
//update color
}

Inside the if block, retrieve the tag we set for each button in the layout, representing the chosen color:

ImageButton imgView = (ImageButton)view;
String color = view.getTag().toString();

We need to use the custom View class to set the color. Move to the DrawingView class now and add the following method:

public void setColor(String newColor){
//set color
}

Inside the method, start by invalidating the View:

invalidate();

Next parse and set the color for drawing:

paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);

Back in your main Activity, in the paintClicked method after retrieving the color tag, call the new method on the custom drawing View object:

drawView.setColor(color);

Now update the UI to reflect the new chosen paint and set the previous one back to normal:

imgView.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint));
currPaint=(ImageButton)view;

Conclusion

You can now run the app and draw on the canvas, choosing colors to draw with. You should see the color palette buttons reflect the currently chosen color. In this tutorial we have worked through the essential features of any touch-drawing app for Android, so you should now have the basic skills to implement your own drawing functions in other apps. In the final part of the series we will implement erasing, choosing brush and eraser sizes, saving drawings and starting new drawings.

Create a Space Defender Game – Game Setup

$
0
0

In this tutorial series, we will be learning how to create a space shooter game just like the classic game Space Defender. Read on!


Series Overview


In this version of Space Defender, the player will have to defend his space by shooting enemies. Every time the player successfully destroys an enemy, they will earn points and when the player has reached 20 or 40 points, their gun will receive an upgrade. To mix things up, this game will send out bonus packages that are worth 5 points. To see the game in action, watch the short video above. Now that we know more about the game we are creating, let’s get started!

This space shooter game will be built using the Corona SDK. Here are some of the things you’ll learn in this tutorial series:

  • A quick introduction to Corona’s Storyboard
  • How to utilizing artwork under the creative commons license
  • How to use custom fonts
  • How to use widgets such as buttons
  • How to build a full game with the Corona SDK

What You’ll Need

In order to use this tutorial, you’ll need to have the Corona SDK installed on your computer. If you do not have the SDK, head over to www.CoronaLabs.com to create a free account and download the free software.


1. Build Configuration

Our first step in setting up our game is the build.settings file. This file handles all of the build time properties of our app such as the orientation and additional options for Android and iOS platforms. Create a new file called build.settings and copy the following settings into your file.

settings = {
    orientation = {
        default = "landscapeRight",
        supported = { "landscapeRight", "landscapeLeft"}
    },
    iphone = {
        plist = {
            UIStatusBarHidden = false,
            UIAppFonts = { "Kemco Pixel.ttf" },
        }
    },
}

One of the great things about Corona SDK is that the language self-documents and most of the settings in this file should be easy to understand. However, let’s quickly walk through these settings.

  • orientation – This table stores how the game will actually look. In our case, we want the game to be played in landscape mode and support both landscape left and right.
  • UIStatusBarHidden – Once we’re playing our game, we want to hide the status bar. So, we set this option to false.
  • UIAppFonts – This option allows us to use custom fonts within our app and since we want to use Kemco Pixel.ttf in our app, we set that here. The font file must be placed in the root folder of your project. Kemco Pixel is available for download at http://www.dafont.com/kemco-pixel.font.

2. Runtime Configuration

After we’ve set our build time configurations, we are going to set up our runtime configurations in our config.lua file. In this file, we will set up the width and height of our game, the type of content scaling, and the frames per second. Create a new file called config.lua and copy the following settings.

application = {
    content = {
        width = 320,
        height = 480,
        scale = "letterBox",
        fps = 30,
    },
}

Similar to our build.settings file, the config.lua is self-documenting except for the scale option. The scale option is a feature that Corona SDK uses to adapt your application for different screen sizes. In the case of the letterBox option, we are telling Corona to display all of our content on the screen while keeping the aspect ratio.  When the content does not cover the entire screen, the app will display black bars in the areas without content (much like watching movies in widescreen mode).

Before we continue building our app, we need to talk about graphics. There are a lot of different ways that you can obtain graphics for your game – hire a graphic designer, hire a friend, draw them yourself, or even buy stock graphics from websites like http://graphicriver.net/.

However, if you are on a shoestring budget, you may not be able to purchase these graphics or you may not have the time to create them. In these kinds of situations, you can use graphics that have been released under the creative commons license. The creative commons license allows you to use graphics that are freely available under certain restrictions such as providing attribution to the author.

For our game, we are going to be using artwork under the creative commons license from OpenGameArt.org. The author that created these graphics is VividRealtiy and here’s a direct link to his work – http://opengameart.org/content/evolutius-gfx-pack. Before continuing, make sure to download the graphic set or download the file for this tutorial.

While there are a ton of websites with graphics under this license, here are three websites I visit often:


3. Storyboard Setup

Now that we have our graphics, we can continue making our game! To continue our game, we have to create a new file called main.lua. This file will be the start point of our game and in fact it’s the start point of every game made with Corona. Once the file is created, open it in your favorite editor and enter the following code:

-- hide the status bar
display.setStatusBar( display.HiddenStatusBar )

This line of code will hide the status bar on iOS. Next, we will use Corona’s storyboard feature to manage our game. The storyboard feature treats different parts of the game as scenes to make it easier for developers to manage the code. For example, our game has three scenes – main.lua, the menu, and the game screen. Once each scene is set up, Corona provides a very easy way to move between scenes. Since this can be a huge hurdle for developers just starting with the Corona SDK, here’s a graphical representation of the layout of our game.

Corona Storyboard

Each blue box represents an individual scene and the arrow between each scene shows how we will navigate through the scenes. Simply put, we start at main.lua and will navigate back and forth between Scene 2 and Scene 3.

Implementing the Storyboard in our app is simple: just require the module and you’re all set! Place the following code into main.lua to incorporate this great feature.

-- include the Corona "storyboard" module
local storyboard = require "storyboard"

Next, we will want to move to the second scene and we will use the storyboard variable to accomplish this.

-- load menu screen
storyboard.gotoScene( "menu" )

Now that we’ve completed our main.lua file, let’s tackle the main menu!


4. Main Menu Creation

The main menu of our game will display a background image, the game title, and a Play Now button. To get started, create a new file called menu.lua and Corona’s storyboard featured.

local storyboard = require( "storyboard" )
local scene = storyboard.newScene()

Now we will add Corona’s widget feature to create our button. The widget feature allows us to easily create common GUI elements such as buttons in our game.

local widget = require "widget"

After the widget library, we will pre-define some variables for our game. These variables will store the screen width, screen height, and the middle of the screen coordinates.

local screenW, screenH, halfW, halfY = display.contentWidth, display.contentHeight, display.contentWidth*0.5, display.contentHeight*0.5

After our variables, we will create our very first function called scene:createScene(). This function is called when the scene is loaded for the very first time or if the scene has been previously removed. Generally speaking, this function is used to add all of the graphical elements to the screen.

function scene:createScene( event )
local group = self.view
end

One important feature of a storyboard is the way it handles objects. All of the display objects that we use within this scene will be placed with the variable group. By placing our display objects within this group, we are letting Corona know that these objects belong to this scene and when the scene needs to be removed, these objects will also be removed.

Inside the function scene:createScene(), we will use the graphic BKG.png (this graphic came from opengameart.org) as the background for our game. Once it’s added, we will center the graphic on the screen and insert it into the group variable.

-- display a background image
local bg = display.newImageRect("images/BKG.png", 480, 320)
bg.x = halfW
bg.y = halfY
group:insert(bg)

Next, we place our game title on the screen using the custom font that we specified in our build.settings file.

-- place a game title on the screen
local gametitle = display.newText("Space Defender",0,0,"Kemco Pixel",40)
gametitle.x = halfW
gametitle.y = 100
group:insert( gametitle )

Our last addition to our first function is the play button. The play button will use Corona’s widget feature and an event listener called onPlayBtnRelease. The event listener will be triggered when the play button is touched. Add the following code inside the scene:createScene() function.

local function onPlayBtnRelease()
storyboard.gotoScene( "game", "slideLeft")
end
playBtn = widget.newButton{
label="Play Now",
labelColor = { default={255} },
defaultFile = "images/Button.png",
overFile = "images/Button_Purple.png",
width=150, height=60,
left=180, top=150,
font = "Kemco Pixel",
fontSize = 18,
onRelease = onPlayBtnRelease -- event listener function
}
group:insert( playBtn )

After our create scene function, we will add an enter scene function. This function will be triggered after the create scene function and will remove the previous scene.

function scene:enterScene( event )
local group = self.view
if storyboard.getPrevious() ~= nil then
storyboard.removeScene(storyboard.getPrevious())
end
end

The last function we will add is the destroyScene function. This function will be called when the scene is removed or destroyed. In our case, we are using widgets and since widgets must be removed manually, we remove the widget inside our destroy scene function.

function scene:destroyScene( event )
local group = self.view
if playBtn then
playBtn:removeSelf()    -- widgets must be manually removed
playBtn = nil
end
end

Finally, we add event listeners to call the different functions we set up – enterScene, destroyScene, and createScene.

scene:addEventListener( "createScene", scene )
scene:addEventListener( "enterScene", scene )
scene:addEventListener( "destroyScene", scene )

Conclusion

And that’s it for Part 1! We’ve learned how to set up our project, how to implement Corona’s storyboard feature, how to start our app, and how to create a menu system. In Part 2, we will discuss the game play of our app. Stay tuned!

Android SDK: Create a Drawing App – Essential Functionality

$
0
0

In this series, we will create a finger-painting app for Android using touch interaction. The user will be able to select from a color palette, choose a brush size, erase, create a new drawing, or save their existing drawing to the device gallery.


Series Format

This series on Creating a Drawing App will be in three parts:

In the first part of the series we created the user interface. In the second part we implemented drawing on the canvas and choosing colors. In this the final part of the series we will introduce the ability to erase, to create new drawings, and to save a drawing to the gallery on the user device. We will look at the options you can use to enhance this app in future tutorials, including pattern fills and opacity.


Final Preview

Drawing App

1. Choosing Brush Sizes

Step 1

Last time we implemented drawing on the canvas, now we can let the user choose a brush size. The brush size options will appear when the user presses the brush button we added to the interface. To respond to this, extend the opening line of your main Activity class declaration to implement the OnClickListener interface:

public class MainActivity extends Activity implements OnClickListener

You will need the following import statements added to the class for this tutorial:

import java.util.UUID;
import android.provider.MediaStore;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.view.View.OnClickListener;
import android.widget.Toast;

Add the following instance variables to the class to store the three dimension values we defined last time:

private float smallBrush, mediumBrush, largeBrush;

Instantiate them in onCreate:

smallBrush = getResources().getInteger(R.integer.small_size);
mediumBrush = getResources().getInteger(R.integer.medium_size);
largeBrush = getResources().getInteger(R.integer.large_size);

We will use these later. You should already have an ImageButton instance variable in the main class named “currPaint” – extend that line to add another now for the drawing button:

private ImageButton currPaint, drawBtn;

In onCreate retrieve a reference to the button from the layout:

drawBtn = (ImageButton)findViewById(R.id.draw_btn);

Set the class up as a click listener for the button:

drawBtn.setOnClickListener(this);

Step 2

Add an onClick method to the class:

@Override
public void onClick(View view){
//respond to clicks
}

Inside the method, check for clicks on the drawing button:

if(view.getId()==R.id.draw_btn){
	//draw button clicked
}

We will be adding conditional blocks to the onClick method for the other buttons later.

Step 3

When the user clicks the button, we will display a dialog presenting them with the three button sizes. Inside the if block, create a Dialog and set the title:

final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Brush size:");

Let’s define the Dialog layout in XML – add a new file in your app’s “res/layout” folder, naming it “brush_chooser.xml” and entering the following outline:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:gravity="center"
	android:orientation="vertical" ></LinearLayout>

Inside the Linear Layout, add a button for each size:

<ImageButton
	android:id="@+id/small_brush"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_weight="1"
	android:contentDescription="@string/sml"
	android:src="@drawable/small" /><ImageButton
	android:id="@+id/medium_brush"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_weight="1"
	android:contentDescription="@string/med"
	android:src="@drawable/medium" /><ImageButton
	android:id="@+id/large_brush"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_weight="1"
	android:contentDescription="@string/lrg"
	android:src="@drawable/large" />

Each button has an ID for identification in the Activity Java code. You will notice that each one also has a content description attribute – add the specified strings to your “res/values” strings XML file:

<string name="sml">Small</string><string name="med">Medium</string><string name="lrg">Large</string>

As you can see in the layout file, each button also has a drawable file listed as its source attribute. Create new files for each of these in your “res/drawables” folder(s) now, starting with “small.xml” and entering the following content:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:dither="true"
	android:shape="oval" ><size
		android:height="@dimen/small_brush"
		android:width="@dimen/small_brush" /><solid android:color="#FF666666" /></shape>

Notice that we use the dimension values we defined. Next add “medium.xml” to the drawables folder, entering the following shape:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:dither="true"
	android:shape="oval" ><size
		android:height="@dimen/medium_brush"
		android:width="@dimen/medium_brush" /><solid android:color="#FF666666" /></shape>

Finally add “large.xml” with the following content:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:dither="true"
	android:shape="oval" ><size
		android:height="@dimen/large_brush"
		android:width="@dimen/large_brush" /><solid android:color="#FF666666" /></shape>

Back in your main Activity class onClick method, after creating the Dialog and setting its title, you can now set the layout:

brushDialog.setContentView(R.layout.brush_chooser);

Step 4

Before we continue with the Dialog, let’s alter the custom drawing View class to use different brush sizes. In your DrawingView class, add the following import statements for this tutorial:

import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.TypedValue;

Add two instance variables to the class:

private float brushSize, lastBrushSize;

We will use the first variable for the brush size and the second to keep track of the last brush size used when the user switches to the eraser, so that we can revert back to the correct size when they decide to switch back to drawing. In the setupDrawing method, before the code already in there, add the following to instantiate these variables:

brushSize = getResources().getInteger(R.integer.medium_size);
lastBrushSize = brushSize;

We use the dimension value for the medium sized brush to begin with. You can now update the line in the method where you set the stroke width with a hard-coded value to use this variable value instead:

drawPaint.setStrokeWidth(brushSize);

Add the following method to the class to set the brush size:

public void setBrushSize(float newSize){
//update size
}

Inside the method, update the brush size with the passed value:

float pixelAmount = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
	newSize, getResources().getDisplayMetrics());
brushSize=pixelAmount;
drawPaint.setStrokeWidth(brushSize);

We will be passing the value from the dimensions file when we call this method, so we have to calculate its dimension value. We update the variable and the Paint object to use the new size. Now add methods to get and set the other size variable we created:

public void setLastBrushSize(float lastSize){
	lastBrushSize=lastSize;
}
public float getLastBrushSize(){
	return lastBrushSize;
}

We will call these methods from the main Activity class.

Step 5

Back in your main Activity class, let’s complete the Dialog code in the onClick method. After setting the content view on the Dialog object, listen for clicks on the three size buttons, starting with the small one:

ImageButton smallBtn = (ImageButton)brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setBrushSize(smallBrush);
		drawView.setLastBrushSize(smallBrush);
		brushDialog.dismiss();
	}
});

We set the size using the methods we added to the custom View class as soon as the user clicks a brush size button, then immediately dismiss the Dialog. Next do the same for the medium and large buttons:

ImageButton mediumBtn = (ImageButton)brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setBrushSize(mediumBrush);
		drawView.setLastBrushSize(mediumBrush);
		brushDialog.dismiss();
	}
});
ImageButton largeBtn = (ImageButton)brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setBrushSize(largeBrush);
		drawView.setLastBrushSize(largeBrush);
		brushDialog.dismiss();
	}
});

Complete the draw button section of onClick by displaying the Dialog:

brushDialog.show();

The Dialog will display until the user makes a selection or goes back to the Activity.

Brush Dialog

Step 6

Use the new method to set the initial brush size in onCreate:

drawView.setBrushSize(mediumBrush);

2. Erasing

Step 1

Now let’s add erasing to the app. In the custom drawing View class, add a boolean instance variable to act as a flag for whether the user is currently erasing or not:

private boolean erase=false;

Initially we will assume that the user is drawing, not erasing. Add the following method to the class:

public void setErase(boolean isErase){
//set erase true or false
}

Inside the method, first update the flag variable:

erase=isErase;

Now alter the Paint object to erase or switch back to drawing:

if(erase) drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
else drawPaint.setXfermode(null);

If you’re looking for an advanced topic to explore, have a look at the PorterDuff.Mode options.

Step 2

Back in the main Activity class, add another ImageButton to the list of instance variables:

private ImageButton currPaint, drawBtn, eraseBtn;

In onCreate, retrieve a reference to the button and set the class up to listen for clicks:

eraseBtn = (ImageButton)findViewById(R.id.erase_btn);
eraseBtn.setOnClickListener(this);

Add a conditional statement for the button in onClick after the conditional for the draw button:

else if(view.getId()==R.id.erase_btn){
	//switch to erase - choose size
}

As with the draw button, we will let the user choose an eraser size from a Dialog. Inside the conditional block for the erase button, create and prepare the Dialog as before:

final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Eraser size:");
brushDialog.setContentView(R.layout.brush_chooser);

We use the same layout as the draw button Dialog. Setup click listeners for the size buttons as before, this time calling the new erase method we added to the View class:

ImageButton smallBtn = (ImageButton)brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setErase(true);
		drawView.setBrushSize(smallBrush);
		brushDialog.dismiss();
	}
});
ImageButton mediumBtn = (ImageButton)brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setErase(true);
		drawView.setBrushSize(mediumBrush);
		brushDialog.dismiss();
	}
});
ImageButton largeBtn = (ImageButton)brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setErase(true);
		drawView.setBrushSize(largeBrush);
		brushDialog.dismiss();
	}
});

We call the method to set the brush size as with the draw button, this time first setting the erase flag to true. Finally, display the Dialog:

brushDialog.show();
Erase Dialog

The user will be able to erase using touch interaction as with drawing:

Drawing Erased

Step 3

When the user clicks the draw button and chooses a brush size, we need to set back to drawing in case they have previously been erasing. In the three click listeners you added for the small, medium and large buttons in the draw button section of onClick, call the erase method with a false parameter – add this in each onClick before calling dismiss on the “brushDialog” object:

drawView.setErase(false);

When the user has been erasing and clicks a paint color button, we will assume that they want to switch back to drawing. In the paintClicked method, before the existing code, call the erase method, passing false:

drawView.setErase(false);

Still inside paintClicked, set the brush size back to the last one used when drawing rather than erasing:

drawView.setBrushSize(drawView.getLastBrushSize());

This type of processing is motivated by assumptions about what the user wants to do based on their actions – you could potentially enhance this app along these lines, so bear that in mind if you want to carry on working on the app later.


3. New Drawings

Step 1

We added a button for the user to start a new drawing, so let’s implement that now. Add another to the list of ImageButton instance variables in your main Activity class:

private ImageButton currPaint, drawBtn, eraseBtn, newBtn;

Instantiate it with a reference to the button listed in the layout, in onCreate, also listening for clicks:

newBtn = (ImageButton)findViewById(R.id.new_btn);
newBtn.setOnClickListener(this);

Step 2

In onClick, add another conditional block for the new button:

else if(view.getId()==R.id.new_btn){
	//new button
}

In your custom drawing View class, add a method to start a new drawing:

public void startNew(){
	drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
	invalidate();
}

The method simply clears the canvas and updates the display.

Back in the main Activity class conditional block for the new button in onCreate, let’s verify that the user definitely wants to start a new drawing:

AlertDialog.Builder newDialog = new AlertDialog.Builder(this);
newDialog.setTitle("New drawing");
newDialog.setMessage("Start new drawing (you will lose the current drawing)?");
newDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
	public void onClick(DialogInterface dialog, int which){
		drawView.startNew();
		dialog.dismiss();
	}
});
newDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
	public void onClick(DialogInterface dialog, int which){
		dialog.cancel();
	}
});
newDialog.show();

The Dialog lets the user change their mind, calling the new method if they decide to go ahead and start a new drawing, in which case the current drawing is cleared.

New Drawing

4. Save Drawings

Step 1

The one remaining part of the app functionality is the ability to save drawings to the device. Add the save button as the last in the sequence of ImageButton instance variables in the main Activity class:

private ImageButton currPaint, drawBtn, eraseBtn, newBtn, saveBtn;

Instantiate it and listen for clicks in onCreate:

saveBtn = (ImageButton)findViewById(R.id.save_btn);
saveBtn.setOnClickListener(this);

Add a conditional for it in onClick:

else if(view.getId()==R.id.save_btn){
			//save drawing
}

Let’s use a similar algorithm to the one we used for creating new drawings, to check that the user wants to go ahead and save:

AlertDialog.Builder saveDialog = new AlertDialog.Builder(this);
saveDialog.setTitle("Save drawing");
saveDialog.setMessage("Save drawing to device Gallery?");
saveDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
	public void onClick(DialogInterface dialog, int which){
		//save drawing
	}
});
saveDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
	public void onClick(DialogInterface dialog, int which){
		dialog.cancel();
	}
});
saveDialog.show();
Save Dialog

If the user chooses to go ahead and save, we need to output the currently displayed View as an image. In the onClick method for the save Dialog, start by enabling the drawing cache on the custom View:

drawView.setDrawingCacheEnabled(true);

Now attempt to write the image to a file:

String imgSaved = MediaStore.Images.Media.insertImage(
	getContentResolver(), drawView.getDrawingCache(),
	UUID.randomUUID().toString()+".png", "drawing");

Take a moment to look over this code. We use the insertImage method to attempt to write the image to the media store for images on the device, which should save it to the user gallery. We pass the content resolver, drawing cache for the displayed View, a randomly generated UUID string for the filename with PNG extension and a short description. The method returns the URL of the image created, or null if the operation was unsuccessful – this lets us give user feedback:

if(imgSaved!=null){
	Toast savedToast = Toast.makeText(getApplicationContext(),"Drawing saved to Gallery!", Toast.LENGTH_SHORT);
	savedToast.show();
}
else{
	Toast unsavedToast = Toast.makeText(getApplicationContext(),"Oops! Image could not be saved.", Toast.LENGTH_SHORT);
	unsavedToast.show();
}

Finally, destroy the drawing cache so that any future drawings saved won’t use the existing cache:

drawView.destroyDrawingCache();
Drawing Saved

On browsing to the device gallery, the user should now be able to see their drawing image:

Drawing In Gallery

Conclusion

This tutorial completes the functionality for our basic drawing app! When you run the app you should be able to draw, choose colors, choose brush and eraser sizes, start new drawings and save drawings to the gallery. We have worked through the basic process of facilitating drawing using touchscreen interaction on Android, but there are many ways in which you could enhance the application so try experimenting with it to build in your own additional functionality. In future tutorials we will cover using pattern fills rather than simply drawing with solid colors, using opacity, and letting the user choose an opacity level. We will also cover drawing on Android devices where the interaction model is not a touchscreen, for example with a mouse or trackball.

Digital Tickets with UIGestureRecognizer

$
0
0

This tutorial will teach you to combine UIGestureRecognizers to force users to make non-trivial gestures to perform an action. We will use the example of redeeming a digital ticket, using the real-world analogy of tearing it in half.


Project Overview

UIGestureRecognizer, and it’s various subclasses, make it easy to recognize different gestures on designated target objects and perform actions when those gestures are recognized. It’s possible to combine different UIGestureRecognizers to force people to make non-trivial gestures to perform an action. This can be used in cases where users are trying to initiate a non-reversible action, where a simple, accident-prone gesture, such as a tap or a swipe, is not suitable.

Tickets indicate a right of admission for various events or venues. They are usually perforated to indicate that the ticket has been used, and is no longer redeemable. Apple has introduced the Passbook app to allow people to use their phone as a ticket or other form of mobile payment. Passbook is limited to presenting tickets with a QR code as their sole method of redemption.

Airlines and large chains have invested in QR scanners so they can begin to accept digital tickets. Smaller venues, bars and nightclubs for example, may not be able or willing to invest in the new equipment. However, they have been forced to offer ticket purchasing online as more people demand it. Unfortunately, the default way these online tickets are redeemed is to compare an order number to a master list. Clearly this is not scalable, leaving both sellers and buyers of digital tickets in a less than ideal situation.

We don’t want the tickets to be accidentally redeemed by a simple gesture. Why don’t we recreate the real-world method of redeeming a ticket, tearing the stub from the main part of the ticket which is usually retained by the person checking the ticket? This can be implemented by using a long press gesture recognizer on the stub and a swipe gesture recognizer on the main part. Think of it as holding the stub and tearing the other part away.


1. Create the Project

Open Xcode and select “Create a new Xcode project”. Choose “Empty Application” and click Next. Enter a name for your project, I’ve called mine “Tickets”. Enter your organization name, company identifier and class prefix. Choose iPhone from Devices and just choose “Use Automatic Reference Counting”. We don’t need unit tests or Core Data for this project.

digital-tickets-create-project

2. Create the View Controllers

Step 1

This project will have two view controllers, one screen to present a list of all available tickets and one screen where the tickets are actually redeemed.

Create a UITableViewController subclass called “TicketListViewController”. This controller doesn’t require a XIB for user interface as it’s just a simple list.

digital-tickets-create-ticket-list-view-controller

Step 2

Create a UIViewController subclass called “RedeemTicketViewController”. Select “With XIB for user interface” as this screen will have a more complex design.

digital-tickets-create-redeem-tickets-view-controller

Step 3

Now create the navigation through the app. At the top of your app delegate import TicketListViewController.h.

#import “TicketListViewController.h”

In application:didFinishLaunchingWithOptions: create an instance of the ticket list view controller and an instance of UINavigationController with the ticket list view controller as its root view controller. Set the root view controller of the window to be the navigation controller.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    TicketListViewController* vc = [[TicketListViewController alloc] initWithStyle:UITableViewStylePlain];
    UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc];
    [self.window setRootViewController:navController];
    [self.window makeKeyAndVisible];
    return YES;
}

Build and run the application to check that everything is hooked up correctly. You should see an empty table view on the screen.


3. Create the List of Tickets

Step 1

For this example we will hard code the tickets into the app. You’ll need to change this section to by more dynamic when you have access to a database which stores the ticket sales.

Edit the init method of the TicketListViewController to set the title of the navigation bar for this screen. This will help guide users through the app.

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        self.navigationItem.title = @"Tickets";
    }
    return self;
}

Step 2

Change the table view data source to have one section and one row. Create a cell with style UITableViewCellStyleSubtitle. Set the text label to show the name of the event and venue, the detail text label to show the date of the event and the accessory type to be UITableViewCellAccessoryDisclosureIndicator.

#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(!cell){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
    cell.textLabel.text = @"Special Guests at Generic Venue";
    cell.detailTextLabel.text = @"19 Jun 2013";
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    return cell;
}

Step 3

At the top of the TicketListViewController.m import RedeemTickerViewController.h.

#import “RedeemTickerViewController.h”

Now change the table view delegate so that when a row is tapped the user is taken to the redeem ticket screen.

#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    RedeemTicketViewController* vc = [[RedeemTicketViewController alloc]initWithNibName:@"RedeemTicketViewController" bundle:nil];
    [self.navigationController pushViewController:vc animated:YES];
}

Step 4

Build and run the app to check that the navigation works correctly. Tapping the row will take you to an empty view with a back button.

digital-tickets-ticket-list-preview

4. Create the Redeem Tickets Screen

Step 1

This is the most interesting screen, with the most interactivity and functionality.

In RedeemTickerViewController.h create an outlet for the ticket stub UIImageView, the main part of the ticket UIImageView and a UIView which will allow us to animate the ticket as though it were tearing off. Declare a BOOL instance variable which we will use to track whether the ticket stub is being ‘held’ in place. Create a UILongPressGestureRecognizer and a UISwipeGestureRecognizer which we’ll use later.

The header file should now be like this:

#import <UIKit/UIKit.h>
@interface RedeemTicketViewController : UIViewController
@property (nonatomic, strong) IBOutlet UIView* containerView;
@property (nonatomic, strong) IBOutlet UIImageView* ticketStub;
@property (nonatomic, strong) IBOutlet UIImageView* ticketMain;
@property (nonatomic, strong) UILongPressGestureRecognizer* longPressGest;
@property (nonatomic, strong) UISwipeGestureRecognizer* swipeGest;
@property (nonatomic, assign) BOOL isHeld;
@end

Step 2

Open RedeemTicketViewController.xib and select the the view controller’s view object. Uncheck “Use Autolayout” in the File Inspector panel to enable pre-iOS6 compatibility. We’ll set the autoresizing mask on each view ourselves to ensure that we cater for the various screen sizes on the iPhone now.

Now add an Image View from the Object Library. This will be for the ticket stub, so connect the File’s Owner ticketStub outlet to the image view. Set the size frame of the image view to be at origin (20, 20) and have size (280, 100). Set the autoresizing mask to have fixed top margin and no autoresizing on the width or height.

digital-tickets-create-ticket-stub-view

Now add a View from the Object Library. This will be the container view so connect the File’s Owner containerView outlet to the view. Set the frame of the view to be at (20, 120) and with size (280, 280). Set the autoresizing mask to have fixed top margin and no autoresizing on the width or height.

digital-tickets-create-ticket-main-container-view

Next drag an Image View from the Object Library into the container view you just created. The image view should fill the container view and have its autoresizing mask set to have all margins fixed and to change the width and the height. Set the File’s Owner ticketMain outlet to the image view.

digital-tickets-create-ticket-main-view

In the Attributes Inspector, choose the images for the stub and the main parts of the ticket using images you’ve imported into your project. Make sure you check “User Interaction Enabled” for both image views and the container view.

Step 3

Edit the init method of the RedeemTicketViewController to set the title of the navigation bar for this screen and initialize self.isHeld to NO.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.navigationItem.title = @"Special Guests at Generic Venue";
        self.isHeld = NO;
    }
    return self;
}

Step 4

Build and run the app to check how it looks so far.

digital-tickets-redeem-ticket-preview

I’ve included a QR code in my images so that if the ticket systems at a venue were upgraded to handle Passbook, it would be seamless to continue using this app after the upgrade.


5. Add the UIGestureRecognizers

Step 1

In viewDidLoad (called after the view is loaded from the XIB), create a UILongPressGestureRecognizer and add it to the ticketStub image view. Create a UISwipeGestureRecognizer and add it to the ticketMain image view.

-(void)viewDidLoad
{
    [super viewDidLoad];
    self.longPressGest = [[UILongPressGestureRecognizer alloc]
                          initWithTarget:self action:@selector(handleLongPress:)];
    self.longPressGest.minimumPressDuration = 0.1;
    [self.ticketStub addGestureRecognizer:self.longPressGest];
    self.swipeGest = [[UISwipeGestureRecognizer alloc]
                      initWithTarget:self action:@selector(handleSwipe:)];
    self.swipeGest.direction = (UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight);
    [self.ticketMain addGestureRecognizer:self.swipeGest];
}

Notice that I’ve set the direction mask on the swipe gesture recognizer to handle either left or right, not up or down. I’ve set the minimum press duration to be 0.1 seconds. These can be tweaked in your app to create the most realistic feeling action.

Step 2

Next write the methods to hand the long press and the swipe, called handleLongPress: and handleSwipe: respectively. The long press method will be used to determine if the swipe gesture actually has any effect. Imagine a physical piece of paper. You can try tearing it as much as you like, but if it’s not held in place then nothing will happen.

handleLongPress: is called whenever the state of the gesture recognizer changes, e.g. the gesture began or ended. We want to check if the gesture has ended, i.e. the finger is lifted, and ensure that self.isHeld is set back to NO. If if the gesture hasn’t ended then set self.isHeld to YES which we will use to allow the swipe to perform its action.

- (void)handleLongPress:(UIGestureRecognizer *)sender
{
	if (sender.state == UIGestureRecognizerStateEnded) {
		self.isHeld = NO;
	} else if(!self.isHeld){
        self.isHeld = YES;
    }
}

Step 3

Next let’s implement handleSwipe:. We check if self.isHeld is true and if so we animate the ticketMain view off the screen using the curl up transition, which looks like the ticket is being torn.

- (void)handleSwipe:(UIGestureRecognizer *)sender
{
    if(self.isHeld){
		[UIView transitionWithView:self.containerView duration:1
						   options:UIViewAnimationOptionTransitionCurlUp
						animations:^ { [sender.view removeFromSuperview]; }
						completion:nil];
	}
}

Step 4

Build and run the app to try out the new gestures. Press and hold the ticket stub and simultaneously swipe across the other part of the ticket. You should see the main part ‘tear off’ and disappear.

digital-tickets-redeem-ticket-animating-preview

Conclusion

We’ve looked at creating a basic navigation structure to show a list of tickets and a ticket screen which can be redeemed by pressing one part of the screen and swiping across another part, creating an effect similar to tearing a ticket in real life.

This is by no means the end of the road. Tapping back and selecting a row from the table will take you right back to the screen with the ticket still intact. You need to write some logic to prevent a redeemed ticket from being able to be redeemed again.

I’ve also been using images throughout to represent tickets. This is to eliminate as much unnecessary code as possible for this tutorial, so we can really concentrate on the important parts. In a real life situation you’d want to use fields that you could dynamically include information about the event.

There are several ways you could improve security and eliminate accidental redemptions. You could record the location and time of the device when the ticket was swiped. Obviously if the ticket was redeemed several days before the event and at a location nowhere near the venue you could safely assume that it was an accident. A ticket redeemed 5 minutes ago right outside the venue is probably someone chancing their luck.

Create a Space Defender Game – Game Logic

$
0
0

In this tutorial series, we will be learning how to create a space shooter game just like the classic game Space Defender. Read on!


Series Overview

In this version of Space Defender, the player will have to defend his space by shooting enemies. Every time the player successfully destroys an enemy, they will earn points and when the player has reached 20 or 40 points, their gun will receive an upgrade. To mix things up, this game will send out bonus packages that are worth 5 points. To see the game in action, watch the short video above.


Where We Left Off…

In part 1 of this series, we learned how to set up our app, how to use custom fonts, how to use a storyboard, and how to set up our main menu. In Part 2 of this series, we will learn how to create the gameplay of our app. So, let’s get started!

Our first step is to create a new file called game.lua. Once it’s created, open the file in your favorite editor.


1. Adding Libraries

Since we are starting a new scene, we have to require some libraries. We will be using the physics engine built into Corona SDK for collision detection.

local storyboard = require( "storyboard" )
local scene = storyboard.newScene()
local physics = require("physics")

2. Configuring Physics

After we have our libraries setup, we will configure the physics engine. The settings below will set the gravity to 0 (just like in space) and set the iterations to 16. The setPositionIterations means the engine will go through 16 positions per frame. Anything higher than 16 can adversely affect game performance.

physics.start()
physics.setGravity(0, 0)
physics.setPositionIterations( 16 )

3. Using A Random Generator

Although this step is not necessary for this tutorial, it’s a good practice to “seed” the random number generator. I like to use the current time to seed the generator.

math.randomseed( os.time() )

4. Setting Up Game Variables

Now we will define some variables for our game. Each variable has a comment next to it explaining the purpose of the variable.

local screenW, screenH, halfW, halfY = display.contentWidth, display.contentHeight, display.contentWidth*0.5, display.contentHeight*0.5
local gameover_returntomenu -- forward declard our game over button
-- Set up game settings
motionx = 0; -- Variable used to move character along y axis
speed = 10; -- Controls the ship speed
playerScore = 0; -- Sets the player score
playerLives = 20; -- Sets the number of lives for the player
slowEnemySpeed = 2375; -- Sets how fast the white ships move across screen
slowEnemySpawn = 2400; -- Sets white ship spawn rate
fastEnemySpeed = 1875; -- Sets how fast the green ships move across screen
fastEnemySpawn = 1800; -- Sets green ship spawn rate
bulletSpeed = 325; -- Sets how fast the bullet travels across screen
bulletSpawn = 250; -- Sets bullet spawn rate

5. Create the Main Game Scene

After creating the variables, we are going setup the scene inside the function scene:createScene. If you remember from Part 1, this function is used to create visual elements and game logic. In a later function, we will call upon these functions to run the game.

In the following code, we are creating the scene:createScene function and adding the background and top/bottom walls. Both walls are set up as static physics objects to prevent the player from going off screen.

function scene:createScene( event )
local group = self.view
-- Set up visual elements and walls
local bg = display.newImageRect("images/BKG.png", 480, 320)
  bg.x = halfW
  bg.y = halfY
  group:insert(bg)
local topwall = display.newRect(0,0,screenW,20)
  topwall.y = -5
  topwall:setFillColor(0,0,0)
  topwall.alpha = 0.01
  physics.addBody( topwall, "static" )
  group:insert(topwall)
local bottomwall = display.newRect(0,0,screenW,20)
  bottomwall.y = 325
  bottomwall:setFillColor(0,0,0)
  bottomwall.alpha = 0.01
  physics.addBody( bottomwall, "static" )
  group:insert(bottomwall)
end

6. Adding HUD Elements

Inside of the same scene:createScene function, but after the bottomwall display object, we are going to add four more display objects. Here’s an explanation of the purpose of each object.

  • btn_up, btn_down: These display objects will act as buttons on the left hand side of the screen and each object will move the ship up or down respectively. However, they are not operable until we set up the move function.
  • enemyHitBar: This display object is set up as a sensor and will only react to physic collisions. When it does react to collisions, it will remove the enemy object and subtract one from player lives.
local btn_up = display.newRect(0,0,75,160)
  btn_up:setReferencePoint(display.TopLeftReferencePoint)
  btn_up.x, btn_up.y = 0,0;
  btn_up.alpha = 0.01
  group:insert(btn_up)
local btn_down = display.newRect(0,0,75,160)
  btn_down:setReferencePoint(display.BottomLeftReferencePoint)
  btn_down.x, btn_down.y = 0, screenH;
  btn_down.alpha = 0.01
  group:insert(btn_down)
local enemyHitBar = display.newRect(-20,0,20,320)
  enemyHitBar:setFillColor(0,0,0)
  enemyHitBar.name = "enemyHitBar"
  physics.addBody( enemyHitBar, { isSensor = true } )
  group:insert(enemyHitBar)

Right after the enemyHitBar display object, we are going to add some GUI elements to display the player score and player lives. We will also show text on the screen that says “Move Up” and “Move Down” to notify the player where they need to touch to move the ship up or down.

local gui_score = display.newText("Score: "..playerScore,0,0,"Kemco Pixel",16)
  gui_score:setReferencePoint(display.TopRightReferencePoint)
  gui_score.x = screenW
  group:insert(gui_score)
local gui_lives = display.newText("Lives: "..playerLives,0,0,"Kemco Pixel",16)
  gui_lives:setReferencePoint(display.BottomRightReferencePoint)
  gui_lives.x = screenW
  gui_lives.y = screenH
  group:insert(gui_lives)
local gui_moveup = display.newText("Move Up",0,0,50,100,"Kemco Pixel",16)
  group:insert(gui_moveup)
local gui_movedown = display.newText("Move Down",0,0,50,23,"Kemco Pixel",16)
  gui_movedown:setReferencePoint(display.BottomLeftReferencePoint)
  gui_movedown.y = screenH
  group:insert(gui_movedown)

7. Adding the Player Ship

Next, we will be adding the player’s ship to the screen. The ship will be added as a dynamic physics object so it can react to collisions with other physics objects. We will go into further depth on collisions later in this tutorial.

local ship = display.newImageRect("images/spaceShip.png", 29, 19)
  ship.x, ship.y = 75, 35
  ship.name = "ship"
  physics.addBody( ship, "dynamic", { friction=0.5, bounce=0 } )
  group:insert(ship)

8. Moving the Player Ship

Do you remember the btn_up and btn_down display objects we added? We are now going to add event listeners to these objects to help make the player ship move. When btn_up is touched, we will make our speed variable negative and when btn_down is touched we will make our speed positive. By making this variable positive and negative, we are telling our next function to move the ship up or down.

-- When the up button is touched, set our motion to move the ship up
function btn_up:touch()
  motionx = -speed;
end
btn_up:addEventListener("touch",btn_up)
-- When the down button is touched, set our motion to move the ship down
function btn_down:touch()
  motionx = speed;
end
btn_down:addEventListener("touch",btn_down)

9. Enabling Movement

After we’ve added event listeners to our btn_up and btn_down display objects, we are going to create two runtime event listeners with their respective functions. These functions will run every frame and the one catch with runtime functions is that you must specify when to stop them. We’ll cover that later. For now, the stop function will set the variable motionx to 0 (because neither button is touched) and the moveguy function will add the variable motionx to our ship’s y position.

local function stop (event)
  if event.phase =="ended" then
    motionx = 0;
  end
end
Runtime:addEventListener("touch", stop )
-- This function will actually move the ship based on the motion
local function moveguy (event)
  ship.y = ship.y + motionx;
end
Runtime:addEventListener("enterFrame", moveguy)

10. Firing Bullets

By now, we have our ship moving, but it’s not firing! To get the ship ready to fire bullets, we have to create the fireShip() function. This function will create new display objects that react to physics collisions and this function will also move the object across the screen from left to right.

To make the game more interesting, we will allow the player to shoot more bullets when they reach a certain score. When the player reaches 20, the ship will shoot two bullets and when the player reaches 40, the ship will fire a third bullet that shoots downward diagonally.

function fireShip()
  bullet = display.newImageRect("images/bullet.png", 13, 8)
  bullet.x = ship.x + 9
  bullet.y = ship.y + 6
  bullet:toFront()
  bullet.name = "bullet"
  physics.addBody( bullet, { isSensor = true } )
  transition.to(bullet, {time = bulletSpeed, x = 500,
    onComplete = function (self)
    self.parent:remove(self); self = nil;
  end; })
  if(playerScore >= 20) then
    secondBullet = display.newImageRect("images/bullet.png", 13, 8)
    secondBullet.x = ship.x + 9
    secondBullet.y = ship.y + 12
    secondBullet:toFront()
    secondBullet.name = "bullet"
    physics.addBody( secondBullet, { isSensor = true } )
    transition.to(secondBullet, {time = bulletSpeed, x = 500,
    onComplete = function (self)
      self.parent:remove(self); self = nil;
    end; })
  end
  if(playerScore >= 40) then
    thirdBullet = display.newImageRect("images/bullet.png", 13, 8)
    thirdBullet.x = ship.x + 9
    thirdBullet.y = ship.y + 12
    thirdBullet:toFront()
    thirdBullet.name = "bullet"
    physics.addBody( thirdBullet, { isSensor = true } )
    transition.to(thirdBullet, {time = bulletSpeed, x = 500, y = ship.y + 100,
    onComplete = function (self)
      self.parent:remove(self); self = nil;
    end; })

11. Creating Enemies

After we’ve set up our ship to fire, we need to give the player some enemies to shoot at! We’ll create two different functions – createSlowEnemy() and createFastEnemy(). Both functions will create a physics display object that moves from right to left with the speed of the enemy and the image being the only difference.

function createSlowEnemy()
  enemy = display.newImageRect("images/enemy.png", 32, 26)
  enemy.rotation = 180
  enemy.x = 500
  enemy.y = math.random(10,screenH-10)
  enemy.name = "enemy"
  physics.addBody( enemy, {isSensor = true } )
  transition.to(enemy, {time = slowEnemySpeed, x = -20 })
end
function createFastEnemy()
  enemy = display.newImageRect("images/fastEnemy.png", 32, 26)
  enemy.rotation = 180
  enemy.x = 500
  enemy.y = math.random(10,screenH-10)
  enemy.name = "enemy"
  physics.addBody( enemy, {isSensor = true } )
  transition.to(enemy, {time = fastEnemySpeed, x = -20 })
end

12. Create Bonus Packages

Next, we’ll create bonus packages for our player to grab inside the function createBonus(). The createBonus() function will create a physics display objects that moves right to left and each bonus package the player grabs, they will earn 5 points.

function createBonus()
  bonus = display.newImageRect("images/bonus.png", 18, 18)
  bonus.rotation = 180
  bonus.x = 500
  bonus.y = math.random(10,screenH-10)
  bonus.name = "bonus"
  physics.addBody( bonus, {isSensor = true } )
  transition.to(bonus, {time = 1475, x = -20,
  onComplete = function ()
    display.remove(bonus)
    bonus = nil
  end; })
end

13. Updating Player Lives

Our next to last function is the updateLives() function. This function will be called every time an enemy gets past the player to give the player the goal of defending his side of space. If the number of lives is above 0, then this function will subtract one life and update the on screen text. Otherwise, it will result in a game over scene.

In the game over scene, we are canceling all of our timers and remove all of our event listeners. With the Corona SDK, it’s very important to remember that you have to explicitly tell your app when to remove runtime listeners and timers (only when the timer is running). After these have been removed, we will display a game over message and allow the player to return to the menu.

function updateLives()
  if(playerLives >= 0) then
    playerLives = playerLives - 1
    gui_lives.text = "Lives: "..playerLives
    gui_lives.x = screenW
  else
    timer.cancel(tmr_fireShip)
    timer.cancel(tmr_sendSlowEnemies)
    timer.cancel(tmr_sendSlowEnemies2)
    timer.cancel(tmr_sendFastEnemies)
    timer.cancel(tmr_sendBonus)
    Runtime:removeEventListener( "collision", onCollision )
    Runtime:removeEventListener("enterFrame", moveguy)
    Runtime:removeEventListener("touch", stop )
    -- Display game over screen
    local gameover_message = display.newText("Game Over!",0,0,"Kemco Pixel",32)
    gameover_message.x = halfW
    gameover_message.y = halfY - 15
    group:insert(gameover_message)
    function returnToMenuTouch(event)
    if(event.phase == "began") then
      storyboard.gotoScene("menu", "slideRight", "1000")
    end
  end
  gameover_returntomenu = display.newText("Return To Menu",0,0,"Kemco Pixel", 28)
  gameover_returntomenu.x = halfW
  gameover_returntomenu.y = halfY + 35
  gameover_returntomenu:addEventListener("touch", returnToMenuTouch)
  group:insert(gameover_returntomenu)
  end
end

14. Collision Detection

We are ready for our final function inside of our scene:createScene() function! This function will handle all of our collision detection by comparing the property myName of object1 to that held by object 2. Each object is passed as a parameter to this function under the variable name event.

To make it easier for you, I’ve broken down the five collision cases.

  • Case 1– Object 1 is a bullet and Object 2 is an enemy
    What’s happening: Remove enemy and update score
  • Case 2– Object 1 is an enemy and Object 2 is an bullet
    What’s happening: Remove enemy and update score
  • Case 3– Object 1 is a ship and Object 2 is an bonus
    What’s happening: Remove bonus and update score
  • Case 4– Object 1 is a enemy and Object 2 is an enemyHitBar
    What’s happening: Remove enemy and update lives
  • Case 5– Object 1 is a enemyHitBar and Object 2 is an enemy
    What’s happening: Remove enemy and update lives
function onCollision( event )
  if(event.object1.name == "bullet" and event.object2.name == "enemy") then
    display.remove(event.object2)
    playerScore = playerScore + 1
  elseif(event.object1.name == "enemy" and event.object2.name == "bullet") then
    display.remove(event.object1)
    playerScore = playerScore + 1
  elseif(event.object1.name == "ship" and event.object2.name == "bonus") then
    display.remove(event.object2)
    playerScore = playerScore + 5
  elseif(event.object1.name == "enemy" and event.object2.name == "enemyHitBar") then
    display.remove(event.object1)
    updateLives()
  elseif(event.object1.name == "enemyHitBar" and event.object2.name == "enemy") then
    display.remove(event.object2)
    updateLives()
  end
  gui_score.text = "Score: " .. playerScore
  gui_score.x = screenW
end

15. Syncing Movement Timers

Since we have everything set up for our game, we just need to make everything move! Inside of the function scene:enterScene()– remember that the enterScene function is outside of the createScene function – we will create 5 timers and one runtime listener. The timers will send out the bullets, enemies, and bonuses while the runtime listener will handle the collision detection.

function scene:enterScene( event )
  local group = self.view
  tmr_fireShip = timer.performWithDelay(bulletSpawn, fireShip, 0)
  tmr_sendSlowEnemies = timer.performWithDelay(slowEnemySpawn, createSlowEnemy, 0)
  tmr_sendSlowEnemies2 = timer.performWithDelay(slowEnemySpawn+(slowEnemySpawn*0.5), createSlowEnemy, 0)
  tmr_sendFastEnemies = timer.performWithDelay(fastEnemySpawn, createFastEnemy, 0)
  tmr_sendBonus = timer.performWithDelay(2500, createBonus, 0)
  Runtime:addEventListener( "collision", onCollision )
end

16. Destroying the Scene

The final addition (I promise!) is the scene:destroyScene() function and the scene event listeners. The destroy scene function will make sure the physics are removed once the player leaves the scene. The scene event listeners will call the createScene, enterScene, and destroyScene respectively.

function scene:destroyScene( event )
  local group = self.view
  package.loaded[physics] = nil
  physics = nil
end
scene:addEventListener( "createScene", scene )
scene:addEventListener( "enterScene", scene )
scene:addEventListener( "destroyScene", scene )
return scene

Conclusion

Congratulations! You have learned about a lot of things such as Corona’s storyboard feature, physics, collisions, and so much more! These are valuable skills that can be applied to almost any game and if you want to build this game for your device, I strongly recommend the official Corona documents on building for the device.

Thank you so much for reading! If you have any questions, please leave them in the comments below.

Tuts+ & Envato Are Coming to the UK

$
0
0

We’re excited to let you know that we’re currently planning our first Tuts+ and Envato meetup in the UK! We’ll have a great venue, some exciting goodies to give away, and you’ll have the opportunity to meet various Envato staff (as well as fellow Tuts+ readers!). If you’re interested in joining us, read on to find out how you can RSVP, and help us choose the meetup location.


What’s Happening?

Although Envato has organized plenty of meetups to date (in places such as Melbourne, Kuala Lumpur, Chicago, and New York!), this will be our first official UK event. First and foremost, it’s a brilliant chance to meet lots of like-minded creative professionals and developers, as well as various members of the Tuts+ team. A few of the editors likely to be in attendance are Michael James Williams, Johnny Winter, Sharon Milne, Joel Bankhead, David Appleyard, Ian Yates, Neil Pearce, and one of our top authors — Martin Perhiniak.

We’ll have a fantastic, funky venue, free food and drink, and lots of exciting goodies to give away to attendees. You’ll have the chance to talk to our team about what we’re doing at Tuts+ and Envato, as well as picking their brains on anything from web design and game development, to illustration and electronics!

Just to give you a feel for what to expect, here’s a selection of photos from last year’s meetup in New York:

london

When and Where?

We’ve fixed a date of Saturday 9th November. It’s far enough away to give you plenty of time to plan, and close enough to the holidays for you to also do a little Christmas shopping before the meetup in the evening! The meetup will be happening late afternoon/evening (although we’ll let you know all the final times closer to the date).

We’re going to be holding it in either London, or Manchester — it’s up to you to decide! If you’re interested in attending, we’d be thrilled to meet you. Submit your RSVP below, and be sure to let us know where you’d like the meetup to be held. We’ll keep you updated as we get closer to the date.


RSVP to Join the Guest List!


We’re incredibly excited to have the opportunity to meet lots of our wonderful readers, and we really hope you’ll be able to make it. Let’s bring Tuts+ and Envato to the UK!

Android SDK: Drawing with Pattern Fills

$
0
0

This tutorial demonstrates how to enhance a drawing application by allowing the user to paint with pattern fills rather than solid colors. Read on!

The touchscreen interaction on many Android devices makes the platform well suited to drawing apps. In a recent series, we created a basic Android drawing app, with the user able to choose from a color palette and draw on a canvas using their finger. In this tutorial, we will expand on what we covered by using pattern fills rather than solid colors. You do not have to complete the drawing app series before attempting this tutorial, but if you have you can use it to enhance the app you built previously.

Here is a preview of the pattern drawing app we will create in this tutorial:

Drawing With Patterns

The tutorial will guide you through the process of building a standalone app, but will also include tips for using the code to enhance the app we created in the series. The source code download folder includes both the app we build in this tutorial and the enhanced version of the general drawing app we built in the series. Here is a preview of the existing app enhanced with the pattern drawing functionality:

Drawing With Patterns

1. Create or Extend an App

Step 1

Although you can complete this tutorial without having worked through the Drawing App series, we will gloss over some of the details of drawing functions that were covered more thoroughly there. We will include complete instructions for using the code in a new app as well as indicating how you could use it to enhance the existing drawing app. If you already have the app we created in the series, you can jump straight to part 2, step 1 of the tutorial now.

If you are starting a new app for this tutorial, create one in Eclipse now, choosing a minimum API level of 14 and other settings of your choice. Let Eclipse create a blank Activity and layout for the app. We will be using much of the same code we used in the drawing app series, so for more information on the functionality please refer to the three tutorials within it.

Add a new class to the app for a custom View in which the drawing will take place, name the class “DrawingView” and extend the opening declaration as follows:

public class DrawingView extends View

You will need the following import statements in the class:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

Add the following instance variables to the class:

//drawing path
private Path drawPath;
//drawing and canvas paint
private Paint drawPaint, canvasPaint;
//initial color
private int paintColor = 0xFF000000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;

Give the class a constructor method in which we call a helper method we will add next:

public DrawingView(Context context, AttributeSet attrs){
	super(context, attrs);
	setupDrawing();
}

Add the helper method now:

private void setupDrawing(){
	drawPath = new Path();
	drawPaint = new Paint();
	drawPaint.setColor(paintColor);
	drawPaint.setAntiAlias(true);
	drawPaint.setStrokeWidth(50);
	drawPaint.setStyle(Paint.Style.STROKE);
	drawPaint.setStrokeJoin(Paint.Join.ROUND);
	drawPaint.setStrokeCap(Paint.Cap.ROUND);
	canvasPaint = new Paint(Paint.DITHER_FLAG);
}

The method sets the class up for drawing. Override the method for when the View is assigned a size next:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
	super.onSizeChanged(w, h, oldw, oldh);
	canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
	drawCanvas = new Canvas(canvasBitmap);
}

Now override the onDraw method to display the picture being drawn by the user:

@Override
protected void onDraw(Canvas canvas) {
	canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
	canvas.drawPath(drawPath, drawPaint);
}

Finally, add the following method to implement the user drawing through touch on the View:

@Override
public boolean onTouchEvent(MotionEvent event) {
	float touchX = event.getX();
	float touchY = event.getY();
	//respond to down, move and up events
	switch (event.getAction()) {
	case MotionEvent.ACTION_DOWN:
		drawPath.moveTo(touchX, touchY);
		break;
	case MotionEvent.ACTION_MOVE:
		drawPath.lineTo(touchX, touchY);
		break;
	case MotionEvent.ACTION_UP:
		drawPath.lineTo(touchX, touchY);
		drawCanvas.drawPath(drawPath, drawPaint);
		drawPath.reset();
		break;
	default:
		return false;
	}
	//redraw
	invalidate();
	return true;
}

Step 2

Now let’s display the custom View in the app user interface. Open your main layout file and include the following outline:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:background="#FFCCCCCC"
	android:orientation="vertical"
	tools:context=".MainActivity" ></LinearLayout>

Inside the Linear Layout, add an instance of the custom View class you created, using the package prefix for your own app:

<com.example.patterndraw.DrawingView
	android:id="@+id/drawing"
	android:layout_width="fill_parent"
	android:layout_height="0dp"
	android:layout_marginBottom="3dp"
	android:layout_marginLeft="5dp"
	android:layout_marginRight="5dp"
	android:layout_marginTop="3dp"
	android:layout_weight="1"
	android:background="#FFFFFFFF" />

We want the canvas area to fill the space apart from the palette buttons we will add next.


2. Pattern Buttons

Step 1

We are going to use 8 pattern fills – you can use the following images or create your own if you prefer. Save them into your project’s drawables folder(s) – they are included in the source code download folder. We will use the file names to refer to the images in the application code.

Pattern
Pattern
Pattern
Pattern
Pattern
Pattern
Pattern
Pattern

You can use your own pattern images if you like, as long as they each contain a tileable bitmap image. We will be using the images on the pattern buttons in the UI as well as in the drawing functions themselves. We show them on the buttons so that the user can see what each pattern looks like before choosing one.

Step 2

Let’s now add a palette to our user interface for the pattern buttons. Open your layout file. If you are working with the app we created in the series, you can add new buttons to the palette area for the patterns – see the tip below and refer to the source code download for a completed example of the existing app with the new functionality added. For the new app, add the following after the custom View element you added:

<LinearLayout
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_gravity="center"
	android:orientation="vertical" ><!-- top row --><LinearLayout
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:orientation="horizontal" ></LinearLayout><!-- bottom row --><LinearLayout
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:orientation="horizontal" ></LinearLayout></LinearLayout>

We will list four buttons on each row. Add the first four inside the Linear Layout for the top row:

<ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern1"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern1" /><ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern2"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern2" /><ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern3"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern3" /><ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern4"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern4" />

Add the next four inside the Linear Layout for the bottom row:

<ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern5"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern5" /><ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern6"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern6" /><ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern7"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern7" /><ImageButton
	android:layout_width="30dp"
	android:layout_height="30dp"
	android:layout_margin="2dp"
	android:background="@drawable/pattern8"
	android:contentDescription="pattern"
	android:onClick="paintClicked"
	android:src="@drawable/paint"
	android:tag="pattern8" />

Tip: For the app we created in the series, alter this code to use the same Image Button structures as your color buttons, but with the pattern listed as drawable instead of a color and the pattern name as tag attribute as in the code above. You can replace some of your color buttons with pattern buttons or simply add some alongside the color buttons. If you are adding lots of buttons to the app, you may need to reduce the sizes so that they fit neatly on a range of screens.

Each Image Button specifies a method to call on user clicks – this method will be able to access the pattern name for a clicked button from its tag attribute.


3. Pattern Fills

Step 1

Tip: You can keep the main Activity class as it is if you completed the series, the only changes you will need to make are to the custom View class as you will see below.

Now we can implement using the pattern fills. In your app’s main Activity class, add the following import:

import android.view.View;

Before the onCreate method, add the following instance variable for the custom View class:

private DrawingView drawView;

In onCreate, instantiate this with a reference to the custom View instance we added to the layout:

drawView = (DrawingView)findViewById(R.id.drawing);

Next add the method we listed as onClick attribute for the pattern buttons:

public void paintClicked(View view){
//set pattern
}

Inside the method, retrieve the tag from the clicked pattern Image Button:

String pattern = view.getTag().toString();

We need the custom View to set the pattern fill based on the user selection. In your “DrawingView” class, add the following method (see the tip below if you are enhancing the existing drawing app for an alternative to adding this method):

public void setPattern(String newPattern){
//set pattern
}

Inside the method, first invalidate the View:

invalidate();

We are going to use the pattern drawable name to bring it into the painting class as a Bitmap. Get a reference to the resource ID for the passed pattern String:

int patternID = getResources().getIdentifier(newPattern, "drawable", "com.example.patterndraw");

Alter the package name to suit your own. Next in the method, decode the resource as a Bitmap, passing the retrieved ID value:

Bitmap patternBMP = BitmapFactory.decodeResource(getResources(), patternID);

Rather than drawing directly with the Bitmap, we are going to draw in a solid color and use the Bitmap as Shader for it. Create a Shader object using the decoded pattern Bitmap:

BitmapShader patternBMPshader = new BitmapShader(patternBMP,
	Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

We want the pattern to repeat throughout the area the user draws on with it. Set the color to solid white and the Shader to use the pattern:

drawPaint.setColor(0xFFFFFFFF);
drawPaint.setShader(patternBMPshader);

This will create the impression that the user is drawing with the pattern as paint, just like with a solid color. It does not matter what size the pattern drawable image is, as long is it is tileable it will be repeated seamlessly throughout the canvas wherever the user chooses to draw with it.

Back in the main Activity paintClicked method, you can now call the new method with the pattern String retrieved from the button tag:

drawView.setPattern(pattern);

The user will be able to tap any pattern they want to use in the palette area and this will be reflected when they then draw on the canvas. For more advanced behavior in the palette buttons, please do refer to the drawing app series if you have not already completed it.

Tip: If you are enhancing the existing drawing app, you can continue using the setColor method from your main Activity, altering the content of the method in the “DrawingView” class to use the above processing if the passed color String does not begin with the “#” character, using the existing processing if it does. If the user has chosen a color rather than a pattern, you also need to set the Shader on the Paint object back to null – see the source code for details.

You should be able to run the app and test the pattern drawing functionality now. Selecting a pattern by tapping the palette buttons, then touching the canvas area with your finger and dragging across the screen will draw with the chosen pattern as it does with a solid color.


Conclusion

In this tutorial we have demonstrated how to implement drawing functions using patterns rather than colors in your Android apps. Using a pattern fill is only slightly different to using solid color, but does require a little more processing as you have seen. Remember to check the source code download for further information if you have been working on the app we created in the drawing app series. There are lots of other options you can explore in drawing apps, including opacity, which we will look at in a forthcoming tutorial. We will also be looking at interaction that does not come from a touchscreen. In the meantime, see if you can enhance your drawing apps in other ways.


Build a Groundhog Whack Game – Interface Creation

$
0
0

In this tutorial series we will create a Whack-a-Groundhog game. The objective of the game is tap on the groundhogs before they disappear. Read on!


1. Intro Screen

The intro screen will allow the user to set some options or begin to play the game.

intro_screen

2. Options Screen

The options screen will allow the user to turn on/off the music and choose at which speed the groundhogs appear.

options_screen

3. Game Screen

The game screen is where all the action takes place. The groundhogs randomly pop out of their holes,
and the player tries to “whack” them before they go back inside. The player can also get to the options screen from here as well.

game_screen

4. New Project

Open the Corona Simulator and choose “New Project”.

GroundHog-Game-Corona_1

On the screen that follows, choose the following settings:

settings

Press the “Next” button, then choose open in editor. This will open “Main.lua” in your default text editor.


5. Project Configuration

Open “Config.lua” and delete everything. Update the file with the following:

application = {
   content = {
     width = 320,
     height = 480,
     scale = "letterBox",
     fps = 30,
   }
}

This sets the project’s default width, height, scale, and FPS. The “letterBox” scale setting means the app will scale up in both directions as uniformly as possible. If necessary the game will display “Letter Boxed”, like you see in some DVD movies or with old apps on an iPhone 5.


6. Hiding the Status Bar

We don’t want the status bar showing in our app, so enter the following in “Main.lua“:

display.setStatusBar(display.HiddenStatusBar);

7. Local Variables

These are all of the variables we’ll be using in this game. Read the comments to understand what each is for.

local allGroundHogs = {} -- Table to hold all the groundhogs
local groundHogXPositions = {240,410,280,145,429,80,208,366}
local groundHogYPositions = {259,259,209,184,166,118,91,99}
local gameScreenGroup -- group that holds the game screen
local introScreenGroup -- group that holds the intro screen
local optionsScreenGroup -- group that holds options screen
local isPlaying = false -- whether the player has started the game or not
local groundHogTimer -- timer that is used to generate groundhogs
local groundHogSpeed = 1500

8. SetUpIntroScreen()

The setUpIntroScreen() function is used to setup the first screen the user will be presented with.

function setUpIntroScreen()
end

9. SetUpOptionsScreen()

The setUpOptionsScreen() function takes care of setting up the options screen.

function setUpOptionsScreen()
end

10. SetUpGameScreen()

The setUpGameScreen() function sets up the game screen.

function setUpGameScreen()
end

11. GetRandomGroundHog()

The getRandomGroundHog() function selects a random groundhog to appear in one of the holes.

function getRandomGroundHog()
end

12. GroundHogSpriteListener()

This function is used to tell when the groundhog’s animation has ended.

function groundHogSpriteListener( event )
end

13. GroundHogHit()

The groundHogHit(e) function is used to tell when the player has tapped a groundhog.

function groundHogHit(e)
end

14. SoundComplete()

When the music stops playing we use this function to start it over again.

function soundComplete()
end

15. Setting Up the Game BackGround

In this step we will begin to setup the game screen. Enter the following code inside the setUpGameScreen() function you entered in the step above.

gameScreenGroup = display.newGroup()
    local gameBackground = display.newImage("background.png", true);
    gameScreenGroup:insert(gameBackground)

At the very bottom of “main.lua”, enter the following:

setUpGameScreen()

If you test now you should see the game background.


16. Setting Up the Image Sheet

The groundhogs are animated using a sprite sheet. Enter the following beneath the line gameScreenGroup:insert(gameBackground) that you entered in the step above.

local options = {width = 142, height = 91, numFrames = 7}
local imageSheet = graphics.newImageSheet("groundhogsheet.png",options)

The options variable is a table that holds the options for the image sheet. The width and height are the width and height of the images in the “groundhogsheet.png”, and the numFrames is equal to the number of images in the .png


17. Sequence Data

Now that we have our imageSheet setup we can setup the animations. The animation(sequence) data is kept in a variable sequenceData. Enter the following beneath the code from above.

local sequenceData = {
    {name="show", start=2 , count = 3,time=1000,loopCount=0,loopDirection="bounce"}
}

Here we name the sequence “show”, the start is the “frame” of the imageSheet that the sequence starts on and count is the number of frames in the sequence.

The loopCount is how many times you want the sequence to play. Zero means forever, and the loopDirection is how you want the sequence to play. The bounce option means play forward, then backwards.


18. GroundHog Sprites

With the imageSheet and sequenceData setup, we can place our groundhogs and animate them. Enter the following code beneath the sequenceData you entered in the step above.

local tempGroundHog
    for i=1, #groundHogXPositions do
     	tempGroundHog = display.newSprite(imageSheet,sequenceData)
	     tempGroundHog.x = groundHogXPositions[i]
	     tempGroundHog.y = groundHogYPositions[i]
	     tempGroundHog:setSequence("show")
	     gameScreenGroup:insert(tempGroundHog)
         table.insert(allGroundHogs,tempGroundHog)
         tempGroundHog:play()
    end

Each time through the loop we create a new tempGroundHog Sprite, set its x an y positions, set the Sequence to “show”, insert it into the gameScreenGroup, and then insert it into the allGroundHogs table.

IF you test now you should see all of the groundhog animations playing. It looks a little wrong though, and that is something we will fix in the next part of this series!


Conclusion

This brings the first part of this series to an end. In the next part we will finish the groundhog animation and complete the game logic. Thanks for reading and stay tuned!

Build a Groundhog Whack Game – Gameplay Logic

$
0
0

In this tutorial series we will create a Whack-a-Groundhog game. The objective of the game is tap on the groundhogs before they disappear. Read on!


Where We Left Off…

In the previous part of this series we added the game background and began animating our groundhogs. In this the final part of the series, we will implement the gameplay, options, and start screen of our app, and then end up with a complete game!


1. setupGameScreen()

As the game is now, the groundhogs animate forever and do not seem to go back inside their holes. We need to add some more keys to our sequenceData to fix this. Add the following within the setupGameScreen() function:

local sequenceData = {
    {name="show", start=2 , count = 3,time=800,loopCount=1,loopDirection="bounce"},
    {name = "blank",start=1,count=1},
    {name = "hit1",start=5,count=1},
    {name = "hit2", start=6,count=1},
    {name = "hit3", start=7,count=1}
}

The “blank” sequence is a blank transparent image which is part of the sprite sheet, and the “hit1″, “hit2″, and “hit3″ sequences are 3 different “hit” states of the groundhog. Look at the “groundhogsheet.png” image to see this for yourself.

Make sure you set the loopCount equal to 1 on the “show” sequence.

Now change the following code block:

tempGroundHog:setSequence("blank")
tempGroundHog:addEventListener('tap', groundHogHit);

Here we have set the sequence to “blank” and have added a tap listener to the groundhog sprite.

Next, remove the following line of code:

tempGroundHog:play()

If you test now, none of the groundhogs should be animating. We will soon be getting a random groundhog animating!


2. groundHogHit()

When the groundhog is tapped we will determine whether or not it was out of its hole. If so, we will change its sequence to one of the three hit states that we added in the previous step.

Add the following inside the groundHogHit() function:

local thisSprite = e.target
thisSprite:removeEventListener( "sprite", groundHogSpriteListener )
    local function hide()
        thisSprite:setSequence("blank")
    end
    if(thisSprite.sequence == "show") then
        local randomIndex = math.random(3)
	thisSprite:setSequence("hit"..randomIndex)
	timer.performWithDelay(1000, hide)
    end

Here we reference the Sprite that was clicked by e.target and remove its Event Listener. We then check if its sequence is equal to “show”. If it is, we generate a number between 1 and 3 and set its sequence equal to "hit"..randomIndex. All this does is generate the strings “hit1″, “hit2″, or “hit3″. Finally, we call the local hide function after 1000 milliseconds which sets the Sprites sequence to “blank”.


3. getRandomGroundHog()

The getRandomGroundHog() function gets a random groundhog and allows it to begin animating. Enter the following inside the getRandomGroundHog() function:

local randomIndex = math.random(#allGroundHogs)
    local randomGroundHog = allGroundHogs[randomIndex]
    if(randomGroundHog.sequence ~="blank") then
         getRandomGroundHog()
    else
        randomGroundHog:addEventListener( "sprite",groundHogSpriteListener )
	randomGroundHog:setSequence("show")
	randomGroundHog:play()
    end

Here we get a randomIndex from the allGroundHogs table. Next, we set the randomGroundHog equal to the index. We then check if its sequence does not equal “blank”, and, if it doesn’t, we call getRandomGroundHog() again. Otherwise, we add the groundHogSpriteListener to set its sequence to “show” and play the sequence.


4. groundHogSpriteListener()

The groundHogSpriteListener() checks if the “show” sequence has finished playing. If so, it sets it to the “blank” sequence. Enter the following within the groundHogSpriteListener():

local thisSprite = event.target --"event.target" references the sprite
    if ( event.phase == "ended" ) then
        if(thisSprite.sequence == "show") then
            thisSprite:setSequence("blank")
        end -- a half second delay
    end

5. Animating Random GroundHogs

With all the above in place we get random groundhogs animating. Add the following to the bottom of the
setUpGameScreen() function:

groundHogTimer = timer.performWithDelay(groundHogSpeed, getRandomGroundHog,0)

If you test now you should see the groundhogs randomly popping out of their holes. While you are there, try clicking on the groundhogs while they are out of their holes. You should see one of the 3 hit states, and then the blank state.

When you are done testing, remove the line you just entered. We only needed it for testing.


6. setUpIntroScreen()

In this step we will begin to setup the intro screen. Enter the following code inside the setUpIntroScreen function:

media.playSound("gameTrack.mp3",soundComplete)
introScreenGroup = display.newGroup()
local titleScreen = display.newImage("titleScreen.png")
local playButton = display.newImage("playButton.png",100,100)
local optionsButton = display.newImage("optionsButton.png",100,170)
introScreenGroup:insert(titleScreen)
introScreenGroup:insert(playButton)
introScreenGroup:insert(optionsButton)

Here we start the soundtrack, setup the intro screen, and add the playButton and optionsButton graphics.

Now call the setUpIntroScreen() function beneath where you are calling the setUpGameScreen() function..

setUpGameScreen()
setUpIntroScreen()

If you test now you should see the Intro Screen. We need to add Event Listeners to the buttons and that is what we'll do in the next steps.


7. Play Button Listener

Enter the following beneath the code you entered in the step above:

playButton:addEventListener("tap",function ()
    transition.to(introScreenGroup, {time = 300, x = -480, onComplete = function()
        groundHogTimer = timer.performWithDelay(groundHogSpeed, getRandomGroundHog,0)
     end
     })
    isPlaying = true
end
)

When the play button is pressed, we animate the intro screen off to the left, set our groundHogTimer to generate random groundhogs, and then set the isPlaying variable to true.

If you test now, you should be able to start a new game, but we would like some options to be available as well. We'll do that next.


8. Options Button Listener

Enter the following beneath the code you entered in the step Above.

optionsButton:addEventListener("tap", function()
    transition.to(optionsScreenGroup, {time = 300, y = 0, onComplete = function()
         introScreenGroup. x = -480
    end
    })
end
)

This code animates the option screen down from above. When the animation is complete, it puts the introScreenGroup off of the main screen to the left.

If you test now and press the options button you'll not see anything happen, and that is because we have not yet created the options screen.


9. setUpOptionsScreen()

Enter the following inside the setUpOptionsScreen() function:

optionsScreenGroup = display.newGroup()
    local optionsScreen = display.newImage("optionsScreen.png")
    local backButton = display.newImage("backButton.png",130,240)
    local soundOnText = display.newText( "Sound On/Off", 75,105, native.systemFontBold, 16 )
    local groundHogSpeedText = display.newText("Speed", 75,145,native.systemFontBold,16)
    optionsScreenGroup:insert(optionsScreen)
    optionsScreenGroup:insert(backButton)
    optionsScreenGroup:insert(soundOnText)
    optionsScreenGroup:insert(groundHogSpeedText)
    optionsScreenGroup.y = -325

Here we setup the optionsScreen, add the backButton, and add a couple of texts.

Now call this function beneath where you are calling setUpIntroScreen:

setUpIntroScreen()
setUpOptionsScreen()

If you test the game now you should see the options screen slide down from above.


10. soundCheckBox

We will be using the checkbox widget to allow the user to turn on/off the sound. Enter the following at the very top of the "main.lua" file.

local widget = require( "widget" )

To be able to use the Switch and other widgets we must first require the "widget" module

Now enter the following beneath the code you entered above in the setUpOptionsScreen.

local soundCheckBox = widget.newSwitch
{
   left = 210,
   top = 98,
   style = "checkbox",
   initialSwitchState = true,
   onPress = function(e)
   	local check = e.target
   	if(check.isOn) then
   		media.playSound("gameTrack.mp3",soundComplete)
   	else
   	  media.stopSound()
   	end
   end
}
optionsScreenGroup:insert(soundCheckBox)

This sets up our checkbox widget by setting "style" equal to "checkbox". We check if the checkbox isOn (if it is selected), and if so we play the "gameTrack.mp3". If not, we stop the sound.

If you test now and go to the options screen, you should be able to turn the sound on or off.


11. speedControl

We use a SegmentedControl to allow the user to choose how fast the groundHogs should appear. Enter the following beneath the code for the checkbox you entered in the step above:

local speedControl = widget.newSegmentedControl
{
   left = 210,
   top = 140,
   segments = { "slow", "medium", "fast"},
   segmentWidth = 50,
   defaultSegment = 1,
   onPress = function(event)
   	local target = event.target
        if(target.segmentNumber == 1)then
        	groundHogSpeed = 1500
        elseif (target.segmentNumber == 2)then
            groundHogSpeed = 1000
        else
        	groundHogSpeed = 700
        end
   end
}
optionsScreenGroup:insert(speedControl)

Here we create a SegmentedControl with 3 segments ("slow","medium","fast"). Depending on which segment the user has pressed we set the groundHogSpeed variable accordingly.

If you test now you should see the SegmentedControl and be able to select a segment, but we need to wire up the back button to get back to the intro screen.


12. backButton

The backButton takes us back to the intro screen. Enter the following code beneath the above step:

backButton:addEventListener("tap", function()
    if(isPlaying == false)then
        introScreenGroup.x = 0
     end
     transition.to(optionsScreenGroup, {time = 300, y = -325, onComplete = function()
         if(isPlaying == true) then
	     groundHogTimer = 	timer.performWithDelay(groundHogSpeed, getRandomGroundHog,0)
	  end
     end
     })
end
)

Here we check whether the game has started. If not, we put the introScreenGroup back into the playing area. We then transition the options screen back up. If the game has started, we set the groundHogtimer to generate random groundhogs.


13. setUpGameScreen() - Adding Options

We need to be able to get to the options screen from the main game screen. Add the following beneath where you are inserting the gameBackground into the gameScreenGroup().

gameScreenGroup:insert(gameBackground)
local optionsButton = display.newImage("optionsButton.png",1,270)
gameScreenGroup:insert(optionsButton)

If you test now you should see the options button on the game screen.

Now we need to wire up the options button with an EventListener. To do so, enter the following code beneath the loop that creates all the groundhogs.

optionsButton:addEventListener("tap", function(e)
    timer.cancel(groundHogTimer)
    transition.to(optionsScreenGroup, {time = 300, y = 0, onComplete = function()
    end
    })
end
)

Here we cancel the groundHogTimer and transition our options screen down.

Testing now, you should be able to get to the options screen from the game screen. You should also be able to set the options as desired.


14. soundComplete()

If you have played long enough for the soundtrack to finish, you may have noticed it did not start over. Enter the following within the soundComplete() function.

media.playSound( "gameTrack.mp3", soundComplete )

This just starts the soundtrack playing again once it completes.


Conclusion

In this series, we learned how to create a fun and interesting Whack-a-Groundhog game. In doing so, you've learned how to utilize sprite sheets and sound. I hope you found this tutorial useful, and thanks for reading!

Tuts+ Supports the MaRS Energy Hackathon!

$
0
0

Tuts+ is helping the world go green by supporting the MaRS Energy Hackathon! Read on to learn more about the event and how you can participate!


Event Overview

The MaRS Discovery District is calling on developers, designers, energy experts, and budding entrepreneurs to create apps and hardware that make use of the Green Button Connect My Data open API and other sample datasets. The goal? Your brainchild will help consumers better understand, access, and manage their electricity consumption. The Green Button standard has already been adopted across the United States, and is starting to take hold across Ontario as well. In fact, to date, it has allowed 2.6 million Ontarians to access and share their electricity data.


Event Details

Come to MaRS to build an app, create a gadget, and join the Green Button Revolution!

The event will run from 8:00 AM EDT to 5:00 PM EDT on both the 7th and the 8th of September, 2013. There is no cost for entry and by participating in the hackathon you will be competing for the following prizes:

  • Best App (value: $1,500)
  • Best App – First Runner-Up (value: $1,000)
  • Best App – Second Runner-Up (value: $500)
  • Chagpar & Associates-backed Prepr Innovation Grant (approximate value: $5,000)
  • Pivot Design Group Award for Best UX/Design (approximate value: $5,000)
  • 5 free Tuts+ Premium subscriptions (approximate value: $900)
  • 5 free accounts for Squarespace for 1 year (approximate value: $500)

Checkout the official hackathon page to learn more.


Participants & Sponsors

With great partners like Affinity Systems, London Hydro, Prepr, Upverter, Tuts+, Gelaskins, Pivot Design Group, Hackernest, and People & Code, as well as an amazing cross-section of industry experts, you’ll have everything you need to make an awesome app or gadget! They’ll also have two soldering irons, a soldering station, and a 3D printer care of the Toronto Tool Library!


Additional Resources

While the best place to learn about the event is the official hackathon website, you may also find the following links helpful:

Android SDK: Drawing with Opacity

$
0
0

This tutorial demonstrates how to allow users to draw with opacity values. While this post builds on related tutorials published on Mobiletuts+, you can dive straight into this lesson without completing the prior posts. Read on!

The Android platform provides the resources to create drawing functionality using touchscreen interaction. In a prior Mobiletuts+ series on Creating a Drawing App, we worked through the essential features of drawing interaction in Android, including selecting from a color palette and choosing brush sizes. In this tutorial, we will focus on how to enhance a drawing application by adding opacity into app drawing functions. You can complete this tutorial without having completed the related posts, but we will reference some of the prior material throughout.


Final Preview

Here is a preview of the opacity drawing functionality:

Drawing With Opacity

The source code download includes the standalone app we build in this tutorial as well as an enhanced version of the app we built during the drawing series. Here is a preview of it with the additional functionality:

Drawing With Opacity

1. Start Your Application

Step 1

As with the pattern drawing tutorial, we will be glossing over some of the details we explored in the previous drawing series so that we can focus on the opacity drawing functionality. In addition to using opacity levels, we will also be adding a control to the user interface so that the user can select their own opacity level for drawing.

If you completed the drawing app series, you can jump straight to part 2 step 1 now. If you are creating a new app for this tutorial, start a new Android project in Eclipse now. Choose 14 as your minimum API level and select other settings of your choice. When creating the app, you can let Eclipse create a blank Activity and layout.

Add a new class to your app, naming it “DrawingView”. Start with the same class content we used for the pattern drawing tutorial:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DrawingView extends View {
	//drawing path
	private Path drawPath;
	//drawing and canvas paint
	private Paint drawPaint, canvasPaint;
	//initial color
	private int paintColor = 0xFFFF0000;
	//canvas
	private Canvas drawCanvas;
	//canvas bitmap
	private Bitmap canvasBitmap;
	//constructor
	public DrawingView(Context context, AttributeSet attrs){
		super(context, attrs);
		setupDrawing();
	}
	//prepare drawing
	private void setupDrawing(){
		drawPath = new Path();
		drawPaint = new Paint();
		drawPaint.setColor(paintColor);
		drawPaint.setAntiAlias(true);
		drawPaint.setStrokeWidth(50);
		drawPaint.setStyle(Paint.Style.STROKE);
		drawPaint.setStrokeJoin(Paint.Join.ROUND);
		drawPaint.setStrokeCap(Paint.Cap.ROUND);
		canvasPaint = new Paint(Paint.DITHER_FLAG);
	}
	//view assigned size
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
		drawCanvas = new Canvas(canvasBitmap);
	}
	//draw view
	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
		canvas.drawPath(drawPath, drawPaint);
	}
	//respond to touch interaction
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float touchX = event.getX();
		float touchY = event.getY();
		//respond to down, move and up events
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			drawPath.moveTo(touchX, touchY);
			break;
		case MotionEvent.ACTION_MOVE:
			drawPath.lineTo(touchX, touchY);
			break;
		case MotionEvent.ACTION_UP:
			drawPath.lineTo(touchX, touchY);
			drawCanvas.drawPath(drawPath, drawPaint);
			drawPath.reset();
			break;
		default:
			return false;
		}
		//redraw
		invalidate();
		return true;
	}
}

The content of the class contains standard drawing app functionality – for more information on the details see the drawing app series.

Step 2

Let’s now include the new View in the app user interface along with the other controls. Open your layout file. Replace the content with the following layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:background="#FFCCCCCC"
	android:orientation="vertical"
	tools:context=".MainActivity" ></LinearLayout>

Inside the Linear Layout, first add an instance of the custom View, changing the package name to suit your own:

<com.example.opacitydraw.DrawingView
	android:id="@+id/drawing"
	android:layout_width="fill_parent"
	android:layout_height="0dp"
	android:layout_marginBottom="3dp"
	android:layout_marginLeft="5dp"
	android:layout_marginRight="5dp"
	android:layout_marginTop="3dp"
	android:layout_weight="1"
	android:background="#FFFFFFFF" />

We will be adding more to the UI in the next section.


2. Add Opacity Control

Step 1

In this section we will be adding a button for controlling the opacity level of our “brush”.

In your layout, after the custom View, add the button for an opacity control:

<ImageButton
	android:id="@+id/opacity_btn"
	android:layout_width="wrap_content"
	android:layout_height="fill_parent"
	android:layout_gravity="center"
	android:contentDescription="opacity"
	android:src="@drawable/opacity" />

The button will launch a control through which the user will be able to set the opacity level for drawing. We will use the ID to refer to the button in the Activity code.

Tip: If you are enhancing the drawing series app, add this button in the top section of your layout file alongside the other buttons, using the same layout properties you used for them.

Step 2

You will notice that the Image Button refers to a drawable file. Let’s create this now. Add a new file to your app’s drawables folder(s) and name it “opacity.xml” to match the source attribute we added to the Image Button XML element. Include the following content:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:dither="true"
	android:shape="oval" ><size
		android:height="20dp"
		android:width="20dp" /><gradient
		android:endColor="#00000000"
		android:startColor="#FF000000"
		android:type="linear" /><stroke
		android:width="1dp"
		android:color="#66000000" /></shape>

The shape is a circle with full opacity at one side and full transparency at the other, with a gradient fill between them.

Tip: If you’re working on the drawing series app, you can use the dimension values for the medium size rather than hard-coding a dimension value in your shape drawable.

Step 3

Now let’s design the little pop-up control we want to appear when the user presses the opacity button. Add a new file to your app’s layout folder, naming it “opacity_chooser.xml”. Enter the following layout outline:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:gravity="center"
	android:orientation="vertical" ></LinearLayout>

Inside the Linear Layout, first add some informative text:

<TextView
	android:id="@+id/opq_txt"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:layout_marginTop="5dp"
	android:gravity="center"
	android:text="100%"
	android:textStyle="bold" />

We will update the text as the user changes the level, starting at 100% for full opacity initially. We will also use the ID in Java. To set the opacity level, the logical Android UI element is a Seek Bar. With a Seek Bar, the user interacts with a slider control, which we will set to have 0% opacity at the left and 100% on the right. Add the Seek Bar after the Text View:

<SeekBar
	android:id="@+id/opacity_seek"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:layout_margin="5dp" />

We will also be referring to this in Java. Finally, add an OK button:

<Button
	android:id="@+id/opq_ok"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="OK" />

When the user clicks OK, we will implement their chosen opacity level for subsequent drawing operations. We will also retain the chosen opacity level if the user launches the control again.

Opacity Level

3. Implement Opacity Changes

Step 1

Tip: You can skip to step 2 now if you are enhancing the app from the drawing series. Add the remaining code to your main and custom drawing View classes.

Open your main Activity class. add the following import statements:

import android.app.Dialog;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

Extend the opening line of the class declaration to implement click listening:

public class MainActivity extends Activity implements OnClickListener

Add an onClick method to your class:

@Override
public void onClick(View view){
//respond to clicks
}

We will be adding a method to the custom View class to set the opacity level for drawing operations. So that we can do this, add an instance variable to the class, before onCreate:

private DrawingView drawView;

In onCreate, get a reference to the instance of the custom View class you have in your app layout:

drawView = (DrawingView)findViewById(R.id.drawing);

We will be able to call methods on this object to control what happens during drawing.

Step 2

Before the onCreate method in your main Activity, add an instance variable for the opacity button:

private ImageButton opacityBtn;

In onCreate, retrieve a reference to the button:

opacityBtn = (ImageButton)findViewById(R.id.opacity_btn);

Listen for clicks on the button:

opacityBtn.setOnClickListener(this);

In your onClick method, add a conditional test for the opacity button (use an else if if you’re enhancing the series app):

if(view.getId()==R.id.opacity_btn){
	//launch opacity chooser
}

Step 3

Now we can launch a chooser control for setting the opacity using the layout we defined. Inside the conditional block for the opacity button in onClick, create a Dialog object, setting the title and layout for it:

final Dialog seekDialog = new Dialog(this);
seekDialog.setTitle("Opacity level:");
seekDialog.setContentView(R.layout.opacity_chooser);

Retrieve references to the Text View and Seek Bar we included in the layout:

final TextView seekTxt = (TextView)seekDialog.findViewById(R.id.opq_txt);
final SeekBar seekOpq = (SeekBar)seekDialog.findViewById(R.id.opacity_seek);

Set the maximum on the Seek Bar:

seekOpq.setMax(100);

We use 100 as the maximum possible alpha level will be 100%.

Step 4

Before we continue with the opacity control, let’s add some required functionality to the custom drawing View class. Start with a new instance variable to store the current opacity level:

private int paintAlpha = 255;

The Paint method we will be using to set the opacity expects a value between 0 and 255. Add a simple get method for the value:

public int getPaintAlpha(){
	return Math.round((float)paintAlpha/255*100);
}

The level is stored as a value between 0 and 255, but we want to display it as a percentage, so we return a value between 0 and 100. Next, add a method to set the value:

public void setPaintAlpha(int newAlpha){
	paintAlpha=Math.round((float)newAlpha/100*255);
	drawPaint.setColor(paintColor);
	drawPaint.setAlpha(paintAlpha);
}

In the above, we parse the percentage value, set the color, and set the alpha.

Step 5

Back in your main Activity class, in onClick after setting the maximum on the Seek Bar control, first retrieve the existing opacity level:

int currLevel = drawView.getPaintAlpha();

Show this in the Text View and Seek Bar display:

seekTxt.setText(currLevel+"%");
seekOpq.setProgress(currLevel);

Now we want the display to update as the user slides the control up and down. Add the following event listening code:

seekOpq.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
	@Override
	public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
		seekTxt.setText(Integer.toString(progress)+"%");
	}
	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {}
	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {}
});

We only need to respond to the progress changed event, in which case we update the Text View – this will let the user see their chosen level as a numeric value. Now we need to listen for the user clicking the OK button. After the Seek Bar change listener code block, retrieve an OK button reference:

Button opqBtn = (Button)seekDialog.findViewById(R.id.opq_ok);

Now handle clicks on it:

opqBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setPaintAlpha(seekOpq.getProgress());
		seekDialog.dismiss();
	}
});

When the button is clicked, we call the new method we added to change the opacity level, passing the level chosen by the user, and then dismiss the Dialog. Complete the opacity button section of onClick by displaying the Dialog:

seekDialog.show();

Tip: If you’re extending the series app with opacity control, you can either assume that the user wants to use full opacity when they choose a new color, or that they want to retain their chosen opacity level. To reset to 100%, you can call the setPaintAlpha method in the paintClicked method. To retain the chosen alpha level, you can add code to the setColor method to reapply the chosen alpha level after setting the new color.


Conclusion

This completes the opacity drawing functionality! You should be able to run your app, set an opacity level, then see the results in your drawing operations. If you were working on the series app and enhanced it with the pattern fills, notice that the opacity control also applies to drawing with patterns. We have now explored the various typical types of processing in drawing apps, but there are still many more you could try out in your own apps. In a forthcoming tutorial, we will look at how you can accommodate users who are not interacting via touch screens!

Creating Titanium Mobile Modules Using CommonJS

$
0
0

In this article we’ll be discussing how to create your own mobile modules for Appcelerator’s Titanium using just JavaScript and a bit of CommonJS knowledge. The module we’ll be creating will be one for Picasa, Google’s image service, and will allow our users to view albums and photos from Picasa without knowing any details about the Picasa API itself. Once we’ve tested our module, we will also run through packaging it for both iOS and Android!


Why Create a Module Using CommonJS?

Why package into a module at all? The code we’re creating is JavaScript after all. We could just copy and paste our code files into any Appcelerator Titanium project in order to utilize them. There’s a number of very good reasons to do this as a module, however.

  • Your modules can be shared and treated as a “black-box” component, just like any other native module can be.
  • Your modules are compiled, meaning you can protect your code should you need to.
  • Modules are easy to maintain and grow across your projects, allowing you to more easily reuse your code.

Now that we have the “why” out of the way, let’s move onto the “how” and create our module!


Setting Up Your Module Project

The easiest way to set up a new mobile module project is via Titanium Studio, so that’s what we’re going to do. Open Titanium Studio and from the File menu, select New > Mobile Module Project. You’ll be presented with a screen like the one below. I’m calling our module “Picasa” and giving it a module ID of “com.boydlee.picasa“. Make a note of what your module ID is should you decide to use a different identifier. This will be very important when we create our JavaScript files later on!

Creating a new Mobile Module Project in Titanium Studio

You’ll probably note at this stage that you can only create your module for iOS OR Android. This is because modules are compiled separately for each platform. I’m going to select and use iOS for our module right now. It’s much quicker to build and test than Android is. We’ll build our Android module using the same code at the end of the tutorial. Hit the Finish button and Titanium Studio should create your module project and display it in the App Explorer pane. Let’s start coding!


Creating Your Module’s JavaScript Entry File

The first thing we need to do is create the JavaScript entry file for our module, which will go into the /assets directory. It must be named in a particular way, which needs to be: your.module.id.js. As I have named my module com.boydlee.picasa, the JavaScript file I need to create needs to be called com.boydlee.picasa.js. Your App Explorer pane should then look something like the photo.

The App Explorer Pane in Titanium Studio

Titanium automatically looks for the entry point file when it loads your module, and that entry point file must have the same name as your module’s identifier. Let’s start building our CommonJS module, starting with the shell code, which will create our Picasa function, add an “Access Levels” object to it for convenience using prototype, and then export the function via CommonJS.

/*
* Our Picasa CommonJS Module
*/
function Picasa(){
   this._username = '';
   this._accessLevel = 'public';
};
Picasa.prototype.ACCESS_LEVELS = {
    PUBLIC: 'public',
    PRIVATE: 'private'
};
//finally, export the module
//you MUST export it in this manner, not using methods.export = !
exports.Picasa = Picasa;

Building Out the Picasa Functionality

Now that we have our basic module shell done, it’s time to fill it out with some functions using prototype. We’re going to keep this module fairly simple, so it’s only going to have two main objectives: search for and return a list of the user’s albums, and return a list of all photographs for an album. First, let’s create a few public getters and setters so we can set the username and access levels.

/*
Getters and setters for our private functions
 */
Picasa.prototype.setUsername = function(value){
    this._username = value;
};
Picasa.prototype.getUsername = function(){
    return this._username;
};
Picasa.prototype.setAccessLevel = function(value){
    this._accessLevel = value;
};
Picasa.prototype.getAccessLevel = function(){
    return this._accessLevel;
};

We’re also going to need a function that can perform HTTP requests. Rather than creating multiple requests for both albums and photo calls to Picasa, we can create one request function that’s reusable. Let’s do that now.

/*
 * Our xhr function accepts a URL and 2 callback functions
 */
Picasa.prototype.xhr = function(url, success, error){
    var client = Ti.Network.createHTTPClient({
         // function called when the response data is available
         onload : function(e) {
             Ti.API.info("Received JSON Text: " + this.responseText);
             var json = JSON.parse(this.responseText);
             success(json);
         },
         // function called when an error occurs, including a timeout
         onerror : function(e) {
             Ti.API.debug(e.error);
             error(e.error);
         },
         timeout : 5000 //in milliseconds
     });
     client.open("GET", url); // Prepare the connection.
     client.send();  // Send the request.
};

Let’s expand our Picasa function with some functions that will take the username and access level and return back valid URLs for the Picasa photo service. We’ll use these URLs later on when we build our HttpRequests.

function Picasa(){
    this._username = '';
    this._accessLevel = 'public';
    var _this = this;
    this.albumnsEndpoint = function(username, accessLevel){
       username = (username === undefined) ? _this._username : username;
       accessLevel = (accessLevel === undefined) ? _this._accessLevel : accessLevel;
       return 'https://picasaweb.google.com/data/feed/api/user/' + username + '?kind=album&access=' + accessLevel + '&alt=json';
    };
    this.albumPhotosEndpoint = function(albumId, username, accessLevel){
       if(albumId === undefined){
           Ti.API.error('This method requires an album ID!');
           return;
       }
       username = (username === undefined) ? _this._username : username;
       accessLevel = (accessLevel === undefined) ? _this._accessLevel : accessLevel;
       return 'https://picasaweb.google.com/data/entry/api/user/' + username + '/albumid/' + albumId + '?alt=json';
    };
};

Accessing Album Data via Picasa

Now that the basic structure of our module is there, we can start building against the Picasa API and adding real functionality and usefulness to our module. The first thing we’ll do is create a function that will allow our user to fetch back a list of their Picasa albums. The JSON information Picasa returns for this call is extremely complex, so we’ll also be simplifying it into a nice neat array of objects that you can easily understand at first glance. Go ahead and create the following function in your CommonJS module.

/*
 * This function will fetch the albums url, parse the JSON response and simplify before
 * passing it to our callback function
 */
Picasa.prototype.getAlbums = function(callback){
    if(this._username !== undefined){
        var albumsUrl = this.albumnsEndpoint(this._username);
        this.xhr(
            albumsUrl,
            function(response){
               var albums = [];
               for(var i = 0; i < response.feed.entry.length; i++){
                   var album = {
                        title: response.feed.entry[i].title.$t,
                        thumbnail: response.feed.entry[i].media$group.media$thumbnail[0].url,
                        thumbnailWidth: response.feed.entry[i].media$group.media$thumbnail[0].width,
                        url: response.feed.entry[i].link[0].href
                   };
                   albums.push(album);
               }
               callback(albums);
            },
            function(failure){
               callback(failure);
        });
    };
};

Accessing Photo Data via Album Links

Now that we’re able to access the album data, we need to use Picasa’s photo endpoints to retrieve a list of a photos for a particular album. This can be done two ways. You can use the album ID and build up a Photos URL endpoint, or you can simply use the URL that is returned back in the album’s HTTP Request. We’ll create both functions just for the sake of usefulness, and a third function called createPhotosArray that will take in a JSON response object and return back a simplified array of images. In your CommonJS module, create the following functions.

/*
 * Takes in a photos URL response JSON object and just returns
 * the important information (an array photo objects)
 */
Picasa.prototype.createPhotosArray = function(response){
    var photos = [];
    for(var i = 0; i < response.feed.entry.length; i++){
        var photo = {
           title: response.feed.entry[i].title.$t,
           url: response.feed.entry[i].content.src
        };
        photos.push(photo);
    }
    return photos;
};
/*
 *
 */
Picasa.prototype.getPhotosByUrl = function(url, callback){
     var _this = this;
    this.xhr(
       url,
       function(response){
          callback(_this.createPhotosArray(response));
       },
       function(failure){
          callback(failure);
    });
};
/*
 *
 */
Picasa.prototype.getPhotosByAlbumId = function(albumId, callback){
    var _this = this;
    if(this._username !== undefined && albumId !== undefined){
        var photosUrl = this.albumPhotosEndpoint(albumId, this._username);
        this.xhr(
            photosUrl,
            function(response){
                callback(_this.createPhotosArray(response));
            },
            function(failure){
               callback(failure);
        });
    }
};

That’s about it as far as our CommonJS module goes! We are now able to set our username and access levels via public property functions, fetch back a list of albums and use that information to then fetch back a corresponding list of photos for each Album ID/Album URL. On to the next section, where we’ll talk about packaging our module for use in a real Titanium Mobile application!


Packaging and Testing Your New Module

Packaging your module couldn’t be simpler when using Titanium Studio. First, click on the “Package” icon within the App Explorer pane. The Package icon looks like a closed box. Then, click on the submenu option “Package – iOS Module“. A new popup window will appear that looks like the one below, with three different packaging options.

  • Titanium SDK– This will package your module and install it directly to your Titanium SDK’s “Modules” directory. On a Mac, this is located with the ~/Application Support/Titanium folder.
  • Mobile Project– This will package your module and install it directly to an app that is currently within your Project Explorer pane. If you want to test a module that you’re working on, this is generally the easiest way.
  • Location– This will package your module and save the resulting ZIP file to a folder you specify. This is the best method if you wish to share your module or potentially upload it to the Appcelerator Marketplace.
Titanium Mobile Module Packaging Options

We’re going to go ahead and choose the first option, create the package, and store it in our Titanium SDK folder. Select that and press “Finish“. Now just sit back and wait for a minute. Titanium Studio will build your new module and when it’s completed you’ll see a yellow notification message appear in the bottom right of the screen. Success!

Module Package Success!

Now that our module is packaged we should probably test it out, right? Let’s create a new mobile project. Choose Titanium Classic from the templates menu, and then Default Project. We are going to create our example project in very simple, “classic” Titanium code. This is because once it’s working, we’ll want to copy our test code into the example.js file of our module for other people to use as a reference. I’m calling my test app Picasa-TestApp with an app ID of com.boydlee.picasatestapp, but you can call yours whatever you like.

Creating a New Titanium Mobile “Classic” Project

This basic template consists of a TabGroup and two windows, all defined within your app.js file. We’re going to simplify the code so that we just have the one tab and the one window. We’ll add a TableView which we’ll populate with our albums data, but before we do, we need to add our new module to our test project. Open the tiapp.xml file, click the “+” button next to the list of app modules, and then choose the Picasa module we packaged up in the previous section.

Adding the Picasa Module to our Test Project

Now let’s add the test code into app.js, which will return a list of albums to our user and show them in a TableView. We’ll also do a require and create a new Picasa object via our module.

//require our Module and create a new instance of it
var PicasaModule = require('com.boydlee.picasa');
var picasa = new PicasaModule.Picasa();
//set the username
picasa.setUsername('boydlee');
//set the access level using our public 'CONSTANTS' object
picasa.setAccessLevel(picasa.ACCESS_LEVELS.PUBLIC);
// this sets the background color of the master UIView (when there are no windows/tab groups on it)
Titanium.UI.setBackgroundColor('#000');
// create tab group
var tabGroup = Titanium.UI.createTabGroup();
//
// create base UI tab and root window
//
var win = Titanium.UI.createWindow({
    title:'Picasa Albums',
    backgroundColor:'#000'
});
var tab1 = Titanium.UI.createTab({
    icon:'KS_nav_views.png',
    title:'Albums',
    window: win
});
//get the albums for this user and access level
picasa.getAlbums(function(response){
   //openAlbumPhotosView(response.feed.entry[0].gphoto$name.$t, response.feed.entry[0].link[0].href);
   var table = Ti.UI.createTableView({
       width: Ti.UI.FILL,
       height: Ti.UI.FILL,
       top: 0,
       left: 0
   });
   table.addEventListener('click', function(e){
       openAlbumPhotosView(e.row.data.title, e.row.data.url);
   });
   var rows = [];
   for(var i = 0; i < response.length; i++){
       var img = Ti.UI.createImageView({
           width: 60,
           height: 60,
           highres: true,
           image: response[i].thumbnail,
           left: 5,
           top: 5
       });
       var label = Ti.UI.createLabel({
           text: response[i].title,
           height: 60,
           font: {
               fontSize: 20,
               fontWeight: 'bold'
           },
           top: 5,
           left: 80,
           width: Ti.UI.SIZE
       });
       var row = Ti.UI.createTableViewRow({
           className: 'albumRow',
           height: 70,
           data: response[i]
       });
       row.add(img);
       row.add(label);
       rows.push(row);
   }
   table.setData(rows);
   win.add(table);
});
//  add tab
tabGroup.addTab(tab1);
// open tab group
tabGroup.open();

Note that I’ve set the access to public and the username to boydlee, which will access the photos on my Picasa account. Once that’s done, try launching your app in the simulator.

Our default window with a list of Picasa Albums showing in a TableView

Finally, we need to add a function called openAlbumPhotosView, which is going to accept an album title and URL, open a new window in the current tab, and then pull back and display all the photos for that album in a ScrollableView.

function openAlbumPhotosView(title, url){
    Ti.API.info('Getting photos for album: ' + title);
    var detailsWin = Ti.UI.createWindow({
        title: title,
        backgroundColor: '#000',
        height: Ti.UI.FILL,
        width: Ti.UI.FILL
    });
    picasa.getPhotosByUrl(url, function(response){
        Ti.API.info(response);
        var imageViews = [];
        for(var i = 0; i < response.length; i++){
            var img = Ti.UI.createImageView({
                image: response[i].url,
                title: response[i].title
            });
            imageViews.push(img);
        }
        var scrollableView = Ti.UI.createScrollableView({
           height: Ti.UI.FILL,
           width: Ti.UI.FILL ,
           views: imageViews
        });
        detailsWin.add(scrollableView);
    });
    tab1.open(detailsWin);
}

Run your code in the simulator for one last time. You should now be able to fetch back a list of albums, select one from the TableView, and then view a Scrollable slide show of the photos for that album. Neat!

ScrollableView showing all the photos from our selected Picasa Album.

Packaging The Picasa Module

All that’s left to do now is package the Picasa module for iOS and Android. First, copy all the code from your test app.js file and paste it into the module project’s /example/app.js. We want this code as an example for other people that want to use our module. Once that’s done, just hit the Package button and you can choose to export your module via a Location, just like we explained back in the Packaging & Testing Your New Module section. For Android, the process is the same, but we have to create a separate module project for that by creating a new Mobile Module project for Android this time around. Copy and paste the example/app.js and com.boydlee.picasa.js code files we already created into the right locations in your Android mobile module project. You can then build and distribute for Android just like we did in iOS!

Tip: You may need to install JDT before you’re able to create modules for Android. An easy step by step guide is available at appcelerator.com


Conclusion

Hopefully you’ve found this tutorial useful. The next time you consider building out Titanium functionality that you wish to share, or use amongst multiple projects, you’ll probably consider wrapping it up into a CommonJS module!

Viewing all 1836 articles
Browse latest View live