本次學習:
1.第三人稱視角研究
2.搖杆區域制作
3.視角限制設計
4.簡單的戰鬥部分
第三人稱視角是什麼大家玩玩遊戲就懂了-0- 羽化不是專業制作人,隻是一個玩家,下面是羽化玩家身份的總結出來的一些經驗,很多詞彙非專業,見諒。。。
1.人物移動 玩過魔獸世界的玩家可能都知道W A S D可以控制人物往8個方向移動,但人的面向不會改變,意味着有左移右移和後退等動作,羽化認為這樣設計比較有真實感,而且效果很好,但有些遊戲比如神鬼寓言就有所改變,W A S D不僅可以控制移動方向,而且還能改變人的面向,這種設計更加靈活,适合ACT,不适合ARPG,是以羽化這是做的一個ARPG,使用的是魔獸世界的移動标準,搖杆第一次點進區域就可自由移動,原來羽化就是這麼想的,看到混亂與秩序的搖杆後覺得更加合理,是以就借鑒了下。
2.視角移動 ARPG也有很多種視角,有斜45°類似火炬之光這種,也有魔獸世界一樣360°的,羽化原來做過斜45°的Demo,固定視角可以解決很多問題,但也伴随着更棘手的問題,既然公司要求,是以就做了這個360°的視角轉換,包括手勢放大縮小。視角改變很容易,但當視角更改以後問題出現了,玩過魔獸世界包括衆多自由視角遊戲的玩家都知道視角雖然可以随便移動,但存在着很多限制,比如你向上滑動時,視角不可能穿過地面而到地下,一定會沿着人的方向放大,這是前人給我們的經驗,比如有一個物體擋在人物和玩家中間時,視角應該拉近,這樣就不會産生視覺死角,諸如此類的情況很多,就運用到了射線Ray,進入我們今天的主題。
大家可以看到,羽化這個Demo用的東西也不多,這是前期的一個Demo,還不算完整,如果有機會給大家分享下羽化現在的Demo。
觸摸屏發展至今,羽化見過最好大的移動搖杆莫過于“混亂與秩序”的移動搖杆了,本身搖杆制作并不困難,主要是學習下這種為玩家帶來友善的思維模式,先把代碼送上:
Rocker.js:
//移動速度
var Speed : float = 0.05;
//視角轉動速度
var Speed_L : float = 0.1;
//角色
var Role : Transform;
//人稱視角
var Player_true : Transform;
//攻擊預設
var AttackPrafab : Transform;
//攻擊方向
var AttackRange : Transform;
//第一點按下的初始點
private var PO_X : int = 0;
private var PO_Y : int = 0;
//第二點按下的初始點
private var PT_X : int = 0;
private var PT_Y : int = 0;
//判斷人是否移動
static var PR_M : boolean = false;
//觸摸點的記錄
private var PM_X : int = 0;
private var PM_Y : int = 0;
private var PS_X : int = 0;
private var PS_Y : int = 0;
//觸摸點與中心點的距離
private var M_X : int = 0;
private var M_Y : int = 0;
//中心的坐标
private var PC_X : int = 0;
private var PC_Y : int = 0;
//旋轉變量
private var touchDeltaPosition : Vector2;
//兩點靜态和動态距離
private var Distance : int = 0;
private var Distance_D : int = 0;
//記錄錄影機距離
static var Camera_Record : float = -2.0;
//兩點的角度
static var Angles : int = 0;
//戰鬥焦點
static var Focus : boolean = false;
//打擊點
private var hit : RaycastHit;
//敵人方位
private var Enemie : Vector3;
//攻擊模式範圍
private var Range : float = 6.0;
//記錄時間
private var CreationTime : double = -10.0;
//處于攻擊動作
private var Attacking : boolean = false;
//Test
static var Test = 0.0;
function Update()
{
//記錄遊戲時間
//Test = Time.time;
//戰鬥狀态
if(Focus)
{
if(Vector3.Distance(transform.position,Enemie) > Range)
{
Focus = false;
Camera.main.depth = -1;
return;
}
Camera.main.depth = -3;
transform.LookAt(Enemie);
Player_true.transform.LookAt(Enemie);
}
if(Time.time > (CreationTime + 0.5) && Role.animation.IsPlaying("Attack") && Attacking)
Instantiate(AttackPrafab, AttackRange.transform.position,Quaternion.identity);
Attacking = false;
//單點觸摸時
if (Input.touchCount == 1)
Player_Move();
Attack();
//退出
if(Input.GetTouch(0).position.y > Screen.height - 50 && Input.GetTouch(0).position.x > Screen.width - 50)
Application.Quit();
//多點觸摸時
else if (Input.touchCount == 2)
Player_Look();
}
//角色移動和靜态視角
function Player_Move()
switch(Input.GetTouch(0).phase)
case TouchPhase.Began:
PO_X = Input.GetTouch(0).position.x;
PO_Y = Input.GetTouch(0).position.y;
PC_X = PO_X;
PC_Y = PO_Y;
var ray = Camera.main.ScreenPointToRay (Input.GetTouch(0).position);
if (Physics.Raycast (ray, hit))
{
if(hit.collider.gameObject.tag == "Enemies")
{
Focus = true;
Enemie = hit.transform.position;
}
else if((PO_X > 200 || PO_Y > 200) && (PO_X < Screen.width - 100 || PO_Y > 100))
Focus = false;
Camera.main.depth = -1;
}
break;
case TouchPhase.Moved:
PM_X = Input.GetTouch(0).position.x;
PM_Y = Input.GetTouch(0).position.y;
if(PO_X < 200 && PO_Y < 200 && PO_X > 0 && PO_Y > 0)
PR_M = true;
else if(Input.touchCount == 1)
touchDeltaPosition = Input.GetTouch(0).deltaPosition;
//視角限制
if(Player_true.transform.localEulerAngles.x <= 60 || Player_true.transform.localEulerAngles.x >= 310)
Player_true.transform.Rotate(Vector3(touchDeltaPosition.y,touchDeltaPosition.x,0) * Time.deltaTime * 100 * Speed_L, Space.World);
else if(Player_true.transform.localEulerAngles.x < 310 && Player_true.transform.localEulerAngles.x > 200)
Player_true.transform.localEulerAngles.x = 311;
else if(Player_true.transform.localEulerAngles.x > 60 && Player_true.transform.localEulerAngles.x < 200)
Player_true.transform.localEulerAngles.x = 59;
Player_true.transform.localEulerAngles.z = 0;
case TouchPhase.Ended:
PO_X = 0;
PO_Y = 0;
PM_X = 0;
PM_Y = 0;
PC_X = 0;
PC_Y = 0;
PR_M = false;
Role.animation.Stop("Run");
Role.animation.Stop("Back");
Role.animation.Stop("Right");
Role.animation.Stop("Left");
Role.animation.PlayQueued("Wait", QueueMode.CompleteOthers);
if(PR_M)
M_X = PM_X - PC_X;
M_Y = PM_Y - PC_Y;
//求距離
Distance = Mathf.Sqrt((M_X * M_X) + (M_Y * M_Y));
//求角度
Angles = Mathf.Atan2(M_X, M_Y)* Mathf.Rad2Deg;
if(Distance >= 50)
PC_X = PC_X*15/16 + PM_X/16;
PC_Y = PC_Y*15/16 + PM_Y/16;
//最大速度限制
if(M_X > 40)
M_X = 40;
else if(M_X < -40)
M_X = -40;
if(M_Y > 50)
M_Y = 50;
else if(M_Y < -30)
M_Y = -30;
//移動判斷和優化
if(Angles>=-45 && Angles <=45)
Role.animation.CrossFade("Run");
if(Angles>=-20 && Angles <=20)
M_X = 0;
else if(Angles>=135 || Angles <=-135)
Role.animation.CrossFade("Back");
if(Angles <=-160 || Angles>=160)
else if(Angles > 45 && Angles < 135)
Role.animation.CrossFade("Right");
if(Angles>=70 && Angles <=110)
M_Y = 0;
else if(Angles > -135 && Angles < -45)
Role.animation.CrossFade("Left");
if(Angles>=-110 && Angles <=-70)
//轉身
transform.localRotation = Quaternion.Euler(0, Player_true.transform.localEulerAngles.y, 0);
//var target = Quaternion.Euler (0, Player_true.transform.localEulerAngles.y, 0);
//transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * 2);
//移動
transform.Translate(M_X * Time.deltaTime * Speed,0, M_Y * Time.deltaTime *Speed);
//遊戲移動視角
function Player_Look()
switch(Input.GetTouch(1).phase)
case TouchPhase.Began:
PT_X = Input.GetTouch(1).position.x;
PT_Y = Input.GetTouch(1).position.y;
M_X = PO_X - PT_X;
M_Y = PO_Y - PT_Y;
Distance = Mathf.Sqrt((M_X * M_X) + (M_Y * M_Y));
PS_X = Input.GetTouch(1).position.x;
PS_Y = Input.GetTouch(1).position.y;
if(PR_M && !Focus)
touchDeltaPosition = Input.GetTouch(1).deltaPosition;
Player_true.transform.Rotate(Vector3(0,touchDeltaPosition.x,0) * Time.deltaTime * 160 * Speed_L, Space.World);
Player_true.transform.Rotate(Vector3(touchDeltaPosition.y,0,0) * Time.deltaTime * 90 * Speed_L, Space.World);
else if((PO_X > 200 || PO_Y > 200) && (PT_X > 200 || PT_Y > 200))
M_X = PS_X - PM_X;
M_Y = PS_Y - PM_Y;
Distance_D = Mathf.Sqrt((M_X * M_X) + (M_Y * M_Y));
if(Distance - Distance_D > 20 && Camera.main.transform.localPosition.z <= 0)
Camera.main.transform.localPosition.z += 0.1;
Camera_Record = Camera.main.transform.localPosition.z;
Distance = Distance_D;
else if(Distance - Distance_D < -20 && Camera.main.transform.localPosition.z >= -4)
Camera.main.transform.localPosition.z -= 0.1;
PT_X = 0;
PT_Y = 0;
PS_X = 0;
PS_Y = 0;
Distance = 0;
Distance_D = 0;
//攻擊判斷
function Attack()
//普通攻擊
if(Input.GetTouch(0).position.y < 100 && Input.GetTouch(0).position.x > Screen.width - 100 && !(Role.animation.IsPlaying("Attack")))
Role.animation.Play("Attack");
Role.animation.CrossFadeQueued("Wait", 0.3,QueueMode.CompleteOthers);
CreationTime = Time.time;
Attacking = true;
//移動攻擊
else if(PR_M && Input.GetTouch(1).position.y < 100 && Input.GetTouch(1).position.x > Screen.width - 100 && !(Role.animation.IsPlaying("Attack")))
PR_M = false;
Role.animation.CrossFadeQueued("Attack", 0.3, QueueMode.PlayNow);
Role.animation.CrossFadeQueued("Wait", 0.3, QueueMode.CompleteOthers);
function OnGUI ()
GUI.Box (Rect(0,Screen.height - 200,200,200), "Rocker");
GUI.Box (Rect(Screen.width - 50,0,50,50), "Quit");
GUI.Box (Rect(Screen.width - 100,Screen.height - 100,100,100), "Attack");
啊,好長,其實也不多,就300行+,上面有羽化的一些注釋,大家可以看看這個腳本,Rocker是綁在Player上的,大家可以從上一張圖看到,羽化見了兩個Player,一個叫Player_True的物體其實是一個空物體裡面放置了一些基本東西包括光和錄影機,旋轉視角的時候就是旋轉的Player_True,其中的好處隻有用過的人才能了解吧~ ~。羽化使用了預設制作攻擊,這是個權益之計,以後說不定會改,這裡最多判斷了兩個點的觸控情況,羽化是分别判斷的,這是吸取了前一個遊戲的經驗,這樣不會亂,因為移動,攻擊,轉視角全部如果寫在一個判斷裡面會造成很多沖突,羽化深有體會。
CameraZoom.js:
var Up : Transform;
private var Forward : boolean = false;
private var Back : boolean = false;
function Update ()
//通過向下和前後雷射判斷視角
var layerMask = 1 << 2;
layerMask = ~layerMask;
var hit : RaycastHit;
//向前雷射 判斷中間阻擋的鏡頭靠近
if(Physics.Raycast (transform.position, Camera.main.transform.TransformDirection (Vector3.forward), hit, (-Camera.main.transform.localPosition.z)))
Forward = true;
var distanceToForward = hit.distance;
if(Camera.main.transform.localPosition.z < - 0.5)
Camera.main.transform.localPosition.z += 0.05;
else
Forward = false;
//向後雷射 判斷後退時靠牆的鏡頭拉近
if(Physics.Raycast (transform.position, Camera.main.transform.TransformDirection (Vector3.forward * (-1)), hit, 0.2,layerMask))
Back = true;
var distanceToBack = hit.distance;
if(distanceToBack > 0.1)
Back = false;
//向下雷射 用于判斷下拉鏡頭的拉近
if (Physics.Raycast (transform.position, -Vector3.up, hit , (-2) * Rocker.Camera_Record, layerMask))
var distanceToGround = hit.distance;
//print(distanceToGround);
if(distanceToGround < 0.1)
Camera.main.transform.localPosition.z += 0.1;
else if(Camera.main.transform.localPosition.z > Rocker.Camera_Record && distanceToGround > 0.2 && !Back && !Forward)
Camera.main.transform.localPosition.z -= 0.02;
Camera.main.transform.localPosition.y = 0.6;
if(!Rocker.Focus)
transform.LookAt(Up);
function OnTriggerStay (other : Collider)
if (/*other.attachedRigidbody &&*/ Camera.main.transform.localPosition.z < - 0.4)
Camera.main.transform.localPosition.z += 0.02;
Back = true;
function OnTriggerExit (other : Collider)
Back = false;
首先,羽化開始認為隻有一個射線就可以搞定了,結果一個射線什麼都判斷不了,後來羽化仔細想了想就寫成了三個射線,加上一個判斷,終于效果出現了,話說羽化在錄影機設定Collider,是怕一個不小心,玩家把整個地圖看透了-0- 如果你想視角更舒暢點可以把other.attachedRigidbody的注釋去掉。這裡有個函數layerMask是判斷射線穿過那個層的,以後肯定用得上,1<<2代碼射線隻能穿過前兩層。
戰鬥部分很簡單,做一個攻擊預設,觸發攻擊動作時就産生預設,在極短時間給怪物造成傷害。絕大多數代碼都在移動部分了,這裡看看一個簡單的AI。
AI.js:
var range = 4;
var Player : Transform;
private var health = 10;
function Update()
Discover();
function OnTriggerEnter (other : Collider)
if (other.attachedRigidbody)
if(other.gameObject.tag == "Attack")
Rocker.Test = 10.2;
transform.Find("Small").animation.CrossFade("Hit");
transform.Find("Small").animation.CrossFadeQueued("Wait", 0.3,QueueMode.CompleteOthers);
health--;
if(health <= 0)
transform.Find("Small").animation.CrossFade("Dead");
Rocker.Focus = false;
Camera.main.depth = -1;
transform.tag = "Dead";
function Discover()
var layerMask = 1 << 3;
if(Vector3.Distance(transform.position,Player.position) > range)
return false;
if(Physics.Linecast(transform.position, Player.position,hit,layerMask))
if(hit.collider.gameObject.tag == "Player" && !(transform.Find("Small").animation.IsPlaying("Dead")))
transform.LookAt(Player);
return true;
else
return false;
return true;
怪物在一定範圍内會發現你,面朝向敵人,簡單的AI,可以添加怪物行走等,這就看大家對自己什麼要求了,當玩家點選怪物後會進入戰鬥視角,這時的移動時圍繞怪物的,是不是很像Fable~ ~羽化的Demo就初步完成了,最後來張截圖。
自己認為視角做得很棒,有什麼不足還望指出~ ~
APK下載下傳位址:
<a target="_blank" href="http://dl.dbank.com/c0bbqt1p13">http://dl.dbank.com/c0bbqt1p13</a>
本文轉蓬萊仙羽51CTO部落格,原文連結:http://blog.51cto.com/dingxiaowei/1366370,如需轉載請自行聯系原作者