射擊類小遊戲講解:太空大戰

相信很多人都玩過太空大戰,版本不一樣,原理相同。沒玩過的看下圖瞭解一下: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

相關文章
相關標籤/搜索