纯java语言做rpg游戏_【纯JAVA语言做个RPG游戏】2.游戏界面及角色移动的基本实现...
繼上次做的地圖編輯器,我大致的做了一個(gè)4000X4000的游戲地圖數(shù)組,不過只畫了一部分,以后要加什么新東西繼續(xù)編輯這個(gè)地圖就行了,保存在了一個(gè)文件中.
現(xiàn)在便可以繼續(xù)下一步,做出游戲的大致界面了.
現(xiàn)在的2D游戲界面常見的大致有兩種形式:
1.一種是地圖在游戲窗體上固定,人在地圖中走動(dòng)(也就是人相對(duì)屏幕移動(dòng)了);
2.而另一種則是游戲人物位置固定,游戲的地圖在移動(dòng),這樣看起來也是人物移動(dòng)了。
前一種方式地圖的大小都給限定死了,不能超出屏幕,要顯示大地圖的話,通常都是將一個(gè)大地圖分成若干個(gè)區(qū)域,然后通過設(shè)置門或者傳送陣之類的進(jìn)行整個(gè)地圖的切換。而后者則沒有限定地圖的大小,人物可以安逸的走到任意大小地圖的任意一個(gè)角落。
當(dāng)然也有一些游戲是將上面兩種模式結(jié)合起來了,比如人物走到超過某一個(gè)位置時(shí),就開始移動(dòng)地圖,這種混合的模式比較廣泛的應(yīng)用于卷軸式游戲中,例如冒險(xiǎn)島,dnf之類的.
這里我選擇的是第二種形式,因?yàn)槲矣X得將地圖分區(qū)太麻煩了,用前面做的地圖編輯器一次性做一張大的地圖更省事。
那么,首先確定好任務(wù)的位置在游戲窗體的正中央,游戲的地圖數(shù)組是一個(gè)二維數(shù)組,游戲地圖不會(huì)小于我們的游戲窗體,所以游戲窗體在任意的一個(gè)時(shí)刻顯示的都僅僅是一個(gè)游戲地圖的一部分(我們在游戲窗體上顯示的時(shí)候,僅僅需要讀取和操作地圖二維數(shù)組的一部分值就夠了)。
我們?nèi)绾蝸碇滥壳霸擄@示哪一片地圖數(shù)組中的內(nèi)容到游戲面板上呢?這就要靠我們控制的角色了,通過上下左右控制角色相對(duì)整張地圖的坐標(biāo),我們可以將整個(gè)游戲面板區(qū)域看作是我們操作的角色的視野,通過角色相對(duì)與整張地圖的坐標(biāo),來得到角色相對(duì)地圖數(shù)組的位置(即角色在數(shù)組中的i,j),這樣就可以找到角色i,j旁邊的一系列數(shù)組元素了。
當(dāng)然,只照上面那樣做的話,游戲的畫面就會(huì)是一格一格動(dòng)的,因?yàn)橐粋€(gè)數(shù)組元素代表的是一個(gè)正方形的圖片,角色的視野元素每變一次都至少一變化了一排的元素,不可能說只變化2分之一或者3分之一排的元素,這樣畫面就是按元素格移動(dòng),而不是像素點(diǎn),看起來就會(huì)很惡心,一點(diǎn)都不流暢,要讓游戲的畫面按像素點(diǎn)移動(dòng)該怎么做呢?
首先,還是和前面一樣的思維,按照角色相對(duì)數(shù)組的坐標(biāo),找出角色位置旁邊的數(shù)組元素,但在顯示這些元素的時(shí)候就不能通過角色相對(duì)數(shù)組的坐標(biāo)來畫了,要用角色相對(duì)地圖的坐標(biāo)來畫。由于角色相對(duì)地圖的坐標(biāo)變化是連續(xù)的,所以這樣畫出來的圖像也是連續(xù)的.
下面上代碼:
1.首先寫一個(gè)游戲界面和元素的配置接口,其他類需要用到一些這里面的基本配置信息時(shí)只需實(shí)現(xiàn)這個(gè)接口。
/**
* 游戲配置接口
* @author yy
*
*/
public interface gameConfig {
//游戲主窗體名字
String title = "場景移動(dòng)小游戲";
//游戲主窗體的大小
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.寫一個(gè)類用來讀取之前做好的游戲地圖數(shù)組文件(讀入的順序和前面寫入時(shí)一樣):
/**
* 讀入地圖文件
* @author yy
*
*/
public class ReadMapFile {
//定義靜態(tài)的三個(gè)數(shù)組,用來保存從地圖文件中讀取到的三個(gè)地圖數(shù)組
static int[][] map1;
static int[][] map2;
static int[][] map3;
/**
* 讀入地圖
* @param path 地圖文件位置
*/
static void readfile(String path){
try{
//從path路徑下的地圖文件中得到文件輸入流
FileInputStream fis = new FileInputStream(path);
//將文件輸入流包裝成基本數(shù)據(jù)輸入流
DataInputStream dis = new DataInputStream(fis);
//按保存時(shí)候的順序依次讀出地圖文件中的三個(gè)地圖數(shù)組
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
for(int jj=0;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.寫一個(gè)玩家類,來確定玩家在游戲地圖中的位置(角色位移偏移量對(duì)50求余,用來補(bǔ)充兩個(gè)元素之間的間隔無法連續(xù)顯示的間隙,從而達(dá)成像素點(diǎn)移動(dòng))。
/**
* 角色類
* @author yy
*
*/
public class Player extends Thread implements gameConfig{
//角色中點(diǎn)相對(duì)游戲面板的位置(在游戲中是不變的)
static int px = panelX/2;
static int py = panelY/2;
//角色中點(diǎn)在整張地圖中的位置(設(shè)置人最開始中點(diǎn)的位置一定要是一個(gè)元素中心的位置,要不然這種移動(dòng)就會(huì)出問題 - -!)
static int x = 25;
static int y = 25;
//角色的偏移量(實(shí)現(xiàn)像素點(diǎn)移動(dòng)關(guān)鍵的部分)
static int mx = 0;
static int my = 0;
//角色的步長
static int step = 1;
//角色是否移動(dòng)
static boolean up = false;
static boolean down = false;
static boolean left = false;
static boolean right = false;
@Override
public void run() {
while(true){
move();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 角色移動(dòng)的方法
*/
public void move(){
if(up){
//改變角色在地圖中的位置
y=y-step;
//改變角色移動(dòng)相對(duì)于固定元素點(diǎn)的偏移量
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;
}
}
//得到角色在數(shù)組中的位置I
public static int getI(){
return (y-(playersize/2))/50;
}
//得到角色在數(shù)組中的位置J
public static int getJ(){
return (x-(playersize/2))/50;
}
}
4.一個(gè)工具類,用來讓程序能用過讀取到數(shù)組中的int數(shù)據(jù),找到相匹配的元素圖片對(duì)象。
/**
* 游戲面板通過讀取數(shù)組中的int來匹配到相應(yīng)的元素圖片方法類
* @author yy
*
*/
public class GetMap implements gameConfig{
//通過數(shù)字匹配圖片
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.寫一個(gè)游戲窗體類,游戲就在這個(gè)窗體上運(yùn)行。
(1)一個(gè)窗體上放一個(gè)游戲面板,這個(gè)游戲面板需要我們自己寫一個(gè)MyPanel類繼承JPanel,重寫里面的paint方法,在這paint方法里面通過數(shù)組畫地圖,用來更新游戲的地圖信息,這樣就可以保證面板repaint的時(shí)候,地圖一直存在。再用一個(gè)刷新線程來不停的對(duì)面板進(jìn)行刷新;
(2)然后對(duì)窗體安裝按鍵監(jiān)聽器,目前只監(jiān)聽負(fù)責(zé)移動(dòng)的上下左右按鍵,來實(shí)現(xiàn)人物的移動(dòng),當(dāng)然這里也可以認(rèn)為是控制地圖的移動(dòng),因?yàn)檫@是相對(duì)的(物理學(xué)相對(duì)運(yùn)動(dòng)(=@__@=)),至于怎么提升移動(dòng)的流暢度也是通過線程處理的,http://y-1746119035.iteye.com/blog/2094687以前已經(jīng)考慮過了..
/**
* 游戲主窗體
* @author yy
*
*/
public class mainFrame extends JFrame implements gameConfig{
//游戲面板
JPanel panel;
public mainFrame() {
init();
}
/**
* 設(shè)置窗體
*/
public void init(){
this.setTitle(title);
this.setSize(frameX, frameY);
this.setLayout(new FlowLayout());
this.setDefaultCloseOperation(3);
//創(chuàng)建游戲面板
panel = setpanel();
this.add(panel);
this.setVisible(true);
//安裝鍵盤監(jiān)聽器
PanelListenner plis = new PanelListenner();
this.addKeyListener(plis);
//啟動(dòng)人物移動(dòng)線程
Player player = new Player();
player.start();
//啟動(dòng)刷新面板線程
UpdateThread ut = new UpdateThread(panel);
ut.start();
}
/**
* 設(shè)置游戲面板
*/
public JPanel setpanel(){
JPanel panel = new MyPanel();
panel.setPreferredSize(new Dimension(panelX, panelY));
panel.setLayout(null);
panel.setBackground(Color.black);
return panel;
}
/**
* 內(nèi)部游戲按鍵監(jiān)聽類
* @author yy
*
*/
class PanelListenner extends KeyAdapter{
//當(dāng)按鍵按下
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;
}
}
//當(dāng)按鍵釋放
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;
}
}
}
/**
* 自定義內(nèi)部游戲面板類
* @author yy
*
*/
class MyPanel extends JPanel{
@Override
public 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++){
//如果這一格沒有超界(由于還沒處理碰撞,這一條暫時(shí)沒用 = =!)
if(i>=0&&j>=0&&i
//畫第一層元素
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);
//由于暫時(shí)還沒弄好游戲角色的移動(dòng)圖,所以角色先用一個(gè)黑色的小球代替一下.... = =!
g.fillOval(Player.px-elesize/2, Player.py-elesize/2, elesize, elesize);
//個(gè)人的一個(gè)小想法,做一個(gè)黑色的圖片,然后中間挖空一個(gè)圓,加上模糊效果,來模擬人的視野
g.drawImage(shadow2.getImage(), 0, 0, 650, 650, null);
}
}
}
6.補(bǔ)充上面的面板刷新線程類(注意游戲面板刷新線程的休眠時(shí)間一定要是最小的,且其他休眠時(shí)間是它的整數(shù)倍)
public class UpdateThread extends Thread{
JPanel panel;
public UpdateThread(JPanel panel) {
this.panel = panel;
}
@Override
public void run() {
while(true){
panel.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
7.最后再來個(gè)啟動(dòng)類,來啟動(dòng)程序(首先讀取到地圖數(shù)組,再打開窗口)
/**
* 開始游戲
* @author yy
*
*/
public class test {
public static void main(String[] args) {
//首先從地圖文件中讀入地圖數(shù)組
ReadMapFile.readfile("D:\\mygame\\map\\map1.map");
//用讀到的地圖數(shù)組創(chuàng)建游戲窗體,開始游戲
mainFrame mf = new mainFrame();
}
}
這樣下來,一個(gè)游戲基本的框架便完成了,接下來的任務(wù)便是加入碰撞處理部分了..
運(yùn)行這個(gè)程序(焦點(diǎn)離開那個(gè)小黑球吧,那是人物角色的替代品? = =!):
試了一下啊,轉(zhuǎn)化的gif畫質(zhì)簡直慘不忍睹,不過還是發(fā)上來試試,gif圖中有卡頓,實(shí)際程序中是沒有的,雖然我不知道iteye是否能支持gif...⊙﹏⊙‖∣°....
源代碼丟下面了,有興趣的可以一起玩玩...
搞到這個(gè)點(diǎn),我特么也是醉了....還好明天沒課└(^o^)┘
總結(jié)
以上是生活随笔為你收集整理的纯java语言做rpg游戏_【纯JAVA语言做个RPG游戏】2.游戏界面及角色移动的基本实现...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 水平垂直投影
- 下一篇: LeetCode 1079. 活字印刷