java游戏学习_Java游戏学习

这几天一直在看 <<

Killer Game Programming in Java>>,非常经典的好书,现在对游戏有了一个基本的认识,过几天就写个贪吃蛇出来.

因为看得有点快,真正准备写代码的时候又发现自己对一些基本知识点还是比较模糊,又返回去看前面.边看边做笔记,感觉确实理解得要更加清楚了.

*FPS 和 按时间准确的Sleeping*

============================

FPS

--------

一个测量animation速度的常用指标就是桢速(每秒显示桢的数量:frames per second),简称FPS.在下面的

代码中,做一次gameUpdate和gameRender的循环就对应一个桢.

比如100FPS表示run()中的每次迭代应该用1000/100 == 10ms.这个迭代时间存在period变量中.

/

/code example:

public void run() {

long beforeTime, timeDiff, sleepTime;

beforeTime = System.currentTimeMillis();

running = true;

while(running) {

gameUpdate();//计算game中的model

gameRender();//画一个image, "double buffer"

paintScreen();//在screen上显示image

timeDiff = System.currentTimeMillis() - beforeTime;

sleepTime = period - timeDiff;//计算需要sleep的时间

if (sleepTime <= 0)

sleepTime = 5;

try {

Thread.sleep(sleepTime);

}catch(InterruptedException e);

beforeTime = System.currentTimeMillis();

}

}

--------

Timer Resolution

连续调用两次timer中间必需的最小时间.这样才能保证每次调用返回不同的时间.

比如:

long t1 = System.currentTimeMillis( );

long t2 = System.currentTimeMillis( );

long diff = t2 - t1;  // 实际输出是0,单位是ms

在win95和win98上,resolution值是55ms,说明只有在每隔55ms后调用timer才会返回不同的值.

在animation loop中,resolution的会导致animation比期望的要慢而且减小了FPS.因为如果gameUpdate

和gameRender的时间小于55ms,那么timeDiff变量就会设为0,那么sleepTime就会比实际需要的时间要大.

为了防止这个问题,每个循环周期时间必须大于55ms,表示最高限制是大约18FPS.这个frame rate被广泛接

受,因为屏幕刷新过慢会表现得象闪屏(excessive flicker)一样.

在Windows2000, NT和XP上, currentTimeMillis()的resolution是10到15ms,这样就可以获得67-100FPS.

这个值对游戏来说是可以接受的.在Mac OS X和Linux上的resolution是1ms,相当好了.

--------

改进过的J2SE Timers

J2SE 1.4.2有一个没有被写入到文档的精确到微秒的timer class: sum.misc.Perf.

Pref计算diff的方法:

Pref perf = Perf.getPerf();

long countFrep = perf.highResFrequency();

long count1 = perf.highResCounter();

long count2 = perf.highResCounter();

long diff = (count2 - count1) * 1000000000L / countFreq;

//转换成纳秒nanoseconds

nanoseconds:十忆分之1秒

J2SE 5.0中解决了这个timer的问题,System.nanoTime(),可以象Pref timer一样来计算时间.

long count1 = System.nanoTime();

long count2 = System.nanoTime();

long diff = (count1 - count2);//单位是纳秒

--------

Non-J2SE Timers

Java 3D timer的计算方法:

long t1 = J3DTimer.getValue();

long t2 = j3DTimer.getValue();

long diff = t2 - t1;//单位是纳秒

*更好的Sleeping*

================

animation循环依赖一个好的timer和精确的sleep方法调用.现在在前面的基础上改进代码,以保证需要

的桢速.

//code example:

private static final int NO_DELAYS_PER_YIELD = 16;

public void run() {

long beforeTime, afterTime, timeDiff, sleepTime;

long overSleepTime = 0L;

int noDelays = 0;

beforeTime = J3DTimer.getValue();

running = true;

while(running) {

gameUpdate();

gameRender();

paintScreen();

afterTime = J3DTimer.getValue();

timeDiff = afterTime - beforeTime;

sleepTime = (period - timeDiff) - overSleepTime;

if (sleepTime > 0) {

try{

Thread.sleep(sleepTime/1000000L); //nano -> ms

}catch(InterrruptedException ex){}

overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;

}else {    //sleepTime <= 0; 桢的时间大于期望的period,

//不sleep直到sleepTime > 0 或 连续运行了NO_DELAYS_PER_YIELD次

overSleepTime = 0L;

if (++noDelays >= NO_DELAYS_PER_YIELD) {

Thread.yield();

noDelays = 0;

}

}

beforeTime = J3DTimer.getValue();

}

}

如果sleep()设置成sleep 10 ms,但是确用了12 ms,那么overSleepTime会被设置成2 ms,下次就会少

sleep 2ms.

如果桢的时间大于期望的period,那么就不浪费时间sleep,而是一直循环,一定次数后调用Thread.yield(),

这样来节省时间而又保证其它线程有机会运行.

*FPS和UPS*

==========

除了FPS,还有一个有用的测量animation速度的指标:UPS. 在现在的animation循环中每次迭代拥有一次

update和render.但是这个对应不是必需的.在循环中,可以每一次render前做两次updates.

//code example:

public void run() {

...

running = true;

while(running) {

gameUpdate();//update 游戏状态

gameUpdate();//再一次update 游戏状态

gameRender();

paintScreen();

//sleep

}

System.exit(0);

}

在上面的代码中,如果游戏提供了50FPS,那么就每秒就会做100次updates.

从Rendering中分离Updates

--------

对于高FPS速率的一个限制是update和render所需要的时间.假设period = 5ms(1000/5 == 200FPS),如果

update和render需要的时间大于5ms,那么200FPS就不可能达到.而它们所需要的时间中大部分是被render所消耗的.

在这种情况下,增加游戏速度的方法是增加UPS的速率.在编程中,也就是在每次迭代中增加gameUpdate的次数.

但是注意,如果增加gameUpdate的次数过多的话会造成游戏不连续,因为有许多游戏状态没有显示出来.

新的代码:

//code example:

private static int MAX_FRAME_SKIPS = 5;

public void run() {

long beforeTime, afterTime, timeDiff, sleepTime;

long overSleepTime = 0L;

int noDelays = 0;

long excess = 0L;

beforeTime = J3DTimer.getValue();

running = true;

while(running) {

gameUpdate();

gameRender();

paintScreen();

afterTime = J3DTimer.getValue();

timeDiff = afterTime - beforeTime;

sleepTime = (period - timeDiff) - overSleepTime;

if (sleepTime > 0) {

try{

Thread.sleep(sleepTime/1000000L); //nano -> ms

}catch(InterrruptedException ex){}

overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;

}else {

excess -= sleepTime;

overSleepTime = 0L;

if (++noDelays >= NO_DELAYS_PER_YIELD) {

Thread.yield();

noDelays = 0;

}

}

beforeTime = J3DTimer.getValue();

int skips = 0;

while((excess > period) && (skips < MAX_FRAME_SKIPS)) {

excess -= period;

gameUpdate();

skips++;

}

}

}   如果update/render实际需要12ms,但是需要的period是10ms,那么sleepTime会是-2ms(由于引入overSleepTime, 所以可能会更小一点).额外的执行时间被加到excess变量中.   当excess达到period大小时,那么相当于丢失了一个桢,在while循环中,为每次丢失执行gameUpdate.但是限制在 MAX_FRAME_SKIPS里.   这样做的优点是,如果一个游戏的update/render速度不能满足期望的FPS时,那么就会另外执行gameUpdate. 这样改变了游戏的状态但是没有马上显示出来,最后用户会看见游戏移动更"快"了,虽然每秒钟显示的桢数并没有 改变.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值