суббота, 10 марта 2012 г.

Пишем игру под Android: Часть 7: Меню для игры и окно приветствия

Базовые навыки по написанию игры уже у Вас надеюсь есть :), я постарался объяснить все как можно подробно, теперь нам нужно облагородить нашу игру добавив в неё красивое меню и красивое окно приветствия загрузки приложения. Ничего трудного в этом нет все делается быстро и безболезненно. Найти ссылки по туторилам, если Вы не писали игру можно ниже:


  1. Пишем игру под Android: Часть 1 — Рисуем картинки на SurfaceView
  2. Пишем игру под Android: Часть 2 — Создаем первый спрайт
  3. Пишем игру под Android: Часть 3 — Спрайтовая анимация, работа с несколькими спрайтами
  4. Пишем игру под Android: Часть 4 — onTouchEvent и определение столкновений
  5. Пишем игру под Android: Часть 5 — Создание полноценной 2D игры
  6. Пишем игру под Android: Часть 6: Добавление звука
  7. Пишем игру под Android: Часть 7: Меню для игры и окно приветствия
  8. Пишем игру под Android: Часть 8: Фоновая музыка в игре

Создание меню

В данной статье мы будем использоваться уже ранее наработаны навыки, ведь мы уже очень умные программисты по работе с Android, нам уже ничего не страшно? И так что нам нужно для начала сделать, для самого начала нам нужно создать layout на котором будет наше красивейшее меню, кнопочки и текствьюхи которые будут рассказывать о красоте нашего приложения и остальном. 

И так… 

Открываем наш проект надеюсь он есть у всех, открываем папку res/layout кликаем правой кнопкой по ней и выбираем раздел New — Android Xml File, в папочке возник файл который мы теперь запускаем и добавляем желаемое количество кнопок и текствьюх и соответственно пишем то что хотим в них видеть.

Сейчас будет немного теории: обычно, при запуске игрушки пользователь сначала видит экран с различными настройками. Необходимо подготовить его перед погружением в игру. Поэтому первое впечатление очень важно. Скучные стандартные кнопки с скучными стандартными шрифтами вряд ли будут хорошим решением. 

Все, теория кончилась, переходим к практике. В созданном и открытом нами xml файле если Вы уже накидали кнопок и текствьюх можете пропустить пару абзацев, если же не накидали то Вам нужно читать дальше — берем и пишем в наш файл следующую разметку:

Some main.xml file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#fff" >
    
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="36dp"
        android:text="Some Game Name"
        android:textSize="50dp" 
        android:textColor="#000"/>

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="180dp"
        android:text="Start"
        android:textSize="30dp" 
        android:textColor="#000"
        android:background="#fff"/>
    
     <Button
         android:id="@+id/button2"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="230dp"
        android:text="Exit"
        android:textSize="30dp" 
        android:textColor="#000"
        android:background="#fff"/>
    </RelativeLayout>


Эта форма будет выглядеть таким образом:


Как видно, она очень простая, если хотите усложнить то можно добавить фоновую картинку и много красивых кнопок, я не заморачивался и по этому у меня она простая. Вы так же можете добавить сюрфейс на задний план что бы на заднем плане все двигалось и жило своей жизнью, но это уже Ваше дело. 

После этого нам нужно сделать так что бы по нажатию на кнопку у нас обрабатывалось нажатие и переход на другую форму это сделать то же не трудно. Если у Вас входом в игру является файл Main.java то оставляем его, пусть живет спокойной жизнью. Создаем еще один файл пусть он будем называться StartActivity.java, он будет принимать все на себя, а Main.java уйдет на второй план, он у нас будет проигрывать сам сюрфейс. 

И так, создали файл и заполним его.

StartActivity.java
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
public class StartActivity extends Activity implements OnClickListener {          
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // если хотим, чтобы приложение постоянно имело портретную ориентацию
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        // если хотим, чтобы приложение было полноэкранным
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // и без заголовка
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        setContentView(R.layout.start);
        //setContentView(new GameView(this,null));
        
        Button startButton = (Button)findViewById(R.id.button1);
        startButton.setOnClickListener(this);
        
        Button exitButton = (Button)findViewById(R.id.button2);
        exitButton.setOnClickListener(this);
    }

    /** Обработка нажатия кнопок */
    public void onClick(View v) {
                switch (v.getId()) {
                        //переход на сюрфейс
                        case R.id.button1: {
                            Intent intent = new Intent();
                            intent.setClass(this, Main.class);
                            startActivity(intent);
                        }break;
                        
                         //выход
                        case R.id.button2: {
                             finish();
                        }break;
                        
                        default:
                                break;
                }
        }
}


Теперь по нажатию на кнопку Start мы будем переходить в нашу игру, а по нажатию на Exit соответственно выходить из программы. Код элементарный и пояснения надеюсь не требует.

Дальше нам нужно сказать Eclipse и Android SDK что запускать первым нужно не Main.java файл а StartActivity.java. Для этого открываем AndroidManifest.xml и находим вот такую строчку:

AndroidManifest.xml
<activity android:name=".Main" android:configChanges="keyboardHidden|orientation" >
                        <intent-filter>
                                <action android:name="android.intent.action.MAIN" />
                                <category android:name="android.intent.category.LAUNCHER" />
                        </intent-filter>
</activity>


И заменяем Main на StartActivity. Запускаем приложение и видим что запустилось первым у нас не сюрфейс, а меню к игре.

Создание Splash картинки

В моем понятии Splash картинка это картинка которая появляется на пару секунд после запуска приложения, такое как бы окно приветствия на котором написано кто разработчик или например что идет загрузка меню игры. Вот сейчас мы такое и сделаем, очень я скажу Вам полезная штуковина! 

Для начала нам нужно добавить в ту же разметку в main.xml одну штуковину которая называется ImageView. Я весь код снова писать не буду, скажу только то что его нужно добавить в самый конец нашего файла что бы он был поверх всех наших вьюшек.

В самом низу main.xml
<ImageView
                android:id="@+id/splashscreen"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:src="@drawable/splash"
                android:layout_gravity="center"/>


Вот так выглядить наш имеджвью. Картинку рисуем любого размера, но желательно что бы она была на весь экран смартфона/планшета так как если будет маленькая то будет плохое качество, картинку растянет под размер, и сразу отпугнет наших пользователей.

Дальше нам нужно прописать «мозг» всего нашего splash экрана. Для этого в том же файле StartActivity.java нам нужно добавить некоторые строчки. Смотрим ниже какие:

StartActivity.ava
private static final int STOPSPLASH = 0;
          private static final long SPLASHTIME = 10000; //Время показа Splash картинки 10 секунд
          private ImageView splash;
          
          private Handler splashHandler = new Handler() { //создаем новый хэндлер
                   public void handleMessage(Message msg) {
                       switch (msg.what) {
                       case STOPSPLASH:
                           //убираем Splash картинку - меняем видимость
                           splash.setVisibility(View.GONE);
                           break;
                       }
                       super.handleMessage(msg);
                   }
                };


И лобавить в onCreate() несколько строк которые будут запускать нашу splash картинку:

public void onCreate(Bundle savedInstanceState) {
        //...
        splash = (ImageView) findViewById(R.id.splashscreen); //получаем индентификатор ImageView с Splash картинкой
        Message msg = new Message();
        msg.what = STOPSPLASH;
        splashHandler.sendMessageDelayed(msg, SPLASHTIME);
    }


Все, теперь запускаем наш проект и увидим нашу картинку и красивое меню.

26 комментариев:

  1. при попытке запустить mainActivity (helo word) при нажатии на кнопку или даже GameView вылетает

    ОтветитьУдалить
    Ответы
    1. Что-то я не уловил мысли, что вылетает, что в геймВью?

      Удалить
  2. Глеб,ты молодец,я могу тебе прислать на почту мое меню,которое я делал.Может ты найдешь что-нибудь интересное!

    ОтветитьУдалить
  3. Здравствуйте! Подскажите как вызвать Activity из сюрфейса? Ведь вот так не вызовешь
    intent.setClass(this, Main.class);
    startActivity(intent);

    ОтветитьУдалить
    Ответы
    1. Все просто, через контекст нужно указать куда переходить, и откуда. Таким образом:

      Context context = getContext();
      Intent intent = new Intent(context, FiledActivity.class);
      context.startActivity(intent);
      ((Activity) context).finish();

      Удалить
    2. Спасибо! Но приложение вылетает когда пытается запустить Activity. Думаю это из-за потока в котором выполняется сюрфейс. Наверно его как-то надо закрыть, а потом запускать активити.
      Проблема у меня вобщем такая - Нажимаю из главного меню баттон Старт, запускается сюрфейс. Потом там играешься, набирая очки, а потом я хочу при проигрыше их записать в таблицу рекордов(т.е. надо включить новый Активити, закрыть сюрфейс-поток, и записать из него очки в новом Активити)!
      Заранее благодарю! Надеюсь вы, что-нибудь поняли из моего бреда)))

      Удалить
    3. Ну как я понял нужно передавать данные с сюрфейса на активность и там записывать в файл или базу. Это все делается именно тем интентом который я написал, только там добавляются еще строки передачи параметров набранных очков. Убивать сюрфейс не обязательно, но это желательное требование для этого тебе нужно доавить в surfaceCreated() проверку, если мы закрываем сюрфейс тогда делаем surfaceHolder.TERMINATED я сталкивался с этой проблемой, и решил её как найду код который я тогда написал я напишу.

      А вообще если ошибка при переходу удали строку ((Activity) context).finish(); возможно ошибка из за этого...

      Удалить
    4. Все наконец допер. Забыл в манифесте зарегать новый Активити.
      Спасибо еще раз!

      Удалить
  4. Спасибо! Буду пробовать.

    ОтветитьУдалить
  5. Ни че,что все в одной активити сделано?)

    ОтветитьУдалить
  6. Привет Глеб!
    У меня 2 вопроса, как реализовать меню паузы к такой игре?
    И почему при вызове активити или класса в меню кнопкой,приложение падает?
    Допустим вот так:
    Intent i = new Intent(this, SettingsActivity.class);
    startActivity(i);

    ОтветитьУдалить
    Ответы
    1. По поводу интернов отвечал на три комментария выше.

      А про паузу могу сказать вот такое:
      Для начала нам нужно наш surfaceView нужн овызывать не из класса, а из xml как я это делал в первой статье про создание игры. Дальше вносим кнопку поверх нашего геймВью и делаем метод который реагирует на клик по этой кнопке.

      Заводим переменную private boolean mPaused;

      и когда кликаем по этой кнопке в методе который мы создали пишем:

      mPaused = !mPaused;

      а в методе run() пишем такое:

      if (mPaused) continue; сразу после while(mRun) { //тут }

      Удалить
  7. А splash картинку куда складывать и в каком она должна быть формате?

    ОтветитьУдалить
    Ответы
    1. обычная png картинка, должна лежать в папке drawable

      Удалить
  8. Здравствуйте! Возможно вопрос совсем наивный, но сообразить не могу. В файле StartActivity.java строка setContentView(R.layout.start); написано start а меню добавляли в файле main.xml. И как в итоге выглядит файл AndroidManifest.xml? Вопрсы вызваны тем что клик по кнопке Start вызывает ошибку.

    ОтветитьУдалить
  9. setContentView(R.layout.start);
    выдаёт ошибку

    ОтветитьУдалить
    Ответы
    1. start измени на имя xml файла, в котором у тебя меню. (по туториалу это main.xml)

      Удалить
  10. ты бы меню покрасивее нарисовал, с анимацией желательно, музыкой. А то выглядит как дешовка. Абы быстрее запостить

    ОтветитьУдалить
    Ответы
    1. Какая разница какое меню в примере? Вы должны сами придумать и сделать меню по своему, мое дело только рассказать как это сделать, а дальше человек который читает эту статью включает фантазию и делает все под себя.

      Удалить
    2. включил, нарисовал в шопе. как сделать?

      Удалить
    3. по тутору который выше

      Удалить
  11. Как сделать портретную ориентацию в самом сюрфейсе?

    ОтветитьУдалить