JAVA 水果机游戏及编码

【转自 http://tech.it168.com/j/2007-11-07/200711070910328_1.shtml】

3.      游戏的开发与编码

在我们开发这个游戏之前,我们先讲一个这个游戏的实现所采用的方法,那就是经典的MVC模式,因为在开发游戏的时候,结构很重要,必须要理清楚每一块负责什么,每一个类负责什么,而MVC模式正好就是解决这种问题的很好的方案,我们可以把游戏的运行流程交由一个类去统一调度,游戏的呈现也就是绘图用专门一个类去负责,而绘图所需的数据可以从一个模型类里面去取,控制的类负责更改模型里面的数据并调用视图类去更新当前的视频,这样整个游戏的流程就很清晰明了。所以我们设计了如下几个类,它们之间互相交互,形成整个游戏的框架。

1,ClientControl

顾名思义,这个类就是我们的控制端类,它负现整个游戏的流程控制以及事件处理。它在MVC里面的角色是C。

2,ClientModel

它就是我们程序运行的时候,放数据的地方,它存放的数据并不是一般的数据,而是需要双方一起交互的数据,它只是做为一个桥梁,连接控制端和视图端的纽带。它是MVC的角色里面是M.。

3,ClientView

它是我们今天需要重点讲解的地方,它是我们MVC里面的视图的实现,它负责呈现整个游戏的界面,并且它也受控制端ClientControl的支配,由ClientControl请求它重绘。它重绘的时候,一些数据将从ClientModel里面去取。

那么我们重点来看一看ClientView的代码:

/*
* ClientView.java
*
* Created on 2007年10月2日, 下午2:00
* 此类专门负责视图的实现,此类中须定义从模型中
* 取出数据并重绘的方法
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package com.hadeslee.apple.client;
import com.hadeslee.apple.common.Bet;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import javax.swing.*;
import java.util.Vector;
/**
*
* @author lbf
*/
public class ClientView extends JPanel {
private ClientModel cm; //模型类的一个对象
private volatile boolean isStar;
private Image starA; //表示当前星星的图片
private Image[] star; //星星的数组
private int x1;
private int y1; //星星的座标
private Image bg2; //表示底衬的那层
private Image ratio; //赔率底衬的那层
private int x;
private int length; //表示跑马灯的位置

/** Creates a new instance of ClientView */
public ClientView(ClientModel cm) {
this.cm = cm;
initOther();
x = 646;
new RunStar().start();
new Draw().start();
}
//初始化视图类的一些参数
private void initOther() {
star = new Image[3];
MediaTracker mt = new MediaTracker(this);
for (int i = 0; i < 3; i++) {
star[i] = Toolkit.getDefaultToolkit().createImage(this.getClass().getResource("pic/game/star/" + (i + 1) + ".png"));
mt.addImage(star[i], i);
}
bg2 = Toolkit.getDefaultToolkit().createImage(this.getClass().getResource("pic/game/bg2.png"));
ratio = Toolkit.getDefaultToolkit().createImage(this.getClass().getResource("pic/game/ratio.png"));
mt.addImage(bg2, 4);
mt.addImage(ratio, 5);
try {
mt.waitForAll();
} catch (Exception exe) {
exe.printStackTrace();
}
starA = star[0];
//把默认的鼠标改成我们自定义的鼠标形式,以配合主题
Image icon = Toolkit.getDefaultToolkit().createImage(this.getClass().getResource("pic/login/icon.png"));
Cursor cu = Toolkit.getDefaultToolkit().createCustomCursor(icon, new Point(0, 0), "my");
this.setCursor(cu);
}
//覆盖的方法
protected void paintComponent(Graphics g) {
//先调用父类的方法,清除以前画的内容
super.paintComponent(g);
//然后设置一些提示,比如屏幕抗锯齿,以及文字抗锯齿
Graphics2D gd = (Graphics2D) g;
gd.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gd.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//画背景
g.drawImage(cm.getBg(), 0, 0, this);
g.drawImage(bg2, 0, 0, this); //再画第二个背景
//画赔率的闪动
if (cm.isRunning()) {
drawRatio(g);
}
//再画桌面
drawTable(g);
//如果现在正在跑,那么就调用跑的那个方法
if (cm.isRunning()) {
drawRunning(g);
}
//如果现在正在赌剪刀石头布,那么就调用比倍的方法
if (cm.isBetting()) {
drawBetting(g);
}
//画出永远存在的星星
if (isStar) {
g.drawImage(starA, x1, y1, this);
}
//画出跑马灯,提示文字
drawTip(g);
}
private void drawTip(Graphics g) {
g.setFont(new Font("宋体", Font.PLAIN, 22));
g.setColor(Color.RED);
g.drawString(cm.getInfo().getTip(), x, 25);
FontMetrics fm = g.getFontMetrics();
length = (int) fm.getStringBounds(cm.getInfo().getTip(),g).getWidth();
}
//画出赔率
private void drawRatio(Graphics g) {
RatioA ra = cm.getRa();
RatioB rb = cm.getRb();
if (ra != null) {
g.drawImage(ratio, ra.x, ra.y, this);
}
if (rb != null) {
g.drawImage(ratio, rb.x, rb.y, this);
}
}
//画出正在跑的方法
private void drawRunning(Graphics g) {
Vector<PP> ps = cm.getP();
for (PP p : ps) {
g.drawImage(p.current, p.x, p.y, this);
}
}
//画出跑完的方法
private void drawRunOver(Graphics g) {
Vector<PP> ps = cm.getP();
for (PP p : ps) {
g.drawImage(p.current, p.x, p.y, this);
}
}
//画出正在比倍的方法
private void drawBetting(Graphics g) {
g.drawImage(cm.getPKBG(), 172, 39, this);
g.drawImage(cm.getPkA(), 267, 245, this);
g.drawImage(cm.getPkB(), 386, 247, this);
}
//画桌面以及桌面上的一些信息
private void drawTable(Graphics g) {
g.drawImage(cm.getTable(), 0, 0, this);
drawMoney(g);
drawBet(g);
}
//画下注的那九格下注数字
private void drawBet(Graphics g) {
Bet b = cm.getBet();
drawNumber(80, 570, 12, 19, b.getBet(1), g, b.getWin(1));
drawNumber(183, 570, 12, 19, b.getBet(2), g, b.getWin(2));
drawNumber(252, 570, 12, 19, b.getBet(3), g, b.getWin(3));
drawNumber(318, 570, 12, 19, b.getBet(4), g, b.getWin(4));
drawNumber(424, 570, 12, 19, b.getBet(5), g, b.getWin(5));
drawNumber(527, 570, 12, 19, b.getBet(6), g, b.getWin(6));
drawNumber(597, 570, 12, 19, b.getBet(7), g, b.getWin(7));
drawNumber(664, 570, 12, 19, b.getBet(8), g, b.getWin(8));
drawNumber(767, 570, 12, 19, b.getBet(9), g, b.getWin(9));
}
//画有余额,赢的钱,用户ID,大小彩金等的方法
private void drawMoney(Graphics g) {
//画余额和赢的钱
int allMoney = cm.getAllMoney();
int winMoney = cm.getWinMoney();
if (allMoney < 10000) {
drawNumber(762, 88, 24, 38, allMoney, g);
} else {
drawNumber(762, 94, 18, 28, allMoney, g);
}
if (winMoney < 10000) {
drawNumber(129, 86, 24, 38, winMoney, g);
} else {
drawNumber(129, 90, 18, 28, winMoney, g);
}
drawNumber(740, 208, 12, 19, cm.getId(), g); //画ID号
//画大彩金和小彩金
int smallBonus = cm.getInfo().getSmallBonus();
int bigBonus = cm.getInfo().getBigBonus();
if (smallBonus < 10000) {
drawNumber(760, 390, 24, 38, smallBonus, g);
} else {
drawNumber(760, 396, 18, 28, smallBonus, g);
}
if (bigBonus < 10000) {
drawNumber(128, 390, 24, 38, bigBonus, g);
} else {
drawNumber(128, 396, 18, 28, bigBonus, g);
}
}
//定义两个重载的方法,分别针对于图放大的图片和一般大小的图片
private void drawNumber(int startX, int startY, int num, Graphics g, boolean isWin) {
drawNumber(startX, startY, 24, 38, num, g, isWin);
}
private void drawNumber(int startX, int startY, int width, int height, int num, Graphics g) {
drawNumber(startX, startY, width, height, num, g, false);
}
private void drawNumber(int startX, int startY, int width, int height, int num, Graphics g, boolean isWin) {
String ns = Integer.toString(num);
int i = 0;
for (int start = ns.length() - 1; start >= 0; start--) {
i++;
char c = ns.charAt(start);
int index = c - 48;
if (isWin) {
g.drawImage(cm.getWinNumber(index), startX - (i * width), startY, width, height, this);
} else {
g.drawImage(cm.getNumber(index), startX - (i * width), startY, width, height, this);
}
}
}
//此类专门用于后台调用重绘线程
private class Draw extends Thread {
public void run() {
while (true) {
try {
x -= 5;
if (x + length < 0) {
x = 800;
}
Thread.sleep(200);
repaint(x,0,length+20,30);
} catch (Exception exe) {
exe.printStackTrace();
}
}
}
}
//此类专门用于跑星星的闪动
private class RunStar extends Thread {
private int total;
public RunStar() {
isStar = true;
x1 = 339;
y1 = 106;
}
public void run() {
int index = 0;
while (true) {
try {
Thread.sleep(100);
if (index < star.length - 1) {
starA = star[++index];
} else {
starA = star[0];
index = 0;
total++;
}
if (total > 1) {
isStar = false;
repaint();
total = 0;
x1 = (int) (Math.random()*100-50) + 339;
y1 = (int) (Math.random()*100-50) + 106;
int sleep = (int) (Math.random()*3000) + 1000;
Thread.sleep(sleep);
isStar = true;
}else{
repaint(x1,y1,150,150);
}
} catch (Exception exe) {
exe.printStackTrace();
}
}
}
}
}

代码其实不长,二百多行而已,我们先来看看如下几个代码片段:

Toolkit.getDefaultToolkit().createImage(this.getClass().getResource("pic/game/bg2.png"));

这句话有两个需要我们注意的地方:

一是我们如何把图片导入程序当中,二是我们如果把图片打包进JAR包,然后如何得到它们的URL。

我们先讲第一个,如何把图片导入程序中,在这里我们用的是Toolkit的方法createImage,它确实是一个很实用的方法,它是一个重载的方法,可以传入很多种参数,除了可以传入URL之处,还可以有如下的重载方法:


Image

createImage(byte[] imagedata) 
          创建一幅图像,该图像对存储在指定字节数组中的图像进行解码。

abstract  Image

createImage(byte[] imagedata, int imageoffset, int imagelength) 
          创建一幅图像,该图像对存储在指定字节数组中指定偏移量和长度处的图像进行解码。

abstract  Image

createImage(ImageProducer producer) 
          使用指定的图像生成器创建一幅图像。

abstract  Image

createImage(String filename) 
          返回从指定文件获取像素数据的图像。

abstract  Image

createImage(URL url) 
          返回一幅图像,该图像从指定 URL 获取像素数据。

有一点需要注意的是,它的createImage是一个异步的方法,也就是说我们调用了这个方法以后,程序会立即返回,并不会等到图片完全加载进内存之后才返回,所以当我们用这种方法加载比较大的图片的时候,如果图片又没有完全进入内存,而我们却去draw它,这个时候就会出现撕裂的情况,大大影响了我们程序的性能以及可玩性,那怎么办呢?

办法有两种,一种是像我们在程序里实现的一样,用一个媒体跟踪器来跟踪我们要加载的图片,然后调用一个同步方法等待它们全部加载进入内存之后才继续往下运行,这样就可以保存在初始化以后,所需要用到的图片确实都全部加载进内存了,这样画的时候,才能保证效果。如下所示:

MediaTracker mt = new MediaTracker(this);

mt.addImage(star[i], i);

...
mt.waitForAll();

我们生成一个媒体跟踪器,然后把我们需要跟踪的图片放到里面去,然后等待所有的图片加载,mt.waitForAll()方法是会抛出一个InterruptedException的方法。我们需要捕获处理它。

另外一种办法就是利用javax.imageio.ImageIO的方法,它的read方法可以同步的把图片完全读入内存,某些情况下这是更方便的方法,因为使用它免去了加媒体跟踪器的代码。javax.imageio.ImageIO的read方法也有很多重载的版本,它的方法如下:


static BufferedImage

read(File input) 
          返回一个 BufferedImage,作为使用 ImageReader(它是从当前已注册 ImageReader 中自动选择的)解码所提供 File 的结果。

static BufferedImage

read(ImageInputStream stream) 
          返回一个 BufferedImage,作为使用 ImageReader(它是从当前已注册 ImageReader 中自动选择的)解码所提供 ImageInputStream 的结果。

static BufferedImage

read(InputStream input) 
          返回一个 BufferedImage,作为使用 ImageReader(它是从当前已注册 ImageReader 中自动选择的)解码所提供 InputStream 的结果。

static BufferedImage

read(URL input) 
          返回一个 BufferedImage,作为使用 ImageReader(它是从当前已注册 ImageReader 中自动选择的)解码所提供 URL 的结果。

所以我们用read方法的话,会显得更加方一些,但是为什么我们在程序当中不使用它,而使用再加繁琐的Toolkit加上MediaTracker的方法呢?因为ImageIO读入内存的图片在呈现的过程中会有如下缺点:

1,当加载的图片是动态的gif图片的时候,图片在呈现的时候,将没有动画效果,它只会读取第一帧。

2,当加载的图片是半透明的时候,图片在呈现的时候,会比用Toolkit加载进来的图片更耗CPU

所以我们选择了用Toolkit而不是ImageIO,当我们没有用到以上两种情况的图片的时候,是完全可以用ImageIO来加载图片的。

图片导入程序中的问题解决了,我们现在来看一看如何把图片打包进JAR包,然后又如何在程序运行的时候把JAR包里面的资源提取出来。在这里我们用的是一个很有用的方法getResource(),它是定义在Class类里面的,当我们把我们的的图片提取出来的时候,可以用相对路径也可以用绝对路来来提取,当我们用相对路径的时候,路径就是相对于当前的class文件所在目录的路径,如果是用绝对路径的时候,路径就是从JAR内部的根目录开始算的。把图片等一些资源打入JAR包有很多好处,一是可以实现资源的初步隐藏,二是可以利用JAR的特性对文件进行一些压缩,因为JAR包就是一个压缩包,只不过后缀名改了而已。

下面我们再来看一下paintComponent方法,它是一个重写的方法,它重写了父类JPanel里面的paintComponent方法,一般来说,当我们要绘制一些内容的时候,都是采用重写此方法的办法,在以前AWT的编程中,对重量型组件进行重写,一般重写的是paint方法,所以在用轻量级组件的时候,这一点要注意,最好不要再重写paint方法了,而是改为重写paintComponent。在它里面我们看到如下三句:

Graphics2D gd = (Graphics2D) g;

gd.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

gd.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

它的意思就是设置图形上下文在绘制的时候,要注意哪些方面,我们可以利用这个方法给图形上下文一些提示。在本文里面我们提示了两点,一点是图片抗锯齿打开,二是文本抗据齿我们也打开,除了这两个之外还要很多提示我们可以设置的,在兴趣的朋友可以查看java.awt.RenderingHints这个类。利用这个特性,我们可以使我们呈现的界面更加完美,不过完美是需要代价的,呈现的越清晰越完美就越需要更多的CPU的运算,所以当电脑的性能不太好的时候,我们可以把这两个提示去掉,让JVM自行把握绘制的质量。

还有一点我们要注意的地方,那就是我们调用repaint的地方。在我们需要重绘的时候,我们可以调用repaint方法,它会发送一个重绘的请求,那个会把这个请求放到重绘线程里面去,在我们调用repaint的时候,有很重要的一点就是尽量不要去调用repaint的默认方法,而要调用repaint(int x,int y,int width,int height)方法,因为它只会请求重绘某一个区域,而repaint()则会重绘整个区域,所以为了性能着想,最好不要重绘整个区域,当你开发了有关JAVA2D的程序后,你会发现,程序的大部份CPU都耗在重绘上面,所以优化重绘区域对于优化整个程序的性能是很有效果的。

对于ClientView的讲解就到这里,对于ClientControl,ClientModel,登录窗口以及播放声音的实现,各位可以自行下载源代码查看,欢迎大家参与讨论。本工程是用NetBeans开发的,NetBeans工程的源代码请点击这里下载。

4.      对未来的展望

JAVA经过十几年的发展,到今天已经成为世界上使用人数最多的语言,这得益于它的平台所提供的功能

的全面,从机顶盒到手机到电脑桌面到企业级的大型应用,JAVA都可以做到。SUN一直都很看重JAVA在桌面上的成绩,每次版本的更新,都会花大力气在构造桌面应用的程序包上,最近又推出了javafx来丰富桌面的开发,相信在JAVA开源以后,有这么多JAVA爱好者以及开源社区的支持,JAVA的明天一定会更加美好。

时间: 2024-08-25 14:49:47

JAVA 水果机游戏及编码的相关文章

OOP面向对象编程之java打飞机游戏

#写在前面 继上一篇OOP面向对象编程之俄罗斯方块项目实现过程,OOP面向对象编程之java打飞机游戏,其实写的很简单,也很容易理解,并且注释写的很清楚了,还有问题,自己私下去补课学习(顺便做50个深蹲,嘿嘿,平时干嘛去了),看图:   #完整代码   敌飞机 package com.tarena.fly; import java.util.Random; /** * 敌飞机: 是飞行物,也是敌人 */ public class Airplane extends FlyingObject imp

最牛叉的街机游戏合集 &amp; 模拟器

亲爱的小伙伴们,是否还记得那年我们玩的疯狂的街机游戏吗,街机中心提供400多个街机游戏,让你爽到底. 例如:拳皇96,拳皇97,恐龙新世纪.名将.快打旋风.惩罚者.魂斗罗.超级玛丽.雪山兄弟.忍者神龟.格斗之王系列.合金弹头系列.侍魂系列.龙虎之拳系列.街头霸王系列.西游释厄传系列和三国战纪系列等经典街机游戏. 性能高的手机可以打开显示设置内的平滑拉伸,缩放模式设置为拉伸至全屏,横屏需要开启系统的自动旋转屏幕.Xperia Play用户可以自定义物理按键,然后把触摸控制可见关闭,屏幕上就不会再显

Python开发接水果小游戏

我研发的Python游戏引擎Pylash已经更新到1.4了.现在我们就来使用它完成一个极其简单的小游戏:接水果.以下是游戏截图: 游戏操作说明:点击屏幕左右两边或者使用键盘方向键控制人物移动,使人物与水果接触得分,碰到非水果的物品,如碎玻璃,就会game over. 接下来是详尽的开发过程,篇幅较长,请看官耐心阅读. Pylash项目地址 由于本次开发用到了pylash,大家可以先去Github上对引擎进行了解. https://github.com/yuehaowang/pylash_engi

Java基础机试题

package day8;import java.util.Scanner;/** * Java基础机试题 * @author:lyrand * */public class convert {        static void exitContinue(){                while (true){            System.out.print("你想继续吗?(y/n)");            Scanner sc = new Scanner(Sys

Java几种常见的编码方式

Java综合 几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言.由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元—— byte 来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解.我们可以把计算机能够理解的语言假定为英语,其它语言要能够在计算机中使用必须经过一次翻译,把它翻译成英语.这个翻译的过程就是编

java加密基础(一) —— BASE64编码

工作之后第一次接触到java加密机制,当时需求是使用RSA做数字签名.当时看到之后一脸懵逼,就查了各种资料. 对于学习过程中我走了不少弯路,主要是因为不知道先看什么在看什么.下面说一下我的学习经历 首先,要了解BASE64算法,因为java加密基本上都用到了BASE64:然后就是对称加密和非对称加密了(下一章节着重介绍,这里就不多做描述了):最后就是了解下数字签名(会在第三章节中介绍到) 我们都知道ASCII字符一共有256,而计算机记录数据的方式只有0.1,所以就只能使用8bit才能表示一个A

《微赢微信公众平台系统5月14最新破解高级运营版+水果机+邀请函+微汽车+微食品+用户CRM》

<微赢微信公众平台系统5月14最新破解高级运营版+水果机+邀请函+微汽车+微食品+用户CRM> 此版本目前是淘宝卖600RMB的,其它VIP源码论坛也都还没有发布,咱们这里完全免费分享出来,但这里先说明下,别拿回去叫着安装不了,这套微赢微信公众平台系统5月14最新破解高级运营版需要在php 5.4版本下面才能安装,目前一般的虚拟主机都无法正常安装. 非常感谢分享此套源码的童鞋,希望更多的童鞋能分享好的资源. 下面是我们亲测截图 微赢高级版正式上线,支持后台一键更新升级.高级版新功能:新版商城,

【转】Java数字抽奖游戏核心代码

1. [代码][Java]代码    package com.luiszhang.test; import java.util.Arrays; /** * NumberLotteryGame * 一个简单的数字彩票游戏类 * @author LuisZhang * 参考了core java 8th中的例3-7的设计思想 */public class NumberLotteryGame {    private int gamesNumber;    // 生成游戏的数量,为以后多线程扩展做考虑 

深入解析java String中getBytes()的编码问题

转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6900536.html Java服务器后台在和Android端App通信时,遇到了两端关于用MD5加密同一包含中文的字符串结果不一致的问题. 具体问题描述: Java服务器后台和Android端AS用了同一个MD5的工具类,且两边项目的默认编码都是UTF-8 ,加密纯英文数字的字符串时,结果一致,对同一包含中文的字符串加密,发现结果不一样,这是为什么呢? 工具类MD5Util代码如下: public cla