【纯JAVA语言做个RPG游戏】2.游戏界面及角色移动的基本实现

2024-08-30 19:48

本文主要是介绍【纯JAVA语言做个RPG游戏】2.游戏界面及角色移动的基本实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

    继上次做的地图编辑器,我大致的做了一个4000X4000的游戏地图数组,不过只画了一部分,以后要加什么新东西继续编辑这个地图就行了,保存在了一个文件中.

    现在便可以继续下一步,做出游戏的大致界面了.

    现在的2D游戏界面常见的大致有两种形式:

    1.一种是地图在游戏窗体上固定,人在地图中走动(也就是人相对屏幕移动了);

    2.而另一种则是游戏人物位置固定,游戏的地图在移动,这样看起来也是人物移动了。

    前一种方式地图的大小都给限定死了,不能超出屏幕,要显示大地图的话,通常都是将一个大地图分成若干个区域,然后通过设置门或者传送阵之类的进行整个地图的切换。而后者则没有限定地图的大小,人物可以安逸的走到任意大小地图的任意一个角落。

    当然也有一些游戏是将上面两种模式结合起来了,比如人物走到超过某一个位置时,就开始移动地图,这种混合的模式比较广泛的应用于卷轴式游戏中,例如冒险岛,dnf之类的.

    这里我选择的是第二种形式,因为我觉得将地图分区太麻烦了,用前面做的地图编辑器一次性做一张大的地图更省事。

    那么,首先确定好任务的位置在游戏窗体的正中央,游戏的地图数组是一个二维数组,游戏地图不会小于我们的游戏窗体,所以游戏窗体在任意的一个时刻显示的都仅仅是一个游戏地图的一部分(我们在游戏窗体上显示的时候,仅仅需要读取和操作地图二维数组的一部分值就够了)。

    我们如何来知道目前该显示哪一片地图数组中的内容到游戏面板上呢?这就要靠我们控制的角色了,通过上下左右控制角色相对整张地图的坐标,我们可以将整个游戏面板区域看作是我们操作的角色的视野,通过角色相对与整张地图的坐标,来得到角色相对地图数组的位置(即角色在数组中的i,j),这样就可以找到角色i,j旁边的一系列数组元素了。

    当然,只照上面那样做的话,游戏的画面就会是一格一格动的,因为一个数组元素代表的是一个正方形的图片,角色的视野元素每变一次都至少一变化了一排的元素,不可能说只变化2分之一或者3分之一排的元素,这样画面就是按元素格移动,而不是像素点,看起来就会很恶心,一点都不流畅,要让游戏的画面按像素点移动该怎么做呢?

    首先,还是和前面一样的思维,按照角色相对数组的坐标,找出角色位置旁边的数组元素,但在显示这些元素的时候就不能通过角色相对数组的坐标来画了,要用角色相对地图的坐标来画。由于角色相对地图的坐标变化是连续的,所以这样画出来的图像也是连续的.

 

下面上代码:

1.首先写一个游戏界面和元素的配置接口,其他类需要用到一些这里面的基本配置信息时只需实现这个接口。

/*** 游戏配置接口* @author yy**/
public interface gameConfig {//游戏主窗体名字String title = "场景移动小游戏";//游戏主窗体的大小int frameX = 700;int frameY = 700;//游戏面板大小int panelX = 650;int panelY = 650;//游戏素材大小int elesize = 50;//人物大小int playersize = 50;//------------[游戏素材]----------//-----第一层ImageIcon icon0 = new ImageIcon("000空白.png");ImageIcon icon1 = new ImageIcon("001草地.png");ImageIcon icon2 = new ImageIcon("002地砖.png");ImageIcon icon3 = new ImageIcon("003召泽地板副本.png");ImageIcon icon100 = new ImageIcon("100红树.png");ImageIcon icon101 = new ImageIcon("101绿树.png");ImageIcon icon102 = new ImageIcon("102绿竹.png");ImageIcon icon103 = new ImageIcon("103高绿树.png");ImageIcon icon150 = new ImageIcon("150岩浆.png");//镜头
//	ImageIcon shadow = new ImageIcon("镜头阴影.png");ImageIcon shadow2 = new ImageIcon("镜头阴影2.png");}

 

2.写一个类用来读取之前做好的游戏地图数组文件(读入的顺序和前面写入时一样):

/*** 读入地图文件* @author yy**/
public class ReadMapFile {//定义静态的三个数组,用来保存从地图文件中读取到的三个地图数组static int[][] map1;static int[][] map2;static int[][] map3;/*** 读入地图* @param path 地图文件位置*/static void readfile(String path){try{//从path路径下的地图文件中得到文件输入流FileInputStream fis = new FileInputStream(path);//将文件输入流包装成基本数据输入流DataInputStream dis = new DataInputStream(fis);//按保存时候的顺序依次读出地图文件中的三个地图数组int i = dis.readInt();int j = dis.readInt();map1 = new int[i][j];map2 = new int[i][j];map3 = new int[i][j];for(int ii=0;ii<i;ii++){for(int jj=0;jj<j;jj++){map1[ii][jj] = dis.readInt();map2[ii][jj] = dis.readInt();map3[ii][jj] = dis.readInt();}}dis.close();fis.close();}catch(Exception e){e.printStackTrace();}}
}

3.写一个玩家类,来确定玩家在游戏地图中的位置(角色位移偏移量对50求余,用来补充两个元素之间的间隔无法连续显示的间隙,从而达成像素点移动)。

/*** 角色类* @author yy**/
public class Player extends Thread implements gameConfig{//角色中点相对游戏面板的位置(在游戏中是不变的)static int px = panelX/2;static int py = panelY/2;//角色中点在整张地图中的位置(设置人最开始中点的位置一定要是一个元素中心的位置,要不然这种移动就会出问题 - -!)static int x = 25;static int y = 25;//角色的偏移量(实现像素点移动关键的部分)static int mx = 0;static int my = 0;//角色的步长static int step = 1;//角色是否移动static boolean up = false;static boolean down = false;static boolean left = false;static boolean right = false;@Overridepublic void run() {while(true){move();try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}/*** 角色移动的方法*/public void move(){if(up){//改变角色在地图中的位置y=y-step;//改变角色移动相对于固定元素点的偏移量my=my-step;}if(down){y=y+step;my=my+step;}if(left){x=x-step;mx=mx-step;}if(right){x=x+step;mx=mx+step;}}//得到角色在数组中的位置Ipublic static int getI(){return (y-(playersize/2))/50;}//得到角色在数组中的位置Jpublic static int getJ(){return (x-(playersize/2))/50;}
}

4.一个工具类,用来让程序能用过读取到数组中的int数据,找到相匹配的元素图片对象。

/*** 游戏面板通过读取数组中的int来匹配到相应的元素图片方法类* @author yy**/
public class GetMap implements gameConfig{//通过数字匹配图片static ImageIcon int2icon(int num){if(num==0){return icon0;}else if(num==1){return icon1;}else if(num==2){return icon2;}else if(num==3){return icon3;}else if(num==100){return icon100;}else if(num==101){return icon101;}else if(num==102){return icon102;}else if(num==103){return icon103;}else if(num==150){return icon150;}return null;}
}

 

5.写一个游戏窗体类,游戏就在这个窗体上运行。

    (1)一个窗体上放一个游戏面板,这个游戏面板需要我们自己写一个MyPanel类继承JPanel,重写里面的paint方法,在这paint方法里面通过数组画地图,用来更新游戏的地图信息,这样就可以保证面板repaint的时候,地图一直存在。再用一个刷新线程来不停的对面板进行刷新;

    (2)然后对窗体安装按键监听器,目前只监听负责移动的上下左右按键,来实现人物的移动,当然这里也可以认为是控制地图的移动,因为这是相对的(物理学相对运动 (=@__@=) ),至于怎么提升移动的流畅度也是通过线程处理的,http://y-1746119035.iteye.com/blog/2094687以前已经考虑过了..

/*** 游戏主窗体* @author yy**/
public class mainFrame extends JFrame implements gameConfig{//游戏面板JPanel panel;public mainFrame() {init();}/*** 设置窗体*/public void init(){this.setTitle(title);this.setSize(frameX, frameY);this.setLayout(new FlowLayout());this.setDefaultCloseOperation(3);//创建游戏面板panel = setpanel();this.add(panel);this.setVisible(true);//安装键盘监听器PanelListenner plis = new PanelListenner();this.addKeyListener(plis);//启动人物移动线程Player player = new Player();player.start();//启动刷新面板线程UpdateThread ut = new UpdateThread(panel);ut.start();}/*** 设置游戏面板*/public JPanel setpanel(){JPanel panel = new MyPanel();panel.setPreferredSize(new Dimension(panelX, panelY));panel.setLayout(null);panel.setBackground(Color.black);return panel;}/*** 内部游戏按键监听类* @author yy**/class PanelListenner extends KeyAdapter{//当按键按下public void keyPressed(KeyEvent e){int code = e.getKeyCode();switch (code) {case KeyEvent.VK_UP:Player.up = true;break;case KeyEvent.VK_DOWN:Player.down = true;break;case KeyEvent.VK_LEFT:Player.left = true;break;case KeyEvent.VK_RIGHT:Player.right = true;break;default:break;}}//当按键释放public void keyReleased(KeyEvent e){int code = e.getKeyCode();switch (code) {case KeyEvent.VK_UP:Player.up = false;break;case KeyEvent.VK_DOWN:Player.down = false;break;case KeyEvent.VK_LEFT:Player.left = false;break;case KeyEvent.VK_RIGHT:Player.right = false;break;default:break;}}}/*** 自定义内部游戏面板类* @author yy**/class MyPanel extends JPanel{@Overridepublic void paint(Graphics g) {super.paint(g);//找到角色旁边的素材,上下左右各5格for(int i=Player.getI()-6;i<=Player.getI()+6;i++){for(int j=Player.getJ()-6;j<=Player.getJ()+6;j++){//如果这一格没有超界(由于还没处理碰撞,这一条暂时没用  = =!)if(i>=0&&j>=0&&i<ReadMapFile.map1.length&&j<ReadMapFile.map1[0].length){//画第一层元素ImageIcon icon = GetMap.int2icon(ReadMapFile.map1[i][j]);g.drawImage(icon.getImage(), (Player.px-elesize/2)+((j-Player.getJ())*elesize)-(Player.mx%elesize), (Player.py-elesize/2)+((i-Player.getI())*elesize)-(Player.my%elesize), elesize, elesize, null);//第二层ImageIcon icon2 = GetMap.int2icon(ReadMapFile.map2[i][j]);g.drawImage(icon2.getImage(), (Player.px-elesize/2)+((j-Player.getJ())*elesize)-(Player.mx%elesize), (Player.py-elesize/2)+((i-Player.getI())*elesize)-(Player.my%elesize), elesize, elesize, null);//第三层ImageIcon icon3 = GetMap.int2icon(ReadMapFile.map3[i][j]);g.drawImage(icon3.getImage(), (Player.px-elesize/2)+((j-Player.getJ())*elesize)-(Player.mx%elesize), (Player.py-elesize/2)+((i-Player.getI())*elesize)-(Player.my%elesize), elesize, elesize, null);}}}
//			g.setColor(Color.black);
//			g.fillRect(0, 0, 50, 650);
//			g.fillRect(0, 0, 650, 50);
//			g.fillRect(600, 0, 50, 650);
//			g.fillRect(0, 600, 650, 50);//由于暂时还没弄好游戏角色的移动图,所以角色先用一个黑色的小球代替一下....  = =!g.fillOval(Player.px-elesize/2, Player.py-elesize/2, elesize, elesize);//个人的一个小想法,做一个黑色的图片,然后中间挖空一个圆,加上模糊效果,来模拟人的视野g.drawImage(shadow2.getImage(), 0, 0, 650, 650, null);}}
}

 6.补充上面的面板刷新线程类(注意游戏面板刷新线程的休眠时间一定要是最小的,且其他休眠时间是它的整数倍)

public class UpdateThread extends Thread{JPanel panel;public UpdateThread(JPanel panel) {this.panel = panel;}@Overridepublic void run() {while(true){panel.repaint();try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}

 7.最后再来个启动类,来启动程序(首先读取到地图数组,再打开窗口)

/*** 开始游戏* @author yy**/
public class test {public static void main(String[] args) {//首先从地图文件中读入地图数组ReadMapFile.readfile("D:\\mygame\\map\\map1.map");//用读到的地图数组创建游戏窗体,开始游戏mainFrame mf = new mainFrame();}
}

 

 

这样下来,一个游戏基本的框架便完成了,接下来的任务便是加入碰撞处理部分了..

运行这个程序(焦点离开那个小黑球吧,那是人物角色的替代品  = =!):



 试了一下啊,转化的gif画质简直惨不忍睹,不过还是发上来试试,gif图中有卡顿,实际程序中是没有的,虽然我不知道iteye是否能支持gif...⊙﹏⊙‖∣°....

源代码丢下面了,有兴趣的可以一起玩玩...
 搞到这个点,我特么也是醉了....还好明天没课└(^o^)┘

 

 


 

 

 

 

这篇关于【纯JAVA语言做个RPG游戏】2.游戏界面及角色移动的基本实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1121765

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法