天天看點

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

作者:程式設計實踐
想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

1、引言

《植物大戰僵屍》是由美國寶開遊戲公司(PopCap Games)開發的一款單機遊戲,自2009年釋出以來,風靡全球。有些城市裡面的小朋友,就是通過了這款遊戲,認識了向日葵、核桃、卷心菜、豌豆、蘑菇,現在我們還能買到《植物大戰僵屍》的玩具公仔。

當你玩這款遊戲而興奮時,有沒有想過遊戲是怎麼開發的,有沒有幻想過閱讀它的源代碼,甚至修改源代碼?

我一直有這個想法,并且在網上找到了一個Java簡易版的《植物大戰僵屍》,代碼寫得非常簡單、清晰,并且用IDEA開發工具,實作了編譯和運作。

如果你也有這個想法,跟随這篇文章,一起過把瘾吧!

2、源代碼下載下傳

我們進入github網站,搜尋PlantsVSZombies:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

搜尋後,可以找到一個Java版本的《植物大戰僵屍》遊戲項目arminkz/PlantsVsZombies:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

下載下傳該項目的源代碼:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

下載下傳該項目後,得到壓縮檔案PlantsVsZombies-master.zip,解壓後得到PlantsVsZombies-master目錄,該目錄的結構如下:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

目錄中有nbproject目錄,表示該項目使用NetBeans工具開發,考慮到大多數人的習慣,我們使用IDEA開發工具吧。

下面描述編譯和運作的詳細過程。

3、建立工程

啟動,建立工程:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

建立Maven工程:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

設定工程名為plants:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

這是工程建立後的視圖:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

4、添加源代碼

将下載下傳的PlantsVsZombies-master\src目錄中的内容,拷貝到plants工程的src\main\java目錄,這是添加後的IDEA工程視圖:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

将PlantsVsZombies-master\src目錄中的images子目錄,整體拷貝到plants工程的src\main\resources目錄,這是添加後的IDEA工程視圖:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

5、學習源代碼

我們可以發現這款《植物大戰僵屍》遊戲的代碼寫得非常好。例如,這是太陽功能對應類Sun.java的代碼,總共才77行,代碼可以直接看懂,特别簡練清晰:

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
/**
* Created by Armin on 6/27/2016.
*/
public class Sun extends JPanel implements MouseListener {
private GamePanel gp;
private Image sunImage;
private int myX;
private int myY;
private int endY;
private int destruct = 200;
public Sun(GamePanel parent, int startX, int startY, int endY) {
this.gp = parent;
this.endY = endY;
setSize(80, 80);
setOpaque(false);
myX = startX;
myY = startY;
setLocation(myX, myY);
sunImage = new ImageIcon(this.getClass().getResource("images/sun.png")).getImage();
addMouseListener(this);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(sunImage, 0, 0, null);
}
public void advance() {
if (myY < endY) {
myY += 4;
} else {
destruct--;
if (destruct < 0) {
gp.remove(this);
gp.getActiveSuns().remove(this);
}
}
setLocation(myX, myY);
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
gp.setSunScore(gp.getSunScore() + 25);
gp.remove(this);
gp.getActiveSuns().remove(this);
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}           

整個程式的入口類是GameWindow,從JFrame派生,用于顯示主視窗,這是代碼:

import javax.swing.*;
import java.awt.event.ActionEvent;
/**
* Created by Armin on 6/25/2016.
*/
public class GameWindow extends JFrame {
enum PlantType {
None,
Sunflower,
Peashooter,
FreezePeashooter
}
//PlantType activePlantingBrush = PlantType.None;
public GameWindow() {
setSize(1012, 785);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLayout(null);
JLabel sun = new JLabel("SUN");
sun.setLocation(37, 80);
sun.setSize(60, 20);
GamePanel gp = new GamePanel(sun);
gp.setLocation(0, 0);
getLayeredPane().add(gp, new Integer(0));
PlantCard sunflower = new PlantCard(new ImageIcon(this.getClass().getResource("images/cards/card_sunflower.png")).getImage());
sunflower.setLocation(110, 8);
sunflower.setAction((ActionEvent e) -> {
gp.setActivePlantingBrush(PlantType.Sunflower);
});
getLayeredPane().add(sunflower, new Integer(3));
PlantCard peashooter = new PlantCard(new ImageIcon(this.getClass().getResource("images/cards/card_peashooter.png")).getImage());
peashooter.setLocation(175, 8);
peashooter.setAction((ActionEvent e) -> {
gp.setActivePlantingBrush(PlantType.Peashooter);
});
getLayeredPane().add(peashooter, new Integer(3));
PlantCard freezepeashooter = new PlantCard(new ImageIcon(this.getClass().getResource("images/cards/card_freezepeashooter.png")).getImage());
freezepeashooter.setLocation(240, 8);
freezepeashooter.setAction((ActionEvent e) -> {
gp.setActivePlantingBrush(PlantType.FreezePeashooter);
});
getLayeredPane().add(freezepeashooter, new Integer(3));
getLayeredPane().add(sun, new Integer(2));
setResizable(false);
setVisible(true);
}
public GameWindow(boolean b) {
Menu menu = new Menu();
menu.setLocation(0, 0);
setSize(1012, 785);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getLayeredPane().add(menu, new Integer(0));
menu.repaint();
setResizable(false);
setVisible(true);
}
static GameWindow gw;
public static void begin() {
gw.dispose();
gw = new GameWindow();
}
public static void main(String[] args) {
gw = new GameWindow(true);
}
}           

6、編輯pom.xml檔案

由于這款《植物大戰僵屍》遊戲僅僅使用标準JDK提供的功能,是以我們這個工程的Maven,不用于管理依賴,僅僅用于打包。

我們在pom.xml檔案中插入<build>節點,pom.xml檔案添加的部分如下:

<build>
<finalName>plants</finalName>
<plugins>
<!--打包jar-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<!--不打包資源檔案,exclude的目錄不是src下面的,是以編譯結果classes為根目錄計算-->
<excludes>
<exclude>*.properties</exclude>
<exclude>*.txt</exclude>
<exclude>*.xml</exclude>
</excludes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<!--MANIFEST.MF 中 Class-Path 加入字首-->
<classpathPrefix>lib/</classpathPrefix>
<!--jar包不包含唯一版本辨別-->
<useUniqueVersions>false</useUniqueVersions>
<!--指定入口類-->
<mainClass>GameWindow</mainClass>
</manifest>
<manifestEntries>
<!--MANIFEST.MF 中 Class-Path 加入資源檔案目錄-->
<Class-Path>./resources/</Class-Path>
</manifestEntries>
</archive>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
</plugin>
<!--拷貝依賴 copy-dependencies-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/lib/
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!--拷貝資源檔案 copy-resources-->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<outputDirectory>${project.build.directory}/resources</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>           

7、編譯代碼

我們使用Maven指令,對工程進行編譯,編譯後得到plants.jar檔案:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

8、開始玩遊戲

軟體編譯完成後,我們在指令行下執行java -jar plants.jar,即可啟動遊戲:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

程式啟動後,顯示這個熟悉的主界面:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

點選“ADVENTURE”(冒險),啟動遊戲,界面如下:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

當然,我們還可以繼續玩:

想嘗試Java版《植物大戰僵屍》的編譯和運作嗎

9、總結

一個不到2000行的程式,不可能完整地實作功能強悍的《植物大戰僵屍》遊戲,但是這個版本,對于遊戲開發入門者和感興趣者,非常有用。