5.4 、讓我們增加玩家
對于這個向導,我們將只是使用一個占位符代替交通工具。我們將在之后載入模型,但那只是沒價值的工作,我們想要先讓游戲的核心能運作。一個 Box 是一個好的占位符,因為它是我們交通工具的基礎模型。
所以,讓我們先增加一個 buildPlayer 的方法并在 initGame 中調用它。我們將接著創建一個 box 做為玩家的幾何體并把這個 Box attach 到 node 。這個玩家 Node 將會是一個類變量,以便我們能在 update 期間訪問它。我將創建一個中心為 (0,0,0) 和大小為 (0.35,0.25,0.5) ,讓它看起來長和寬。 Node 接著被移到坐標 (100,0,100) 。我還沒設置它的高度,我將在之后才那么做。
private void buildPlayer() {
//box 代替
Box b = new Box( "box" , new Vector3f(), 0.35f,0.25f,0.5f);
b.setModelBound( new BoundingBox());
b.updateModelBound();
player = new Node( "Player Node" );
player .setLocalTranslation( new Vector3f(100,0, 100));
scene .attachChild( player );
player .attachChild(b);
player .updateWorldBound();
}
如果你現在運行這個,實際上不會看到 player ,因為它深陷在 terrain 下面。我們在 update 里面設置 height ,這是因為我們將很快讓交通工具在平面上移動,并需要讓它保持在 terrain 上。所以,為了保持 box 在地面的頂部行駛,增加:
// 確保當玩家離開平面時我們不會墜落。
// 當我們增加沖突時, fence 將做它自己的工作并保持玩家在里面。
float characterMinHeight =
tb .getHeight( player .getLocalTranslation()) +
((BoundingBox) player .getWorldBound()). yExtent ;
if (
!Float. isInfinite (characterMinHeight) &&
!Float. isNaN (characterMinHeight)
)
player .getLocalTranslation(). y = characterMinHeight;
首先,我們獲取玩家當前位置對應的 terrain 的高度。接著加上 BoundingBix 的偏移,我們這么做是因為 Box 位置的點是 Box 的中心。如果我們沒加上偏移, box 將有一半沉入地下。我們使用包圍對象的 BoundingBox 去獲取對象的高度( yExtent )(但實際上如果你對模型了解得很好,你可以使用值代替)。最后,我們檢查獲取的高度去確認沒有得到一些糟糕的值(非數字、無窮大等)。我們這么做是因為目前我們沒做任何事去阻止玩家駕駛出 terrain 。
我們現在已經在 terrain 上擁有了玩家!
( 現在你可能還看不到這個畫面,別急,后面會看到的 )
5.5 、跟隨攝像機( ChaseCamera )
好了,現在我們有了玩家,我們應該有能力移動它。這將是一個第三人稱游戲,意味我們在玩游戲的時候能看到自己的玩家(而不是以 player 的眼睛去看)。所以我們想要攝像頭一直指向玩家并跟隨他。為了這么做,我們將使用 ChaseCamera 。 ChaseCamera 將通過定義它跟隨距離的參數一直追蹤一個給出的對象。 ChaseCamera 也定義一些值讓它平滑跟隨。那就是它不能突然轉向玩家。這種突然性的效果當然也是可以定義的。
所以,當我們使用 ChaseCamera ,視圖將一直對著玩家。鼠標將允許 camera 在玩家四周旋轉并一直面向它。鼠標滾輪將允許 camera 縮放(盡管在這個例子中縮放值很?。?。
因此,創建一個 buildChaseCamera 方法并從 initGame 中調用它。我們在這里設置 ChaseCamera 的參數并創建它。 ChaseCamera 對象將成為一個類變量以致我們能 update 它(所以把它加到類的頂部)。
我們相對 ChaseCamera 設置的參數有一些。首先,我們將設置 Camera 的目標偏移玩家。我們通常想讓 camera 看起來在玩家上一點。所以我們設置偏移( offset )值為( 0 ,玩家的 Y*1.5 , 0 ) . 這將讓 camera 指向指向一個在玩家原始高度上面多一半的一個點。下一步,我們將設置滾動( rollout )值。這些值決定了我們能拉近或推遠攝像機多少。我這里不想給太多自由,因此這個級別實際上很小。所以我們設置最大為 6 個單元,而最小為 3 個單元。下一步我們將設置 camera 能向上轉動多高,在這個例子中為 45 度,注意是弧度。最后,我們將為 camera 設置開始起點的球形坐標, roll out 為 5 并升高 30 度。因為 camera 在一個“彈簧”系統中,如果交通工具行駛太快時,它能延遲落后一定距離。因此,我們將增加 camera 能落后的最小和最大值。 8 和 2 應該是可以的。
我們在一個 hash map 中設置這些參數。
private void buildChaseCamera() {
Vector3f targetOffset = new Vector3f();
targetOffset. y =
((BoundingBox) player .getWorldBound()). yExtent *1.5f;
HashMap<String, Object> props = new HashMap<String, Object>();
props.put(ThirdPersonMouseLook. PROP_MAXROLLOUT , "6" );
props.put(ThirdPersonMouseLook. PROP_MINROLLOUT , "3" );
props.put(
ThirdPersonMouseLook. PROP_MAXASCENT ,
"" +45*FastMath. DEG_TO_RAD
);
props.put(
ChaseCamera. PROP_INITIALSPHERECOORDS ,
new Vector3f(5,0,30*FastMath. DEG_TO_RAD )
);
props.put(ChaseCamera. PROP_TARGETOFFSET , targetOffset);
chaser = new ChaseCamera( cam , player , props);
chaser .setMaxDistance(8);
chaser .setMinDistance(2);
}
我們現在已經設置好了自己的 ChaseCamera ,但它在調用 update 方法之前不會產生任何作用。因此在 update 中加入:
chaser . update (interpolation);
現在,當應用程序運行時,你能看到 camera 在它初始化的位置并光滑地把鏡頭拉近直到最大的 6 個單元。你能接著移動鼠標去圍繞 box 旋轉 camera ,也能滾動鼠標滑輪去將 camera 拉近或推遠一點。那就是所激動的,但沒有什么東西可以追蹤,因為 box 只是停在那里。讓我們糾正那個。
這里補充一點是作者漏掉的,我們還需要在 game 的 update 方面里面加入下面代碼以保證 camera 的位置一直在 terrain 上面:
// 我們不想 chase camera 走到世界下面,因此讓它一直在水平面上 2 個單元。
if ( cam .getLocation(). y < ( tb .getHeight( cam .getLocation())+2)) {
cam .getLocation(). y = tb .getHeight( cam .getLocation()) + 2;
cam .update();
}
5.6 、我們自定義的輸入處理
我們將創建自己的輸入處理器( InputHandler )從而允許我們駕駛交通工具。這個 handler 的目標是允許我們行駛向前、向后和轉向。我想這些控制的鍵被設置為: WASD 。幸運的是,為了做到這個,我們將能使用在 jME 中構建的 action 。名字是, KeyNodeForwardAction , KeyNodeBackwardAction , KeyNodeRotateRightAction 和 KeyNodeRotateLeftAction 。這些 action 處理一個 node 的旋轉和移動,這些都基于速度和傳入的時間。
InputAction 很直觀。你簡單將觸發器( trigger )賦給 action ,并把鍵( key )賦給這些觸發器。然后在每次 update 期間它將檢查是否有任何鍵被按下,如果它們是 trigger 賦予的按鍵,那么則讓 trigger 去調用相應的 action 。
創建一個新的叫做 FlagRushInputHandler 的類,它繼承自 InputHandler 。這個類將只有 2 個方法, setKeyBindings 和 setActions 。 setKeyBindings 將創建 KeyBindingManager 并賦予 W,A,S,D 到相應的 trigger 名字,而 setActions 將為每個 trigger 創建 InputAction 。
FlagRushInputHandler.java
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.action.KeyNodeBackwardAction;
import com.jme.input.action.KeyNodeForwardAction;
import com.jme.input.action.KeyNodeRotateLeftAction;
import com.jme.scene.Spatial;
/**
* 游戲的 InputHnadler 。這控制了一個給出的 Spatial
* 允許我們去把它往前移、往后移和左右旋轉。
* @author John
*
*/
public class FlagRushInputHandler extends InputHandler {
/**
* 提供用于控制的 node 。 api 將處理 input 的創建
* @param node 我們想移動的那個 node
* @param api library 將處理 input 的創建
*/
public FlagRushInputHandler(Spatial node, String api){
setKeyBindings(api);
setActions(node);
}
/**
* 將 action 類賦給 trigger 。這些 action 處理結點前移、后移和旋轉
* @param node 用于控制的結點
*/
private void setActions(Spatial node) {
KeyNodeForwardAction forward =
new KeyNodeForwardAction(node,30f);
addAction(forward, "forward" , true );
KeyNodeBackwardAction backward =
new KeyNodeBackwardAction(node,15f);
addAction(backward, "backward" , true );
KeyNodeRotateLeftAction rotateLeft =
new KeyNodeRotateLeftAction(node,5f);
rotateLeft.setLockAxis(
node.getLocalRotation().getRotationColumn(1)
);
addAction(rotateLeft, "turnLeft" , true );
KeyNodeRotateRightAction rotateRight =
new KeyNodeRotateRightAction(node,5f);
rotateRight.setLockAxis(
node.getLocalRotation().getRotationColumn(1)
);
addAction(rotateRight, "turnRight" , true );
}
/**
* 創建 keyboard 對象,當鍵被按下時允許我們獲取鍵盤的值。
* 它接著設置 action 作為觸發器的基礎,如果確認了鍵被按下( WASD )
* @param api
*/
private void setKeyBindings(String api) {
KeyBindingManager keyboard =
KeyBindingManager. getKeyBindingManager ();
keyboard.set( "forward" , KeyInput. KEY_W );
keyboard.set( "backward" , KeyInput. KEY_S );
keyboard.set( "turnLeft" , KeyInput. KEY_A );
keyboard.set( "turnRight" , KeyInput. KEY_D );
}
}
當這個類真的寫完后,我們在自己的游戲中使用。創建一個 buildInput 方法,由 initGame 方法調用。這個方法將只有一行:
input = new FlagRushInputHandler(
player ,
settings . getRenderer ()
);
正如你所猜的,這里 input 也是類變量。為什么要在類里面呢?因為你將在游戲的 update 期間調用它的 update 。
就是那樣!不管相信與否,我們現在具有做游戲的條件。 Box 能被駕駛?,F在試試看。注意 ChaseCamera 將會落后于 box 一點然后嘗試趕上,帶來更平滑和真實的感覺。
接下來,我們將改進 box 的移動以便它能加速和減速。我們也將讓它和環境交互得更好。繼續收看!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
