Результаты поиска
| |
DasTem | Дата: Суббота, 15 Сентября 2012, 18:04 | Сообщение # 1 | Тема: Заработок в интернете! |
почетный гость
Сейчас нет на сайте
| за что это? заработок идет до 18000$ и это за месяц, все в скайпе расскажу + экран покажу
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Суббота, 15 Сентября 2012, 17:27 | Сообщение # 2 | Тема: Заработок в интернете! |
почетный гость
Сейчас нет на сайте
| Вот ссылка: https://61584855.give1get4.net Когда регнетесь вам надо будет вложить до 70$ ну сначала 25. и все, дальше приглашайте людей и получайте доход. полностью со скринами и описанием вы можете написать мне в скайп: lenin.zb
Разрабатывается браузерная онлайн игра "Династия"
|
|
| | |
DasTem | Дата: Суббота, 15 Сентября 2012, 03:20 | Сообщение # 4 | Тема: Custom View, скроллинг и жесты в Android на примере простого |
почетный гость
Сейчас нет на сайте
| В статье описываются детали реализации простого вьювера картинок и показываются некоторые тонкости имплементации скроллинга и обработки жестов.
И так, начнем. Ми будем разрабатывать приложения для просмотра картинок. Готовое приложение выглядит так (хотя скриншоты, конечно, слабо передают функционал): Установить приложение можно либо из Маркета, либо установив вручную отсюда. Исходный код доступен здесь.
Главным элементом нашего приложения является класс ImageViewer который мы и будем разрабатывать. Но нужно также отметить, что для выбора файла для просмотра я не стал изобретать велосипед и взял готовый «компонент» здесь. Компонент представляет собой activity, который вызывается при старте из главного activity. После выбора файла, мы его загружаем и показываем на экране с помощью класса ImageViewer. Рассмотрим класс более подробно.
Класс является наследником класса View и переопределяет только один его метод onDraw. Также класс содержит конструктор и метод загрузки изображения: Code public class ImageViewer extends View { private Bitmap image = null; public ImageViewer(Context context) { super(context); } @Override public void onDraw(Canvas canvas) { if (image != null) canvas.drawBitmap(image, 0, 0, null); }
public void loadImage(String fileName) { image = BitmapFactory.decodeFile(fileName); } } Если мы загрузим картинку по размерам больше чем экран смартфона, то отобразится только часть ее и у нас не будет способа ее подвинуть или уменьшить.
Добавим теперь возможность скроллинга. Скроллинг по своей сути представляет собой жест, при котором пользователь дотрагивается пальцем к экрану, передвигает его не отрывая, и отпускает. Для того чтоб иметь возможность обрабатывать события связанные с тач-скрином, нужно переопределить метод onTouchEvent. Метод принимает один параметр типа MotionEvent и должен возвратить true в случае обработки события. Через этот метод можно реализовать поддержку любого жеста, включая скроллинг. Для распознавания скроллинга нам нужно зафиксировать момент дотрагивания, перемещения и отпускания. К счастью нету необходимости делать это вручную так как в Android SDK есть класс делающий всю работу за нас. Таким образом для того чтоб распознать жест скроллинга, нужно добавить в наш класс поле типа GestureDetector которое инициализируется объектом реализующим интерфейс OnGestureListener (именно этот объект будет получать события скроллинга). Также нужно переопределить метод onTouchEvent в классе ImageViewer и передавать обработку событий из него в наш объект типа OnGestureListener. Измененный класс ImageViewer (без неизмененных методов) представлен ниже: Code public class ImageViewer extends View { private Bitmap image = null; private final GestureDetector gestureDetector; public ImageViewer(Context context) { super(context); gestureDetector = new GestureDetector(context, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event) { if (gestureDetector.onTouchEvent(event)) return true; return true; }
private class MyGestureListener extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { scrollBy((int)distanceX, (int)distanceY); return true; } } } Как видно на самом деле ми наследуем MyGestureListener не от OnGestureListener, а от SimpleOnGestureListener. Последний класс просто реализует интерфейс OnGestureListener с помощью пустых методов. Этим мы избавляем себя от реализации всех методов, выбирая только те, что нужно.
Теперь если загрузить большую картинку, мы, по крайней мере, сможем скролить ее. Но: во первых мы можем проскроллить за рамки картинки, во вторых нету скроллбаров, которые бы подсказали нам где мы находимся и сколько еще осталось до краев.
Решим для начала вторую проблему. Поиск в Интернет приводит нас к тому, что нужно переопределить методы computeHorizontalScrollRange и computeVerticalScrollRange. Эти методы должны возвратить реальные размеры картинки (на самом деле есть еще методы которые имеют отношение к скроллбарам – это методы computeHorizontalScrollExtent, computeHorizontalScrollOffset и такая же пара для вертикального скроллбара. Если переопределить и их, то тогда возвращать можно более произвольные значения). Но этого оказывается недостаточно – скроллбары в первых нужно включить, во вторых проинициализировать. Включаются они методами setHorizontalScrollBarEnabled и setVerticalScrollBarEnabled, инициализируются методом initializeScrollbars. Но вот незадача – последний метод принимает немного непонятный параметр типа TypedArray. Этот параметр должен содержать в себе набор стандартных для View атрибутов. Список можно увидеть здесь в таблице «XML Attributes». Если бы мы создавали наш view из XML, Android runtime бы автоматически составил такой список. Но так как мы создаем класс программно, нужно также создать этот список программно. Для этого нужно создать файл attrs.xml в каталоге res\values с таким содержимым: Code <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="View"> <attr name="android:background"/> <attr name="android:clickable"/> <attr name="android:contentDescription"/> <attr name="android:drawingCacheQuality"/> <attr name="android:duplicateParentState"/> <attr name="android:fadeScrollbars"/> <attr name="android:fadingEdge"/> <attr name="android:fadingEdgeLength"/> <attr name="android:fitsSystemWindows"/> <attr name="android:focusable"/> <attr name="android:focusableInTouchMode"/> <attr name="android:hapticFeedbackEnabled"/> <attr name="android:id"/> <attr name="android:isScrollContainer"/> <attr name="android:keepScreenOn"/> <attr name="android:longClickable"/> <attr name="android:minHeight"/> <attr name="android:minWidth"/> <attr name="android:nextFocusDown"/> <attr name="android:nextFocusLeft"/> <attr name="android:nextFocusRight"/> <attr name="android:nextFocusUp"/> <attr name="android:onClick"/> <attr name="android:padding"/> <attr name="android:paddingBottom"/> <attr name="android:paddingLeft"/> <attr name="android:paddingRight"/> <attr name="android:paddingTop"/> <attr name="android:saveEnabled"/> <attr name="android:scrollX"/> <attr name="android:scrollY"/> <attr name="android:scrollbarAlwaysDrawHorizontalTrack"/> <attr name="android:scrollbarAlwaysDrawVerticalTrack"/> <attr name="android:scrollbarDefaultDelayBeforeFade"/> <attr name="android:scrollbarFadeDuration"/> <attr name="android:scrollbarSize"/> <attr name="android:scrollbarStyle"/> <attr name="android:scrollbarThumbHorizontal"/> <attr name="android:scrollbarThumbVertical"/> <attr name="android:scrollbarTrackHorizontal"/> <attr name="android:scrollbarTrackVertical"/> <attr name="android:scrollbars"/> <attr name="android:soundEffectsEnabled"/> <attr name="android:tag"/> <attr name="android:visibility"/> </declare-styleable> </resources> В файле просто перечислены все атрибуты, которые были указаны в таблице, упомянутой выше (кроме некоторых на которые указывает компилятор как на ошибку – видимо в документации список приведен самый последний). Измененный класс ImageViewer (кроме неизменных методов): Code public class ImageViewer extends View { private Bitmap image = null; private final GestureDetector gestureDetector; public ImageViewer(Context context) { super(context); gestureDetector = new GestureDetector(context, new MyGestureListener()); // init scrollbars setHorizontalScrollBarEnabled(true); setVerticalScrollBarEnabled(true);
TypedArray a = context.obtainStyledAttributes(R.styleable.View); initializeScrollbars(a); a.recycle(); } @Override protected int computeHorizontalScrollRange() { return image.getWidth(); }
@Override protected int computeVerticalScrollRange() { return image.getHeight(); } }
Не хотелось бы на этом останавливаться, поэтому давайте добавим поддержку жеста «бросок» (fling). Этот жест есть просто дополнение к жесту скроллинга, но учитывается скорость перемещения пальца в последние моменты (перед отпусканием), и если она не нулевая, скроллинг продолжается с постепенным затуханием. Поддержка этого жеста уже заложена в GestureDetector – поэтому нам нужно всего лишь переопределить метод onFling в классе MyGestureListener. Отловив это событие нам нужно еще некоторое время изменять положение скроллинга. Конечно, это можно сделать «вручную» с помощью таймеров или еще как, но опять же в Android SDK уже есть класс, реализующий нужный функционал. Поэтому нужно добавить в класс ImageViewer еще одно поле типа Scroller, которое и будет заниматься «остаточным» скроллингом – для старта скроллинга нужно вызвать его метод fling. Также нужно показать скроллбары (они ведь прячутся когда не нужны) вызовом метода awakenScrollBars. И последнее что нужно сделать – это переопределить метод computeScroll, который должен непосредственно делать скроллинг с помощью метода scrollTo (класс Scroller сам не занимается скроллингом – он просто работает с координатами). Код измененного класса ImageViewer представлен ниже: Code public class ImageViewer extends View { private Bitmap image = null; private final GestureDetector gestureDetector; private final Scroller scroller; public ImageViewer(Context context) { super(context); gestureDetector = new GestureDetector(context, new MyGestureListener()); scroller = new Scroller(context); // init scrollbars setHorizontalScrollBarEnabled(true); setVerticalScrollBarEnabled(true);
TypedArray a = context.obtainStyledAttributes(R.styleable.View); initializeScrollbars(a); a.recycle(); }
@Override public void computeScroll() { if (scroller.computeScrollOffset()) { int oldX = getScrollX(); int oldY = getScrollY(); int x = scroller.getCurrX(); int y = scroller.getCurrY(); scrollTo(x, y); if (oldX != getScrollX() || oldY != getScrollY()) { onScrollChanged(getScrollX(), getScrollY(), oldX, oldY); }
postInvalidate(); } }
private class MyGestureListener extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(getScrollX(), getScrollY(), -(int)velocityX, -(int)velocityY, 0, image.getWidth() - getWidth(), 0, image.getHeight() - getHeight()); awakenScrollBars(scroller.getDuration()); return true; } } } В завершения разговора о жесте fling надо сделать одну мелочь – при прикосновении пальцем во время скроллинга от броска, нужно остановить скроллинг. На этот раз мы это сделаем «вручную» в методе onTouchEvent. Измененный метод представлен ниже: Code @Override public boolean onTouchEvent(MotionEvent event) { // check for tap and cancel fling if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { if (!scroller.isFinished()) scroller.abortAnimation(); }
if (gestureDetector.onTouchEvent(event)) return true; return true; } Уже можно любоваться достаточно интересной физикой, но можно увидеть некоторые «глюки» при скроллинге за пределы картинки. Это происходит из-за того, что fling работает только в пределах картинки, а скроллинг без броска работает везде. Т.е. мы сможем выйти за рамки картинки только если очень плавно скролить (чтоб не срабатывал fling). Исправить этот «косяк» можно путем введения ограничение на обработку в метод onFling и обрабатывать бросок только если он не выходит за границы картинки. Измененный метод представлен ниже: Code @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { boolean scrollBeyondImage = ((getScrollX() < 0) || (getScrollX() > image.getWidth()) || (getScrollY() < 0) || (getScrollY() > image.getHeight())); if (scrollBeyondImage) return false;
scroller.fling(getScrollX(), getScrollY(), -(int)velocityX, -(int)velocityY, 0, image.getWidth() - getWidth(), 0, image.getHeight() - getHeight()); awakenScrollBars(scroller.getDuration());
return true; } Теперь мы опять можем беспрепятственно скролить за рамки картинки. Кажется, эту проблему мы уже вспоминали… У нее есть элегантное решение, лежащее в том, что при отпускании пальца (при завершении скроллинга за рамками картинки) нужно картинку плавно вернуть в «положенное» место. И опять мы это сделаем «вручную» в методе onTouchEvent: Code @Override public boolean onTouchEvent(MotionEvent event) { // check for tap and cancel fling if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { if (!scroller.isFinished()) scroller.abortAnimation(); }
if (gestureDetector.onTouchEvent(event)) return true;
// check for pointer release if ((event.getPointerCount() == 1) && ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)) { int newScrollX = getScrollX(); if (getScrollX() < 0) newScrollX = 0; else if (getScrollX() > image.getWidth() - getWidth()) newScrollX = image.getWidth() - getWidth();
int newScrollY = getScrollY(); if (getScrollY() < 0) newScrollY = 0; else if (getScrollY() > image.getHeight() - getHeight()) newScrollY = image.getHeight() - getHeight();
if ((newScrollX != getScrollX()) || (newScrollY != getScrollY())) { scroller.startScroll(getScrollX(), getScrollY(), newScrollX - getScrollX(), newScrollY - getScrollY()); awakenScrollBars(scroller.getDuration()); } }
return true; } Вот теперь с уверенностью можно сказать что со скроллингом мы разобрались. Можем переходить к последнему жесту который хотелось бы реализовать – это жест pinch zoom.
Со стороны жест выглядит как растягивание или сжатие чего-то воображаемого на экране смартфона двумя пальцами. Пошагово жест происходит так: нажатие одним пальцем, нажатие вторым пальцем, изменение положения одного или двух пальцев не отпуская, отпускание второго пальца. Для определения величины масштабирования нужно вычислить соотношение между расстояниями между пальцами в момент начала жеста и в момент окончания жеста. Расстояние между пальцами находится по формуле sqrt(pow(x2 – x1, 2) + pow(y2 – y1, 2)). Также нужно отметить некоторое положение скроллинга которое нужно сохранять – ведь если жестом увеличить картинку, то положение скроллинга изменится (из-за измененного размера картинки). Это положение – а точнее точка, положение которой нужно сохранить, в терминологии Android SDK называется фокальной точкой, и находиться она посередине между двумя пальцами. Реализовать жест как всегда можно самому, но и это к счастью уже реализовано в Android SDK (правда, только начиная с версии 2.2). Поможет в этом класс ScaleGestureDetector, инстанс которого добавим в наш класс. ScaleGestureDetector инициализируется обьектом, поддерживающим интерфейс OnScaleGestureListener, поэтому создадим также внутренний класс MyScaleGestureListener, который реализует методы onScaleBegin, onScale и onScaleEnd. Не забываем передать управление ScaleGestureDetector из метода onTouchEvent. Ну и самое главное – нужно как-то использовать данные масштабирования: их нужно учитывать во всех местах, где раньше фигурировали ширина и высота картинки (т.е. фактически нужно умножить эти параметры на коэффициент масштабирования). Финальный код класса ImageViewer можно посмотреть в исходниках. На этом все. Надеюсь статься окажется полезной. http://gcup.ru/forum/7-26398-1
Разрабатывается браузерная онлайн игра "Династия"
Сообщение отредактировал DasTem - Суббота, 15 Сентября 2012, 03:21 |
|
| |
DasTem | Дата: Суббота, 15 Сентября 2012, 03:12 | Сообщение # 5 | Тема: Пишем игру для Android используя Canvas |
почетный гость
Сейчас нет на сайте
| Сегодня я хочу рассказать о том, как написать простую логическую игру для Android OS с использованием Canvas. С этой игрой я познакомился около 5 лет назад на своем телефоне. Название забылось, а поиски на нескольких тематических форумах ни к чему не привели и я решил написать свою реализации этой игры. Разработка для меня скорее хобби, хотя иногда берусь за небольшие проекты. После некоторых раздумий я решил не использовать движок, а писать самостоятельно. Причины такого решения: желание получить опыт работы с Canvas'ом. Суть такова... Имеется игровое поле подобное шахматной доске, однако без деления клеток на черные и белые. Размер поля может быть произвольным, однако играть на поле меньше, чем 2х3 не имеет особого смысла. Мне нравится играть на поле 10х10.
Количество игроков может произвольным, однако на мой взгляд интереснее играть вдвоем или вчетвером. Если играть втроем на прямоугольном поле, то возникает дисбаланс, поскольку один из игроков будет отдален от «пустого» угла. Если играть компанией больше, чем 4 человека, то сложно воплощать свою стратегию.
Каждый игрок по очереди должен положить один объект (назовем его атомом) в свободную ячейку или в ячейку, где уже есть его атомы. Если в ячейке накапливается «критическая масса», равная числу соседних ячеек, то атомы из данной ячейки перемещаются в соседние ячейки, при этом находящиеся в соседних ячейках атомы «захватываются», т.е. теперь они принадлежат игроку, чьи атомы разлетелись.
Пара картинок для пояснения сути.
Пустое поле 4х4 с указанием критической массы для каждой ячейки. Игровая ситуация после третьего хода. Игровая ситуация после четвертого хода (первыми ходят синие). Видно, что в верхнем левом углу скопилось критическое количество атомов. Бах! Они разлетелись, захватив 2 синих атома в ячейке [0][1]. И в этой ячейке [0][1] теперь тоже критическое количество. Цепная реакция! Ситуация после разлета. Конец четвертого хода. Синие сейчас совершат пятый ход. Реализация. Графическая часть. Приступим к реализации. Создадим производный от View класс. Code public class GameView extends View {
private Bitmap mBitmap; private Canvas mCanvas; private Paint paint, mBitmapPaint; private float canvasSize; private final int horizontalCountOfCells, verticalCountOfCells;
public GameView(Context context, AttributeSet attrs) { super(context, attrs); //размер игрового поля horizontalCountOfCells =10; verticalCountOfCells =10; //в xml разметке позднее пропишем размер вьюхи равный 300dp canvasSize=(int)convertDpToPixel(300, context);
mBitmap = Bitmap.createBitmap((int) canvasSize, (int) canvasSize, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); mBitmapPaint = new Paint(Paint.DITHER_FLAG);
//определяем параметры кисти, которой будем рисовать сетку и атомы paint =new Paint(); paint.setAntiAlias(true); paint.setDither(true); paint.setColor(0xffff0505); paint.setStrokeWidth(5f); paint.setStyle(Paint.Style.STROKE); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeCap(Paint.Cap.ROUND);
//рисуем сетку for(int x=0;x< horizontalCountOfCells +1;x++) mCanvas.drawLine((float)x* canvasSize / horizontalCountOfCells, 0, (float)x* canvasSize / horizontalCountOfCells, canvasSize, paint); for(int y=0;y< verticalCountOfCells +1;y++) mCanvas.drawLine(0, (float)y* canvasSize / verticalCountOfCells, canvasSize, (float)y* canvasSize / verticalCountOfCells, paint); }
@Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); }
//переводим dp в пиксели public float convertDpToPixel(float dp,Context context){ Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); return dp * (metrics.densityDpi/160f); } } Здесь я допустил немного говнокода кода плохого качества, а именно захардкордил в коде цвет линий, разделяющих ячейки игрового поля и размер вьюхи считается равным 300 dp. Этот размер можно получить из объекта attrs класса AttributeSet, однако не будем загромождать код.
Также сразу набросаем Activity, дабы убедиться, что все рисуется красиво. Code public class GameActivity extends Activity { Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } } И разметка main.xml Code <LinearLayout xmlns:android=«schemas.android.com/apk/res/android» android:orientation=«vertical» android:layout_width=«fill_parent» android:layout_height=«fill_parent» android:background="#aaa" android:gravity=«center_horizontal»> <com.devindi.chain.GameView android:layout_width=«300dp» android:layout_height=«300dp» android:id="@+id/game_view" android:layout_gravity=«center» android:background="#000"/> </LinearLayout> Код после этого этапа Теперь добавим возможность изменять масштаб игрового поля, поскольку возможны промахи мимо нужной ячейки по причине их малого размера. Для этого переопределяем нужные нам методы реализации ScaleGestureDetector.SimpleOnScaleGestureListener интерфейса OnScaleGestureListener в нашем классе GameView. Code private final ScaleGestureDetector scaleGestureDetector; private final int viewSize; private float mScaleFactor; public GameView(Context context, AttributeSet attrs) { //в xml разметке позднее пропишем размер вьюхи равный 300dp viewSize=(int)convertDpToPixel(300, context); mScaleFactor=1f;//значение зума по умолчанию canvasSize=(int)(viewSize*mScaleFactor);//определяем размер канваса … scaleGestureDetector=new ScaleGestureDetector(context, new MyScaleGestureListener()); } @Override protected void onDraw(Canvas canvas) { canvas.save(); canvas.scale(mScaleFactor, mScaleFactor);//зумируем канвас canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); canvas.restore(); }
//в случае касания пальем передаем управление MyScaleGestureListener @Override public boolean onTouchEvent(MotionEvent event) { scaleGestureDetector.onTouchEvent(event); return true; }
//унаследовались от ScaleGestureDetector.SimpleOnScaleGestureListener, чтобы не писать пустую реализацию ненужных //методов интерфейса OnScaleGestureListener private class MyScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { //обрабатываем "щипок" пальцами @Override public boolean onScale(ScaleGestureDetector scaleGestureDetector) { float scaleFactor=scaleGestureDetector.getScaleFactor();//получаем значение зума относительно предыдущего состояния //получаем координаты фокальной точки - точки между пальцами float focusX=scaleGestureDetector.getFocusX(); float focusY=scaleGestureDetector.getFocusY(); //следим чтобы канвас не уменьшили меньше исходного размера и не допускаем увеличения больше чем в 2 раза if(mScaleFactor*scaleFactor>1 && mScaleFactor*scaleFactor<2){ mScaleFactor *= scaleGestureDetector.getScaleFactor(); canvasSize =viewSize*mScaleFactor;//изменяем хранимое в памяти значение размера канваса //используется при расчетах //по умолчанию после зума канвас отскролит в левый верхний угол. //Скролим канвас так, чтобы на экране оставалась //область канваса, над которой был жест зума //Для получения данной формулы достаточно школьных знаний математики (декартовы координаты). int scrollX=(int)((getScrollX()+focusX)*scaleFactor-focusX); scrollX=Math.min( Math.max(scrollX, 0), (int) canvasSize -viewSize); int scrollY=(int)((getScrollY()+focusY)*scaleFactor-focusY); scrollY=Math.min( Math.max(scrollY, 0), (int) canvasSize -viewSize); scrollTo(scrollX, scrollY); } //вызываем перерисовку принудительно invalidate(); return true; } } Получившийся код Обратите внимание, что установлены границы значения увеличинения (увеличение будет на промежутке от 1 до 2), а также производится скролл игрового поля к точке зумирования (точка посередине между пальцами) с проверкой на показ области вне игрового поля. Скролл к точке зуммирования (фокальной точке) производится следующим образом — расчитывается координаты фокальной точки относительно начала канваса(верхний левый угол), умножаются на коэффициент зуммирования и вычитаются координаты точки относительно вьюхи. После этого берем ближайшее значение из промежутка [0, canvasSize -viewSize] для предотвращения скролла за пределы игрового поля.
Теперь напишем обработку скролла, одиночного тапа и двойного тапа (двойной тап будет возвращать к исходному масштабу игрового поля). Code private final GestureDetector detector; public GameView(Context context, AttributeSet attrs) { ... detector=new GestureDetector(context, new MyGestureListener()); }
//в случае касания пальем передаем обработку Motion Event'а MyGestureListener'у и MyScaleGestureListener'у @Override public boolean onTouchEvent(MotionEvent event) { detector.onTouchEvent(event); scaleGestureDetector.onTouchEvent(event); return true; }
//унаследовались от GestureDetector.SimpleOnGestureListener, чтобы не писать пустую //реализацию ненужных методов интерфейса OnGestureListener private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { //обрабатываем скролл (перемещение пальца по экрану) @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { //не даем канвасу показать края по горизонтали if(getScrollX()+distanceX< canvasSize -viewSize && getScrollX()+distanceX>0){ scrollBy((int)distanceX, 0); } //не даем канвасу показать края по вертикали if(getScrollY()+distanceY< canvasSize -viewSize && getScrollY()+distanceY>0){ scrollBy(0, (int)distanceY); } return true; }
//обрабатываем одиночный тап @Override public boolean onSingleTapConfirmed(MotionEvent event){ //получаем координаты ячейки, по которой тапнули int cellX=(int)((event.getX()+getScrollX())/mScaleFactor); int cellY=(int)((event.getY()+getScrollY())/mScaleFactor); return true; }
//обрабатываем двойной тап @Override public boolean onDoubleTapEvent(MotionEvent event){ //зумируем канвас к первоначальному виду mScaleFactor=1f; canvasSize =viewSize; scrollTo(0, 0);//скролим, чтобы не было видно краев канваса. invalidate();//перерисовываем канвас return true; } } Получившийся код
При одиночном тапе мы рассчитываем координаты ячейки, по которой тапнули, например верхняя левая ячейка будет иметь координаты 0,0.
Напишем метод рисования атомов drawAtoms. параметры метода — координаты ячейки, в которой рисуем атомы, цвет и количество атомов. Code void drawAtoms(int cellX, int cellY, int color, int count){ //считаем координаты центра ячейки float x0=((1f/(2* horizontalCountOfCells))*viewSize+(1f/ horizontalCountOfCells)*cellX*viewSize); float y0=((1f/(2* verticalCountOfCells))*viewSize+(1f/ verticalCountOfCells)*cellY*viewSize); paint.setColor(color); switch (count){ //todo non-absolute values case 1: drawAtoms(cellX, cellY, color, 0);//стираем существующие атомы mCanvas.drawCircle(x0, y0, 3, paint);//рисуем один атом в центре ячейки break; case 2: drawAtoms(cellX, cellY, color, 0); //рисуем пару атомов на удалении от центра ячейки mCanvas.drawCircle(x0-7, y0, 3, paint); mCanvas.drawCircle(x0+7, y0, 3, paint); break; case 3: drawAtoms(cellX, cellY, color, 0); //рисуем три атома в вершинах правильного треугольника с центром в центре ячейки mCanvas.drawCircle(x0 - 7, y0 + 4, 3, paint); mCanvas.drawCircle(x0 + 7, y0 + 4, 3, paint); mCanvas.drawCircle(x0, y0-8, 3, paint); break; case 4: drawAtoms(cellX, cellY, color, 0); //рисуем 4 атом в вершинах квадрата с центром в центре ячейки mCanvas.drawCircle(x0-7, y0-7, 3, paint); mCanvas.drawCircle(x0-7, y0+7, 3, paint); mCanvas.drawCircle(x0+7, y0+7, 3, paint); mCanvas.drawCircle(x0+7, y0-7, 3, paint); break; case 0: //устанавливаем кисти режим стирания paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); //заполнение обойденного участка paint.setStyle(Paint.Style.FILL); //рисуем большой круг, на месте которого ничего не останется mCanvas.drawCircle(x0, y0, 17, paint); //возвращаем исходные параметры paint.setStyle(Paint.Style.STROKE); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); break; } invalidate();//перерисовываем канвас } На данный момент метод имеет недоработку в виде абсолютных значений размеров атомов и расстояний между ними. К чему это может привести? При размере поля меньше, чем 10х10 атомы будут смотреться мелкими, а при большем размере поля могут не влезть в ячейку.
Осталось добавить скроллбары. Этот процесс хорошо описан здесь: http://gcup.ru/forum/7-26399-1
Прежде чем приступить к описанию игровой логики добавим глобальную переменную типа GameLogic (наш класс, описывающий логику) с именем logic и добавим в метод обработки одиночного тапа onSingleTapConfirmed вызов будущего метода логической обработки добавления нового атома. logic.addAtom(cellX, cellY); Также необходим сеттер для этой переменной. Код Реализация. Игровая логика.
Создадим класс GameLogic, описывающий логику игры. Также нам понадобится внутренний класс Cell, хранящий параметры ячейки игрового поля. Code public class GameLogic { private class Cell{ int player=0, countOfAtoms=0;//хозяин ячейки, количество атомов в ячейке final int maxCountOfAtoms;//емкость ячейки Cell(int maxCountOfAtoms){ this.maxCountOfAtoms=maxCountOfAtoms; } public int getCountOfAtoms() { return countOfAtoms; } public int getPlayer() { return player; } public void setPlayer(int player) { this.player = player; } public void resetCount() { this.countOfAtoms = 0; } public void addAtom(){ this.countOfAtoms++; } boolean isFilled(){ return this.countOfAtoms == this.maxCountOfAtoms; } } } Сам класс GameLogic Code private final GameView view; private final GameActivity activity; private int moveNumber=0, currentPlayer=0; private final int COUNT_OF_PLAYERS, BOARD_WIDTH, BOARD_HEIGHT; private final Cell[][] cells;
private final int[] colors={0xff1d76fc, 0xfffb1d76, 0xff76fb1d, 0xffa21cfb};//цвета атомов private final Handler mHandler;
public GameLogic(GameView view, GameActivity activity) { this.view = view; this.activity=activity; mHandler=new Handler(); //инициализируем игровые параметры (количество игроков, размер доски) this.COUNT_OF_PLAYERS=2; this.BOARD_HEIGHT=10; this.BOARD_WIDTH=10; cells=new Cell[BOARD_WIDTH][BOARD_HEIGHT]; for(int x=0; x<BOARD_WIDTH; x++){ for(int y=0; y<BOARD_HEIGHT; y++){ if((x==0 || x==BOARD_WIDTH-1) && (y==0 || y==BOARD_HEIGHT-1)){ cells[x][y]=new Cell(2);//угловые ячейки имеют емкость 2 }else if((x==0 || x==BOARD_WIDTH-1) || (y==0 || y==BOARD_HEIGHT-1)){ cells[x][y]=new Cell(3);//краевые, но не угловые - 3 }else{ cells[x][y]=new Cell(4);//остальные - 4 } } } }
//вызывается из вьюхи по одиночному тапу public void addAtom(final int cellX, final int cellY) { //получаем ячейку, в которую добавляем атом, если ее нет в массиве - выходим из функции. final Cell currentCell; try{ currentCell=cells[cellX][cellY]; }catch (IndexOutOfBoundsException ex){ return; } //если в ячейке уже есть атомы этого игрока if(currentCell.getPlayer()==currentPlayer){ currentCell.addAtom(); view.drawAtoms(cellX, cellY, colors[currentPlayer], currentCell.getCountOfAtoms()); //если ячейка заполнена if(currentCell.isFilled()){ final List<Cell> nearby=new ArrayList<Cell>(4);//лист соседних ячеек selfAddCell(cellX, cellY-1, nearby); selfAddCell(cellX, cellY+1, nearby); selfAddCell(cellX-1, cellY, nearby); selfAddCell(cellX+1, cellY, nearby); for(Cell nearbyCell:nearby){ nearbyCell.setPlayer(currentPlayer);//соседним ячейкам устанавливаем нового владельца } delayedAddAtom(cellX, cellY-1); delayedAddAtom(cellX, cellY+1); delayedAddAtom(cellX-1, cellY); delayedAddAtom(cellX+1, cellY); //через секунду произойдет вызов метода run() mHandler.postDelayed(new Runnable() { @Override public void run() { //текущая ячейка становится нейтральной (ничейной) currentCell.setPlayer(-1); //и пустой currentCell.resetCount(); view.drawAtoms(cellX, cellY, 0x000000, 0); } }, 1000); return; } }else if(currentCell.getPlayer()==-1){ currentCell.addAtom(); view.drawAtoms(cellX, cellY, colors[currentPlayer], currentCell.getCountOfAtoms()); currentCell.setPlayer(currentPlayer); }else{ return; } }
//через секунду соседние ячйки получат по атому private void delayedAddAtom(final int cellX, final int cellY){ mHandler.postDelayed(new Runnable() { @Override public void run() { addAtom(cellX, cellY); } }, 1000); }
//добавляем в лист target существующие ячейки private void selfAddCell(int cellX, int cellY, List<Cell> target){ try{ target.add(cells[cellX][cellY]); }catch (IndexOutOfBoundsException ignore){} } Код Здесь мы видим конструктор, который создает двумерный массив объектов Cell и метод addAtom, вызываемый из вьюхи по одиночному тапу и из себя при условии что ячейка наполнена атомами под завязку. Теперь можно добавлять атомы в ячейки, а при накоплении в ячейке критического количества атомов они разлетятся в стороны через секунду. Однако сейчас во время этой секунды можно добавлять атомы. Избавимся от этого, добавив переменную-флаг isLock и методы isLock(), lock() и unlock() в класс GameView.
Также необходимо добавить смену игрока после хода, подсчет очков и обработку конца игры (когда все атомы принадлежат одному игроку и каждый игрок совершил как минимум 1 ход). Добавим в конец метода addAtom() следующий код Code int[] score=scoring(); if(moveNumber==0){ endTurn(score); }else { //проверка на конец игры. Нельзя делать в теле метода endTurn() потому что //не определим конец игры при бесконечной реакции int losersCount=0; for(int i=0; i<COUNT_OF_PLAYERS; i++){ if(score[i]==0) losersCount++; } if(losersCount+1==COUNT_OF_PLAYERS){ isEndGame=true; } if(!mHandler.hasMessages(0)){ view.unlock(); endTurn(score); } } }
//обработка конца хода private void endTurn(int[] score){ if(!isEndGame){ if(currentPlayer == COUNT_OF_PLAYERS-1){ moveNumber++; currentPlayer=0; }else { currentPlayer++; } }else{ activity.endGame(currentPlayer, score[currentPlayer]); } //извещаем активити, о том, что поменялся счет, текущий игрок и номер хода activity.setMoveNumber(moveNumber); activity.setPlayerName(currentPlayer); activity.setScore(score);
}
//подсчет очков int[] scoring(){ int[] score=new int[COUNT_OF_PLAYERS]; for(int x=0; x<BOARD_WIDTH; x++){ for(int y=0; y<BOARD_HEIGHT; y++){ if(cells[x][y].getPlayer()!=-1){ score[cells[x][y].getPlayer()]+=cells[x][y].getCountOfAtoms(); } } } return score; } Весь код Осталось написать реализацию методов Code void setPlayerName(int playerID){}
void setScore(int[] score){}
void setMoveNumber(int moveNumber){}
void endGame(int winnerID, int score){} Это будет домашним заданием. Тут все тривиально.
После сборки обработать напильником
Далее требуется небольшое допиливание получившегося приложения в виде создания Activity для настройки игры (размер игрового поля, количество игроков, цвет атомов и имена игроков, однако это не тема данной статьи. Итоговый код можно посмотреть на гитхабе, получившуюся игру попробовать на Google Play
Надеюсь, кому-нибудь статья поможет. Прошу большими камнями не кидаться. Осознаю низкое
Разрабатывается браузерная онлайн игра "Династия"
Сообщение отредактировал DasTem - Суббота, 15 Сентября 2012, 03:21 |
|
| |
DasTem | Дата: Суббота, 15 Сентября 2012, 02:24 | Сообщение # 6 | Тема: Маленький вопрос про вывод данных |
почетный гость
Сейчас нет на сайте
| Вот есть код: Code $site = c("edit1")->text; $get = file_get_contents("http://ololool.ru/hit?t2q3e8.2;r;s13366*768*24;uhtrdrtp%3A//$site/;0.789101"); c("browser1")->text = "$get"; Что должна делать программа: После того как юзер введет сайт в edit1 и нажмет на кнопку, все то что будет на странице должно быть в браузере. Код не работает. почему нечего не выводит?
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Понедельник, 03 Сентября 2012, 18:06 | Сообщение # 7 | Тема: Не заливается база |
почетный гость
Сейчас нет на сайте
| Ku-Klux, мне бы программу чтобы разделить, ато листать до 30мб долго очень)
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Воскресенье, 02 Сентября 2012, 19:19 | Сообщение # 8 | Тема: Что за бред с Delphi 2010 |
почетный гость
Сейчас нет на сайте
| Quote (karuy) Почему это бред? Может в Output Directory задано что-то типа D:/ ? ну даже если так, файл не создаетсяДобавлено (02.09.2012, 19:19) --------------------------------------------- karuy, помогло, в настройках билда Output Directory поменял)
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Воскресенье, 02 Сентября 2012, 19:03 | Сообщение # 9 | Тема: Что за бред с Delphi 2010 |
почетный гость
Сейчас нет на сайте
| +1 вернемся в моей проблеме) Quote (kefir_plus) Делфи? Ужасная среда увижу оффтоп минус в репу. И по твоей истории репутации, тебя уже за это наказывали
Разрабатывается браузерная онлайн игра "Династия"
Сообщение отредактировал DasTem - Воскресенье, 02 Сентября 2012, 19:03 |
|
| |
DasTem | Дата: Воскресенье, 02 Сентября 2012, 18:37 | Сообщение # 10 | Тема: Что за бред с Delphi 2010 |
почетный гость
Сейчас нет на сайте
| Запускаю проект делфи, компилю, ошибок 0, пишет почему то что создало Project1.exe на диске "D", хоть должно быть в папке с исходником. в итого не там не там файла нету, все других проекты компиляться норм! Что это?!
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Воскресенье, 02 Сентября 2012, 18:33 | Сообщение # 11 | Тема: Visual Basic - можно ли открыть .ехе? |
почетный гость
Сейчас нет на сайте
| Quote (JHawk) Можно ли открыть .ехе как проект ВБ? можно декомпилировать, что не будет хорошо. исправь название темы
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Воскресенье, 02 Сентября 2012, 18:30 | Сообщение # 12 | Тема: Не заливается база |
почетный гость
Сейчас нет на сайте
| Quote (Ku-Klux) попробуй базу залить частями через SQL (выполняя запросы) огооо, это очень долго будет. Может быть как то разбить на 2 файла?
Разрабатывается браузерная онлайн игра "Династия"
Сообщение отредактировал DasTem - Воскресенье, 02 Сентября 2012, 18:30 |
|
| |
DasTem | Дата: Воскресенье, 02 Сентября 2012, 15:08 | Сообщение # 13 | Тема: Не заливается база |
почетный гость
Сейчас нет на сайте
| Пробовал, он вообще к базе не подключается
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Воскресенье, 02 Сентября 2012, 14:42 | Сообщение # 14 | Тема: Не заливается база |
почетный гость
Сейчас нет на сайте
| Вот есть база 91мб весит, в phpmyadmin когда загружаю, то когда доходит до 71%, выбивается ошибка: Code Веб-страница недоступна Веб-страница по адресу http://sql3.hostinger.com.ua/phpmyadmin/import.php, возможно, временно недоступна или постоянно перемещена по новому адресу. Ошибка 103 (net::ERR_CONNECTION_ABORTED): Неизвестная ошибка. тайм аут. Вопрос: как загрузить базу?)
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Вторник, 28 Августа 2012, 12:42 | Сообщение # 15 | Тема: Продам графику для браузерной игры! |
почетный гость
Сейчас нет на сайте
| Ku-Klux, Да Жанр: средневековье!
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Вторник, 28 Августа 2012, 12:20 | Сообщение # 16 | Тема: Продам графику для браузерной игры! |
почетный гость
Сейчас нет на сайте
| SilkT, Выложу вечером, так как рисуется профессионалами
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Вторник, 28 Августа 2012, 12:17 | Сообщение # 17 | Тема: Продам графику для браузерной игры! |
почетный гость
Сейчас нет на сайте
| Продам графику для браузерной игры! Рисуется с 0! Продажа строго в одни руки Жанр: средневековье! Контакты: ICQ: 606887687 Skype: lenin.zb Связь в ЛС. Качество профессиональное! Цена договорная, жду предложений!
Разрабатывается браузерная онлайн игра "Династия"
Сообщение отредактировал DasTem - Вторник, 28 Августа 2012, 12:42 |
|
| |
DasTem | Дата: Среда, 22 Августа 2012, 22:01 | Сообщение # 18 | Тема: Онлайн конвертер Unity JS -> C# |
почетный гость
Сейчас нет на сайте
| баян но спасибо)
Разрабатывается браузерная онлайн игра "Династия"
Сообщение отредактировал DasTem - Среда, 22 Августа 2012, 22:02 |
|
| |
DasTem | Дата: Вторник, 21 Августа 2012, 22:17 | Сообщение # 19 | Тема: Новый Файловый хостинг |
почетный гость
Сейчас нет на сайте
| jiumx60rus, нет, ты заливаешь файл и чем больше его скачают тем больше ты получишь
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
DasTem | Дата: Вторник, 21 Августа 2012, 22:11 | Сообщение # 20 | Тема: Новый Файловый хостинг |
почетный гость
Сейчас нет на сайте
| ЭтоЧудоИзЧудес, ну этот тоже норм, быстрый. Дизайн сменю и добавлю плату за скачивание!
Разрабатывается браузерная онлайн игра "Династия"
|
|
| |
|