射击类小游戏讲解:太空大战

相信很多人都玩过太空大战,版本不一样,原理相同。没玩过的看下图了解一下:html

太空

首先先分解一下游戏的构成:java

  1. 大批敌军
  2. 子弹
  3. 保卫机

这三个角色之间基本的互动请看流程图git

基本流程图

流程图
以上是基本的操做流程图,具体实现的源码将在文章最后的小结部分给你们github

代码实现难点讲解

我把我认为可能须要考虑的问题归类以下:算法

  1. 如何实现并发?api

    • 实现多线程老是绕不开RunnableThread,可是咱们今天不调用Thread,只用runnable,并且不是继承。除此以外,今天用的package不是你们常知的java.awt.*而是javafx.scene.*
  2. 三个角色运动状态不一样,如何初始化用同一个类调用?数组

    • 很简单,虽然运动轨迹不一样,可是基本的变量:当前位置,速度,尺寸都是能够从同一个构造函数初始的。这里咱们介绍如下源码做者Joe Finney的构造办法:
public class Shape
{
    //在这以前你须要要个最基础的图形构造类,除非你不想创造复杂的图形或者直接想调用图像
    //这边的长方形能够理解位积木
    private Rectangle[] p = new Rectangle[50];
    private int number = 0;

    private double xPosition = 0.0;
    private double yPosition = 0.0;
    //给图形声明初始的位置,须要考虑一下本来图像的尺寸,位置过高或过低都会看不到完整的图形
    public Shape(double x, double y)
    {
        this.xPosition = x;
        this.yPosition = y;
    }
    //把新的积木拼到原有的积木上,也能够使用arraylist实现
    public void addRectangle(Rectangle r)
    {
        if (number < p.length)
        {
            p[number] = p;
            number++;
        }    
    }
    //以上都是静态的并且不会在画面上显示
    //addShapeTo方法帮助咱们把积木们放到屏幕里
    public void addShapeTo(GameArena a)
    {
        for (int i = 0 ; i < number; i++)
        {
            p[i].setXPosition(p[i].getXPosition() + xPosition);
            p[i].setYPosition(p[i].getYPosition() + yPosition);
            a.addRectangle(p[i]);
        }
    }
    //移动积木堆们,根据咱们给定的速度。积木堆们以总体的形式运动
    public void move(double x, double y)
    {
        xPosition += x;
        yPosition += y;

        for (int i=0; i<numberParts; i++)
        {
            p[i].setXPosition(p[i].getXPosition() + x);//注意,在屏幕里表示向右
            p[i].setYPosition(p[i].getYPosition() + y);//注意,在屏幕里表示向下
        }
    }
    //如名字所写,带有目的性,表示直接移动到指定的位置
    //在这边的做用是子弹打出去后最后又回到原来的位置,由于速度很快,因此屏幕上看不到回来的效果
    //感受子弹好辛苦,可是不想给子弹单独建个数组了,麻烦子弹了...
    public void moveTo(double x, double y)
    {
        move(x-xPosition, y-yPosition);
        xPosition = x;
        yPosition = y;
    }
    //获取第i个积木
    public Rectangle getP(int i)
    {
        return p[i];
    }
    //判断两个积木快是否相撞
    boolean collides (Shape c)
    {
        for (int i=0; i<number; i++)
        {
            for (int j=0; j<c.number; j++)
            {
                if (p[i].collides(c.getP(j)))//这个调用的是Rectangle里的方法,不是这个类里的
                    return true;
            }
        }

        return false;
    }

    public double getXPosition()
    {
        return xPosition;
    }

    public double getYPosition()
    {
        return yPosition;
    }

    public void removeShapeFrom(GameArena a)
    {
        for (int i=0; i<number; i++)
        {
            a.removeRectangle(p[i]);
        }
    }
}

界面效果

源码

小编以前说了,若是你想引用现有的图像作你的角色....
ojbk多线程

原理和上面的代码差很少,不拼积木确定比拼积木轻松的,因此咱们只要创造图像类。
如下是小编的源码并发

/**
* @author Hephaest
*/
//没错!什么类都没import!
public class ImageView 
{
    private double xPosition;            
    private double yPosition;            
    private double width;                
    private double height;                
    private String url;
    private ImageView[] part = new ImageView[100];
    private int numberPart = 0;

    public double getXPosition()
    {
        return xPosition;
    }


    public double getYPosition()
    {
        return yPosition;
    }


    public void setXPosition(double x)
    {
        this.xPosition = x;
    }

    public void setYPosition(double y)
    {
        this.yPosition = y;
    }

    public double getWidth()
    {
        return width;
    }

    public double getHeight()
    {
        return height;
    }

    public String getUrl()
    {
        return url;
    }
    //定义图像的时候须要位置,尺寸,还有哪里来的图
    public ImageView(double x, double y, double w, double h, String url)
    {
        xPosition = x;
        yPosition = y;
        width = w;
        height = h;
        this.url = url;
    }    
    //碰撞的算法小编没优化
    public boolean collides(ImageView i)
    {
        return (xPosition < i.xPosition + i.width &&
            xPosition + width > i.xPosition &&
            yPosition < i.yPosition + i.height &&
            yPosition + height > i.yPosition);
    }
    //运动的方法和以前的一致
    public void move(double x, double y)
    {
        xPosition += x;
        yPosition += y;
        for (int i=0; i<numberPart; i++)
        {
            part[i].setXPosition(part[i].getXPosition() + x);
            part[i].setYPosition(part[i].getYPosition() + y);
        }
    }
    
    public void moveTo(double x, double y)
    {
        move(x-xPosition, y-yPosition);
        xPosition = x;
        yPosition = y;
    }
    
    public void removeShapeFrom(GameArena a)
    {
        for (int i=0; i<numberPart; i++)
        {
            a.removeImage(part[i]);
        }
    }
}

界面效果

图像

补充

头疼

最后至于图形界面框那部分,之前鼠标操控的办法,请参考原做者的javafx使用办法。小编为了利用javafx实现图像移动绞尽脑汁。不过最终仍是找到办法了:oracle

/**
*@author Joe Finney
*override by hephaest
*/
//用hashmap 创造ImageView的数组对象,由于ImageView是节点
  private Map<ImageView, javafx.scene.image.ImageView> images = new HashMap<>();
  @override
  private void frameUpdate ()
    {
        if (!this.exiting)
        {
            // Remove any deleted objects from the scene.
            synchronized (this)
            {
                for (Object o: removeList)
                {
                    if (o instanceof Rectangle)
                    {
                        Rectangle r = (Rectangle) o;
                        javafx.scene.shape.Rectangle rectangle = rectangles.get(r);
                        root.getChildren().remove(rectangle);

                        rectangles.remove(r);
                    }
                    //我本身添加的
                    if (o instanceof ImageView)
                    {
                        ImageView i = (ImageView) o;
                        javafx.scene.image.ImageView image = images.get(i);
                        root.getChildren().remove(image);
    
                        images.remove(i);
                    }
                }

                removeList.clear();

                // Add any new objects to the scene.
                for (Object o: addList)
                {
                    if (o instanceof Rectangle)
                    {
                        Rectangle r = (Rectangle) o;
                        javafx.scene.shape.Rectangle rectangle = new javafx.scene.shape.Rectangle(0, 0, r.getWidth(), r.getHeight());
                        root.getChildren().add(rectangle);
                        rectangles.put(r, rectangle);
                    }
                    //我本身添加的
                    if (o instanceof ImageView)
                    {
                        ImageView i = (ImageView) o;
                        javafx.scene.image.ImageView image = new javafx.scene.image.ImageView(i.getUrl());
                        root.getChildren().add(image);
                        images.put(i, image);
                    }
                }

                addList.clear();
            }

            for(Map.Entry<Rectangle, javafx.scene.shape.Rectangle> entry : rectangles.entrySet())
            {
                Rectangle r = entry.getKey();
                javafx.scene.shape.Rectangle rectangle = entry.getValue();

                rectangle.setTranslateX(r.getXPosition() - r.getWidth()/2);
                rectangle.setTranslateY(r.getYPosition() - r.getHeight()/2);
                rectangle.setFill(getColourFromString(r.getColour()));
            }
            //我本身添加的
            for(Map.Entry<ImageView, javafx.scene.image.ImageView> entry : images.entrySet())
            {
                ImageView i = entry.getKey();
                javafx.scene.image.ImageView image = entry.getValue();
                image.setTranslateX(i.getXPosition() - i.getWidth()/2);
                image.setTranslateY(i.getYPosition()- i.getHeight()/2);
                image.setImage(new Image(i.getUrl()));
            }
        }
    }

除此以外,addImage和removeaddImage只要改个变量名字就行了。

小结

这几天摸索的经验总结:

  • API很重要!学会查javadoc!
  • 若是想作游戏开发仍是用java.awt.*吧

相关代码分享

github完整源码连接:Thanks to finneyj
Oracle-avafx.scene.image Javadoc:javafx.scene.image

相关文章
相关标签/搜索