1. sylsau's Avatar
    Hello,

    I create that thread to present you a tutorial aiming to learn you how to create a Running Man Game Animation on Android. You can discover the tutorial in video on Youtube :



    Create a Running Man Game Animation on Android

    Making a Running Man Animation on Android is a great way to learn how to work with Bitmaps, Thread and SurfaceView. First thing to make a Running Man Game Animation is to have a character to animate. For that, we will use the following sprite sheet :

    [Tutorial] Create a Running Man Game Animation on Android-running_man.png

    Like you can see, our character sprite sheet has 8 frames. Each frame show the character in a different position when he runs.

    For our animation, we are going to create a custom GameView extending the SurfaceView class and implementing the Runnable interface. First, we need to define some properties like the game thread, the surface holder, the canvas where running man will be drawn, the bitmap used to load the sprite sheet and some parameters to customize the running man animation like the speed, the size or the position of the man on the screen :

    Code:
        class GameView extends SurfaceView implements Runnable {
    
            private Thread gameThread;
            private SurfaceHolder ourHolder;
            private volatile boolean playing;
            private Canvas canvas;
            private Bitmap bitmapRunningMan;
            private boolean isMoving;
            private float runSpeedPerSecond = 500;
            private float manXPos = 10, manYPos = 10;
            private int frameWidth = 230, frameHeight = 274;
            private int frameCount = 8;
            private int currentFrame = 0;
            private long fps;
            private long timeThisFrame;
            private long lastFrameChangeTime = 0;
            private int frameLengthInMillisecond = 50;
    
            // ...
    
        }
    To draw correctly the good frame for the running man, we need two Rectangle instances. One used to define the current frame in the sprite sheet and an other to define where to draw the current frame on the screen :

    Code:
            private Rect frameToDraw = new Rect(0, 0, frameWidth, frameHeight);
            private RectF whereToDraw = new RectF(manXPos, manYPos, 
                                          manXPos + frameWidth, frameHeight);
    On the GameView constructor, we get the surface holder and then, we load the sprite sheet into the bitmapRunningMan variable. We apply a scale transformation according values defined in frameWidth and frameHeight parameters :

    Code:
            public GameView(Context context) {
                super(context);
                ourHolder = getHolder();
                bitmapRunningMan = BitmapFactory.decodeResource(getResources(), 
                                 R.drawable.running_man);
                bitmapRunningMan = Bitmap.createScaledBitmap(bitmapRunningMan, 
                                 frameWidth * frameCount, frameHeight, false);
            }
    Now, it's time to make the event loop for animation inside the run method overrided from Runnable interface :

    Code:
     
    
            @Override
            public void run() {
                while (playing) {
                    long startFrameTime = System.currentTimeMillis();
                    update();
                    draw();
    
                    timeThisFrame = System.currentTimeMillis() - startFrameTime;
    
                    if (timeThisFrame >= 1) {
                        fps = 1000 / timeThisFrame;
                    }
                }
            }
    Note that we animate the character while the playing variable is set to true. Like usual in a game, we update the elements and then we draw before to calculate frame per seconds. The update method is just used here to move the man positions in X and Y. Note that when the man reach the end of the screen horizontally or vertically, we set its position to the left or top of the screen :

    Code:
            public void update() {
                if (isMoving) {
                    manXPos = manXPos + runSpeedPerSecond / fps;
    
                    if (manXPos > getWidth()) {
                        manYPos += (int) frameHeight;
                        manXPos = 10;
                    }
    
                    if (manYPos + frameHeight > getHeight()) {
                        manYPos = 10;
                    }
                }
            }
    Before to write the draw method, we need to define a method to manage the current frame to display for the character. We change the current frame only when he have ended the frame duration :

    Code:
            public void manageCurrentFrame() {
                long time = System.currentTimeMillis();
    
                if (isMoving) {
                    if (time > lastFrameChangeTime + frameLengthInMillisecond) {
                        lastFrameChangeTime = time;
                        currentFrame++;
    
                        if (currentFrame >= frameCount) {
                            currentFrame = 0;
                        }
                    }
                }
    
                frameToDraw.left = currentFrame * frameWidth;
                frameToDraw.right = frameToDraw.left + frameWidth;
            }
    And now, we define the draw method :

    Code:
            public void draw() {
                if (ourHolder.getSurface().isValid()) {
                    canvas = ourHolder.lockCanvas();
                    canvas.drawColor(Color.WHITE);
                    whereToDraw.set((int) manXPos, (int) manYPos, (int) manXPos 
                                       + frameWidth, (int) manYPos + frameHeight);
                    manageCurrentFrame();
                    canvas.drawBitmap(bitmapRunningMan, frameToDraw, whereToDraw, null);
                    ourHolder.unlockCanvasAndPost(canvas);
                }
            }
    First, we check if the surface is valid. Then we lock the canvas and we draw the character current frame. Last, we unlock the canvas and post it on the Surface View. Finally, we define two methods to pause or resume the running man animation :

    Code:
            public void pause() {
                playing = false;
    
                try {
                    gameThread.join();
                } catch(InterruptedException e) {
                    Log.e("ERR", "Joining Thread");
                }
            }
    
            public void resume() {
                playing = true;
                gameThread = new Thread(this);
                gameThread.start();
            }
    To start the running man animation, we're going to wait the user click on the surface view. So, we need to override the onTouchEvent method and wait for an ACTION_DOWN event. When the event is made, we have just to change the isMoving boolean value. If man is running, we stop it. If man doesn't run, we start to move it :

    Code:
            @Override
            public boolean onTouchEvent(MotionEvent event) {
                switch (event.getAction() & MotionEvent.ACTION_MASK) {
                    case MotionEvent.ACTION_DOWN :
                        isMoving = !isMoving;
                        break;
                }
    
                return true;
            }
    Last thing to make is to assemble all the pieces of the puzzle, create the game view on the main activity, set it as the content view and then resume or pause the game animation when the activity is resumed or paused :

    Code:
    package com.ssaurel.runningman;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    
    public class RunningManAnimation extends AppCompatActivity {
    
        private GameView gameView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            gameView = new GameView(this);
            setContentView(gameView);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            gameView.resume();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            gameView.pause();
        }
    
        class GameView extends SurfaceView implements Runnable {
    
            private Thread gameThread;
            private SurfaceHolder ourHolder;
            private volatile boolean playing;
            private Canvas canvas;
            private Bitmap bitmapRunningMan;
            private boolean isMoving;
            private float runSpeedPerSecond = 500;
            private float manXPos = 10, manYPos = 10;
            private int frameWidth = 230, frameHeight = 274;
            private int frameCount = 8;
            private int currentFrame = 0;
            private long fps;
            private long timeThisFrame;
            private long lastFrameChangeTime = 0;
            private int frameLengthInMillisecond = 50;
    
            private Rect frameToDraw = new Rect(0, 0, frameWidth, frameHeight);
    
            private RectF whereToDraw = new RectF(manXPos, manYPos, manXPos + frameWidth, frameHeight);
    
            public GameView(Context context) {
                super(context);
                ourHolder = getHolder();
                bitmapRunningMan = BitmapFactory.decodeResource(getResources(), R.drawable.running_man);
                bitmapRunningMan = Bitmap.createScaledBitmap(bitmapRunningMan, frameWidth * frameCount, frameHeight, false);
            }
    
            @Override
            public void run() {
                while (playing) {
                    long startFrameTime = System.currentTimeMillis();
                    update();
                    draw();
    
                    timeThisFrame = System.currentTimeMillis() - startFrameTime;
    
                    if (timeThisFrame >= 1) {
                        fps = 1000 / timeThisFrame;
                    }
                }
            }
    
            public void update() {
                if (isMoving) {
                    manXPos = manXPos + runSpeedPerSecond / fps;
    
                    if (manXPos > getWidth()) {
                        manYPos += (int) frameHeight;
                        manXPos = 10;
                    }
    
                    if (manYPos + frameHeight > getHeight()) {
                        manYPos = 10;
                    }
                }
            }
    
            public void manageCurrentFrame() {
                long time = System.currentTimeMillis();
    
                if (isMoving) {
                    if (time > lastFrameChangeTime + frameLengthInMillisecond) {
                        lastFrameChangeTime = time;
                        currentFrame++;
    
                        if (currentFrame >= frameCount) {
                            currentFrame = 0;
                        }
                    }
                }
    
                frameToDraw.left = currentFrame * frameWidth;
                frameToDraw.right = frameToDraw.left + frameWidth;
            }
    
            public void draw() {
                if (ourHolder.getSurface().isValid()) {
                    canvas = ourHolder.lockCanvas();
                    canvas.drawColor(Color.WHITE);
                    whereToDraw.set((int) manXPos, (int) manYPos, (int) manXPos + frameWidth, (int) manYPos + frameHeight);
                    manageCurrentFrame();
                    canvas.drawBitmap(bitmapRunningMan, frameToDraw, whereToDraw, null);
                    ourHolder.unlockCanvasAndPost(canvas);
                }
            }
    
            public void pause() {
                playing = false;
    
                try {
                    gameThread.join();
                } catch(InterruptedException e) {
                    Log.e("ERR", "Joining Thread");
                }
            }
    
            public void resume() {
                playing = true;
                gameThread = new Thread(this);
                gameThread.start();
            }
    
            @Override
            public boolean onTouchEvent(MotionEvent event) {
                switch (event.getAction() & MotionEvent.ACTION_MASK) {
                    case MotionEvent.ACTION_DOWN :
                        isMoving = !isMoving;
                        break;
                }
    
                return true;
            }
        }
    }
    Now, you have just to run the application on your Android emulator or on your real device and to enjoy your Running Man Game Animation.

    Don't hesitate to try this code and give me your feedbacks.

    Thanks.

    Sylvain
    10-03-2016 03:48 AM
  2. dpham00's Avatar
    Thanks for sharing
    10-03-2016 07:15 AM

Similar Threads

  1. [GAME] [FREE] Find The differences puzzle game
    By KaloyanBeshev in forum Android Games
    Replies: 2
    Last Post: 10-17-2016, 03:53 PM
  2. Outlook contact notes on Android
    By AC Question in forum Ask a Question
    Replies: 1
    Last Post: 10-03-2016, 11:42 AM
  3. How to turn off the reminder on notafaction
    By AC Question in forum Ask a Question
    Replies: 1
    Last Post: 10-03-2016, 01:41 AM
  4. Replies: 1
    Last Post: 10-03-2016, 01:36 AM
  5. why is my samsung gt s7262 auto on and of
    By AC Question in forum Ask a Question
    Replies: 1
    Last Post: 10-03-2016, 01:32 AM

Tags for this Thread

LINK TO POST COPIED TO CLIPBOARD