Dylan Poon

"Whoever is trying to bring you down is already below you"

By

In-Depth Blog Post #3: Progress!

In the two weeks since last post, I have been working hard on moving my In-Depth Project forwards, and have a hefty load of content to show for my efforts. The two apps I have made in addition to the last week’s are still simple, but are moving away from ‘proof-of-concept’, and are heading towards some fun, playable, glitch-free games. I also tried to configure my screen recording software so that I could record myself coding (like I proposed initially) but had no luck. For this week, I will be describing the process of what I learned, and include videos of the final products.


Status Update:


 

Animated Movement Test:

Though this next app was a continuation of last week’s movement test app, I had to make drastic changes to the code to add in the factor of a moving animated image. I also learned that each frame in an animation is not many individual images, but rather a collection of images in a row, which is ‘cut’ up into pieces by the code.

private int frameCount = 12;

Above is the line that tells the program to split the spritesheet into 12 parts, giving us 12 frames of animation, which can be looped to create a fluid animation, and in my case, a walking cycle.

In addition to adding animations, I also figured out how to make the app landscape, by going through the AndroidManifest and adding in the line

android:screenOrientation=”landscape”

This one proof of concept app will set the scene for the rest of what I make, as movement and animations will be important in my final product.

Code at the bottom of the post


Snake Game:

Moving on from simpler apps, I decided to learn how to make something playable. Randy recommended that I try to make the classic game “snake”. After working on this project for a couple of days, I finally got the game to begin to work, as well as learned new functions regarding touching the screen. The function is not shown in the video below for gameplay purposes, but I coded the game so that touching the right side of the screen moves the direction of the snake right 90 degrees, while the other side does the opposite. This function can also be incorporated into making buttons in specific places, and not just large portions of the screen.

As always, code is at the bottom of the post!


How to Have a Beautiful Mind:


#2 to ask for clarification whenever you are unclear or in doubt about something the mentor tells you or shows you:

Me: Wait, so this snippet of code actually controls how fast the image moves across the screen, right?

Randy: Yes, just don’t change it too drastically.

After Randy taught me some more about how to position my code I asked for clarification on where and how to place snippets into the code. After hearing it for the second time, I felt much more confident that I would remember this for future projects, as well as got more information on how the function works.

#3 to support a point your mentor makes with additional facts, figures, evidence etc:

When Randy and I discussed setting values for sizes, frames, frames per second and other value related inputs, Randy mentioned that I should be sure that the input of the value is for the correct variable. I affirmed his point by intentionally switching the height and width values to prove to myself that his point was a valuable lesson, as I had previously spent a large amount of time troubleshooting something that I could have easily fixed in 2 seconds. (I forgot to check the height and widths I had set)

#5 to share a personal story that illustrates the conversation topic:

In sort of a joking manner, Randy had me try to create a sprite sheet of his dog walking for an example of how to make a sprite sheet. I did so, and came back the next week, where we applied the animation and made a somewhat silly idea into a working project, which strengthened our communication, and also made the conversions we had much more interesting.

#10 to modify an idea to make it more acceptable to yourself and to make it stronger or more practical:

While my mentor and I were messing with the code of the animation test I made, I initially thought that this was pointless fun. But after sleeping on it, i realized that messing with this code may help me in my final project, and came to realize that it is possible to borrow portions of the code, rearrange the numbers, and create a fully functioning app out of borrowed code. (Not what I am going to do, but may help if I get stuck on a problem)


~ In-Depth is well on it’s way, so I will be working hard to hone my new knowledge! (and also type faster)


Code:


 

Animation Movement Test:

MainActivity.java

package com.dpoon.animatedmovetest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainActivity extends Activity {
    GameView gameView;                                   // The view and the sprite sheet animation logic

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        gameView = new GameView(this);                        // Initialize gameView and set it as the view
        setContentView(gameView);
    }

    // Here is our implementation of GameView. It is an inner class.
    // Notice we implement runnable so we have A thread and can override the run method.
    class GameView extends SurfaceView implements Runnable {
        Thread gameThread = null;                                                     // This is our thread
        SurfaceHolder ourHolder;        // We need a SurfaceHolder when we use Paint and Canvas in a thread
        volatile boolean playing;                        // Set and unset when the game is running- or not.
        Canvas canvas;                                                       // A Canvas and a Paint object
        Paint paint;
        long fps;                                               // This variable tracks the game frame rate
        private long timeThisFrame;                               // This is used to help calculate the fps
        Bitmap bitmapBob;                                               // Declare an object of type Bitmap
        boolean isMoving = false;                                              // Bob starts off not moving
        float walkSpeedPerSecond = 250;                             // He can walk at 250 pixels per second
        float bobXPosition = 10;                                       // He starts 10 pixels from the left

        // New for the sprite sheet animation
        private int frameWidth = 90;                       // These values can be anything, as long as the
        private int frameHeight = 150;                          // ratio doesn't distort the sprite too much
        private int frameCount = 12;                       // How many frames are there on the sprite sheet?
        private int currentFrame = 0;                             // Start at the first frame - where else?
        private long lastFrameChangeTime = 0;               // What time was it when we last changed frames
        private int frameLengthInMilliseconds = 100;                     // How long should each frame last

        // A rectangle to define an area of the sprite sheet that represents 1 frame
        private Rect frameToDraw = new Rect( 0, 0, frameWidth, frameHeight);

        // A rect that defines an area of the screen on which to draw
        RectF whereToDraw = new RectF( bobXPosition, 0, bobXPosition + frameWidth, frameHeight);

        // When we initialize (call new()) gameView, this special constructor method runs
        public GameView(Context context) {
            super(context);                                // Ask the SurfaceView class to set up our object.
            ourHolder = getHolder();                                // Initialize ourHolder and paint objects
            paint = new Paint();
            bitmapBob = BitmapFactory.decodeResource(this.getResources(), R.drawable.bobby);        // Load Bob

            // Scale the bitmap to the correct size. Android automatically scales bitmaps based on screen
            bitmapBob = Bitmap.createScaledBitmap(bitmapBob, frameWidth * frameCount, frameHeight, false);

            playing = true;                                        // Set our boolean to true - game on!
        }

        @Override
        public void run() {
            while (playing) {
                long startFrameTime = System.currentTimeMillis();   // Capture the current time in milliseconds
                update();                                                                   // Update the frame
                draw();                                                                       // Draw the frame

                // Calculate the fps. Use the result to time animations and more.
                timeThisFrame = System.currentTimeMillis() - startFrameTime;

                if (timeThisFrame >= 1) { fps = 1000 / timeThisFrame; }
            }
        }

        // Everything that needs to be updated goes in here. In later projects we will
        // have dozens of objects. We will also do other things like collision detection.
        public void update() {
            // If bob is moving (the player is touching the screen)
            // then move him to the right based on his target speed and the current fps.
            if(isMoving){ bobXPosition = bobXPosition + (walkSpeedPerSecond / fps); }
        }

        public void getCurrentFrame(){
            long time  = System.currentTimeMillis();
            if(isMoving) {                                                   // Only animate if bob is moving
                if ( time > lastFrameChangeTime + frameLengthInMilliseconds) {
                    lastFrameChangeTime = time;
                    currentFrame++;
                    if (currentFrame >= frameCount) { currentFrame = 0; }
                }
            }
            //update the left and right values of the source of the next frame on the spritesheet
            frameToDraw.left = currentFrame * frameWidth;
            frameToDraw.right = frameToDraw.left + frameWidth;
        }


        public void draw() {                                                // Draw the newly updated scene
            if (ourHolder.getSurface().isValid()) {     // Make sure our drawing surface is valid or we crash
                canvas = ourHolder.lockCanvas();                               // Lock the canvas ready to draw
                canvas.drawColor(Color.argb(255,  26, 128, 182));                  // Draw the background color
                paint.setColor(Color.argb(255,  249, 129, 0));            // Choose the brush color for drawing
                paint.setTextSize(45);                                            // Make the text a bit bigger
                canvas.drawText("FPS:" + fps, 20, 40, paint);          // Display the current fps on the screen
                whereToDraw.set((int)bobXPosition, 0, (int)bobXPosition + frameWidth, frameHeight); // Draw bob
                getCurrentFrame();
                canvas.drawBitmap(bitmapBob, frameToDraw, whereToDraw, paint);
                ourHolder.unlockCanvasAndPost(canvas);                         // Draw everything to the screen
            }
        }

        public void pause() {                     // If MainActivity is paused/stopped shutdown our thread.
            playing = false;
            try { gameThread.join(); }
            catch (InterruptedException e) { Log.e("Error:", "joining thread"); }
        }

        public void resume() {                         // If MainActivity is started theb start our thread.
            playing = true;
            gameThread = new Thread(this);
            gameThread.start();
        }

        // The SurfaceView class implements onTouchListener, so we can override this method.
        @Override
        public boolean onTouchEvent(MotionEvent motionEvent) {
            switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:                                  // Player has touched the screen
                    isMoving = true;                         // Set isMoving so Bob is moved in the update method
                    break;
                case MotionEvent.ACTION_UP:                            // Player has removed finger from screen
                    isMoving = false;                                        // Set isMoving so Bob does not move
                    break;
            }
            return true;
        }
    }                                                      // This is the end of our GameView inner class

    @Override
    protected void onResume() {                   // This method executes when the player starts the game
        super.onResume();
        gameView.resume();                                    // Tell the gameView resume method to execute
    }

    @Override
    protected void onPause() {                     // This method executes when the player quits the game
        super.onPause();
        gameView.pause();                                      // Tell the gameView pause method to execute
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.dpoon.animatedmovetest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
            android:screenOrientation="landscape"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Snake Game:

MainActivity.java

package com.dpoon.snake;

import android.app.Activity;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;

public class MainActivity extends Activity {
    SnakeEngine snakeEngine;                                        // Declare an instance of SnakeEngine

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Display display = getWindowManager().getDefaultDisplay(); // Get the pixel dimensions of the screen

        Point size = new Point();                              // Initialize the result into a Point object
        display.getSize(size);

        snakeEngine = new SnakeEngine(this, size);        // Create a new instance of the SnakeEngine class

        setContentView(snakeEngine);                           // Make snakeEngine the view of the Activity
    }
    @Override
    protected void onResume() {                                          // Start the thread in snakeEngine
        super.onResume();
        snakeEngine.resume();
    }

    @Override
    protected void onPause() {                                          // Stop the thread in snakeEngine
        super.onPause();
        snakeEngine.pause();
    }

}

SnakeEngine.java

package com.dpoon.snake;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.media.AudioManager;
import android.media.SoundPool;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.Random;

class SnakeEngine extends SurfaceView implements Runnable {
    private Thread thread = null;                                 // Our game thread for the main game loop
    private Context context;                                         // To hold a reference to the Activity

    private SoundPool soundPool;                                                // for plaing sound effects
    int eat_bob;
    int snake_crash;

    public enum Heading {UP, RIGHT, DOWN, LEFT}                            // For tracking movement Heading
    private Heading heading = Heading.RIGHT;                               // Start by heading to the right
    private int screenX;                                               // To hold the screen size in pixels
    private int screenY;
    private int snakeLength;                                                       // How long is the snake
    private int bobX;                                                               // Where is Bob hiding?
    private int bobY;
    private int blockSize;                                         // The size in pixels of a snake segment
    private final int NUM_BLOCKS_WIDE = 40;                    // The size in segments of the playable area
    private int numBlocksHigh;
    private long nextFrameTime;                                          // Control pausing between updates
    private final long FPS = 10;                                     // Update the game 10 times per second
    private final long MILLIS_PER_SECOND = 1000;                 // There are 1000 milliseconds in a second
    private int score;                                              // How many points does the player have
    private int[] snakeXs;                                  // The location in the grid of all the segments
    private int[] snakeYs;
    private volatile boolean isPlaying;    // Everything we need for drawing is the game currently playing?
    private Canvas canvas;                                                        // A canvas for our paint
    private SurfaceHolder surfaceHolder;                                          // Required to use canvas
    private Paint paint;                                                       // Some paint for our canvas
    public SnakeEngine(Context context, Point size) {
        super(context);

        context = context;

        screenX = size.x;
        screenY = size.y;

        blockSize = screenX / NUM_BLOCKS_WIDE;                      // Work out how many pixels each block is
        numBlocksHigh = screenY / blockSize;     // How many blocks of the same size will fit into the height

        soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);                      // Set the sound up
        eat_bob = soundPool.load(context, R.raw.eat_bob, 1);              // Prepare the two sounds in memory
        snake_crash = soundPool.load(context, R.raw.snake_crash, 1);

        surfaceHolder = getHolder();                                        // Initialize the drawing objects
        paint = new Paint();

        snakeXs = new int[200];                // If you score 200 you are rewarded with a crash achievement!
        snakeYs = new int[200];

        newGame();                                                                          // Start the game
    }

    @Override
    public void run() {
        while (isPlaying) {

            if(updateRequired()) {// Update 10 times a second
                update();
                draw();
            }
        }
    }

    public void pause() {
        isPlaying = false;
        try {
            thread.join();
        } catch (InterruptedException e) {
            // Error
        }
    }

    public void resume() {
        isPlaying = true;
        thread = new Thread(this);
        thread.start();
    }
    public void newGame() {
        snakeLength = 1;                                                 // Start with a single snake segment
        snakeXs[0] = NUM_BLOCKS_WIDE / 2;
        snakeYs[0] = numBlocksHigh / 2;
        spawnBob();                                                               // Get Bob ready for dinner
        score = 0;                                                                         // Reset the score
        nextFrameTime = System.currentTimeMillis();          // Setup nextFrameTime so an update is triggered
    }
    public void spawnBob() {
        Random random = new Random();
        bobX = random.nextInt(NUM_BLOCKS_WIDE - 1) + 1;
        bobY = random.nextInt(numBlocksHigh - 1) + 1;
    }
    private void eatBob(){
        snakeLength++;                                             // Got him! Increase the size of the snake
        spawnBob();                                                                             //replace Bob
        score = score + 1;                                                                 //add to the score
        soundPool.play(eat_bob, 1, 1, 0, 0, 1);
    }
    private void moveSnake(){
        for (int i = snakeLength; i > 0; i--) {                                              // Move the body
            snakeXs[i] = snakeXs[i - 1];        // Start at back and move to position of segment in front of it
            snakeYs[i] = snakeYs[i - 1];        // Exclude the head because the head has nothing in front of it
        }

        switch (heading) {                                        // Move the head in the appropriate heading
            case UP:
                snakeYs[0]--;
                break;
            case RIGHT:
                snakeXs[0]++;
                break;
            case DOWN:
                snakeYs[0]++;
                break;
            case LEFT:
                snakeXs[0]--;
                break;
        }
    }
    private boolean detectDeath(){
        boolean dead = false;                                                          // Has the snake died?
        if (snakeXs[0] == -1) dead = true;                                             // Hit the screen edge
        if (snakeXs[0] >= NUM_BLOCKS_WIDE) dead = true;
        if (snakeYs[0] == -1) dead = true;
        if (snakeYs[0] == numBlocksHigh) dead = true;

        for (int i = snakeLength - 1; i > 0; i--) {                                          // Eaten itself?
            if ((i > 4) && (snakeXs[0] == snakeXs[i]) && (snakeYs[0] == snakeYs[i])) {
                dead = true;
            }
        }

        return dead;
    }
    public void update() {
        if (snakeXs[0] == bobX && snakeYs[0] == bobY) {                 // Did the head of the snake eat Bob?
            eatBob();
        }

        moveSnake();

        if (detectDeath()) {
            soundPool.play(snake_crash, 1, 1, 0, 0, 1);                                           //start again
            newGame();
        }
    }
    public void draw() {
        if (surfaceHolder.getSurface().isValid()) {                               // Get a lock on the canvas
            canvas = surfaceHolder.lockCanvas();
            canvas.drawColor(Color.argb(255, 26, 128, 182));      // Fill the screen with Game Code School blue
            paint.setColor(Color.argb(255, 255, 255, 255));   // Set color of the paint to draw the snake white
            paint.setTextSize(90);                                                        // Scale the HUD text
            canvas.drawText("Score:" + score, 10, 70, paint);

            for (int i = 0; i < snakeLength; i++) {                       // Draw the snake one block at a time
                canvas.drawRect(snakeXs[i] * blockSize, (snakeYs[i] * blockSize),
                        (snakeXs[i] * blockSize) + blockSize, (snakeYs[i] * blockSize) + blockSize, paint);
            }


            paint.setColor(Color.argb(255, 255, 0, 0));           // Set the color of the paint to draw Bob red
            canvas.drawRect(bobX * blockSize, (bobY * blockSize),                                   // Draw Bob
                    (bobX * blockSize) + blockSize, (bobY * blockSize) + blockSize, paint);


            surfaceHolder.unlockCanvasAndPost(canvas);  // Unlock canvas and reveal the graphics for this frame
        }
    }
    public boolean updateRequired() {
        if(nextFrameTime <= System.currentTimeMillis()){                    // Are we due to update the frame
            // Tenth of a second has passed. Setup when the next update will be triggered
            nextFrameTime =System.currentTimeMillis() + MILLIS_PER_SECOND / FPS;
            return true;                      // Return true so that the update and draw functions are executed
        }
        return false;
    }
    @Override
    public boolean onTouchEvent(MotionEvent motionEvent) {
        switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_UP:
                if (motionEvent.getX() >= screenX / 2) {
                    switch(heading){
                        case UP:
                            heading = Heading.RIGHT;
                            break;
                        case RIGHT:
                            heading = Heading.DOWN;
                            break;
                        case DOWN:
                            heading = Heading.LEFT;
                            break;
                        case LEFT:
                            heading = Heading.UP;
                            break;
                    }
                } else {
                    switch(heading){
                        case UP:
                            heading = Heading.LEFT;
                            break;
                        case LEFT:
                            heading = Heading.DOWN;
                            break;
                        case DOWN:
                            heading = Heading.RIGHT;
                            break;
                        case RIGHT:
                            heading = Heading.UP;
                            break;
                    }
                }
        }
        return true;
    }

}

Leave a Reply

Your email address will not be published. Required fields are marked *

css.php