JavaFX游戏制作思路

最近在学习JavaFX游戏制作,这篇文章将自己的思考所得记录下来。

JavaFX游戏制作主要是在Canvas内绘画。每一帧都先检查所有的动作,然后将相应的组件绘制上去。

由此可见,最重要的类就是组件,其中组件包括,位置,长宽,图片以及图层等信息,并且每个组件可能还会有动作(比如游戏中的敌人,会自己找到你,并且攻击你)。

我定义了一个基础的Component类记录组件的基础功能

@Data
public class Component{

    // 对应图片的url
    private String image_url;
    private Image image;
    // 位置信息(从左上角开始)
    private Integer pos_x;
    private Integer pos_y;
    // 宽度和高度
    private Integer width;
    private Integer height;
    // 图层数,代表什么时候被渲染,数越大,表示越在表层
    private Integer layout;
    // 用于进行碰撞检测
    private Rect rect;


    class Rect{
        // 方形中心坐标
        public Integer x;
        public Integer y;
        public Integer width;
        public Integer height;
        public Rect(Integer x, Integer y, Integer width, Integer height){
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }
    }

    public Component(Image image, Integer width, Integer height, Integer layout){
        this.image = image;
        this.width = width;
        this.height = height;
        this.layout = layout;
        this.pos_x = 0;
        this.pos_y = 0;
        Rect rect = new Rect(pos_x+(width/2), pos_y+(height/2), width, height);
    }

    public Component(String image, Integer width, Integer height, Integer layout){
        this.image = new Image(image, width, height, true, true);
        this.width = width;
        this.height = height;
        this.layout = layout;
        this.pos_x = 0;
        this.pos_y = 0;
        Rect rect = new Rect(pos_x+(width/2), pos_y+(height/2), width, height);
    }

    public Component(String image, Integer width, Integer height, Integer x, Integer y, Integer layout){
        this.image = new Image(image, width, height, true, true);
        this.width = width;
        this.height = height;
        this.layout = layout;
        this.pos_x = x;
        this.pos_y = y;
        Rect rect = new Rect(pos_x+(width/2), pos_y+(height/2), width, height);
    }

    // 移动
    public void gotoxy(Integer x, Integer y){
        this.pos_x = x;
        this.pos_y = y;
    }

    // 碰撞检测
    // 返回true表示碰撞了,返回false表示没有碰撞
    public boolean checkimpact(Rect rect){
        // 从两个方向上进行检测
        // 两个轴上的距离
        int distance_x = Math.abs(rect.x-this.rect.x);
        int distance_y = Math.abs(rect.y-this.rect.y);
        // 两个轴上的最大距离
        int max_distance_x = Math.min(rect.width, this.rect.width);
        int max_distance_y = Math.min(rect.height, this.rect.height);
        if(distance_x<=max_distance_x || distance_y<=max_distance_y){
            return true;
        }
        return false;
    }
}

这只是定义了基础的功能,但是不包括人物的动作,为了让每个类都能有自己的动作,我们需要一个接口

@FunctionalInterface
public interface FrameDoing {

    // 每一帧的动作
    public void doing(Component component);

}

并且定义一个类,继承Component以及实现FrameDoing接口

public class Role extends Component implements FrameDoing{

    // 每一帧的动作
    private FrameDoing frameDoing;

    public Role(String image, Integer width, Integer height, Integer x, Integer y,Integer layout, FrameDoing frameDoing) {
        super(image, width, height, x, y,layout);
        this.frameDoing = frameDoing;
    }


    @Override
    public void doing(Component component) {
        if(this.frameDoing != null){
            frameDoing.doing(component);
        }
    }

}

有了组件的基础信息,我们就需要一个管理组件的类,因此定义了Painter类,这个类负责检查每个组件的动作,并且将图片绘制到画布上。

@Data
public class Painter {

    // 传入一个canves的绘画对象
    private Canvas canvas;
    private GraphicsContext gc;
    // 记录所有组件的List
    private List<Component> componentList;


    public Painter(Canvas canvas){
        this.canvas = canvas;
        this.gc = canvas.getGraphicsContext2D();
        this.componentList = new ArrayList<Component>();
    }

    public Painter(Canvas canvas, GraphicsContext gc){
        this.canvas = canvas;
        this.gc = gc;
        this.componentList = new ArrayList<Component>();
    }

    // 开启每一帧的动作
    public void start(int frame){
        new Thread(() -> {
            while(true) {
                checkframe();
                try {
                    Thread.sleep(1000/frame);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(() -> {
            while (true){
                paint();
                try {
                    Thread.sleep(1000/frame);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    // 检查每个角色每一帧的动作
    public void checkframe(){
        for(int i=0; i<componentList.size(); i++){
            if(componentList.get(i) instanceof Role){
                ((Role) componentList.get(i)).doing(componentList.get(i));
            }
        }
    }

    // 清空画布
    public void flush(){
        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
    }

    // 提供一个绘制方法,可以在画布上绘图
    public void paint(){
        System.out.println("开始绘图");
        flush();
        // 根据layout等级,从小到大绘制
        int num = 0;
        // 从1开始
        int currentLayout = 1;
        while(num < componentList.size()){
            for(int i=0; i<componentList.size(); i++){
                if(componentList.get(i).getLayout() == currentLayout){
                    // 绘图
                    gc.drawImage(componentList.get(i).getImage(),
                            componentList.get(i).getPos_x(),
                            componentList.get(i).getPos_y(),
                            componentList.get(i).getWidth(),
                            componentList.get(i).getHeight());
                    num++;
                }
            }
            currentLayout++;
        }
    }

    // 往画家的组件库中添加组件
    // 返回插入列表的索引
    public Integer addComponent(Component component){
        componentList.add(component);
        return componentList.size()-1;
    }

    // 从组件库中取组件
    public Component getComponent(Integer index){
        if(this.componentList.size() == 0){
            return null;
        }
        if(index < 0){
            return componentList.get(0);
        }
        if(index >= componentList.size()){
            return componentList.get(componentList.size()-1);
        }
        return componentList.get(index);
    }
}

有了上面的信息,就需要一个主类来执行程序

public class Direct extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Canvas canvas = new Canvas(500,500);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        Painter painter = new Painter(canvas, gc);

        // 创建组件
        Component component1 = new Role("/image/aaa.jpg", 250, 300, 0,0,1, new FrameDoing(){

            @Override
            public void doing(Component component) {
                System.out.println("第一个角色的动作");
                component.setPos_x(component.getPos_x()+5);


            }
        });
        Component component2 = new Role("/image/刻晴111.png", 100, 100, 0, 0, 2, new FrameDoing() {
            @Override
            public void doing(Component component) {
                System.out.println("第二个角色的动作");
                component.setPos_x(component.getPos_x()+10);
            }
        });

        painter.addComponent(component1);
        painter.addComponent(component2);



        Pane pane = new Pane();
        pane.getChildren().add(painter.getCanvas());
        Scene scene = new Scene(pane, 500, 500);
        primaryStage.setScene(scene);
        painter.start(20);

        primaryStage.setHeight(600);
        primaryStage.setWidth(800);
        primaryStage.show();

    }
}

如此一来,便可以运行啦。

我们创建一个角色的时候,需要传入FrameDoing的实现类,其中的参数在执行动作的时候传入的就是自己本身,这样就可以在doing(Component component)方法中对自己的各种信息作出改变。

因此,定义一个组件就变得简单了,只需要在实例化的时候给定基本信息,然后给定重写的doing(Component component)方法即可。

这样一来,添加组件的操作就变得简单规范啦,当然,如果要写一个比较完整的游戏还是有很多东西要做的,这只是游戏制作的很小一部分,同时也是一种代码实现思路,希望对你有所帮助。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值