Javaでゲームを作る入門 @ うぃき

ひなちゃんぺろぺろ

最終更新:

takejava

- view
管理者のみ編集可
小鳥遊ひなちゃんを走らせるだけのデスクトップアクセサリを作成しましたので、ソースを公開します。

ポイントしては、
背景の透明化が1.7で簡単にできるようになった?ので、それを使っています。
※新しい機能を使っているわけではないので、仕様が変わったというのが正しいかもしれないですが、正直よくわかっていません
また、タスクトレイに表示させたくなかったので、ダイアログを使っています。
システムトレイも初めて使ってみました。あまりに簡単にできたので拍子抜けしました。

ゲームに使える部分といったら・・・、どこなんでしょうね(゚Д゚)

追記:
見直してみると
if (hinaIndex + 1 < hinaImage.size())
hinaIndex += 1;
else
hinaIndex = 0;
これって
hinaIndex = (hinaIndex + 1) % hinaImage.size();
でよくね?思いました(^ω^)ペロ
基本はわかりやすさ重視!


package main;
 
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.MenuItem;
import java.awt.MouseInfo;
import java.awt.PointerInfo;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
 
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
 
import resource.ImageManager;
 
/**
 * エントリポイント
 *
 * @author T△T
 *
 */
public class HinaStandUp {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// JDialog.setDefaultLookAndFeelDecorated(true); // 枠付きで窓の背景を透明化する
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new HinaTrayIcon();
			}
		});
	}
}
 
/**
 * 実質メインのクラス
 *
 * @author T△T
 *
 */
class HinaTrayIcon {
	// 設定用
	int deltaX;
	int fps;
	double scale;
	int baseHeight; // 読み込んだ画像の最大の高さを保持
	int dispWidth;
	int dispHeight;
	Hina hina;
	Setting setting;
 
	JLabel labelActualFPS = new JLabel();
	JLabel labelHinaPoint = new JLabel();
 
	FPSKeeper fpsKeeper; // スレッドループ用
 
	HinaTrayIcon() {
		// ポップアップメニューの準備
		PopupMenu menu = new PopupMenu();
		MenuItem item = new MenuItem("設定");
		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				if (setting != null) {
					PointerInfo pointerInfo = MouseInfo.getPointerInfo();
					setting.setLocation(
							pointerInfo.getLocation().x - setting.getWidth(),
							pointerInfo.getLocation().y - setting.getHeight());
					setting.pack();
					setting.setVisible(true);
				}
			}
		});
		menu.add(item);
		item = new MenuItem("終了");
		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				if (hina != null) {
					hina.setVisible(false);
					hina.dispose();
				}
				if (setting != null) {
					setting.setVisible(false);
					setting.dispose();
				}
				System.exit(0);
			}
		});
		menu.add(item);
 
		// システムトレイの準備
		SystemTray tray = SystemTray.getSystemTray();
		TrayIcon icon = new TrayIcon(
				ImageManager.getInstance().getImage("icon.png"),
				"小鳥遊ひなちゃんが走るだけのやーつ!だお",
				menu);
		try {
			tray.add(icon);
		} catch (AWTException e) {
			e.printStackTrace();
		}
 
		// 初期化処理
		deltaX = -11;
		fps = 20;
		scale = 0.5;
 
		// 他のオブジェクトの準備
		fpsKeeper = new FPSKeeper(20);
		hina = new Hina();
		setting = new Setting();
 
		// ひなの描画スタート
		Thread thread = new Thread(hina);
		thread.start();
	}
 
	/**
	 * ひなちゃんのダイアログ
	 * アンデコして、背景をかぎりなく透明に近い値に設定
	 * Ver1.7だと
	 * 0.001f クリックできない限りなく透明な値
	 * 0.01f クリックできる限りなく透明な値
	 * Ver1.6だと
	 * 0.001fも0.01fも透明にならない
	 *
	 * @author T△T
	 *
	 */
	class Hina extends JDialog implements Runnable {
		/**
		 *
		 */
		private static final long serialVersionUID = 2544997992113824284L;
		int frameCount;
		int hinaX;
		int hinaIndex;
		boolean isDragged;
		ArrayList<BufferedImage> hinaImage = new ArrayList<BufferedImage>();
		ArrayList<BufferedImage> hinaFlipImage = new ArrayList<BufferedImage>();
		ArrayList<BufferedImage> hinaImageMotionBlur = new ArrayList<BufferedImage>();
		ArrayList<BufferedImage> hinaFlipImageMotionBlur = new ArrayList<BufferedImage>();
 
		Hina() {
			// ひなちゃんをクリックした状態でESCキーで終了
			addKeyListener(new KeyAdapter() {
				@Override
				public void keyPressed(KeyEvent e) {
					if (e.getKeyChar() == KeyEvent.VK_ESCAPE)
						System.exit(0);
				}
			});
			GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
			DisplayMode displayMode = env.getDefaultScreenDevice().getDisplayMode();
			// 変数widthとheightに画面の解像度の幅と高さを代入
			dispWidth = displayMode.getWidth();
			dispHeight = displayMode.getHeight();
			setIgnoreRepaint(true);
			setUndecorated(true); // タイトルバーとサイズ変更の枠を消す
			setBackground(new Color(0, 0, 0, 0.001f));
			setAlwaysOnTop(true);
			setFocusable(true);
			hinaImage.add(ImageManager.getInstance().getImage("01.png"));
			hinaImage.add(ImageManager.getInstance().getImage("02.png"));
			hinaImage.add(ImageManager.getInstance().getImage("03.png"));
			hinaImage.add(ImageManager.getInstance().getImage("04.png"));
			hinaImage.add(ImageManager.getInstance().getImage("05.png"));
			hinaImage.add(ImageManager.getInstance().getImage("06.png"));
			hinaImage.add(ImageManager.getInstance().getImage("07.png"));
			hinaImage.add(ImageManager.getInstance().getImage("08.png"));
			hinaFlipImage.add(ImageManager.getInstance().getImage("01.png", true));
			hinaFlipImage.add(ImageManager.getInstance().getImage("02.png", true));
			hinaFlipImage.add(ImageManager.getInstance().getImage("03.png", true));
			hinaFlipImage.add(ImageManager.getInstance().getImage("04.png", true));
			hinaFlipImage.add(ImageManager.getInstance().getImage("05.png", true));
			hinaFlipImage.add(ImageManager.getInstance().getImage("06.png", true));
			hinaFlipImage.add(ImageManager.getInstance().getImage("07.png", true));
			hinaFlipImage.add(ImageManager.getInstance().getImage("08.png", true));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("01_20.png"));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("02_20.png"));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("03_20.png"));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("04_20.png"));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("05_20.png"));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("06_20.png"));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("07_20.png"));
			hinaImageMotionBlur.add(ImageManager.getInstance().getImage("08_20.png"));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("01_20.png", true));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("02_20.png", true));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("03_20.png", true));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("04_20.png", true));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("05_20.png", true));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("06_20.png", true));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("07_20.png", true));
			hinaFlipImageMotionBlur.add(ImageManager.getInstance().getImage("08_20.png", true));
			// 適当にベースの高さを算出
			for (int index = 0; index < hinaImage.size(); index++) {
				BufferedImage image = hinaImage.get(index);
				baseHeight = Math.max(baseHeight, image.getHeight());
			}
			// 窓の初期サイズと位置決定
			setBounds(
					0,
					dispHeight / 2 - (int) (baseHeight * scale) / 2,
					dispWidth,
					(int) (baseHeight * scale));
			addMouseMotionListener(new MouseAdapter() {
				@Override
				public void mouseDragged(MouseEvent e) {
					isDragged = true;
					hinaX = e.getX();
					setLocation(0, e.getYOnScreen() - getHeight() / 2);
				}
			});
			addMouseListener(new MouseAdapter() {
				@Override
				public void mouseReleased(MouseEvent e) {
					isDragged = false;
				}
 
				@Override
				public void mousePressed(MouseEvent e) {
					isDragged = true;
					hinaX = e.getX();
					setLocation(0, e.getYOnScreen() - getHeight() / 2);
				}
			});
 
			// 初期化
			frameCount = 0;
			hinaX = getWidth();
			hinaIndex = 0;
			isDragged = false;
 
			setVisible(true);
		}
 
		@Override
		public void run() {
			while (true) {
				repaint();
				fpsKeeper.sleep();
			}
		}
 
		@Override
		public void paint(Graphics g) {
			super.paint(g); // ALLリセット
			// 計測値更新
			// 大体1秒ごとにFPSの実測値を更新
			if (frameCount++ % fps == 0)
				labelActualFPS.setText(fpsKeeper.getFPS());
			labelHinaPoint.setText("" + frameCount);
			// ひなちゃん更新
			// 画像の要素番号更新
			if (hinaIndex + 1 < hinaImage.size())
				hinaIndex += 1;
			else
				hinaIndex = 0;
			// 画像や座標やサイズの決定
 
			BufferedImage image;
			if (deltaX < -50)
				image = hinaImageMotionBlur.get(hinaIndex);
			else if (50 < deltaX)
				image = hinaFlipImageMotionBlur.get(hinaIndex);
			else
				image = deltaX <= 0 ? hinaImage.get(hinaIndex) : hinaFlipImage.get(hinaIndex);
 
			int width = (int) (image.getWidth() * scale);
			int height = (int) (image.getHeight() * scale);
			if (!isDragged) { // ニュートラルな状態
				hinaX += deltaX; // 移動
				if (hinaX < -width - 10) { // 画面左端でリセット
					hinaX = getWidth() + width;
				}
				if (dispWidth + height + 10 < hinaX) { // 画面右端でリセット
					hinaX = -width;
				}
			}
			// 描画更新
			g.drawImage(image, hinaX - width / 2, 0, width, height, null);
		}
 
		@Override
		public void update(Graphics g) {
			paint(g);
		}
	}
 
	class Setting extends JDialog {
		/**
		 *
		 */
		private static final long serialVersionUID = -1392076571566176523L;
		Font font = new Font("", Font.BOLD, 20);
		JSlider sliderDeltaX;
		JSlider sliderFPS;
		JSlider sliderScale;
		JLabel labelDeltaX;
		JLabel labelFPS;
		JLabel labelScale;
 
		Setting() {
			super();
			setTitle("設定");
			setLocationRelativeTo(null);
			setMinimumSize(new Dimension(400, 160));
			setPreferredSize(new Dimension(400, 160));
 
			/**
			 * 計測パネル
			 */
			JPanel panelCalc = new JPanel();
			JPanel panelActualFPS = new JPanel();
			panelActualFPS.setBorder(new TitledBorder(new EtchedBorder(), "FPS計測値"));
			labelActualFPS.setFont(font);
			labelActualFPS.setText("" + fps);
			panelActualFPS.add(labelActualFPS);
			JPanel panelHinaPoint = new JPanel();
			panelHinaPoint.setBorder(new TitledBorder(new EtchedBorder(), "ひなポイント"));
			labelHinaPoint.setFont(font);
			labelHinaPoint.setText("0");
			panelHinaPoint.add(labelHinaPoint);
			panelCalc.setLayout(new GridLayout(2, 0));
			panelCalc.add(panelActualFPS);
			panelCalc.add(panelHinaPoint);
 
			/**
			 * 移動量パネル
			 */
			JPanel panelDeltaX = new JPanel();
			panelDeltaX.setBorder(new TitledBorder(new EtchedBorder(), "x軸方向の移動量"));
			labelDeltaX = new JLabel("" + deltaX);
			sliderDeltaX = new JSlider(-100, 100, deltaX);
			sliderDeltaX.setMajorTickSpacing(10);
			sliderDeltaX.setMinorTickSpacing(1);
			sliderDeltaX.setPaintTicks(true);
			sliderDeltaX.setToolTipText("x軸方向の移動量");
			sliderDeltaX.addChangeListener(new ChangeListener() {
				@Override
				public void stateChanged(ChangeEvent e) {
					deltaX = sliderDeltaX.getValue();
					labelDeltaX.setText("" + deltaX);
				}
			});
			panelDeltaX.setLayout(new BorderLayout());
			panelDeltaX.add(labelDeltaX, BorderLayout.NORTH);
			panelDeltaX.add(sliderDeltaX, BorderLayout.CENTER);
 
			/**
			 * FPSパネル
			 */
			JPanel panelFPS = new JPanel();
			panelFPS.setBorder(new TitledBorder(new EtchedBorder(), "FPS"));
			labelFPS = new JLabel("" + fps);
			sliderFPS = new JSlider(JSlider.VERTICAL, 1, 300, 20);
			sliderFPS.setMajorTickSpacing(10);
			sliderFPS.setMinorTickSpacing(1);
			sliderFPS.setPaintTicks(true);
			sliderFPS.setToolTipText("FPS");
			sliderFPS.addChangeListener(new ChangeListener() {
				@Override
				public void stateChanged(ChangeEvent e) {
					fps = sliderFPS.getValue();
					labelFPS.setText("" + fps);
					fpsKeeper.setFPS(fps);
				}
			});
			panelFPS.setLayout(new BorderLayout());
			panelFPS.add(labelFPS, BorderLayout.CENTER);
			panelFPS.add(sliderFPS, BorderLayout.EAST);
 
			/**
			 * スケール値パネル
			 */
			JPanel panelScale = new JPanel();
			panelScale.setBorder(new TitledBorder(new EtchedBorder(), "スケール"));
			labelScale = new JLabel("" + scale);
			sliderScale = new JSlider(JSlider.VERTICAL, 1, 1000, 50);
			sliderScale.setMajorTickSpacing(10);
			sliderScale.setMinorTickSpacing(1);
			sliderScale.setPaintTicks(true);
			sliderScale.setToolTipText("スケール");
			sliderScale.addChangeListener(new ChangeListener() {
				@Override
				public void stateChanged(ChangeEvent e) {
					scale = (double) sliderScale.getValue() / 100;
					labelScale.setText("" + scale);
					// スケール値に応じて、ダイアログの高さを更新
					hina.setBounds(
							0,
							dispHeight / 2 - (int) (baseHeight * scale) / 2,
							dispWidth,
							(int) (baseHeight * scale));
				}
			});
			panelScale.setLayout(new BorderLayout());
			panelScale.add(labelScale, BorderLayout.CENTER);
			panelScale.add(sliderScale, BorderLayout.EAST);
 
			// グリッドレイアウトで均等に広げて配置
			setLayout(new GridLayout());
			add(panelCalc);
			add(panelDeltaX);
			add(panelFPS);
			add(panelScale);
			pack();
 
			// ボリュームの左クリックみたいなやーつ
			addWindowListener(new WindowAdapter() {
				@Override
				public void windowDeactivated(WindowEvent e) {
					setVisible(false);
				}
			});
		}
	}
}
 

続きまして、ひなちゃんの画像を読み込むクラスです。
x軸方向での反転をさせた画像をもつようにしているだけです。
aaaaというクラスが画像をもっています。
aaaaとか意味のない名前付けは良い子は真似しないでくださいね。
いいクラス名が思いつかなかったんだよ(^ω^)スマンネ

package resource;
 
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
 
import javax.imageio.ImageIO;
 
public class ImageManager {
	static private final ImageManager instance = new ImageManager();
 
	public static ImageManager getInstance() {
		return instance;
	}
 
	private ImageManager() {
		initAll();
	}
 
	private final String base = "resource/image/";
	private final String[] fileNames = {
			"icon.png",
			"01.png",
			"02.png",
			"03.png",
			"04.png",
			"05.png",
			"06.png",
			"07.png",
			"08.png",
			"01_20.png",
			"02_20.png",
			"03_20.png",
			"04_20.png",
			"05_20.png",
			"06_20.png",
			"07_20.png",
			"08_20.png",
	};
	HashMap<String, aaaa> imagePool = new HashMap<String, aaaa>();
 
	class aaaa {
		BufferedImage image;
		BufferedImage xFlipImage;
 
		aaaa(BufferedImage image) {
			this.image = image;
			// ついでに反転画像をもっておく
			xFlipImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
			Graphics2D g = xFlipImage.createGraphics();
			g.drawImage(image, image.getWidth(), 0, -image.getWidth(), image.getHeight(), null);
			g.dispose();
		}
 
		BufferedImage get(boolean isFlip) {
			if (isFlip)
				return xFlipImage;
			return image;
		}
	}
 
	public void initAll() {
		for (int index = 0; index < fileNames.length; index++) {
			String fileName = fileNames[index];
 
			BufferedImage img = null;
			try {
				img = ImageIO.read(ImageManager.class.getClassLoader().getResourceAsStream(base + fileName));
			} catch (IOException e) {
				e.printStackTrace();
			}
			imagePool.put(fileName, new aaaa(img));
		}
	}
 
	public BufferedImage getImage(String fileName) {
		return getImage(fileName, false);
	}
 
	public BufferedImage getImage(String fileName, boolean isXFlip) {
		return imagePool.get(fileName).get(isXFlip);
	}
 
	public void printAllData() {
		Iterator<String> ite = imagePool.keySet().iterator();
		while (ite.hasNext()) {
			String fileName = ite.next();
			System.out.print("ファイル名:" + fileName);
			System.out.println(" 内容:" + imagePool.get(fileName));
		}
	}
}
 


記事メニュー
ウィキ募集バナー