十 FPS 開寶箱場景設計 Step01 在開啟新專案時, 於選擇初始匯入 Standard Assets, 並設定專案名及專案 位置 Step02 開啟後如下圖 1
Step03 首先先將基礎場景必需物建置完成 新增一個基礎方塊,GameObject > Create Other > Cube: Step04 設置位置至 (0, 0, 0), 並調整大小為 (50, 1, 50): 2
Step05 新增一平行光源,GameObject > Create Other > Directional Light: Step06 調整位置至 (0, 10, 0), 旋轉 X 至 40: 3
Step07 將預設的第一人稱的預製物件 (prefab) 拖拉至場景中 位於 Project 視窗內 Standard Assets > Prefabs > First Person Controller Step08 如下圖所示 : 4
Step09 由於第一人稱的預製物件中已存在攝影機, 故將原始場景中的攝影機 (Main Camera) 進行刪除, 如下圖所示 Step10 接著將已準備好的寶箱資源包匯入, 在 Project 視窗中按右鍵,Import Package 5
Step11 將全部資源都匯入 完成後如下圖所示 : 6
Step12 將寶箱物件拖拉至場景中, 並對 Y 軸旋轉 180 度, 如下圖所示 Step13 點選寶箱物件階層下的 Box01 物件 7
Step14 新增一個 Mesh Collider 至此物件上,Component > Physics > Mesh Collider: Step15 新增此一層碰撞器, 是為了防此人物可以穿透這個物件 點選寶箱最上層級 TreasureBox, 並新增一個 Box Collider,Component > Physics > Box Collider: 8
Step16 調整方塊碰撞器的數值,Size X:1.5,Z:1.5,Center Y:0.5: Step17 並將位置調整高度至 Y:0.5 高, 並將碰撞器中的 Is Trigger 勾選 : 勾選 Trigger 是為了啟動觸發器, 在其他物件進入時會啟動部份的程式腳本 9
Step18 在 Project 視窗中按右鍵,Create > JavaScript 新增一個 Java 腳本 Step19 重新命名為 OpenBox 10
Step20 將以下程式碼全部貼至 OpenBox.js 內 : //Is The Box Auto Open? var AutoOpen = true; private var canopen = false; //Set Animation to Clamp At The End animation["open"].wrapmode = WrapMode.ClampForever; //Stop Animation At Start animation.stop(); function Update () //If CanOpen, Play Open Animation if(canopen) animation.play("open"); function OnTriggerEnter (other : Collider) //When Player Enter This Collider //If This Box is Auto Open if(autoopen) //The Collider Tag "Player" Entered, The Box Open if(other.gameobject.tag == "Player") canopen = true; function OnTriggerStay (other : Collider) //When Player Stay In This Collider //If This Box is NOT Auto Open if(!autoopen) //The Collider Tag "Player" Stayed, and The Player Press Open, The Box Open if(other.gameobject.tag == "Player") canopen = other.getcomponent("control").canopen; function OnTriggerExit (other : Collider) //When Player Exit This Collider //If This Box is NOT Auto Open if(!autoopen) //The Collider Tag "Player" Exit, The canopen Switch Will Be False if(other.gameobject.tag == "Player") canopen = false; 11
Step21 並將 OpenBox 腳本拖拉至 Hierarchy 視窗內的 TreasureBox, 將程式腳本附 予至物件上, 如下圖所示 Step22 將寶箱動畫設定 Animation 內的 Play Automatically 勾選取消, 使它在執行 後不會自動播放動作, 如下圖所示 12
Step23 點選 First Person Controller, 並將它的 Tag 設定成為 Player, 如下兩圖所示 13
Step24 完成後執行, 會發現將 FPS 角色移靠近箱子後, 箱子會自動開啟, 如下圖 所示 以下製作使用鍵盤事件開啟寶箱作法 Step01 在 Project 視窗內點選右鍵,Create > Prefab 來新增一個預製物件, 如下圖 所示 14
Step02 並重新命名為 Box, 如下圖所示 Step03 將 Hierarchy 視窗中的 TreasureBox 拖拉至 Project 視窗內的 Box, 將寶箱製 作成為預製物件, 如下圖所示 15
Step04 再次新增一個 JavaScript, 命名為 Control, 如下圖所示 Step05 將以下程式碼全部貼至 Control.js 內 : private var canopen = false; function Update () //When Press G Key, Can_Open_Switch is On if(input.getkey("g")) canopen = true; else canopen = false; 16
Step06 並將 Control 腳本拖拉至 Hierarchy 視窗內的 First Person Controller, 將程 式腳本附予至物件上, 如下圖所示 Step07 在 Hierarchy 視窗中點選 TreasureBox, 按右鍵,Duplicate 複製一個寶箱, 如下圖所示 17
Step08 並位移至較旁邊處, 不要使兩個箱子重疊, 如下圖所示 Step09 將複製出來的寶箱中,OpenBox 腳本下的 Auto Open 勾選取消, 如下圖所 示 18
tep10 按下開始後查看, 角色靠近第一個箱子時, 第一個箱子會自動開啟, 而靠 近第二個箱子時, 並不會自動開啟, 而是要在靠近箱子後, 按下鍵盤中的 G 鍵, 寶箱才會開啟, 如下圖所示 Step11 全部完成後儲存場景, 命名為 OpenBoxExample, 即可完成本範例, 如下 圖所示 19
十一 3D 場景中常用互動效果 11.1 雙攝影機切換效果 Step01 設置兩個攝影機 Step02 一個可使用 look scripts + FPS scripts Step03 一個可使用攝影機縮放 scripts Step04 兩個皆加入攝影機切換 scripts Step05 就可以完成 FPS 視角與第二監視器視角相互切換 攝影機縮放 scripts var x:float; var y:float; var cameraview:float; function OnGUI () if(gui.repeatbutton(rect(0,0,60,30),"zoom+")) x= Time.deltaTime * cameraview; camera.fieldofview+=x; if(gui.repeatbutton(rect(70,0,60,30),"zoom-")) y= Time.deltaTime * cameraview; camera.fieldofview-=y; 直接運行即可 攝影機切換 scripts var camera0 : Camera; var camera1 : Camera; function Update () if (Input.GetKey ("1")) camera1.enabled = true; camera0.enabled = false; if (Input.GetKey ("2")) 20
camera1.enabled = false; camera0.enabled = true; function OnGUI () GUI.Box (Rect (10,10,100,90), "Camera Switch"); // Make the first button. If it is pressed, Application.Loadlevel (1) will be executed if (GUI.Button (Rect (20,40,80,20), "Camera 1")) camera1.enabled = true; camera0.enabled = false; // Make the second button. if (GUI.Button (Rect (20,70,80,20), "Camera 2")) camera1.enabled = false; camera0.enabled = true; 11.2 FPS 使用滑鼠拖曳剛體物件 Step01 增加 Script 並拉進 FPS 攝影機及可 var spring = 50.0; var damper = 5.0; var drag = 10.0; var angulardrag = 5.0; var distance = 0.2; var attachtocenterofmass = false; private var springjoint : SpringJoint; function Update () 21
if (!Input.GetMouseButtonDown (0)) return; var maincamera = FindCamera(); var hit : RaycastHit; if (!Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition), 100)) return; hit, if (!hit.rigidbody hit.rigidbody.iskinematic) return; if (!springjoint) var go = new GameObject("Rigidbody dragger"); body = go.addcomponent ("Rigidbody"); springjoint = go.addcomponent ("SpringJoint"); body.iskinematic = true; springjoint.transform.position = hit.point; if (attachtocenterofmass) var anchor = transform.transformdirection(hit.rigidbody.centerofmass) + hit.rigidbody.transform.position; anchor = springjoint.transform.inversetransformpoint(anchor); springjoint.anchor = anchor; else springjoint.anchor = Vector3.zero; springjoint.spring = spring; springjoint.damper = damper; springjoint.maxdistance = distance; 22
springjoint.connectedbody = hit.rigidbody; StartCoroutine ("DragObject", hit.distance); function DragObject (distance : float) var olddrag = springjoint.connectedbody.drag; var oldangulardrag = springjoint.connectedbody.angulardrag; springjoint.connectedbody.drag = drag; springjoint.connectedbody.angulardrag = angulardrag; var maincamera = FindCamera(); while (Input.GetMouseButton (0)) var ray = maincamera.screenpointtoray (Input.mousePosition); springjoint.transform.position = ray.getpoint(distance); yield; if (springjoint.connectedbody) springjoint.connectedbody.drag = olddrag; springjoint.connectedbody.angulardrag = oldangulardrag; springjoint.connectedbody = null; function FindCamera () if (camera) return camera; else return Camera.main; 23
11.3 FPS 推倒場景物件功能 Step01 增加 Script 並拉進 FPS 攝影機及可 // this script pushes all rigidbodies that the character touches var pushpower = 2.0; function OnControllerColliderHit (hit : ControllerColliderHit) var body : Rigidbody = hit.collider.attachedrigidbody; // no rigidbody if (body == null body.iskinematic) return; // We dont want to push objects below us if (hit.movedirection.y < -0.3) return; // Calculate push direction from move direction, // we only push objects to the sides never up and down var pushdir = Vector3 (hit.movedirection.x, 0, hit.movedirection.z); // If you know how fast your character is trying to move, // then you can also multiply the push velocity by that. // Apply the push body.velocity = pushdir * pushpower; 11.4 FPS 飛行視角與加速前進 Step01 增加 Script 並拉進 FPS 攝影機及可 Step02 按住 Shift 可以加速度前進 var speed = 6.0; var jumpspeed = 8.0; var gravity = 0; private var movedirection = Vector3.zero; private var grounded : boolean = false; function FixedUpdate() my=movedirection.y; movedirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); 24
movedirection = transform.transformdirection(movedirection); if (grounded) movedirection *= speed; else movedirection *= speed+transform.position.y/5; movedirection.y=my; if(input.getkey ("e")) transform.translate(0, -1, 0); if (Input.GetKey ("q") ) transform.translate(0, 1, 0); if(input.getkey("left shift")) speed=6*5; else speed=6; movedirection.y -= gravity * Time.deltaTime; var controller : CharacterController = GetComponent(CharacterController); var flags = controller.move(movedirection * Time.deltaTime); grounded = (flags & CollisionFlags.CollidedBelow)!= 0; @script RequireComponent(CharacterController) private var mywalker: FPSWalker=null; var maxheight: float=250; function Start () mywalker = gameobject.getcomponent(fpswalker); @script RequireComponent(FPSWalker) 25