效果图:
针对上次的只有更新窗口时画面才会重画,解决方法很简单:
只需要新建一个线程,隔一段时间就通知游戏窗口重画,不停的在窗口上重画,看起来就好像是动态的画面,实质上只是静态的图片
隔多久呢?
一般的2D游戏的帧数在20-30fps左右
什么是帧/FPS:
帧(Frame):画在游戏窗口上的一张静态图
帧速率(FPS/帧数):静态图片更新速度的快慢(FPS = 1秒 / 1秒内渲染的帧数)
首先修改Game类,添加以下代码:
//.....
private int fps;//游戏的FPS
//.....
//.....
/**
* 退出游戏
*/
public void exit()
{
System.exit(1);
}
/**
* 返回游戏的FPS
*/
public int getFPS()
{
return fps;
}
/**
* 设置游戏的FPS
* @param fps 新FPS
* @return 是否设置成功
*/
public Boolean setFPS(int fps)
{
if(fps <= 0)
return false;
else
{
this.fps = fps;
return true;
}
}
//.....
新建类RenderThread,用来定时重画窗口
package com.game.engine;
public class RenderThread implements Runnable
{
private Thread thread;
private boolean exited = false;//游戏是否退出
private int interval;//每次绘制隔多久
public Game game;
public RenderThread(Game g)
{
game = g;
interval = 1000 / game.getFPS();//计算出隔多久重画一次(毫秒)
System.out.println("[Render]Created");
System.out.println("[Render]Render interval: " + interval + " ms");
}
@Override
public void run()
{
System.out.println(thread.getName() + "Start rendering");
while (!exited)
{
game.repaint();//通知游戏窗口重画
try
{
Thread.sleep(interval);//间隔一定时间渲染一次,来实现稳定fps
}
catch (Exception e)
{
System.out.println(thread.getName() + "Error: " + e.toString());
break;
}
}
System.out.println(thread.getName() + "Stop rendering");
game.exit();
}
public void start()
{
if(thread == null)
{
thread = new Thread(this, "[RenderThread]");
thread.start();
}
}
}
在启动游戏的同时启动渲染线程
//Game类...
private RenderThread render;
//....
public Game(int windowWidth, int windowHeight, int fps)
{
width = windowWidth;
height = windowHeight;
this.fps = fps;
windowTitle = "Game";
backgroundColor = Color.BLACK;
gameObjects = new ArrayList<GameObject>();
createWindow();
render = new RenderThread(this);//初始化线程
render.start();//启动线程
}
启动游戏,看看效果:
可以看到不需要刷新窗口就能显示出来
下面让Sprite动起来,以便更好地观察效果
修改GameObject类,添加onTick方法,这个方法会在游戏渲染每一帧的时候被调用
/**
* 在游戏的每帧被调用
*/
public void onTick()
{
}
下面为了修改让这个方法有实际用途,创建类Player继承Sprite,就可以重写onTick方法:
package com.undertale;
import com.game.engine.GameObject;
import com.game.engine.Sprite;
public class Player extends Sprite
{
public Player(String filePath)
{
super(filePath);
}
@Override
public void onTick()
{
this.transfer(1, 0);//在当前位置的基础上向右移动1个单位长度
}
}
修改Main方法:
game = new Game(700, 500, 30);//30fps
Player s = new Player("C:\\Users\\Administrator\\Desktop\\a\\spr_maincharad\\char_forward_0.png");
s.setPosition(50, 50);
game.addGameObject(s);
启动游戏,好像还是没效果?
因为onTick方法还没有被调用!
修改一下Game类,调用onTick方法
//重写窗体绘制方法
@Override
public void paint(Graphics g)
{
//渲染所有的sprite
for(int i = 0; i < gameObjects.size(); i++)
{
gameObjects.get(i).onTick();
gameObjects.get(i).draw(g);
}
}
为什么有拖影?因为我们在画第二帧时第一帧画的内容并没有被清除掉,所以会有长长的拖影
在每次渲染时清除上次画的内容即可
//重写窗体绘制方法
@Override
public void paint(Graphics g)
{
clear(g);
//渲染所有的sprite
for(int i = 0; i < gameObjects.size(); i++)
{
gameObjects.get(i).onTick();
gameObjects.get(i).draw(g);
}
}
public void clear(Graphics g)
{
g.setColor(Color.BLACK);//设置画笔颜色为黑色
g.fillRect(0, 0, width, height);//填充整个窗口为黑色
}
再运行一次:
但是出现了闪烁现象,这是下一次要解决的