首页 > 代码库 > JavaFX战旗类游戏开发 第四课 属性框和菜单的创建

JavaFX战旗类游戏开发 第四课 属性框和菜单的创建

  上一课中,我们创建了游戏角色。这节课中,我们将会创建菜单,以便后面使用。

  由于只是Demo,我创建的是最简单的形式,如下图所示:

  

  基于游戏开发中的UI控件通常需要有事件(比如图中的移动,攻击,待机,是有事件处理的),我们应该首先创建自己的文字控件。

  文字控件代码如下:

  

import com.sun.javafx.tk.FontMetrics;
import com.sun.javafx.tk.Toolkit;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
/**
 * 文字物件
 * @author Wing Mei
 */
@SuppressWarnings("restriction")
public class TextObject extends BaseObject {

    private String text;
    private Font font = Font.getDefault();
    private double fontSize = Font.getDefault().getSize();
    private Paint color = Color.BLACK;
    
    public TextObject() {
	}
    
    public TextObject(String text){
    	this.text = text;
    }
    
	@Override
	public void draw(GraphicsContext gContext) {
		gContext.save();
		gContext.setFont(font);
		gContext.setFill(color);
		if (text != null) {
			gContext.fillText(text, getX(), getY());
		}
		gContext.restore();
	}

	@Override
	public void update() {

	}
	
	@Override
    public boolean isCollisionWith(double x,double y){
        if(x > getX() && y > getY() - getHeight() && x < getX() + getWidth() && y < getY() - getHeight()  + getHeight()){
            return true;
        }
        return false;
    }

	@Override
	public double getWidth(){
        FontMetrics  fm = Toolkit.getToolkit().getFontLoader().getFontMetrics(font);
     	return fm.computeStringWidth(text);
	}
	
	@Override
	public double getHeight(){
      	FontMetrics fm = Toolkit.getToolkit().getFontLoader().getFontMetrics(font);
      	return fm.getLineHeight();
	}
	
	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

	public Font getFont() {
		return font;
	}

	public void setFont(Font font) {
		this.font = font;
	}

	public Paint getColor() {
		return color;
	}

	public void setColor(Paint color) {
		this.color = color;
	}
	
    public double getFontSize() {
        return fontSize;
    }

    public void setFontSize(double fontSize) {
        this.fontSize = fontSize;
        this.font = new Font(font.getFamily(), fontSize);
    }
}
  由于JavaFX的文字坐标貌似是从左下角开始的,所以碰撞的方法进行了处理。这里,我们使用了FontMetrics来获取文字的宽度和高度(感觉高度不是很准确的样子,先凑合着使用)。

  有了文字控件,属性框和操作菜单就要简单很多。其实就是一个背景+N个文字控件而已。

  下面是属性框的代码:

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;

public class PropertyMenu extends BaseObject {
	private TextObject[] textObjects;
	private Paint color = Color.BLACK;
	private int spaceLine = 5;

	public PropertyMenu(int width, int height) {
		setWidth(width);
		setHeight(height);
		textObjects = new TextObject[7];
		for (int i = 0; i < textObjects.length; i++) {
			textObjects[i] = new TextObject();
			textObjects[i].setColor(Color.WHITE);
		}

	}
    /**
     * 初始化载入某个角色的属性
     * @param player 角色
     */
	public void initPlayer(BasePlayer player) {
		setProperty(textObjects[0], "姓名", player.getName());
		setProperty(textObjects[1], "等级", String.valueOf(player.getLv()));
		setProperty(textObjects[2], "攻击", String.valueOf(player.getAttack()));
		setProperty(textObjects[3], "防御", String.valueOf(player.getDefense()));
		setProperty(textObjects[4], "移动力", String.valueOf(player.getMove()));
		setProperty(textObjects[5], "HP", String.valueOf(player.getHp()) + "/" + player.getHpMax());
		setProperty(textObjects[6], "EXP", String.valueOf(player.getExp()));
	}

	private void setProperty(TextObject textObject, String propertyName, String value) {
		textObject.setText(propertyName + ":" + value);
		textObject.setFontSize(16);
	}

	@Override
	public void draw(GraphicsContext gContext) {
		gContext.save();
		gContext.setStroke(color);
		gContext.setGlobalAlpha(0.8f);
		gContext.fillRect(x, y, width, height);
		if (textObjects != null) {
			for (int i = 0; i < textObjects.length; i++) {
				textObjects[i].setX((getWidth() - textObjects[i].getWidth()) / 2 + getX());
				textObjects[i].setY(getY() + spaceLine * (i + 1) + textObjects[i].getHeight() * (i + 1));
				textObjects[i].draw(gContext);
			}
		}
		gContext.restore();
	}

	@Override
	public void update() {

	}
}
  属性框的代码很简单,就是绘制一个背景+N个TextObject。通过传入BasePlayer来设置每个TextObject的值。由于属性框无需事件,我们在这里也不做事件处理。

  另外,本Demo纯属学习使用,并未做深层次的类结构划分(在我另外的游戏开发库中有做),所以文字控件的绘制需要坐标相对于当前菜单的坐标,需要进行一定的处理。

  接下来是操作菜单的代码:

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;

public class ActionMenu extends BaseObject {
	private TextObject[] textObjects;
	private Paint color = Color.BLACK;
	private int spaceLine = 5;

	private OnMenuItemClickListener onMenuItemClickListener;

	public ActionMenu(String[] strs, int width, int height) {
		setWidth(width);
		setHeight(height);
		textObjects = new TextObject[strs.length];
		for (int i = 0; i < textObjects.length; i++) {
			textObjects[i] = new TextObject();
			textObjects[i].setText(strs[i]);
			textObjects[i].setColor(Color.WHITE);
			textObjects[i].setFontSize(16);
		}

	}
    /**
     * 鼠标事件,会执行回调
     * @param e 鼠标事件
     */
	public void onm ousePressed(MouseEvent e) {
		if (onMenuItemClickListener != null)
			for (int i = 0; i < textObjects.length; i++) {
				if (textObjects[i].isCollisionWith(e.getX(), e.getY())) {
					onMenuItemClickListener.onMenuItemClick(i);
				}
			}
	}

	@Override
	public void draw(GraphicsContext gContext) {
		gContext.save();
		gContext.setGlobalAlpha(0.8f);
		gContext.setStroke(color);
		gContext.fillRect(x, y, width, height);
		for (int i = 0; i < textObjects.length; i++) {
			textObjects[i].setX((getWidth() - textObjects[i].getWidth()) / 2 + getX());
			textObjects[i].setY(getY() + spaceLine * (i + 1) + textObjects[i].getHeight() * (i + 1));
			textObjects[i].draw(gContext);
		}
		gContext.restore();
	}

	@Override
	public void update() {

	}
	

	public TextObject[] getTextObjects() {
		return textObjects;
	}

	public void setTextObjects(TextObject[] textObjects) {
		this.textObjects = textObjects;
	}

	public OnMenuItemClickListener getOnMenuItemClickListener() {
		return onMenuItemClickListener;
	}

	public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
		this.onMenuItemClickListener = onMenuItemClickListener;
	}

	public interface OnMenuItemClickListener {
		public void onMenuItemClick(int index);
	}
}
  
   在操作菜单中,我们新增加了一个OnMenuItemListener,来用于执行鼠标点击后的回调。这也是Java事件机制中很常用的做法。另外,定义了一个onMousePressed方法,注意,由于是自定义的类,这里的onMousePressed事件是不会执行的。我们需要在Canvas的鼠标事件中,调用该方法来模拟执行鼠标操作事件。主要是鼠标点击,通过遍历TextObject列表来判断点击的是哪个TextObject,然后通过OnMenuItemListener来执行回调。方便我们在其他地方做事件处理。

  这样一来,我们两个菜单就都创建好了。接下来,加进我们的Canvas中看看效果吧。

  在MainCanvas中加入定义:

	// 操作菜单
	private ActionMenu actionMenu;
	// 属性菜单
	private PropertyMenu propertyMenu;

  然后初始化:

		// 初始化操作菜单
		actionMenu = new ActionMenu(new String[] { "移动", "攻击", "待机" }, 50, 100);
		actionMenu.setLocation(100, 50);
		actionMenu.setOnMenuItemClickListener(index ->{
		    System.out.println("你点击的是:" + index);
		});
		
		// 属性菜单
		propertyMenu = new PropertyMenu(100, 200);
		propertyMenu.initPlayer(players.get(0));
		// 鼠标事件
		setOnMousePressed(e ->{
			actionMenu.onMousePressed(e);
		});

  在这里的两个事件都用的是Lambada表达式,不熟悉JDK8的可以自行修改。另外,PropertyMenu由于要载入Player的属性,请在Player都初始化完成后,再添加该代码。

  接下来,我们需要将两个菜单绘制出来,在draw方法中添加如下代码:

	actionMenu.draw(gContext);
	propertyMenu.draw(gContext);

  然后我们可以运行看看效果了:


  尝试一下事件把,点击移动,攻击,待机等等。


  事件执行良好吧?

  那么这一节课到此结束了。

  下一节课,我们将会用到定时器的内容,将会创建一个定时器供后面使用。

  本文章为个人原创,版权所有,转载请注明出处:http://blog.csdn.net/ml3947。另外我的个人博客:http://www.wjfxgame.com.