Исходник игры Мозаика для Android

Все исходники / Язык программирования C# / OS Android / Смартфоны, планшеты / Мобильные игры / Исходник игры Мозаика для Android
Оглавление:
  1. Исходник игры на Xamarin.Android
  2. Интерфейс на Android.RelativeLayout
  3. Активные элементы на Android.ImageView
  4. Получение размеров RelativeLayout
  5. Подгонка размеров ImageView для заполнения экрана
  6. Пространственные координаты
  7. Событие прикосновения Touch
  8. Использование свойства View.Tag
  9. Прикрепленные файлы исходника

Исходник игры на Xamarin.Android

Игра мозаика для Android Исходник игры для Android написан на языке C# на платформе Xamarin.Android. Исходник представляет игру Мозаика, где картинки можно передвигать и таким образом составлять красивые узоры. Xamarin надстройка для среды .NET позволяющая создавать Android приложения на языке C# с использованием всех возможностей популярного языка. Xamarin.Android обеспечивает получить полный доступ к нативному (родному) Android SDK без каких либо ограничений. Программирование приложений на языке C# с помощью Xamarin интуитивно понятно и позволяет быстро перестраиваться в программировании самых различных десктоповых и веб приложений для Windows, Lunix, Android, iOS.

Интерфейс на Android.RelativeLayout

Компоновка элементов интерфейса игры Мозаика базируется на макете RelativeLayout, удобном контейнере для размещения элементов и групп элементов. Использование RelativeLayout предоставляет возможность создавать позиции элементов в точных единицах. Контейнер хоть и носит название относительный, но позволяет использовать абсолютные координаты для позиционирования элементов. В данном исходнике это и было использовано. Макет RelativeLayout создаётся в XML дизайнере, а дочерние элементы, картинки упакованные в ImageView, создаются и добавляются на поле макета программным способом. Перед показом игрового поля кратковременно демонстрируется экранная заставка.

Активные элементы на Android.ImageView

Для управления частичками Мозаики, файлы картинок упакованы в контейнер Android.ImageView. Такая оболочка для изображений имеет много полезных свойств. При помощи ImageView можно позиционировать изображение на макете по пиксельным координатам, масштабировать по горизонтали и вертикали, добавлять цветовую маску для вложенного изображения и др. Методы унаследованные от родительского класса View SetY(…), SetX(…) размещают контейнеры картинок по осям X и Y в любом месте родительского макета RelativeLayout в пиксельных единицах. Используя пространственные координаты, можно эффективно управлять положением картинок на экране смартфона или планшета. Представления ImageView создаются и добавляются в родительский контейнер программным способом. Картинки для наполнения ImageView загружаются из ресурсов Drawable и далее программно каждая в свое представление.

Получение размеров RelativeLayout

Получить программно ширину и высоту контейнера RelativeLayout во время создания или восстановления невозможно. В течение работы OnCreate(...), OnStart(..), OnResume(...) создаются только объекты визуальных классов, при этом все методы и свойства измерения на выходе выдают нулевые значения. Фактически они еще не «знают» как расположит их на экране родительский контейнер. Надежное получение ширины и высоты макета для размещения элементов ImageView гарантированно после полного создания дерева представлений. К сожалению, подобно программированию в Windows, в Android нет событий OnShow(), где можно перед показом элемента (окна) узнать его размеры. Но выход всегда есть: можно запросить размеры интересующего нас объекта RelativeLayout в отложенной задаче, которая получает доступ к интересуемым размерам после завершения подготовки пользовательского интерфейса.

// Добавляем в поток интерфейса асинхронную задачу прорисовки
 // картинок только после получения действительных 
// размеров главного макета-контейнера.
// Задача исполнится после готовности
// пользовательского интерфейса.
layoutMain.Post(() =>
    {
        ComputePos(layoutMain);      
        ShufflePositions();
        InitImages(layoutMain);  
    }
);

// -- Аналог кода на Java --
view.Post(new Runnable() {
    @Override
    public void run() {
        view.getHeight();
    }
});
//  --

// -- Аналог кода на Kotlin --
view.Post(Runnable { view.getHeight() })
// --

Подгонка размеров ImageView для заполнения экрана

Размеры представлений ImageView, а значит и картинок в них, высчитываются автоматически при визуализации MainActivity (Activity это единица экрана с пользовательским интерфейсом, Activity в приложении может быть несколько). Изображения загруженные в ImageView квадратные, а физические размеры высчитываются в пикселях. За основу расчетов, в данном исходнике игры, принята ширина главного макета в вертикальном положении. По вертикали картинки располагаются на экране до максимального заполнения. При загрузке на смартфоне, планшете или другом Android устройстве с различными размерами дисплеев визуально получается гармоничное заполнение разноцветными квадратиками.

// Вычисление позиций и размеров квадратиков
void ComputePos(RelativeLayout layoutMain)
{
    int widthLayout = layoutMain.Width;
    int heightLayout = layoutMain.Height;

    // Размер картинок высчитывается точно для горизонтали, 
    // чтобы гармонично смотрелось по ширине.
    int widthRect = widthLayout / NumberRectHorizontal;

    // Картинка квадратная.
     int heightRect = widthRect;

    // Количество строк до заполнения контейнера по высоте.
     int numberRectVertical = heightLayout / heightRect;

    // Все позиции в пространстве.
    RectPositionImages = new RectF[NumberRectHorizontal * numberRectVertical];

    // Расчет позиций в пространстве.
    int countPosY = 0;
    int countPosX = 0;
    for (int i = 0; i < RectPositionImages.Length; i++)
    {
        var rect = new RectF
        {
            Left = widthRect * countPosX,
            Top = widthRect * countPosY,
        };
        rect.Right = rect.Left + widthRect;
        rect.Bottom = rect.Top + heightRect;

        RectPositionImages[i] = rect;

        if (countPosX > 0 && countPosX % (NumberRectHorizontal - 1) == 0)
        {
            countPosX = -1;
            countPosY++;
        }
        countPosX++;
    }
}

Пространственные координаты

Координаты позиций и размерность элементов ImageView вычисляются после создания дерева представлений MainActivity и хранятся в массиве прямоугольников RectF[] RectPositionImages. Хранение координат отдельно от контейнеров картинок позволяет контролировать их положения на экране дисплея и корректировать в случае необходимости. Пространственными координаты названы, потому что они не привязаны к элементам макета и только виртуально делят пространство главного контейнера на ячейки. Пространственные координаты вычисляются на основе количества ячеек по ширине экрана. Элементы ImageView размещаются на позициях в контейнере RelativeLayout исчисляемых в пикселях. При создании приложения создается игровое поле с размерами ячеек высчитанных соответственно размеру экрана данного Android устройства.

// Количество позиций в пространстве
RectPositionImages = new RectF[NumberRectHorizontal * numberRectVertical];

// Расчет позиций в пространстве.
int countPosY = 0;
int countPosX = 0;
for (int i = 0; i < RectPositionImages.Length; i++)
{
    var rect = new RectF
    {
        Left = widthRect * countPosX,
        Top = widthRect * countPosY,
    };
    rect.Right = rect.Left + widthRect;
    rect.Bottom = rect.Top + heightRect;

    RectPositionImages[i] = rect;

    if (countPosX > 0 && countPosX % (NumberRectHorizontal - 1) == 0)
    {
        countPosX = -1;
        countPosY++;
    }
    countPosX++;
}

Событие прикосновения Touch

Для взаимодействия пользователя с игрой Мозаика применяется событие Touch(...). Это событие представляет собой реагирование на прикосновение пальцем или стилусом к сенсорному экрану. Прикосновение к объекту ImageView вызывает видимое изменение размеров и прозрачности картинки. Такая полезная обратная связь даёт информацию игроку Мозаики о том, что он действительно выбрал необходимый квадратик и может его перемещать в желаемое место для создания задуманного узора. В процессе перемещения также есть обратный сигнал о том, что квадратик находится над нужным местом и его можно отпустить. При этом цвет перемещаемой картинки изменяется если она находится на допустимом расстоянии от целевого места размещения. После отпускания картинки (подсобытие MotionEventActions.Up) активная картинка обменивается координатами позиции с нижележащей картинкой.

private void ImageView_Touch(object sender, View.TouchEventArgs e)
{
    // Координаты курсора
    MotionEvent motionEvent = e.Event;

    // Принимаем абсолютные координаты курсора.
    float cursorX = motionEvent.RawX;
    float cursorY = motionEvent.RawY;


    // Главный макет для доступа ко всем картинкам.
    RelativeLayout layoutMain = 
        this.FindViewById(Resource.Id.LayoutMain);

    // Активная картинка
    ImageView ivSender = (ImageView)sender;

    // Позиции активной картинки в пространстве экрана
    Positions posSender = (Positions)ivSender.Tag;

    // Центр картинки сделаем посередине, чтобы её 
    // было видно из-под пальца.
    ivSender.PivotX = ivSender.Width / 2;
    ivSender.PivotY = ivSender.Height / 2;


    // Прикасаемся, т.е. нажимаем.
    if (motionEvent.Action == MotionEventActions.Down)
    {
        // Увеличиваем активную картинку для удобного
        // вождения пальцем.
        ivSender.ScaleX = 2.0f;
        ivSender.ScaleY = 2.0f;

        // Делаем картинку полупрозрачной, чтобы было видно 
        // картинки под ней.
        ivSender.Alpha = 0.5f;

        // Поднимаем над всеми.
        ivSender.BringToFront();

        // Запоминаем данную позицию активной картинки.
        posSender.Position = new RectF
        {
            Left = ivSender.GetX(),
            Top = ivSender.GetY(),
            Right = ivSender.GetX() + ivSender.Width,
            Bottom = ivSender.GetY() + ivSender.Height
        };

        // Постоянная дельта на время вождения картинки.
        // Дельта это разница между координатным положением 
        // картинки по данной оси и местом соприкосновения пальца.
        // Измеряется в абсолютных единицах.
        // Расстояние от места прикосновения до начала координат активной картинки.
        DeltaX = cursorX - ivSender.GetX();
        DeltaY = cursorY - ivSender.GetY();
    }

    // Перемещение активной картинки в поисках места для создания узора.
    if (motionEvent.Action == MotionEventActions.Move)
    {
        // Из координат курсора, во время перемещения, вычитаем расстояние от места прикосновения
        // до начала координат выбранной картинки.
        // Благодаря этому картинка относительно пальца будет неподвижна,
        // и будет двигаться точно под пальцем.
        ivSender.SetX(cursorX - DeltaX);
        ivSender.SetY(cursorY - DeltaY);

        // Отлавливаем место возможного отпускания картинки.
        for (int i = 0; i < RectPositionImages.Length; i++)
        {
            if (ivSender.GetX() < (RectPositionImages[i].Left + 20) &&
            ivSender.GetX() > (RectPositionImages[i].Left - 20) &&
            ivSender.GetY() < (RectPositionImages[i].Top + 20) &&
            ivSender.GetY() > (RectPositionImages[i].Top - 20))
            {
                // Если место найдено просигналим изменением цвета 
                // передвигаемой картинки.
                ivSender.SetColorFilter(Color.DarkViolet);

                 // Устанавливаем флаг место найдено.
                 posSender.isFoundPos = true;

                 // Запоминаем новую позицию для активной картинки.
                 posSender.newPos = RectPositionImages[i];

                 break;
            }

            // Если отдалились от места возможного приземления
            // снимаем цветовую сигнализацию перемещаемой картикни.
            ivSender.ClearColorFilter();

            // Снимаем флаг обнаружения места приземления.
            posSender.isFoundPos = false;
        }
    }


    // Поднимаем палец. Отпускаем картинку.
    if (motionEvent.Action == MotionEventActions.Up)
    {

        // Возвращаем картинке нормальный масштаб.
        ivSender.ScaleX = 1;
        ivSender.ScaleY = 1;

        // Восстанавливаем непрозрачность.
        ivSender.Alpha = 1.0f;

        // Отпуская курсор принимаем новые координаты для данной картинки.
        // А картинка которая была уже на этой позиции отправляется на прежнее
        // место активной картинки.
        if (posSender.isFoundPos == true)
        {

            // Извлекаем информацию о нижележащей картинке под перемещаемой.
            for (int img = 0; img < layoutMain.ChildCount; img++)
            {
                ImageView imageView = (ImageView)layoutMain.GetChildAt(img);

                if (imageView.GetX() == posSender.newPos.Left && imageView.GetY() == posSender.newPos.Top)
                {
                    // --- Если картинку нашли ---

                    // Поднимаем её над всеми картинками.
                    imageView.BringToFront();

                   // Перемещаем с анимацией картинку на старое место передвигаемой картинки.
                   imageView.Animate().TranslationX(posSender.Position.Left);
                   imageView.Animate().TranslationY(posSender.Position.Top);

                    break;
                }
            }

            // Активная картинка устанавливается на новое место.
            ivSender.Animate().TranslationX(posSender.newPos.Left);
            ivSender.Animate().TranslationY(posSender.newPos.Top);

            // Сбрасываем флаг найденного места.
            posSender.isFoundPos = false;

            // Восстанавливаем настоящий цвет.
            ivSender.ClearColorFilter();
        }
        else
        {
            // Если нижележащая картинка не найдена,
            // возвращаем активную картинку на прежнее место.
            ivSender.Animate().TranslationX(posSender.Position.Left);
             ivSender.Animate().TranslationY(posSender.Position.Top);

            // Восстанавливаем настоящий цвет.
            ivSender.ClearColorFilter();
        }
    }
}


Использование свойства View.Tag

Свойство Tag класса Android.View предназначено для хранения пользовательской информации непосредственно в объекте класса. ImageView является наследником класса View и соответственно тоже имеет данное свойство. Свойство удобно тем, что в нем можно хранить любой объект. В исходнике игры Мозаика ImageView.Tag используется для хранения информации о текущей и новой позиции владельца свойства при исполнении события Touch(...). Для этого создан класс Positions в котором храниться координатная информация для необходимых перемещений цветных квадратиков.

// Собственный класс обязательно наследуем от Java.Lang.Object
// иначе свойству элементов Tag не сможем
// присвоить объект класса.
class Positions : Java.Lang.Object
{
    // Текущая позиция
    public RectF Position;

    // Новая позиция
    public RectF newPos;

    // Флаг обнаружения места приземления.
    public bool isFoundPos;
}

Прикрепленные файлы исходника

К статье прикреплен архив исходника игры Мозаика для скачивания. Исходник написан на языке C# на платформе Xamarin.Android. Инструмент программирования MS Visual Studio 2019. Целевая платформа API 26 (Android 8.0).

Скачать исходник

Тема: «Исходник игры Мозаика для Android» Операционная система Android AndroidMosaic.zip Размер:996 КбайтЗагрузки:642