Qook叀いおもちゃをAndroidに移怍しお䞖界䞭の人ず共有する

KDPV

実際、私は論理玩具が倧奜きです。 いいえ、「3列に䞊んでいる」、「類䌌したものを芋぀ける」、および「犬に逌をやる」など、あたり興味がありたせん。 しかし、本圓に耇雑な仕掛けが数週間萜ち着いお匕き出される可胜性がありたす。 これは、たったく新しいSony携垯電話が手に入った2004幎に私に起こったこずずたったく同じです。 このT68Iが優れた電話をかけ、カラヌ写真を衚瀺し、BTを介しお連絡先を送信する機胜さえありたすが、気づかなかったのです。 しかし、Q-いいえ。 そしお、䜕時間私は小さなディスプレむの埌ろに座っお、必死にボヌルを前埌に远いかけたしたが、もう芚えおいたせん。 しかし、私は非垞によく芚えおいたす。最新のプラットフォヌムのいずれかにこのゲヌムの移怍版を曞くずいう考えは、最初のHello World以来、私を行かせたせんでした。 確かに、叀き良き時代の少なくずも䜕らかの皮類のゲヌム゚ンゞンをリベットしようずする私の詊みはすべおクラッシュしたした...䞀般に、圌らは䜕かに぀いおクラッシュしたした。 しかし、私は長い間、しっかりずJavaで、そしおAndroidのごく最近のずきから曞いたので、おもちゃの移怍ずいうアむデアが぀いに実珟する機䌚を芋぀けたした。 それが䜕であり、どのように生たれたのかを芋たいですか その埌-猫の䞋。

ゲヌムの意味は䜕ですか


Qは非垞に耇雑なロゞックゲヌムであり、その意味は、競技堎のすべおの色のボヌルを同じ色の穎に転がすこずです。 たるでビリダヌドのようです。 たた、ビリダヌドの堎合ず同様に、ボヌルは盎線でのみ移動し、最初の障害物固定レンガたたは別のボヌルのいずれかたでしか移動したせん。 同時に、すべおのレベルは最も単玔な解決策を排陀するような方法で構築されおいたす-これが矎しさです。

ただわからない さお、ここに最初のレベルの写真がありたす。 ちなみに、あなたのレゞャヌで、このたさにレベルを枡す方法を考え出しおください。



それは良いですか それからそれを曞く方法を理解したしょう。

䜕を曞いおいるの


私が最初に思い぀いたのは、䜕らかの物理゚ンゞンを䜿甚するこずでした。 たずえば、Unity。 しかし、そのようなおもちゃの重量ずバッテリヌの消費量を調べたずころ 、フィヌルド党䜓でボヌルを矎しく転がすためだけに゚ンゞン党䜓を䜿甚するずいうアむデアはすぐになくなりたした。 しかし、このゲヌム専甚の小さな゚ンゞンを曞くずいうアむデアが浮かびたした。特に子䟛の頃には埗られなかったのはこの郚分だったからです。 そこで、ボヌルず䜎消費電力で自転車を再発明したす。 ずころで、AndroidからJavaで発明したす。 行こう

ゲヌム芁玠を遞択する


これは、䜕かのコヌドを蚘述するずきに最初に行うこずです。 私たちが持っおいるものを芋おみたしょう...ので、もう䞀床写真を芋おください...

うん フィヌルドには...芁玠がありたす 論理的ですか

public abstract class Item implements Serializable { private Color color; public Item(Color color) { setColor(color); } public Color getColor() { return color; } private void setColor(Color color) { this.color = color; } @Override public String toString() { return "Item{" + "color=" + color + '}'; } } 

䜕、座暙はどこですか ボヌルはどのようにしおそれがどこにあるのかを知るのですか 圌のビゞネスではありたせん。

さお、もう少し詳しく芋お、ここにどんな芁玠があるのか​​芋おみたしょう

ブロックする 圌にずっおすべおがシンプルです。圌は正方圢で、灰色で、どこにも移動したせん。 䞎えも受けもしない-あたり発展しおいない囜の経枈。 確かに、ブロックが芁玠であるこずを忘れおはなりたせん。

 public class Block extends Item { public Block() { super(Color.GRAY); } } 

ボヌル 。 ボヌルはもう少し耇雑です。䞞く、倚色で、垞にどこかで転がりたす。 たた、芁玠。

 public class Ball extends Item { public Ball(Color color) { super(color); } } 

穎 。 たあ、たたはポケット-あなたが䞀番奜きなように。 ボヌルずブロックの間に䜕かがありたす。それは四角くお動きがないように芋えたすが、マルチカラヌでもありたす。

 public class Hole extends Item { public Hole(Color color) { super(color); } } 

そのため、すでに基本的な芁玠を把握しおいたす。 さお、圌らがどこにあるのかを考えおください

ラむティングレベル


レベル自䜓に぀いおは少し埌で説明したすが、珟時点では、フィヌルド内の芁玠の䜍眮を担圓するクラスが必芁です。なぜなら、あなたず私はそれら自䜓が色ず名前しか知らないず決めたからです。

 public class Level implements Serializable { private Item[][] field; private int ballsCount; public Level(Item[][] field) { this.field = field; } } 

さお、初めは良いです。 䜕らかの皮類の芁玠の2次元配列を栌玍するレベルがありたす。 すべおのボヌル、ブロック、穎に芁玠があるので、可胜です。 毎回フィヌルドに残っおいるボヌルの数をカりントしないために、2番目の倉数が必芁です。 ただし、正盎なずころ、このビゞネスを数える必芁がありたす

 private int countBallsOnLevel(Item[][] field) { int ballsCount = 0; for (Item[] aField : field) { for (int j = 0; j < field[0].length; j++) { if (aField[j] != null && aField[j].getClass().equals(Ball.class)) { ballsCount++; } } } return ballsCount; } 

二次の耇雑さ、ええ。 そのため、次の移動埌にこの倀を再集蚈したくありたせん。 さお、コンストラクタに1行远加したす

 this.ballsCount = countBallsOnLevel(field); 

これで、レベルの準備ができたした。 今、蚈画は最も興味深いです

゚ンゞンを曞く


すべおのゲヌムメカニックに特別なクラスを甚意しおください。 さお、たずえば、フィヌルドには、可倉レベルの構成ず、フィヌルドに残っおいるボヌルの数が栌玍されたす

 private Level level; private int ballsCount; public Field(Level level) { this.level = level; this.ballsCount = level.getBallsCount(); } 

玠晎らしい。 ゚ンゞンから少し泚意をそらし、小さな列挙型を曞きたす

 public enum Direction { LEFT, RIGHT, UP, DOWN, NOWHERE } 

ええ、ボヌルが動いおいる方向。 もう䞀床話を進めお、フィヌルド䞊の目的の芁玠の座暙を保存する非垞に小さな叀兞を曞きたしょう。 なんで そしお、以䞋を曞くために

 public class Coordinates { private int horizontal; private int vertical; public Coordinates(int horizontal, int vertical) { this.horizontal = horizontal; this.vertical = vertical; } } 

やっず、゚ンゞンに戻っお過劎を続けるこずができたす。

あなたが最初にしたいこずは、ボヌルを動かすために私たちのフィヌルドを教えるこずです。

 private Coordinates moveRight(int xCoord, int yCoord) { try { while (level.getField()[yCoord][xCoord + 1] == null) { level.getField()[yCoord][xCoord + 1] = level.getField()[yCoord][xCoord]; level.getField()[yCoord][xCoord++] = null; } } catch (ArrayIndexOutOfBoundsException ex) { } return new Coordinates(xCoord, yCoord); } 

たずえば、この方法は、ある皋床の障害物ができるたでボヌルを転がしたす。 さお、たたはフィヌルドが終了するたで。 ちなみに、これはおそらく䟋倖の抑制が䜕らかの圢で正圓化される唯䞀のケヌスです。

これほど耇雑なこずはありたせん-ボヌルを巊、䞊、䞋に動かす他の方法が曞かれおいたす。 これらのメ゜ッドをどこか䞊のレベルで呌び出す方法を孊ぶ必芁がありたす。

 private Coordinates moveItem(Coordinates coordinates, Direction direction) { int horizontal = coordinates.getHorizontal(); int vertical = coordinates.getVertical(); if (direction.equals(Direction.NOWHERE) || level.getField()[vertical][horizontal] == null) { return null; } Class clazz = level.getField()[vertical][horizontal].getClass(); if (!clazz.equals(Ball.class)) { return null; } switch (direction) { case RIGHT: return moveRight(horizontal, vertical); case LEFT: return moveLeft(horizontal, vertical); case UP: return moveUp(horizontal, vertical); case DOWN: return moveDown(horizontal, vertical); } return null; } 

さお、ここで私たちの座暙は䟿利です。 曞く量を枛らすように蚀った。

だから、倚かれ少なかれ乗るこずを孊んだ。 今、私たちはロヌルするこずを孊びたす。 すべおが同じで、メ゜ッドのみが操䜜の結果を返したす-ボヌルを食べたかどうか

 private boolean acceptRight(Coordinates coordinates) { try { int horizontal = coordinates.getHorizontal(); int vertical = coordinates.getVertical(); Item upItem = level.getField()[vertical][horizontal + 1]; Item item = level.getField()[vertical][horizontal]; if (upItem == null || !upItem.getClass().equals(Hole.class) || !(upItem.getColor().equals(item.getColor()))) { return false; } level.getField()[vertical][horizontal] = null; } catch (ArrayIndexOutOfBoundsException ex) { } return true; } 

そしお、たったく同じラッパヌレベルアップ

 private boolean acceptHole(Coordinates coordinates, Direction direction) { boolean isAccepted = false; switch (direction) { case UP: isAccepted = acceptUp(coordinates); break; case DOWN: isAccepted = acceptDown(coordinates); break; case RIGHT: isAccepted = acceptRight(coordinates); break; case LEFT: isAccepted = acceptLeft(coordinates); break; } if (!isAccepted) { return false; } catchBall(); return checkWin(); } 

ボヌルが食べられた埌でも、残りの数を数える必芁がありたす。 いいえ、ONはありたせん。

 private void catchBall() { ballsCount--; } 

なんで なぜなら、1回の移動で1぀のボヌルしか移動できないからです。これは、もはや転がるこずができないこずを意味したす。 レベルが完了したこずを確認するこずはもう難しくありたせん

 private boolean checkWin() { return ballsCount == 0; } 

さお、今私たちはフィヌルドの呚りにボヌルを転がしお転がすこずができたす。 歩く方法を孊ぶこずは残っおいる

 public boolean makeTurn(Coordinates coordinates, Direction direction) { Coordinates newCoordinates = moveItem(coordinates, direction); return newCoordinates != null && acceptHole(newCoordinates, direction); } 

新しいこずはありたせん。方向が刀明した堎合は方向ず座暙を取り、ボヌルが芋぀かった堎合は、ボヌルを新しい堎所に移動し、穎に抌し蟌みたした。 芋぀かった堎合、trueを返したす。

さお、それが゚ンゞン党䜓です。 このため、ここで団結しがみ぀く䟡倀はありたしたか

次に、画面にすべおを衚瀺するように電話を教える必芁がありたす。

ビュヌを曞く


Androidのアプリケヌションむンタヌフェむスの䞻芁な芁玠はViewです。 ビュヌ、぀たり。 これはボタンであり、入力フィヌルドであり、...私たちの競技堎です。 確かに、誰かが私たちのためにすでにそれを曞いおいるこずを願うのは奇劙です。 だからあなたは自分でそれをしなければなりたせん。 これを行うには、クラス党䜓を䜜成し、Androidのビルトむンビュヌから継承しお、そのラむフサむクル、このビゞネスを画面に配眮する機胜などにアクセスしたす。

 public class FieldView extends View { private final double ROUND_RECT_SIZE = 0.15; private final int PADDING_DIVIDER = 4; int paddingSize = 0; private int elementSize; private Field field; private Size fieldSize; private Size maxViewSize; public FieldView(Context context, AttributeSet attrs) { super(context, attrs); } } 


ここで定数が必芁な理由は埌で説明したすが、ここでは、ビュヌの倧きさに぀いお考えおみたしょう。 画面䞊で可胜な限り倚くのスペヌスを占有する必芁があるこずは明らかですが、そこからクロヌルしないでください。 たた、芁玠のサむズはビュヌ自䜓のサむズに比䟋する必芁があるこずは明らかです。 同時に、䜕かを䞀定に蚭定するこずはできたせん。数千台の異なる携垯電話の䞋で独自の芋解を曞くこずはできたせん。 ただし、画面に配眮するずきに、ビュヌで䜕かを行うこずができたす。 XMLマヌクアップにmath_parentディメンションがあるため、このサむズのランタむムを決定できたす。

 public Size countFieldSize() { if (maxViewSize == null) { maxViewSize = new Size(this.getWidth(), this.getHeight()); } int horizontalElementsNum = field.getField()[0].length; int verticalElementsNum = field.getField().length; int maxHorizontalElSize = maxViewSize.getWidth() / horizontalElementsNum; int maxVerticalElSize = maxViewSize.getHeight() / verticalElementsNum; this.elementSize = (maxHorizontalElSize < maxVerticalElSize) ? maxHorizontalElSize : maxVerticalElSize; int newWidth = this.elementSize * horizontalElementsNum; int newHeight = this.elementSize * verticalElementsNum; return new Size(newWidth, newHeight); } 

サむズは座暙ずほが同じで、OxずOyに応じたサむズを保存するためにのみ必芁です。 アルゎリズムは簡単です誰かがこれらのサむズを決定しおいるかどうかを調べ、ピクセル単䜍で高さず幅を取埗し、1぀の芁玠が氎平および垂盎に占める量を蚈算し、小さい芁玠を遞択し、芁玠サむズにその数を掛けおビュヌ自䜓のサむズを再蚈算したした行ず列で。

そしお、たあ、このこずを呌び出すこずを忘れないでください

 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Size countedFieldSize = countFieldSize(); if (fieldSize == null || !fieldSize.equals(countedFieldSize)) { this.fieldSize = countedFieldSize; setFieldSize(this.fieldSize); paddingSize = (int) (Math.sqrt(elementSize) / PADDING_DIVIDER); } } 

setFieldSizeは䜕をしたすか はい、お願いしたす

 public void setFieldSize(Size size) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(size.getWidth(), size.getHeight()); params.gravity = Gravity.CENTER_HORIZONTAL; this.setLayoutParams(params); } 

圌らは眺めを取り、それに寞法を付けたした。 䜕が欲しかった

したがっお、サむズを決定したした。 ここで、䜕らかの圢で競技堎を描く必芁がありたす。 耇雑ではなく、onDrawで行われたす。 確かに、䜕かを描く前に、ゲヌム芁玠自䜓をどこかに芋぀ける必芁がありたす。

描く


私が最初に思い぀いたのは、たくさんのマヌクアップファむルをドロアブルで取埗し、それらを座暙でキャンバスに配眮するこずでした。 残念ながら、この玠晎らしいアむデアは、芁玠の盞察的なサむズを蚭定するこずが䞍可胜であるこずを打ち砎りたした。 ぀たり、ブロックの角を䞞くしお、dpに蚭定できたす。 そしお、それらは実際に䞞められたす。 唯䞀の問題は、フィヌルド䞊の同じ芁玠の数に応じお、芁玠のサむズが異なるこずです。 たた、フィヌルドが6 * 6ゲヌムの最小サむズの堎合、ブロックはわずかに䞞みを垯びた正方圢になりたす。 そしお、私たちが持っおいるフィヌルドがすでに13 * 13最倧サむズである堎合-それはわずかに正方圢のボヌルになりたす。 glyい。
ただし、drawRectのような䜕らかの䜎レベルの描画に煩わされるよりも、既成の芁玠でキャンバスに描画するずいうアむデアが奜きです。 芁玠をたくさんやっおみたしょうか

Drawableを生成するための別のメ゜ッドがありたす䜕らかの理由で別のファクトリヌに移動したかったのですがselectDrawableは、芁玠のむンスタンスを取埗し、それが誰であるかを芋぀けお、それを描画可胜にしたす。 たずえば、ブロックは次のように描画されたす。

 Class clazz = item.getClass(); Color color = item.getColor(); if (clazz.equals(Block.class)) { GradientDrawable bgShape = new GradientDrawable(); bgShape.setColor(ContextCompat.getColor(getContext(), R.color.gray)); bgShape.setCornerRadius((float) (elementSize * ROUND_RECT_SIZE)); return bgShape; } 

さお、定数が圹に立ちたした。 これで、䞞み付け半埄は芁玠自䜓のサむズに䟝存したす。 たさに私たちが望んだもの。

次に、ボヌル甚のドロアブルがどのように構築されおいるかを芋おみたしょう。

 if (clazz.equals(Ball.class)) { GradientDrawable bgShape = new GradientDrawable(); bgShape.setColor(ContextCompat.getColor(getContext(), R.color.gray)); bgShape.setCornerRadius(elementSize); switch (color) { case GREEN: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.green)); return bgShape; case RED: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.red)); return bgShape; case BLUE: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.blue)); return bgShape; case YELLOW: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.yellow)); return bgShape; case PURPLE: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.purple)); return bgShape; case CYAN: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.cyan)); return bgShape; } } 

はい、それほど耇雑ではありたせん。 最初に圌らはボヌルを描き、それから圌らは必芁なペンキでそれを泚いだ。 なぜスむッチがあり、ボヌルから出たものだけで色を蚭定できないのですか
これらは異なる色だからです。 芁玠に栌玍される色はJavaからの通垞の列挙型であり、色ずしおdrawableを受け入れるのは、通垞の文字列倀を持぀通垞のAndroidリ゜ヌスです。 たずえば、これは少し赀いものです

 <color name="red">#D81B60</color> 

あるものを他のものず接着するのは悪い考えです。ある日、赀では青が足りず、フォント党般をいじっお、リ゜ヌスファむルを修正するのではなく、党䜓を曞き盎さなければならないず思い浮かぶでしょう。

さお、初心者向け-穎からドロアブルを構築したす。

 if (clazz.equals(Hole.class)) { GradientDrawable bgShape = new GradientDrawable(); bgShape.setCornerRadius((float) (elementSize * ROUND_RECT_SIZE)); switch (color) { case GREEN: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.green)); return bgShape; case RED: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.red)); return bgShape; case BLUE: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.blue)); return bgShape; case YELLOW: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.yellow)); return bgShape; case PURPLE: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.purple)); return bgShape; case CYAN: bgShape.setColor(ContextCompat.getColor(getContext(), R.color.cyan)); return bgShape; } } 

繰り返したすが、新しいものは䜕もありたせん。穎を描いお、塗装し、サプリカントに枡したした。

それで、あなたは䜕かを忘れたしたか うヌん...穎、ボヌル、ブロック...そしお空の堎所 たずえば、配列でnullが怜出されるずどうなりたすか

 if (item == null) { GradientDrawable bgShape = new GradientDrawable(); bgShape.setColor(ContextCompat.getColor(getContext(), android.R.color.transparent)); bgShape.setCornerRadius((float) (elementSize * ROUND_RECT_SIZE)); return bgShape; } 

はい、これはたったく同じ矎しい䞞い正方圢であるため、新しいものは䜕もありたせん。 すみたせん、ただ芋えたせん。

完了、芁玠を構築できたす。 そこで䜕を止めたしたか ああ...はい それらを描く方法に぀いお

 @Override protected void onDraw(Canvas canvas) { if (field == null) { return; } for (int i = 0; i < field.getField().length; i++) { for (int j = 0; j < field.getField()[0].length; j++) { Drawable d = selectDrawable(field.getField()[i][j]); d.setBounds(j * elementSize + paddingSize, i * elementSize + paddingSize, (j + 1) * elementSize - paddingSize, (i + 1) * elementSize - paddingSize); d.draw(canvas); } } } 


よくここに。 フィヌルドを歩き回り、各芁玠に぀いお、そのグラフィック衚珟を芋぀け、そのサむズずむンデントを盞互に蚭定し、キャンバスに描画したす。 ちなみに、ここではキャンバス䞊に描画するドロヌアブルであり、キャンバス自䜓にドロりアブルを描画しないのは興味深いこずです。 これを行うには、毎回ドロアブルをビットマップに倉換する必芁があり、これは長い時間です。

䜕が起こったのか芋おみたしょうか これを行うには、コンストラクタヌで芁玠が盎接蚭定されるテストレベルを䜜成したす削陀しお削陀したすので、心配しないでください

それで、曞かない方がいい
 public class Level { private Item[][] field; public Item[][] getField() { return field; } public Level() { field = new Item[6][6]; field[0][0] = new Block(); field[0][1] = new Block(); field[0][2] = new Hole(Color.RED); field[0][3] = new Block(); field[0][4] = new Block(); field[0][5] = new Block(); field[1][0] = new Block(); field[1][1] = new Ball(Color.RED); field[1][2] = new Ball(Color.GREEN); field[1][3] = new Ball(Color.YELLOW); field[1][4] = new Ball(Color.CYAN); field[1][5] = new Block(); field[2][0] = new Block(); field[2][1] = new Hole(Color.GREEN); field[2][2] = new Hole(Color.YELLOW); field[2][3] = new Hole(Color.PURPLE); field[2][4] = new Hole(Color.CYAN); field[2][5] = new Hole(Color.BLUE); field[3][0] = new Block(); field[3][1] = new Ball(Color.PURPLE); field[3][5] = new Block(); field[4][0] = new Block(); field[4][1] = new Block(); field[4][3] = new Ball(Color.BLUE); field[4][5] = new Block(); field[5][1] = new Block(); field[5][2] = new Block(); field[5][3] = new Block(); field[5][4] = new Block(); } } 


そしお今、私たちは自分の芋解をある皮の掻動に結び付けお、このビゞネスを始めたす



最埌に、それは䜕かを瀺しおいたす

そしお今、そのような矎しい写真に觊発されお、私たちは察話性の私たちの芋解を教えたす

ボヌルを打ちたす


芁玠を移動する方法を知っおいる゚ンゞンが既にあるので、ビュヌず䜕らかの圢で盞互䜜甚する適切なメ゜ッドを呌び出す方法を芋぀けるこずしかできたせん。

さたざたな方法で競技堎ず察話したす。 ナヌザヌがたったく気に入らない堎合は、元のゲヌムず同じようにコントロヌルを䜜成するこずもできたす。仮想ゞョむスティックを取り付けお、青色になるたでクリックしたす。 たた、タッチスクリヌンのネむティブゞェスチャはただ正しい方向にスワむプおよびボヌルをスワむプしおいるこずを思い出すこずができたす。 わかった、䜕をする぀もりだったの それでは行こう

䞀般に、Androidには組み蟌みのGestureManagerがありたすが、䜿甚方法がわからなかったか、テストデバむスで動䜜したしたが、䜕らかの理由で、曲線ハンドルを認識できるように起動したせんでした。出おきた。 それでは、自分で曞いおみたしょう

したがっお、ボヌルは正確に4぀の方向䞊䞋巊右に移動できたす。 確かに、これに加えお、圌らはどこにもたったく動かないかもしれたせんが、これはたったく面癜くありたせん。 したがっお、ボヌルの動きの方向を刀断するには、4぀の簡単なゞェスチャヌだけを認識する必芁がありたす。

本圓に気にするこずなく、別のメ゜ッドを曞き始めたす。

 public Direction getSwipeDirection(float downHorizontal, float upHorizontal, float downVertical, float upVertical) { float xDistance = Math.abs(upHorizontal - downHorizontal); float yDistance = Math.abs(upVertical - downVertical); double swipeLength = getSwipeLength(xDistance, yDistance); if (swipeLength < elementSize / 2) { return Direction.NOWHERE; } if (xDistance >= yDistance) { if (upHorizontal > downHorizontal) { return Direction.RIGHT; } return Direction.LEFT; } if (yDistance > xDistance) { if (upVertical > downVertical) { return Direction.DOWN; } return Direction.UP; } return Direction.DOWN; } 

方向は䞊で説明したEnumであり、他のすべおは非垞に単玔です。4぀の座暙を取埗し取埗元はただ重芁ではありたせん、垂盎方向ず氎平方向の距離を蚈算したした。 その埌、圌らは高校の幟䜕孊コヌスを思い出し、スワむプ自䜓の長さを芋぀けたした。 それが非垞に小さい堎合、ナヌザヌはそれずは䜕の関係もないず考え、䜕もしたせん。 svaypが良かった堎合、どこが良かったのかを刀断し、ナヌザヌに方向を返したす。 かっこいい 私も奜きです。

さお、悲しみでスワむプの方向を半分に決定するこずを孊んだずしたしょう。 申し蚳ありたせんが、どのボヌルをスノァむプヌルしたすか 正しくしたしょう。

したがっお、接点の座暙がありたす分離点の座暙もありたすが、それをどうしたすかそしお、これらの座暙から、芁玠を芋぀ける必芁がありたす...うヌん。

 public Coordinates getElementCoordinates(float horizontal, float vertical) { float xElCoordinate = horizontal / elementSize; float yElCoordinate = vertical / elementSize; return new Coordinates((int) xElCoordinate, (int) yElCoordinate); } 

異垞なこずは䜕もありたせん。 すべおの芁玠が同じサむズであり、サむズがわかっおいる自分で決定した堎合、およびフィヌルドのサむズをすでに蚈算しおいる堎合は、取埗および分割するだけです。 そしお、その座暙によっお芁玠を操䜜するこずぱンゞンのタスクです

これで、私たちは間違いなく、どこにいるのかを掚枬できたす。 すべおを゚ンゞンに転送し、それを鳎らすだけです。 しかし、これはビュヌのタスクではありたせん。 圌女の仕事は芋せるこずであり、いく぀かの行動を凊理するためには、断片的にたたは掻動的に必芁です。 フラグメントはたばらですが、いく぀かのアクティビティがありたす。 onTouchLictenerビュヌにハングアップしたす。

 private OnTouchListener onFieldTouchListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downHorizontal = event.getX(); downVertical = event.getY(); break; case MotionEvent.ACTION_UP: upHorizontal = event.getX(); upVertical = event.getY(); boolean isWin = fieldView.getField().makeTurn( fieldView.getElementCoordinates(downHorizontal, downVertical), fieldView.getSwipeDirection(downHorizontal, upHorizontal, downVertical, upVertical) ); } 

よくここに。 タッチするず、座暙が保存され、ディスプレむを離すず、さらに数個の座暙が取埗されたす。次に、すべおをヒヌプに収集し、ビュヌに枡したす。 ビュヌはさらにすべおを提䟛し、ブヌル倀の結果を取埗したすが、これは倚かれ少なかれではありたせんが、レベルの完了の兆候は私たちに戻りたす。 それを凊理するためだけに残り、再描画する必芁があるこずをビュヌに䌝えるこずを忘れないでください。

リスナヌに远加したす。

 fieldView.invalidate(); if (isWin) { animateView(fieldView); try { levelManager.finishLevel(); openLevel(levelManager.getCurrentLevelNumber()); } catch (GameException ex) { onMenuClick(); } } } return true; 

私たちはチェックしお、䜕かをしたした。 正確に䜕をしたか、少し埌で理解したすが、今のずころは、いじっおみたしょう。 レベルから䜙分なものをすべお削陀し、2぀のボヌルだけを残しお、そのうちの1぀をポケットに入れたす



ぞぞ。 それも動䜜したす。 次に進む

レベルを探しおいたす


よくここに。 実甚的な゚ンゞンを䜜成し、それを矎しく描画するこずを孊びたした。今では、正確に描画するこずを孊んだこずを決定するだけです。 レベルで、ええ。

私たちの目暙はゲヌムを移怍するこずであり、新しいものを曞くこずではないため、最初は自分でレベルを描画する぀もりはありたせんでした。 だから、元のQをどこかに持っお行き、よく振っお、そこから3〜4人の゜ニヌの匁護士を倖したあ、それはただ圌女のおもちゃです、出来䞊がり-完了です。

それは、元のおもちゃの怜玢がそこで終わっおいないだけです。 T68自䜓は長らく消えおおり、䜕らかの理由でネットワヌク䞊に写真がありたせんでした。2016幎に別の元のデバむスを芋぀けたした...䞀般に芋぀けるのは問題です。悲劇。

しかし、オリゞナルを探しおいたずきに、Windows 98でこのおもちゃの移怍版に偶然出くわしたした。そしお、このおもちゃのレベルがオリゞナルから取り陀かれおいるだけでなく、各シンボルがブロック、ボヌル、たたは穎のいずれかを衚す通垞のtxtファむルにもあるこずに気付いたずき、私はどれほど驚きたしたか。著者ず話をしお、䜿甚するための同意を確保した埌、私は圌らを明確な良心を持っお自分自身に連れお行き、圌らず䜕をすべきかを理解しようずしたした。

そのように、あなたは確かにtxtファむルを取り、おもちゃに入れるこずができたす。しかし、私の゚ンゞンをそれらず連携させるには、ただ詊しおみる必芁がありたす。そうでなければなりたせん。やっおみたす

ラむティングレベルマネヌゞャヌ


それは実際には矎しく聞こえたす-txtファむルから通垞レベルのオブゞェクトを䜜成し、圌らが蚀う人に䞎える別のクラス。そしお同時に、珟圚のレベルでのレベル、それらのうちのどれだけがそこにあるのか、その他の生掻の快適さを扱いたす。

マネヌゞャヌは䜕らかの状態たずえば、珟圚のレベルの番号を保持しおいるため、ある時点で、この状態自䜓が存圚しない新しいものを愚かに䜜成するず、䞍快になりたす叀いマネヌゞャヌは特に䞍快になりたすが、システムが安党に撮圱したす。害のないように、シングルトンを改善したしょう

 public class LevelManager { private static final String LEVELS_FOLDER = "levels"; private static final String LEVEL_FILE_EXTENSION = ".lev"; private static final int EMPTY_CELL = 0; private static final int BLOCK_CELL = 1; private static final int GREEN_BALL_CELL = 2; private static final int RED_BALL_CELL = 3; private static final int BLUE_BALL_CELL = 4; private static final int YELLOW_BALL_CELL = 5; private static final int PURPLE_BALL_CELL = 6; private static final int CYAN_BALL_CELL = 7; private static final int GREEN_HOLE_CELL = 22; private static final int RED_HOLE_CELL = 33; private static final int BLUE_HOLE_CELL = 44; private static final int YELLOW_HOLE_CELL = 55; private static final int PURPLE_HOLE_CELL = 66; private static final int CYAN_HOLE_CELL = 77; private static Context context; private static SharedSettingsManager sharedSettingsManager; private static LevelManager instance; private LevelManager() { } public static LevelManager build(Context currentContext) { context = currentContext; sharedSettingsManager = SharedSettingsManager.build(currentContext); if (instance == null) { instance = new LevelManager(); } return instance; } 

この定数の束は䜕ですかこれらは、凡䟋の芁玠です。実際、頑固に正盎に取られたレベルは次のように



なりたす。各桁は䜕かを意味したす。そしお、毎回ゲヌムの助けにならないようにするために、目に芋える明確な定数を導入し、それらでのみ動䜜したす。䜕らかの理由でそこにあるsharedSettingsManagerに぀いお、別の時間をお話ししたすが、今はマネヌゞャヌにレベルを開き、そこから適切なオブゞェクトを䜜成するように教え

たしょう。デヌタストリヌムからさたざたな数字や文字を取埗するために、スキャナヌがありたすので、ファむルに蚭定したす

 private Scanner openLevel(int levelNumber) throws IOException { AssetManager assetManager = context.getAssets(); InputStream inputStream = assetManager.open( LEVELS_FOLDER + "/" + String.valueOf(levelNumber) + LEVEL_FILE_EXTENSION); BufferedReader bufferedReader = new BufferedReader (new InputStreamReader(inputStream)); return new Scanner(bufferedReader); } 

はい、ほずんど忘れおいたした。私たちが持っおいるすべおのレベルはアンドロむド資産に保存されおいたすが、コンテキストを持っおいる堎合にのみ問題をなくすこずができたす。そしお、圌らは圌らの番号によっお呌び出されたす。したがっお、必芁なレベルの番号を転送するだけで、目的のファむルで動䜜する既補のスキャナヌを取埗できたす。

次に、凡䟋芁玠をアむテムに倉換したす。

 private Item convertLegendToItem(int itemLegend) { switch (itemLegend) { case EMPTY_CELL: return null; case BLOCK_CELL: return new Block(); case GREEN_BALL_CELL: return new Ball(Color.GREEN); case RED_BALL_CELL: return new Ball(Color.RED); case BLUE_BALL_CELL: return new Ball(Color.BLUE); case YELLOW_BALL_CELL: return new Ball(Color.YELLOW); case PURPLE_BALL_CELL: return new Ball(Color.PURPLE); case CYAN_BALL_CELL: return new Ball(Color.CYAN); case GREEN_HOLE_CELL: return new Hole(Color.GREEN); case RED_HOLE_CELL: return new Hole(Color.RED); case BLUE_HOLE_CELL: return new Hole(Color.BLUE); case YELLOW_HOLE_CELL: return new Hole(Color.YELLOW); case PURPLE_HOLE_CELL: return new Hole(Color.PURPLE); case CYAN_HOLE_CELL: return new Hole(Color.CYAN); } return null; } 

1぀の倧きなスむッチず耇雑なものはありたせん。

最埌に、レベル党䜓を凊理する方法を孊習したす。

 public Level getLevel(int levelNumber) throws IOException { Scanner scanner = openLevel(levelNumber); int levelWidth = scanner.nextInt(); int levelHeight = scanner.nextInt(); Item levelMatrix[][] = new Item[levelHeight][levelWidth]; for (int i = 0; i < levelHeight; i++) { for (int j = 0; j < levelWidth; j++) { levelMatrix[i][j] = convertLegendToItem(scanner.nextInt()); } } Level level = new Level(levelMatrix); sharedSettingsManager.setCurrentLevel(levelNumber); return level; } 

私たちは数を取りたした-レベルを返したした。奇跡 しかし、レベルを開くこずに加えお、ボヌルが残っおいない瞬間に「完了」する必芁もありたす。ボヌルがそこに残っおいるかどうかを刀断するのぱンゞンのタスクですが、このように突然終了したレベルを凊理したす

 public void finishLevel() { sharedSettingsManager.setCurrentLevel( sharedSettingsManager.getCurrentLevel() + 1 ); if (sharedSettingsManager.getCurrentLevel() > sharedSettingsManager.getMaxLevel()) { throw new GameException(GameExceptionCodes.INCORRECT_LEVEL); } } 

ええ、圌らはこのレベルが合栌したこずをメモし、珟圚のレベルの番号を倉曎し、すべおのレベルが終了した堎合、䟋倖を吐き出したした。甘い次に、たずえば第2レベルの



Xeでおもちゃを実行しおみおくださいそしお真実は、それが機胜するずいうこずです。次に、ナヌザヌの実瞟を保存する方法を孊習したす

別のマネヌゞャヌ


ナヌザヌが毎回すべおのレベルを匷制されるず、ナヌザヌは怒っお䞍満になりたす。そのため、開始ず終了の間にデヌタが倱われないように、珟圚のレベルず最埌に䜿甚可胜なレベルの倀を䜕らかの方法で保存する必芁がありたす。

このような単玔なものを保存するために、AndroidにはSharedSettingsがありたす。通垞、蚭定を保存するために䜿甚されたすが、私たちにずっおも機胜したす。別のマネヌゞャヌを取埗する

 public static final String LAST_LEVEL = "current_level"; public static final String MAX_LEVEL = "max_level"; public static final String WAS_RAN_BEFORE = "was_ran_before"; private static final String APP_PREFS = "qook_prefs"; public static Context context; public static SharedSettingsManager instance; SharedPreferences sharedPreferences; private SharedSettingsManager() { sharedPreferences = context.getSharedPreferences(APP_PREFS, Context.MODE_PRIVATE); } 

たた、シングルトン、ええ。私たちがどこに突入したかは決しおわかりたせんが、䜕らかの理由でスレッドセヌフ自䜓が共有蚭定を共有しおいるかどうかを調べるために、特別な欲求はありたせん。

ここで、珟圚および最倧のレベルを提䟛する方法を孊びたす。

回

 public int getMaxLevel() { return sharedPreferences.getInt(MAX_LEVEL, 1); } 

そしお2

 public int getCurrentLevel() { return sharedPreferences.getInt(LAST_LEVEL, 1); } 

今、それらを曞き戻しおみおください。2぀の別々のメ゜ッドの䞊のどこかを呌び出すこずは特に望たないため、これを実行したしょう。

 private void setMaxLevel(int maxLevel) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putInt(MAX_LEVEL, maxLevel); editor.apply(); } 


 public void setCurrentLevel(int currentLevel) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putInt(LAST_LEVEL, currentLevel); editor.apply(); if (getMaxLevel() < currentLevel) { setMaxLevel(currentLevel); } } 

さお、次のレベルの終わりに、珟圚のレベルを倉曎するだけで十分です。最埌かどうかは、マネヌゞャヌが私たちなしで刀断したす。そうでなければ、なぜ圌はここに座っおいるのですか

マヌクアップを远加



おもちゃはすでに遊んでいる、それは良いです。これ以倖は䜕もありたせん-悪いです。しかし、あなたは本圓にボタンを「最初に」抌しお、メニュヌに行き、どのレベルを芋たいのか。そのため、このよう



なダむスを競技堎の䞊に远加したす。ボタンに簡単なコヌドを远加するこずはできないため、その仕組みを説明したくありたせん。さらに、マネヌゞャヌぞの呌び出したたは別のアクティビティぞの切り替え以倖には䜕もありたせん

しかし、マヌクアップ自䜓がどのように興味深いかを瀺すこずは理にかなっおいたす。
 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/game_activity" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/background" android:gravity="center_vertical" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".ui.activities.LevelActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/level_counter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="16dp" android:paddingTop="5dp" android:text="01 / 60" android:textSize="34sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right" android:paddingBottom="10dp"> <ImageButton android:id="@+id/back_level_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:src="@drawable/menu_icon" /> <ImageButton android:id="@+id/reset_level_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:src="@drawable/restore_level" /> <ImageButton android:id="@+id/undo_step_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:src="@drawable/undo_step" /> </LinearLayout> </LinearLayout> <org.grakovne.qook.ui.views.FieldView android:id="@+id/field" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal" android:background="@drawable/field_ground" android:foregroundGravity="center" /> </LinearLayout> 


たあ、私たちはおもちゃ自䜓を理解したした。あずは、さらに2、3の画面を远加し、任意のレベルを遞択し、ランドスケヌプマヌクアップを远加しお、これを脇に眮くだけです。ささいなこず

レベルのメニュヌを曞く


このビゞネスには別の画面が関䞎したす。これは退屈以䞊に芋えたす。各レベルは小さな正方圢で、開いおいるがただ開いおいないさたざたな色のレベルで、開いおいるレベルをクリックするこずはできたせん。マヌクアップを䜜成したすか

 <TextView android:id="@+id/title_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:paddingBottom="16dp" android:text="@string/app_name" android:textAllCaps="true" android:textSize="48sp" android:textStyle="bold" /> <GridView android:id="@+id/level_grid" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="9" android:numColumns="5"> </GridView> 

芋出しず小さなグリッドビュヌは新しいものではありたせん。次に、このビュヌを䜕か䟿利なもので埋める方法を

芋぀けたす。そのためには、新しいビュヌを䜜成し、デヌタを埋め、clickListenerをアタッチしお芪に詰め蟌むアダプタヌを䜜成する必芁がありたす。このようなもの

 public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater vi; vi = LayoutInflater.from(getContext()); @SuppressLint("ViewHolder") View view = vi.inflate(R.layout.level_item, null); Integer currentLevelNumber = getItem(position); if (currentLevelNumber != null) { Button levelButton = (Button) view.findViewById(R.id.level_item_button); if (levelButton != null) { levelButton.setText(String.valueOf(currentLevelNumber)); if (position < maxOpenedLevel) { levelButton.setBackgroundResource(R.drawable.opened_level_item); levelButton.setClickable(true); levelButton.setOnClickListener(clickListener); levelButton.setId(currentLevelNumber); } else { levelButton.setBackgroundResource(R.drawable.closed_level_item); levelButton.setClickable(false); } } } return view; } 

わあすべおのレベルボタンを正方圢にするだけです。これを行うには、独自のボタン継承者を䜜成し、いく぀かの魔法を远加したす。

 public class LevelButton extends Button { @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); } } 

ぞぞ。なんずいう幅、そのような高さ。誰も気づきたせんでした。アクティビティを䜜成するずきにすべおを呌び出すだけです

 @Override public void onResume() { super.onResume(); manager = LevelManager.build(getBaseContext()); View.OnClickListener levelClick = new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getBaseContext(), LevelActivity.class); intent.putExtra(DESIRED_LEVEL, v.getId()); intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); startActivity(intent); } }; LevelGridAdapter adapter = new LevelGridAdapter(this, R.layout.level_item, getListOfLevelNumbers(), manager.getMaximalLevelNumber(), levelClick); adapter.setNotifyOnChange(false); levelGrid.setAdapter(adapter); levelGrid.setVerticalScrollBarEnabled(false); } 

ボタンをクリックしたした-レベルが開きたした。 矎人

遊ぶ


メむンメニュヌ、ヘルプ、そしおもちろん「䜜者に぀いお」をゲヌムに远加したら、ここで埗たものをプレむしおみたしょう。


私の歯から跳ね返るのは私の最初の3぀のレベルです-coli痛になりたした。しかし、それは機胜したす。䞡方のレベルが保存され、さらに3぀のアセンブリが戻るように、ディスプレむを反転しおもアプリケヌションがドロップされず、党䜓がきれいに芋えたす。

さお、このこずは機胜するので、この良さをGoogle Playに投皿し、奜きな人を芋぀けお

登録し、25ドルのGoogleを支払い、しばらく埅っお、プロゞェクトを䜜成し、デヌタフィヌルドに入力したす... 、こんなかわいいダむ

Google Playで利甚可胜

よくここに。おもちゃを曞きたした。それを通過しお、別の子䟛時代の倢が実珟したこずをマヌクするだけです。そしお今、あなたの蚱可を埗お、特に鋭い知人がその䞍圚のために私を完党に匕き裂くたで、「最埌の動きをキャンセル」ボタンの曞き蟌みを終了したす。

敬具テトリスを掗濯機GrakovNeに移怍したす

Source: https://habr.com/ru/post/J283132/


All Articles