- PVSM.RU - https://www.pvsm.ru -
Эта маленькая статья для новичков в мире Android, которые решили создать свою первую игру, но столкнулись с определенными трудностями или не знают с чего начать. Это не урок как создать игру от А до Я. Я просто рассмотрю разные аспекты создания игры в форме «вопрос — ответ».
1. Первые шаги
2. Создание игрового цикла
3. Как растянуть карту на весь экран
4. Проверка на столкновение или Hit Testing
5. Внедряем оплату PayPal
Вы создали свой новый проект и первое окно (Activity) в игре. Наверняка тут располагаются кнопки меню (начать игру, настройки и т.д.).
Пример:
public class FirstPage extends Activity implements OnClickListener {
private Button new_game_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Убираем верхнюю панель с названием приложения
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Переходим в режим FULLSCREEN
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Экран будет всегда активен
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.main);
// Инициализируем кнопку для запуска игры
new_game_btn = (Button) findViewById(R.id.new_game_btn);
}
// Обработчик события нажатия на кнопку
public void onClick(View v) {
if (v.getId() == new_game_btn.getId()) {
Intent intent = new Intent(FirstPage.this, Main.class);
startActivity(intent);
}
}
}
Создадим новый класс, который и будет сердцем нашей игры. Назовем его GameView.
public class GameView extends SurfaceView {
private Bitmap bmp;
private SurfaceHolder holder;
public GameView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas(null);
onDraw(c);
holder.unlockCanvasAndPost(c);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bmp, 10, 10, null);
}
}
Если Вы не забыли, то когда пользователь нажимает на кнопку «Начать игру», у нас выполняется этот код:
Intent intent = new Intent(FirstPage.this, Main.class);
startActivity(intent);
Создаем новый класс:
public class Main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Не забываем убрать Title и перейти в Fullscreen
setContentView(new GameView(this));
}
}
Каждый раз, когда создаем Activity, не забывает прописать ее в AndroidManifest.xml.
Пример:
<activity android:name=".Main" android:noHistory="true" android:label="@string/app_name" android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden" ></activity>
Если Вы хотите изменить Activity которая запускается по умолчанию при старте приложения, это делается так:
<activity
android:name=".pages.FirstPage"
android:label="@string/app_name" android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Если Вы все сделали правильно, то запустив приложение, увидим примерно такую картину:
Теперь нам нужно создать игровой цикл или так называемый game loop. Его нужно запускать в отдельном потоке. Создадим новый класс:
public class GameLoopThread extends Thread {
private GameView view;
private boolean running = false;
public GameLoopThread(GameView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
@Override
public void run() {
while (running) {
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
}
Далее нужно запустить наш поток из SurfaceView.
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
Теперь наш отдельный поток будет вызывать метод onDraw, в котором мы можем создавать анимацию. Но скорость анимации будет не постоянной. Чтобы это исправить, нам нужно определить скорость игры, задав число FPS, «frames per second». Сколько фреймов в секунду мы хотим показывать. Чтобы установить скорость потока 10 FPS, внесем изменения в GameLoopThread.
static final long FPS = 10;
@Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
Допустим у Вас есть карта (изображение) и Вы хотите, чтобы она занимала всю площадь экрана, не зависимо от размеров устройства.
Добавим новые переменные в GameView.
public static Bitmap _map; // game map
public static double _ratioWidth; // ratio for scale map
public static double _ratioHeight; // ratio for scale map
public static float _scale;
Инициализируем карту:
public void initMap() {
if (_map == null) {
_scale = getResources().getDisplayMetrics().density;
_map = BitmapFactory.decodeResource(getResources(), R.drawable.map_1);
_ratioWidth = (double)_map.getWidth() / (double)this.getWidth();
_ratioHeight = (double)_map.getHeight() / (double)this.getHeight();
}
}
Создадим дополнительный класс для изменения размеров изображения:
public class MapHelper {
public static Bitmap RescaleBitmap(Bitmap bmp) {
Bitmap rescaled = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth() / GameView._ratioWidth), (int)(bmp.getHeight() / GameView._ratioHeight), true);
return rescaled;
}
}
Остается только вывести нашу карту на экран, для этого добавляем код в метод onDraw класса GameView:
initMap(); // initialize map
// Clear canvas
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
canvas.drawPaint(paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC));
// Scale map to fullscreen size and draw it
Bitmap rescaledMap = MapHelper.RescaleBitmap(_map);
canvas.drawBitmap(rescaledMap, 0, 0, null);
Реализуем простую проверку на столкновение. Представим, что у Вас есть некий объект на экране и вы хотите проверить, нажал ли на него игрок пальцем.
Сделать это очень просто:
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if (x >= xOfYourBitmap && x < (xOfYourBitmap + yourBitmap.getWidth())
&& y >= yOfYourBitmap && y < (yOfYourBitmap + yourBitmap.getHeight())) {
// Игрок кликнул на объект
}
return super.onTouchEvent(event);
}
Скачиваем PayPal SDK и подключаем к нашему проекту библиотеку PayPal_MPL.jar.
Добавляем следующие строчки в AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<activity android:name="com.paypal.android.MEP.PayPalActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:configChanges="keyboardHidden|orientation"/>
В Activity из которой мы хотим производить оплату, добавляем две переменные:
private static final String PAYPAL_APP_ID = "APP-59X48022DA021071J";
PayPal mPayPal;
PAYPAL_APP_ID можно получить в аккаунте разработчика после регистрации на X.com.
В метод onCreate добавляем следующий код:
mPayPal = PayPal.initWithAppID(Advertisment.this.getBaseContext(), PAYPAL_APP_ID, PayPal.ENV_LIVE);
mPayPal.setLanguage("en_US");
CheckoutButton payButton = mPayPal.getCheckoutButton(this, PayPal.BUTTON_194x37, PayPal.PAYMENT_TYPE_GOODS);
payButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
PayPalPayment newPayment = new PayPalPayment();
newPayment.setSubtotal(BigDecimal.valueOf(0.99));
newPayment.setCurrencyType("USD");
newPayment.setRecipient("myemail@hotmail.com");
newPayment.setMerchantName("My app name");
Intent checkoutIntent = PayPal.getInstance().checkout(newPayment, Advertisment.this);
startActivityForResult(checkoutIntent, 1);
}
});
И создаем последний метод для обработки результата оплаты:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(resultCode) {
case Activity.RESULT_OK:
// Оплата прошла успешно
break;
case Activity.RESULT_CANCELED:
// Пользователь отказался оплачивать
break;
case PayPalActivity.RESULT_FAILURE:
// Ошибка при оплате
break;
}
}
На этом позвольте завершить мой маленький доклад. Я не Android Guru, но надеюсь, что моя статья поможет кому-то в разработке приложения или уменьшит время «гуугления».
Автор: MMW
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/6814
Нажмите здесь для печати.