280 likes | 483 Vues
Java 游戏开发 学习情境 6 :小虫动画程序设计. 08 游戏 3-2. 目标: 用 Java 多线程为游戏添加动画,掌握多线程的实现、控制方法。掌握通过双缓冲消除图像闪烁的方法。 任务: 创建一个燃烧的火焰动画,为 “ 电梯模拟游戏 ” 添加人行走,电梯运行的动画. 目标与任务. 教师. 学生. 模仿完成: 绘制图像案例. 提供: 绘制图像案例. 项目核心. 提供: 线程控制图像 间歇显示. 协作完成: 线程控制图像 间歇显示成动画. 功能扩展. 提供: 电梯游戏的 动画和界面. 协作完成: 电梯游戏的 场景动画加载. 应用升级.
E N D
Java游戏开发学习情境6:小虫动画程序设计 08游戏3-2
目标: • 用Java多线程为游戏添加动画,掌握多线程的实现、控制方法。掌握通过双缓冲消除图像闪烁的方法。 • 任务: • 创建一个燃烧的火焰动画,为“电梯模拟游戏”添加人行走,电梯运行的动画 目标与任务
教师 学生 模仿完成: 绘制图像案例 提供: 绘制图像案例 项目核心 提供: 线程控制图像 间歇显示 协作完成: 线程控制图像 间歇显示成动画 功能扩展 提供: 电梯游戏的 动画和界面 协作完成: 电梯游戏的 场景动画加载 应用升级 学习过程
步骤一:用屏外绘制技术在一个窗体上显示图像步骤一:用屏外绘制技术在一个窗体上显示图像 • 步骤二:创建线程实现图像的间歇显示 • 步骤三:多个图形顺序显示形成动画 • 步骤四:为“电梯模拟游戏”添加人行走,电梯运行的动画 • 步骤五:课后创新作业:自主设计一个程序实现:显示滚动的字幕 详细步骤
Java在屏幕上画图的方法 • paint,repaint,update,superpaint都可在屏幕上画图 • 注意几个方法的区别
paint在当前容器中绘制图形,要这个类中存在这个方法则自动加载。paint在当前容器中绘制图形,要这个类中存在这个方法则自动加载。 • repaint重绘当前图形,会先执行update中的方法,再执行paint中的方法。 • update更新当前图形,通过repaint来调用。 • superpaint是隐式调用,当继承一个容器后会自动调用此方法,用于显示当前容器中的组件。 几个方法的区别
前面介绍的技术都是通过Graphics2D容器直接把对象绘制到窗体上。前面介绍的技术都是通过Graphics2D容器直接把对象绘制到窗体上。 • 屏外缓冲就是创建一个虚拟的applet来缓冲存放将要绘制的图形。 • 屏外缓冲绘图的目的是加快图形的绘制,消除屏闪。 缓冲屏幕 真实屏幕 屏外绘制缓冲器
屏外缓冲绘图步骤: • Image memoryimage,pic; //1 创建两个Image对象,一个用于屏外绘图,一个用于屏内绘图 • Graphics memoryg; //2 创建一个屏外绘图的画笔 • memoryimage = createImage(x,y); //3 在内存中创建屏外绘图区的大小 • memoryg = memoryimage.getGraphics(); //4 获取屏外绘图区的画笔 项目核心:屏外缓冲绘图
绘图的paint方法: paint(Graphics g) { memoryg.drawImage(pic,x,y,null); //5 在内存中画出pic对象的图像 g.drawImage(memory) //6 在屏幕上画出内存中的图像 } 例:一个在窗体飞动的物体, 运行结果如图所示: 项目核心:屏外缓冲绘图
动画是游戏开发中常用的技术。 • 动画是一帧接一帧的连续播放图像。 • 用线程控制连续变化的一系列图像间歇显示就能形成动画。 动画和线程
程序:静态的计算机高级语言编写的代码。 • 进程:程序的一次执行。 • 线程:程序中的部分代码的一次执行过程。 • 多进程:操作系统中多个程序并发执行。 • 多线程:程序中多个片断并发执行。 • Java通过继承Thread类和实现Runnable接口创建线程。 线程相关概念
新建状态 就绪状态 阻塞状态 运行状态 死亡状态 线程的状态和生命周期
新建 : 新建的线程处于新建状态 • 就绪 : 在创建线程后,它将处于就绪状态,等待 start() 方法被调用 • 执行: 线程在开始执行时进入运行状态 • 阻塞:在线程等待一个事件时(例如输入/输出操作),就称其处于阻塞状态。 • 死亡:在 run() 方法已完成执行或其 stop() 方法被调用之后,线程就处于死亡状态。 线程的状态和生命周期
Java 中的线程优先级是在 Thread 类中定义的常量 • NORM_PRIORITY : 值为 5 • MAX_PRIORITY : 值为 10 • MIN_PRIORITY : 值为 1 • 缺省优先级为 NORM_PRIORITY • 有关优先级的方法有两个: • final void setPriority(int newp) : 修改线程的当前优先级 • final int getPriority() : 返回线程的优先级 线程的调度和优先级
通过以下两种方法创建 Thread 对象: • 1.声明一个 Thread 类的子类,并覆盖 run() 方法。 class mythread extends Thread { public void run( ) {/* 覆盖该方法*/ } } • 2.声明一个实现 Runnable 接口的类,并实现 run() 方法。 class mythread implements Runnable{ public void run( ) {/* 实现该方法*/ } } 多线程的实现方法
新建状态 就绪状态 阻塞状态 运行状态 死亡状态 1.终止线程 stop()方法。 2.测试线程状态 isAlive()方法。 3.线程的暂停和恢复 sleep() suspend()和resume() join() resume() run() sleep() join() stop() 多线程的控制
有时两个或多个线程可能会试图同时访问一个资源有时两个或多个线程可能会试图同时访问一个资源 • 例如,一个线程可能尝试从一个文件中读取数据,而另一个线程则尝试在同一文件中修改数据 • 在此情况下,数据可能会变得不一致 • 为了确保在任何时间点一个共享的资源只被一个线程使用,使用了“互斥” • 两种方式实现互斥: • 使用互斥方法 synchronized void methodA() { } 使用互斥块 synchronized(object) { //要互斥的语句 } 多线程的互斥
为避免轮流检测,Java提供了一个线程间通信机制,使用wait()、notify()和notifyAll()方法 。 • 这些方法仅在 synchronized 方法中才能被调用。 • wait()方法告知被调用的线程退出监视器并进入等待状态,直到其他线程进入相同的监视器并调用 notify( ) 方法。 多线程的同步
当两个线程循环依赖于一对同步对象时将发生死锁。例如:当两个线程循环依赖于一对同步对象时将发生死锁。例如: • 一个线程进入对象ObjA上的监视器,而另一个线程进入对象ObjB上的监视器。如果ObjA中的线程试图调用ObjB上的任何 synchronized 方法,就将发生死锁。 • 死锁很少发生,但一旦发生就很难调试 线程的死锁
一帧就是指动画中的一幅图像 • 动画就是由一定时间间隔播放的帧图像构成的。 • 帧更换的速度直接影响动画的效果 • 帧速与进程调度间的关系。 使用线程来创建动画
通过屏外缓冲方式,利用线程间隔显示连续变化的图像,形成动画,程序运行结果如图所示。 功能扩展:燃烧的火焰动画
程序运行结果如图所示,屏幕中有一个移动的方块,当它与其它方块重合(游戏中叫碰撞)时,会用不同的颜色显示重合部分程序运行结果如图所示,屏幕中有一个移动的方块,当它与其它方块重合(游戏中叫碰撞)时,会用不同的颜色显示重合部分 用线程实现图形的间歇显示例二
(1)通过覆盖update(Graphics g)来消除闪烁 在动画的实现中,经常用到repaint()函数来重画屏幕,实现动画的加载,其实在java中repaint()是通过两个步骤来实现刷新功能的,首先它调用public void update()来刷新屏幕,其次再调用paint(Graphcis g)来重画屏幕,这就容易造成闪烁,特别是一些需要重画背景的程序,如果下一桢图象可以完全覆盖上一桢图象的话,便可以重写update函数如下来消除闪烁: (2)通过双缓存消除闪烁 为了防止屏幕重画出现的闪烁,先创建一个屏外绘图区,重画完毕,直接将成品再绘制到小程序屏幕上去。offScreenImage=createImage(w,h);//创建屏外绘图区offScreen=offScreenImage.getGraphics();//取得绘图环境 Java动画中消除屏闪的两招
电梯模拟游戏中,人走向、离开电梯;电梯开、关门;灯闪烁;电梯上、下运行;都是动画,它们只在游戏剧情需要时才播放,我们将使用多线程和synchronized方法,保证这些动画符合游戏规定的逻辑正确播放。 应用升级:为“电梯模拟游戏”添加人行走,电梯运行的动画
public void personEntered( PersonMoveEvent personEvent ) { // find Panel associated with Person that issued event AnimatedPanel panel = getPersonPanel( personEvent ); if ( panel != null ) { // determine velocity double time = TIME_TO_ELEVATOR / ANIMATION_DELAY; double distance = elevatorPanel.getPosition().getX() - panel.getPosition().getX() + 2 * OFFSET; panel.setVelocity( distance / time, -1.5 ); // Person starts walking panel.setMoving( true ); panel.playAnimation( 0 ); panel.setLoop( true ); } } // end method personEntered 应用升级:为“电梯模拟游戏”添加人走进电梯,电梯运行的动画(事件处理方法之一 )
// invoked when Door has closed in model public void doorClosed( DoorEvent doorEvent ) { // get DoorEvent Location String location = doorEvent.getLocation().getLocationName(); // play animation of Door closing doorPanel.playAnimation( 1 ); doorPanel.setAnimationRate( 2 ); doorPanel.setDisplayLastFrame( true ); // play sound clip of Door closing if ( doorCloseClip != null ) doorCloseClip.play(); } // end method doorClosed 应用升级:为“电梯模拟游戏”添加电梯关门的动画
// invoked when Button has been pressed in model public void buttonPressed( ButtonEventbuttonEvent ) { // get ButtonEvent Location String location = buttonEvent.getLocation().getLocationName(); // press Elevator Button if from Elevator if ( location.equals( ELEVATOR_NAME ) ) { elevatorButtonPanel.playAnimation( 0 ); elevatorButtonPanel.setDisplayLastFrame( true ); } 应用升级:为“电梯模拟游戏”添加按钮被按下的动画
自主设计一个程序实现:显示向左滚动的字幕 。 课后创新作业。 前一帧 后一帧