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

来源:互联网 发布:摩拜单车在上海的数据 编辑:程序博客网 时间:2024/05/16 12:35

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

  由于只是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;    }    @Overridepublic void draw(GraphicsContext gContext) {gContext.save();gContext.setFont(font);gContext.setFill(color);if (text != null) {gContext.fillText(text, getX(), getY());}gContext.restore();}@Overridepublic 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;    }@Overridepublic double getWidth(){        FontMetrics  fm = Toolkit.getToolkit().getFontLoader().getFontMetrics(font);     return fm.computeStringWidth(text);}@Overridepublic 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);}@Overridepublic 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();}@Overridepublic 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 onMousePressed(MouseEvent e) {if (onMenuItemClickListener != null)for (int i = 0; i < textObjects.length; i++) {if (textObjects[i].isCollisionWith(e.getX(), e.getY())) {onMenuItemClickListener.onMenuItemClick(i);}}}@Overridepublic 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();}@Overridepublic 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. 


2 0
原创粉丝点击