最近在学习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)方法即可。
这样一来,添加组件的操作就变得简单规范啦,当然,如果要写一个比较完整的游戏还是有很多东西要做的,这只是游戏制作的很小一部分,同时也是一种代码实现思路,希望对你有所帮助。
|