- 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
Animation
In this part of the Java 2D games tutorial, we will work with animation.
Animation is a rapid display of sequence of images which creates an illusion of movement. We will animate a star on our Board. We will implement the movement in three basic ways. We will use a Swing timer, a standard utility timer, and a thread.
Animation is a complex subject in game programming. Java games are expected to run on multiple operating systems with different hardware specifications. Threads give the most accurate timing solutions. However, for our simple 2D games, other two options can be an option too.
Swing timer
In the first example we will use a Swing timer to create animation. This is the easiest but also the least effective way of animating objects in Java games.
SwingTimerExample.java
package com.zetcode; import java.awt.EventQueue; import javax.swing.JFrame; public class SwingTimerExample extends JFrame { public SwingTimerExample() { initUI(); } private void initUI() { add(new Board()); setResizable(false); pack(); setTitle("Star"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame ex = new SwingTimerExample(); ex.setVisible(true); } }); } }
This is the main class for the code example.
setResizable(false); pack();
The setResizable()
sets whether the frame can be resized. The pack()
method causes this window to be sized to fit the preferred size and layouts of its children. Note that the order in which these two methods are called is important. (The setResizable()
changes the insets of the frame on some platforms; calling this method after the pack()
method might lead to incorrect results—the star would not go precisely into the right-bottom border of the window.)
Board.java
package com.zetcode; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { private final int B_WIDTH = 350; private final int B_HEIGHT = 350; private final int INITIAL_X = -40; private final int INITIAL_Y = -40; private final int DELAY = 25; private Image star; private Timer timer; private int x, y; public Board() { initBoard(); } private void loadImage() { ImageIcon ii = new ImageIcon("star.png"); star = ii.getImage(); } private void initBoard() { setBackground(Color.BLACK); setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT)); setDoubleBuffered(true); loadImage(); x = INITIAL_X; y = INITIAL_Y; timer = new Timer(DELAY, this); timer.start(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); drawStar(g); } private void drawStar(Graphics g) { g.drawImage(star, x, y, this); Toolkit.getDefaultToolkit().sync(); } @Override public void actionPerformed(ActionEvent e) { x += 1; y += 1; if (y > B_HEIGHT) { y = INITIAL_Y; x = INITIAL_X; } repaint(); } }
In the Board
class we move a star that from the upper-left corner to the right-bottom corner.
private final int B_WIDTH = 350; private final int B_HEIGHT = 350; private final int INITIAL_X = -40; private final int INITIAL_Y = -40; private final int DELAY = 25;
Five constants are defined. The first two constants are the board width and height. The third and fourth are the initial coordinates of the star. The last one determines the speed of the animation.
private void loadImage() { ImageIcon ii = new ImageIcon("star.png"); star = ii.getImage(); }
In the loadImage()
method we create an instance of the ImageIcon
class. The image is located in the project directory. The getImage()
method will return the the Image
object from this class. This object will be drawn on the board.
setDoubleBuffered(true);
The JPanel
component will use a buffer to paint. This means that all drawing will be done in memory first. Later the off-screen buffer will be copied to the screen. In this simple example, we might not notice any differences.
timer = new Timer(DELAY, this); timer.start();
Here we create a Swing Timer
class and call its start()
method. Every DELAY
ms the timer will call the actionPerformed()
method. In order to use the actionPerformed()
method, we must implement the ActionListener
interface.
@Override public void paintComponent(Graphics g) { super.paintComponent(g); drawStar(g); }
Custom painting is done in the paintComponent()
method. Note that we also call the paintComponent()
method of its parent. The actual painting is delegated to the drawStar() method.
private void drawStar(Graphics g) { g.drawImage(star, x, y, this); Toolkit.getDefaultToolkit().sync(); }
In the drawStar() method, we draw the image on the window with the usage of the drawImage()
method. The Toolkit.getDefaultToolkit().sync()
synchronises the painting on systems that buffer graphics events. Without this line, the animation might not be smooth on Linux.
@Override public void actionPerformed(ActionEvent e) { x += 1; y += 1; if (y > B_HEIGHT) { y = INITIAL_Y; x = INITIAL_X; } repaint(); }
The actionPerformed()
method is repeatedly called by the timer. Inside the method, we increase the x and y values of the star object. Then we call the repaint()
method which will cause the paintComponent()
to be called. This way we regularly repaint the Board
thus making the animation.

Utility timer
This is very similar to the previous way. We use the java.util.Timer
instead of the javax.Swing.Timer
. For Java Swing games this way should be more accurate.
UtilityTimerExample.java
package com.zetcode; import java.awt.EventQueue; import javax.swing.JFrame; public class UtilityTimerExample extends JFrame { public UtilityTimerExample() { initUI(); } private void initUI() { add(new Board()); setResizable(false); pack(); setTitle("Star"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame ex = new UtilityTimerExample(); ex.setVisible(true); } }); } }
This is the main class.
Board.java
package com.zetcode; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.util.Timer; import java.util.TimerTask; import javax.swing.ImageIcon; import javax.swing.JPanel; public class Board extends JPanel { private final int B_WIDTH = 350; private final int B_HEIGHT = 350; private final int INITIAL_X = -40; private final int INITIAL_Y = -40; private final int INITIAL_DELAY = 100; private final int PERIOD_INTERVAL = 25; private Image star; private Timer timer; private int x, y; public Board() { initBoard(); } private void loadImage() { ImageIcon ii = new ImageIcon("star.png"); star = ii.getImage(); } private void initBoard() { setBackground(Color.BLACK); setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT)); setDoubleBuffered(true); loadImage(); x = INITIAL_X; y = INITIAL_Y; timer = new Timer(); timer.scheduleAtFixedRate(new ScheduleTask(), INITIAL_DELAY, PERIOD_INTERVAL); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); drawStar(g); } private void drawStar(Graphics g) { g.drawImage(star, x, y, this); Toolkit.getDefaultToolkit().sync(); } private class ScheduleTask extends TimerTask { @Override public void run() { x += 1; y += 1; if (y > B_HEIGHT) { y = INITIAL_Y; x = INITIAL_X; } repaint(); } } }
In this example, the timer will regularly call the run()
method of the ScheduleTask
class.
timer = new Timer(); timer.scheduleAtFixedRate(new ScheduleTask(), INITIAL_DELAY, PERIOD_INTERVAL);
Here we create a timer and schedule a task with a specific interval. There is an initial delay.
@Override public void run() { ... }
Each 10ms the timer will call this run()
method.
Thread
Animating objects using a thread is the most effective and accurate way of animation.
ThreadAnimationExample.java
package com.zetcode; import java.awt.EventQueue; import javax.swing.JFrame; public class ThreadAnimationExample extends JFrame { public ThreadAnimationExample() { initUI(); } private void initUI() { add(new Board()); setResizable(false); pack(); setTitle("Star"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame ex = new ThreadAnimationExample(); ex.setVisible(true); } }); } }
This is the main class.
Board.java
package com.zetcode; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import javax.swing.ImageIcon; import javax.swing.JPanel; public class Board extends JPanel implements Runnable { private final int B_WIDTH = 350; private final int B_HEIGHT = 350; private final int INITIAL_X = -40; private final int INITIAL_Y = -40; private final int DELAY = 25; private Image star; private Thread animator; private int x, y; public Board() { initBoard(); } private void loadImage() { ImageIcon ii = new ImageIcon("star.png"); star = ii.getImage(); } private void initBoard() { setBackground(Color.BLACK); setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT)); setDoubleBuffered(true); loadImage(); x = INITIAL_X; y = INITIAL_Y; } @Override public void addNotify() { super.addNotify(); animator = new Thread(this); animator.start(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); drawStar(g); } private void drawStar(Graphics g) { g.drawImage(star, x, y, this); Toolkit.getDefaultToolkit().sync(); } private void cycle() { x += 1; y += 1; if (y > B_HEIGHT) { y = INITIAL_Y; x = INITIAL_X; } } @Override public void run() { long beforeTime, timeDiff, sleep; beforeTime = System.currentTimeMillis(); while (true) { cycle(); repaint(); timeDiff = System.currentTimeMillis() - beforeTime; sleep = DELAY - timeDiff; if (sleep < 0) { sleep = 2; } try { Thread.sleep(sleep); } catch (InterruptedException e) { System.out.println("Interrupted: " + e.getMessage()); } beforeTime = System.currentTimeMillis(); } } }
In the previous examples, we executed a task at specific intervals. In this example, the animation will take place inside a thread. The run()
method is called only once. This is why why we have a while loop in the method. From this method, we call the cycle()
and the repaint()
methods.
@Override public void addNotify() { super.addNotify(); animator = new Thread(this); animator.start(); }
The addNotify()
method is called after our JPanel
has been added to the JFrame
component. This method is often used for various initialisation tasks.
We want our game run smoothly, at constant speed. Therefore we compute the system time.
timeDiff = System.currentTimeMillis() - beforeTime; sleep = DELAY - timeDiff;
The cycle()
and the repaint()
methods might take different time at various while cycles. We calculate the time both methods run and subtract it from the DELAY
constant. This way we want to ensure that each while cycle runs at constant time. In our case, it is DELAY
ms each cycle.
This part of the Java 2D games tutorial covered animation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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