- GUI
- Windows API tutorial
- Introduction to Windows API
- Windows API main functions
- System functions in Windows API
- Strings in Windows API
- Date & time in Windows API
- A window in Windows API
- First steps in UI
- Windows API menus
- Windows API dialogs
- Windows API controls I
- Windows API controls II
- Windows API controls III
- Advanced controls in Windows API
- Custom controls in Windows API
- The GDI in Windows API
- PyQt4 tutorial
- PyQt5 tutorial
- Qt4 tutorial
- Introduction to Qt4 toolkit
- Qt4 utility classes
- Strings in Qt4
- Date and time in Qt4
- Working with files and directories in Qt4
- First programs in Qt4
- Menus and toolbars in Qt4
- Layout management in Qt4
- Events and signals in Qt4
- Qt4 Widgets
- Qt4 Widgets II
- Painting in Qt4
- Custom widget in Qt4
- The Breakout game in Qt4
- Qt5 tutorial
- Introduction to Qt5 toolkit
- Strings in Qt5
- Date and time in Qt5
- Containers in Qt5
- Working with files and directories in Qt5
- First programs in Qt5
- Menus and toolbars in Qt5
- Layout management in Qt5
- Events and signals in Qt5
- Qt5 Widgets
- Qt5 Widgets II
- Painting in Qt5
- Custom widget in Qt5
- Snake in Qt5
- The Breakout game in Qt5
- PySide tutorial
- Tkinter tutorial
- Tcl/Tk tutorial
- Qt Quick tutorial
- Java Swing tutorial
- JavaFX tutorial
- Java SWT tutorial
- wxWidgets tutorial
- Introduction to wxWidgets
- wxWidgets helper classes
- First programs in wxWidgets
- Menus and toolbars in wxWidgets
- Layout management in wxWidgets
- Events in wxWidgets
- Dialogs in wxWidgets
- wxWidgets widgets
- wxWidgets widgets II
- Drag and Drop in wxWidgets
- Device Contexts in wxWidgets
- Custom widgets in wxWidgets
- The Tetris game in wxWidgets
- wxPython tutorial
- Introduction to wxPython
- First Steps
- Menus and toolbars
- Layout management in wxPython
- Events in wxPython
- wxPython dialogs
- Widgets
- Advanced widgets in wxPython
- Drag and drop in wxPython
- Internationalisation
- Application skeletons in wxPython
- The GDI
- Mapping modes
- Creating custom widgets
- Tips and Tricks
- wxPython Gripts
- The Tetris game in wxPython
- C# Winforms Mono tutorial
- Java Gnome tutorial
- Introduction to Java Gnome
- First steps in Java Gnome
- Layout management in Java Gnome
- Layout management II in Java Gnome
- Menus in Java Gnome
- Toolbars in Java Gnome
- Events in Java Gnome
- Widgets in Java Gnome
- Widgets II in Java Gnome
- Advanced widgets in Java Gnome
- Dialogs in Java Gnome
- Pango in Java Gnome
- Drawing with Cairo in Java Gnome
- Drawing with Cairo II
- Nibbles in Java Gnome
- QtJambi tutorial
- GTK+ tutorial
- Ruby GTK tutorial
- GTK# tutorial
- Visual Basic GTK# tutorial
- PyGTK tutorial
- Introduction to PyGTK
- First steps in PyGTK
- Layout management in PyGTK
- Menus in PyGTK
- Toolbars in PyGTK
- Signals & events in PyGTK
- Widgets in PyGTK
- Widgets II in PyGTK
- Advanced widgets in PyGTK
- Dialogs in PyGTK
- Pango
- Pango II
- Drawing with Cairo in PyGTK
- Drawing with Cairo II
- Snake game in PyGTK
- Custom widget in PyGTK
- PHP GTK tutorial
- C# Qyoto tutorial
- Ruby Qt tutorial
- Visual Basic Qyoto tutorial
- Mono IronPython Winforms tutorial
- Introduction
- First steps in IronPython Mono Winforms
- Layout management
- Menus and toolbars
- Basic Controls in Mono Winforms
- Basic Controls II in Mono Winforms
- Advanced Controls in Mono Winforms
- Dialogs
- Drag & drop in Mono Winforms
- Painting
- Painting II in IronPython Mono Winforms
- Snake in IronPython Mono Winforms
- The Tetris game in IronPython Mono Winforms
- FreeBASIC GTK tutorial
- Jython Swing tutorial
- JRuby Swing tutorial
- Visual Basic Winforms tutorial
- JavaScript GTK tutorial
- Ruby HTTPClient tutorial
- Ruby Faraday tutorial
- Ruby Net::HTTP tutorial
- Java 2D games tutorial
- Java 2D tutorial
- Cairo graphics tutorial
- PyCairo tutorial
- HTML5 canvas tutorial
- Python tutorial
- Python language
- Interactive Python
- Python lexical structure
- Python data types
- Strings in Python
- Python lists
- Python dictionaries
- Python operators
- Keywords in Python
- Functions in Python
- Files in Python
- Object-oriented programming in Python
- Modules
- Packages in Python
- Exceptions in Python
- Iterators and Generators
- Introspection in Python
- Ruby tutorial
- PHP tutorial
- Visual Basic tutorial
- Visual Basic
- Visual Basic lexical structure
- Basics
- Visual Basic data types
- Strings in Visual Basic
- Operators
- Flow control
- Visual Basic arrays
- Procedures & functions in Visual Basic
- Organizing code in Visual Basic
- Object-oriented programming
- Object-oriented programming II in Visual Basic
- Collections in Visual Basic
- Input & output
- Tcl tutorial
- C# tutorial
- Java tutorial
- AWK tutorial
- Jetty tutorial
- Tomcat Derby tutorial
- Jtwig tutorial
- Android tutorial
- Introduction to Android development
- First Android application
- Android Button widgets
- Android Intents
- Layout management in Android
- Android Spinner widget
- SeekBar widget
- Android ProgressBar widget
- Android ListView widget
- Android Pickers
- Android menus
- Dialogs
- Drawing in Android
- Java EE 5 tutorials
- Introduction
- Installing Java
- Installing NetBeans 6
- Java Application Servers
- Resin CGIServlet
- JavaServer Pages, (JSPs)
- Implicit objects in JSPs
- Shopping cart
- JSP & MySQL Database
- Java Servlets
- Sending email in a Servlet
- Creating a captcha in a Servlet
- DataSource & DriverManager
- Java Beans
- Custom JSP tags
- Object relational mapping with iBATIS
- Jsoup tutorial
- MySQL tutorial
- MySQL quick tutorial
- MySQL storage engines
- MySQL data types
- Creating, altering and dropping tables in MySQL
- MySQL expressions
- Inserting, updating, and deleting data in MySQL
- The SELECT statement in MySQL
- MySQL subqueries
- MySQL constraints
- Exporting and importing data in MySQL
- Joining tables in MySQL
- MySQL functions
- Views in MySQL
- Transactions in MySQL
- MySQL stored routines
- MySQL Python tutorial
- MySQL Perl tutorial
- MySQL C API programming tutorial
- MySQL Visual Basic tutorial
- MySQL PHP tutorial
- MySQL Java tutorial
- MySQL Ruby tutorial
- MySQL C# tutorial
- SQLite tutorial
- SQLite C tutorial
- SQLite PHP tutorial
- SQLite Python tutorial
- SQLite Perl tutorial
- SQLite Ruby tutorial
- SQLite C# tutorial
- SQLite Visual Basic tutorial
- PostgreSQL C tutorial
- PostgreSQL Python tutorial
- PostgreSQL Ruby tutorial
- PostgreSQL PHP tutorial
- PostgreSQL Java tutorial
- Apache Derby tutorial
- SQLAlchemy tutorial
- MongoDB PHP tutorial
- MongoDB Java tutorial
- MongoDB JavaScript tutorial
- MongoDB Ruby tutorial
- Spring JdbcTemplate tutorial
- JDBI tutorial
Space Invaders
In this part of the Java 2D games tutorial we will create a simple Space Invaders game clone.
Space Invaders is an arcade video game designed by Tomohiro Nishikado. It was first released in 1978. The player controls a cannon. He is about to save the Earth from invasion of evil space invaders.
Development
In our Java clone we have 24 invaders. These aliens heavily shell the ground. When the player shoots a missile, he can shoot another one only when it hits an alien or the top of the Board. The player shoots with the Alt key. Aliens launch randomly their bombs. Each alien shoots a bomb only after the previous one hits the bottom.
SpaceInvaders.java
package spaceinvaders; import javax.swing.JFrame; public class SpaceInvaders extends JFrame implements Commons { public SpaceInvaders() { add(new Board()); setTitle("Space Invaders"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(BOARD_WIDTH, BOARD_HEIGTH); setLocationRelativeTo(null); setVisible(true); setResizable(false); } public static void main(String[] args) { new SpaceInvaders(); } }
This is the main class.
Commons.java
package spaceinvaders; public interface Commons { public static final int BOARD_WIDTH = 358; public static final int BOARD_HEIGTH = 350; public static final int GROUND = 290; public static final int BOMB_HEIGHT = 5; public static final int ALIEN_HEIGHT = 12; public static final int ALIEN_WIDTH = 12; public static final int BORDER_RIGHT = 30; public static final int BORDER_LEFT = 5; public static final int GO_DOWN = 15; public static final int NUMBER_OF_ALIENS_TO_DESTROY = 24; public static final int CHANCE = 5; public static final int DELAY = 17; public static final int PLAYER_WIDTH = 15; public static final int PLAYER_HEIGHT = 10; }
The Commons.java
file has some common constants. They are self-explanatory.
Alien.java
package spaceinvaders; import javax.swing.ImageIcon; public class Alien extends Sprite { private Bomb bomb; private final String shot = "../spacepix/alien.png"; public Alien(int x, int y) { this.x = x; this.y = y; bomb = new Bomb(x, y); ImageIcon ii = new ImageIcon(this.getClass().getResource(shot)); setImage(ii.getImage()); } public void act(int direction) { this.x += direction; } public Bomb getBomb() { return bomb; } public class Bomb extends Sprite { private final String bomb = "../spacepix/bomb.png"; private boolean destroyed; public Bomb(int x, int y) { setDestroyed(true); this.x = x; this.y = y; ImageIcon ii = new ImageIcon(this.getClass().getResource(bomb)); setImage(ii.getImage()); } public void setDestroyed(boolean destroyed) { this.destroyed = destroyed; } public boolean isDestroyed() { return destroyed; } } }
This is the Alien
sprite. Each alien has an inner Bomb
class.
public void act(int direction) { this.x += direction; }
The act()
method is called from the Board
class. It is used to position an alien in horizontal direction.
public Bomb getBomb() { return bomb; }
The getBomb()
method is called, when the alien is about to drop a bomb.
Player.java
package spaceinvaders; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; public class Player extends Sprite implements Commons{ private final int START_Y = 280; private final int START_X = 270; private final String player = "../spacepix/player.png"; private int width; public Player() { ImageIcon ii = new ImageIcon(this.getClass().getResource(player)); width = ii.getImage().getWidth(null); setImage(ii.getImage()); setX(START_X); setY(START_Y); } public void act() { x += dx; if (x <= 2) x = 2; if (x >= BOARD_WIDTH - 2*width) x = BOARD_WIDTH - 2*width; } public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = -2; } if (key == KeyEvent.VK_RIGHT) { dx = 2; } } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) { dx = 0; } } }
This is the Player
sprite. We control the cannon with the cursor keys.
private final int START_Y = 280; private final int START_X = 270;
These are the initial coordinates of the player sprite.
if (key == KeyEvent.VK_LEFT) { dx = -2; }
If we press the left cursor key, the dx
variable is set to -2. Next time the act()
method is called, the player moves to the left.
if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) { dx = 0; }
If we release the left or the right cursor, the dx
variable is set to zero. The player sprite stops moving.
Shot.java
package spaceinvaders; import javax.swing.ImageIcon; public class Shot extends Sprite { private String shot = "../spacepix/shot.png"; private final int H_SPACE = 6; private final int V_SPACE = 1; public Shot() { } public Shot(int x, int y) { ImageIcon ii = new ImageIcon(this.getClass().getResource(shot)); setImage(ii.getImage()); setX(x + H_SPACE); setY(y - V_SPACE); } }
This is the Shot
sprite. The shot is triggered with the ALT key. The H_SPACE
and the V_SPACE
constants are used to position the missile appropriately.
Sprite.java
package spaceinvaders; import java.awt.Image; public class Sprite { private boolean visible; private Image image; protected int x; protected int y; protected boolean dying; protected int dx; public Sprite() { visible = true; } public void die() { visible = false; } public boolean isVisible() { return visible; } protected void setVisible(boolean visible) { this.visible = visible; } public void setImage(Image image) { this.image = image; } public Image getImage() { return image; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public int getY() { return y; } public int getX() { return x; } public void setDying(boolean dying) { this.dying = dying; } public boolean isDying() { return this.dying; } }
This is the basic Sprite
class. Other sprites inherit from it. It has some common functionality.
Board.java
package spaceinvaders; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Toolkit; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Iterator; import java.util.Random; import javax.swing.ImageIcon; import javax.swing.JPanel; public class Board extends JPanel implements Runnable, Commons { private Dimension d; private ArrayList aliens; private Player player; private Shot shot; private int alienX = 150; private int alienY = 5; private int direction = -1; private int deaths = 0; private boolean ingame = true; private final String expl = "../spacepix/explosion.png"; private final String alienpix = "../spacepix/alien.png"; private String message = "Game Over"; private Thread animator; public Board() { addKeyListener(new TAdapter()); setFocusable(true); d = new Dimension(BOARD_WIDTH, BOARD_HEIGTH); setBackground(Color.black); gameInit(); setDoubleBuffered(true); } public void addNotify() { super.addNotify(); gameInit(); } public void gameInit() { aliens = new ArrayList(); ImageIcon ii = new ImageIcon(this.getClass().getResource(alienpix)); for (int i=0; i < 4; i++) { for (int j=0; j < 6; j++) { Alien alien = new Alien(alienX + 18*j, alienY + 18*i); alien.setImage(ii.getImage()); aliens.add(alien); } } player = new Player(); shot = new Shot(); if (animator == null || !ingame) { animator = new Thread(this); animator.start(); } } public void drawAliens(Graphics g) { Iterator it = aliens.iterator(); while (it.hasNext()) { Alien alien = (Alien) it.next(); if (alien.isVisible()) { g.drawImage(alien.getImage(), alien.getX(), alien.getY(), this); } if (alien.isDying()) { alien.die(); } } } public void drawPlayer(Graphics g) { if (player.isVisible()) { g.drawImage(player.getImage(), player.getX(), player.getY(), this); } if (player.isDying()) { player.die(); ingame = false; } } public void drawShot(Graphics g) { if (shot.isVisible()) g.drawImage(shot.getImage(), shot.getX(), shot.getY(), this); } public void drawBombing(Graphics g) { Iterator i3 = aliens.iterator(); while (i3.hasNext()) { Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb(); if (!b.isDestroyed()) { g.drawImage(b.getImage(), b.getX(), b.getY(), this); } } } public void paint(Graphics g) { super.paint(g); g.setColor(Color.black); g.fillRect(0, 0, d.width, d.height); g.setColor(Color.green); if (ingame) { g.drawLine(0, GROUND, BOARD_WIDTH, GROUND); drawAliens(g); drawPlayer(g); drawShot(g); drawBombing(g); } Toolkit.getDefaultToolkit().sync(); g.dispose(); } public void gameOver() { Graphics g = this.getGraphics(); g.setColor(Color.black); g.fillRect(0, 0, BOARD_WIDTH, BOARD_HEIGTH); g.setColor(new Color(0, 32, 48)); g.fillRect(50, BOARD_WIDTH/2 - 30, BOARD_WIDTH-100, 50); g.setColor(Color.white); g.drawRect(50, BOARD_WIDTH/2 - 30, BOARD_WIDTH-100, 50); Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = this.getFontMetrics(small); g.setColor(Color.white); g.setFont(small); g.drawString(message, (BOARD_WIDTH - metr.stringWidth(message))/2, BOARD_WIDTH/2); } public void animationCycle() { if (deaths == NUMBER_OF_ALIENS_TO_DESTROY) { ingame = false; message = "Game won!"; } // player player.act(); // shot if (shot.isVisible()) { Iterator it = aliens.iterator(); int shotX = shot.getX(); int shotY = shot.getY(); while (it.hasNext()) { Alien alien = (Alien) it.next(); int alienX = alien.getX(); int alienY = alien.getY(); if (alien.isVisible() && shot.isVisible()) { if (shotX >= (alienX) && shotX <= (alienX + ALIEN_WIDTH) && shotY >= (alienY) && shotY <= (alienY+ALIEN_HEIGHT) ) { ImageIcon ii = new ImageIcon(getClass().getResource(expl)); alien.setImage(ii.getImage()); alien.setDying(true); deaths++; shot.die(); } } } int y = shot.getY(); y -= 4; if (y < 0) shot.die(); else shot.setY(y); } // aliens Iterator it1 = aliens.iterator(); while (it1.hasNext()) { Alien a1 = (Alien) it1.next(); int x = a1.getX(); if (x >= BOARD_WIDTH - BORDER_RIGHT && direction != -1) { direction = -1; Iterator i1 = aliens.iterator(); while (i1.hasNext()) { Alien a2 = (Alien) i1.next(); a2.setY(a2.getY() + GO_DOWN); } } if (x <= BORDER_LEFT && direction != 1) { direction = 1; Iterator i2 = aliens.iterator(); while (i2.hasNext()) { Alien a = (Alien)i2.next(); a.setY(a.getY() + GO_DOWN); } } } Iterator it = aliens.iterator(); while (it.hasNext()) { Alien alien = (Alien) it.next(); if (alien.isVisible()) { int y = alien.getY(); if (y > GROUND - ALIEN_HEIGHT) { ingame = false; message = "Invasion!"; } alien.act(direction); } } // bombs Iterator i3 = aliens.iterator(); Random generator = new Random(); while (i3.hasNext()) { int shot = generator.nextInt(15); Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb(); if (shot == CHANCE && a.isVisible() && b.isDestroyed()) { b.setDestroyed(false); b.setX(a.getX()); b.setY(a.getY()); } int bombX = b.getX(); int bombY = b.getY(); int playerX = player.getX(); int playerY = player.getY(); if (player.isVisible() && !b.isDestroyed()) { if ( bombX >= (playerX) && bombX <= (playerX+PLAYER_WIDTH) && bombY >= (playerY) && bombY <= (playerY+PLAYER_HEIGHT) ) { ImageIcon ii = new ImageIcon(this.getClass().getResource(expl)); player.setImage(ii.getImage()); player.setDying(true); b.setDestroyed(true);; } } if (!b.isDestroyed()) { b.setY(b.getY() + 1); if (b.getY() >= GROUND - BOMB_HEIGHT) { b.setDestroyed(true); } } } } public void run() { long beforeTime, timeDiff, sleep; beforeTime = System.currentTimeMillis(); while (ingame) { repaint(); animationCycle(); timeDiff = System.currentTimeMillis() - beforeTime; sleep = DELAY - timeDiff; if (sleep < 0) sleep = 2; try { Thread.sleep(sleep); } catch (InterruptedException e) { System.out.println("interrupted"); } beforeTime = System.currentTimeMillis(); } gameOver(); } private class TAdapter extends KeyAdapter { public void keyReleased(KeyEvent e) { player.keyReleased(e); } public void keyPressed(KeyEvent e) { player.keyPressed(e); int x = player.getX(); int y = player.getY(); if (ingame) { if (e.isAltDown()) { if (!shot.isVisible()) shot = new Shot(x, y); } } } } }
The main logic of the game is located in the Board
class.
for (int i=0; i < 4; i++) { for (int j=0; j < 6; j++) { Alien alien = new Alien(alienX + 18*j, alienY + 18*i); alien.setImage(ii.getImage()); aliens.add(alien); } } player = new Player(); shot = new Shot();
In the gameInit()
method we set up 24 aliens. The alien image size is 12x12px. We put 6px space among the aliens. We also create the player and the shot objects.
public void drawBombing(Graphics g) { Iterator i3 = aliens.iterator(); while (i3.hasNext()) { Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb(); if (!b.isDestroyed()) { g.drawImage(b.getImage(), b.getX(), b.getY(), this); } } }
The drawBombing()
method draws bombs launched by the aliens.
if (ingame) { g.drawLine(0, GROUND, BOARD_WIDTH, GROUND); drawAliens(g); drawPlayer(g); drawShot(g); drawBombing(g); }
Inside the paint()
method, we draw the ground, the aliens, the player, the shot, and the bombs.
Next we will examine the animationCycle()
method.
if (deaths == NUMBER_OF_ALIENS_TO_DESTROY) { ingame = false; message = "Game won!"; }
If we destroy all aliens, we win the game. (24 in this game)
if (alien.isVisible() && shot.isVisible()) { if (shotX >= (alienX) && shotX <= (alienX + ALIEN_WIDTH) && shotY >= (alienY) && shotY <= (alienY+ALIEN_HEIGHT) ) { ImageIcon ii = new ImageIcon(getClass().getResource(expl)); alien.setImage(ii.getImage()); alien.setDying(true); deaths++; shot.die(); } }
If the shot triggered by the player collides with an alien, the alien ship is destroyed. More precisely, the dying flag is set. We use it to display an explosion. The deaths variable increases and the shot sprite is destroyed.
if (x >= BOARD_WIDTH - BORDER_RIGHT && direction != -1) { direction = -1; Iterator i1 = aliens.iterator(); while (i1.hasNext()) { Alien a2 = (Alien) i1.next(); a2.setY(a2.getY() + GO_DOWN); } }
If the aliens reach the right end of the Board
, they move down and change their direction to the left.
Iterator it = aliens.iterator(); while (it.hasNext()) { Alien alien = (Alien) it.next(); if (alien.isVisible()) { int y = alien.getY(); if (y > GROUND - ALIEN_HEIGHT) { ingame = false; message = "Invasion!"; } alien.act(direction); } }
This code moves aliens. If they reach the bottom, the invasion begins.
int shot = generator.nextInt(15); Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb(); if (shot == CHANCE && a.isVisible() && b.isDestroyed()) { b.setDestroyed(false); b.setX(a.getX()); b.setY(a.getY()); }
This is the code that determines whether the alien will drop a bomb. The alien must not be destroyed. Eg. it must be visible. The bomb's destroyed flag must be set. In other words, it is alien's first bomb dropping or previous dropped bomb already hit the ground. If these two conditions are fulfilled, the bombing is left to the chance.
if (!b.isDestroyed()) { b.setY(b.getY() + 1); if (b.getY() >= GROUND - BOMB_HEIGHT) { b.setDestroyed(true); } }
If the bomb is not destroyed, it goes 1px to the ground. If it hits the bottom, the destroyed flag is set. The alien is now ready to drop another bomb.
public void keyReleased(KeyEvent e) { player.keyReleased(e); }
The actual processing of this particular KeyEvent
is delegated to the player sprite.

This was the Space Invaders game.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论