Improve movement of space aliens
Asked Answered
F

1

7

enter image description here

I code a mini Android game scenario inspired by Space Invaders and Moon Patrol. It is possible to shoot an alien horizontally (see above).

It is also possible to shoot an alien vertically (see below).

enter image description here

But adding aliens doesn't "scale", it will be very difficult to add for instance 15 aliens moving with respect to all possible collisions. The original space invaders and moon patrol solved this, is it possible to develop a different strategy than the one I am using? The exact movement of aliens is not important, only that it is "fun".

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.support.v4.view.MotionEventCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.ArrayList;
import java.util.List;

public class ParallaxView extends SurfaceView implements Runnable {

    List<Background> backgrounds;

    private volatile boolean running;
    private Thread gameThread = null;

    // For drawing
    private Paint paint;
    private Canvas canvas;
    private SurfaceHolder ourHolder;

    // Holds a reference to the Activity
    Context context;

    // Control the fps
    long fps = 60;

    // Screen resolution
    int screenWidth;
    int screenHeight;

    private void update() {
        // Update all the background positions
        for (Background bg : backgrounds) {
            bg.update(fps);
        }

    }

    ParallaxView(Context context, int screenWidth, int screenHeight) {
        super(context);

        this.context = context;

        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;

        // Initialize our drawing objects
        ourHolder = getHolder();
        paint = new Paint();

        // Initialize our array list
        backgrounds = new ArrayList<>();

        //load the background data into the Background objects and
        // place them in our GameObject arraylist

        backgrounds.add(new Background(
                this.context,
                screenWidth,
                screenHeight,
                "bg", 0, 120, 50));

        backgrounds.add(new Background(
                this.context,
                screenWidth,
                screenHeight,
                "grass", 70, 110, 200));

        // Add more backgrounds here

    }

    @Override
    public void run() {

        while (running) {
            long startFrameTime = System.currentTimeMillis();

            update();
            if (j > 2000) {
                j = -50;
                k = 0;
            }
            if (o > 2000) {
                o = -50;
                l = 0;
            }
            draw();

            // Calculate the fps this frame
            long timeThisFrame = System.currentTimeMillis() - startFrameTime;
            if (timeThisFrame >= 1) {
                fps = 1000 / timeThisFrame;
            }
        }
    }

    int numberOfshots = 1;
    int[] i = new int[200];
    int j = 0;
    int k = 0;
    int l = 0;
    int m = 0;
    int o = 0;
    boolean down = true;
    long lastTurn = System.currentTimeMillis();
    int xbuggy = 0;
    int xbuggy2 = 0;
    boolean down2 = true;
    long lastTurn2 = System.currentTimeMillis();
    long lastTurn3 = System.currentTimeMillis();
    boolean jump = false;
    boolean shoot = false;
    int ind = 0;

    private void draw() {

        if (ourHolder.getSurface().isValid()) {
            //First we lock the area of memory we will be drawing to
            canvas = ourHolder.lockCanvas();
            if (jump) {
                xbuggy = xbuggy + 4;
            }
            if (shoot) {
                xbuggy2 = xbuggy2 + 4;
            }

            if (System.currentTimeMillis() - lastTurn3 >= 1000) {
                // Change direction here
                jump = false;
                lastTurn3 = System.currentTimeMillis();
                xbuggy = 0;
            }
            //draw a background color
            canvas.drawColor(Color.argb(255, 0, 0, 0));

            // Draw the background parallax
            drawBackground(0);

            // Draw the rest of the game
            paint.setTextSize(60);
            paint.setColor(Color.argb(255, 255, 255, 255));

            //canvas.drawText("MOONPATROL3000", 350, screenHeight / 100 * 5, paint);

            int resID = context.getResources().getIdentifier("vehicle",
                    "drawable", context.getPackageName());

            int alienResID = context.getResources().getIdentifier("object3_hdpi",
                    "drawable", context.getPackageName());

            int alienResID2 = context.getResources().getIdentifier("object2_hdpi",
                    "drawable", context.getPackageName());

            int alienResID3 = context.getResources().getIdentifier("object1_hdpi",
                    "drawable", context.getPackageName());

            // Load the bitmap using the id
            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resID);
            Bitmap alienbitmap = BitmapFactory.decodeResource(context.getResources(), alienResID);
            Bitmap alienbitmap2 = BitmapFactory.decodeResource(context.getResources(), alienResID2);
            Bitmap alienbitmap3 = BitmapFactory.decodeResource(context.getResources(), alienResID3);




            //paint.setTextSize(220);
            for (int i1 = 0; i1 < numberOfshots; i1++) {


                // if horizontal missile hits alien 0
                if (java.lang.Math.abs(j - i[i1]) * 2  < (alienbitmap.getWidth() + 60) && java.lang.Math.abs(k +150+ screenHeight / 100 * 45 - (float) (screenHeight * 0.61)) * 2  < (alienbitmap.getHeight() + 60)) {
                    //y1[i2] = -random.nextInt(1000); // reset to new vertical position
                    //score += 1;
                    //onScoreListener.onScore(score);
                    Log.d("missile", "missile hit! ");
                    j=-200;
                }

                // if vertical missile hits alien 0
                if (java.lang.Math.abs(j - 185) * 2  < (alienbitmap.getWidth() + 60) && java.lang.Math.abs(j + 150 + screenHeight / 100 * 45 - (screenHeight / 100 * 95 - i[i1] - xbuggy2)) * 2  < (alienbitmap.getHeight() + 60)) {
                    //y1[i2] = -random.nextInt(1000); // reset to new vertical position
                    //score += 1;
                    //onScoreListener.onScore(score);
                    Log.d("missile", "missile hit! ");
                    j=-200;
                }


                // if horizontal missile hits alien 1, right now this won't happen
                if (java.lang.Math.abs(j - i[i1]) * 2  < (alienbitmap.getWidth() + 60) && java.lang.Math.abs(k +150+ screenHeight / 100 * 45 - (float) (screenHeight * 0.61)) * 2  < (alienbitmap.getHeight() + 60)) {
                    //y1[i2] = -random.nextInt(1000); // reset to new vertical position
                    //score += 1;
                    //onScoreListener.onScore(score);
                    Log.d("missile", "missile hit! ");
                    j=-200;
                }

                // if vertical missile hits alien 1
                if (java.lang.Math.abs(o + 10 - 185) * 2  < (alienbitmap.getWidth() + 60) && java.lang.Math.abs(l + screenHeight / 100 * 25 - (screenHeight / 100 * 95 - i[i1] - xbuggy2)) * 2  < (alienbitmap.getHeight() + 60)) {
                    //y1[i2] = -random.nextInt(1000); // reset to new vertical position
                    //score += 1;
                    //onScoreListener.onScore(score);
                    Log.d("missile", "missile hit! ");
                    o=-200;
                }


                canvas.drawText("o", i[i1], (float) (screenHeight * 0.61), paint);
                canvas.drawText("o", 185, screenHeight / 100 * 95 - i[i1] - xbuggy2, paint);




                if (i1 == numberOfshots - 1 && i[i1] > screenWidth) {
                    if (numberOfshots > 0) numberOfshots--;
                    if (ind > 0) ind--;
                }
            }
            if (System.currentTimeMillis() - lastTurn >= 2000) {
                // Change direction here
                down = !down;
                lastTurn = System.currentTimeMillis();
            }

            if (System.currentTimeMillis() - lastTurn2 >= 7000) {
                // Change direction here
                down2 = !down2;
                lastTurn2 = System.currentTimeMillis();
            }

            canvas.drawBitmap(alienbitmap, j, k +150+ screenHeight / 100 * 45, paint);
            canvas.drawBitmap(alienbitmap2, o + 10, l + screenHeight / 100 * 25, paint);
            //canvas.drawBitmap(alienbitmap3, j+20, k+screenHeight / 100 * 5, paint);
            drawBackground(1);
            canvas.drawBitmap(bitmap, 50, (float) (screenHeight * 0.5) - xbuggy, paint);
            // Draw the foreground parallax

            for (int n = 0; n < numberOfshots; n++)
                i[n] = i[n] + 20;

            j = j + 10;
            o = o + 7;
            if (!down)
                k=k+2;
            else
                k=k-2;

            if (!down2)
                l++;
            else
                l--;

            // Unlock and draw the scene
            ourHolder.unlockCanvasAndPost(canvas);
        }
    }

    // Clean up our thread if the game is stopped
    public void pause() {
        running = false;
        try {
            gameThread.join();
        } catch (InterruptedException e) {
            // Error
        }
    }

    // Make a new thread and start it
    // Execution moves to our run method
    public void resume() {
        running = true;
        gameThread = new Thread(this);
        gameThread.start();
    }

    private void drawBackground(int position) {

        // Make a copy of the relevant background
        Background bg = backgrounds.get(position);

        // define what portion of images to capture and
        // what coordinates of screen to draw them at

        // For the regular bitmap
        Rect fromRect1 = new Rect(0, 0, bg.width - bg.xClip, bg.height);
        Rect toRect1 = new Rect(bg.xClip, bg.startY, bg.width, bg.endY);

        // For the reversed background
        Rect fromRect2 = new Rect(bg.width - bg.xClip, 0, bg.width, bg.height);
        Rect toRect2 = new Rect(0, bg.startY, bg.xClip, bg.endY);

        //draw the two background bitmaps
        if (!bg.reversedFirst) {
            canvas.drawBitmap(bg.bitmap, fromRect1, toRect1, paint);
            canvas.drawBitmap(bg.bitmapReversed, fromRect2, toRect2, paint);
        } else {
            canvas.drawBitmap(bg.bitmap, fromRect2, toRect2, paint);
            canvas.drawBitmap(bg.bitmapReversed, fromRect1, toRect1, paint);
        }
    }

    // Because we call this from onTouchEvent, this code will be executed for both
    // normal touch events and for when the system calls this using Accessibility
    @Override
    public boolean performClick() {
        super.performClick();
        launchMissile();
        return true;
    }

    private void launchMissile() {
        i[ind] = 350;
        ind++;
        xbuggy2 = 0;
        shoot = true;
    }

    // event listener for when the user touches the screen
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean gameOver = false;
        //if (paused) {
        //   paused = false;
        //}
        int action = MotionEventCompat.getActionMasked(event);
        int coordX = (int) event.getX();
        int coordY = (int) event.getY();
        Log.d("coordY", "coordY " + coordY);
        if (coordX < 220 && xbuggy == 0 && action == MotionEvent.ACTION_MOVE) {
            jump = true;
            shoot = false;
            lastTurn3 = System.currentTimeMillis();
            return true; // do nothing
        }

        if (coordX > 219 && action == MotionEvent.ACTION_DOWN) {
            numberOfshots++;
            performClick();
            return true;
        }
        return true;
    }
}

Update

I have started to encapsulate the logic for the aliens according to the following.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class Alien {
    public Alien(){}
    public Alien(Context context, String name) {
        setAlienResID(context.getResources().getIdentifier("object3_hdpi",
                "drawable", context.getPackageName()));
        setAlienbitmap(BitmapFactory.decodeResource(context.getResources(), this.getAlienResID()));
    }
    public int getAlienResID() {
        return alienResID;
    }

    public void setAlienResID(int alienResID) {
        this.alienResID = alienResID;
    }

    public Bitmap getAlienbitmap() {
        return alienbitmap;
    }

    public void setAlienbitmap(Bitmap alienbitmap) {
        this.alienbitmap = alienbitmap;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    int alienResID;
    Bitmap alienbitmap;
    int width;
    int height;
}


public class AttackingAlien extends Alien {
    public AttackingAlien(Context context, String name) {
        super(context, name);
    }
}

Update 2

I have changed the strategy. Now I am drawing a spaceship which is going to bomb the moon buggy.

enter image description here

The relevant code is

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.support.v4.view.MotionEventCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.ArrayList;
import java.util.List;

public class ParallaxView extends SurfaceView implements Runnable {

    List<Background> backgrounds;

    private volatile boolean running;
    private Thread gameThread = null;

    // For drawing
    private Paint paint;
    private Canvas canvas;
    private SurfaceHolder ourHolder;

    // Holds a reference to the Activity
    Context context;

    // Control the fps
    long fps = 60;

    // Screen resolution
    int screenWidth;
    int screenHeight;

    private void update() {
        // Update all the background positions
        for (Background bg : backgrounds) {
            bg.update(fps);
        }

    }

    ParallaxView(Context context, int screenWidth, int screenHeight) {
        super(context);

        this.context = context;

        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;

        // Initialize our drawing objects
        ourHolder = getHolder();
        paint = new Paint();

        // Initialize our array list
        backgrounds = new ArrayList<>();

        //load the background data into the Background objects and
        // place them in our GameObject arraylist

        backgrounds.add(new Background(
                this.context,
                screenWidth,
                screenHeight,
                "bg", 0, 120, 50));

        backgrounds.add(new Background(
                this.context,
                screenWidth,
                screenHeight,
                "grass", 70, 110, 200));

        // Add more backgrounds here

    }

    @Override
    public void run() {

        while (running) {
            long startFrameTime = System.currentTimeMillis();

            update();
            if (j > 2000) {
                j = -50;
                k = 0;
            }
            if (o > 2000) {
                o = -50;
                l = 0;
            }
            draw();

            // Calculate the fps this frame
            long timeThisFrame = System.currentTimeMillis() - startFrameTime;
            if (timeThisFrame >= 1) {
                fps = 1000 / timeThisFrame;
            }
        }
    }

    int numberOfshots = 1;
    int[] i = new int[200];
    int j = 0;
    int k = 0;
    int l = 0;
    int m = 0;
    int o = 0;
    boolean down = true;
    long lastTurn = System.currentTimeMillis();
    int xbuggy = 0;
    int xbuggy2 = 0;
    boolean down2 = true;
    long lastTurn2 = System.currentTimeMillis();
    long lastTurn3 = System.currentTimeMillis();
    long lastTurn4 = System.currentTimeMillis();
    boolean jump = false;
    boolean shoot = false;
    int ind = 0;
    int numberOfAlienshots = 1;
    int missileOffSetY = 0;
    private void draw() {

        if (ourHolder.getSurface().isValid()) {
            //First we lock the area of memory we will be drawing to
            canvas = ourHolder.lockCanvas();
            if (jump) {
                xbuggy = xbuggy + 4;
            }
            if (shoot) {
                xbuggy2 = xbuggy2 + 4;
            }



            if (System.currentTimeMillis() - lastTurn4 >= 2000) {
                // Change direction here
               //jump = false;
                lastTurn4 = System.currentTimeMillis();
                missileOffSetY = 0;
            }

            if (System.currentTimeMillis() - lastTurn3 >= 1000) {
                // Change direction here
                jump = false;
                lastTurn3 = System.currentTimeMillis();
                xbuggy = 0;
            }
            //draw a background color
            canvas.drawColor(Color.argb(255, 0, 0, 0));

            // Draw the background parallax
            drawBackground(0);

            // Draw the rest of the game
            paint.setTextSize(60);
            paint.setColor(Color.argb(255, 255, 255, 255));

            //canvas.drawText("MOONPATROL3000", 350, screenHeight / 100 * 5, paint);

            int resID = context.getResources().getIdentifier("vehicle",
                    "drawable", context.getPackageName());

            Alien alien1 = new AttackingAlien(context, "right_side_hdpi");
            Alien alien2 = new AttackingAlien(context, "object2_hdpi");
            Alien alien3 = new AttackingAlien(context, "object1_hdpi");

            int alienResID = context.getResources().getIdentifier("right_side_hdpi",
                    "drawable", context.getPackageName());

            int alienResID2 = context.getResources().getIdentifier("right_side_hdpi",
                    "drawable", context.getPackageName());

            int alienResID3 = context.getResources().getIdentifier("right_side_hdpi",
                    "drawable", context.getPackageName());

            // Load the bitmap using the id
            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resID);
            Bitmap alienbitmap = BitmapFactory.decodeResource(context.getResources(), alienResID);
            Bitmap alienbitmap2 = BitmapFactory.decodeResource(context.getResources(), alienResID2);
            Bitmap alienbitmap3 = BitmapFactory.decodeResource(context.getResources(), alienResID3);

            //paint.setTextSize(220);

            //for (int i1 = 0; i1 < numberOfAlienshots; i1++) {
            if (missileOffSetY < 300) {
                canvas.drawText("|", o + 10 + alienbitmap2.getWidth() / 2, l + screenHeight / 100 * 25 + 75 + missileOffSetY, paint);

                missileOffSetY = missileOffSetY + 10;
            }

            for (int i1 = 0; i1 < numberOfshots; i1++) {


                // if horizontal missile hits alien 0
                if (java.lang.Math.abs(j - i[i1]) * 2  < (alien1.getWidth() + 60) && java.lang.Math.abs(k +150+ screenHeight / 100 * 45 - (float) (screenHeight * 0.61)) * 2  < (alien1.getHeight() + 60)) {
                    //y1[i2] = -random.nextInt(1000); // reset to new vertical position
                    //score += 1;
                    //onScoreListener.onScore(score);
                    Log.d("missile", "missile hit! ");
                    j=-200;
                }

                // if vertical missile hits alien 0
                if (java.lang.Math.abs(j - 185) * 2  < (alienbitmap.getWidth() + 60) && java.lang.Math.abs(j + 150 + screenHeight / 100 * 45 - (screenHeight / 100 * 95 - i[i1] - xbuggy2)) * 2  < (alienbitmap.getHeight() + 60)) {
                    j=-200;
                }


                // if horizontal missile hits alien 1, right now this won't happen
                if (java.lang.Math.abs(j - i[i1]) * 2  < (alienbitmap.getWidth() + 60) && java.lang.Math.abs(k +150+ screenHeight / 100 * 45 - (float) (screenHeight * 0.61)) * 2  < (alienbitmap.getHeight() + 60)) {
                    j=-200;
                }

                // if vertical missile hits alien 1
                if (java.lang.Math.abs(o + 10 - 185) * 2  < (alienbitmap.getWidth() + 60) && java.lang.Math.abs(l + screenHeight / 100 * 25 - (screenHeight / 100 * 95 - i[i1] - xbuggy2)) * 2  < (alienbitmap.getHeight() + 60)) {
                    o=-200;
                }


                canvas.drawText("o", i[i1], (float) (screenHeight * 0.61), paint);
                canvas.drawText("o", 185, screenHeight / 100 * 95 - i[i1] - xbuggy2, paint);




                if (i1 == numberOfshots - 1 && i[i1] > screenWidth) {
                    if (numberOfshots > 0) numberOfshots--;
                    if (ind > 0) ind--;
                }
            }
            if (System.currentTimeMillis() - lastTurn >= 2000) {
                // Change direction here
                down = !down;
                lastTurn = System.currentTimeMillis();
            }

            if (System.currentTimeMillis() - lastTurn2 >= 7000) {
                // Change direction here
                down2 = !down2;
                lastTurn2 = System.currentTimeMillis();
            }

          //  canvas.drawBitmap(alien1.getAlienbitmap(), j, k +150+ screenHeight / 100 * 45, paint);
            canvas.drawBitmap(alienbitmap2, o + 10, l + screenHeight / 100 * 25, paint);
            //canvas.drawBitmap(alienbitmap3, j+20, k+screenHeight / 100 * 5, paint);
            drawBackground(1);
            canvas.drawBitmap(bitmap, 50, (float) (screenHeight * 0.5) - xbuggy, paint);
            // Draw the foreground parallax

            for (int n = 0; n < numberOfshots; n++)
                i[n] = i[n] + 20;

            j = j + 10;
            o = o + 7;
            if (!down)
                k=k+2;
            else
                k=k-2;

            if (!down2)
                l++;
            else
                l--;

            // Unlock and draw the scene
            ourHolder.unlockCanvasAndPost(canvas);
        }
    }

    // Clean up our thread if the game is stopped
    public void pause() {
        running = false;
        try {
            gameThread.join();
        } catch (InterruptedException e) {
            // Error
        }
    }

    // Make a new thread and start it
    // Execution moves to our run method
    public void resume() {
        running = true;
        gameThread = new Thread(this);
        gameThread.start();
    }

    private void drawBackground(int position) {

        // Make a copy of the relevant background
        Background bg = backgrounds.get(position);

        // define what portion of images to capture and
        // what coordinates of screen to draw them at

        // For the regular bitmap
        Rect fromRect1 = new Rect(0, 0, bg.width - bg.xClip, bg.height);
        Rect toRect1 = new Rect(bg.xClip, bg.startY, bg.width, bg.endY);

        // For the reversed background
        Rect fromRect2 = new Rect(bg.width - bg.xClip, 0, bg.width, bg.height);
        Rect toRect2 = new Rect(0, bg.startY, bg.xClip, bg.endY);

        //draw the two background bitmaps
        if (!bg.reversedFirst) {
            canvas.drawBitmap(bg.bitmap, fromRect1, toRect1, paint);
            canvas.drawBitmap(bg.bitmapReversed, fromRect2, toRect2, paint);
        } else {
            canvas.drawBitmap(bg.bitmap, fromRect2, toRect2, paint);
            canvas.drawBitmap(bg.bitmapReversed, fromRect1, toRect1, paint);
        }
    }

    // Because we call this from onTouchEvent, this code will be executed for both
    // normal touch events and for when the system calls this using Accessibility
    @Override
    public boolean performClick() {
        super.performClick();
        launchMissile();
        return true;
    }

    private void launchMissile() {
        i[ind] = 350; // what does it do?
        ind++;
        xbuggy2 = 0;
        shoot = true;
    }

    // event listener for when the user touches the screen
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean gameOver = false;
        //if (paused) {
        //   paused = false;
        //}
        int action = MotionEventCompat.getActionMasked(event);
        int coordX = (int) event.getX();
        int coordY = (int) event.getY();
        Log.d("coordY", "coordY " + coordY);
        if (coordX < 220 && xbuggy == 0 && action == MotionEvent.ACTION_MOVE) {
            jump = true;
            shoot = false;
            lastTurn3 = System.currentTimeMillis();
            return true; // do nothing
        }

        if (coordX > 219 && action == MotionEvent.ACTION_DOWN) {
            numberOfshots++;
            performClick();
            return true;
        }
        return true;
    }
}
Filet answered 31/5, 2018 at 2:37 Comment(15)
Why not just generalize as much as possible and make a list of all aliens / missiles being fired? 15 should be small enough to not have to worry about using quadtreesCathycathyleen
@Cathycathyleen Yes I will try that. The problem might be that the aliens have different offsets, but I can make that a variable too.Filet
What do you mean by offsets?Cathycathyleen
@Cathycathyleen Drawing a typical alien looks like canvas.drawBitmap(alienbitmap, j, k +150+ screenHeight / 100 * 45, paint) The j is the offset in x direction and the k+150... is the offset in the y direction. Those are different for different aliens. I suppose I must put them in an array and use variables instead. It is because the calls are so different that I couldn't loop over a collection of aliens.Filet
That's where OOP would be useful. And yes, very bad practice to use constants everywhere.Cathycathyleen
@Cathycathyleen I might create a class "World" and let that class handle the creation of objects, and then a class called "Simulation" to step the time forward. But these comments are still too abstract to help me.Filet
What I specifically meant was to have multiple alien classes inheriting from a base class Alien; this way you can easily use an array of Alien's. The challenge is to work out a set of methods shared by all aliens - obvious examples include draw, collision detection, AI timestep, physics timestep, attack (might be part of AI), and so on.Cathycathyleen
@Cathycathyleen Thanks. There is a value in trying what you propose. I was also thinking of studying the original space invaders for the pattern of aliens where one alien suddenly attacks in a planned way.Filet
@Cathycathyleen I have started and updated the question with Alien classes. Please have a look if you like.Filet
Asking for code review is very broad. We don't know what functionality you want for the aliens, other than the basic ones like draw and collide (which you haven't implemented). And of course SO is not a coding service.Cathycathyleen
@Cathycathyleen I'm narrowing it down to trying to make the aliens move along a path. I might try that and make a new question, how to make alien or spaceship move along a Path object. Do you agree that is better question?Filet
For a generic path that might be quite math-heavy (I don't know for sure, maybe Java offers just such functionalities).Cathycathyleen
@Cathycathyleen Thank you for the information. I have changed the strategy. Now I am drawing a spaceship above the moon buggy and the spaceship is going to bomb downwards. It is beginning to work. Please look at the update in the question and if you can comment.Filet
First and foremost I would give the variables more sensible names (e.g. int[] i = new int[200]?). Next I'd stop using constant literals everywhere, and try to OOP-ise the code as much as possible. Finally I'd separate the update logic and draw routines (there are online tutorials about how to write a good game loop - and it is less trivial than most people think). Again, this is turning into a very broad code review-y style question; you must boil it down to more specific issues.Cathycathyleen
@Cathycathyleen I am working on it. Thanks for the comment.Filet
M
3

Your biggest error seems to be allocating 4 bitmaps in the draw routine. Allocate those bitmaps in the onCreate and simply call the global bitmaps, that you initialized onCreate(). That will fix your problem. You can draw them in their locations.

    private void draw() {
        Alien alien1 = new AttackingAlien(context, "right_side_hdpi");
        Alien alien2 = new AttackingAlien(context, "object2_hdpi");
        Alien alien3 = new AttackingAlien(context, "object1_hdpi");

You allocated a bunch of memory objects invoking the context and expanding the drawable and a bunch of other work. You could have just used the same aliens from last tick.

        int alienResID = context.getResources().getIdentifier("right_side_hdpi",
                "drawable", context.getPackageName());

        int alienResID2 = context.getResources().getIdentifier("right_side_hdpi",
                "drawable", context.getPackageName());

        int alienResID3 = context.getResources().getIdentifier("right_side_hdpi",
                "drawable", context.getPackageName());

The aliens IDs didn't change from the previous tick.

        // Load the bitmap using the id
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resID);
        Bitmap alienbitmap = BitmapFactory.decodeResource(context.getResources(), alienResID);
        Bitmap alienbitmap2 = BitmapFactory.decodeResource(context.getResources(), alienResID2);
        Bitmap alienbitmap3 = BitmapFactory.decodeResource(context.getResources(), alienResID3);

These are the same bitmaps from last tick and bitmaps are giant and getting them from resources is slow, and you are doing this EVERY tick.

    }
}

Most of the other stuff is going to shave off half a ms here and there, but this here is going to maybe get you up to the right FPS ballpark.


Don't worry though you have others.

Much of your problem is solved by making the routines faster. This is a good time to say, you're doing it wrong. The typical and correct way to do this is to have a loop ticks every 17ms or so. And for the rest of the time is paused. Some errors are glaring.

Your biggest error seems to be allocating 4 bitmaps in the draw routine.

But, the draw routine only draws. You draw the stuff that needs to happen on your canvas and that's it. You don't allocate anything you don't inflate anything, you take the numbers you have for where stuff is, and draw the stuff you already have loaded in the memory in that location.

You are ticking and doing the collision detection, in the draw routine, and allocating a bunch of objects for this when they have to be thrown in the woodchipper in a split second anyway.

You should not create any objects outside your initialization or in those special cases when a new alien exists. You shouldn't ever use a "new" anywhere in a draw routine. Ever.

You are using brute force for collision detection, eventually don't. Find a nice acceleration structure you like and use that. For 1 object it won't matter though.

Don't call some alien class, while it looks prettier, you want raw numbers of the bounding boxes of the aliens. You want then to keep them in some sort of structure that allows referencing them very quickly (You need the frame in less than 17ms). Calling a bunch of width commands isn't really that helpful, even if they changed size just change the number for the hitbox. These methods allow you some nice structures for the data like having a sorted array of hitboxes you can binary search into them and find whether the moved object hits the object in a log(n) time with log(n) updates to the structure, or some various methods for traversing an Axis Aligned Bound Box Tree. This is something you eventually will need but it might work without this so long as you're keeping it simple. Though, truthfully it's just your bitmaps there doing most of the slowing.

There's a lot of other basic issues like putting the bounding box inside the if statement rather than making two additional rectangles. But there's other issues like making rectangles at all! You call the draw with the actual location rather than some allocate a big ass couple objects to call a function with. Just call the function with the numbers.


You should have a routine that does the drawing for you based on the locations of the stuff. It should be able to draw everything it needs to draw in less then 17ms. If it can't you won't ever hit the 60fps you need to hit. So in that case draw less stuff and do a better job of it. Does that space background need to be a bitmap? Could you just draw a bunch of points for the sky, and adjust the graphics accordingly. Your draw routine never allocates anything. Period. If you need to allocate something it should have been during the init. Allocations are the bane of your existence.

Your touches update the position of the stuff. The AI/Physics tick also updates the positions of things and checks for collusion. The draw only draws the stuff based on the positions and stuff in memory.

Run the update location tick in its own thread. You only need to cope with the concurrency bits where you would be reading and writing the same data. It just needs to synchronize the changing data reading of the data for the draw, so toss those sections (position update in touch, position update in tick, and getting the position for the draw routine itself) in a synchronized block with the same object.

Masque answered 10/6, 2018 at 23:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.