📜 ⬆️ ⬇️

Creating a simple 2D game on Android

Good day to all!

When I wrote this "game" I had a lot of questions about the looping of sprites so that they would appear after a certain time, there were also problems with detecting collisions of two sprites and more, I want to highlight all these questions today in this post since On the Internet, I did not find a normal answer to my questions and had to do it myself. Fasting does not pretend to anything, I am new to the development of games for android and I write for newcomers to the industry. To whom it became interesting I ask under kat.

Formulation of the problem:


The game must be a field (scene) on which the ninja and ghosts are located. A ninja must protect his base from these ghosts by shooting at them.

An example of such a game can be viewed in the android market . Although I strongly swung, we will only have a similar idea.
')
Here is what the game will look like:
image

Start of development


Create a project. Run Eclipse - File - Android Project - Defens - Main.java.

Open our Main.java file and change all the code to the code below:

Main.java
public class Main extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //  ,       setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //  ,     getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); //    requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(new GameView(this)); } } 


The code below tells our main function that we need to run not the * .xml theme file, but the class that we own is the scene itself.
 setContentView(new GameView(this)); 


Next you need to create a GameView.java class that will serve as the main class for us to draw all the objects. Also in this class there will be our stream in which the drawing of objects in the stream will be processed to reduce the load of the game on the processor. Here's what the class will look like when nothing happens on stage:

GameView.java
 import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; import towe.def.GameView.GameThread; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; public class GameView extends SurfaceView { /**  GameLoopThread*/ private GameThread mThread; public int shotX; public int shotY; /**   */ private boolean running = false; //-------------Start of GameThread--------------------------------------------------\\ public class GameThread extends Thread { /** */ private GameView view; /** */ public GameThread(GameView view) { this.view = view; } /**  */ public void setRunning(boolean run) { running = run; } /** ,    */ public void run() { while (running) { Canvas canvas = null; try { //  Canvas- canvas = view.getHolder().lockCanvas(); synchronized (view.getHolder()) { //   onDraw(canvas); } } catch (Exception e) { } finally { if (canvas != null) { view.getHolder().unlockCanvasAndPost(canvas); } } } } } //-------------End of GameThread--------------------------------------------------\\ public GameView(Context context) { super(context); mThread = new GameThread(this); /*       */ getHolder().addCallback(new SurfaceHolder.Callback() { /***    */ public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; mThread.setRunning(false); while (retry) { try { //    mThread.join(); retry = false; } catch (InterruptedException e) { } } } /**    */ public void surfaceCreated(SurfaceHolder holder) { mThread.setRunning(true); mThread.start(); } /**    */ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } }); } /**     */ protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); } } 


From the comments I hope it is clear what function is doing. This class is basic for this, in it we will perform all the actions (functions) that will occur in the game, but for the beginning we need to make several more classes. Proceed to the next item - creating sprites.

Create sprites


Sprites are small pictures in 2D games that move. It can be men, ammunition or even clouds. In this game we will have three different types of sprites: Ninja image ghost image and projectile image .

Now we will use non-animated sprites, but in the future I will insert sprites into the project, if I’m pulling to learn how to make sprites, I’ll ask you in the second lesson on creating a game for android.

Now upload these pictures to the res / drawable folder so that Eclipse can see these pictures and paste them into your project.

The following figure should visually help to understand how the player will be located on the screen.
image
A boring picture ... Let's better create this player himself.

We need to place the sprite on the screen, how to do it? Create a class Player.java and write the following into it:

Player.java
 import android.graphics.Bitmap; import android.graphics.Canvas; public class Player { /**  */ GameView gameView; // Bitmap bmp; //     int x; int y; // public Player(GameView gameView, Bitmap bmp) { this.gameView = gameView; this.bmp = bmp; //  this.x = 0; //    this.y = gameView.getHeight() / 2; //   } //   public void onDraw(Canvas c) { c.drawBitmap(bmp, x, y, null); } } 


Everything is very simple and clear, our player will stand still and do nothing except how to shoot at the enemy, but the shooting will be implemented in a bullet class (shell), which we will do next.

Create another class file and call it Bullet.java, this class will determine the coordinates of the flight, flight speed and other parameters of the bullet. And so, we created the file, and write the following to it:

Bullet.java
 import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; public class Bullet { /***/ private Bitmap bmp; /***/ public int x; public int y; /**  =15*/ private int mSpeed=25; public double angle; /***/ public int width; /***/ public int height; public GameView gameView; /***/ public Bullet(GameView gameView, Bitmap bmp) { this.gameView=gameView; this.bmp=bmp; this.x = 0; //   this.y = 120; //   this.width = 27; //  this.height = 40; //  //          angle = Math.atan((double)(y - gameView.shotY) / (x - gameView.shotX)); } /** ,  */ private void update() { x += mSpeed * Math.cos(angle); //     mSpeed     angle y += mSpeed * Math.sin(angle); //    -//- } /**  */ public void onDraw(Canvas canvas) { update(); //          canvas.drawBitmap(bmp, x, y, null); } } 


From the comments it should be clear that the bullet performs only one action - it must fly in the direction indicated by the player.

We draw sprites on stage


In order to draw these two classes that we have created, we need to edit the code in the GameView.java class, add a few methods that will return our drawings to us. I will not write all the code completely; I will only give the code of the methods I need.

To begin with, we need to create objects of the Bullet and Player classes in order to display them on the screen, for this we create a list of bullets, so that they never end with us, and an ordinary player class object.

GameView Cap
 private List<Bullet> ball = new ArrayList<Bullet>(); private Player player; Bitmap players; 


Next, we need to assign pictures to our classes, find the GameView constructor and insert two lines at the very end:

GameView.java - GameView Designer
 players= BitmapFactory.decodeResource(getResources(), R.drawable.player2); player= new Player(this, guns); 


And in the onDraw method (Canvas c); make these sprites visible. We pass through the entire collection of our items generated in the list.

GameView, java
  /**     */ protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); Iterator<Bullet> j = ball.iterator(); while(j.hasNext()) { Bullet b = j.next(); if(bx >= 1000 || bx <= 1000) { b.onDraw(canvas); } else { j.remove(); } } canvas.drawBitmap(guns, 5, 120, null); } 


And in order for the bullets to start flying out when you click on the screen, you need to create the createSprites () method; which will return our sprite.

GameView.java
  public Bullet createSprite(int resouce) { Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce); return new Bullet(this, bmp); } 


Well, in the end we create another method - onTouch (); which will actually catch all the touches on the screen and direct the bullet to the point where the screen was tapped.

GameView.java
 public boolean onTouchEvent(MotionEvent e) { shotX = (int) e.getX(); shotY = (int) e.getY(); if(e.getAction() == MotionEvent.ACTION_DOWN) ball.add(createSprite(R.drawable.bullet)); return true; } 


If you want to do so that pressing would not be processed once, i.e. 1 click - 1 bullet, and 1 click - and until you release it will shoot, you need to delete if (e.getAction () == MotionEvent.ACTION_DOWN) {}
and leave only ball.add (createSprite (R.drawable.bullet)); .

Everything, we start our game and we try to shoot. It should go like this:


The enemies


In order for us not to be bored playing, you need to create enemies. To do this, we will have to create another class that will be called Enemy.java and which will be able to display and direct our enemy to our base. The class is pretty simple so look at the code below:

Enemy.java
 import java.util.Random; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; public class Enemy { /**   */ public int x; public int y; /***/ public int speed; /**   */ public int width; public int height; public GameView gameView; public Bitmap bmp; /** */ public Enemy(GameView gameView, Bitmap bmp){ this.gameView = gameView; this.bmp = bmp; Random rnd = new Random(); this.x = 900; this.y = rnd.nextInt(300); this.speed = rnd.nextInt(10); this.width = 9; this.height = 8; } public void update(){ x -= speed; } public void onDraw(Canvas c){ update(); c.drawBitmap(bmp, x, y, null); } } 


And so what happens in this class? I tell you: we have declared vital variables for our enemy, height, width and coordinates. To place them on the stage, I used the Random () class so that when they appear on the scene, they appear at all at one point, and at different points and at different coordinates. The speed is also random in our country, so that each enemy goes at a different speed, our speed starts from 0 and ends at 10, 10 - the maximum speed of which the enemy can reach. They will move from the right to the left, so that they are not immediately visible on the stage, I threw them at 900 pixels for the screen visibility. So until they reach it will be possible to prepare in full for the attack.

Next, we need to display the enemy on the scene, for this in the GameView.java class we do the following:

Create a list of enemies so that they never end and create a bitmap that will contain a sprite:

GameView Cap
 private List<Enemy> enemy = new ArrayList<Enemy>(); Bitmap enemies; 


Next, create a new stream to set the speed of the appearance of enemies on the screen:

GameView Cap
 private Thread thred = new Thread(this); 


And implement the Runuble class, here’s what the initialization of the GameView class should look like:
 public class GameView extends SurfaceView implements Runnable 


Now you need to create eclips to create a run () method, create it, it will look like this:

At the very bottom of the GameView class
 public void run() { while(true) { Random rnd = new Random(); try { Thread.sleep(rnd.nextInt(2000)); enemy.add(new Enemy(this, enemies)); } catch (InterruptedException e) { e.printStackTrace(); } } } 

Here we create a stream that will create a sprite from 0 to 2000 milliseconds or every 0, 1 or 2 seconds.

Now in the constructor we write at the very end we initialize our sprite with a class for display on the stage:

GameView Constructor
 enemies = BitmapFactory.decodeResource(getResources(), R.drawable.target); enemy.add(new Enemy(this, enemies)); 


And of course, we need to declare these methods in onDraw (); That means we write the following in it:

OnDraw () method in gameview
 Iterator<Enemy> i = enemy.iterator(); while(i.hasNext()) { Enemy e = i.next(); if(ex >= 1000 || ex <= 1000) { e.onDraw(canvas); } else { i.remove(); } } 

Again we go through the collection of enemies with the help of an iterator and check - if the enemy has gone beyond the limit of 1000 pixels - delete it, because if we do not remove our memory, it will harden and the phone will hang, but we do not need such problems. The whole game is ready for launch.

We start our game and what will we see? Here's what:


But what do I see? Oh no!!! Bullets don't kill our ghosts. What should we do? And I will tell you what to do, we need to create a method that will form a rectangle around each sprite - and will compare them on collisions. The next topic will be about this.

Collision detection


And so, we have a sprite, we have a scene, we all even move beautifully, but what is the use of all this when we have nothing on the scene except going to these sprites here?

With this function, I dug in full, even somehow it turned out that I was hysterical and went for a walk along the street)) The most difficult method, although it looks quite harmless ...

Okay, let's create this method and do not talk much ... Somewhere at the end of the GameView class, create the testCollision () method and write the following code:

At the very bottom of the GameView.java class
 /*  */ private void testCollision() { Iterator<Bullet> b = ball.iterator(); while(b.hasNext()) { Bullet balls = b.next(); Iterator<Enemy> i = enemy.iterator(); while(i.hasNext()) { Enemy enemies = i.next(); if ((Math.abs(balls.x - enemies.x) <= (balls.width + enemies.width) / 2f) && (Math.abs(balls.y - enemies.y) <= (balls.height + enemies.height) / 2f)) { i.remove(); b.remove(); } } } } 


And so, what happens with us in this method? We create one iterator and run a loop to view the entire collection of sprites, and say that every next bullet sprite will be the first.

Next, create another iterator with another list of sprites and again redefine and say that each next enemy sprite will be the first. And we create a branch operator - if () which actually checks our sprites for collisions. In it, I used a mathematical function module (abs) which returns me an absolute integer from two rectangles.

Inside the ifa, a comparison is made between two rectangles Module from (Bullet on coordinate X minus coordinate of enemy on coordinate X is less than or equal to the width of the bullet plus width of the enemy / 2 (divide by two to find the center of the rectangle)) and (Module from (Bullet on coordinate Y minus coordinate enemy Y coordinate is less than or equal to the width of the bullet plus the width of the enemy / 2 (divide by two to find the center of the rectangle)));

And at the end of all, if the bullet did reach the enemy, we remove him from the scene with the ends.

Well, in order for this function to work, we write it to the run () method that is in the GameThread class, below our onDraw () drawing method.

Here is what we get after launching the application:

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


All Articles