人生如夢遊戲間,RPG遊戲開源開發講座 JAVA篇 3 ——邯鄲學步
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
書接前文,事表上回。話說上回書提到“畫面閃爍問題和角色動作的變更”是目前我們所面臨的兩大難點之一,本次,將就解決畫面閃爍的前提條件——角色動作變更,也即“動畫”進行較為深入的分析。
大家都很清楚的知道,所謂的動畫,並不是一個“會動的畫”,而是一組“連續變動的畫”,就好比Flash
要實現這點,首先我們需要一組連續的影象。
如下圖:
日常生活中,我們很少會不知道自己應該邁左腳還是邁右腳,但對計算機而言,這是我們必須明確提示給他的條件。所以,我們還需要一個變數充當“計步器”,以明確下步狀態。
private int count;
而要想實現動畫,最重要的一點,就是畫面的連續,即多步操作的處理,為此我們使用到了Java中的Thread,也即執行緒。
在Java中,目前不支援如C#式的函式直接被執行緒呼叫方式。Java要實現執行緒,必需要通過繼承Thread類或實現Runnable介面。
我們以Thread類的繼承為例:
//內部類,用於處理計步動作。
private class AnimationThread extends Thread {
while (true) {
// count計步
if (count == 0) {
count = 1;
} else if (count == 1) {
count = 0;
}
// 重繪畫面。
repaint();
// 每300毫秒改變一次動作。
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
所謂的繼承,也可以簡單的理解為COPY下所繼承類的全部方法,而這裡我們重寫了run()方法,沒有改變其他。也就是說,我們將以自己的方式執行AnimationThread這個類。
另外,在處理drawRole方法時,我們將其內部變更如下:
//以count作為影象的偏移數值
g.drawImage(roleImage, x*CS, y*CS, x*CS+CS, y*CS+CS,
count*CS, 0, CS+count*CS, CS, this);
推導公式如下圖:
最後,我們在MyPanel構建之初即啟動執行緒,令執行緒的相關操作活性化。
//例項化內部執行緒AnimationThread
threadAnime = new Thread(new AnimationThread());
//啟動執行緒
threadAnime.start();
MyPanel程式碼如下:
package org.loon.chair.example3;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
/**
* Example3中自定義面板,用於描繪底層地圖。
*
* @author chenpeng
*
* Loon Framework in Game
*
* PS:請注意,此處與前例不同,新增鍵盤事件監聽
*/
public class MyPanel extends JPanel implements KeyListener {
//窗體的寬與高
private static final int WIDTH = 480;
private static final int HEIGHT = 480;
//設定背景方格預設行數
private static final int ROW = 15;
//設定背景方格預設列數
private static final int COL = 15;
//單個影象大小,我預設採用32x32圖形,可根據需要調整比例。
//當時,始終應和窗體大小比例協調;比如32x32的圖片,如何
//一行設定15個,那麼就是480,也就是本例子預設的窗體大小,
//當然,我們也可以根據ROW*CS,COl*CS在初始化時自動調整
//窗體大小,以後的例子中會用到類似情況。總之一句話,程式設計
//是[為目的而存在的],所有的方法,大家都可任意嘗試和使用。
private static final int CS = 32;
//設定地圖,通常在rpg型別遊戲開發中,以[二維陣列]物件為
//基礎進行地圖處理,用以描繪出X座標和Y座標。實際上,即令
//再華麗的RPG類遊戲,都是從這些簡單的X,Y座標開始的。
//PS:所謂[陣列],大家可以簡單的理解為即資料的集合,一維陣列
//僅包含X軸,而二維是由X,Y兩個軸組成的,X與Y的交織點,即為
//一條資料。
private int[][] map = {
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,1,1,1,1,1,0,0,0,0,1},
{1,0,0,0,0,1,0,0,0,1,0,0,0,0,1},
{1,0,0,0,0,1,0,0,0,1,0,0,0,0,1},
{1,0,0,0,0,1,0,0,0,1,0,0,0,0,1},
{1,0,0,0,0,1,1,0,1,1,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
//設定顯示影象物件
private Image floorImage;
private Image wallImage;
//角色
private Image roleImage;
//角色座標
private int x, y;
//增加計步器
private int count;
//此處我們新增一組常數,用以區別左右上下按鍵的觸發,
//之所以採用數字進行區別,原因大家都很清楚^^,數字
//運算效率高嘛~
private static final int LEFT = 0;
private static final int RIGHT = 1;
private static final int UP = 2;
private static final int DOWN = 3;
private Thread threadAnime;
public MyPanel() {
//設定初始構造時面板大小
setPreferredSize(new Dimension(WIDTH, HEIGHT));
//於初始化時載入圖形
loadImage();
//初始化角色所在位置,由於本例行列皆為15,估x與y的極限數值也皆為15,
//即由15x15的方格影象,組成了角色的可見活動區域。
x = 8;
y = 8;
//在面板構建時賦予計步器初值
count = 0;
//設定焦點在本窗體並付與監聽物件
setFocusable(true);
addKeyListener(this);
//例項化內部執行緒AnimationThread
threadAnime = new Thread(new AnimationThread());
//啟動執行緒
threadAnime.start();
}
//描繪窗體,此處在預設JPanel基礎上構建底層地圖.
public void paintComponent(Graphics g) {
super.paintComponent(g);
//畫出地圖
drawMap(g);
//畫出人物
drawRole(g);
}
/**
* 載入影象
*
*/
private void loadImage() {
//獲得當前類對應的相對位置image資料夾下的地板影象
ImageIcon icon = new ImageIcon(getClass().getResource("image/floor.gif"));
//將地板影象例項付與floorImage
floorImage = icon.getImage();
//獲得當前類對應的相對位置image資料夾下的牆體影象
icon = new ImageIcon(getClass().getResource("image/wall.gif"));
//將牆體影象例項付與wallImage
wallImage = icon.getImage();
icon = new ImageIcon(getClass().getResource("image/hero.gif"));
roleImage = icon.getImage();
}
/**
* 繪製角色
*/
private void drawRole(Graphics g) {
//以count作為影象的偏移數值
g.drawImage(roleImage, x*CS, y*CS, x*CS+CS, y*CS+CS,
count*CS, 0, CS+count*CS, CS, this);
}
private void drawMap(Graphics g) {
//在Java或任何遊戲開發中,演算法都是最重要的一步,本例盡使用
//簡單的雙層for迴圈進行地圖描繪,
for (int x = 0; x < ROW; x++) {
for (int j = 0; j < COL; j++) {
// switch作為java中的轉換器,用於執行和()中數值相等
// 的case操作。請注意,在case操作中如果不以break退出
// 執行;switch函式將持續運算到最後一個case為止。
switch (map[x][j]) {
case 0 : //map的標記為0時畫出地板
//在指定位置[描繪]出我們所載入的圖形,以下同
g.drawImage(floorImage, j * CS, x * CS, this);
break;
case 1 : //map的標記為1時畫出城牆
g.drawImage(wallImage, j * CS, x * CS, this);
break;
//我們可以依次類推出無數的背景組合,如定義椅子為2、寶座為3等
//很容易即可勾勒出一張背景地圖。
default: //當所有case值皆不匹配時,將執行此操作。
break;
}